2 % (c) The AQUA Project, Glasgow University, 1993-1998
6 module AbsCStixGen ( genCodeAbstractC ) where
8 #include "HsVersions.h"
10 import Ratio ( Rational )
16 import AbsCUtils ( getAmodeRep, mixedTypeLocn,
17 nonemptyAbsC, mkAbsCStmts, mkAbsCStmtList
19 import SMRep ( fixedItblSize,
21 rET_VEC_SMALL, rET_VEC_BIG
23 import Constants ( mIN_UPD_SIZE )
24 import CLabel ( CLabel, mkReturnInfoLabel, mkReturnPtLabel )
25 import ClosureInfo ( infoTableLabelFromCI, entryLabelFromCI,
26 fastLabelFromCI, closureUpdReqd
28 import Const ( Literal(..) )
29 import Maybes ( maybeToBool )
30 import PrimOp ( primOpNeedsWrapper, PrimOp(..) )
31 import PrimRep ( isFloatingRep, PrimRep(..) )
32 import StixInfo ( genCodeInfoTable, genBitmapInfoTable )
33 import StixMacro ( macroCode, checkCode )
34 import StixPrim ( primCode, amodeToStix, amodeToStix' )
35 import UniqSupply ( returnUs, thenUs, mapUs, getUniqueUs, UniqSM )
36 import Util ( naturalMergeSortLe )
37 import Panic ( panic )
38 import BitSet ( intBS )
40 #ifdef REALLY_HASKELL_1_3
41 ord = fromEnum :: Char -> Int
45 For each independent chunk of AbstractC code, we generate a list of
46 @StixTree@s, where each tree corresponds to a single Stix instruction.
47 We leave the chunks separated so that register allocation can be
48 performed locally within the chunk.
51 genCodeAbstractC :: AbstractC -> UniqSM [[StixTree]]
54 = mapUs gentopcode (mkAbsCStmtList absC) `thenUs` \ trees ->
55 returnUs ([StComment SLIT("Native Code")] : trees)
58 a2stix' = amodeToStix'
59 volsaves = volatileSaves
60 volrestores = volatileRestores
62 macro_code = macroCode
63 -- real code follows... ---------
66 Here we handle top-level things, like @CCodeBlock@s and
76 gentopcode (CCodeBlock label absC)
77 = gencode absC `thenUs` \ code ->
78 returnUs (StSegment TextSegment : StFunBegin label : code [StFunEnd label])
80 gentopcode stmt@(CStaticClosure label _ _ _)
81 = genCodeStaticClosure stmt `thenUs` \ code ->
82 returnUs (StSegment DataSegment : StLabel label : code [])
84 gentopcode stmt@(CRetVector label _ _ _)
85 = genCodeVecTbl stmt `thenUs` \ code ->
86 returnUs (StSegment TextSegment : code [StLabel label])
88 gentopcode stmt@(CRetDirect uniq absC srt liveness)
89 = gencode absC `thenUs` \ code ->
90 genBitmapInfoTable liveness srt closure_type False `thenUs` \ itbl ->
91 returnUs (StSegment TextSegment :
92 itbl (StLabel lbl_info : StLabel lbl_ret : code []))
94 lbl_info = mkReturnInfoLabel uniq
95 lbl_ret = mkReturnPtLabel uniq
96 closure_type = case liveness of
97 LvSmall _ -> rET_SMALL
100 gentopcode stmt@(CClosureInfoAndCode cl_info slow Nothing _ _)
103 = genCodeInfoTable stmt `thenUs` \ itbl ->
104 returnUs (StSegment TextSegment : itbl [])
107 = genCodeInfoTable stmt `thenUs` \ itbl ->
108 gencode slow `thenUs` \ slow_code ->
109 returnUs (StSegment TextSegment : itbl (StFunBegin slow_lbl :
110 slow_code [StFunEnd slow_lbl]))
112 slow_is_empty = not (maybeToBool (nonemptyAbsC slow))
113 slow_lbl = entryLabelFromCI cl_info
115 gentopcode stmt@(CClosureInfoAndCode cl_info slow (Just fast) _ _) =
116 -- ToDo: what if this is empty? ------------------------^^^^
117 genCodeInfoTable stmt `thenUs` \ itbl ->
118 gencode slow `thenUs` \ slow_code ->
119 gencode fast `thenUs` \ fast_code ->
120 returnUs (StSegment TextSegment : itbl (StFunBegin slow_lbl :
121 slow_code (StFunEnd slow_lbl : StFunBegin fast_lbl :
122 fast_code [StFunEnd fast_lbl])))
124 slow_lbl = entryLabelFromCI cl_info
125 fast_lbl = fastLabelFromCI cl_info
127 gentopcode stmt@(CSRT lbl closures)
128 = returnUs [ StSegment TextSegment
130 , StData DataPtrRep (map StCLbl closures)
133 gentopcode stmt@(CBitmap lbl mask)
134 = returnUs [ StSegment TextSegment
136 , StData WordRep (StInt (toInteger (length mask)) :
137 map (StInt . toInteger . intBS) mask)
141 = gencode absC `thenUs` \ code ->
142 returnUs (StSegment TextSegment : code [])
150 -> UniqSM StixTreeList
152 genCodeVecTbl (CRetVector label amodes srt liveness)
153 = genBitmapInfoTable liveness srt closure_type True `thenUs` \itbl ->
154 returnUs (\xs -> vectbl : itbl xs)
156 vectbl = StData PtrRep (reverse (map a2stix amodes))
157 closure_type = case liveness of
158 LvSmall _ -> rET_VEC_SMALL
159 LvLarge _ -> rET_VEC_BIG
167 -> UniqSM StixTreeList
169 genCodeStaticClosure (CStaticClosure _ cl_info cost_centre amodes)
170 = returnUs (\xs -> table ++ xs)
172 table = StData PtrRep [StCLbl (infoTableLabelFromCI cl_info)] :
173 map (\amode -> StData (getAmodeRep amode) [a2stix amode]) amodes ++
174 [StData PtrRep padding_wds]
176 -- always at least one padding word: this is the static link field
177 -- for the garbage collector.
178 padding_wds = if closureUpdReqd cl_info then
179 take (1 + max 0 (mIN_UPD_SIZE - length amodes)) zeros
183 zeros = StInt 0 : zeros
186 -- Watch out for VoidKinds...cf. PprAbsC
188 | getAmodeRep item == VoidRep = StInt 0
189 | otherwise = a2stix item
194 Now the individual AbstractC statements.
200 -> UniqSM StixTreeList
204 @AbsCNop@s just disappear.
208 gencode AbsCNop = returnUs id
212 Split markers are a NOP in this land.
216 gencode CSplitMarker = returnUs id
220 AbstractC instruction sequences are handled individually, and the
221 resulting StixTreeLists are joined together.
225 gencode (AbsCStmts c1 c2)
226 = gencode c1 `thenUs` \ b1 ->
227 gencode c2 `thenUs` \ b2 ->
232 Initialising closure headers in the heap...a fairly complex ordeal if
233 done properly. For now, we just set the info pointer, but we should
234 really take a peek at the flags to determine whether or not there are
235 other things to be done (setting cost centres, age headers, global
240 gencode (CInitHdr cl_info reg_rel _)
242 lhs = a2stix (CVal reg_rel PtrRep)
243 lbl = infoTableLabelFromCI cl_info
245 returnUs (\xs -> StAssign PtrRep lhs (StCLbl lbl) : xs)
253 gencode (CCheck macro args assts)
254 = gencode assts `thenUs` \assts_stix ->
255 checkCode macro args assts_stix
259 Assignment, the curse of von Neumann, is the center of the code we
260 produce. In most cases, the type of the assignment is determined
261 by the type of the destination. However, when the destination can
262 have mixed types, the type of the assignment is ``StgWord'' (we use
263 PtrRep for lack of anything better). Think: do we also want a cast
264 of the source? Be careful about floats/doubles.
268 gencode (CAssign lhs rhs)
269 | getAmodeRep lhs == VoidRep = returnUs id
271 = let pk = getAmodeRep lhs
272 pk' = if mixedTypeLocn lhs && not (isFloatingRep pk) then IntRep else pk
276 returnUs (\xs -> StAssign pk' lhs' rhs' : xs)
280 Unconditional jumps, including the special ``enter closure'' operation.
281 Note that the new entry convention requires that we load the InfoPtr (R2)
282 with the address of the info table before jumping to the entry code for Node.
284 For a vectored return, we must subtract the size of the info table to
285 get at the return vector. This depends on the size of the info table,
286 which varies depending on whether we're profiling etc.
291 = returnUs (\xs -> StJump (a2stix dest) : xs)
293 gencode (CFallThrough (CLbl lbl _))
294 = returnUs (\xs -> StFallThrough lbl : xs)
296 gencode (CReturn dest DirectReturn)
297 = returnUs (\xs -> StJump (a2stix dest) : xs)
299 gencode (CReturn table (StaticVectoredReturn n))
300 = returnUs (\xs -> StJump dest : xs)
302 dest = StInd PtrRep (StIndex PtrRep (a2stix table)
303 (StInt (toInteger (-n-fixedItblSize-1))))
305 gencode (CReturn table (DynamicVectoredReturn am))
306 = returnUs (\xs -> StJump dest : xs)
308 dest = StInd PtrRep (StIndex PtrRep (a2stix table) dyn_off)
309 dyn_off = StPrim IntSubOp [StPrim IntNegOp [a2stix am],
310 StInt (toInteger (fixedItblSize+1))]
314 Now the PrimOps, some of which may need caller-saves register wrappers.
318 gencode (COpStmt results op args vols)
319 -- ToDo (ADR?): use that liveness mask
320 | primOpNeedsWrapper op
322 saves = volsaves vols
323 restores = volrestores vols
325 p2stix (nonVoid results) op (nonVoid args)
327 returnUs (\xs -> saves ++ code (restores ++ xs))
329 | otherwise = p2stix (nonVoid results) op (nonVoid args)
331 nonVoid = filter ((/= VoidRep) . getAmodeRep)
335 Now the dreaded conditional jump.
337 Now the if statement. Almost *all* flow of control are of this form.
339 if (am==lit) { absC } else { absCdef }
353 gencode (CSwitch discrim alts deflt)
357 [(tag,alt_code)] -> case maybe_empty_deflt of
358 Nothing -> gencode alt_code
359 Just dc -> mkIfThenElse discrim tag alt_code dc
361 [(tag1@(MachInt i1 _), alt_code1),
362 (tag2@(MachInt i2 _), alt_code2)]
363 | deflt_is_empty && i1 == 0 && i2 == 1
364 -> mkIfThenElse discrim tag1 alt_code1 alt_code2
365 | deflt_is_empty && i1 == 1 && i2 == 0
366 -> mkIfThenElse discrim tag2 alt_code2 alt_code1
368 -- If the @discrim@ is simple, then this unfolding is safe.
369 other | simple_discrim -> mkSimpleSwitches discrim alts deflt
371 -- Otherwise, we need to do a bit of work.
372 other -> getUniqueUs `thenUs` \ u ->
374 (CAssign (CTemp u pk) discrim)
375 (CSwitch (CTemp u pk) alts deflt))
378 maybe_empty_deflt = nonemptyAbsC deflt
379 deflt_is_empty = case maybe_empty_deflt of
383 pk = getAmodeRep discrim
385 simple_discrim = case discrim of
393 Finally, all of the disgusting AbstractC macros.
397 gencode (CMacroStmt macro args) = macro_code macro args
399 gencode (CCallProfCtrMacro macro _)
400 = returnUs (\xs -> StComment macro : xs)
402 gencode (CCallProfCCMacro macro _)
403 = returnUs (\xs -> StComment macro : xs)
407 Here, we generate a jump table if there are more than four (integer)
408 alternatives and the jump table occupancy is greater than 50%.
409 Otherwise, we generate a binary comparison tree. (Perhaps this could
414 intTag :: Literal -> Integer
415 intTag (MachChar c) = fromInt (ord c)
416 intTag (MachInt i _) = i
417 intTag _ = panic "intTag"
419 fltTag :: Literal -> Rational
421 fltTag (MachFloat f) = f
422 fltTag (MachDouble d) = d
423 fltTag _ = panic "fltTag"
427 :: CAddrMode -> [(Literal,AbstractC)] -> AbstractC
428 -> UniqSM StixTreeList
430 mkSimpleSwitches am alts absC
431 = getUniqLabelNCG `thenUs` \ udlbl ->
432 getUniqLabelNCG `thenUs` \ ujlbl ->
434 joinedAlts = map (\ (tag,code) -> (tag, mkJoin code ujlbl)) alts
435 sortedAlts = naturalMergeSortLe leAlt joinedAlts
436 -- naturalMergeSortLe, because we often get sorted alts to begin with
438 lowTag = intTag (fst (head sortedAlts))
439 highTag = intTag (fst (last sortedAlts))
441 -- lowest and highest possible values the discriminant could take
442 lowest = if floating then targetMinDouble else targetMinInt
443 highest = if floating then targetMaxDouble else targetMaxInt
446 if not floating && choices > 4 && highTag - lowTag < toInteger (2 * choices) then
447 mkJumpTable am' sortedAlts lowTag highTag udlbl
449 mkBinaryTree am' floating sortedAlts choices lowest highest udlbl
451 `thenUs` \ alt_code ->
452 gencode absC `thenUs` \ dflt_code ->
454 returnUs (\xs -> alt_code (StLabel udlbl : dflt_code (StLabel ujlbl : xs)))
457 floating = isFloatingRep (getAmodeRep am)
458 choices = length alts
460 (x@(MachChar _),_) `leAlt` (y,_) = intTag x <= intTag y
461 (x@(MachInt _ _),_) `leAlt` (y,_) = intTag x <= intTag y
462 (x,_) `leAlt` (y,_) = fltTag x <= fltTag y
466 We use jump tables when doing an integer switch on a relatively dense
467 list of alternatives. We expect to be given a list of alternatives,
468 sorted by tag, and a range of values for which we are to generate a
469 table. Of course, the tags of the alternatives should lie within the
470 indicated range. The alternatives need not cover the range; a default
471 target is provided for the missing alternatives.
473 If a join is necessary after the switch, the alternatives should
474 already finish with a jump to the join point.
479 :: StixTree -- discriminant
480 -> [(Literal, AbstractC)] -- alternatives
481 -> Integer -- low tag
482 -> Integer -- high tag
483 -> CLabel -- default label
484 -> UniqSM StixTreeList
487 mkJumpTable am alts lowTag highTag dflt
488 = getUniqLabelNCG `thenUs` \ utlbl ->
489 mapUs genLabel alts `thenUs` \ branches ->
490 let cjmpLo = StCondJump dflt (StPrim IntLtOp [am, StInt (toInteger lowTag)])
491 cjmpHi = StCondJump dflt (StPrim IntGtOp [am, StInt (toInteger highTag)])
493 offset = StPrim IntSubOp [am, StInt lowTag]
495 jump = StJump (StInd PtrRep (StIndex PtrRep (StCLbl utlbl) offset))
497 table = StData PtrRep (mkTable branches [lowTag..highTag] [])
499 mapUs mkBranch branches `thenUs` \ alts ->
501 returnUs (\xs -> cjmpLo : cjmpHi : jump :
502 StSegment DataSegment : tlbl : table :
503 StSegment TextSegment : foldr1 (.) alts xs)
506 genLabel x = getUniqLabelNCG `thenUs` \ lbl -> returnUs (lbl, x)
508 mkBranch (lbl,(_,alt)) =
509 gencode alt `thenUs` \ alt_code ->
510 returnUs (\xs -> StLabel lbl : alt_code xs)
512 mkTable _ [] tbl = reverse tbl
513 mkTable [] (x:xs) tbl = mkTable [] xs (StCLbl dflt : tbl)
514 mkTable alts@((lbl,(tag,_)):rest) (x:xs) tbl
515 | intTag tag == x = mkTable rest xs (StCLbl lbl : tbl)
516 | otherwise = mkTable alts xs (StCLbl dflt : tbl)
520 We generate binary comparison trees when a jump table is inappropriate.
521 We expect to be given a list of alternatives, sorted by tag, and for
522 convenience, the length of the alternative list. We recursively break
523 the list in half and do a comparison on the first tag of the second half
524 of the list. (Odd lists are broken so that the second half of the list
525 is longer.) We can handle either integer or floating kind alternatives,
526 so long as they are not mixed. (We assume that the type of the discriminant
527 determines the type of the alternatives.)
529 As with the jump table approach, if a join is necessary after the switch, the
530 alternatives should already finish with a jump to the join point.
535 :: StixTree -- discriminant
536 -> Bool -- floating point?
537 -> [(Literal, AbstractC)] -- alternatives
538 -> Int -- number of choices
539 -> Literal -- low tag
540 -> Literal -- high tag
541 -> CLabel -- default code label
542 -> UniqSM StixTreeList
545 mkBinaryTree am floating [(tag,alt)] _ lowTag highTag udlbl
546 | rangeOfOne = gencode alt
548 = let tag' = a2stix (CLit tag)
549 cmpOp = if floating then DoubleNeOp else IntNeOp
550 test = StPrim cmpOp [am, tag']
551 cjmp = StCondJump udlbl test
553 gencode alt `thenUs` \ alt_code ->
554 returnUs (\xs -> cjmp : alt_code xs)
557 rangeOfOne = not floating && intTag lowTag + 1 >= intTag highTag
558 -- When there is only one possible tag left in range, we skip the comparison
560 mkBinaryTree am floating alts choices lowTag highTag udlbl
561 = getUniqLabelNCG `thenUs` \ uhlbl ->
562 let tag' = a2stix (CLit splitTag)
563 cmpOp = if floating then DoubleGeOp else IntGeOp
564 test = StPrim cmpOp [am, tag']
565 cjmp = StCondJump uhlbl test
567 mkBinaryTree am floating alts_lo half lowTag splitTag udlbl
568 `thenUs` \ lo_code ->
569 mkBinaryTree am floating alts_hi (choices - half) splitTag highTag udlbl
570 `thenUs` \ hi_code ->
572 returnUs (\xs -> cjmp : lo_code (StLabel uhlbl : hi_code xs))
575 half = choices `div` 2
576 (alts_lo, alts_hi) = splitAt half alts
577 splitTag = fst (head alts_hi)
584 :: CAddrMode -- discriminant
586 -> AbstractC -- if-part
587 -> AbstractC -- else-part
588 -> UniqSM StixTreeList
591 mkIfThenElse discrim tag alt deflt
592 = getUniqLabelNCG `thenUs` \ ujlbl ->
593 getUniqLabelNCG `thenUs` \ utlbl ->
594 let discrim' = a2stix discrim
595 tag' = a2stix (CLit tag)
596 cmpOp = if (isFloatingRep (getAmodeRep discrim)) then DoubleNeOp else IntNeOp
597 test = StPrim cmpOp [discrim', tag']
598 cjmp = StCondJump utlbl test
602 gencode (mkJoin alt ujlbl) `thenUs` \ alt_code ->
603 gencode deflt `thenUs` \ dflt_code ->
604 returnUs (\xs -> cjmp : alt_code (dest : dflt_code (join : xs)))
606 mkJoin :: AbstractC -> CLabel -> AbstractC
609 | mightFallThrough code = mkAbsCStmts code (CJump (CLbl lbl PtrRep))
613 %---------------------------------------------------------------------------
615 This answers the question: Can the code fall through to the next
616 line(s) of code? This errs towards saying True if it can't choose,
617 because it is used for eliminating needless jumps. In other words, if
618 you might possibly {\em not} jump, then say yes to falling through.
621 mightFallThrough :: AbstractC -> Bool
623 mightFallThrough absC = ft absC True
625 ft AbsCNop if_empty = if_empty
627 ft (CJump _) if_empty = False
628 ft (CReturn _ _) if_empty = False
629 ft (CSwitch _ alts deflt) if_empty
630 = ft deflt if_empty ||
631 or [ft alt if_empty | (_,alt) <- alts]
633 ft (AbsCStmts c1 c2) if_empty = ft c2 (ft c1 if_empty)
634 ft _ if_empty = if_empty
636 {- Old algorithm, which called nonemptyAbsC for every subexpression! =========
637 fallThroughAbsC (AbsCStmts c1 c2)
638 = case nonemptyAbsC c2 of
639 Nothing -> fallThroughAbsC c1
640 Just x -> fallThroughAbsC x
641 fallThroughAbsC (CJump _) = False
642 fallThroughAbsC (CReturn _ _) = False
643 fallThroughAbsC (CSwitch _ choices deflt)
644 = (not (isEmptyAbsC deflt) && fallThroughAbsC deflt)
645 || or (map (fallThroughAbsC . snd) choices)
646 fallThroughAbsC other = True
648 isEmptyAbsC :: AbstractC -> Bool
649 isEmptyAbsC = not . maybeToBool . nonemptyAbsC
650 ================= End of old, quadratic, algorithm -}