| 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 | |
| 10 | functor x86GenerateTransfers(S: X86_GENERATE_TRANSFERS_STRUCTS): X86_GENERATE_TRANSFERS = |
| 11 | struct |
| 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 ()) |
| 2102 | end |