Commit | Line | Data |
---|---|---|
7f918cf1 CE |
1 | (* Copyright (C) 2009,2014,2016-2017 Matthew Fluet. |
2 | * Copyright (C) 1999-2007 Henry Cejtin, Matthew Fluet, Suresh | |
3 | * Jagannathan, and Stephen Weeks. | |
4 | * Copyright (C) 1997-2000 NEC Research Institute. | |
5 | * | |
6 | * MLton is released under a BSD-style license. | |
7 | * See the file MLton-LICENSE for details. | |
8 | *) | |
9 | ||
10 | functor Machine (S: MACHINE_STRUCTS): MACHINE = | |
11 | struct | |
12 | ||
13 | open S | |
14 | ||
15 | structure ObjptrTycon = ObjptrTycon () | |
16 | structure Runtime = Runtime () | |
17 | structure Scale = Scale () | |
18 | structure RepType = RepType (structure CFunction = CFunction | |
19 | structure CType = CType | |
20 | structure Label = Label | |
21 | structure ObjptrTycon = ObjptrTycon | |
22 | structure Prim = Prim | |
23 | structure RealSize = RealSize | |
24 | structure Runtime = Runtime | |
25 | structure Scale = Scale | |
26 | structure WordSize = WordSize | |
27 | structure WordX = WordX | |
28 | structure WordXVector = WordXVector) | |
29 | structure ObjectType = RepType.ObjectType | |
30 | ||
31 | structure Type = RepType | |
32 | ||
33 | structure ChunkLabel = Id (val noname = "ChunkLabel") | |
34 | ||
35 | structure Register = | |
36 | struct | |
37 | datatype t = T of {index: int option ref, | |
38 | ty: Type.t} | |
39 | ||
40 | local | |
41 | fun make f (T r) = f r | |
42 | in | |
43 | val indexOpt = ! o (make #index) | |
44 | val ty = make #ty | |
45 | end | |
46 | ||
47 | fun layout (T {index, ty, ...}) = | |
48 | let | |
49 | open Layout | |
50 | in | |
51 | seq [str (concat ["R", Type.name ty]), | |
52 | paren (case !index of | |
53 | NONE => str "NONE" | |
54 | | SOME i => Int.layout i), | |
55 | str ": ", | |
56 | Type.layout ty] | |
57 | end | |
58 | ||
59 | val toString = Layout.toString o layout | |
60 | ||
61 | fun index (r as T {index, ...}) = | |
62 | case !index of | |
63 | NONE => | |
64 | Error.bug (concat ["Machine.Register: register ", | |
65 | toString r, " missing index"]) | |
66 | | SOME i => i | |
67 | ||
68 | fun setIndex (r as T {index, ...}, i) = | |
69 | case !index of | |
70 | NONE => index := SOME i | |
71 | | SOME _ => | |
72 | Error.bug (concat ["Machine.Register: register ", | |
73 | toString r, " index already set"]) | |
74 | ||
75 | fun new (ty, i) = T {index = ref i, | |
76 | ty = ty} | |
77 | ||
78 | fun equals (r, r') = | |
79 | (case (indexOpt r, indexOpt r') of | |
80 | (SOME i, SOME i') => i = i' | |
81 | | _ => false) | |
82 | andalso CType.equals (Type.toCType (ty r), Type.toCType (ty r')) | |
83 | ||
84 | val equals = | |
85 | Trace.trace2 ("Machine.Register.equals", layout, layout, Bool.layout) equals | |
86 | ||
87 | val isSubtype: t * t -> bool = | |
88 | fn (T {index = i, ty = t}, T {index = i', ty = t'}) => | |
89 | (case (!i, !i') of | |
90 | (SOME i, SOME i') => i = i' | |
91 | | _ => false) | |
92 | andalso Type.isSubtype (t, t') | |
93 | andalso CType.equals (Type.toCType t, Type.toCType t') | |
94 | end | |
95 | ||
96 | structure Global = | |
97 | struct | |
98 | datatype t = T of {index: int, | |
99 | isRoot: bool, | |
100 | ty: Type.t} | |
101 | ||
102 | fun layout (T {index, isRoot, ty, ...}) = | |
103 | let | |
104 | open Layout | |
105 | in | |
106 | seq [str "glob ", | |
107 | record [("index", Int.layout index), | |
108 | ("isRoot", Bool.layout isRoot), | |
109 | ("ty", Type.layout ty)]] | |
110 | end | |
111 | ||
112 | local | |
113 | fun make f (T r) = f r | |
114 | in | |
115 | val index = make #index | |
116 | val isRoot = make #isRoot | |
117 | val ty = make #ty | |
118 | end | |
119 | ||
120 | val nonRootCounter = Counter.new 0 | |
121 | fun numberOfNonRoot () = Counter.value nonRootCounter | |
122 | ||
123 | val memo = CType.memo (fn _ => Counter.new 0) | |
124 | fun numberOfType t = Counter.value (memo t) | |
125 | ||
126 | fun new {isRoot, ty} = | |
127 | let | |
128 | val isRoot = isRoot orelse not (Type.isObjptr ty) | |
129 | val counter = | |
130 | if isRoot | |
131 | then memo (Type.toCType ty) | |
132 | else nonRootCounter | |
133 | val g = T {index = Counter.next counter, | |
134 | isRoot = isRoot, | |
135 | ty = ty} | |
136 | in | |
137 | g | |
138 | end | |
139 | ||
140 | fun equals (T {index = i, isRoot = r, ty}, | |
141 | T {index = i', isRoot = r', ty = ty'}) = | |
142 | i = i' | |
143 | andalso r = r' | |
144 | andalso Type.equals (ty, ty') | |
145 | ||
146 | val isSubtype: t * t -> bool = | |
147 | fn (T {index = i, isRoot = r, ty}, | |
148 | T {index = i', isRoot = r', ty = ty'}) => | |
149 | i = i' | |
150 | andalso r = r' | |
151 | andalso Type.isSubtype (ty, ty') | |
152 | andalso CType.equals (Type.toCType ty, Type.toCType ty') | |
153 | end | |
154 | ||
155 | structure StackOffset = | |
156 | struct | |
157 | datatype t = T of {offset: Bytes.t, | |
158 | ty: Type.t} | |
159 | ||
160 | local | |
161 | fun make f (T r) = f r | |
162 | in | |
163 | val ty = make #ty | |
164 | end | |
165 | ||
166 | fun layout (T {offset, ty}): Layout.t = | |
167 | let | |
168 | open Layout | |
169 | in | |
170 | seq [str (concat ["S", Type.name ty]), | |
171 | paren (Bytes.layout offset), | |
172 | str ": ", Type.layout ty] | |
173 | end | |
174 | ||
175 | val equals: t * t -> bool = | |
176 | fn (T {offset = b, ty}, T {offset = b', ty = ty'}) => | |
177 | Bytes.equals (b, b') andalso Type.equals (ty, ty') | |
178 | ||
179 | val isSubtype: t * t -> bool = | |
180 | fn (T {offset = b, ty = t}, T {offset = b', ty = t'}) => | |
181 | Bytes.equals (b, b') andalso Type.isSubtype (t, t') | |
182 | ||
183 | val interfere: t * t -> bool = | |
184 | fn (T {offset = b, ty = ty}, T {offset = b', ty = ty'}) => | |
185 | let | |
186 | val max = Bytes.+ (b, Type.bytes ty) | |
187 | val max' = Bytes.+ (b', Type.bytes ty') | |
188 | in | |
189 | Bytes.> (max, b') andalso Bytes.> (max', b) | |
190 | end | |
191 | ||
192 | fun shift (T {offset, ty}, size): t = | |
193 | T {offset = Bytes.- (offset, size), | |
194 | ty = ty} | |
195 | end | |
196 | ||
197 | structure Operand = | |
198 | struct | |
199 | datatype t = | |
200 | ArrayOffset of {base: t, | |
201 | index: t, | |
202 | offset: Bytes.t, | |
203 | scale: Scale.t, | |
204 | ty: Type.t} | |
205 | | Cast of t * Type.t | |
206 | | Contents of {oper: t, | |
207 | ty: Type.t} | |
208 | | Frontier | |
209 | | GCState | |
210 | | Global of Global.t | |
211 | | Label of Label.t | |
212 | | Null | |
213 | | Offset of {base: t, | |
214 | offset: Bytes.t, | |
215 | ty: Type.t} | |
216 | | Register of Register.t | |
217 | | Real of RealX.t | |
218 | | StackOffset of StackOffset.t | |
219 | | StackTop | |
220 | | Word of WordX.t | |
221 | ||
222 | val ty = | |
223 | fn ArrayOffset {ty, ...} => ty | |
224 | | Cast (_, ty) => ty | |
225 | | Contents {ty, ...} => ty | |
226 | | Frontier => Type.cpointer () | |
227 | | GCState => Type.gcState () | |
228 | | Global g => Global.ty g | |
229 | | Label l => Type.label l | |
230 | | Null => Type.cpointer () | |
231 | | Offset {ty, ...} => ty | |
232 | | Real r => Type.real (RealX.size r) | |
233 | | Register r => Register.ty r | |
234 | | StackOffset s => StackOffset.ty s | |
235 | | StackTop => Type.cpointer () | |
236 | | Word w => Type.ofWordX w | |
237 | ||
238 | fun layout (z: t): Layout.t = | |
239 | let | |
240 | open Layout | |
241 | fun constrain (ty: Type.t): Layout.t = | |
242 | if !Control.showTypes | |
243 | then seq [str ": ", Type.layout ty] | |
244 | else empty | |
245 | in | |
246 | case z of | |
247 | ArrayOffset {base, index, offset, scale, ty} => | |
248 | seq [str (concat ["X", Type.name ty, " "]), | |
249 | tuple [layout base, layout index, Scale.layout scale, | |
250 | Bytes.layout offset], | |
251 | constrain ty] | |
252 | | Cast (z, ty) => | |
253 | seq [str "Cast ", tuple [layout z, Type.layout ty]] | |
254 | | Contents {oper, ty} => | |
255 | seq [str (concat ["C", Type.name ty, " "]), | |
256 | paren (layout oper)] | |
257 | | Frontier => str "<Frontier>" | |
258 | | GCState => str "<GCState>" | |
259 | | Global g => Global.layout g | |
260 | | Label l => Label.layout l | |
261 | | Null => str "NULL" | |
262 | | Offset {base, offset, ty} => | |
263 | seq [str (concat ["O", Type.name ty, " "]), | |
264 | tuple [layout base, Bytes.layout offset], | |
265 | constrain ty] | |
266 | | Real r => RealX.layout r | |
267 | | Register r => Register.layout r | |
268 | | StackOffset so => StackOffset.layout so | |
269 | | StackTop => str "<StackTop>" | |
270 | | Word w => WordX.layout w | |
271 | end | |
272 | ||
273 | val toString = Layout.toString o layout | |
274 | ||
275 | val rec equals = | |
276 | fn (ArrayOffset {base = b, index = i, ...}, | |
277 | ArrayOffset {base = b', index = i', ...}) => | |
278 | equals (b, b') andalso equals (i, i') | |
279 | | (Cast (z, t), Cast (z', t')) => | |
280 | Type.equals (t, t') andalso equals (z, z') | |
281 | | (Contents {oper = z, ...}, Contents {oper = z', ...}) => | |
282 | equals (z, z') | |
283 | | (GCState, GCState) => true | |
284 | | (Global g, Global g') => Global.equals (g, g') | |
285 | | (Label l, Label l') => Label.equals (l, l') | |
286 | | (Offset {base = b, offset = i, ...}, | |
287 | Offset {base = b', offset = i', ...}) => | |
288 | equals (b, b') andalso Bytes.equals (i, i') | |
289 | | (Real r, Real r') => RealX.equals (r, r') | |
290 | | (Register r, Register r') => Register.equals (r, r') | |
291 | | (StackOffset so, StackOffset so') => StackOffset.equals (so, so') | |
292 | | (Word w, Word w') => WordX.equals (w, w') | |
293 | | _ => false | |
294 | ||
295 | val stackOffset = StackOffset o StackOffset.T | |
296 | ||
297 | fun interfere (write: t, read: t): bool = | |
298 | let | |
299 | fun inter read = interfere (write, read) | |
300 | in | |
301 | case (read, write) of | |
302 | (Cast (z, _), _) => interfere (write, z) | |
303 | | (_, Cast (z, _)) => interfere (z, read) | |
304 | | (ArrayOffset {base, index, ...}, _) => | |
305 | inter base orelse inter index | |
306 | | (Contents {oper, ...}, _) => inter oper | |
307 | | (Global g, Global g') => Global.equals (g, g') | |
308 | | (Offset {base, ...}, _) => inter base | |
309 | | (Register r, Register r') => Register.equals (r, r') | |
310 | | (StackOffset so, StackOffset so') => | |
311 | StackOffset.interfere (so, so') | |
312 | | _ => false | |
313 | end | |
314 | ||
315 | val rec isLocation = | |
316 | fn ArrayOffset _ => true | |
317 | | Cast (z, _) => isLocation z | |
318 | | Contents _ => true | |
319 | | GCState => true | |
320 | | Global _ => true | |
321 | | Offset _ => true | |
322 | | Register _ => true | |
323 | | StackOffset _ => true | |
324 | | _ => false | |
325 | end | |
326 | ||
327 | structure Switch = Switch (open Atoms | |
328 | structure Type = Type | |
329 | structure Use = Operand) | |
330 | ||
331 | structure Statement = | |
332 | struct | |
333 | datatype t = | |
334 | Move of {dst: Operand.t, | |
335 | src: Operand.t} | |
336 | | Noop | |
337 | | PrimApp of {args: Operand.t vector, | |
338 | dst: Operand.t option, | |
339 | prim: Type.t Prim.t} | |
340 | | ProfileLabel of ProfileLabel.t | |
341 | ||
342 | val layout = | |
343 | let | |
344 | open Layout | |
345 | in | |
346 | fn Move {dst, src} => | |
347 | mayAlign [Operand.layout dst, | |
348 | seq [str " = ", Operand.layout src]] | |
349 | | Noop => str "Noop" | |
350 | | PrimApp {args, dst, prim, ...} => | |
351 | let | |
352 | val rest = | |
353 | seq [Prim.layout prim, str " ", | |
354 | Vector.layout Operand.layout args] | |
355 | in | |
356 | case dst of | |
357 | NONE => rest | |
358 | | SOME z => | |
359 | mayAlign [Operand.layout z, | |
360 | seq [str " = ", rest]] | |
361 | end | |
362 | | ProfileLabel l => | |
363 | seq [str "ProfileLabel ", ProfileLabel.layout l] | |
364 | end | |
365 | ||
366 | fun move (arg as {dst, src}) = | |
367 | if Operand.equals (dst, src) | |
368 | then Noop | |
369 | else Move arg | |
370 | ||
371 | val move = | |
372 | Trace.trace ("Machine.Statement.move", | |
373 | fn {dst, src} => | |
374 | Layout.record [("dst", Operand.layout dst), | |
375 | ("src", Operand.layout src)], | |
376 | layout) | |
377 | move | |
378 | ||
379 | fun moves {srcs, dsts} = | |
380 | Vector.fromListRev | |
381 | (Vector.fold2 (srcs, dsts, [], fn (src, dst, ac) => | |
382 | move {src = src, dst = dst} :: ac)) | |
383 | ||
384 | fun object {dst, header, size} = | |
385 | let | |
386 | datatype z = datatype Operand.t | |
387 | fun bytes (b: Bytes.t): Operand.t = | |
388 | Word (WordX.fromIntInf (Bytes.toIntInf b, WordSize.csize ())) | |
389 | val temp = Register (Register.new (Type.cpointer (), NONE)) | |
390 | in | |
391 | Vector.new4 | |
392 | (Move {dst = Contents {oper = Frontier, | |
393 | ty = Type.objptrHeader ()}, | |
394 | src = Word (WordX.fromIntInf (Word.toIntInf header, | |
395 | WordSize.objptrHeader ()))}, | |
396 | PrimApp {args = Vector.new2 (Frontier, | |
397 | bytes (Runtime.normalMetaDataSize ())), | |
398 | dst = SOME temp, | |
399 | prim = Prim.cpointerAdd}, | |
400 | (* CHECK; if objptr <> cpointer, need non-trivial coercion here. *) | |
401 | Move {dst = dst, src = Cast (temp, Operand.ty dst)}, | |
402 | PrimApp {args = Vector.new2 (Frontier, bytes size), | |
403 | dst = SOME Frontier, | |
404 | prim = Prim.cpointerAdd}) | |
405 | end | |
406 | ||
407 | fun foldOperands (s, ac, f) = | |
408 | case s of | |
409 | Move {dst, src} => f (dst, f (src, ac)) | |
410 | | PrimApp {args, dst, ...} => | |
411 | Vector.fold (args, Option.fold (dst, ac, f), f) | |
412 | | _ => ac | |
413 | ||
414 | fun foldDefs (s, a, f) = | |
415 | case s of | |
416 | Move {dst, ...} => f (dst, a) | |
417 | | PrimApp {dst, ...} => (case dst of | |
418 | NONE => a | |
419 | | SOME z => f (z, a)) | |
420 | | _ => a | |
421 | end | |
422 | ||
423 | structure FrameInfo = | |
424 | struct | |
425 | datatype t = T of {frameLayoutsIndex: int} | |
426 | ||
427 | fun layout (T {frameLayoutsIndex, ...}) = | |
428 | Layout.record [("frameLayoutsIndex", Int.layout frameLayoutsIndex)] | |
429 | ||
430 | fun equals (T {frameLayoutsIndex = i}, T {frameLayoutsIndex = i'}) = | |
431 | i = i' | |
432 | end | |
433 | ||
434 | structure Live = | |
435 | struct | |
436 | datatype t = | |
437 | Global of Global.t | |
438 | | Register of Register.t | |
439 | | StackOffset of StackOffset.t | |
440 | ||
441 | val layout: t -> Layout.t = | |
442 | fn Global g => Global.layout g | |
443 | | Register r => Register.layout r | |
444 | | StackOffset s => StackOffset.layout s | |
445 | ||
446 | val equals: t * t -> bool = | |
447 | fn (Global g, Global g') => Global.equals (g, g') | |
448 | | (Register r, Register r') => Register.equals (r, r') | |
449 | | (StackOffset s, StackOffset s') => StackOffset.equals (s, s') | |
450 | | _ => false | |
451 | ||
452 | val ty = | |
453 | fn Global g => Global.ty g | |
454 | | Register r => Register.ty r | |
455 | | StackOffset s => StackOffset.ty s | |
456 | ||
457 | val isSubtype: t * t -> bool = | |
458 | fn (Global g, Global g') => Global.isSubtype (g, g') | |
459 | | (Register r, Register r') => Register.isSubtype (r, r') | |
460 | | (StackOffset s, StackOffset s') => StackOffset.isSubtype (s, s') | |
461 | | _ => false | |
462 | ||
463 | val interfere: t * t -> bool = | |
464 | fn (l, l') => | |
465 | equals (l, l') | |
466 | orelse (case (l, l') of | |
467 | (StackOffset s, StackOffset s') => | |
468 | StackOffset.interfere (s, s') | |
469 | | _ => false) | |
470 | ||
471 | val fromOperand: Operand.t -> t option = | |
472 | fn Operand.Global g => SOME (Global g) | |
473 | | Operand.Register r => SOME (Register r) | |
474 | | Operand.StackOffset s => SOME (StackOffset s) | |
475 | | _ => NONE | |
476 | ||
477 | val toOperand: t -> Operand.t = | |
478 | fn Global g => Operand.Global g | |
479 | | Register r => Operand.Register r | |
480 | | StackOffset s => Operand.StackOffset s | |
481 | end | |
482 | ||
483 | structure Transfer = | |
484 | struct | |
485 | datatype t = | |
486 | Arith of {args: Operand.t vector, | |
487 | dst: Operand.t, | |
488 | overflow: Label.t, | |
489 | prim: Type.t Prim.t, | |
490 | success: Label.t} | |
491 | | CCall of {args: Operand.t vector, | |
492 | frameInfo: FrameInfo.t option, | |
493 | func: Type.t CFunction.t, | |
494 | return: Label.t option} | |
495 | | Call of {label: Label.t, | |
496 | live: Live.t vector, | |
497 | return: {return: Label.t, | |
498 | handler: Label.t option, | |
499 | size: Bytes.t} option} | |
500 | | Goto of Label.t | |
501 | | Raise | |
502 | | Return | |
503 | | Switch of Switch.t | |
504 | ||
505 | fun layout t = | |
506 | let | |
507 | open Layout | |
508 | in | |
509 | case t of | |
510 | Arith {prim, args, dst, overflow, success, ...} => | |
511 | seq [str "Arith ", | |
512 | record [("prim", Prim.layout prim), | |
513 | ("args", Vector.layout Operand.layout args), | |
514 | ("dst", Operand.layout dst), | |
515 | ("overflow", Label.layout overflow), | |
516 | ("success", Label.layout success)]] | |
517 | | CCall {args, frameInfo, func, return} => | |
518 | seq [str "CCall ", | |
519 | record | |
520 | [("args", Vector.layout Operand.layout args), | |
521 | ("frameInfo", Option.layout FrameInfo.layout frameInfo), | |
522 | ("func", CFunction.layout (func, Type.layout)), | |
523 | ("return", Option.layout Label.layout return)]] | |
524 | | Call {label, live, return} => | |
525 | seq [str "Call ", | |
526 | record [("label", Label.layout label), | |
527 | ("live", Vector.layout Live.layout live), | |
528 | ("return", Option.layout | |
529 | (fn {return, handler, size} => | |
530 | record [("return", Label.layout return), | |
531 | ("handler", | |
532 | Option.layout Label.layout handler), | |
533 | ("size", Bytes.layout size)]) | |
534 | return)]] | |
535 | | Goto l => seq [str "Goto ", Label.layout l] | |
536 | | Raise => str "Raise" | |
537 | | Return => str "Return " | |
538 | | Switch s => Switch.layout s | |
539 | end | |
540 | ||
541 | fun foldOperands (t, ac, f) = | |
542 | case t of | |
543 | Arith {args, dst, ...} => Vector.fold (args, f (dst, ac), f) | |
544 | | CCall {args, ...} => Vector.fold (args, ac, f) | |
545 | | Switch s => | |
546 | Switch.foldLabelUse | |
547 | (s, ac, {label = fn (_, a) => a, | |
548 | use = f}) | |
549 | | _ => ac | |
550 | ||
551 | fun foldDefs (t, a, f) = | |
552 | case t of | |
553 | Arith {dst, ...} => f (dst, a) | |
554 | | _ => a | |
555 | end | |
556 | ||
557 | structure Kind = | |
558 | struct | |
559 | datatype t = | |
560 | Cont of {args: Live.t vector, | |
561 | frameInfo: FrameInfo.t} | |
562 | | CReturn of {dst: Live.t option, | |
563 | frameInfo: FrameInfo.t option, | |
564 | func: Type.t CFunction.t} | |
565 | | Func | |
566 | | Handler of {frameInfo: FrameInfo.t, | |
567 | handles: Live.t vector} | |
568 | | Jump | |
569 | ||
570 | fun layout k = | |
571 | let | |
572 | open Layout | |
573 | in | |
574 | case k of | |
575 | Cont {args, frameInfo} => | |
576 | seq [str "Cont ", | |
577 | record [("args", Vector.layout Live.layout args), | |
578 | ("frameInfo", FrameInfo.layout frameInfo)]] | |
579 | | CReturn {dst, frameInfo, func} => | |
580 | seq [str "CReturn ", | |
581 | record | |
582 | [("dst", Option.layout Live.layout dst), | |
583 | ("frameInfo", Option.layout FrameInfo.layout frameInfo), | |
584 | ("func", CFunction.layout (func, Type.layout))]] | |
585 | | Func => str "Func" | |
586 | | Handler {frameInfo, handles} => | |
587 | seq [str "Handler ", | |
588 | record [("frameInfo", FrameInfo.layout frameInfo), | |
589 | ("handles", | |
590 | Vector.layout Live.layout handles)]] | |
591 | | Jump => str "Jump" | |
592 | end | |
593 | ||
594 | val frameInfoOpt = | |
595 | fn Cont {frameInfo, ...} => SOME frameInfo | |
596 | | CReturn {frameInfo, ...} => frameInfo | |
597 | | Handler {frameInfo, ...} => SOME frameInfo | |
598 | | _ => NONE | |
599 | end | |
600 | ||
601 | structure Block = | |
602 | struct | |
603 | datatype t = T of {kind: Kind.t, | |
604 | label: Label.t, | |
605 | live: Live.t vector, | |
606 | raises: Live.t vector option, | |
607 | returns: Live.t vector option, | |
608 | statements: Statement.t vector, | |
609 | transfer: Transfer.t} | |
610 | ||
611 | fun clear (T {label, ...}) = Label.clear label | |
612 | ||
613 | local | |
614 | fun make g (T r) = g r | |
615 | in | |
616 | val kind = make #kind | |
617 | val label = make #label | |
618 | end | |
619 | ||
620 | fun layout (T {kind, label, live, raises, returns, statements, transfer}) = | |
621 | let | |
622 | open Layout | |
623 | in | |
624 | align [seq [Label.layout label, | |
625 | str ": ", | |
626 | record [("kind", Kind.layout kind), | |
627 | ("live", Vector.layout Live.layout live), | |
628 | ("raises", | |
629 | Option.layout (Vector.layout Live.layout) | |
630 | raises), | |
631 | ("returns", | |
632 | Option.layout (Vector.layout Live.layout) | |
633 | returns)]], | |
634 | indent (align | |
635 | [align (Vector.toListMap | |
636 | (statements, Statement.layout)), | |
637 | Transfer.layout transfer], | |
638 | 4)] | |
639 | end | |
640 | ||
641 | fun layouts (block, output' : Layout.t -> unit) = output' (layout block) | |
642 | ||
643 | fun foldDefs (T {kind, statements, transfer, ...}, a, f) = | |
644 | let | |
645 | val a = | |
646 | case kind of | |
647 | Kind.CReturn {dst, ...} => | |
648 | (case dst of | |
649 | NONE => a | |
650 | | SOME z => f (Live.toOperand z, a)) | |
651 | | _ => a | |
652 | val a = | |
653 | Vector.fold (statements, a, fn (s, a) => | |
654 | Statement.foldDefs (s, a, f)) | |
655 | val a = Transfer.foldDefs (transfer, a, f) | |
656 | in | |
657 | a | |
658 | end | |
659 | end | |
660 | ||
661 | structure Chunk = | |
662 | struct | |
663 | datatype t = T of {blocks: Block.t vector, | |
664 | chunkLabel: ChunkLabel.t, | |
665 | regMax: CType.t -> int} | |
666 | ||
667 | fun layouts (T {blocks, ...}, output : Layout.t -> unit) = | |
668 | Vector.foreach (blocks, fn block => Block.layouts (block, output)) | |
669 | ||
670 | fun clear (T {blocks, ...}) = | |
671 | Vector.foreach (blocks, Block.clear) | |
672 | end | |
673 | ||
674 | structure ProfileInfo = | |
675 | struct | |
676 | datatype t = | |
677 | T of {frameSources: int vector, | |
678 | labels: {label: ProfileLabel.t, | |
679 | sourceSeqsIndex: int} vector, | |
680 | names: string vector, | |
681 | sourceSeqs: int vector vector, | |
682 | sources: {nameIndex: int, | |
683 | successorsIndex: int} vector} | |
684 | ||
685 | val empty = T {frameSources = Vector.new0 (), | |
686 | labels = Vector.new0 (), | |
687 | names = Vector.new0 (), | |
688 | sourceSeqs = Vector.new0 (), | |
689 | sources = Vector.new0 ()} | |
690 | ||
691 | fun clear (T {labels, ...}) = | |
692 | Vector.foreach (labels, ProfileLabel.clear o #label) | |
693 | ||
694 | fun layout (T {frameSources, labels, names, sourceSeqs, sources}) = | |
695 | Layout.record | |
696 | [("frameSources", Vector.layout Int.layout frameSources), | |
697 | ("labels", | |
698 | Vector.layout (fn {label, sourceSeqsIndex} => | |
699 | Layout.record | |
700 | [("label", ProfileLabel.layout label), | |
701 | ("sourceSeqsIndex", | |
702 | Int.layout sourceSeqsIndex)]) | |
703 | labels), | |
704 | ("names", Vector.layout String.layout names), | |
705 | ("sourceSeqs", Vector.layout (Vector.layout Int.layout) sourceSeqs), | |
706 | ("sources", | |
707 | Vector.layout (fn {nameIndex, successorsIndex} => | |
708 | Layout.record [("nameIndex", Int.layout nameIndex), | |
709 | ("successorsIndex", | |
710 | Int.layout successorsIndex)]) | |
711 | sources)] | |
712 | ||
713 | fun layouts (pi, output) = output (layout pi) | |
714 | ||
715 | fun isOK (T {frameSources, labels, names, sourceSeqs, sources}): bool = | |
716 | let | |
717 | val namesLength = Vector.length names | |
718 | val sourceSeqsLength = Vector.length sourceSeqs | |
719 | val sourcesLength = Vector.length sources | |
720 | in | |
721 | !Control.profile = Control.ProfileNone | |
722 | orelse | |
723 | (Vector.forall (frameSources, fn i => | |
724 | 0 <= i andalso i < sourceSeqsLength) | |
725 | andalso (Vector.forall | |
726 | (labels, fn {sourceSeqsIndex = i, ...} => | |
727 | 0 <= i andalso i < sourceSeqsLength)) | |
728 | andalso (Vector.forall | |
729 | (sourceSeqs, fn v => | |
730 | Vector.forall | |
731 | (v, fn i => 0 <= i andalso i < sourcesLength))) | |
732 | andalso (Vector.forall | |
733 | (sources, fn {nameIndex, successorsIndex} => | |
734 | 0 <= nameIndex | |
735 | andalso nameIndex < namesLength | |
736 | andalso 0 <= successorsIndex | |
737 | andalso successorsIndex < sourceSeqsLength))) | |
738 | end | |
739 | ||
740 | fun modify (T {frameSources, labels, names, sourceSeqs, sources}) | |
741 | : {newProfileLabel: ProfileLabel.t -> ProfileLabel.t, | |
742 | delProfileLabel: ProfileLabel.t -> unit, | |
743 | getProfileInfo: unit -> t} = | |
744 | let | |
745 | val {get: ProfileLabel.t -> int, set, ...} = | |
746 | Property.getSet | |
747 | (ProfileLabel.plist, | |
748 | Property.initRaise ("ProfileInfo.extend", ProfileLabel.layout)) | |
749 | val _ = | |
750 | Vector.foreach | |
751 | (labels, fn {label, sourceSeqsIndex} => | |
752 | set (label, sourceSeqsIndex)) | |
753 | val new = ref [] | |
754 | fun newProfileLabel l = | |
755 | let | |
756 | val i = get l | |
757 | val l' = ProfileLabel.new () | |
758 | val _ = set (l', i) | |
759 | val _ = List.push (new, {label = l', sourceSeqsIndex = i}) | |
760 | in | |
761 | l' | |
762 | end | |
763 | fun delProfileLabel l = set (l, ~1) | |
764 | fun getProfileInfo () = | |
765 | let | |
766 | val labels = Vector.concat | |
767 | [labels, Vector.fromList (!new)] | |
768 | val labels = Vector.keepAll | |
769 | (labels, fn {label, ...} => | |
770 | get label <> ~1) | |
771 | val pi = T {frameSources = frameSources, | |
772 | labels = Vector.concat | |
773 | [labels, Vector.fromList (!new)], | |
774 | names = names, | |
775 | sourceSeqs = sourceSeqs, | |
776 | sources = sources} | |
777 | in | |
778 | Assert.assert ("Machine.getProfileInfo", fn () => isOK pi); | |
779 | pi | |
780 | end | |
781 | in | |
782 | {newProfileLabel = newProfileLabel, | |
783 | delProfileLabel = delProfileLabel, | |
784 | getProfileInfo = getProfileInfo} | |
785 | end | |
786 | end | |
787 | ||
788 | structure Program = | |
789 | struct | |
790 | datatype t = T of {chunks: Chunk.t list, | |
791 | frameLayouts: {frameOffsetsIndex: int, | |
792 | isC: bool, | |
793 | size: Bytes.t} vector, | |
794 | frameOffsets: Bytes.t vector vector, | |
795 | handlesSignals: bool, | |
796 | main: {chunkLabel: ChunkLabel.t, | |
797 | label: Label.t}, | |
798 | maxFrameSize: Bytes.t, | |
799 | objectTypes: ObjectType.t vector, | |
800 | profileInfo: ProfileInfo.t option, | |
801 | reals: (Global.t * RealX.t) list, | |
802 | vectors: (Global.t * WordXVector.t) list} | |
803 | ||
804 | fun clear (T {chunks, profileInfo, ...}) = | |
805 | (List.foreach (chunks, Chunk.clear) | |
806 | ; Option.app (profileInfo, ProfileInfo.clear)) | |
807 | ||
808 | fun frameSize (T {frameLayouts, ...}, | |
809 | FrameInfo.T {frameLayoutsIndex, ...}) = | |
810 | #size (Vector.sub (frameLayouts, frameLayoutsIndex)) | |
811 | ||
812 | fun layouts (T {chunks, frameLayouts, frameOffsets, handlesSignals, | |
813 | main = {label, ...}, | |
814 | maxFrameSize, objectTypes, profileInfo, ...}, | |
815 | output': Layout.t -> unit) = | |
816 | let | |
817 | open Layout | |
818 | val output = output' | |
819 | in | |
820 | output (record | |
821 | [("handlesSignals", Bool.layout handlesSignals), | |
822 | ("main", Label.layout label), | |
823 | ("maxFrameSize", Bytes.layout maxFrameSize), | |
824 | ("frameOffsets", | |
825 | Vector.layout (Vector.layout Bytes.layout) frameOffsets), | |
826 | ("frameLayouts", | |
827 | Vector.layout (fn {frameOffsetsIndex, isC, size} => | |
828 | record [("frameOffsetsIndex", | |
829 | Int.layout frameOffsetsIndex), | |
830 | ("isC", Bool.layout isC), | |
831 | ("size", Bytes.layout size)]) | |
832 | frameLayouts)]) | |
833 | ; Option.app (profileInfo, fn pi => | |
834 | (output (str "\nProfileInfo:") | |
835 | ; ProfileInfo.layouts (pi, output))) | |
836 | ; output (str "\nObjectTypes:") | |
837 | ; Vector.foreachi (objectTypes, fn (i, ty) => | |
838 | output (seq [str "opt_", Int.layout i, | |
839 | str " = ", ObjectType.layout ty])) | |
840 | ; output (str "\n") | |
841 | ; List.foreach (chunks, fn chunk => Chunk.layouts (chunk, output)) | |
842 | end | |
843 | ||
844 | structure Alloc = | |
845 | struct | |
846 | datatype t = T of Live.t list | |
847 | ||
848 | fun layout (T ds) = List.layout Live.layout ds | |
849 | ||
850 | fun forall (T ds, f) = List.forall (ds, f o Live.toOperand) | |
851 | ||
852 | fun defineLive (T ls, l) = T (l :: ls) | |
853 | ||
854 | fun define (T ds, z) = | |
855 | case Live.fromOperand z of | |
856 | NONE => T ds | |
857 | | SOME d => T (d :: ds) | |
858 | ||
859 | val new: Live.t list -> t = T | |
860 | ||
861 | fun doesDefine (T ls, l': Live.t): bool = | |
862 | let | |
863 | val oper' = Live.toOperand l' | |
864 | in | |
865 | case List.peek (ls, fn l => | |
866 | Operand.interfere (Live.toOperand l, oper')) of | |
867 | NONE => false | |
868 | | SOME l => Live.isSubtype (l, l') | |
869 | end | |
870 | ||
871 | val doesDefine = | |
872 | Trace.trace2 | |
873 | ("Machine.Program.Alloc.doesDefine", | |
874 | layout, Live.layout, Bool.layout) | |
875 | doesDefine | |
876 | end | |
877 | ||
878 | fun typeCheck (program as | |
879 | T {chunks, frameLayouts, frameOffsets, | |
880 | maxFrameSize, objectTypes, profileInfo, reals, | |
881 | vectors, ...}) = | |
882 | let | |
883 | val _ = | |
884 | if !Control.profile = Control.ProfileTimeLabel | |
885 | then | |
886 | List.foreach | |
887 | (chunks, fn Chunk.T {blocks, ...} => | |
888 | Vector.foreach | |
889 | (blocks, fn Block.T {kind, label, statements, ...} => | |
890 | if (case kind of | |
891 | Kind.Func => true | |
892 | | _ => false) | |
893 | orelse (0 < Vector.length statements | |
894 | andalso (case Vector.first statements of | |
895 | Statement.ProfileLabel _ => true | |
896 | | _ => false)) | |
897 | then () | |
898 | else print (concat ["missing profile info: ", | |
899 | Label.toString label, "\n"]))) | |
900 | else () | |
901 | val profileLabelIsOk = | |
902 | case profileInfo of | |
903 | NONE => | |
904 | if !Control.profile = Control.ProfileNone | |
905 | then fn _ => false | |
906 | else Error.bug | |
907 | "Machine.Program.typeCheck.profileLabelIsOk: profileInfo = NONE" | |
908 | | SOME (ProfileInfo.T {frameSources, | |
909 | labels = profileLabels, ...}) => | |
910 | if !Control.profile = Control.ProfileNone | |
911 | orelse (Vector.length frameSources | |
912 | <> Vector.length frameLayouts) | |
913 | then Error.bug | |
914 | "Machine.Program.typeCheck.profileLabelIsOk: profileInfo = SOME" | |
915 | else | |
916 | let | |
917 | val {get = profileLabelCount, ...} = | |
918 | Property.get | |
919 | (ProfileLabel.plist, | |
920 | Property.initFun (fn _ => ref 0)) | |
921 | val _ = | |
922 | Vector.foreach | |
923 | (profileLabels, fn {label, ...} => | |
924 | let | |
925 | val r = profileLabelCount label | |
926 | in | |
927 | if 0 = !r | |
928 | then r := 1 | |
929 | else Error.bug | |
930 | "Machine.Program.typeCheck.profileLabelIsOk: duplicate profile label" | |
931 | end) | |
932 | in | |
933 | fn l => | |
934 | let | |
935 | val r = profileLabelCount l | |
936 | in | |
937 | if 1 = !r | |
938 | then (r := 2; true) | |
939 | else false | |
940 | end | |
941 | end | |
942 | fun getFrameInfo (FrameInfo.T {frameLayoutsIndex, ...}) = | |
943 | Vector.sub (frameLayouts, frameLayoutsIndex) | |
944 | val _ = | |
945 | Vector.foreach | |
946 | (frameLayouts, fn {frameOffsetsIndex, size, ...} => | |
947 | Err.check | |
948 | ("frameLayouts", | |
949 | fn () => (0 <= frameOffsetsIndex | |
950 | andalso frameOffsetsIndex < Vector.length frameOffsets | |
951 | andalso Bytes.<= (size, maxFrameSize) | |
952 | andalso Bytes.<= (size, Runtime.maxFrameSize) | |
953 | andalso Bytes.isWord32Aligned size), | |
954 | fn () => Layout.record [("frameOffsetsIndex", | |
955 | Int.layout frameOffsetsIndex), | |
956 | ("size", Bytes.layout size)])) | |
957 | val _ = | |
958 | Vector.foreach | |
959 | (objectTypes, fn ty => | |
960 | Err.check ("objectType", | |
961 | fn () => ObjectType.isOk ty, | |
962 | fn () => ObjectType.layout ty)) | |
963 | fun tyconTy (opt: ObjptrTycon.t): ObjectType.t = | |
964 | Vector.sub (objectTypes, ObjptrTycon.index opt) | |
965 | open Layout | |
966 | fun globals (name, gs, isOk, layout) = | |
967 | List.foreach | |
968 | (gs, fn (g, s) => | |
969 | let | |
970 | val ty = Global.ty g | |
971 | in | |
972 | Err.check | |
973 | (concat ["global ", name], | |
974 | fn () => isOk (ty, s), | |
975 | fn () => seq [layout s, str ": ", Type.layout ty]) | |
976 | end) | |
977 | val _ = | |
978 | globals ("real", reals, | |
979 | fn (t, r) => Type.equals (t, Type.real (RealX.size r)), | |
980 | RealX.layout) | |
981 | val _ = | |
982 | globals ("vector", vectors, | |
983 | fn (t, v) => | |
984 | Type.equals (t, Type.ofWordXVector v), | |
985 | WordXVector.layout) | |
986 | (* Check for no duplicate labels. *) | |
987 | local | |
988 | val {get, ...} = | |
989 | Property.get (Label.plist, | |
990 | Property.initFun (fn _ => ref false)) | |
991 | in | |
992 | val _ = | |
993 | List.foreach | |
994 | (chunks, fn Chunk.T {blocks, ...} => | |
995 | Vector.foreach | |
996 | (blocks, fn Block.T {label, ...} => | |
997 | let | |
998 | val r = get label | |
999 | in | |
1000 | if !r | |
1001 | then Error.bug "Machine.Program.typeCheck: duplicate label" | |
1002 | else r := true | |
1003 | end)) | |
1004 | end | |
1005 | val {get = labelBlock: Label.t -> Block.t, | |
1006 | set = setLabelBlock, ...} = | |
1007 | Property.getSetOnce (Label.plist, | |
1008 | Property.initRaise ("block", Label.layout)) | |
1009 | val _ = | |
1010 | List.foreach | |
1011 | (chunks, fn Chunk.T {blocks, ...} => | |
1012 | Vector.foreach | |
1013 | (blocks, fn b as Block.T {label, ...} => | |
1014 | setLabelBlock (label, b))) | |
1015 | fun checkOperand (x: Operand.t, alloc: Alloc.t): unit = | |
1016 | let | |
1017 | datatype z = datatype Operand.t | |
1018 | fun ok () = | |
1019 | case x of | |
1020 | ArrayOffset {base, index, offset, scale, ty} => | |
1021 | (checkOperand (base, alloc) | |
1022 | ; checkOperand (index, alloc) | |
1023 | ; (Operand.isLocation base | |
1024 | andalso | |
1025 | (Type.arrayOffsetIsOk {base = Operand.ty base, | |
1026 | index = Operand.ty index, | |
1027 | offset = offset, | |
1028 | tyconTy = tyconTy, | |
1029 | result = ty, | |
1030 | scale = scale}))) | |
1031 | | Cast (z, t) => | |
1032 | (checkOperand (z, alloc) | |
1033 | ; (Type.castIsOk | |
1034 | {from = Operand.ty z, | |
1035 | to = t, | |
1036 | tyconTy = tyconTy})) | |
1037 | | Contents {oper, ...} => | |
1038 | (checkOperand (oper, alloc) | |
1039 | ; Type.isCPointer (Operand.ty oper)) | |
1040 | | Frontier => true | |
1041 | | GCState => true | |
1042 | | Global _ => | |
1043 | (* We don't check that globals are defined because | |
1044 | * they aren't captured by liveness info. It would | |
1045 | * be nice to fix this. | |
1046 | *) | |
1047 | true | |
1048 | | Label l => | |
1049 | (let val _ = labelBlock l | |
1050 | in true | |
1051 | end handle _ => false) | |
1052 | | Null => true | |
1053 | | Offset {base, offset, ty} => | |
1054 | (checkOperand (base, alloc) | |
1055 | ; (Operand.isLocation base | |
1056 | andalso | |
1057 | (case base of | |
1058 | Operand.GCState => true | |
1059 | | _ => | |
1060 | Type.offsetIsOk {base = Operand.ty base, | |
1061 | offset = offset, | |
1062 | tyconTy = tyconTy, | |
1063 | result = ty}))) | |
1064 | | Real _ => true | |
1065 | | Register r => Alloc.doesDefine (alloc, Live.Register r) | |
1066 | | StackOffset (so as StackOffset.T {offset, ty, ...}) => | |
1067 | Bytes.<= (Bytes.+ (offset, Type.bytes ty), | |
1068 | maxFrameSize) | |
1069 | andalso Alloc.doesDefine (alloc, Live.StackOffset so) | |
1070 | andalso (case Type.deLabel ty of | |
1071 | NONE => true | |
1072 | | SOME l => | |
1073 | let | |
1074 | val Block.T {kind, ...} = | |
1075 | labelBlock l | |
1076 | fun doit fi = | |
1077 | let | |
1078 | val {size, ...} = | |
1079 | getFrameInfo fi | |
1080 | in | |
1081 | Bytes.equals | |
1082 | (size, | |
1083 | Bytes.+ (offset, | |
1084 | Runtime.labelSize ())) | |
1085 | end | |
1086 | in | |
1087 | case kind of | |
1088 | Kind.Cont {frameInfo, ...} => | |
1089 | doit frameInfo | |
1090 | | Kind.CReturn {frameInfo, ...} => | |
1091 | (case frameInfo of | |
1092 | NONE => true | |
1093 | | SOME fi => doit fi) | |
1094 | | Kind.Func => true | |
1095 | | Kind.Handler {frameInfo, ...} => | |
1096 | doit frameInfo | |
1097 | | Kind.Jump => true | |
1098 | end) | |
1099 | | StackTop => true | |
1100 | | Word _ => true | |
1101 | in | |
1102 | Err.check ("operand", ok, fn () => Operand.layout x) | |
1103 | end | |
1104 | fun checkOperands (v, a) = | |
1105 | Vector.foreach (v, fn z => checkOperand (z, a)) | |
1106 | fun check' (x, name, isOk, layout) = | |
1107 | Err.check (name, fn () => isOk x, fn () => layout x) | |
1108 | val labelKind = Block.kind o labelBlock | |
1109 | fun checkKind (k: Kind.t, alloc: Alloc.t): Alloc.t option = | |
1110 | let | |
1111 | datatype z = datatype Kind.t | |
1112 | exception No | |
1113 | fun frame (FrameInfo.T {frameLayoutsIndex}, | |
1114 | useSlots: bool, | |
1115 | isC: bool): bool = | |
1116 | let | |
1117 | val {frameOffsetsIndex, isC = isC', ...} = | |
1118 | Vector.sub (frameLayouts, frameLayoutsIndex) | |
1119 | handle Subscript => raise No | |
1120 | in | |
1121 | isC = isC' | |
1122 | andalso | |
1123 | (not useSlots | |
1124 | orelse | |
1125 | let | |
1126 | val Alloc.T zs = alloc | |
1127 | val liveOffsets = | |
1128 | List.fold | |
1129 | (zs, [], fn (z, liveOffsets) => | |
1130 | case z of | |
1131 | Live.StackOffset (StackOffset.T {offset, ty}) => | |
1132 | if Type.isObjptr ty | |
1133 | then offset :: liveOffsets | |
1134 | else liveOffsets | |
1135 | | _ => raise No) | |
1136 | val liveOffsets = Array.fromList liveOffsets | |
1137 | val () = QuickSort.sortArray (liveOffsets, Bytes.<=) | |
1138 | val liveOffsets = Vector.fromArray liveOffsets | |
1139 | val liveOffsets' = | |
1140 | Vector.sub (frameOffsets, frameOffsetsIndex) | |
1141 | handle Subscript => raise No | |
1142 | in | |
1143 | Vector.equals (liveOffsets, liveOffsets', | |
1144 | Bytes.equals) | |
1145 | end) | |
1146 | end handle No => false | |
1147 | fun slotsAreInFrame (fi: FrameInfo.t): bool = | |
1148 | let | |
1149 | val {size, ...} = getFrameInfo fi | |
1150 | in | |
1151 | Alloc.forall | |
1152 | (alloc, fn z => | |
1153 | case z of | |
1154 | Operand.StackOffset (StackOffset.T {offset, ty}) => | |
1155 | Bytes.<= (Bytes.+ (offset, Type.bytes ty), size) | |
1156 | | _ => false) | |
1157 | end | |
1158 | in | |
1159 | case k of | |
1160 | Cont {args, frameInfo} => | |
1161 | if frame (frameInfo, true, false) | |
1162 | andalso slotsAreInFrame frameInfo | |
1163 | then SOME (Vector.fold | |
1164 | (args, alloc, fn (z, alloc) => | |
1165 | Alloc.defineLive (alloc, z))) | |
1166 | else NONE | |
1167 | | CReturn {dst, frameInfo, func, ...} => | |
1168 | let | |
1169 | val ok = | |
1170 | (case dst of | |
1171 | NONE => true | |
1172 | | SOME z => | |
1173 | Type.isSubtype (CFunction.return func, | |
1174 | Live.ty z)) | |
1175 | andalso | |
1176 | (if CFunction.mayGC func | |
1177 | then (case frameInfo of | |
1178 | NONE => false | |
1179 | | SOME fi => | |
1180 | (frame (fi, true, true) | |
1181 | andalso slotsAreInFrame fi)) | |
1182 | else if !Control.profile = Control.ProfileNone | |
1183 | then true | |
1184 | else (case frameInfo of | |
1185 | NONE => false | |
1186 | | SOME fi => frame (fi, false, true))) | |
1187 | in | |
1188 | if ok | |
1189 | then SOME (case dst of | |
1190 | NONE => alloc | |
1191 | | SOME z => Alloc.defineLive (alloc, z)) | |
1192 | else NONE | |
1193 | end | |
1194 | | Func => SOME alloc | |
1195 | | Handler {frameInfo, ...} => | |
1196 | if frame (frameInfo, false, false) | |
1197 | then SOME alloc | |
1198 | else NONE | |
1199 | | Jump => SOME alloc | |
1200 | end | |
1201 | fun checkStatement (s: Statement.t, alloc: Alloc.t) | |
1202 | : Alloc.t option = | |
1203 | let | |
1204 | datatype z = datatype Statement.t | |
1205 | in | |
1206 | case s of | |
1207 | Move {dst, src} => | |
1208 | let | |
1209 | val _ = checkOperand (src, alloc) | |
1210 | val alloc = Alloc.define (alloc, dst) | |
1211 | val _ = checkOperand (dst, alloc) | |
1212 | in | |
1213 | if Type.isSubtype (Operand.ty src, Operand.ty dst) | |
1214 | andalso Operand.isLocation dst | |
1215 | then SOME alloc | |
1216 | else NONE | |
1217 | end | |
1218 | | Noop => SOME alloc | |
1219 | | PrimApp {args, dst, prim, ...} => | |
1220 | let | |
1221 | val _ = checkOperands (args, alloc) | |
1222 | val alloc = | |
1223 | case dst of | |
1224 | NONE => SOME alloc | |
1225 | | SOME z => | |
1226 | let | |
1227 | val alloc = Alloc.define (alloc, z) | |
1228 | val _ = checkOperand (z, alloc) | |
1229 | in | |
1230 | SOME alloc | |
1231 | end | |
1232 | val ok = | |
1233 | Type.checkPrimApp | |
1234 | {args = Vector.map (args, Operand.ty), | |
1235 | prim = prim, | |
1236 | result = Option.map (dst, Operand.ty)} | |
1237 | in | |
1238 | if ok | |
1239 | then alloc | |
1240 | else NONE | |
1241 | end | |
1242 | | ProfileLabel l => | |
1243 | if profileLabelIsOk l | |
1244 | then SOME alloc | |
1245 | else NONE | |
1246 | end | |
1247 | fun liveIsOk (live: Live.t vector, | |
1248 | a: Alloc.t): bool = | |
1249 | Vector.forall (live, fn z => Alloc.doesDefine (a, z)) | |
1250 | fun liveSubset (live: Live.t vector, | |
1251 | live': Live.t vector): bool = | |
1252 | Vector.forall | |
1253 | (live, fn z => Vector.exists (live', fn z' => | |
1254 | Live.equals (z, z'))) | |
1255 | fun goto (Block.T {live, | |
1256 | raises = raises', | |
1257 | returns = returns', ...}, | |
1258 | raises: Live.t vector option, | |
1259 | returns: Live.t vector option, | |
1260 | alloc: Alloc.t): bool = | |
1261 | liveIsOk (live, alloc) | |
1262 | andalso | |
1263 | (case (raises, raises') of | |
1264 | (_, NONE) => true | |
1265 | | (SOME gs, SOME gs') => | |
1266 | Vector.equals (gs', gs, Live.isSubtype) | |
1267 | | _ => false) | |
1268 | andalso | |
1269 | (case (returns, returns') of | |
1270 | (_, NONE) => true | |
1271 | | (SOME os, SOME os') => | |
1272 | Vector.equals (os', os, Live.isSubtype) | |
1273 | | _ => false) | |
1274 | fun checkCont (cont: Label.t, size: Bytes.t, alloc: Alloc.t) = | |
1275 | let | |
1276 | val Block.T {kind, live, ...} = labelBlock cont | |
1277 | in | |
1278 | if Vector.forall (live, fn z => Alloc.doesDefine (alloc, z)) | |
1279 | then | |
1280 | (case kind of | |
1281 | Kind.Cont {args, frameInfo, ...} => | |
1282 | (if Bytes.equals (size, | |
1283 | #size (getFrameInfo frameInfo)) | |
1284 | then | |
1285 | SOME | |
1286 | (live, | |
1287 | SOME | |
1288 | (Vector.map | |
1289 | (args, fn z => | |
1290 | case z of | |
1291 | Live.StackOffset s => | |
1292 | Live.StackOffset | |
1293 | (StackOffset.shift (s, size)) | |
1294 | | _ => z))) | |
1295 | else NONE) | |
1296 | | _ => NONE) | |
1297 | else NONE | |
1298 | end | |
1299 | fun callIsOk {alloc: Alloc.t, | |
1300 | dst: Label.t, | |
1301 | live: Live.t vector, | |
1302 | raises: Live.t vector option, | |
1303 | return, | |
1304 | returns: Live.t vector option} = | |
1305 | let | |
1306 | val {raises, returns, size} = | |
1307 | case return of | |
1308 | NONE => | |
1309 | {raises = raises, | |
1310 | returns = returns, | |
1311 | size = Bytes.zero} | |
1312 | | SOME {handler, return, size} => | |
1313 | let | |
1314 | val (contLive, returns) = | |
1315 | Err.check' | |
1316 | ("cont", | |
1317 | fn () => checkCont (return, size, alloc), | |
1318 | fn () => Label.layout return) | |
1319 | fun checkHandler () = | |
1320 | case handler of | |
1321 | NONE => SOME raises | |
1322 | | SOME h => | |
1323 | let | |
1324 | val Block.T {kind, live, ...} = | |
1325 | labelBlock h | |
1326 | in | |
1327 | if liveSubset (live, contLive) | |
1328 | then | |
1329 | (case kind of | |
1330 | Kind.Handler {handles, ...} => | |
1331 | SOME (SOME handles) | |
1332 | | _ => NONE) | |
1333 | else NONE | |
1334 | end | |
1335 | val raises = | |
1336 | Err.check' | |
1337 | ("handler", checkHandler, | |
1338 | fn () => Option.layout Label.layout handler) | |
1339 | in | |
1340 | {raises = raises, | |
1341 | returns = returns, | |
1342 | size = size} | |
1343 | end | |
1344 | val b = labelBlock dst | |
1345 | val alloc = | |
1346 | Alloc.T | |
1347 | (Vector.fold | |
1348 | (live, [], fn (z, ac) => | |
1349 | case z of | |
1350 | Live.StackOffset (StackOffset.T {offset, ty}) => | |
1351 | if Bytes.< (offset, size) | |
1352 | then ac | |
1353 | else (Live.StackOffset | |
1354 | (StackOffset.T | |
1355 | {offset = Bytes.- (offset, size), | |
1356 | ty = ty})) :: ac | |
1357 | | _ => ac)) | |
1358 | in | |
1359 | goto (b, raises, returns, alloc) | |
1360 | end | |
1361 | fun transferOk | |
1362 | (t: Transfer.t, | |
1363 | raises: Live.t vector option, | |
1364 | returns: Live.t vector option, | |
1365 | alloc: Alloc.t): bool = | |
1366 | let | |
1367 | fun jump (l: Label.t, a: Alloc.t) = | |
1368 | let | |
1369 | val b as Block.T {kind, ...} = labelBlock l | |
1370 | in | |
1371 | (case kind of | |
1372 | Kind.Jump => true | |
1373 | | _ => false) | |
1374 | andalso goto (b, raises, returns, a) | |
1375 | end | |
1376 | datatype z = datatype Transfer.t | |
1377 | in | |
1378 | case t of | |
1379 | Arith {args, dst, overflow, prim, success, ...} => | |
1380 | let | |
1381 | val _ = checkOperands (args, alloc) | |
1382 | val alloc = Alloc.define (alloc, dst) | |
1383 | val _ = checkOperand (dst, alloc) | |
1384 | in | |
1385 | Prim.mayOverflow prim | |
1386 | andalso jump (overflow, alloc) | |
1387 | andalso jump (success, alloc) | |
1388 | andalso | |
1389 | Type.checkPrimApp | |
1390 | {args = Vector.map (args, Operand.ty), | |
1391 | prim = prim, | |
1392 | result = SOME (Operand.ty dst)} | |
1393 | end | |
1394 | | CCall {args, frameInfo = fi, func, return} => | |
1395 | let | |
1396 | val _ = checkOperands (args, alloc) | |
1397 | in | |
1398 | CFunction.isOk (func, {isUnit = Type.isUnit}) | |
1399 | andalso | |
1400 | Vector.equals (args, CFunction.args func, | |
1401 | fn (z, t) => | |
1402 | Type.isSubtype (Operand.ty z, t)) | |
1403 | andalso | |
1404 | case return of | |
1405 | NONE => true | |
1406 | | SOME l => | |
1407 | let | |
1408 | val Block.T {live, ...} = labelBlock l | |
1409 | in | |
1410 | liveIsOk (live, alloc) | |
1411 | andalso | |
1412 | case labelKind l of | |
1413 | Kind.CReturn | |
1414 | {frameInfo = fi', func = f, ...} => | |
1415 | CFunction.equals (func, f) | |
1416 | andalso (Option.equals | |
1417 | (fi, fi', FrameInfo.equals)) | |
1418 | | _ => false | |
1419 | end | |
1420 | end | |
1421 | | Call {label, live, return} => | |
1422 | Vector.forall | |
1423 | (live, fn z => Alloc.doesDefine (alloc, z)) | |
1424 | andalso | |
1425 | callIsOk {alloc = alloc, | |
1426 | dst = label, | |
1427 | live = live, | |
1428 | raises = raises, | |
1429 | return = return, | |
1430 | returns = returns} | |
1431 | | Goto l => jump (l, alloc) | |
1432 | | Raise => | |
1433 | (case raises of | |
1434 | NONE => false | |
1435 | | SOME zs => | |
1436 | Vector.forall | |
1437 | (zs, fn z => Alloc.doesDefine (alloc, z))) | |
1438 | | Return => | |
1439 | (case returns of | |
1440 | NONE => false | |
1441 | | SOME zs => | |
1442 | Vector.forall | |
1443 | (zs, fn z => Alloc.doesDefine (alloc, z))) | |
1444 | | Switch s => | |
1445 | Switch.isOk | |
1446 | (s, {checkUse = fn z => checkOperand (z, alloc), | |
1447 | labelIsOk = fn l => jump (l, alloc)}) | |
1448 | end | |
1449 | val transferOk = | |
1450 | Trace.trace | |
1451 | ("Machine.Program.typeCheck.transferOk", | |
1452 | fn (t, _, _, a) => | |
1453 | Layout.tuple [Transfer.layout t, Alloc.layout a], | |
1454 | Bool.layout) | |
1455 | transferOk | |
1456 | fun blockOk (Block.T {kind, live, raises, returns, statements, | |
1457 | transfer, ...}): bool = | |
1458 | let | |
1459 | val live = Vector.toList live | |
1460 | val _ = | |
1461 | Err.check | |
1462 | ("live", | |
1463 | fn () => | |
1464 | let | |
1465 | fun loop zs = | |
1466 | case zs of | |
1467 | [] => true | |
1468 | | z :: zs => | |
1469 | List.forall | |
1470 | (zs, fn z' => | |
1471 | not (Live.interfere (z, z'))) | |
1472 | in | |
1473 | loop live | |
1474 | end, | |
1475 | fn () => List.layout Live.layout live) | |
1476 | val alloc = Alloc.new live | |
1477 | val alloc = | |
1478 | Err.check' | |
1479 | ("kind", | |
1480 | fn () => checkKind (kind, alloc), | |
1481 | fn () => Kind.layout kind) | |
1482 | val alloc = | |
1483 | Vector.fold | |
1484 | (statements, alloc, fn (s, alloc) => | |
1485 | Err.check' | |
1486 | ("statement", | |
1487 | fn () => checkStatement (s, alloc), | |
1488 | fn () => Statement.layout s)) | |
1489 | val _ = | |
1490 | Err.check | |
1491 | ("transfer", | |
1492 | fn () => transferOk (transfer, raises, returns, alloc), | |
1493 | fn () => Transfer.layout transfer) | |
1494 | in | |
1495 | true | |
1496 | end | |
1497 | val _ = | |
1498 | List.foreach | |
1499 | (chunks, | |
1500 | fn Chunk.T {blocks, ...} => | |
1501 | let | |
1502 | in | |
1503 | Vector.foreach | |
1504 | (blocks, fn b => | |
1505 | check' (b, "block", blockOk, Block.layout)) | |
1506 | end) | |
1507 | val _ = clear program | |
1508 | in | |
1509 | () | |
1510 | end handle Err.E e => (Layout.outputl (Err.layout e, Out.error) | |
1511 | ; Error.bug "Machine.typeCheck") | |
1512 | ||
1513 | fun clearLabelNames (T {chunks, ...}): unit = | |
1514 | List.foreach | |
1515 | (chunks, fn Chunk.T {blocks, ...} => | |
1516 | Vector.foreach | |
1517 | (blocks, fn Block.T {label, ...} => | |
1518 | Label.clearPrintName label)) | |
1519 | end | |
1520 | ||
1521 | end |