1 (* Copyright (C) 2009 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.
6 * MLton is released under a BSD-style license.
7 * See the file MLton-LICENSE for details.
10 functor amd64Translate(S: AMD64_TRANSLATE_STRUCTS): AMD64_TRANSLATE =
15 val tracerTop = amd64.tracerTop
17 fun argsToString(ss: string list): string
18 = "(" ^ (concat (List.separate(ss, ", "))) ^ ")"
20 structure Machine = amd64MLton.Machine
25 structure Label = Label
27 structure Register = Register
28 structure Scale = Scale
29 structure StackOffset = StackOffset
31 structure WordSize = WordSize
32 structure WordX = WordX
35 datatype z = datatype WordSize.prim
41 fun toAMD64Operand (g: t) : (amd64.Operand.t * amd64.Size.t) vector =
43 val ty = Machine.Type.toCType (ty g)
48 then amd64MLton.global_base ty
49 else amd64MLton.globalObjptrNonRoot_base)
53 index = amd64.Immediate.int index,
54 scale = amd64.Scale.fromCType ty,
55 size = amd64.Size.BYTE,
56 class = amd64MLton.Classes.Globals}
57 val sizes = amd64.Size.fromCType ty
59 (#1 o Vector.mapAndFold)
60 (sizes, 0, fn (size,offset) =>
61 (((amd64.Operand.memloc o amd64.MemLoc.shift)
63 disp = amd64.Immediate.int offset,
64 scale = amd64.Scale.One,
65 size = size}, size), offset + amd64.Size.toBytes size))
73 fun get (f: ('a * 'b) -> 'c) (i: int) (v: ('a * 'b) vector) =
78 val rec toAMD64Operand : t -> (amd64.Operand.t * amd64.Size.t) vector =
79 fn ArrayOffset {base, index, offset, scale, ty}
81 val base = toAMD64Operand base
82 val _ = Assert.assert("amd64Translate.Operand.toAMD64Operand: Array/base",
83 fn () => Vector.length base = 1)
84 val base = getOp0 base
85 val index = toAMD64Operand index
86 val _ = Assert.assert("amd64Translate.Operand.toAMD64Operand: Array/index",
87 fn () => Vector.length index = 1)
88 val index = getOp0 index
91 Scale.One => amd64.Scale.One
92 | Scale.Two => amd64.Scale.Two
93 | Scale.Four => amd64.Scale.Four
94 | Scale.Eight => amd64.Scale.Eight
95 val ty = Type.toCType ty
97 case (amd64.Operand.deMemloc base,
98 amd64.Operand.deImmediate index,
99 amd64.Operand.deMemloc index) of
100 (SOME base, SOME index, _) =>
105 size = amd64.Size.BYTE,
106 class = amd64MLton.Classes.Heap}
107 | (SOME base, _, SOME index) =>
112 size = amd64.Size.BYTE,
113 class = amd64MLton.Classes.Heap}
114 | _ => Error.bug (concat ["amd64Translate.Operand.toAMD64Operand: ",
115 "strange Offset: base: ",
116 amd64.Operand.toString base,
118 amd64.Operand.toString index])
120 if Bytes.isZero offset
122 else amd64.MemLoc.shift
124 disp = amd64.Immediate.int (Bytes.toInt offset),
125 scale = amd64.Scale.One,
126 size = amd64.Size.BYTE}
127 val sizes = amd64.Size.fromCType ty
129 (#1 o Vector.mapAndFold)
130 (sizes, 0, fn (size,offset) =>
131 (((amd64.Operand.memloc o amd64.MemLoc.shift)
133 disp = amd64.Immediate.int offset,
134 scale = amd64.Scale.One,
135 size = size}, size), offset + amd64.Size.toBytes size))
137 | Cast (z, _) => toAMD64Operand z
138 | Contents {oper, ty} =>
140 val ty = Type.toCType ty
141 val base = toAMD64Operand oper
142 val _ = Assert.assert("amd64Translate.Operand.toAMD64Operand: Contents/base",
143 fn () => Vector.length base = 1)
144 val base = getOp0 base
146 case amd64.Operand.deMemloc base of
150 index = amd64.Immediate.zero,
151 scale = amd64.Scale.One,
152 size = amd64.Size.BYTE,
153 class = amd64MLton.Classes.Heap}
154 | _ => Error.bug (concat
155 ["amd64Translate.Operand.toAMD64Operand: ",
156 "strange Contents: base: ",
157 amd64.Operand.toString base])
158 val sizes = amd64.Size.fromCType ty
160 (#1 o Vector.mapAndFold)
161 (sizes, 0, fn (size,offset) =>
162 (((amd64.Operand.memloc o amd64.MemLoc.shift)
164 disp = amd64.Immediate.int offset,
165 scale = amd64.Scale.One,
166 size = size}, size), offset + amd64.Size.toBytes size))
170 val frontier = amd64MLton.gcState_frontierContentsOperand ()
172 Vector.new1 (frontier, valOf (amd64.Operand.size frontier))
175 Vector.new1 (amd64.Operand.label amd64MLton.gcState_label,
176 amd64MLton.pointerSize)
177 | Global g => Global.toAMD64Operand g
179 Vector.new1 (amd64.Operand.immediate_label l, amd64MLton.pointerSize)
181 Vector.new1 (amd64.Operand.immediate_zero, amd64MLton.wordSize)
182 | Offset {base = GCState, offset, ty} =>
184 val offset = Bytes.toInt offset
185 val ty = Type.toCType ty
186 val offset = amd64MLton.gcState_offset {offset = offset, ty = ty}
188 Vector.new1 (offset, valOf (amd64.Operand.size offset))
190 | Offset {base, offset, ty} =>
192 val offset = Bytes.toInt offset
193 val ty = Type.toCType ty
194 val base = toAMD64Operand base
195 val _ = Assert.assert("amd64Translate.Operand.toAMD64Operand: Offset/base",
196 fn () => Vector.length base = 1)
197 val base = getOp0 base
199 case amd64.Operand.deMemloc base of
203 index = amd64.Immediate.int offset,
204 scale = amd64.Scale.One,
205 size = amd64.Size.BYTE,
206 class = amd64MLton.Classes.Heap}
207 | _ => Error.bug (concat ["amd64Translate.Operand.toAMD64Operand: ",
208 "strange Offset: base: ",
209 amd64.Operand.toString base])
210 val sizes = amd64.Size.fromCType ty
212 (#1 o Vector.mapAndFold)
213 (sizes, 0, fn (size,offset) =>
214 (((amd64.Operand.memloc o amd64.MemLoc.shift)
216 disp = amd64.Immediate.int offset,
217 scale = amd64.Scale.One,
218 size = size}, size), offset + amd64.Size.toBytes size))
220 | Real _ => Error.bug "amd64Translate.Operand.toAMD64Operand: Real unimplemented"
223 val ty = Machine.Type.toCType (Register.ty r)
224 val index = Machine.Register.index r
225 val base = amd64.Immediate.label (amd64MLton.local_base ty)
229 index = amd64.Immediate.int index,
230 scale = amd64.Scale.fromCType ty,
231 size = amd64.Size.BYTE,
232 class = amd64MLton.Classes.Locals}
233 val sizes = amd64.Size.fromCType ty
235 (#1 o Vector.mapAndFold)
236 (sizes, 0, fn (size,offset) =>
237 (((amd64.Operand.memloc o amd64.MemLoc.shift)
239 disp = amd64.Immediate.int offset,
240 scale = amd64.Scale.One,
241 size = size}, size), offset + amd64.Size.toBytes size))
243 | StackOffset (StackOffset.T {offset, ty}) =>
245 val offset = Bytes.toInt offset
246 val ty = Type.toCType ty
249 {base = amd64MLton.gcState_stackTopContents (),
250 index = amd64.Immediate.int offset,
251 scale = amd64.Scale.One,
252 size = amd64.Size.BYTE,
253 class = amd64MLton.Classes.Stack}
254 val sizes = amd64.Size.fromCType ty
256 (#1 o Vector.mapAndFold)
257 (sizes, 0, fn (size,offset) =>
258 (((amd64.Operand.memloc o amd64.MemLoc.shift)
260 disp = amd64.Immediate.int offset,
261 scale = amd64.Scale.One,
262 size = size}, size), offset + amd64.Size.toBytes size))
266 val stackTop = amd64MLton.gcState_stackTopContentsOperand ()
268 Vector.new1 (stackTop, valOf (amd64.Operand.size stackTop))
273 Vector.new1 (amd64.Operand.immediate_word w, size)
275 case WordSize.prim (WordX.size w) of
276 W8 => single amd64.Size.BYTE
277 | W16 => single amd64.Size.WORD
278 | W32 => single amd64.Size.LONG
279 | W64 => single amd64.Size.QUAD
283 type transInfo = amd64MLton.transInfo
287 structure Kind = Machine.Kind
289 fun toAMD64Blocks {label, kind,
290 transInfo as {frameInfoToAMD64, live, liveInfo,
293 amd64Liveness.LiveInfo.setLiveOperands
294 (liveInfo, label, live label);
300 (amd64.Block.mkBlock'
301 {entry = SOME (amd64.Entry.jump {label = label}),
310 amd64.MemLocSet.empty,
312 => case amd64.Operand.deMemloc operand
313 of SOME memloc => amd64.MemLocSet.add(args, memloc)
317 (amd64.Block.mkBlock'
318 {entry = SOME (amd64.Entry.func {label = label,
323 | Kind.Cont {args, frameInfo, ...}
325 val frameInfo = frameInfoToAMD64 frameInfo
328 (args, amd64.MemLocSet.empty,
331 (Operand.toAMD64Operand (Live.toOperand operand), args,
332 fn ((operand,_),args) =>
333 case amd64.Operand.deMemloc operand of
334 SOME memloc => amd64.MemLocSet.add(args, memloc)
338 (amd64.Block.mkBlock'
339 {entry = SOME (amd64.Entry.cont {label = label,
341 frameInfo = frameInfo}),
345 | Kind.Handler {frameInfo, ...}
349 (amd64.Block.mkBlock'
350 {entry = SOME (amd64.Entry.handler
351 {frameInfo = frameInfoToAMD64 frameInfo,
353 live = amd64.MemLocSet.empty}),
357 | Kind.CReturn {dst, frameInfo, func}
361 NONE => Vector.new0 ()
362 | SOME dst => Operand.toAMD64Operand (Live.toOperand dst)
366 frameInfo = Option.map (frameInfo, frameInfoToAMD64),
369 transInfo = transInfo}
373 structure Statement =
375 open Machine.Statement
377 fun comments statement
378 = if !Control.Native.commented > 0
380 val comment = (Layout.toString o layout) statement
383 (amd64.Block.mkBlock'
385 statements = [amd64.Assembly.comment
390 (amd64.Block.mkBlock'
392 statements = [amd64.Assembly.comment
397 else (AppendList.empty,AppendList.empty)
399 fun toAMD64Blocks {statement,
400 transInfo as {...} : transInfo}
407 comment_end) = comments statement
409 val dsts = Operand.toAMD64Operand dst
410 val srcs = Operand.toAMD64Operand src
411 (* Operand.toAMD64Operand returns multi-word
412 * operands in and they will be moved in order,
413 * so it suffices to check for aliasing between
414 * the first dst and second src.
417 if Vector.length srcs > 1
418 andalso amd64.Operand.mayAlias
419 (#1 (Vector.sub (dsts, 0)),
420 #1 (Vector.sub (srcs, 1)))
421 then (Vector.rev dsts, Vector.rev srcs)
427 (amd64.Block.mkBlock'
430 = (Vector.toList o Vector.map2)
431 (dsts,srcs,fn ((dst,_),(src,srcsize)) =>
433 case amd64.Size.class srcsize
434 of amd64.Size.INT => amd64.Assembly.instruction_mov
438 | amd64.Size.FLT => amd64.Assembly.instruction_sse_movs
445 | PrimApp {dst, prim, args}
447 val (comment_begin, comment_end) = comments statement
448 val args = (Vector.concatV o Vector.map)
449 (args, Operand.toAMD64Operand)
452 NONE => Vector.new0 ()
453 | SOME dst => Operand.toAMD64Operand dst
457 (amd64MLton.prim {prim = prim,
460 transInfo = transInfo}),
465 (amd64.Block.mkProfileBlock'
471 open Machine.Transfer
475 (amd64.Block.mkBlock'
478 transfer = SOME (amd64.Transfer.goto
483 val (test,testsize) =
484 Vector.sub (Operand.toAMD64Operand test, 0)
486 if Label.equals(a, b)
487 then AppendList.single
488 (amd64.Block.mkBlock'
491 transfer = SOME (amd64.Transfer.goto {target = a})})
492 else AppendList.single
499 = [amd64.Assembly.instruction_test
504 = SOME (amd64.Transfer.iff
505 {condition = amd64.Instruction.NZ,
510 fun cmp (test, k, a, b)
512 val (test,testsize) =
513 Vector.sub (Operand.toAMD64Operand test, 0)
515 if Label.equals(a, b)
516 then AppendList.single
517 (amd64.Block.mkBlock'
520 transfer = SOME (amd64.Transfer.goto {target = a})})
521 else AppendList.single
522 ((* if (test = k) goto a
528 = [amd64.Assembly.instruction_cmp
530 src2 = amd64.Operand.immediate k,
533 = SOME (amd64.Transfer.iff
534 {condition = amd64.Instruction.E,
539 fun switch(test, cases, default)
541 val test = Operand.toAMD64Operand test
542 val (test,_) = Vector.sub(test, 0)
545 (amd64.Block.mkBlock'
548 transfer = SOME (amd64.Transfer.switch
551 default = default})})
554 fun doSwitchWord (test, cases, default)
555 = (case (cases, default)
557 => Error.bug "amd64Translate.Transfer.doSwitchWord"
558 | ([(_,l)], NONE) => goto l
559 | ([], SOME l) => goto l
560 | ([(w1,l1),(w2,l2)], NONE) =>
561 if WordX.isZero w1 andalso WordX.isOne w2
563 else if WordX.isZero w2 andalso WordX.isOne w1
565 else cmp(test,amd64.Immediate.word w1,l1,l2)
566 | ([(k',l')], SOME l)
567 => cmp(test,amd64.Immediate.word k',l',l)
568 | ((_,l)::cases, NONE)
569 => switch(test, amd64.Transfer.Cases.word cases, l)
571 => switch(test, amd64.Transfer.Cases.word cases, l))
573 fun comments transfer
574 = if !Control.Native.commented > 0
576 val comment = (Layout.toString o layout) transfer
579 (amd64.Block.mkBlock'
581 statements = [amd64.Assembly.comment comment],
584 else AppendList.empty
587 fun toAMD64Blocks {returns, transfer,
588 transInfo as {frameInfoToAMD64, ...}: transInfo}
590 of Arith {prim, args, dst, overflow, success, ...}
592 val args = (Vector.concatV o Vector.map)
593 (args, Operand.toAMD64Operand)
594 val dsts = Operand.toAMD64Operand dst
598 amd64MLton.arith {prim = prim,
603 transInfo = transInfo})
605 | CCall {args, frameInfo, func, return}
607 val args = (Vector.concatV o Vector.map)
608 (args, Operand.toAMD64Operand)
612 amd64MLton.ccall {args = args,
613 frameInfo = (Option.map
614 (frameInfo, frameInfoToAMD64)),
617 transInfo = transInfo})
623 (amd64.Block.mkBlock'
627 = SOME (amd64.Transfer.return
631 NONE => Error.bug "amd64Translate.Transfer.toAMD64Blocsk: Return"
633 amd64.MemLocSet.empty,
634 fn (operand, live) =>
636 (Operand.toAMD64Operand operand, live,
637 fn ((operand,_),live) =>
638 case amd64.Operand.deMemloc operand of
639 SOME memloc => amd64.MemLocSet.add(live, memloc)
640 | NONE => live))})}))
645 (amd64.Block.mkBlock'
649 = SOME (amd64.Transfer.raisee
651 = amd64.MemLocSet.add
653 (amd64.MemLocSet.empty,
654 amd64MLton.gcState_stackBottomContents ()),
655 amd64MLton.gcState_exnStackContents ())})}))
656 | Switch (Machine.Switch.T {cases, default, test, ...})
659 doSwitchWord (test, Vector.toList cases, default))
661 => (AppendList.append
668 transfer = SOME (amd64.Transfer.goto {target = label})})))
669 | Call {label, live, return, ...}
673 (live, amd64.MemLocSet.empty, fn (operand, live) =>
675 (Operand.toAMD64Operand (Live.toOperand operand), live,
676 fn ((operand, _), live) =>
677 case amd64.Operand.deMemloc operand of
679 | SOME memloc => amd64.MemLocSet.add (live, memloc)))
680 val com = comments transfer
683 NONE => amd64.Transfer.tail {target = label,
685 | SOME {return, handler, size} =>
686 amd64.Transfer.nontail {target = label,
690 size = Bytes.toInt size}
695 (amd64.Block.mkBlock' {entry = NONE,
697 transfer = SOME transfer}))
705 fun toAMD64Blocks {block = T {label,
712 transInfo as {...} : transInfo}
717 (Entry.toAMD64Blocks {label = label,
719 transInfo = transInfo},
723 = if !Control.Native.commented > 0
730 Operand.toString (Live.toOperand l)))]
732 [amd64.Assembly.comment comment]
736 Vector.foldr(statements,
737 (Transfer.toAMD64Blocks
738 {returns = (Option.map
740 Vector.map (v, Live.toOperand))),
742 transInfo = transInfo}),
745 (Statement.toAMD64Blocks
746 {statement = statement,
747 transInfo = transInfo}, l)))
749 val pseudo_blocks = AppendList.toList pseudo_blocks
751 val blocks = amd64.Block.compress pseudo_blocks
761 fun toAMD64Chunk {chunk = T {blocks, ...},
766 val addData = fn l => List.push (data, l)
767 val {get = live : Label.t -> amd64.Operand.t list,
770 = Property.getSetOnce
771 (Label.plist, Property.initRaise ("live", Label.layout))
772 val _ = Vector.foreach
773 (blocks, fn Block.T {label, live, ...} =>
775 (Vector.toList o #1 o Vector.unzip o
776 Vector.concatV o Vector.map)
777 (live, Operand.toAMD64Operand o Live.toOperand)))
778 val transInfo = {addData = addData,
779 frameInfoToAMD64 = frameInfoToAMD64,
783 = List.concat (Vector.toListMap
786 => Block.toAMD64Blocks
788 transInfo = transInfo}))
789 val _ = Vector.foreach (blocks, fn Block.T {label, ...} =>
791 val data = List.concatRev (!data)
795 else (amd64.Assembly.pseudoop_data())::data
797 amd64.Chunk.T {data = data, blocks = amd64Blocks}
801 fun translateChunk {chunk: amd64MLton.Machine.Chunk.t,
803 liveInfo: amd64Liveness.LiveInfo.t}:
804 {chunk: amd64.Chunk.t}
805 = {chunk = Chunk.toAMD64Chunk {chunk = chunk,
806 frameInfoToAMD64 = frameInfoToAMD64,
807 liveInfo = liveInfo}}
809 val (translateChunk, translateChunk_msg)
814 fun translateChunk_totals ()
815 = (translateChunk_msg ();