Import Upstream version 20180207
[hcoop/debian/mlton.git] / mlton / codegen / x86-codegen / x86-generate-transfers.fun
CommitLineData
7f918cf1
CE
1(* Copyright (C) 2009 Matthew Fluet.
2 * Copyright (C) 1999-2008 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 x86GenerateTransfers(S: X86_GENERATE_TRANSFERS_STRUCTS): X86_GENERATE_TRANSFERS =
11struct
12
13 open S
14 open x86
15 open x86JumpInfo
16 open x86LoopInfo
17 open x86Liveness
18 open LiveInfo
19 open Liveness
20
21 local
22 open Runtime
23 in
24 structure CFunction = CFunction
25 end
26
27 val ones : int * WordSize.t -> WordX.t
28 = fn (i, ws) => (WordX.notb o WordX.lshift)
29 (WordX.allOnes ws,
30 WordX.fromIntInf (IntInf.fromInt i, ws))
31
32 val tracerTop = x86.tracerTop
33
34 structure x86LiveTransfers
35 = x86LiveTransfers(structure x86 = x86
36 structure x86Liveness = x86Liveness
37 structure x86JumpInfo = x86JumpInfo
38 structure x86LoopInfo = x86LoopInfo)
39
40 val pointerSize = x86MLton.pointerSize
41 val wordSize = x86MLton.wordSize
42
43 val normalRegs =
44 let
45 val transferRegs
46 =
47 (*
48 Register.eax::
49 Register.al::
50 *)
51 Register.ebx::
52 Register.bl::
53 Register.ecx::
54 Register.cl::
55 Register.edx::
56 Register.dl::
57 Register.edi::
58 Register.esi::
59 (*
60 Register.esp::
61 Register.ebp::
62 *)
63 nil
64 in
65 {frontierReg = Register.esp,
66 stackTopReg = Register.ebp,
67 transferRegs = fn Entry.Jump _ => transferRegs
68 | Entry.CReturn _ => Register.eax::Register.al::transferRegs
69 | _ => []}
70 end
71
72 val reserveEspRegs =
73 let
74 val transferRegs
75 =
76 (*
77 Register.eax::
78 Register.al::
79 *)
80 Register.ebx::
81 Register.bl::
82 Register.ecx::
83 Register.cl::
84 Register.edx::
85 Register.dl::
86 (*
87 Register.edi::
88 *)
89 Register.esi::
90 (*
91 Register.esp::
92 Register.ebp::
93 *)
94 nil
95 in
96 {frontierReg = Register.edi,
97 stackTopReg = Register.ebp,
98 transferRegs = fn Entry.Jump _ => transferRegs
99 | Entry.CReturn _ => Register.eax::Register.al::transferRegs
100 | _ => []}
101 end
102
103 val picUsesEbxRegs =
104 let
105 val transferRegs
106 =
107 (*
108 Register.eax::
109 Register.al::
110 *)
111 (*
112 Register.ebx::
113 Register.bl::
114 *)
115 Register.ecx::
116 Register.cl::
117 Register.edx::
118 Register.dl::
119 Register.edi::
120 Register.esi::
121 (*
122 Register.esp::
123 Register.ebp::
124 *)
125 nil
126 in
127 {frontierReg = Register.esp,
128 stackTopReg = Register.ebp,
129 transferRegs = fn Entry.Jump _ => transferRegs
130 | Entry.CReturn _ => Register.eax::Register.al::transferRegs
131 | _ => []}
132 end
133
134 val transferFltRegs : Entry.t -> Int.t = fn Entry.Jump _ => 6
135 | Entry.CReturn _ => 6
136 | _ => 0
137
138 val indexReg = x86.Register.eax
139
140 val stackTop = x86MLton.gcState_stackTopContents
141 val frontier = x86MLton.gcState_frontierContents
142
143 datatype gef = GEF of {generate : gef ->
144 {label : Label.t,
145 falling : bool,
146 unique : bool} ->
147 Assembly.t AppendList.t,
148 effect : gef ->
149 {label : Label.t,
150 transfer : Transfer.t} ->
151 Assembly.t AppendList.t,
152 fall : gef ->
153 {label : Label.t,
154 live : LiveSet.t} ->
155 Assembly.t AppendList.t}
156
157 fun generateTransfers {chunk as Chunk.T {data, blocks, ...},
158 optimize: int,
159 newProfileLabel: x86.ProfileLabel.t -> x86.ProfileLabel.t,
160 liveInfo : x86Liveness.LiveInfo.t,
161 jumpInfo : x86JumpInfo.t,
162 reserveEsp: bool,
163 picUsesEbx: bool}
164 = let
165 val {frontierReg, stackTopReg, transferRegs} =
166 if reserveEsp
167 then reserveEspRegs
168 else if picUsesEbx
169 then picUsesEbxRegs
170 else normalRegs
171 val allClasses = !x86MLton.Classes.allClasses
172 val livenessClasses = !x86MLton.Classes.livenessClasses
173 val livenessClasses = ClassSet.add(livenessClasses,
174 x86MLton.Classes.StaticNonTemp)
175 val nonlivenessClasses = ClassSet.-(allClasses, livenessClasses)
176 val holdClasses = !x86MLton.Classes.holdClasses
177 val farflushClasses = ClassSet.-(nonlivenessClasses, holdClasses)
178 val nearflushClasses = ClassSet.-(nonlivenessClasses, holdClasses)
179 val runtimeClasses = !x86MLton.Classes.runtimeClasses
180 val cstaticClasses = !x86MLton.Classes.cstaticClasses
181 val heapClasses = !x86MLton.Classes.heapClasses
182 val ccallflushClasses = ClassSet.+(cstaticClasses, heapClasses)
183
184 fun removeHoldMemLocs memlocs
185 = MemLocSet.subset
186 (memlocs,
187 fn m => not (ClassSet.contains(holdClasses, MemLoc.class m)))
188
189 val stackAssume = {register = stackTopReg,
190 memloc = stackTop (),
191 weight = 1024,
192 sync = false,
193 reserve = false}
194 val frontierAssume = {register = frontierReg,
195 memloc = frontier (),
196 weight = 2048,
197 sync = false,
198 reserve = false}
199 val cStackAssume = {register = Register.esp,
200 memloc = x86MLton.c_stackPContents,
201 weight = 2048, (* ??? *)
202 sync = false,
203 reserve = true}
204 val picUsesEbxAssume = {register = Register.ebx,
205 memloc = x86MLton.globalOffsetTableContents,
206 weight = 2048, (* ??? *)
207 sync = false,
208 reserve = true}
209
210 fun blockAssumes l =
211 let
212 val l = frontierAssume :: stackAssume :: l
213 val l = if reserveEsp then cStackAssume :: l else l
214 val l = if picUsesEbx then picUsesEbxAssume :: l else l
215 in
216 Assembly.directive_assume {assumes = l }
217 end
218
219 fun runtimeTransfer live setup trans
220 = AppendList.appends
221 [AppendList.single
222 (Assembly.directive_force
223 {commit_memlocs = removeHoldMemLocs live,
224 commit_classes = ClassSet.empty,
225 remove_memlocs = MemLocSet.empty,
226 remove_classes = ClassSet.empty,
227 dead_memlocs = MemLocSet.empty,
228 dead_classes = ClassSet.empty}),
229 setup,
230 AppendList.fromList
231 [(Assembly.directive_clearflt ()),
232 (Assembly.directive_force
233 {commit_memlocs = MemLocSet.empty,
234 commit_classes = farflushClasses,
235 remove_memlocs = MemLocSet.empty,
236 remove_classes = ClassSet.empty,
237 dead_memlocs = MemLocSet.empty,
238 dead_classes = ClassSet.empty})],
239 trans]
240
241 fun farEntry l = AppendList.cons (blockAssumes [], l)
242
243 fun farTransfer live setup trans
244 = AppendList.appends
245 [AppendList.single
246 (Assembly.directive_force
247 {commit_memlocs = removeHoldMemLocs live,
248 commit_classes = ClassSet.empty,
249 remove_memlocs = MemLocSet.empty,
250 remove_classes = ClassSet.empty,
251 dead_memlocs = MemLocSet.empty,
252 dead_classes = ClassSet.empty}),
253 setup,
254 AppendList.fromList
255 [(Assembly.directive_cache
256 {caches = [{register = stackTopReg,
257 memloc = stackTop (),
258 reserve = true},
259 {register = frontierReg,
260 memloc = frontier (),
261 reserve = true}]}),
262 (Assembly.directive_clearflt ()),
263 (Assembly.directive_force
264 {commit_memlocs = MemLocSet.empty,
265 commit_classes = farflushClasses,
266 remove_memlocs = MemLocSet.empty,
267 remove_classes = ClassSet.empty,
268 dead_memlocs = MemLocSet.empty,
269 dead_classes = ClassSet.empty})],
270 trans]
271
272 val profileStackTopCommit' =
273 x86.Assembly.directive_force
274 {commit_memlocs = MemLocSet.singleton (stackTop ()),
275 commit_classes = ClassSet.empty,
276 remove_memlocs = MemLocSet.empty,
277 remove_classes = ClassSet.empty,
278 dead_memlocs = MemLocSet.empty,
279 dead_classes = ClassSet.empty}
280 val profileStackTopCommit =
281 if !Control.profile <> Control.ProfileNone
282 then AppendList.single profileStackTopCommit'
283 else AppendList.empty
284
285 val _
286 = Assert.assert
287 ("x86GenerateTransfers.verifyLiveInfo",
288 fn () => x86Liveness.LiveInfo.verifyLiveInfo {chunk = chunk,
289 liveInfo = liveInfo})
290 val _
291 = Assert.assert
292 ("x86GenerateTransfers.verifyJumpInfo",
293 fn () => x86JumpInfo.verifyJumpInfo {chunk = chunk,
294 jumpInfo = jumpInfo})
295
296 val _
297 = Assert.assert
298 ("x86GenerateTransfers.verifyEntryTransfer",
299 fn () => x86EntryTransfer.verifyEntryTransfer {chunk = chunk})
300
301 local
302 val {get: Label.t -> {block:Block.t},
303 set,
304 destroy}
305 = Property.destGetSetOnce
306 (Label.plist, Property.initRaise ("gotoInfo", Label.layout))
307
308 val labels
309 = List.fold
310 (blocks, [],
311 fn (block as Block.T {entry, ...}, labels)
312 => let
313 val label = Entry.label entry
314 in
315 set(label, {block = block}) ;
316 label::labels
317 end)
318
319 fun loop labels
320 = let
321 val (labels, b)
322 = List.fold
323 (labels, ([], false),
324 fn (label, (labels, b))
325 => case x86JumpInfo.getNear (jumpInfo, label)
326 of x86JumpInfo.Count 0
327 => let
328 val {block = Block.T {transfer, ...}}
329 = get label
330 in
331 List.foreach
332 (Transfer.nearTargets transfer,
333 fn label
334 => x86JumpInfo.decNear (jumpInfo, label));
335 (labels, true)
336 end
337 | _ => (label::labels, b))
338 in
339 if b
340 then loop labels
341 else List.map (labels, #block o get)
342 end
343 val blocks = loop labels
344
345 val _ = destroy ()
346 in
347 val chunk = Chunk.T {data = data, blocks = blocks}
348 end
349
350 val loopInfo
351 = x86LoopInfo.createLoopInfo {chunk = chunk, farLoops = false}
352 val isLoopHeader
353 = fn label => isLoopHeader(loopInfo, label)
354 handle _ => false
355
356 val liveTransfers
357 = x86LiveTransfers.computeLiveTransfers
358 {chunk = chunk,
359 transferRegs = transferRegs,
360 transferFltRegs = transferFltRegs,
361 liveInfo = liveInfo,
362 jumpInfo = jumpInfo,
363 loopInfo = loopInfo}
364
365 val getLiveRegsTransfers
366 = #1 o x86LiveTransfers.getLiveTransfers
367 val getLiveFltRegsTransfers
368 = #2 o x86LiveTransfers.getLiveTransfers
369
370 val {get = getLayoutInfo : Label.t -> Block.t option,
371 set = setLayoutInfo,
372 destroy = destLayoutInfo}
373 = Property.destGetSet(Label.plist,
374 Property.initRaise ("layoutInfo", Label.layout))
375 val _
376 = List.foreach
377 (blocks,
378 fn block as Block.T {entry, ...}
379 => let
380 val label = Entry.label entry
381 in
382 setLayoutInfo(label, SOME block)
383 end)
384
385 val {get = getProfileLabel : Label.t -> ProfileLabel.t option,
386 set = setProfileLabel,
387 destroy = destProfileLabel}
388 = Property.destGetSetOnce
389 (Label.plist,
390 Property.initRaise ("profileLabel", Label.layout))
391 val _
392 = List.foreach
393 (blocks,
394 fn Block.T {entry, profileLabel, ...}
395 => let
396 val label = Entry.label entry
397 in
398 setProfileLabel(label, profileLabel)
399 end)
400
401 local
402 val stack = ref []
403 val queue = ref (Queue.empty ())
404 in
405 fun enque x = queue := Queue.enque(!queue, x)
406 fun push x = stack := x::(!stack)
407
408 fun deque () = (case (!stack)
409 of [] => (case Queue.deque(!queue)
410 of NONE => NONE
411 | SOME(queue', x) => (queue := queue';
412 SOME x))
413 | x::stack' => (stack := stack';
414 SOME x))
415 end
416
417 fun pushCompensationBlock {label, id}
418 = let
419 val label' = Label.new label
420 val live = getLive(liveInfo, label)
421 val profileLabel = getProfileLabel label
422 val profileLabel' = Option.map (profileLabel, newProfileLabel)
423 val block
424 = Block.T {entry = Entry.jump {label = label'},
425 profileLabel = profileLabel',
426 statements
427 = (Assembly.directive_restoreregalloc
428 {live = MemLocSet.add
429 (MemLocSet.add
430 (LiveSet.toMemLocSet live,
431 stackTop ()),
432 frontier ()),
433 id = id})::
434 nil,
435 transfer = Transfer.goto {target = label}}
436 in
437 setLive(liveInfo, label', live);
438 setProfileLabel(label', profileLabel');
439 incNear(jumpInfo, label');
440 Assert.assert("x86GenerateTransfers.pushCompensationBlock",
441 fn () => getNear(jumpInfo, label') = Count 1);
442 x86LiveTransfers.setLiveTransfersEmpty(liveTransfers, label');
443 setLayoutInfo(label', SOME block);
444 push label';
445 label'
446 end
447
448 val c_stackP = x86MLton.c_stackPContentsOperand
449
450 fun cacheEsp () =
451 if reserveEsp
452 then AppendList.empty
453 else
454 AppendList.single
455 ((* explicit cache in case there are no args *)
456 Assembly.directive_cache
457 {caches = [{register = Register.esp,
458 memloc = valOf (Operand.deMemloc c_stackP),
459 reserve = true}]})
460
461 fun unreserveEsp () =
462 if reserveEsp
463 then AppendList.empty
464 else AppendList.single (Assembly.directive_unreserve
465 {registers = [Register.esp]})
466
467 local
468 val set: (word * String.t * Label.t) HashSet.t =
469 HashSet.new {hash = #1}
470 in
471 fun makeDarwinSymbolStubLabel name =
472 let
473 val hash = String.hash name
474 in
475 (#3 o HashSet.lookupOrInsert)
476 (set, hash,
477 fn (hash', name', _) =>
478 hash = hash' andalso name = name',
479 fn () =>
480 (hash, name,
481 Label.newString (concat ["L_", name, "_stub"])))
482 end
483
484 fun makeDarwinSymbolStubs () =
485 HashSet.fold
486 (set, [], fn ((_, name, label), assembly) =>
487 (Assembly.pseudoop_symbol_stub ()) ::
488 (Assembly.label label) ::
489 (Assembly.pseudoop_indirect_symbol (Label.fromString name)) ::
490 (Assembly.instruction_hlt ()) ::
491 (Assembly.instruction_hlt ()) ::
492 (Assembly.instruction_hlt ()) ::
493 (Assembly.instruction_hlt ()) ::
494 (Assembly.instruction_hlt ()) ::
495 assembly)
496 end
497
498 datatype z = datatype Entry.t
499 datatype z = datatype Transfer.t
500 fun generateAll (gef as GEF {effect,...})
501 {label, falling, unique} :
502 Assembly.t AppendList.t
503 = (case getLayoutInfo label
504 of NONE => AppendList.empty
505 | SOME (Block.T {entry, profileLabel, statements, transfer})
506 => let
507 val _ = setLayoutInfo(label, NONE)
508(*
509 val isLoopHeader = fn _ => false
510*)
511 fun near label =
512 let
513 val align =
514 if isLoopHeader label handle _ => false
515 then
516 AppendList.single
517 (Assembly.pseudoop_p2align
518 (Immediate.int 4,
519 NONE,
520 SOME (Immediate.int 7)))
521 else if falling
522 then AppendList.empty
523 else
524 AppendList.single
525 (Assembly.pseudoop_p2align
526 (Immediate.int 4,
527 NONE,
528 NONE))
529 val assumes =
530 if falling andalso unique
531 then AppendList.empty
532 else
533 (* near entry & live transfer assumptions *)
534 AppendList.fromList
535 [(blockAssumes
536 (List.map
537 (getLiveRegsTransfers
538 (liveTransfers, label),
539 fn (memloc,register,sync)
540 => {register = register,
541 memloc = memloc,
542 sync = sync,
543 weight = 1024,
544 reserve = false}))),
545 (Assembly.directive_fltassume
546 {assumes
547 = (List.map
548 (getLiveFltRegsTransfers
549 (liveTransfers, label),
550 fn (memloc,sync)
551 => {memloc = memloc,
552 sync = sync,
553 weight = 1024}))})]
554 in
555 AppendList.appends
556 [align,
557 AppendList.single
558 (Assembly.label label),
559 AppendList.fromList
560 (ProfileLabel.toAssemblyOpt profileLabel),
561 assumes]
562 end
563 val pre
564 = case entry
565 of Jump {label}
566 => near label
567 | CReturn {dsts, frameInfo, func, label}
568 => let
569 fun getReturn () =
570 if Vector.isEmpty dsts
571 then AppendList.empty
572 else let
573 val srcs =
574 Vector.fromList
575 (List.map
576 (Operand.cReturnTemps
577 (CFunction.return func),
578 #dst))
579 in
580 (AppendList.fromList o Vector.fold2)
581 (dsts, srcs, [], fn ((dst,dstsize),src,stmts) =>
582 case Size.class dstsize of
583 Size.INT =>
584 (x86.Assembly.instruction_mov
585 {dst = dst,
586 src = Operand.memloc src,
587 size = dstsize})::stmts
588 | Size.FLT =>
589 (x86.Assembly.instruction_pfmov
590 {dst = dst,
591 src = Operand.memloc src,
592 size = dstsize})::stmts
593 | _ => Error.bug "x86GenerateTransfers.generateAll: CReturn")
594 end
595 in
596 case frameInfo of
597 SOME fi =>
598 let
599 val FrameInfo.T {size, frameLayoutsIndex}
600 = fi
601 val finish
602 = AppendList.appends
603 [let
604 val stackTop
605 = x86MLton.gcState_stackTopContentsOperand ()
606 val bytes
607 = x86.Operand.immediate_int (~ size)
608 in
609 AppendList.cons
610 ((* stackTop += bytes *)
611 x86.Assembly.instruction_binal
612 {oper = x86.Instruction.ADD,
613 dst = stackTop,
614 src = bytes,
615 size = pointerSize},
616 profileStackTopCommit)
617 end,
618 (* assignTo dst *)
619 getReturn ()]
620 in
621 AppendList.appends
622 [AppendList.fromList
623 [Assembly.pseudoop_p2align
624 (Immediate.int 4, NONE, NONE),
625 Assembly.pseudoop_long
626 [Immediate.int frameLayoutsIndex],
627 Assembly.label label],
628 AppendList.fromList
629 (ProfileLabel.toAssemblyOpt profileLabel),
630 if CFunction.maySwitchThreads func
631 then (* entry from far assumptions *)
632 farEntry finish
633 else (* near entry & live transfer assumptions *)
634 AppendList.append
635 (AppendList.fromList
636 [(blockAssumes
637 (List.map
638 (getLiveRegsTransfers
639 (liveTransfers, label),
640 fn (memloc,register,sync)
641 => {register = register,
642 memloc = memloc,
643 sync = sync,
644 weight = 1024,
645 reserve = false}))),
646 (Assembly.directive_fltassume
647 {assumes
648 = (List.map
649 (getLiveFltRegsTransfers
650 (liveTransfers, label),
651 fn (memloc,sync)
652 => {memloc = memloc,
653 sync = sync,
654 weight = 1024}))})],
655 finish)]
656 end
657 | NONE =>
658 AppendList.append (near label, getReturn ())
659 end
660 | Func {label,...}
661 => AppendList.appends
662 [AppendList.fromList
663 [Assembly.pseudoop_p2align
664 (Immediate.int 4, NONE, NONE),
665 Assembly.pseudoop_global label,
666 Assembly.pseudoop_hidden label,
667 Assembly.label label],
668 AppendList.fromList
669 (ProfileLabel.toAssemblyOpt profileLabel),
670 (* entry from far assumptions *)
671 (farEntry AppendList.empty)]
672 | Cont {label,
673 frameInfo = FrameInfo.T {size,
674 frameLayoutsIndex},
675 ...}
676 =>
677 AppendList.appends
678 [AppendList.fromList
679 [Assembly.pseudoop_p2align
680 (Immediate.int 4, NONE, NONE),
681 Assembly.pseudoop_long
682 [Immediate.int frameLayoutsIndex],
683 Assembly.label label],
684 AppendList.fromList
685 (ProfileLabel.toAssemblyOpt profileLabel),
686 (* entry from far assumptions *)
687 (farEntry
688 (let
689 val stackTop
690 = x86MLton.gcState_stackTopContentsOperand ()
691 val bytes
692 = x86.Operand.immediate_int (~ size)
693 in
694 AppendList.cons
695 ((* stackTop += bytes *)
696 x86.Assembly.instruction_binal
697 {oper = x86.Instruction.ADD,
698 dst = stackTop,
699 src = bytes,
700 size = pointerSize},
701 profileStackTopCommit)
702 end))]
703 | Handler {frameInfo = (FrameInfo.T
704 {frameLayoutsIndex, size}),
705 label,
706 ...}
707 => AppendList.appends
708 [AppendList.fromList
709 [Assembly.pseudoop_p2align
710 (Immediate.int 4, NONE, NONE),
711 Assembly.pseudoop_long
712 [Immediate.int frameLayoutsIndex],
713 Assembly.label label],
714 AppendList.fromList
715 (ProfileLabel.toAssemblyOpt profileLabel),
716 (* entry from far assumptions *)
717 (farEntry
718 (let
719 val stackTop
720 = x86MLton.gcState_stackTopContentsOperand ()
721 val bytes
722 = x86.Operand.immediate_int (~ size)
723 in
724 AppendList.cons
725 ((* stackTop += bytes *)
726 x86.Assembly.instruction_binal
727 {oper = x86.Instruction.ADD,
728 dst = stackTop,
729 src = bytes,
730 size = pointerSize},
731 profileStackTopCommit)
732 end))]
733 val pre
734 = AppendList.appends
735 [if !Control.Native.commented > 1
736 then AppendList.single
737 (Assembly.comment (Entry.toString entry))
738 else AppendList.empty,
739 if !Control.Native.commented > 2
740 then AppendList.single
741 (Assembly.comment
742 (LiveSet.fold
743 (getLive(liveInfo, label),
744 "",
745 fn (memloc, s)
746 => concat [s,
747 MemLoc.toString memloc,
748 " "])))
749 else AppendList.empty,
750 pre]
751
752 val (statements,_)
753 = List.foldr
754 (statements,
755 ([],
756 Liveness.liveIn
757 (livenessTransfer {transfer = transfer,
758 liveInfo = liveInfo})),
759 fn (assembly,(statements,live))
760 => let
761 val Liveness.T {liveIn,dead, ...}
762 = livenessAssembly {assembly = assembly,
763 live = live}
764 in
765 (if LiveSet.isEmpty dead
766 then assembly::statements
767 else assembly::
768 (Assembly.directive_force
769 {commit_memlocs = MemLocSet.empty,
770 commit_classes = ClassSet.empty,
771 remove_memlocs = MemLocSet.empty,
772 remove_classes = ClassSet.empty,
773 dead_memlocs = LiveSet.toMemLocSet dead,
774 dead_classes = ClassSet.empty})::
775 statements,
776 liveIn)
777 end)
778
779 val statements = AppendList.fromList statements
780
781 val transfer = effect gef {label = label,
782 transfer = transfer}
783 in
784 AppendList.appends
785 [pre,
786 statements,
787 transfer]
788 end)
789
790 and effectDefault (gef as GEF {fall,...})
791 {label, transfer} : Assembly.t AppendList.t
792 = AppendList.append
793 (if !Control.Native.commented > 1
794 then AppendList.single
795 (Assembly.comment
796 (Transfer.toString transfer))
797 else AppendList.empty,
798 case transfer
799 of Goto {target}
800 => fall gef
801 {label = target,
802 live = getLive(liveInfo, target)}
803 | Iff {condition, truee, falsee}
804 => let
805 val condition_neg
806 = Instruction.condition_negate condition
807
808 val truee_live
809 = getLive(liveInfo, truee)
810 val truee_live_length
811 = LiveSet.size truee_live
812
813 val falsee_live
814 = getLive(liveInfo, falsee)
815 val falsee_live_length
816 = LiveSet.size falsee_live
817
818 fun fall_truee ()
819 = let
820 val id = Directive.Id.new ()
821 val falsee'
822 = pushCompensationBlock {label = falsee,
823 id = id};
824 in
825 AppendList.append
826 (AppendList.fromList
827 [Assembly.directive_force
828 {commit_memlocs = MemLocSet.empty,
829 commit_classes = nearflushClasses,
830 remove_memlocs = MemLocSet.empty,
831 remove_classes = ClassSet.empty,
832 dead_memlocs = MemLocSet.empty,
833 dead_classes = ClassSet.empty},
834 Assembly.instruction_jcc
835 {condition = condition_neg,
836 target = Operand.label falsee'},
837 Assembly.directive_saveregalloc
838 {live = MemLocSet.add
839 (MemLocSet.add
840 (LiveSet.toMemLocSet falsee_live,
841 stackTop ()),
842 frontier ()),
843 id = id}],
844 (fall gef
845 {label = truee,
846 live = truee_live}))
847 end
848
849 fun fall_falsee ()
850 = let
851 val id = Directive.Id.new ()
852 val truee' = pushCompensationBlock {label = truee,
853 id = id};
854 in
855 AppendList.append
856 (AppendList.fromList
857 [Assembly.directive_force
858 {commit_memlocs = MemLocSet.empty,
859 commit_classes = nearflushClasses,
860 remove_memlocs = MemLocSet.empty,
861 remove_classes = ClassSet.empty,
862 dead_memlocs = MemLocSet.empty,
863 dead_classes = ClassSet.empty},
864 Assembly.instruction_jcc
865 {condition = condition,
866 target = Operand.label truee'},
867 Assembly.directive_saveregalloc
868 {live = MemLocSet.add
869 (MemLocSet.add
870 (LiveSet.toMemLocSet truee_live,
871 stackTop ()),
872 frontier ()),
873 id = id}],
874 (fall gef
875 {label = falsee,
876 live = falsee_live}))
877 end
878 in
879 case (getLayoutInfo truee,
880 getLayoutInfo falsee)
881 of (NONE, SOME _) => fall_falsee ()
882 | (SOME _, NONE) => fall_truee ()
883 | _
884 => let
885 fun default' ()
886 = if truee_live_length <= falsee_live_length
887 then fall_falsee ()
888 else fall_truee ()
889
890 fun default ()
891 = case (getNear(jumpInfo, truee),
892 getNear(jumpInfo, falsee))
893 of (Count 1, Count 1) => default' ()
894 | (Count 1, _) => fall_truee ()
895 | (_, Count 1) => fall_falsee ()
896 | _ => default' ()
897 in
898 case (getLoopDistance(loopInfo, label, truee),
899 getLoopDistance(loopInfo, label, falsee))
900 of (NONE, NONE) => default ()
901 | (SOME _, NONE) => fall_truee ()
902 | (NONE, SOME _) => fall_falsee ()
903 | (SOME dtruee, SOME dfalsee)
904 => (case Int.compare(dtruee, dfalsee)
905 of EQUAL => default ()
906 | LESS => fall_falsee ()
907 | GREATER => fall_truee ())
908 end
909 end
910 | Switch {test, cases, default}
911 => let
912 val Liveness.T {dead, ...}
913 = livenessTransfer {transfer = transfer,
914 liveInfo = liveInfo}
915
916 val size
917 = case Operand.size test
918 of SOME size => size
919 | NONE => Size.LONG
920
921 val default_live
922 = getLive(liveInfo, default)
923
924 val cases
925 = Transfer.Cases.mapToList
926 (cases,
927 fn (k, target)
928 => let
929 val target_live
930 = getLive(liveInfo, target)
931 val id = Directive.Id.new ()
932 val target' = pushCompensationBlock
933 {label = target,
934 id = id}
935 in
936 AppendList.fromList
937 [Assembly.instruction_cmp
938 {src1 = test,
939 src2 = Operand.immediate_word k,
940 size = size},
941 Assembly.instruction_jcc
942 {condition = Instruction.E,
943 target = Operand.label target'},
944 Assembly.directive_saveregalloc
945 {live = MemLocSet.add
946 (MemLocSet.add
947 (LiveSet.toMemLocSet target_live,
948 stackTop ()),
949 frontier ()),
950 id = id}]
951 end)
952 in
953 AppendList.appends
954 [AppendList.single
955 (Assembly.directive_force
956 {commit_memlocs = MemLocSet.empty,
957 commit_classes = nearflushClasses,
958 remove_memlocs = MemLocSet.empty,
959 remove_classes = ClassSet.empty,
960 dead_memlocs = MemLocSet.empty,
961 dead_classes = ClassSet.empty}),
962 AppendList.appends cases,
963 if LiveSet.isEmpty dead
964 then AppendList.empty
965 else AppendList.single
966 (Assembly.directive_force
967 {commit_memlocs = MemLocSet.empty,
968 commit_classes = ClassSet.empty,
969 remove_memlocs = MemLocSet.empty,
970 remove_classes = ClassSet.empty,
971 dead_memlocs = LiveSet.toMemLocSet dead,
972 dead_classes = ClassSet.empty}),
973 (fall gef
974 {label = default,
975 live = default_live})]
976 end
977 | Tail {target, live}
978 => (* flushing at far transfer *)
979 (farTransfer live
980 AppendList.empty
981 (AppendList.single
982 (Assembly.instruction_jmp
983 {target = Operand.label target,
984 absolute = false})))
985 | NonTail {target, live, return, handler, size}
986 => let
987 val _ = enque return
988 val _ = case handler
989 of SOME handler => enque handler
990 | NONE => ()
991
992 val stackTopTemp
993 = x86MLton.stackTopTempContentsOperand ()
994 val stackTopTempMinusWordDeref'
995 = x86MLton.stackTopTempMinusWordDeref ()
996 val stackTopTempMinusWordDeref
997 = x86MLton.stackTopTempMinusWordDerefOperand ()
998 val stackTop
999 = x86MLton.gcState_stackTopContentsOperand ()
1000 val stackTopMinusWordDeref'
1001 = x86MLton.gcState_stackTopMinusWordDeref ()
1002 val stackTopMinusWordDeref
1003 = x86MLton.gcState_stackTopMinusWordDerefOperand ()
1004 val bytes
1005 = x86.Operand.immediate_int size
1006
1007 val liveReturn = x86Liveness.LiveInfo.getLive(liveInfo, return)
1008 val liveHandler
1009 = case handler
1010 of SOME handler
1011 => x86Liveness.LiveInfo.getLive(liveInfo, handler)
1012 | _ => LiveSet.empty
1013 val live = MemLocSet.unions [live,
1014 LiveSet.toMemLocSet liveReturn,
1015 LiveSet.toMemLocSet liveHandler]
1016 in
1017 (* flushing at far transfer *)
1018 (farTransfer live
1019 (if !Control.profile <> Control.ProfileNone
1020 then (AppendList.fromList
1021 [(* stackTopTemp = stackTop + bytes *)
1022 x86.Assembly.instruction_mov
1023 {dst = stackTopTemp,
1024 src = stackTop,
1025 size = pointerSize},
1026 x86.Assembly.instruction_binal
1027 {oper = x86.Instruction.ADD,
1028 dst = stackTopTemp,
1029 src = bytes,
1030 size = pointerSize},
1031 (* *(stackTopTemp - WORD_SIZE) = return *)
1032 x86.Assembly.instruction_mov
1033 {dst = stackTopTempMinusWordDeref,
1034 src = Operand.immediate_label return,
1035 size = pointerSize},
1036 x86.Assembly.directive_force
1037 {commit_memlocs = MemLocSet.singleton stackTopTempMinusWordDeref',
1038 commit_classes = ClassSet.empty,
1039 remove_memlocs = MemLocSet.empty,
1040 remove_classes = ClassSet.empty,
1041 dead_memlocs = MemLocSet.empty,
1042 dead_classes = ClassSet.empty},
1043 (* stackTop = stackTopTemp *)
1044 x86.Assembly.instruction_mov
1045 {dst = stackTop,
1046 src = stackTopTemp,
1047 size = pointerSize},
1048 profileStackTopCommit'])
1049 else (AppendList.fromList
1050 [(* stackTop += bytes *)
1051 x86.Assembly.instruction_binal
1052 {oper = x86.Instruction.ADD,
1053 dst = stackTop,
1054 src = bytes,
1055 size = pointerSize},
1056 (* *(stackTop - WORD_SIZE) = return *)
1057 x86.Assembly.instruction_mov
1058 {dst = stackTopMinusWordDeref,
1059 src = Operand.immediate_label return,
1060 size = pointerSize},
1061 x86.Assembly.directive_force
1062 {commit_memlocs = MemLocSet.singleton stackTopMinusWordDeref',
1063 commit_classes = ClassSet.empty,
1064 remove_memlocs = MemLocSet.empty,
1065 remove_classes = ClassSet.empty,
1066 dead_memlocs = MemLocSet.empty,
1067 dead_classes = ClassSet.empty}]))
1068 (AppendList.single
1069 (Assembly.instruction_jmp
1070 {target = Operand.label target,
1071 absolute = false})))
1072 end
1073 | Return {live}
1074 => let
1075 val stackTopMinusWordDeref
1076 = x86MLton.gcState_stackTopMinusWordDerefOperand ()
1077 in
1078 (* flushing at far transfer *)
1079 (farTransfer live
1080 AppendList.empty
1081 (AppendList.single
1082 (* jmp *(stackTop - WORD_SIZE) *)
1083 (x86.Assembly.instruction_jmp
1084 {target = stackTopMinusWordDeref,
1085 absolute = true})))
1086 end
1087 | Raise {live}
1088 => let
1089 val exnStack
1090 = x86MLton.gcState_exnStackContentsOperand ()
1091 val stackTopTemp
1092 = x86MLton.stackTopTempContentsOperand ()
1093 val stackTop
1094 = x86MLton.gcState_stackTopContentsOperand ()
1095 val stackBottom
1096 = x86MLton.gcState_stackBottomContentsOperand ()
1097 in
1098 (* flushing at far transfer *)
1099 (farTransfer live
1100 (if !Control.profile <> Control.ProfileNone
1101 then (AppendList.fromList
1102 [(* stackTopTemp = stackBottom + exnStack *)
1103 x86.Assembly.instruction_mov
1104 {dst = stackTopTemp,
1105 src = stackBottom,
1106 size = pointerSize},
1107 x86.Assembly.instruction_binal
1108 {oper = x86.Instruction.ADD,
1109 dst = stackTopTemp,
1110 src = exnStack,
1111 size = pointerSize},
1112 (* stackTop = stackTopTemp *)
1113 x86.Assembly.instruction_mov
1114 {dst = stackTop,
1115 src = stackTopTemp,
1116 size = pointerSize},
1117 profileStackTopCommit'])
1118 else (AppendList.fromList
1119 [(* stackTop = stackBottom + exnStack *)
1120 x86.Assembly.instruction_mov
1121 {dst = stackTop,
1122 src = stackBottom,
1123 size = pointerSize},
1124 x86.Assembly.instruction_binal
1125 {oper = x86.Instruction.ADD,
1126 dst = stackTop,
1127 src = exnStack,
1128 size = pointerSize}]))
1129 (AppendList.single
1130 (* jmp *(stackTop - WORD_SIZE) *)
1131 (x86.Assembly.instruction_jmp
1132 {target = x86MLton.gcState_stackTopMinusWordDerefOperand (),
1133 absolute = true})))
1134 end
1135 | CCall {args, frameInfo, func, return}
1136 => let
1137 datatype z = datatype CFunction.Convention.t
1138 datatype z = datatype CFunction.SymbolScope.t
1139 datatype z = datatype CFunction.Target.t
1140 val CFunction.T {convention,
1141 return = returnTy,
1142 symbolScope,
1143 target, ...} = func
1144 val stackTopMinusWordDeref
1145 = x86MLton.gcState_stackTopMinusWordDerefOperand ()
1146 val Liveness.T {dead, ...}
1147 = livenessTransfer {transfer = transfer,
1148 liveInfo = liveInfo}
1149 val c_stackP = x86MLton.c_stackPContentsOperand
1150 val c_stackPDerefFloat = x86MLton.c_stackPDerefFloatOperand
1151 val c_stackPDerefDouble = x86MLton.c_stackPDerefDoubleOperand
1152 val applyFFTempFun = x86MLton.applyFFTempFunContentsOperand
1153 val applyFFTempArg = x86MLton.applyFFTempArgContentsOperand
1154 val (fptrArg, args) =
1155 case target of
1156 Direct _ => (AppendList.empty, args)
1157 | Indirect =>
1158 let
1159 val (fptrArg, args) =
1160 case args of
1161 fptrArg::args => (fptrArg, args)
1162 | _ => Error.bug "x86GenerateTransfers.generateAll: CCall"
1163 in
1164 (AppendList.single
1165 (Assembly.instruction_mov
1166 {src = #1 fptrArg,
1167 dst = applyFFTempFun,
1168 size = #2 fptrArg}),
1169 args)
1170 end
1171 val (pushArgs, size_args)
1172 = List.fold
1173 (args, (AppendList.empty, 0),
1174 fn ((arg, size), (assembly_args, size_args)) =>
1175 let
1176 val (assembly_arg, size_arg) =
1177 if Size.eq (size, Size.DBLE)
1178 then (AppendList.fromList
1179 [Assembly.instruction_binal
1180 {oper = Instruction.SUB,
1181 dst = c_stackP,
1182 src = Operand.immediate_int 8,
1183 size = pointerSize},
1184 Assembly.instruction_pfmov
1185 {src = arg,
1186 dst = c_stackPDerefDouble,
1187 size = size}],
1188 Size.toBytes size)
1189 else if Size.eq (size, Size.SNGL)
1190 then (AppendList.fromList
1191 [Assembly.instruction_binal
1192 {oper = Instruction.SUB,
1193 dst = c_stackP,
1194 src = Operand.immediate_int 4,
1195 size = pointerSize},
1196 Assembly.instruction_pfmov
1197 {src = arg,
1198 dst = c_stackPDerefFloat,
1199 size = size}],
1200 Size.toBytes size)
1201 else if Size.eq (size, Size.BYTE)
1202 orelse Size.eq (size, Size.WORD)
1203 then (AppendList.fromList
1204 [Assembly.instruction_movx
1205 {oper = Instruction.MOVZX,
1206 dst = applyFFTempArg,
1207 src = arg,
1208 dstsize = wordSize,
1209 srcsize = size},
1210 Assembly.instruction_ppush
1211 {src = applyFFTempArg,
1212 base = c_stackP,
1213 size = wordSize}],
1214 Size.toBytes wordSize)
1215 else (AppendList.single
1216 (Assembly.instruction_ppush
1217 {src = arg,
1218 base = c_stackP,
1219 size = size}),
1220 Size.toBytes size)
1221 in
1222 (AppendList.append (assembly_arg, assembly_args),
1223 size_arg + size_args)
1224 end)
1225 val (pushArgs, aligned_size_args) =
1226 let
1227 val space = 16 - (size_args mod 16)
1228 in
1229 if space = 16
1230 then (pushArgs, size_args)
1231 else (AppendList.append
1232 (AppendList.single
1233 (Assembly.instruction_binal
1234 {oper = Instruction.SUB,
1235 dst = c_stackP,
1236 src = Operand.immediate_int space,
1237 size = pointerSize}),
1238 pushArgs),
1239 size_args + space)
1240 end
1241 val flush =
1242 case frameInfo of
1243 SOME (FrameInfo.T {size, ...}) =>
1244 (* Entering runtime *)
1245 let
1246 val return = valOf return
1247 val _ = enque return
1248
1249 val stackTopTemp
1250 = x86MLton.stackTopTempContentsOperand ()
1251 val stackTopTempMinusWordDeref'
1252 = x86MLton.stackTopTempMinusWordDeref ()
1253 val stackTopTempMinusWordDeref
1254 = x86MLton.stackTopTempMinusWordDerefOperand ()
1255 val stackTop
1256 = x86MLton.gcState_stackTopContentsOperand ()
1257 val stackTopMinusWordDeref'
1258 = x86MLton.gcState_stackTopMinusWordDeref ()
1259 val stackTopMinusWordDeref
1260 = x86MLton.gcState_stackTopMinusWordDerefOperand ()
1261 val bytes = x86.Operand.immediate_int size
1262
1263 val live =
1264 x86Liveness.LiveInfo.getLive(liveInfo, return)
1265 val {defs, ...} = Transfer.uses_defs_kills transfer
1266 val live =
1267 List.fold
1268 (defs,
1269 live,
1270 fn (oper,live) =>
1271 case Operand.deMemloc oper of
1272 SOME memloc => LiveSet.remove (live, memloc)
1273 | NONE => live)
1274 in
1275 (runtimeTransfer (LiveSet.toMemLocSet live)
1276 (if !Control.profile <> Control.ProfileNone
1277 then (AppendList.fromList
1278 [(* stackTopTemp = stackTop + bytes *)
1279 x86.Assembly.instruction_mov
1280 {dst = stackTopTemp,
1281 src = stackTop,
1282 size = pointerSize},
1283 x86.Assembly.instruction_binal
1284 {oper = x86.Instruction.ADD,
1285 dst = stackTopTemp,
1286 src = bytes,
1287 size = pointerSize},
1288 (* *(stackTopTemp - WORD_SIZE) = return *)
1289 x86.Assembly.instruction_mov
1290 {dst = stackTopTempMinusWordDeref,
1291 src = Operand.immediate_label return,
1292 size = pointerSize},
1293 x86.Assembly.directive_force
1294 {commit_memlocs = MemLocSet.singleton stackTopTempMinusWordDeref',
1295 commit_classes = ClassSet.empty,
1296 remove_memlocs = MemLocSet.empty,
1297 remove_classes = ClassSet.empty,
1298 dead_memlocs = MemLocSet.empty,
1299 dead_classes = ClassSet.empty},
1300 (* stackTop = stackTopTemp *)
1301 x86.Assembly.instruction_mov
1302 {dst = stackTop,
1303 src = stackTopTemp,
1304 size = pointerSize},
1305 profileStackTopCommit'])
1306 else (AppendList.fromList
1307 [(* stackTop += bytes *)
1308 x86.Assembly.instruction_binal
1309 {oper = x86.Instruction.ADD,
1310 dst = stackTop,
1311 src = bytes,
1312 size = pointerSize},
1313 (* *(stackTop - WORD_SIZE) = return *)
1314 x86.Assembly.instruction_mov
1315 {dst = stackTopMinusWordDeref,
1316 src = Operand.immediate_label return,
1317 size = pointerSize},
1318 x86.Assembly.directive_force
1319 {commit_memlocs = MemLocSet.singleton stackTopMinusWordDeref',
1320 commit_classes = ClassSet.empty,
1321 remove_memlocs = MemLocSet.empty,
1322 remove_classes = ClassSet.empty,
1323 dead_memlocs = MemLocSet.empty,
1324 dead_classes = ClassSet.empty}]))
1325 (AppendList.single
1326 (Assembly.directive_force
1327 {commit_memlocs = LiveSet.toMemLocSet live,
1328 commit_classes = runtimeClasses,
1329 remove_memlocs = MemLocSet.empty,
1330 remove_classes = ClassSet.empty,
1331 dead_memlocs = MemLocSet.empty,
1332 dead_classes = ClassSet.empty})))
1333 end
1334 | NONE =>
1335 AppendList.single
1336 (Assembly.directive_force
1337 {commit_memlocs = let
1338 val s = MemLocSet.empty
1339 val s = if CFunction.modifiesFrontier func
1340 then MemLocSet.add
1341 (s, frontier ())
1342 else s
1343 val s = if CFunction.readsStackTop func
1344 then MemLocSet.add
1345 (s, stackTop ())
1346 else s
1347 in
1348 s
1349 end,
1350 commit_classes = ccallflushClasses,
1351 remove_memlocs = MemLocSet.empty,
1352 remove_classes = ClassSet.empty,
1353 dead_memlocs = LiveSet.toMemLocSet dead,
1354 dead_classes = ClassSet.empty})
1355 val call =
1356 case target of
1357 Direct name =>
1358 let
1359 datatype z = datatype MLton.Platform.OS.t
1360 datatype z = datatype Control.Format.t
1361
1362 val name =
1363 case convention of
1364 Cdecl => name
1365 | Stdcall => concat [name, "@", Int.toString size_args]
1366
1367 val label = fn () => Label.fromString name
1368
1369 (* how to access imported functions: *)
1370 (* Windows rewrites the symbol __imp__name *)
1371 val coff = fn () => Label.fromString ("_imp__" ^ name)
1372 val macho = fn () => makeDarwinSymbolStubLabel name
1373 val elf = fn () => Label.fromString (name ^ "@PLT")
1374
1375 val importLabel = fn () =>
1376 case !Control.Target.os of
1377 Cygwin => coff ()
1378 | Darwin => macho ()
1379 | MinGW => coff ()
1380 | _ => elf ()
1381
1382 val direct = fn () =>
1383 AppendList.fromList
1384 [Assembly.directive_ccall (),
1385 Assembly.instruction_call
1386 {target = Operand.label (label ()),
1387 absolute = false}]
1388
1389 val plt = fn () =>
1390 AppendList.fromList
1391 [Assembly.directive_ccall (),
1392 Assembly.instruction_call
1393 {target = Operand.label (importLabel ()),
1394 absolute = false}]
1395
1396 val indirect = fn () =>
1397 AppendList.fromList
1398 [Assembly.directive_ccall (),
1399 Assembly.instruction_call
1400 {target = Operand.memloc_label (importLabel ()),
1401 absolute = true}]
1402 in
1403 case (symbolScope,
1404 !Control.Target.os,
1405 !Control.positionIndependent) of
1406 (* Private functions can be easily reached
1407 * with a direct (eip-relative) call.
1408 *)
1409 (Private, _, _) => direct ()
1410 (* Call at the point of definition. *)
1411 | (Public, MinGW, _) => direct ()
1412 | (Public, Cygwin, _) => direct ()
1413 | (Public, Darwin, _) => direct ()
1414 (* ELF requires PLT even for public fns. *)
1415 | (Public, _, true) => plt ()
1416 | (Public, _, false) => direct ()
1417 (* Windows always does indirect calls to
1418 * imported functions. The importLabel has
1419 * the function address written to it.
1420 *)
1421 | (External, MinGW, _) => indirect ()
1422 | (External, Cygwin, _) => indirect ()
1423 (* Darwin needs to generate special stubs
1424 * that are filled in by the dynamic linker.
1425 * This is needed even for non-PIC.
1426 *)
1427 | (External, Darwin, _) => plt ()
1428 (* ELF systems create procedure lookup
1429 * tables (PLT) which proxy the call to
1430 * libraries. The PLT does not contain an
1431 * address, but instead a stub function.
1432 *)
1433 | (External, _, true) => plt ()
1434 | (External, _, false) => direct ()
1435 end
1436 | Indirect =>
1437 AppendList.fromList
1438 [Assembly.directive_ccall (),
1439 Assembly.instruction_call
1440 {target = applyFFTempFun,
1441 absolute = true}]
1442 val kill
1443 = if isSome frameInfo
1444 then AppendList.single
1445 (Assembly.directive_force
1446 {commit_memlocs = MemLocSet.empty,
1447 commit_classes = ClassSet.empty,
1448 remove_memlocs = MemLocSet.empty,
1449 remove_classes = ClassSet.empty,
1450 dead_memlocs = MemLocSet.empty,
1451 dead_classes = runtimeClasses})
1452 else AppendList.single
1453 (Assembly.directive_force
1454 {commit_memlocs = MemLocSet.empty,
1455 commit_classes = ClassSet.empty,
1456 remove_memlocs = MemLocSet.empty,
1457 remove_classes = ClassSet.empty,
1458 dead_memlocs = let
1459 val s = MemLocSet.empty
1460 val s = if CFunction.modifiesFrontier func
1461 then MemLocSet.add
1462 (s, frontier ())
1463 else s
1464 val s = if CFunction.writesStackTop func
1465 then MemLocSet.add
1466 (s, stackTop ())
1467 else s
1468 in
1469 s
1470 end,
1471 dead_classes = ccallflushClasses})
1472 val getResult =
1473 AppendList.single
1474 (Assembly.directive_return
1475 {returns = Operand.cReturnTemps returnTy})
1476 val fixCStack =
1477 if aligned_size_args > 0
1478 andalso convention = CFunction.Convention.Cdecl
1479 then (AppendList.single
1480 (Assembly.instruction_binal
1481 {oper = Instruction.ADD,
1482 dst = c_stackP,
1483 src = Operand.immediate_int aligned_size_args,
1484 size = pointerSize}))
1485 else AppendList.empty
1486 val continue
1487 = if CFunction.maySwitchThreads func
1488 then (* Returning from runtime *)
1489 (farTransfer MemLocSet.empty
1490 AppendList.empty
1491 (AppendList.single
1492 (* jmp *(stackTop - WORD_SIZE) *)
1493 (x86.Assembly.instruction_jmp
1494 {target = stackTopMinusWordDeref,
1495 absolute = true})))
1496 else case return
1497 of NONE => AppendList.empty
1498 | SOME l => (if isSome frameInfo
1499 then (* Don't need to trampoline,
1500 * since didn't switch threads,
1501 * but can't fall because
1502 * frame layout data is prefixed
1503 * to l's code; use fallNone
1504 * to force a jmp with near
1505 * jump assumptions.
1506 *)
1507 fallNone
1508 else fall)
1509 gef
1510 {label = l,
1511 live = getLive (liveInfo, l)}
1512 in
1513 AppendList.appends
1514 [cacheEsp (),
1515 fptrArg,
1516 pushArgs,
1517 flush,
1518 call,
1519 kill,
1520 getResult,
1521 fixCStack,
1522 unreserveEsp (),
1523 continue]
1524 end)
1525
1526 and effectJumpTable (gef as GEF {...})
1527 {label, transfer} : Assembly.t AppendList.t
1528 = case transfer
1529 of Switch {test, cases, default}
1530 => let
1531 val ws =
1532 case Operand.size test of
1533 SOME Size.BYTE => WordSize.word8
1534 | SOME Size.WORD => WordSize.word16
1535 | SOME Size.LONG => WordSize.word32
1536 | _ => Error.bug "x86GenerateTransfers.effectJumpTable: Switch"
1537
1538 val zero = WordX.zero ws
1539 val one = WordX.one ws
1540 val two = WordX.add (one, one)
1541 fun even w = WordX.isZero (WordX.mod (w, two, {signed = false}))
1542 fun incFn w = WordX.add (w, one)
1543 fun decFn w = WordX.sub (w, one)
1544 fun halfFn w = WordX.div (w, two, {signed = false})
1545 fun ltFn (w1, w2) = WordX.lt (w1, w2, {signed = false})
1546 val min = WordX.min (ws, {signed = false})
1547 fun minFn (w1, w2) = if WordX.lt (w1, w2, {signed = false})
1548 then w1
1549 else w2
1550 val max = WordX.max (ws, {signed = false})
1551 fun maxFn (w1, w2) = if WordX.gt (w1, w2, {signed = false})
1552 then w1
1553 else w2
1554 fun range (w1, w2) = WordX.sub (w2, w1)
1555
1556 val Liveness.T {dead, ...}
1557 = livenessTransfer {transfer = transfer,
1558 liveInfo = liveInfo}
1559
1560 fun reduce(cases)
1561 = let
1562 fun reduce' cases
1563 = let
1564 val (minK,maxK,length,
1565 allEven,allOdd)
1566 = List.fold
1567 (cases,
1568 (max, min, 0,
1569 true, true),
1570 fn ((k,_),
1571 (minK,maxK,length,
1572 allEven,allOdd))
1573 => let
1574 val isEven = even k
1575 in
1576 (minFn(k,minK),
1577 maxFn(k,maxK),
1578 length + 1,
1579 allEven andalso isEven,
1580 allOdd andalso not isEven)
1581 end)
1582 in
1583 if length > 1 andalso
1584 (allEven orelse allOdd)
1585 then let
1586 val f = if allOdd
1587 then halfFn o decFn
1588 else halfFn
1589 val cases'
1590 = List.map
1591 (cases,
1592 fn (k,target)
1593 => (f k, target))
1594
1595 val (cases'',
1596 minK'', maxK'', length'',
1597 shift'', mask'')
1598 = reduce' cases'
1599
1600 val shift' = 1 + shift''
1601 val mask'
1602 = WordX.orb
1603 (WordX.lshift(mask'', WordX.one WordSize.word32),
1604 if allOdd
1605 then WordX.one WordSize.word32
1606 else WordX.zero WordSize.word32)
1607 in
1608 (cases'',
1609 minK'', maxK'', length'',
1610 shift', mask')
1611 end
1612 else (cases,
1613 minK, maxK, length,
1614 0, WordX.zero WordSize.word32)
1615 end
1616 in
1617 reduce' cases
1618 end
1619
1620 fun doitTable(cases,
1621 minK, _, rangeK, shift, mask)
1622 = let
1623 val jump_table_label
1624 = Label.newString "jumpTable"
1625
1626 val idT = Directive.Id.new ()
1627 val defaultT =
1628 Promise.delay
1629 (fn () =>
1630 let
1631 val _ = incNear(jumpInfo, default)
1632 in
1633 pushCompensationBlock
1634 {label = default,
1635 id = idT}
1636 end)
1637
1638 val rec filler
1639 = fn ([],_) => []
1640 | (cases as (i,target)::cases',j)
1641 => if WordX.equals (i, j)
1642 then let
1643 val target'
1644 = pushCompensationBlock
1645 {label = target,
1646 id = idT}
1647 in
1648 (Immediate.label target')::
1649 (filler(cases', incFn j))
1650 end
1651 else (Immediate.label
1652 (Promise.force defaultT))::
1653 (filler(cases, incFn j))
1654
1655 val jump_table = filler (cases, minK)
1656
1657 val idD = Directive.Id.new ()
1658 val defaultD = pushCompensationBlock
1659 {label = default,
1660 id = idD}
1661
1662 val default_live = getLive(liveInfo, default)
1663 val live
1664 = List.fold
1665 (cases,
1666 default_live,
1667 fn ((_,target), live)
1668 => LiveSet.+(live, getLive(liveInfo, target)))
1669
1670 val indexTemp
1671 = MemLoc.imm
1672 {base = Immediate.label (Label.fromString "indexTemp"),
1673 index = Immediate.zero,
1674 scale = Scale.Four,
1675 size = Size.LONG,
1676 class = MemLoc.Class.Temp}
1677 val checkTemp
1678 = MemLoc.imm
1679 {base = Immediate.label (Label.fromString "checkTemp"),
1680 index = Immediate.zero,
1681 scale = Scale.Four,
1682 size = Size.LONG,
1683 class = MemLoc.Class.Temp}
1684 val address
1685 = MemLoc.basic
1686 {base = Immediate.label jump_table_label,
1687 index = indexTemp,
1688 scale = Scale.Four,
1689 size = Size.LONG,
1690 class = MemLoc.Class.Code}
1691
1692 val size
1693 = case Operand.size test
1694 of SOME size => size
1695 | NONE => Size.LONG
1696 val indexTemp' = indexTemp
1697 val indexTemp = Operand.memloc indexTemp
1698 val checkTemp' = checkTemp
1699 val checkTemp = Operand.memloc checkTemp
1700 val address = Operand.memloc address
1701 in
1702 AppendList.appends
1703 [if Size.lt(size, Size.LONG)
1704 then AppendList.single
1705 (Assembly.instruction_movx
1706 {oper = Instruction.MOVZX,
1707 src = test,
1708 srcsize = size,
1709 dst = indexTemp,
1710 dstsize = Size.LONG})
1711 else AppendList.single
1712 (Assembly.instruction_mov
1713 {src = test,
1714 dst = indexTemp,
1715 size = Size.LONG}),
1716 if LiveSet.isEmpty dead
1717 then AppendList.empty
1718 else AppendList.single
1719 (Assembly.directive_force
1720 {commit_memlocs = MemLocSet.empty,
1721 commit_classes = ClassSet.empty,
1722 remove_memlocs = MemLocSet.empty,
1723 remove_classes = ClassSet.empty,
1724 dead_memlocs = LiveSet.toMemLocSet dead,
1725 dead_classes = ClassSet.empty}),
1726 if shift > 0
1727 then let
1728 val idC = Directive.Id.new ()
1729 val defaultC = pushCompensationBlock
1730 {label = default,
1731 id = idC}
1732 val _ = incNear(jumpInfo, default)
1733 in
1734 AppendList.appends
1735 [AppendList.fromList
1736 [Assembly.instruction_mov
1737 {src = indexTemp,
1738 dst = checkTemp,
1739 size = Size.LONG},
1740 Assembly.instruction_binal
1741 {oper = Instruction.AND,
1742 src = Operand.immediate_word
1743 (ones (shift, WordSize.word32)),
1744 dst = checkTemp,
1745 size = Size.LONG}],
1746 if WordX.isZero mask
1747 then AppendList.empty
1748 else AppendList.single
1749 (Assembly.instruction_binal
1750 {oper = Instruction.SUB,
1751 src = Operand.immediate_word mask,
1752 dst = checkTemp,
1753 size = Size.LONG}),
1754 AppendList.fromList
1755 [Assembly.directive_force
1756 {commit_memlocs = MemLocSet.empty,
1757 commit_classes = nearflushClasses,
1758 remove_memlocs = MemLocSet.empty,
1759 remove_classes = ClassSet.empty,
1760 dead_memlocs = MemLocSet.singleton checkTemp',
1761 dead_classes = ClassSet.empty},
1762 Assembly.instruction_jcc
1763 {condition = Instruction.NZ,
1764 target = Operand.label defaultC},
1765 Assembly.directive_saveregalloc
1766 {id = idC,
1767 live = MemLocSet.add
1768 (MemLocSet.add
1769 (LiveSet.toMemLocSet default_live,
1770 stackTop ()),
1771 frontier ())},
1772 Assembly.instruction_sral
1773 {oper = Instruction.SAR,
1774 count = Operand.immediate_int shift,
1775 dst = indexTemp,
1776 size = Size.LONG}]]
1777 end
1778 else AppendList.empty,
1779 if WordX.equals (minK, zero)
1780 then AppendList.empty
1781 else AppendList.single
1782 (Assembly.instruction_binal
1783 {oper = Instruction.SUB,
1784 src = Operand.immediate_word minK,
1785 dst = indexTemp,
1786 size = Size.LONG}),
1787 AppendList.fromList
1788 [Assembly.directive_force
1789 {commit_memlocs = MemLocSet.empty,
1790 commit_classes = nearflushClasses,
1791 remove_memlocs = MemLocSet.empty,
1792 remove_classes = ClassSet.empty,
1793 dead_memlocs = MemLocSet.empty,
1794 dead_classes = ClassSet.empty},
1795 Assembly.directive_cache
1796 {caches = [{register = indexReg,
1797 memloc = indexTemp',
1798 reserve = false}]},
1799 Assembly.instruction_cmp
1800 {src1 = indexTemp,
1801 src2 = Operand.immediate_word rangeK,
1802 size = Size.LONG},
1803 Assembly.instruction_jcc
1804 {condition = Instruction.A,
1805 target = Operand.label defaultD},
1806 Assembly.directive_saveregalloc
1807 {id = idD,
1808 live = MemLocSet.add
1809 (MemLocSet.add
1810 (LiveSet.toMemLocSet default_live,
1811 stackTop ()),
1812 frontier ())},
1813 Assembly.instruction_jmp
1814 {target = address,
1815 absolute = true},
1816 Assembly.directive_saveregalloc
1817 {id = idT,
1818 live = MemLocSet.add
1819 (MemLocSet.add
1820 (LiveSet.toMemLocSet live,
1821 stackTop ()),
1822 frontier ())},
1823 Assembly.directive_force
1824 {commit_memlocs = MemLocSet.empty,
1825 commit_classes = ClassSet.empty,
1826 remove_memlocs = MemLocSet.empty,
1827 remove_classes = ClassSet.empty,
1828 dead_memlocs = MemLocSet.singleton indexTemp',
1829 dead_classes = ClassSet.empty}],
1830 AppendList.fromList
1831 [Assembly.pseudoop_data (),
1832 Assembly.pseudoop_p2align
1833 (Immediate.int 4, NONE, NONE),
1834 Assembly.label jump_table_label,
1835 Assembly.pseudoop_long jump_table,
1836 Assembly.pseudoop_text ()]]
1837 end
1838
1839 fun doit(cases)
1840 = let
1841 val (cases,
1842 minK, maxK, length,
1843 shift, mask)
1844 = reduce(cases)
1845
1846 val rangeK
1847 = range(minK,maxK)
1848 in
1849 if length >= 8
1850 andalso
1851 WordX.lt (WordX.div(rangeK,two,{signed=false}),
1852 WordX.fromIntInf (IntInf.fromInt length, ws),
1853 {signed = false})
1854 then let
1855 val cases
1856 = List.insertionSort
1857 (cases,
1858 fn ((k,_),(k',_))
1859 => ltFn(k,k'))
1860 in
1861 doitTable(cases,
1862 minK, maxK, rangeK,
1863 shift, mask)
1864 end
1865 else effectDefault gef
1866 {label = label,
1867 transfer = transfer}
1868 end
1869 in
1870 case cases
1871 of Transfer.Cases.Word cases
1872 => doit cases
1873 end
1874 | _ => effectDefault gef
1875 {label = label,
1876 transfer = transfer}
1877
1878 and fallNone (GEF {...})
1879 {label, live} : Assembly.t AppendList.t
1880 = let
1881 val liveRegsTransfer = getLiveRegsTransfers
1882 (liveTransfers, label)
1883 val liveFltRegsTransfer = getLiveFltRegsTransfers
1884 (liveTransfers, label)
1885
1886 val live
1887 = List.fold
1888 (liveRegsTransfer,
1889 live,
1890 fn ((memloc,_,_),live)
1891 => LiveSet.remove(live,memloc))
1892 val live
1893 = List.fold
1894 (liveFltRegsTransfer,
1895 live,
1896 fn ((memloc,_),live)
1897 => LiveSet.remove(live,memloc))
1898
1899 fun default ()
1900 = AppendList.fromList
1901 ((* flushing at near transfer *)
1902 (Assembly.directive_cache
1903 {caches = [{register = stackTopReg,
1904 memloc = stackTop (),
1905 reserve = true},
1906 {register = frontierReg,
1907 memloc = frontier (),
1908 reserve = true}]})::
1909 (Assembly.directive_fltcache
1910 {caches
1911 = List.map
1912 (liveFltRegsTransfer,
1913 fn (memloc,_)
1914 => {memloc = memloc})})::
1915 (Assembly.directive_cache
1916 {caches
1917 = List.map
1918 (liveRegsTransfer,
1919 fn (temp,register,_)
1920 => {register = register,
1921 memloc = temp,
1922 reserve = true})})::
1923 (Assembly.directive_force
1924 {commit_memlocs = LiveSet.toMemLocSet live,
1925 commit_classes = nearflushClasses,
1926 remove_memlocs = MemLocSet.empty,
1927 remove_classes = ClassSet.empty,
1928 dead_memlocs = MemLocSet.empty,
1929 dead_classes = ClassSet.empty})::
1930 (Assembly.instruction_jmp
1931 {target = Operand.label label,
1932 absolute = false})::
1933 (Assembly.directive_unreserve
1934 {registers
1935 = (stackTopReg)::
1936 (frontierReg)::
1937 (List.map
1938 (liveRegsTransfer,
1939 fn (_,register,_)
1940 => register))})::
1941 nil)
1942 in
1943 case getLayoutInfo label
1944 of NONE
1945 => default ()
1946 | SOME (Block.T {...})
1947 => (push label;
1948 default ())
1949 end
1950
1951 and fallDefault (gef as GEF {generate,...})
1952 {label, live} : Assembly.t AppendList.t
1953 = let
1954 datatype z = datatype x86JumpInfo.status
1955 val liveRegsTransfer = getLiveRegsTransfers
1956 (liveTransfers, label)
1957 val liveFltRegsTransfer = getLiveFltRegsTransfers
1958 (liveTransfers, label)
1959
1960 val live
1961 = List.fold
1962 (liveRegsTransfer,
1963 live,
1964 fn ((memloc,_,_),live)
1965 => LiveSet.remove(live,memloc))
1966 val live
1967 = List.fold
1968 (liveFltRegsTransfer,
1969 live,
1970 fn ((memloc,_),live)
1971 => LiveSet.remove(live,memloc))
1972
1973 fun default jmp
1974 = AppendList.appends
1975 [AppendList.fromList
1976 [(* flushing at near transfer *)
1977 (Assembly.directive_cache
1978 {caches = [{register = stackTopReg,
1979 memloc = stackTop (),
1980 reserve = true},
1981 {register = frontierReg,
1982 memloc = frontier (),
1983 reserve = true}]}),
1984 (Assembly.directive_fltcache
1985 {caches
1986 = List.map
1987 (liveFltRegsTransfer,
1988 fn (memloc,_)
1989 => {memloc = memloc})}),
1990 (Assembly.directive_cache
1991 {caches
1992 = List.map
1993 (liveRegsTransfer,
1994 fn (temp,register,_)
1995 => {register = register,
1996 memloc = temp,
1997 reserve = true})}),
1998 (Assembly.directive_force
1999 {commit_memlocs = LiveSet.toMemLocSet live,
2000 commit_classes = nearflushClasses,
2001 remove_memlocs = MemLocSet.empty,
2002 remove_classes = ClassSet.empty,
2003 dead_memlocs = MemLocSet.empty,
2004 dead_classes = ClassSet.empty})],
2005 if jmp
2006 then AppendList.single
2007 (Assembly.instruction_jmp
2008 {target = Operand.label label,
2009 absolute = false})
2010 else AppendList.empty,
2011 AppendList.single
2012 (Assembly.directive_unreserve
2013 {registers
2014 = (stackTopReg)::
2015 (frontierReg)::
2016 (List.map
2017 (liveRegsTransfer,
2018 fn (_,register,_)
2019 => register))})]
2020 in
2021 case getLayoutInfo label
2022 of NONE
2023 => default true
2024 | SOME (Block.T {...})
2025 => (case getNear(jumpInfo, label)
2026 of Count 1
2027 => generate gef
2028 {label = label,
2029 falling = true,
2030 unique = true}
2031 | _ => AppendList.append
2032 (default false,
2033 AppendList.cons
2034 (Assembly.directive_reset (),
2035 (generate gef
2036 {label = label,
2037 falling = true,
2038 unique = false}))))
2039 end
2040
2041 fun make {generate, effect, fall}
2042 = generate (GEF {generate = generate,
2043 effect = effect,
2044 fall = fall})
2045
2046 val generate
2047 = case optimize
2048 of 0 => make {generate = generateAll,
2049 effect = effectDefault,
2050 fall = fallNone}
2051 | _ => make {generate = generateAll,
2052 effect = effectJumpTable,
2053 fall = fallDefault}
2054
2055 val _ = List.foreach
2056 (blocks,
2057 fn Block.T {entry, ...}
2058 => (case entry
2059 of Func {label, ...} => enque label
2060 | _ => ()))
2061 fun doit () : Assembly.t list list
2062 = (case deque ()
2063 of NONE => []
2064 | SOME label
2065 => (case AppendList.toList (generate {label = label,
2066 falling = false,
2067 unique = false})
2068 of [] => doit ()
2069 | block => block::(doit ())))
2070 val assembly = doit ()
2071 val symbol_stubs = makeDarwinSymbolStubs ()
2072 val _ = destLayoutInfo ()
2073 val _ = destProfileLabel ()
2074
2075 val assembly = [Assembly.pseudoop_text ()]::assembly
2076 val assembly =
2077 if List.isEmpty symbol_stubs
2078 then assembly
2079 else symbol_stubs :: assembly
2080 val assembly =
2081 if List.isEmpty data
2082 then assembly
2083 else data::assembly
2084 in
2085 assembly
2086 end
2087
2088 val (generateTransfers, generateTransfers_msg)
2089 = tracerTop
2090 "generateTransfers"
2091 generateTransfers
2092
2093 fun generateTransfers_totals ()
2094 = (generateTransfers_msg ();
2095 Control.indent ();
2096 x86Liveness.LiveInfo.verifyLiveInfo_msg ();
2097 x86JumpInfo.verifyJumpInfo_msg ();
2098 x86EntryTransfer.verifyEntryTransfer_msg ();
2099 x86LoopInfo.createLoopInfo_msg ();
2100 x86LiveTransfers.computeLiveTransfers_totals ();
2101 Control.unindent ())
2102end