|
|
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.