Import Debian changes 20180207-1
[hcoop/debian/mlton.git] / mlton / backend / allocate-registers.fun
CommitLineData
7f918cf1
CE
1(* Copyright (C) 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
10functor AllocateRegisters (S: ALLOCATE_REGISTERS_STRUCTS): ALLOCATE_REGISTERS =
11struct
12
13open S
14
15structure R = Rssa
16
17local
18 open Rssa
19in
20 structure Func = Func
21 structure Function = Function
22 structure Kind = Kind
23 structure Label = Label
24 structure Type = Type
25 structure Var = Var
26end
27
28local
29 open Machine
30in
31 structure CType = CType
32 structure Operand = Operand
33 structure Register = Register
34 structure Runtime = Runtime
35 structure StackOffset = StackOffset
36end
37
38structure Live = Live (Rssa)
39
40structure Allocation:
41 sig
42 structure Stack:
43 sig
44 type t
45
46 val get: t * Type.t -> t * {offset: Bytes.t}
47 val layout: t -> Layout.t
48 val new: StackOffset.t list -> t
49 val size: t -> Bytes.t
50 end
51
52 type t
53
54 val getRegister: t * Type.t -> Register.t
55 val getStack: t * Type.t -> {offset: Bytes.t}
56 val layout: t -> Layout.t
57 val new: StackOffset.t list * Register.t list -> t
58 val stack: t -> Stack.t
59 val stackSize: t -> Bytes.t
60 end =
61 struct
62 structure Stack =
63 struct
64 (* Keep a list of allocated slots sorted in increasing order of offset.
65 *)
66 datatype t = T of {offset: Bytes.t, size: Bytes.t} list
67
68 fun layout (T alloc) =
69 List.layout (fn {offset, size} =>
70 Layout.record [("offset", Bytes.layout offset),
71 ("size", Bytes.layout size)])
72 alloc
73
74 fun size (T alloc) =
75 case alloc of
76 [] => Bytes.zero
77 | _ => let
78 val {offset, size} = List.last alloc
79 in
80 Bytes.+ (offset, size)
81 end
82
83 fun new (alloc): t =
84 let
85 val a =
86 Array.fromListMap (alloc, fn StackOffset.T {offset, ty} =>
87 {offset = offset,
88 size = Type.bytes ty})
89 val () =
90 QuickSort.sortArray
91 (a, fn (r, r') => Bytes.<= (#offset r, #offset r'))
92 fun loop (alloc, ac) =
93 case alloc of
94 [] => List.rev ac
95 | [a] => List.rev (a::ac)
96 | (a1 as {offset = offset1, size = size1})::(a2 as {offset = offset2, size = size2})::alloc =>
97 if Bytes.equals (Bytes.+ (offset1, size1), offset2)
98 then loop ({offset = offset1, size = Bytes.+ (size1, size2)}::alloc, ac)
99 else loop (a2::alloc, a1::ac)
100 in
101 T (loop (Array.toList a, []))
102 end
103
104 fun get (T alloc, ty) =
105 let
106 val slotSize = Type.bytes ty
107 fun loop (alloc, a as {offset, size}, ac) =
108 let
109 val prevEnd = Bytes.+ (offset, size)
110 val begin = Type.align (ty, prevEnd)
111 fun coalesce () =
112 if Bytes.equals (prevEnd, begin)
113 then ({offset = offset, size = Bytes.+ (size, slotSize)}, ac)
114 else ({offset = begin, size = slotSize}, a :: ac)
115 in
116 case alloc of
117 [] =>
118 let
119 val (a, ac) = coalesce ()
120 in
121 (T (rev (a :: ac)), {offset = begin})
122 end
123 | (a' as {offset, size}) :: alloc =>
124 if Bytes.> (Bytes.+ (begin, slotSize), offset)
125 then loop (alloc, a',
126 if Bytes.isZero offset andalso Bytes.isZero size
127 then ac
128 else a :: ac)
129 else let
130 val (a'' as {offset = o', size = s'}, ac) =
131 coalesce ()
132 val alloc =
133 List.appendRev
134 (ac,
135 if Bytes.equals (Bytes.+ (o', s'), offset)
136 then {offset = o', size = Bytes.+ (size, s')} :: alloc
137 else a'' :: a' :: alloc)
138 in
139 (T alloc, {offset = begin})
140 end
141 end
142 in
143 loop (alloc, {offset = Bytes.zero, size = Bytes.zero}, [])
144 end
145 val get =
146 Trace.trace2
147 ("AllocateRegisters.Allocation.Stack.get",
148 layout, Type.layout,
149 Layout.tuple2 (layout, fn {offset} =>
150 Layout.record [("offset", Bytes.layout offset)]))
151 get
152 end
153 structure Registers =
154 struct
155 (* A register allocation keeps track of the registers that have
156 * already been allocated, for each runtime type. The reason that
157 * we associate them with runtime types rather than Rssa types is
158 * that the register indices that the codegens use are based on
159 * runtime types.
160 *)
161 datatype t = T of CType.t -> {alloc: Register.t list,
162 next: int} ref
163
164 fun layout (T f) =
165 List.layout
166 (fn t =>
167 let
168 val {alloc, next} = ! (f t)
169 in
170 Layout.record [("ty", CType.layout t),
171 ("next", Int.layout next),
172 ("alloc", List.layout Register.layout alloc)]
173 end)
174 CType.all
175
176 fun compress {next, alloc} =
177 let
178 fun loop (next, alloc) =
179 let
180 fun done () = {alloc = alloc,
181 next = next}
182 in
183 case alloc of
184 [] => done ()
185 | r :: alloc =>
186 if next = Register.index r
187 then loop (next + 1, alloc)
188 else done ()
189 end
190 in
191 loop (next, alloc)
192 end
193
194 fun new (rs: Register.t list): t =
195 let
196 fun sameType (r, r') =
197 CType.equals
198 (Type.toCType (Register.ty r),
199 Type.toCType (Register.ty r'))
200 val rss = List.equivalence (rs, sameType)
201 in
202 T (CType.memo
203 (fn t =>
204 case List.peek (rss, fn rs =>
205 case rs of
206 [] => false
207 | r :: _ =>
208 CType.equals
209 (t, Type.toCType (Register.ty r))) of
210 NONE => ref {alloc = [], next = 0}
211 | SOME rs =>
212 ref
213 (compress
214 {next = 0,
215 alloc =
216 QuickSort.sortList
217 (rs, fn (r, r') =>
218 Register.index r <= Register.index r')})))
219 end
220
221 fun get (T f, ty: Type.t) =
222 let
223 val t = Type.toCType ty
224 val r = f t
225 val {alloc, next} = !r
226 val reg = Register.new (ty, SOME next)
227 val _ =
228 r := compress {alloc = alloc,
229 next = next + 1}
230 in
231 reg
232 end
233 end
234
235 datatype t = T of {registers: Registers.t,
236 stack: Stack.t ref}
237
238 local
239 fun make s (T x) = s x
240 in
241 val stack = ! o (make #stack)
242 val stackSize = Stack.size o stack
243 end
244
245 fun layout (T {registers, stack}) =
246 Layout.record
247 [("stack", Stack.layout (!stack)),
248 ("registers", Registers.layout registers)]
249
250 fun getStack (T {stack, ...}, ty) =
251 let
252 val (s, offset) = Stack.get (!stack, ty)
253 val _ = stack := s
254 in
255 offset
256 end
257
258 fun getRegister (T {registers, ...}, ty) =
259 Registers.get (registers, ty)
260
261 fun new (stack, registers) =
262 T {registers = Registers.new registers,
263 stack = ref (Stack.new stack)}
264 end
265
266structure Info =
267 struct
268 type t = {live: Operand.t vector,
269 liveNoFormals: Operand.t vector,
270 size: Bytes.t}
271
272 fun layout ({live, liveNoFormals, size, ...}: t) =
273 Layout.record
274 [("live", Vector.layout Operand.layout live),
275 ("liveNoFormals", Vector.layout Operand.layout liveNoFormals),
276 ("size", Bytes.layout size)]
277 end
278
279(* ------------------------------------------------- *)
280(* allocate *)
281(* ------------------------------------------------- *)
282
283fun allocate {formalsStackOffsets,
284 function = f: Rssa.Function.t,
285 varInfo: Var.t -> {operand: Machine.Operand.t option ref option,
286 ty: Type.t}} =
287 let
288 fun diagnostics f =
289 Control.diagnostics
290 (fn display =>
291 let
292 open Layout
293 fun diagVar (x: Var.t): unit =
294 display (seq
295 [Var.layout x, str " ",
296 Option.layout
297 (fn r => Option.layout Operand.layout (!r))
298 (#operand (varInfo x))])
299 fun diagStatement (s: R.Statement.t): unit =
300 R.Statement.foreachDef (s, diagVar o #1)
301 in
302 f (display, diagVar, diagStatement)
303 end)
304 val _ =
305 Control.diagnostic (fn () =>
306 let open Layout
307 in seq [str "Function allocs for ",
308 Func.layout (Function.name f)]
309 end)
310 val {labelLive, remLabelLive} =
311 Live.live (f, {shouldConsider = isSome o #operand o varInfo})
312 val {args, blocks, name, ...} = Function.dest f
313 (*
314 * Decide which variables will live in stack slots and which
315 * will live in registers.
316 * Initially,
317 * - all formals are put in stack slots
318 * - everything else is put in a register.
319 * Variables get moved to the stack if they are
320 * - live at the beginning of a Cont block; such variables are
321 * live while the frame is suspended during a non-tail call
322 * and must be stack allocated to be traced during a GC
323 * - live at the beginning of a CReturn block that mayGC; such
324 * variables are live while the frame is suspended during a
325 * C call and must be stack allocated to be traced during
326 * the potential GC
327 * Both of the above are indiced by Kind.frameStyle kind =
328 * Kind.OffsetsAndSize
329 *)
330 datatype place = Stack | Register
331 val {get = place: Var.t -> place ref, rem = removePlace, ...} =
332 Property.get (Var.plist, Property.initFun (fn _ => ref Register))
333 (* !hasHandler = true iff handlers are installed in this function. *)
334 val hasHandler: bool ref = ref false
335 fun forceStack (x: Var.t): unit = place x := Stack
336 val _ = Vector.foreach (args, forceStack o #1)
337 val _ =
338 Vector.foreach
339 (blocks,
340 fn R.Block.T {kind, label, statements, ...} =>
341 let
342 val {beginNoFormals, ...} = labelLive label
343 val _ =
344 case Kind.frameStyle kind of
345 Kind.None => ()
346 | Kind.OffsetsAndSize =>
347 Vector.foreach (beginNoFormals, forceStack)
348 | Kind.SizeOnly => ()
349 val _ =
350 if not (!hasHandler)
351 andalso (Vector.exists
352 (statements, fn s =>
353 let
354 datatype z = datatype R.Statement.t
355 in
356 case s of
357 SetHandler _ => true
358 | SetExnStackLocal => true
359 | SetExnStackSlot => true
360 | SetSlotExnStack => true
361 | _ => false
362 end))
363 then hasHandler := true
364 else ()
365 in
366 ()
367 end)
368 fun allocateVar (x: Var.t, a: Allocation.t): unit =
369 let
370 val {operand, ty} = varInfo x
371 in
372 if isSome operand
373 then let
374 val oper =
375 case ! (place x) of
376 Stack =>
377 let
378 val {offset} = Allocation.getStack (a, ty)
379 in
380 Operand.StackOffset
381 (StackOffset.T {offset = offset, ty = ty})
382 end
383 | Register =>
384 Operand.Register
385 (Allocation.getRegister (a, ty))
386 val () = removePlace x
387 val _ =
388 case operand of
389 NONE => ()
390 | SOME r => r := SOME oper
391 in
392 ()
393 end
394 else ()
395 end
396 val allocateVar =
397 Trace.trace2
398 ("AllocateRegisters.allocateVar", Var.layout, Allocation.layout, Unit.layout)
399 allocateVar
400 (* Set the stack slots for the formals.
401 * Also, create a stack allocation that includes all formals; if
402 * link and handler stack slots are required, then they will be
403 * allocated against this stack.
404 *)
405 val stack =
406 Allocation.Stack.new
407 (Vector.foldr2
408 (args, formalsStackOffsets args, [],
409 fn ((x, _), so, stack) =>
410 (valOf (#operand (varInfo x)) := SOME (Operand.StackOffset so)
411 ; so :: stack)))
412 (* Allocate stack slots for the link and handler, if necessary. *)
413 val handlerLinkOffset =
414 if !hasHandler
415 then
416 let
417 (* Choose fixed and permanently allocated stack
418 * slots that do not conflict with formals.
419 *)
420 val (stack, {offset = handler, ...}) =
421 Allocation.Stack.get (stack, Type.label (Label.newNoname ()))
422 val (_, {offset = link, ...}) =
423 Allocation.Stack.get (stack, Type.exnStack ())
424 in
425 SOME {handler = handler, link = link}
426 end
427 else NONE
428 fun getOperands (xs: Var.t vector): Operand.t vector =
429 Vector.map (xs, fn x => valOf (! (valOf (#operand (varInfo x)))))
430 val getOperands =
431 Trace.trace
432 ("AllocateRegisters.getOperands",
433 Vector.layout Var.layout, Vector.layout Operand.layout)
434 getOperands
435 val {get = labelInfo: R.Label.t -> Info.t, set = setLabelInfo, ...} =
436 Property.getSetOnce (R.Label.plist,
437 Property.initRaise ("labelInfo", R.Label.layout))
438 val setLabelInfo =
439 Trace.trace2
440 ("AllocateRegisters.setLabelInfo",
441 R.Label.layout, Info.layout, Unit.layout)
442 setLabelInfo
443 (* Do a DFS of the control-flow graph. *)
444 val () =
445 Function.dfs
446 (f, fn R.Block.T {args, label, kind, statements, transfer, ...} =>
447 let
448 val {begin, beginNoFormals, handler = handlerLive,
449 link = linkLive} = labelLive label
450 val () = remLabelLive label
451 fun addHS (ops: Operand.t vector): Operand.t vector =
452 case handlerLinkOffset of
453 NONE => ops
454 | SOME {handler, link} =>
455 let
456 val extra = []
457 val extra =
458 case handlerLive of
459 NONE => extra
460 | SOME h =>
461 Operand.stackOffset {offset = handler,
462 ty = Type.label h}
463 :: extra
464 val extra =
465 if linkLive
466 then
467 Operand.stackOffset {offset = link,
468 ty = Type.exnStack ()}
469 :: extra
470 else extra
471 in
472 Vector.concat [Vector.fromList extra, ops]
473 end
474 val liveNoFormals = getOperands beginNoFormals
475 val (stackInit, registersInit) =
476 Vector.fold
477 (liveNoFormals, ([],[]), fn (oper, (stack, registers)) =>
478 case oper of
479 Operand.StackOffset s => (s::stack, registers)
480 | Operand.Register r => (stack, r::registers)
481 | _ => (stack, registers))
482 val stackInit =
483 case handlerLinkOffset of
484 NONE => stackInit
485 | SOME {handler, link} =>
486 StackOffset.T {offset = handler,
487 ty = Type.label (Label.newNoname ())}
488 :: StackOffset.T {offset = link,
489 ty = Type.exnStack ()}
490 :: stackInit
491 val a = Allocation.new (stackInit, registersInit)
492 val size =
493 case kind of
494 Kind.Handler =>
495 (case handlerLinkOffset of
496 NONE => Error.bug "AllocateRegisters.allocate: Handler with no handler offset"
497 | SOME {handler, ...} =>
498 Bytes.+ (Runtime.labelSize (), handler))
499 | _ =>
500 let
501 val size =
502 Bytes.+
503 (Runtime.labelSize (),
504 Bytes.alignWord32 (Allocation.stackSize a))
505 in
506 case !Control.align of
507 Control.Align4 => size
508 | Control.Align8 => Bytes.alignWord64 size
509 end
510 val _ =
511 if Bytes.isWord32Aligned size
512 then ()
513 else Error.bug (concat ["AllocateRegisters.allocate: ",
514 "bad size ",
515 Bytes.toString size,
516 " in ", Label.toString label])
517 val _ = Vector.foreach (args, fn (x, _) => allocateVar (x, a))
518 (* Must compute live after allocateVar'ing the args, since that
519 * sets the operands for the args.
520 *)
521 val live = getOperands begin
522 fun one (var, _) = allocateVar (var, a)
523 val _ =
524 Vector.foreach (statements, fn statement =>
525 R.Statement.foreachDef (statement, one))
526 val _ = R.Transfer.foreachDef (transfer, one)
527 val _ =
528 setLabelInfo (label, {live = addHS live,
529 liveNoFormals = addHS liveNoFormals,
530 size = size})
531 in
532 fn () => ()
533 end)
534 val () =
535 diagnostics
536 (fn (display, diagVar, diagStatement) =>
537 let
538 open Layout
539 val _ =
540 display (seq [str "function ", Func.layout name,
541 str " handlerLinkOffset ",
542 Option.layout
543 (fn {handler, link} =>
544 record [("handler", Bytes.layout handler),
545 ("link", Bytes.layout link)])
546 handlerLinkOffset])
547 val _ = Vector.foreach (args, diagVar o #1)
548 val _ =
549 Vector.foreach
550 (blocks, fn R.Block.T {label, args, statements, ...} =>
551 let
552 val {live, ...} = labelInfo label
553 val () = display (R.Label.layout label)
554 val () =
555 display
556 (seq [str "live: ", Vector.layout Operand.layout live])
557 val () = Vector.foreach (args, diagVar o #1)
558 val () = Vector.foreach (statements, diagStatement)
559 in
560 ()
561 end)
562 in ()
563 end)
564 in
565 {handlerLinkOffset = handlerLinkOffset,
566 labelInfo = labelInfo}
567 end
568
569val allocate =
570 Trace.trace
571 ("AllocateRegisters.allocate",
572 fn {function, ...} => Func.layout (Function.name function),
573 Layout.ignore)
574 allocate
575
576end