2 % (c) The AQUA Project, Glasgow University, 1993-1996
6 #include "HsVersions.h"
8 module AbsCStixGen ( genCodeAbstractC ) where
18 import AbsCUtils ( getAmodeRep, mixedTypeLocn,
19 nonemptyAbsC, mkAbsCStmts, mkAbsCStmtList
21 import CgCompInfo ( mIN_UPD_SIZE )
22 import ClosureInfo ( infoTableLabelFromCI, entryLabelFromCI,
23 fastLabelFromCI, closureUpdReqd
25 import HeapOffs ( hpRelToInt )
26 import Literal ( Literal(..) )
27 import Maybes ( maybeToBool )
28 import OrdList ( OrdList )
29 import PrimOp ( primOpNeedsWrapper, PrimOp(..) )
30 import PrimRep ( isFloatingRep, PrimRep(..) )
31 import StixInfo ( genCodeInfoTable )
32 import StixMacro ( macroCode )
33 import StixPrim ( primCode, amodeToStix, amodeToStix' )
34 import UniqSupply ( returnUs, thenUs, mapUs, getUnique, UniqSM(..) )
35 import Util ( naturalMergeSortLe, panic )
37 #ifdef REALLY_HASKELL_1_3
38 ord = fromEnum :: Char -> Int
42 For each independent chunk of AbstractC code, we generate a list of
43 @StixTree@s, where each tree corresponds to a single Stix instruction.
44 We leave the chunks separated so that register allocation can be
45 performed locally within the chunk.
48 genCodeAbstractC :: AbstractC -> UniqSM [[StixTree]]
51 = mapUs gentopcode (mkAbsCStmtList absC) `thenUs` \ trees ->
52 returnUs ([StComment SLIT("Native Code")] : trees)
55 a2stix' = amodeToStix'
56 volsaves = volatileSaves
57 volrestores = volatileRestores
59 macro_code = macroCode
61 -- real code follows... ---------
64 Here we handle top-level things, like @CCodeBlock@s and
74 gentopcode (CCodeBlock label absC)
75 = gencode absC `thenUs` \ code ->
76 returnUs (StSegment TextSegment : StFunBegin label : code [StFunEnd label])
78 gentopcode stmt@(CStaticClosure label _ _ _)
79 = genCodeStaticClosure stmt `thenUs` \ code ->
80 returnUs (StSegment DataSegment : StLabel label : code [])
82 gentopcode stmt@(CRetUnVector _ _) = returnUs []
84 gentopcode stmt@(CFlatRetVector label _)
85 = genCodeVecTbl stmt `thenUs` \ code ->
86 returnUs (StSegment TextSegment : code [StLabel label])
88 gentopcode stmt@(CClosureInfoAndCode cl_info slow Nothing _ _ _)
91 = genCodeInfoTable stmt `thenUs` \ itbl ->
92 returnUs (StSegment TextSegment : itbl [])
95 = genCodeInfoTable stmt `thenUs` \ itbl ->
96 gencode slow `thenUs` \ slow_code ->
97 returnUs (StSegment TextSegment : itbl (StFunBegin slow_lbl :
98 slow_code [StFunEnd slow_lbl]))
100 slow_is_empty = not (maybeToBool (nonemptyAbsC slow))
101 slow_lbl = entryLabelFromCI cl_info
103 gentopcode stmt@(CClosureInfoAndCode cl_info slow (Just fast) _ _ _) =
104 -- ToDo: what if this is empty? ------------------------^^^^
105 genCodeInfoTable stmt `thenUs` \ itbl ->
106 gencode slow `thenUs` \ slow_code ->
107 gencode fast `thenUs` \ fast_code ->
108 returnUs (StSegment TextSegment : itbl (StFunBegin slow_lbl :
109 slow_code (StFunEnd slow_lbl : StFunBegin fast_lbl :
110 fast_code [StFunEnd fast_lbl])))
112 slow_lbl = entryLabelFromCI cl_info
113 fast_lbl = fastLabelFromCI cl_info
116 = gencode absC `thenUs` \ code ->
117 returnUs (StSegment TextSegment : code [])
121 Vector tables are trivial!
127 -> UniqSM StixTreeList
129 genCodeVecTbl (CFlatRetVector label amodes)
130 = returnUs (\xs -> vectbl : xs)
132 vectbl = StData PtrRep (reverse (map a2stix amodes))
136 Static closures are not so hard either.
142 -> UniqSM StixTreeList
144 genCodeStaticClosure (CStaticClosure _ cl_info cost_centre amodes)
145 = returnUs (\xs -> table : xs)
147 table = StData PtrRep (StCLbl info_lbl : body)
148 info_lbl = infoTableLabelFromCI cl_info
150 body = if closureUpdReqd cl_info then
151 take (max mIN_UPD_SIZE (length amodes')) (amodes' ++ zeros)
155 zeros = StInt 0 : zeros
157 amodes' = map amodeZeroVoid amodes
159 -- Watch out for VoidKinds...cf. PprAbsC
161 | getAmodeRep item == VoidRep = StInt 0
162 | otherwise = a2stix item
166 Now the individual AbstractC statements.
172 -> UniqSM StixTreeList
176 @AbsCNop@s just disappear.
180 gencode AbsCNop = returnUs id
184 Split markers are a NOP in this land.
188 gencode CSplitMarker = returnUs id
192 AbstractC instruction sequences are handled individually, and the
193 resulting StixTreeLists are joined together.
197 gencode (AbsCStmts c1 c2)
198 = gencode c1 `thenUs` \ b1 ->
199 gencode c2 `thenUs` \ b2 ->
204 Initialising closure headers in the heap...a fairly complex ordeal if
205 done properly. For now, we just set the info pointer, but we should
206 really take a peek at the flags to determine whether or not there are
207 other things to be done (setting cost centres, age headers, global
212 gencode (CInitHdr cl_info reg_rel _ _)
214 lhs = a2stix (CVal reg_rel PtrRep)
215 lbl = infoTableLabelFromCI cl_info
217 returnUs (\xs -> StAssign PtrRep lhs (StCLbl lbl) : xs)
221 Assignment, the curse of von Neumann, is the center of the code we
222 produce. In most cases, the type of the assignment is determined
223 by the type of the destination. However, when the destination can
224 have mixed types, the type of the assignment is ``StgWord'' (we use
225 PtrRep for lack of anything better). Think: do we also want a cast
226 of the source? Be careful about floats/doubles.
230 gencode (CAssign lhs rhs)
231 | getAmodeRep lhs == VoidRep = returnUs id
233 = let pk = getAmodeRep lhs
234 pk' = if mixedTypeLocn lhs && not (isFloatingRep pk) then IntRep else pk
238 returnUs (\xs -> StAssign pk' lhs' rhs' : xs)
242 Unconditional jumps, including the special ``enter closure'' operation.
243 Note that the new entry convention requires that we load the InfoPtr (R2)
244 with the address of the info table before jumping to the entry code for Node.
249 = returnUs (\xs -> StJump (a2stix dest) : xs)
251 gencode (CFallThrough (CLbl lbl _))
252 = returnUs (\xs -> StFallThrough lbl : xs)
254 gencode (CReturn dest DirectReturn)
255 = returnUs (\xs -> StJump (a2stix dest) : xs)
257 gencode (CReturn table (StaticVectoredReturn n))
258 = returnUs (\xs -> StJump dest : xs)
260 dest = StInd PtrRep (StIndex PtrRep (a2stix table)
261 (StInt (toInteger (-n-1))))
263 gencode (CReturn table (DynamicVectoredReturn am))
264 = returnUs (\xs -> StJump dest : xs)
266 dest = StInd PtrRep (StIndex PtrRep (a2stix table) dyn_off)
267 dyn_off = StPrim IntSubOp [StPrim IntNegOp [a2stix am], StInt 1]
271 Now the PrimOps, some of which may need caller-saves register wrappers.
275 gencode (COpStmt results op args liveness_mask vols)
276 -- ToDo (ADR?): use that liveness mask
277 | primOpNeedsWrapper op
279 saves = volsaves vols
280 restores = volrestores vols
282 p2stix (nonVoid results) op (nonVoid args)
284 returnUs (\xs -> saves ++ code (restores ++ xs))
286 | otherwise = p2stix (nonVoid results) op (nonVoid args)
288 nonVoid = filter ((/= VoidRep) . getAmodeRep)
292 Now the dreaded conditional jump.
294 Now the if statement. Almost *all* flow of control are of this form.
296 if (am==lit) { absC } else { absCdef }
310 gencode (CSwitch discrim alts deflt)
314 [(tag,alt_code)] -> case maybe_empty_deflt of
315 Nothing -> gencode alt_code
316 Just dc -> mkIfThenElse discrim tag alt_code dc
318 [(tag1@(MachInt i1 _), alt_code1),
319 (tag2@(MachInt i2 _), alt_code2)]
320 | deflt_is_empty && i1 == 0 && i2 == 1
321 -> mkIfThenElse discrim tag1 alt_code1 alt_code2
322 | deflt_is_empty && i1 == 1 && i2 == 0
323 -> mkIfThenElse discrim tag2 alt_code2 alt_code1
325 -- If the @discrim@ is simple, then this unfolding is safe.
326 other | simple_discrim -> mkSimpleSwitches discrim alts deflt
328 -- Otherwise, we need to do a bit of work.
329 other -> getUnique `thenUs` \ u ->
331 (CAssign (CTemp u pk) discrim)
332 (CSwitch (CTemp u pk) alts deflt))
335 maybe_empty_deflt = nonemptyAbsC deflt
336 deflt_is_empty = case maybe_empty_deflt of
340 pk = getAmodeRep discrim
342 simple_discrim = case discrim of
350 Finally, all of the disgusting AbstractC macros.
354 gencode (CMacroStmt macro args) = macro_code macro args
356 gencode (CCallProfCtrMacro macro _)
357 = returnUs (\xs -> StComment macro : xs)
359 gencode (CCallProfCCMacro macro _)
360 = returnUs (\xs -> StComment macro : xs)
364 Here, we generate a jump table if there are more than four (integer) alternatives and
365 the jump table occupancy is greater than 50%. Otherwise, we generate a binary
366 comparison tree. (Perhaps this could be tuned.)
370 intTag :: Literal -> Integer
371 intTag (MachChar c) = toInteger (ord c)
372 intTag (MachInt i _) = i
373 intTag _ = panic "intTag"
375 fltTag :: Literal -> Rational
377 fltTag (MachFloat f) = f
378 fltTag (MachDouble d) = d
379 fltTag _ = panic "fltTag"
383 :: CAddrMode -> [(Literal,AbstractC)] -> AbstractC
384 -> UniqSM StixTreeList
386 mkSimpleSwitches am alts absC
387 = getUniqLabelNCG `thenUs` \ udlbl ->
388 getUniqLabelNCG `thenUs` \ ujlbl ->
390 joinedAlts = map (\ (tag,code) -> (tag, mkJoin code ujlbl)) alts
391 sortedAlts = naturalMergeSortLe leAlt joinedAlts
392 -- naturalMergeSortLe, because we often get sorted alts to begin with
394 lowTag = intTag (fst (head sortedAlts))
395 highTag = intTag (fst (last sortedAlts))
397 -- lowest and highest possible values the discriminant could take
398 lowest = if floating then targetMinDouble else targetMinInt
399 highest = if floating then targetMaxDouble else targetMaxInt
402 if not floating && choices > 4 && highTag - lowTag < toInteger (2 * choices) then
403 mkJumpTable am' sortedAlts lowTag highTag udlbl
405 mkBinaryTree am' floating sortedAlts choices lowest highest udlbl
407 `thenUs` \ alt_code ->
408 gencode absC `thenUs` \ dflt_code ->
410 returnUs (\xs -> alt_code (StLabel udlbl : dflt_code (StLabel ujlbl : xs)))
413 floating = isFloatingRep (getAmodeRep am)
414 choices = length alts
416 (x@(MachChar _),_) `leAlt` (y,_) = intTag x <= intTag y
417 (x@(MachInt _ _),_) `leAlt` (y,_) = intTag x <= intTag y
418 (x,_) `leAlt` (y,_) = fltTag x <= fltTag y
422 We use jump tables when doing an integer switch on a relatively dense
423 list of alternatives. We expect to be given a list of alternatives,
424 sorted by tag, and a range of values for which we are to generate a
425 table. Of course, the tags of the alternatives should lie within the
426 indicated range. The alternatives need not cover the range; a default
427 target is provided for the missing alternatives.
429 If a join is necessary after the switch, the alternatives should
430 already finish with a jump to the join point.
435 :: StixTree -- discriminant
436 -> [(Literal, AbstractC)] -- alternatives
437 -> Integer -- low tag
438 -> Integer -- high tag
439 -> CLabel -- default label
440 -> UniqSM StixTreeList
443 mkJumpTable am alts lowTag highTag dflt
444 = getUniqLabelNCG `thenUs` \ utlbl ->
445 mapUs genLabel alts `thenUs` \ branches ->
446 let cjmpLo = StCondJump dflt (StPrim IntLtOp [am, StInt lowTag])
447 cjmpHi = StCondJump dflt (StPrim IntGtOp [am, StInt highTag])
449 offset = StPrim IntSubOp [am, StInt lowTag]
450 jump = StJump (StInd PtrRep (StIndex PtrRep (StCLbl utlbl) offset))
453 table = StData PtrRep (mkTable branches [lowTag..highTag] [])
455 mapUs mkBranch branches `thenUs` \ alts ->
457 returnUs (\xs -> cjmpLo : cjmpHi : jump :
458 StSegment DataSegment : tlbl : table :
459 StSegment TextSegment : foldr1 (.) alts xs)
462 genLabel x = getUniqLabelNCG `thenUs` \ lbl -> returnUs (lbl, x)
464 mkBranch (lbl,(_,alt)) =
465 gencode alt `thenUs` \ alt_code ->
466 returnUs (\xs -> StLabel lbl : alt_code xs)
468 mkTable _ [] tbl = reverse tbl
469 mkTable [] (x:xs) tbl = mkTable [] xs (StCLbl dflt : tbl)
470 mkTable alts@((lbl,(tag,_)):rest) (x:xs) tbl
471 | intTag tag == x = mkTable rest xs (StCLbl lbl : tbl)
472 | otherwise = mkTable alts xs (StCLbl dflt : tbl)
476 We generate binary comparison trees when a jump table is inappropriate.
477 We expect to be given a list of alternatives, sorted by tag, and for
478 convenience, the length of the alternative list. We recursively break
479 the list in half and do a comparison on the first tag of the second half
480 of the list. (Odd lists are broken so that the second half of the list
481 is longer.) We can handle either integer or floating kind alternatives,
482 so long as they are not mixed. (We assume that the type of the discriminant
483 determines the type of the alternatives.)
485 As with the jump table approach, if a join is necessary after the switch, the
486 alternatives should already finish with a jump to the join point.
491 :: StixTree -- discriminant
492 -> Bool -- floating point?
493 -> [(Literal, AbstractC)] -- alternatives
494 -> Int -- number of choices
495 -> Literal -- low tag
496 -> Literal -- high tag
497 -> CLabel -- default code label
498 -> UniqSM StixTreeList
501 mkBinaryTree am floating [(tag,alt)] _ lowTag highTag udlbl
502 | rangeOfOne = gencode alt
504 = let tag' = a2stix (CLit tag)
505 cmpOp = if floating then DoubleNeOp else IntNeOp
506 test = StPrim cmpOp [am, tag']
507 cjmp = StCondJump udlbl test
509 gencode alt `thenUs` \ alt_code ->
510 returnUs (\xs -> cjmp : alt_code xs)
513 rangeOfOne = not floating && intTag lowTag + 1 >= intTag highTag
514 -- When there is only one possible tag left in range, we skip the comparison
516 mkBinaryTree am floating alts choices lowTag highTag udlbl
517 = getUniqLabelNCG `thenUs` \ uhlbl ->
518 let tag' = a2stix (CLit splitTag)
519 cmpOp = if floating then DoubleGeOp else IntGeOp
520 test = StPrim cmpOp [am, tag']
521 cjmp = StCondJump uhlbl test
523 mkBinaryTree am floating alts_lo half lowTag splitTag udlbl
524 `thenUs` \ lo_code ->
525 mkBinaryTree am floating alts_hi (choices - half) splitTag highTag udlbl
526 `thenUs` \ hi_code ->
528 returnUs (\xs -> cjmp : lo_code (StLabel uhlbl : hi_code xs))
531 half = choices `div` 2
532 (alts_lo, alts_hi) = splitAt half alts
533 splitTag = fst (head alts_hi)
540 :: CAddrMode -- discriminant
542 -> AbstractC -- if-part
543 -> AbstractC -- else-part
544 -> UniqSM StixTreeList
547 mkIfThenElse discrim tag alt deflt
548 = getUniqLabelNCG `thenUs` \ ujlbl ->
549 getUniqLabelNCG `thenUs` \ utlbl ->
550 let discrim' = a2stix discrim
551 tag' = a2stix (CLit tag)
552 cmpOp = if (isFloatingRep (getAmodeRep discrim)) then DoubleNeOp else IntNeOp
553 test = StPrim cmpOp [discrim', tag']
554 cjmp = StCondJump utlbl test
558 gencode (mkJoin alt ujlbl) `thenUs` \ alt_code ->
559 gencode deflt `thenUs` \ dflt_code ->
560 returnUs (\xs -> cjmp : alt_code (dest : dflt_code (join : xs)))
562 mkJoin :: AbstractC -> CLabel -> AbstractC
565 | mightFallThrough code = mkAbsCStmts code (CJump (CLbl lbl PtrRep))
569 %---------------------------------------------------------------------------
571 This answers the question: Can the code fall through to the next
572 line(s) of code? This errs towards saying True if it can't choose,
573 because it is used for eliminating needless jumps. In other words, if
574 you might possibly {\em not} jump, then say yes to falling through.
577 mightFallThrough :: AbstractC -> Bool
579 mightFallThrough absC = ft absC True
581 ft AbsCNop if_empty = if_empty
583 ft (CJump _) if_empty = False
584 ft (CReturn _ _) if_empty = False
585 ft (CSwitch _ alts deflt) if_empty
586 = ft deflt if_empty ||
587 or [ft alt if_empty | (_,alt) <- alts]
589 ft (AbsCStmts c1 c2) if_empty = ft c2 (ft c1 if_empty)
590 ft _ if_empty = if_empty
592 {- Old algorithm, which called nonemptyAbsC for every subexpression! =========
593 fallThroughAbsC (AbsCStmts c1 c2)
594 = case nonemptyAbsC c2 of
595 Nothing -> fallThroughAbsC c1
596 Just x -> fallThroughAbsC x
597 fallThroughAbsC (CJump _) = False
598 fallThroughAbsC (CReturn _ _) = False
599 fallThroughAbsC (CSwitch _ choices deflt)
600 = (not (isEmptyAbsC deflt) && fallThroughAbsC deflt)
601 || or (map (fallThroughAbsC . snd) choices)
602 fallThroughAbsC other = True
604 isEmptyAbsC :: AbstractC -> Bool
605 isEmptyAbsC = not . maybeToBool . nonemptyAbsC
606 ================= End of old, quadratic, algorithm -}