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, panic )
37 import BitSet ( intBS )
39 #ifdef REALLY_HASKELL_1_3
40 ord = fromEnum :: Char -> Int
44 For each independent chunk of AbstractC code, we generate a list of
45 @StixTree@s, where each tree corresponds to a single Stix instruction.
46 We leave the chunks separated so that register allocation can be
47 performed locally within the chunk.
50 genCodeAbstractC :: AbstractC -> UniqSM [[StixTree]]
53 = mapUs gentopcode (mkAbsCStmtList absC) `thenUs` \ trees ->
54 returnUs ([StComment SLIT("Native Code")] : trees)
57 a2stix' = amodeToStix'
58 volsaves = volatileSaves
59 volrestores = volatileRestores
61 macro_code = macroCode
62 -- real code follows... ---------
65 Here we handle top-level things, like @CCodeBlock@s and
75 gentopcode (CCodeBlock label absC)
76 = gencode absC `thenUs` \ code ->
77 returnUs (StSegment TextSegment : StFunBegin label : code [StFunEnd label])
79 gentopcode stmt@(CStaticClosure label _ _ _)
80 = genCodeStaticClosure stmt `thenUs` \ code ->
81 returnUs (StSegment DataSegment : StLabel label : code [])
83 gentopcode stmt@(CRetVector label _ _ _)
84 = genCodeVecTbl stmt `thenUs` \ code ->
85 returnUs (StSegment TextSegment : code [StLabel label])
87 gentopcode stmt@(CRetDirect uniq absC srt liveness)
88 = gencode absC `thenUs` \ code ->
89 genBitmapInfoTable liveness srt closure_type False `thenUs` \ itbl ->
90 returnUs (StSegment TextSegment :
91 itbl (StLabel lbl_info : StLabel lbl_ret : code []))
93 lbl_info = mkReturnInfoLabel uniq
94 lbl_ret = mkReturnPtLabel uniq
95 closure_type = case liveness of
96 LvSmall _ -> rET_SMALL
99 gentopcode stmt@(CClosureInfoAndCode cl_info slow Nothing _ _)
102 = genCodeInfoTable stmt `thenUs` \ itbl ->
103 returnUs (StSegment TextSegment : itbl [])
106 = genCodeInfoTable stmt `thenUs` \ itbl ->
107 gencode slow `thenUs` \ slow_code ->
108 returnUs (StSegment TextSegment : itbl (StFunBegin slow_lbl :
109 slow_code [StFunEnd slow_lbl]))
111 slow_is_empty = not (maybeToBool (nonemptyAbsC slow))
112 slow_lbl = entryLabelFromCI cl_info
114 gentopcode stmt@(CClosureInfoAndCode cl_info slow (Just fast) _ _) =
115 -- ToDo: what if this is empty? ------------------------^^^^
116 genCodeInfoTable stmt `thenUs` \ itbl ->
117 gencode slow `thenUs` \ slow_code ->
118 gencode fast `thenUs` \ fast_code ->
119 returnUs (StSegment TextSegment : itbl (StFunBegin slow_lbl :
120 slow_code (StFunEnd slow_lbl : StFunBegin fast_lbl :
121 fast_code [StFunEnd fast_lbl])))
123 slow_lbl = entryLabelFromCI cl_info
124 fast_lbl = fastLabelFromCI cl_info
126 gentopcode stmt@(CSRT lbl closures)
127 = returnUs [ StSegment TextSegment
129 , StData DataPtrRep (map StCLbl closures)
132 gentopcode stmt@(CBitmap lbl mask)
133 = returnUs [ StSegment TextSegment
135 , StData WordRep (StInt (toInteger (length mask)) :
136 map (StInt . toInteger . intBS) mask)
140 = gencode absC `thenUs` \ code ->
141 returnUs (StSegment TextSegment : code [])
149 -> UniqSM StixTreeList
151 genCodeVecTbl (CRetVector label amodes srt liveness)
152 = genBitmapInfoTable liveness srt closure_type True `thenUs` \itbl ->
153 returnUs (\xs -> vectbl : itbl xs)
155 vectbl = StData PtrRep (reverse (map a2stix amodes))
156 closure_type = case liveness of
157 LvSmall _ -> rET_VEC_SMALL
158 LvLarge _ -> rET_VEC_BIG
166 -> UniqSM StixTreeList
168 genCodeStaticClosure (CStaticClosure _ cl_info cost_centre amodes)
169 = returnUs (\xs -> table : xs)
171 table = StData PtrRep (StCLbl info_lbl : body)
172 info_lbl = infoTableLabelFromCI cl_info
174 -- always at least one padding word: this is the static link field
175 -- for the garbage collector.
176 body = if closureUpdReqd cl_info then
177 take (1 + max mIN_UPD_SIZE (length amodes')) (amodes' ++ zeros)
181 zeros = StInt 0 : zeros
183 amodes' = map amodeZeroVoid amodes
185 -- Watch out for VoidKinds...cf. PprAbsC
187 | getAmodeRep item == VoidRep = StInt 0
188 | otherwise = a2stix item
192 Now the individual AbstractC statements.
198 -> UniqSM StixTreeList
202 @AbsCNop@s just disappear.
206 gencode AbsCNop = returnUs id
210 Split markers are a NOP in this land.
214 gencode CSplitMarker = returnUs id
218 AbstractC instruction sequences are handled individually, and the
219 resulting StixTreeLists are joined together.
223 gencode (AbsCStmts c1 c2)
224 = gencode c1 `thenUs` \ b1 ->
225 gencode c2 `thenUs` \ b2 ->
230 Initialising closure headers in the heap...a fairly complex ordeal if
231 done properly. For now, we just set the info pointer, but we should
232 really take a peek at the flags to determine whether or not there are
233 other things to be done (setting cost centres, age headers, global
238 gencode (CInitHdr cl_info reg_rel _)
240 lhs = a2stix (CVal reg_rel PtrRep)
241 lbl = infoTableLabelFromCI cl_info
243 returnUs (\xs -> StAssign PtrRep lhs (StCLbl lbl) : xs)
251 gencode (CCheck macro args assts)
252 = gencode assts `thenUs` \assts_stix ->
253 checkCode macro args assts_stix
257 Assignment, the curse of von Neumann, is the center of the code we
258 produce. In most cases, the type of the assignment is determined
259 by the type of the destination. However, when the destination can
260 have mixed types, the type of the assignment is ``StgWord'' (we use
261 PtrRep for lack of anything better). Think: do we also want a cast
262 of the source? Be careful about floats/doubles.
266 gencode (CAssign lhs rhs)
267 | getAmodeRep lhs == VoidRep = returnUs id
269 = let pk = getAmodeRep lhs
270 pk' = if mixedTypeLocn lhs && not (isFloatingRep pk) then IntRep else pk
274 returnUs (\xs -> StAssign pk' lhs' rhs' : xs)
278 Unconditional jumps, including the special ``enter closure'' operation.
279 Note that the new entry convention requires that we load the InfoPtr (R2)
280 with the address of the info table before jumping to the entry code for Node.
282 For a vectored return, we must subtract the size of the info table to
283 get at the return vector. This depends on the size of the info table,
284 which varies depending on whether we're profiling etc.
289 = returnUs (\xs -> StJump (a2stix dest) : xs)
291 gencode (CFallThrough (CLbl lbl _))
292 = returnUs (\xs -> StFallThrough lbl : xs)
294 gencode (CReturn dest DirectReturn)
295 = returnUs (\xs -> StJump (a2stix dest) : xs)
297 gencode (CReturn table (StaticVectoredReturn n))
298 = returnUs (\xs -> StJump dest : xs)
300 dest = StInd PtrRep (StIndex PtrRep (a2stix table)
301 (StInt (toInteger (-n-fixedItblSize-1))))
303 gencode (CReturn table (DynamicVectoredReturn am))
304 = returnUs (\xs -> StJump dest : xs)
306 dest = StInd PtrRep (StIndex PtrRep (a2stix table) dyn_off)
307 dyn_off = StPrim IntSubOp [StPrim IntNegOp [a2stix am],
308 StInt (toInteger (fixedItblSize+1))]
312 Now the PrimOps, some of which may need caller-saves register wrappers.
316 gencode (COpStmt results op args vols)
317 -- ToDo (ADR?): use that liveness mask
318 | primOpNeedsWrapper op
320 saves = volsaves vols
321 restores = volrestores vols
323 p2stix (nonVoid results) op (nonVoid args)
325 returnUs (\xs -> saves ++ code (restores ++ xs))
327 | otherwise = p2stix (nonVoid results) op (nonVoid args)
329 nonVoid = filter ((/= VoidRep) . getAmodeRep)
333 Now the dreaded conditional jump.
335 Now the if statement. Almost *all* flow of control are of this form.
337 if (am==lit) { absC } else { absCdef }
351 gencode (CSwitch discrim alts deflt)
355 [(tag,alt_code)] -> case maybe_empty_deflt of
356 Nothing -> gencode alt_code
357 Just dc -> mkIfThenElse discrim tag alt_code dc
359 [(tag1@(MachInt i1 _), alt_code1),
360 (tag2@(MachInt i2 _), alt_code2)]
361 | deflt_is_empty && i1 == 0 && i2 == 1
362 -> mkIfThenElse discrim tag1 alt_code1 alt_code2
363 | deflt_is_empty && i1 == 1 && i2 == 0
364 -> mkIfThenElse discrim tag2 alt_code2 alt_code1
366 -- If the @discrim@ is simple, then this unfolding is safe.
367 other | simple_discrim -> mkSimpleSwitches discrim alts deflt
369 -- Otherwise, we need to do a bit of work.
370 other -> getUniqueUs `thenUs` \ u ->
372 (CAssign (CTemp u pk) discrim)
373 (CSwitch (CTemp u pk) alts deflt))
376 maybe_empty_deflt = nonemptyAbsC deflt
377 deflt_is_empty = case maybe_empty_deflt of
381 pk = getAmodeRep discrim
383 simple_discrim = case discrim of
391 Finally, all of the disgusting AbstractC macros.
395 gencode (CMacroStmt macro args) = macro_code macro args
397 gencode (CCallProfCtrMacro macro _)
398 = returnUs (\xs -> StComment macro : xs)
400 gencode (CCallProfCCMacro macro _)
401 = returnUs (\xs -> StComment macro : xs)
405 Here, we generate a jump table if there are more than four (integer)
406 alternatives and the jump table occupancy is greater than 50%.
407 Otherwise, we generate a binary comparison tree. (Perhaps this could
412 intTag :: Literal -> Integer
413 intTag (MachChar c) = fromInt (ord c)
414 intTag (MachInt i _) = i
415 intTag _ = panic "intTag"
417 fltTag :: Literal -> Rational
419 fltTag (MachFloat f) = f
420 fltTag (MachDouble d) = d
421 fltTag _ = panic "fltTag"
425 :: CAddrMode -> [(Literal,AbstractC)] -> AbstractC
426 -> UniqSM StixTreeList
428 mkSimpleSwitches am alts absC
429 = getUniqLabelNCG `thenUs` \ udlbl ->
430 getUniqLabelNCG `thenUs` \ ujlbl ->
432 joinedAlts = map (\ (tag,code) -> (tag, mkJoin code ujlbl)) alts
433 sortedAlts = naturalMergeSortLe leAlt joinedAlts
434 -- naturalMergeSortLe, because we often get sorted alts to begin with
436 lowTag = intTag (fst (head sortedAlts))
437 highTag = intTag (fst (last sortedAlts))
439 -- lowest and highest possible values the discriminant could take
440 lowest = if floating then targetMinDouble else targetMinInt
441 highest = if floating then targetMaxDouble else targetMaxInt
444 if not floating && choices > 4 && highTag - lowTag < toInteger (2 * choices) then
445 mkJumpTable am' sortedAlts lowTag highTag udlbl
447 mkBinaryTree am' floating sortedAlts choices lowest highest udlbl
449 `thenUs` \ alt_code ->
450 gencode absC `thenUs` \ dflt_code ->
452 returnUs (\xs -> alt_code (StLabel udlbl : dflt_code (StLabel ujlbl : xs)))
455 floating = isFloatingRep (getAmodeRep am)
456 choices = length alts
458 (x@(MachChar _),_) `leAlt` (y,_) = intTag x <= intTag y
459 (x@(MachInt _ _),_) `leAlt` (y,_) = intTag x <= intTag y
460 (x,_) `leAlt` (y,_) = fltTag x <= fltTag y
464 We use jump tables when doing an integer switch on a relatively dense
465 list of alternatives. We expect to be given a list of alternatives,
466 sorted by tag, and a range of values for which we are to generate a
467 table. Of course, the tags of the alternatives should lie within the
468 indicated range. The alternatives need not cover the range; a default
469 target is provided for the missing alternatives.
471 If a join is necessary after the switch, the alternatives should
472 already finish with a jump to the join point.
477 :: StixTree -- discriminant
478 -> [(Literal, AbstractC)] -- alternatives
479 -> Integer -- low tag
480 -> Integer -- high tag
481 -> CLabel -- default label
482 -> UniqSM StixTreeList
485 mkJumpTable am alts lowTag highTag dflt
486 = getUniqLabelNCG `thenUs` \ utlbl ->
487 mapUs genLabel alts `thenUs` \ branches ->
488 let cjmpLo = StCondJump dflt (StPrim IntLtOp [am, StInt (toInteger lowTag)])
489 cjmpHi = StCondJump dflt (StPrim IntGtOp [am, StInt (toInteger highTag)])
491 offset = StPrim IntSubOp [am, StInt lowTag]
493 jump = StJump (StInd PtrRep (StIndex PtrRep (StCLbl utlbl) offset))
495 table = StData PtrRep (mkTable branches [lowTag..highTag] [])
497 mapUs mkBranch branches `thenUs` \ alts ->
499 returnUs (\xs -> cjmpLo : cjmpHi : jump :
500 StSegment DataSegment : tlbl : table :
501 StSegment TextSegment : foldr1 (.) alts xs)
504 genLabel x = getUniqLabelNCG `thenUs` \ lbl -> returnUs (lbl, x)
506 mkBranch (lbl,(_,alt)) =
507 gencode alt `thenUs` \ alt_code ->
508 returnUs (\xs -> StLabel lbl : alt_code xs)
510 mkTable _ [] tbl = reverse tbl
511 mkTable [] (x:xs) tbl = mkTable [] xs (StCLbl dflt : tbl)
512 mkTable alts@((lbl,(tag,_)):rest) (x:xs) tbl
513 | intTag tag == x = mkTable rest xs (StCLbl lbl : tbl)
514 | otherwise = mkTable alts xs (StCLbl dflt : tbl)
518 We generate binary comparison trees when a jump table is inappropriate.
519 We expect to be given a list of alternatives, sorted by tag, and for
520 convenience, the length of the alternative list. We recursively break
521 the list in half and do a comparison on the first tag of the second half
522 of the list. (Odd lists are broken so that the second half of the list
523 is longer.) We can handle either integer or floating kind alternatives,
524 so long as they are not mixed. (We assume that the type of the discriminant
525 determines the type of the alternatives.)
527 As with the jump table approach, if a join is necessary after the switch, the
528 alternatives should already finish with a jump to the join point.
533 :: StixTree -- discriminant
534 -> Bool -- floating point?
535 -> [(Literal, AbstractC)] -- alternatives
536 -> Int -- number of choices
537 -> Literal -- low tag
538 -> Literal -- high tag
539 -> CLabel -- default code label
540 -> UniqSM StixTreeList
543 mkBinaryTree am floating [(tag,alt)] _ lowTag highTag udlbl
544 | rangeOfOne = gencode alt
546 = let tag' = a2stix (CLit tag)
547 cmpOp = if floating then DoubleNeOp else IntNeOp
548 test = StPrim cmpOp [am, tag']
549 cjmp = StCondJump udlbl test
551 gencode alt `thenUs` \ alt_code ->
552 returnUs (\xs -> cjmp : alt_code xs)
555 rangeOfOne = not floating && intTag lowTag + 1 >= intTag highTag
556 -- When there is only one possible tag left in range, we skip the comparison
558 mkBinaryTree am floating alts choices lowTag highTag udlbl
559 = getUniqLabelNCG `thenUs` \ uhlbl ->
560 let tag' = a2stix (CLit splitTag)
561 cmpOp = if floating then DoubleGeOp else IntGeOp
562 test = StPrim cmpOp [am, tag']
563 cjmp = StCondJump uhlbl test
565 mkBinaryTree am floating alts_lo half lowTag splitTag udlbl
566 `thenUs` \ lo_code ->
567 mkBinaryTree am floating alts_hi (choices - half) splitTag highTag udlbl
568 `thenUs` \ hi_code ->
570 returnUs (\xs -> cjmp : lo_code (StLabel uhlbl : hi_code xs))
573 half = choices `div` 2
574 (alts_lo, alts_hi) = splitAt half alts
575 splitTag = fst (head alts_hi)
582 :: CAddrMode -- discriminant
584 -> AbstractC -- if-part
585 -> AbstractC -- else-part
586 -> UniqSM StixTreeList
589 mkIfThenElse discrim tag alt deflt
590 = getUniqLabelNCG `thenUs` \ ujlbl ->
591 getUniqLabelNCG `thenUs` \ utlbl ->
592 let discrim' = a2stix discrim
593 tag' = a2stix (CLit tag)
594 cmpOp = if (isFloatingRep (getAmodeRep discrim)) then DoubleNeOp else IntNeOp
595 test = StPrim cmpOp [discrim', tag']
596 cjmp = StCondJump utlbl test
600 gencode (mkJoin alt ujlbl) `thenUs` \ alt_code ->
601 gencode deflt `thenUs` \ dflt_code ->
602 returnUs (\xs -> cjmp : alt_code (dest : dflt_code (join : xs)))
604 mkJoin :: AbstractC -> CLabel -> AbstractC
607 | mightFallThrough code = mkAbsCStmts code (CJump (CLbl lbl PtrRep))
611 %---------------------------------------------------------------------------
613 This answers the question: Can the code fall through to the next
614 line(s) of code? This errs towards saying True if it can't choose,
615 because it is used for eliminating needless jumps. In other words, if
616 you might possibly {\em not} jump, then say yes to falling through.
619 mightFallThrough :: AbstractC -> Bool
621 mightFallThrough absC = ft absC True
623 ft AbsCNop if_empty = if_empty
625 ft (CJump _) if_empty = False
626 ft (CReturn _ _) if_empty = False
627 ft (CSwitch _ alts deflt) if_empty
628 = ft deflt if_empty ||
629 or [ft alt if_empty | (_,alt) <- alts]
631 ft (AbsCStmts c1 c2) if_empty = ft c2 (ft c1 if_empty)
632 ft _ if_empty = if_empty
634 {- Old algorithm, which called nonemptyAbsC for every subexpression! =========
635 fallThroughAbsC (AbsCStmts c1 c2)
636 = case nonemptyAbsC c2 of
637 Nothing -> fallThroughAbsC c1
638 Just x -> fallThroughAbsC x
639 fallThroughAbsC (CJump _) = False
640 fallThroughAbsC (CReturn _ _) = False
641 fallThroughAbsC (CSwitch _ choices deflt)
642 = (not (isEmptyAbsC deflt) && fallThroughAbsC deflt)
643 || or (map (fallThroughAbsC . snd) choices)
644 fallThroughAbsC other = True
646 isEmptyAbsC :: AbstractC -> Bool
647 isEmptyAbsC = not . maybeToBool . nonemptyAbsC
648 ================= End of old, quadratic, algorithm -}