|
|
1.1 ! root 1: (* Copyright 1989 by AT&T Bell Laboratories *) ! 2: (* coder.sml ! 3: * ! 4: * J.H. Reppy ! 5: * Cornell University ! 6: * Ithaca, NY 14853 ! 7: * [email protected] ! 8: * ! 9: * HISTORY: ! 10: * 11/20/89 created ! 11: * ! 12: * This is a machine independent code scheduler for RISC machines with 32-bit ! 13: * instructions. We assume that the machine has delayed branches. ! 14: *) ! 15: ! 16: signature CODER = ! 17: sig ! 18: eqtype label sharing type label = BaseCoder.label ! 19: type instruction ! 20: type sdi ! 21: ! 22: val baseLab : label (* The symbolic base address of the current code block. *) ! 23: ! 24: val newLabel : unit -> label ! 25: val define : label -> unit ! 26: ! 27: val emitLong : int -> unit ! 28: val emitString : string -> unit ! 29: val emitReal : string -> unit ! 30: val emitLabel : (label * int) -> unit ! 31: (* L3: emitLabel(L2, k) is equivalent to L3: emitLong(k+L2-L3) *) ! 32: ! 33: val mark : unit -> unit ! 34: ! 35: val emit : instruction -> unit ! 36: val delay : instruction -> unit ! 37: val emitSDI : sdi -> unit ! 38: ! 39: val comment : string -> unit ! 40: ! 41: val finish : unit -> unit ! 42: ! 43: end (* signature CODER *) ! 44: ! 45: functor Coder ( ! 46: structure M : MACHINSTR and E : EMITTER ! 47: sharing type M.instruction = E.instruction ! 48: and type M.label = E.label) : CODER = ! 49: struct ! 50: ! 51: open BaseCoder M ! 52: ! 53: datatype sdi_nd = SDI_ND of { ! 54: instr : sdi, ! 55: size : int ref ! 56: } ! 57: ! 58: datatype data ! 59: = LABEL of label ! 60: | MARK ! 61: | LONGconst of int ! 62: | STRINGconst of string ! 63: | REALconst of string ! 64: | ADDRconst of (label * int) ! 65: ! 66: datatype blk_list ! 67: = CODEBLK of (instruction list * blk_list) ! 68: | SDI of (sdi_nd * blk_list) ! 69: | DATABLK of (data list * blk_list) ! 70: | NILBLK ! 71: ! 72: fun revCode l = let ! 73: fun rev (NILBLK, l) = l ! 74: | rev (CODEBLK(x, rest), l) = rev(rest, CODEBLK(x, l)) ! 75: | rev (SDI(x, rest), l) = rev(rest, SDI(x, l)) ! 76: | rev (DATABLK(x, rest), l) = rev(rest, DATABLK(x, l)) ! 77: in ! 78: rev (l, NILBLK) ! 79: end ! 80: ! 81: datatype codept ! 82: = NILpt ! 83: | LABELpt of (label * codept) ! 84: | SDIpt of (sdi_nd * codept) ! 85: ! 86: val baseLab = newLabel() (* The base address of the current code block. *) ! 87: ! 88: val codeList = ref (DATABLK([LABEL baseLab], NILBLK)) ! 89: val codePtList = ref NILpt ! 90: val codeLen = ref 0 ! 91: val numSlots = ref 0 ! 92: val numNops = ref 0 ! 93: ! 94: fun emitCode I = (codeList := ! 95: case !codeList ! 96: of (CODEBLK(cl, rest)) => CODEBLK(I::cl, rest) ! 97: | lst => CODEBLK([I], lst)) ! 98: ! 99: (* Insert an instruction/nop pair into the code list. If the instruction is a jump, ! 100: * then start a DATABLK to insure that the jump is the end of a code block. *) ! 101: fun emitDelay I = let ! 102: val clst = case !codeList ! 103: of (CODEBLK(cl, rest)) => CODEBLK(nop::I::cl, rest) ! 104: | lst => CODEBLK([nop, I], lst) ! 105: in ! 106: codeList := ! 107: case (instrKind I) of IK_JUMP => DATABLK(nil, clst) | _ => clst ! 108: end ! 109: ! 110: fun emitData D = (codeList := ! 111: case !codeList ! 112: of (DATABLK(dl, rest)) => DATABLK(D::dl, rest) ! 113: | lst => DATABLK([D], lst)) ! 114: ! 115: fun reset () = ( ! 116: codeList := DATABLK([LABEL baseLab], NILBLK); ! 117: codePtList := NILpt; codeLen := 0; ! 118: numSlots := 0; numNops := 0) ! 119: ! 120: fun padString s = (case ((size s) mod 4) ! 121: of 0 => s ! 122: | 1 => (s ^ "\000\000\000") ! 123: | 2 => (s ^ "\000\000") ! 124: | 3 => (s ^ "\000")) ! 125: ! 126: fun emitLong i = ( ! 127: emitData (LONGconst i); ! 128: codeLen := !codeLen + 4) ! 129: ! 130: fun emitString s = let ! 131: val s' = padString s ! 132: in ! 133: emitData (STRINGconst s'); ! 134: codeLen := !codeLen + (size s') ! 135: end ! 136: ! 137: fun emitReal r = ( ! 138: emitData (REALconst r); ! 139: codeLen := !codeLen + 8) ! 140: ! 141: fun emitLabel args = ( ! 142: emitData (ADDRconst args); ! 143: codeLen := !codeLen + 4) ! 144: ! 145: fun define (l as Label{addr, ...}) = ( ! 146: addr := !codeLen; ! 147: codePtList := LABELpt(l, !codePtList); ! 148: emitData (LABEL l)) ! 149: ! 150: fun mark () = (emitData MARK; codeLen := !codeLen + 4) ! 151: ! 152: fun emit I = (emitCode I; codeLen := !codeLen + 4) ! 153: fun delay I = (emitDelay I; codeLen := !codeLen + 8) ! 154: ! 155: fun emitSDI I = let ! 156: val minSz = minSize I ! 157: val nd = SDI_ND{instr = I, size = ref minSz} ! 158: in ! 159: codeList := SDI(nd, !codeList); ! 160: codePtList := SDIpt(nd, !codePtList); ! 161: codeLen := !codeLen + minSz ! 162: end ! 163: ! 164: val comment = E.comment ! 165: ! 166: ! 167: fun computeSizes () = let ! 168: fun reverse l = let (* reverse l and remove leading labels *) ! 169: fun clean (LABELpt(_, rest)) = clean rest ! 170: | clean l = l ! 171: fun rev (NILpt, l) = clean l ! 172: | rev (LABELpt(lab, rest), l) = rev(rest, LABELpt(lab, l)) ! 173: | rev (SDIpt(lab, rest), l) = rev(rest, SDIpt(lab, l)) ! 174: in ! 175: rev (l, NILpt) ! 176: end ! 177: fun deltaSize (SDI_ND{instr, size}) = let ! 178: val (isMax, newSz) = sizeOf instr ! 179: val dsz = newSz - (!size) ! 180: in ! 181: if (dsz > 0) ! 182: then (size := newSz; (isMax, dsz)) ! 183: else (isMax, 0) ! 184: end ! 185: fun adjust (NILpt, 0, _) = () ! 186: | adjust (NILpt, delta, l) = ( ! 187: codeLen := !codeLen + delta; ! 188: adjust (reverse l, 0, NILpt)) ! 189: | adjust (LABELpt(lab as Label{addr, ...}, rest), delta, l) = ( ! 190: addr := !addr + delta; ! 191: adjust(rest, delta, LABELpt(lab, l))) ! 192: | adjust (SDIpt(sdi, rest), delta, l) = ( ! 193: case (deltaSize sdi) ! 194: of (true, dsz) => adjust(rest, delta+dsz, l) ! 195: | (false, dsz) => adjust(rest, delta+dsz, SDIpt(sdi, l))) ! 196: val codePts = reverse(!codePtList) ! 197: in ! 198: codePtList := NILpt; ! 199: adjust (codePts, 0, NILpt) ! 200: end (* computeSizes *) ! 201: ! 202: ! 203: (** Instruction scheduling and machine code emission **) ! 204: ! 205: datatype instr_nd (* Nodes in the resource dependency graph *) ! 206: = IND of { ! 207: id : int, (* unique id for equality testing *) ! 208: instr : instruction, (* The instruction *) ! 209: nsuccs : int, (* The number of successors *) ! 210: succs : instr_nd list, ! 211: succlock : bool, (* If this instruction has an interlock with one of *) ! 212: (* its successors, then this is true. *) ! 213: maxpathlen : int, (* The length of the longest path to a leaf. *) ! 214: npreds : int ref (* The number of predecessors. This is incremented *) ! 215: (* when building the graph, and decremented as *) ! 216: (* each predecessor is scheduled. *) ! 217: } ! 218: ! 219: fun member (IND{id = x, ...}, lst) = let ! 220: fun mem nil = false ! 221: | mem (IND{id = y, ...}::rest) = ((x = y) orelse (mem rest)) ! 222: in ! 223: mem lst ! 224: end ! 225: ! 226: fun merge (nil, lst) = lst ! 227: | merge (nd :: rest, lst) = if (member(nd, lst)) ! 228: then merge (rest, lst) ! 229: else merge (rest, nd :: lst) ! 230: ! 231: fun incPreds nil = () ! 232: | incPreds (IND{npreds, ...} :: rest) = (npreds := !npreds + 1; incPreds rest) ! 233: ! 234: (* Order a pair of instructions *) ! 235: fun orderInstrPair (nd1 as IND a, nd2 as IND b) = ! 236: if ((#succlock a) = (#succlock b)) ! 237: then let val n1 = (#nsuccs a) and n2 = (#nsuccs b) ! 238: in ! 239: if (n1 = n2) ! 240: then let val p1 = (#maxpathlen a) and p2 = (#maxpathlen b) ! 241: in ! 242: if ((p1 > p2) orelse ((p1 = p2) andalso ((#id a) > (#id b)))) ! 243: then (nd1, nd2) ! 244: else (nd2, nd1) ! 245: end ! 246: else if (n1 > n2) ! 247: then (nd1, nd2) ! 248: else (nd2, nd1) ! 249: (******************************** ! 250: if ((n1 > n2) ! 251: orelse ((n1 = n2) andalso ((#maxpathlen a) >= (#maxpathlen b)))) ! 252: then (nd1, nd2) ! 253: else (nd2, nd1) ! 254: ********************************) ! 255: end ! 256: else if (#succlock a) ! 257: then (nd1, nd2) ! 258: else (nd2, nd1) ! 259: ! 260: (* Schedule and emit the instructions of a straight-line block of code. *) ! 261: fun schedBlock (exitInstr, blks) = let ! 262: val exitDep = case exitInstr ! 263: of NONE => (fn _ => false) ! 264: | (SOME e) => let ! 265: val (exitUses, exitDefs) = rUseDef e ! 266: val f = exists (fn r => (exists (fn x => (r = x)) exitUses)) ! 267: val g = exists (fn r => (exists (fn x => (r = x)) exitDefs)) ! 268: in ! 269: fn I => let ! 270: val (u, d) = rUseDef I ! 271: in ! 272: (f d) orelse (g d) orelse (g u) ! 273: end ! 274: end ! 275: (* make a new instr_nd *) ! 276: fun mkINd (n, I, nil) = ! 277: IND{id = n, instr = I, ! 278: nsuccs = 0, succs = nil, succlock = exitDep I, ! 279: maxpathlen = 0, npreds = ref 0} ! 280: | mkINd (n, I, succLst) = let ! 281: fun f (nil, len, lock, mpl) = (len, lock, mpl) ! 282: | f (IND{instr, maxpathlen, ...} :: rest, len, lock, mpl) = ! 283: f (rest, len+1, hazard (I, instr), ! 284: if (maxpathlen > mpl) then maxpathlen else mpl) ! 285: val (len, lock, mpl) = f (succLst, 0, false, 0) ! 286: in ! 287: IND{id = n, instr = I, ! 288: nsuccs = len, succs = succLst, succlock = lock, ! 289: maxpathlen = mpl+1, npreds = ref 0} ! 290: end ! 291: (* resource use/def vectors *) ! 292: val lastUse = array (numResources, nil) ! 293: val lastDef = array (numResources, nil) ! 294: (* find resource dependencies *) ! 295: fun findDeps rsrc = let ! 296: fun add (nil, lst) = lst ! 297: | add (r :: rest, lst) = add (rest, merge(rsrc sub r, lst)) ! 298: in ! 299: add ! 300: end ! 301: val findUseDeps = findDeps lastUse ! 302: val findDefDeps = findDeps lastDef ! 303: (* update resource use/def vectors *) ! 304: fun updateUseDefs nd = let ! 305: val ndl = [nd] ! 306: val updateUses = ! 307: app (fn r => update(lastUse, r, nd::(lastUse sub r))) ! 308: val updateDefs = ! 309: app (fn r => (update(lastDef, r, ndl); update(lastUse, r, nil))) ! 310: in ! 311: fn (ruses, rdefs) => (updateDefs rdefs; updateUses ruses) ! 312: end ! 313: (* extract the dependency graph roots from the use/def vectors *) ! 314: fun roots () = let ! 315: fun isRoot (IND{npreds, ...}) = (!npreds = 0) ! 316: fun rootsOf (nil, lst) = lst ! 317: | rootsOf (nd::rest, lst) = if (isRoot nd) ! 318: then rootsOf (rest, nd::lst) ! 319: else rootsOf (rest, lst) ! 320: fun mergeRoots (~1, lst) = lst ! 321: | mergeRoots (i, lst) = let ! 322: val rlst = rootsOf (merge (lastDef sub i, lastUse sub i), nil) ! 323: in ! 324: mergeRoots (i-1, merge (rlst, lst)) ! 325: end ! 326: in ! 327: mergeRoots (numResources-1, nil) ! 328: end ! 329: (* Build the dependency graph for a list of instructions, returning the list ! 330: * of roots (instructions without predecessors). *) ! 331: fun buildDepGraph blkList = let ! 332: fun doInstrs (nil, n) = n ! 333: | doInstrs (I :: rest, n) = ( ! 334: case (instrKind I) ! 335: of IK_NOP => (numSlots := !numSlots + 1) ! 336: | IK_INSTR => let ! 337: val (ruses, rdefs) = rUseDef I ! 338: (* find use/def, def/use and def/def dependencies *) ! 339: val succLst = findUseDeps (rdefs, ! 340: findDefDeps (rdefs, findDefDeps (ruses, nil))) ! 341: val nd = mkINd (n, I, succLst) ! 342: in ! 343: incPreds succLst; ! 344: updateUseDefs nd (ruses, rdefs) ! 345: end ! 346: | IK_JUMP => ( ! 347: ErrorMsg.impossible "[Coder.doInstrs: unexpected jump]") ! 348: (* end case *); ! 349: doInstrs (rest, n+1)) ! 350: fun build (NILBLK, _) = () ! 351: | build (CODEBLK(instrs, rest), n) = ! 352: build (rest, doInstrs (instrs, n)) ! 353: | build _ = ErrorMsg.impossible "[Coder.build: bad block]" ! 354: in ! 355: build (blkList, 0) ! 356: end (* buildDepGraph *) ! 357: ! 358: (* Choose the next instruction from a list of candidates. Instructions that ! 359: * don't interlock with the previously scheduled instruction are given ! 360: * priority. *) ! 361: fun chooseNextInstr (prev, (x :: rest)) = let ! 362: val prevLock = case prev ! 363: of (NONE) => (fn _ => false) ! 364: | (SOME prev) => (fn (IND{instr, ...}) => hazard(prev, instr)) ! 365: (* choose1 & choose2 find the best instruction to schedule next. ! 366: * choose1 is used as long as the current choice interlocks with prev, ! 367: * choose2 is used after a non-interlocking choice is found. *) ! 368: fun choose1 (choice, done, nil) = (choice, done) ! 369: | choose1 (choice, done, nd::rest) = if (prevLock nd) ! 370: then let ! 371: val (a, b) = orderInstrPair (choice, nd) ! 372: in ! 373: choose1 (a, b::done, rest) ! 374: end ! 375: else choose2 (nd, choice::done, rest) ! 376: and choose2 (choice, done, nil) = (choice, done) ! 377: | choose2 (choice, done, nd::rest) = if (prevLock nd) ! 378: then choose2 (choice, nd::done, rest) ! 379: else let ! 380: val (a, b) = orderInstrPair (choice, nd) ! 381: in ! 382: choose2 (a, b::done, rest) ! 383: end ! 384: val (IND{succs, instr, ...}, remainder) = if (prevLock x) ! 385: then choose1 (x, nil, rest) ! 386: else choose2 (x, nil, rest) ! 387: fun addSuccs (nil, candidates) = candidates ! 388: | addSuccs ((nd as IND{npreds, ...}) :: rest, candidates) = let ! 389: val n = !npreds ! 390: in ! 391: npreds := n-1; ! 392: if (n = 1) ! 393: then addSuccs(rest, nd::candidates) ! 394: else addSuccs(rest, candidates) ! 395: end ! 396: in ! 397: (instr, addSuccs(succs, remainder)) ! 398: end ! 399: | chooseNextInstr _ = (ErrorMsg.impossible "[Coder.chooseNextInstr]") ! 400: ! 401: (* assign an order to the instructions, based on the dependency graph. *) ! 402: fun assignOrder () = let ! 403: fun emitNop () = (numNops := !numNops + 1; nop) ! 404: fun checkLock (i1, i2, cl) = if (needsNop(i1, i2)) ! 405: then (numNops := !numNops + 1; nop :: cl) ! 406: else cl ! 407: fun order (NONE, nil, _) = let ! 408: val (SOME e) = exitInstr ! 409: in ! 410: numNops := !numNops + 1; ! 411: [nop, e] ! 412: end ! 413: | order (SOME prev, nil, cl) = ( ! 414: case exitInstr ! 415: of NONE => (prev :: cl) ! 416: | (SOME e) => ( ! 417: if (exitDep prev) ! 418: then ( ! 419: numNops := !numNops + 1; ! 420: nop :: e :: checkLock(prev, e, prev :: cl)) ! 421: else (prev :: e :: cl)) ! 422: (* end case *)) ! 423: | order (prev, rest, cl) = let ! 424: val (next, remainder) = chooseNextInstr (prev, rest) ! 425: val newCL = case prev ! 426: of (SOME x) => checkLock(x, next, (x :: cl)) ! 427: | NONE => cl ! 428: in ! 429: order (SOME next, remainder, newCL) ! 430: end ! 431: in ! 432: rev (order(NONE, roots(), nil)) ! 433: end (* assignOrder *) ! 434: in ! 435: buildDepGraph blks; ! 436: assignOrder () ! 437: end (* schedBlock *) ! 438: ! 439: fun sched (NILBLK, blst) = blst ! 440: | sched (DATABLK(dl, rest), blst) = let ! 441: fun adjust (nil, dl) = dl ! 442: | adjust ((lab as LABEL(Label{addr, ...})) :: rest, dl) = ( ! 443: addr := !addr - 4*(!numSlots - !numNops); ! 444: adjust (rest, lab :: dl)) ! 445: | adjust (d :: rest, dl) = adjust(rest, d :: dl) ! 446: in ! 447: sched (rest, DATABLK(adjust (dl, nil), blst)) ! 448: end ! 449: | sched (arg, blst) = let ! 450: fun insertCode (nil, nil, bl) = bl ! 451: | insertCode (nil, cl, bl) = CODEBLK(cl, bl) ! 452: | insertCode (I::rest, cl, bl) = ( ! 453: case (instrKind I) ! 454: of IK_JUMP => ! 455: insertCode ((tl rest), nil, DATABLK(nil, CODEBLK(nop::I::cl, bl))) ! 456: | _ => insertCode (rest, I::cl, bl)) ! 457: fun findBlk (CODEBLK(cl, rest), lst) = findBlk(rest, CODEBLK(cl, lst)) ! 458: | findBlk (SDI(SDI_ND{instr, size}, rest), lst) = ! 459: findBlk (insertCode(expand (instr, !size), nil, rest), lst) ! 460: | findBlk (rest, bl) = (rest, bl) ! 461: val (rest, b as CODEBLK(cl, bl)) = findBlk (arg, NILBLK) ! 462: val newCL = (case cl ! 463: of (i1 :: i2 :: instrs) => (case (instrKind i1, instrKind i2) ! 464: of (IK_NOP, IK_JUMP) => ( ! 465: numSlots := !numSlots + 1; ! 466: schedBlock (SOME i2, CODEBLK(instrs, bl))) ! 467: | _ => schedBlock (NONE, b)) ! 468: | _ => schedBlock (NONE, b)) ! 469: in ! 470: sched (rest, CODEBLK(newCL, blst)) ! 471: end ! 472: ! 473: (* reverse the code list, while expanding SDIs and reversing instruction lists. ! 474: * This pass is the alternative to the scheduling pass. *) ! 475: fun noSched (NILBLK, bl) = bl ! 476: | noSched (CODEBLK(cl, rest), bl) = noSched (rest, CODEBLK(rev cl, bl)) ! 477: | noSched (SDI(SDI_ND{instr, size}, rest), bl) = ! 478: noSched (rest, CODEBLK(expand(instr, !size), bl)) ! 479: | noSched (DATABLK(dl, rest), bl) = noSched (rest, DATABLK(rev dl, bl)) ! 480: ! 481: fun finish () = let ! 482: val emitInstrs = app E.emitInstr ! 483: fun emitDataList nil = () ! 484: | emitDataList (d :: rest) = (case d ! 485: of (LABEL lab) => E.define lab ! 486: | MARK => E.mark () ! 487: | (LONGconst n) => E.emitLong n ! 488: | (STRINGconst s) => E.emitString s ! 489: | (REALconst r) => E.emitReal r ! 490: | (ADDRconst args) => E.emitAddr args ! 491: (* end case *); ! 492: emitDataList rest) ! 493: fun emitBlk NILBLK = () ! 494: | emitBlk (CODEBLK(cl, rest)) = (emitInstrs cl; emitBlk rest) ! 495: | emitBlk (DATABLK(dl, rest)) = (emitDataList dl; emitBlk rest) ! 496: | emitBlk _ = (ErrorMsg.impossible "[Coder.finish.emitBlk]") ! 497: fun schedule cl = if (!System.Control.CG.scheduling) ! 498: then revCode (sched (revCode cl, NILBLK)) ! 499: else noSched (cl, NILBLK) ! 500: val _ = computeSizes () ! 501: val newCL = schedule (!codeList before (codeList := NILBLK)) ! 502: in ! 503: E.init (!codeLen - 4*(!numSlots - !numNops)); ! 504: emitBlk newCL; ! 505: reset() ! 506: end (* finish *) ! 507: ! 508: end (* Coder *)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.