2 % (c) The AQUA Project, Glasgow University, 1993-1995
6 #include "HsVersions.h"
11 -- and, of course, that's not enough...
12 AbstractC, Target, StixTree, SplitUniqSupply, SUniqSM(..)
16 import AbsPrel ( PrimOp(..), primOpNeedsWrapper, isCompareOp
17 IF_ATTACK_PRAGMAS(COMMA tagOf_PrimOp)
18 IF_ATTACK_PRAGMAS(COMMA pprPrimOp)
20 import CgCompInfo ( mIN_UPD_SIZE )
21 import ClosureInfo ( infoTableLabelFromCI, entryLabelFromCI, fastLabelFromCI,
25 import Maybes ( Maybe(..), maybeToBool )
27 import PrimKind ( isFloatingKind )
28 import SMRep ( SMRep(..), SMSpecRepKind(..), SMUpdateKind(..) )
30 import StixInfo ( genCodeInfoTable )
36 For each independent chunk of AbstractC code, we generate a list of @StixTree@s,
37 where each tree corresponds to a single Stix instruction. We leave the chunks
38 separated so that register allocation can be performed locally within the chunk.
41 -- hacking with Uncle Will:
42 #define target_STRICT target@(Target _ _ _ _ _ _ _ _)
47 -> SUniqSM [[StixTree]]
49 genCodeAbstractC target_STRICT absC =
50 mapSUs gentopcode (mkAbsCStmtList absC) `thenSUs` \ trees ->
51 returnSUs ([StComment SLIT("Native Code")] : trees)
53 -- "target" munging things... ---
54 a2stix = amodeToStix target
55 a2stix' = amodeToStix' target
56 volsaves = volatileSaves target
57 volrestores = volatileRestores target
58 p2stix = primToStix target
59 macro_code = macroCode target
61 -- real code follows... ---------
64 Here we handle top-level things, like @CCodeBlock@s and
75 gentopcode (CCodeBlock label absC) =
76 gencode absC `thenSUs` \ code ->
77 returnSUs (StSegment TextSegment : StFunBegin label : code [StFunEnd label])
79 gentopcode stmt@(CStaticClosure label _ _ _) =
80 genCodeStaticClosure stmt `thenSUs` \ code ->
81 returnSUs (StSegment DataSegment : StLabel label : code [])
83 gentopcode stmt@(CRetUnVector _ _) = returnSUs []
85 gentopcode stmt@(CFlatRetVector label _) =
86 genCodeVecTbl stmt `thenSUs` \ code ->
87 returnSUs (StSegment TextSegment : code [StLabel label])
89 gentopcode stmt@(CClosureInfoAndCode cl_info slow Nothing _ _ _)
92 = genCodeInfoTable hp_rel a2stix stmt `thenSUs` \ itbl ->
93 returnSUs (StSegment TextSegment : itbl [])
96 = genCodeInfoTable hp_rel a2stix stmt `thenSUs` \ itbl ->
97 gencode slow `thenSUs` \ slow_code ->
98 returnSUs (StSegment TextSegment : itbl (StFunBegin slow_lbl :
99 slow_code [StFunEnd slow_lbl]))
101 slow_is_empty = not (maybeToBool (nonemptyAbsC slow))
102 slow_lbl = entryLabelFromCI cl_info
104 gentopcode stmt@(CClosureInfoAndCode cl_info slow (Just fast) _ _ _) =
105 -- ToDo: what if this is empty? ------------------------^^^^
106 genCodeInfoTable hp_rel a2stix stmt `thenSUs` \ itbl ->
107 gencode slow `thenSUs` \ slow_code ->
108 gencode fast `thenSUs` \ fast_code ->
109 returnSUs (StSegment TextSegment : itbl (StFunBegin slow_lbl :
110 slow_code (StFunEnd slow_lbl : StFunBegin fast_lbl :
111 fast_code [StFunEnd fast_lbl])))
113 slow_lbl = entryLabelFromCI cl_info
114 fast_lbl = fastLabelFromCI cl_info
117 gencode absC `thenSUs` \ code ->
118 returnSUs (StSegment TextSegment : code [])
122 Vector tables are trivial!
129 -> SUniqSM StixTreeList
131 genCodeVecTbl (CFlatRetVector label amodes) =
132 returnSUs (\xs -> vectbl : xs)
134 vectbl = StData PtrKind (reverse (map a2stix amodes))
138 Static closures are not so hard either.
145 -> SUniqSM StixTreeList
147 genCodeStaticClosure (CStaticClosure _ cl_info cost_centre amodes) =
148 returnSUs (\xs -> table : xs)
150 table = StData PtrKind (StCLbl info_lbl : body)
151 info_lbl = infoTableLabelFromCI cl_info
153 body = if closureUpdReqd cl_info then
154 take (max mIN_UPD_SIZE (length amodes')) (amodes' ++ zeros)
158 zeros = StInt 0 : zeros
160 amodes' = map amodeZeroVoid amodes
162 -- Watch out for VoidKinds...cf. PprAbsC
164 | getAmodeKind item == VoidKind = StInt 0
165 | otherwise = a2stix item
169 Now the individual AbstractC statements.
176 -> SUniqSM StixTreeList
180 @AbsCNop@s just disappear.
184 gencode AbsCNop = returnSUs id
188 OLD:@CComment@s are passed through as the corresponding @StComment@s.
192 --UNUSED:gencode (CComment s) = returnSUs (\xs -> StComment s : xs)
196 Split markers are a NOP in this land.
200 gencode CSplitMarker = returnSUs id
204 AbstractC instruction sequences are handled individually, and the
205 resulting StixTreeLists are joined together.
209 gencode (AbsCStmts c1 c2) =
210 gencode c1 `thenSUs` \ b1 ->
211 gencode c2 `thenSUs` \ b2 ->
216 Initialising closure headers in the heap...a fairly complex ordeal if
217 done properly. For now, we just set the info pointer, but we should
218 really take a peek at the flags to determine whether or not there are
219 other things to be done (setting cost centres, age headers, global
224 gencode (CInitHdr cl_info reg_rel _ _) =
226 lhs = a2stix (CVal reg_rel PtrKind)
227 lbl = infoTableLabelFromCI cl_info
229 returnSUs (\xs -> StAssign PtrKind lhs (StCLbl lbl) : xs)
233 Assignment, the curse of von Neumann, is the center of the code we
234 produce. In most cases, the type of the assignment is determined
235 by the type of the destination. However, when the destination can
236 have mixed types, the type of the assignment is ``StgWord'' (we use
237 PtrKind for lack of anything better). Think: do we also want a cast
238 of the source? Be careful about floats/doubles.
242 gencode (CAssign lhs rhs)
243 | getAmodeKind lhs == VoidKind = returnSUs id
245 let pk = getAmodeKind lhs
246 pk' = if mixedTypeLocn lhs && not (isFloatingKind pk) then IntKind else pk
250 returnSUs (\xs -> StAssign pk' lhs' rhs' : xs)
254 Unconditional jumps, including the special ``enter closure'' operation.
255 Note that the new entry convention requires that we load the InfoPtr (R2)
256 with the address of the info table before jumping to the entry code for Node.
260 gencode (CJump dest) =
261 returnSUs (\xs -> StJump (a2stix dest) : xs)
263 gencode (CFallThrough (CLbl lbl _)) =
264 returnSUs (\xs -> StFallThrough lbl : xs)
266 gencode (CReturn dest DirectReturn) =
267 returnSUs (\xs -> StJump (a2stix dest) : xs)
269 gencode (CReturn table (StaticVectoredReturn n)) =
270 returnSUs (\xs -> StJump dest : xs)
272 dest = StInd PtrKind (StIndex PtrKind (a2stix table)
273 (StInt (toInteger (-n-1))))
275 gencode (CReturn table (DynamicVectoredReturn am)) =
276 returnSUs (\xs -> StJump dest : xs)
278 dest = StInd PtrKind (StIndex PtrKind (a2stix table) dyn_off)
279 dyn_off = StPrim IntSubOp [StPrim IntNegOp [a2stix am], StInt 1]
283 Now the PrimOps, some of which may need caller-saves register wrappers.
287 gencode (COpStmt results op args liveness_mask vols)
288 -- ToDo (ADR?): use that liveness mask
289 | primOpNeedsWrapper op =
291 saves = volsaves vols
292 restores = volrestores vols
294 p2stix (nonVoid results) op (nonVoid args)
296 returnSUs (\xs -> saves ++ code (restores ++ xs))
298 | otherwise = p2stix (nonVoid results) op (nonVoid args)
300 nonVoid = filter ((/= VoidKind) . getAmodeKind)
304 Now the dreaded conditional jump.
306 Now the if statement. Almost *all* flow of control are of this form.
308 if (am==lit) { absC } else { absCdef }
322 gencode (CSwitch discrim alts deflt)
326 [(tag,alt_code)] -> case maybe_empty_deflt of
327 Nothing -> gencode alt_code
328 Just dc -> mkIfThenElse discrim tag alt_code dc
330 [(tag1@(MachInt i1 _), alt_code1),
331 (tag2@(MachInt i2 _), alt_code2)]
332 | deflt_is_empty && i1 == 0 && i2 == 1
333 -> mkIfThenElse discrim tag1 alt_code1 alt_code2
334 | deflt_is_empty && i1 == 1 && i2 == 0
335 -> mkIfThenElse discrim tag2 alt_code2 alt_code1
337 -- If the @discrim@ is simple, then this unfolding is safe.
338 other | simple_discrim -> mkSimpleSwitches discrim alts deflt
340 -- Otherwise, we need to do a bit of work.
341 other -> getSUnique `thenSUs` \ u ->
343 (CAssign (CTemp u pk) discrim)
344 (CSwitch (CTemp u pk) alts deflt))
347 maybe_empty_deflt = nonemptyAbsC deflt
348 deflt_is_empty = case maybe_empty_deflt of
352 pk = getAmodeKind discrim
354 simple_discrim = case discrim of
362 Finally, all of the disgusting AbstractC macros.
366 gencode (CMacroStmt macro args) = macro_code macro args
368 gencode (CCallProfCtrMacro macro _) =
369 returnSUs (\xs -> StComment macro : xs)
371 gencode (CCallProfCCMacro macro _) =
372 returnSUs (\xs -> StComment macro : xs)
376 Here, we generate a jump table if there are more than four (integer) alternatives and
377 the jump table occupancy is greater than 50%. Otherwise, we generate a binary
378 comparison tree. (Perhaps this could be tuned.)
382 intTag :: BasicLit -> Integer
383 intTag (MachChar c) = toInteger (ord c)
384 intTag (MachInt i _) = i
385 intTag _ = panic "intTag"
387 fltTag :: BasicLit -> Rational
389 fltTag (MachFloat f) = f
390 fltTag (MachDouble d) = d
391 fltTag _ = panic "fltTag"
396 -> CAddrMode -> [(BasicLit,AbstractC)] -> AbstractC
397 -> SUniqSM StixTreeList
399 mkSimpleSwitches am alts absC =
400 getUniqLabelNCG `thenSUs` \ udlbl ->
401 getUniqLabelNCG `thenSUs` \ ujlbl ->
403 joinedAlts = map (\ (tag,code) -> (tag, mkJoin code ujlbl)) alts
404 sortedAlts = naturalMergeSortLe leAlt joinedAlts
405 -- naturalMergeSortLe, because we often get sorted alts to begin with
407 lowTag = intTag (fst (head sortedAlts))
408 highTag = intTag (fst (last sortedAlts))
410 -- lowest and highest possible values the discriminant could take
411 lowest = if floating then targetMinDouble else targetMinInt
412 highest = if floating then targetMaxDouble else targetMaxInt
414 -- These should come from somewhere else, depending on the target arch
415 -- (Note that the floating point values aren't terribly important.)
417 targetMinDouble = MachDouble (-1.7976931348623157e+308)
418 targetMaxDouble = MachDouble (1.7976931348623157e+308)
419 targetMinInt = mkMachInt (-2147483647)
420 targetMaxInt = mkMachInt 2147483647
423 if not floating && choices > 4 && highTag - lowTag < toInteger (2 * choices) then
424 mkJumpTable am' sortedAlts lowTag highTag udlbl
426 mkBinaryTree am' floating sortedAlts choices lowest highest udlbl
428 `thenSUs` \ alt_code ->
429 gencode absC `thenSUs` \ dflt_code ->
431 returnSUs (\xs -> alt_code (StLabel udlbl : dflt_code (StLabel ujlbl : xs)))
434 floating = isFloatingKind (getAmodeKind am)
435 choices = length alts
437 (x@(MachChar _),_) `leAlt` (y,_) = intTag x <= intTag y
438 (x@(MachInt _ _),_) `leAlt` (y,_) = intTag x <= intTag y
439 (x,_) `leAlt` (y,_) = fltTag x <= fltTag y
443 We use jump tables when doing an integer switch on a relatively dense list of
444 alternatives. We expect to be given a list of alternatives, sorted by tag,
445 and a range of values for which we are to generate a table. Of course, the tags of
446 the alternatives should lie within the indicated range. The alternatives need
447 not cover the range; a default target is provided for the missing alternatives.
449 If a join is necessary after the switch, the alternatives should already finish
450 with a jump to the join point.
456 -> StixTree -- discriminant
457 -> [(BasicLit, AbstractC)] -- alternatives
458 -> Integer -- low tag
459 -> Integer -- high tag
460 -> CLabel -- default label
461 -> SUniqSM StixTreeList
464 mkJumpTable am alts lowTag highTag dflt =
465 getUniqLabelNCG `thenSUs` \ utlbl ->
466 mapSUs genLabel alts `thenSUs` \ branches ->
467 let cjmpLo = StCondJump dflt (StPrim IntLtOp [am, StInt lowTag])
468 cjmpHi = StCondJump dflt (StPrim IntGtOp [am, StInt highTag])
470 offset = StPrim IntSubOp [am, StInt lowTag]
471 jump = StJump (StInd PtrKind (StIndex PtrKind (StCLbl utlbl) offset))
474 table = StData PtrKind (mkTable branches [lowTag..highTag] [])
476 mapSUs mkBranch branches `thenSUs` \ alts ->
478 returnSUs (\xs -> cjmpLo : cjmpHi : jump :
479 StSegment DataSegment : tlbl : table :
480 StSegment TextSegment : foldr1 (.) alts xs)
483 genLabel x = getUniqLabelNCG `thenSUs` \ lbl -> returnSUs (lbl, x)
485 mkBranch (lbl,(_,alt)) =
486 gencode alt `thenSUs` \ alt_code ->
487 returnSUs (\xs -> StLabel lbl : alt_code xs)
489 mkTable _ [] tbl = reverse tbl
490 mkTable [] (x:xs) tbl = mkTable [] xs (StCLbl dflt : tbl)
491 mkTable alts@((lbl,(tag,_)):rest) (x:xs) tbl
492 | intTag tag == x = mkTable rest xs (StCLbl lbl : tbl)
493 | otherwise = mkTable alts xs (StCLbl dflt : tbl)
497 We generate binary comparison trees when a jump table is inappropriate.
498 We expect to be given a list of alternatives, sorted by tag, and for
499 convenience, the length of the alternative list. We recursively break
500 the list in half and do a comparison on the first tag of the second half
501 of the list. (Odd lists are broken so that the second half of the list
502 is longer.) We can handle either integer or floating kind alternatives,
503 so long as they are not mixed. (We assume that the type of the discriminant
504 determines the type of the alternatives.)
506 As with the jump table approach, if a join is necessary after the switch, the
507 alternatives should already finish with a jump to the join point.
513 -> StixTree -- discriminant
514 -> Bool -- floating point?
515 -> [(BasicLit, AbstractC)] -- alternatives
516 -> Int -- number of choices
517 -> BasicLit -- low tag
518 -> BasicLit -- high tag
519 -> CLabel -- default code label
520 -> SUniqSM StixTreeList
523 mkBinaryTree am floating [(tag,alt)] _ lowTag highTag udlbl
524 | rangeOfOne = gencode alt
526 let tag' = a2stix (CLit tag)
527 cmpOp = if floating then DoubleNeOp else IntNeOp
528 test = StPrim cmpOp [am, tag']
529 cjmp = StCondJump udlbl test
531 gencode alt `thenSUs` \ alt_code ->
532 returnSUs (\xs -> cjmp : alt_code xs)
535 rangeOfOne = not floating && intTag lowTag + 1 >= intTag highTag
536 -- When there is only one possible tag left in range, we skip the comparison
538 mkBinaryTree am floating alts choices lowTag highTag udlbl =
539 getUniqLabelNCG `thenSUs` \ uhlbl ->
540 let tag' = a2stix (CLit splitTag)
541 cmpOp = if floating then DoubleGeOp else IntGeOp
542 test = StPrim cmpOp [am, tag']
543 cjmp = StCondJump uhlbl test
545 mkBinaryTree am floating alts_lo half lowTag splitTag udlbl
546 `thenSUs` \ lo_code ->
547 mkBinaryTree am floating alts_hi (choices - half) splitTag highTag udlbl
548 `thenSUs` \ hi_code ->
550 returnSUs (\xs -> cjmp : lo_code (StLabel uhlbl : hi_code xs))
553 half = choices `div` 2
554 (alts_lo, alts_hi) = splitAt half alts
555 splitTag = fst (head alts_hi)
563 -> CAddrMode -- discriminant
565 -> AbstractC -- if-part
566 -> AbstractC -- else-part
567 -> SUniqSM StixTreeList
570 mkIfThenElse discrim tag alt deflt =
571 getUniqLabelNCG `thenSUs` \ ujlbl ->
572 getUniqLabelNCG `thenSUs` \ utlbl ->
573 let discrim' = a2stix discrim
574 tag' = a2stix (CLit tag)
575 cmpOp = if (isFloatingKind (getAmodeKind discrim)) then DoubleNeOp else IntNeOp
576 test = StPrim cmpOp [discrim', tag']
577 cjmp = StCondJump utlbl test
581 gencode (mkJoin alt ujlbl) `thenSUs` \ alt_code ->
582 gencode deflt `thenSUs` \ dflt_code ->
583 returnSUs (\xs -> cjmp : alt_code (dest : dflt_code (join : xs)))
585 mkJoin :: AbstractC -> CLabel -> AbstractC
588 | mightFallThrough code = mkAbsCStmts code (CJump (CLbl lbl PtrKind))
592 %---------------------------------------------------------------------------
594 This answers the question: Can the code fall through to the next
595 line(s) of code? This errs towards saying True if it can't choose,
596 because it is used for eliminating needless jumps. In other words, if
597 you might possibly {\em not} jump, then say yes to falling through.
600 mightFallThrough :: AbstractC -> Bool
602 mightFallThrough absC = ft absC True
604 ft AbsCNop if_empty = if_empty
606 ft (CJump _) if_empty = False
607 ft (CReturn _ _) if_empty = False
608 ft (CSwitch _ alts deflt) if_empty
609 = ft deflt if_empty ||
610 or [ft alt if_empty | (_,alt) <- alts]
612 ft (AbsCStmts c1 c2) if_empty = ft c2 (ft c1 if_empty)
613 ft _ if_empty = if_empty
615 {- Old algorithm, which called nonemptyAbsC for every subexpression! =========
616 fallThroughAbsC (AbsCStmts c1 c2) =
617 case nonemptyAbsC c2 of
618 Nothing -> fallThroughAbsC c1
619 Just x -> fallThroughAbsC x
620 fallThroughAbsC (CJump _) = False
621 fallThroughAbsC (CReturn _ _) = False
622 fallThroughAbsC (CSwitch _ choices deflt)
623 = (not (isEmptyAbsC deflt) && fallThroughAbsC deflt)
624 || or (map (fallThroughAbsC . snd) choices)
625 fallThroughAbsC other = True
627 isEmptyAbsC :: AbstractC -> Bool
628 isEmptyAbsC = not . maybeToBool . nonemptyAbsC
629 ================= End of old, quadratic, algorithm -}