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
19 import PprAbsC ( dumpRealC )
20 import SMRep ( fixedItblSize,
22 rET_VEC_SMALL, rET_VEC_BIG
24 import Constants ( mIN_UPD_SIZE )
25 import CLabel ( CLabel, mkReturnInfoLabel, mkReturnPtLabel,
26 mkClosureTblLabel, mkClosureLabel,
27 labelDynamic, mkSplitMarkerLabel )
28 import ClosureInfo ( infoTableLabelFromCI, entryLabelFromCI,
29 fastLabelFromCI, closureUpdReqd,
30 staticClosureNeedsLink
32 import Literal ( Literal(..), word2IntLit )
33 import Maybes ( maybeToBool )
34 import PrimOp ( primOpNeedsWrapper, PrimOp(..) )
35 import PrimRep ( isFloatingRep, PrimRep(..) )
36 import StixInfo ( genCodeInfoTable, genBitmapInfoTable )
37 import StixMacro ( macroCode, checkCode )
38 import StixPrim ( primCode, foreignCallCode, amodeToStix, amodeToStix' )
39 import Outputable ( pprPanic, ppr )
40 import UniqSupply ( returnUs, thenUs, mapUs, getUniqueUs, UniqSM )
41 import Util ( naturalMergeSortLe )
42 import Panic ( panic )
43 import TyCon ( tyConDataCons )
44 import DataCon ( dataConWrapId )
45 import BitSet ( intBS )
46 import Name ( NamedThing(..) )
47 import CmdLineOpts ( opt_Static, opt_EnsureSplittableC )
50 For each independent chunk of AbstractC code, we generate a list of
51 @StixTree@s, where each tree corresponds to a single Stix instruction.
52 We leave the chunks separated so that register allocation can be
53 performed locally within the chunk.
56 genCodeAbstractC :: AbstractC -> UniqSM [StixTree]
62 a2stix' = amodeToStix'
63 volsaves = volatileSaves
64 volrestores = volatileRestores
66 macro_code = macroCode
67 -- real code follows... ---------
70 Here we handle top-level things, like @CCodeBlock@s and
80 gentopcode (CCodeBlock lbl absC)
81 = gencode absC `thenUs` \ code ->
82 returnUs (StSegment TextSegment : StFunBegin lbl : code [StFunEnd lbl])
84 gentopcode stmt@(CStaticClosure lbl _ _ _)
85 = genCodeStaticClosure stmt `thenUs` \ code ->
88 then StSegment DataSegment
89 : StLabel lbl : code []
90 else StSegment DataSegment
91 : StData PtrRep [StInt 0] -- DLLised world, need extra zero word
92 : StLabel lbl : code []
95 gentopcode stmt@(CRetVector lbl _ _ _)
96 = genCodeVecTbl stmt `thenUs` \ code ->
97 returnUs (StSegment TextSegment : code [StLabel lbl])
99 gentopcode stmt@(CRetDirect uniq absC srt liveness)
100 = gencode absC `thenUs` \ code ->
101 genBitmapInfoTable liveness srt closure_type False `thenUs` \ itbl ->
102 returnUs (StSegment TextSegment :
103 itbl (StLabel lbl_info : StLabel lbl_ret : code []))
105 lbl_info = mkReturnInfoLabel uniq
106 lbl_ret = mkReturnPtLabel uniq
107 closure_type = case liveness of
108 LvSmall _ -> rET_SMALL
111 gentopcode stmt@(CClosureInfoAndCode cl_info slow Nothing _)
114 = genCodeInfoTable stmt `thenUs` \ itbl ->
115 returnUs (StSegment TextSegment : itbl [])
118 = genCodeInfoTable stmt `thenUs` \ itbl ->
119 gencode slow `thenUs` \ slow_code ->
120 returnUs (StSegment TextSegment : itbl (StFunBegin slow_lbl :
121 slow_code [StFunEnd slow_lbl]))
123 slow_is_empty = not (maybeToBool (nonemptyAbsC slow))
124 slow_lbl = entryLabelFromCI cl_info
126 gentopcode stmt@(CClosureInfoAndCode cl_info slow (Just fast) _) =
127 -- ToDo: what if this is empty? ------------------------^^^^
128 genCodeInfoTable stmt `thenUs` \ itbl ->
129 gencode slow `thenUs` \ slow_code ->
130 gencode fast `thenUs` \ fast_code ->
131 returnUs (StSegment TextSegment : itbl (StFunBegin slow_lbl :
132 slow_code (StFunEnd slow_lbl : StFunBegin fast_lbl :
133 fast_code [StFunEnd fast_lbl])))
135 slow_lbl = entryLabelFromCI cl_info
136 fast_lbl = fastLabelFromCI cl_info
138 gentopcode stmt@(CSRT lbl closures)
139 = returnUs [ StSegment TextSegment
141 , StData DataPtrRep (map mk_StCLbl_for_SRT closures)
144 mk_StCLbl_for_SRT :: CLabel -> StixTree
145 mk_StCLbl_for_SRT label
147 = StIndex Int8Rep (StCLbl label) (StInt 1)
151 gentopcode stmt@(CBitmap lbl mask)
152 = returnUs [ StSegment TextSegment
154 , StData WordRep (StInt (toInteger (length mask)) :
155 map (StInt . toInteger . intBS) mask)
158 gentopcode stmt@(CClosureTbl tycon)
159 = returnUs [ StSegment TextSegment
160 , StLabel (mkClosureTblLabel tycon)
161 , StData DataPtrRep (map (StCLbl . mkClosureLabel . getName . dataConWrapId)
162 (tyConDataCons tycon) )
165 gentopcode stmt@(CModuleInitBlock lbl absC)
166 = gencode absC `thenUs` \ code ->
167 getUniqLabelNCG `thenUs` \ tmp_lbl ->
168 getUniqLabelNCG `thenUs` \ flag_lbl ->
169 returnUs ( StSegment DataSegment
171 : StData IntRep [StInt 0]
172 : StSegment TextSegment
174 : StCondJump tmp_lbl (StPrim IntNeOp
175 [StInd IntRep (StCLbl flag_lbl),
177 : StAssign IntRep (StInd IntRep (StCLbl flag_lbl)) (StInt 1)
180 , StAssign PtrRep stgSp
181 (StIndex PtrRep stgSp (StInt (-1)))
182 , StJump NoDestInfo (StInd WordRep stgSp)
186 = gencode absC `thenUs` \ code ->
187 returnUs (StSegment TextSegment : code [])
194 -> UniqSM StixTreeList
196 genCodeVecTbl (CRetVector lbl amodes srt liveness)
197 = genBitmapInfoTable liveness srt closure_type True `thenUs` \itbl ->
198 returnUs (\xs -> vectbl : itbl xs)
200 vectbl = StData PtrRep (reverse (map a2stix amodes))
201 closure_type = case liveness of
202 LvSmall _ -> rET_VEC_SMALL
203 LvLarge _ -> rET_VEC_BIG
211 -> UniqSM StixTreeList
213 genCodeStaticClosure (CStaticClosure _ cl_info cost_centre amodes)
214 = returnUs (\xs -> table ++ xs)
216 table = StData PtrRep [StCLbl (infoTableLabelFromCI cl_info)] :
217 map do_one_amode amodes ++
218 [StData PtrRep (padding_wds ++ static_link)]
221 = StData (promote_to_word (getAmodeRep amode)) [a2stix amode]
223 -- We need to promote any item smaller than a word to a word
225 | sizeOf pk >= sizeOf IntRep = pk
228 upd_reqd = closureUpdReqd cl_info
231 | upd_reqd = take (max 0 (mIN_UPD_SIZE - length amodes)) zeros
234 static_link | upd_reqd || staticClosureNeedsLink cl_info = [StInt 0]
237 zeros = StInt 0 : zeros
240 -- Watch out for VoidKinds...cf. PprAbsC
242 | getAmodeRep item == VoidRep = StInt 0
243 | otherwise = a2stix item
248 Now the individual AbstractC statements.
254 -> UniqSM StixTreeList
258 @AbsCNop@s just disappear.
262 gencode AbsCNop = returnUs id
266 Split markers just insert a __stg_split_marker, which is caught by the
267 split-mangler later on and used to split the assembly into chunks.
272 | opt_EnsureSplittableC = returnUs (\xs -> StLabel mkSplitMarkerLabel : xs)
273 | otherwise = returnUs id
277 AbstractC instruction sequences are handled individually, and the
278 resulting StixTreeLists are joined together.
282 gencode (AbsCStmts c1 c2)
283 = gencode c1 `thenUs` \ b1 ->
284 gencode c2 `thenUs` \ b2 ->
289 Initialising closure headers in the heap...a fairly complex ordeal if
290 done properly. For now, we just set the info pointer, but we should
291 really take a peek at the flags to determine whether or not there are
292 other things to be done (setting cost centres, age headers, global
297 gencode (CInitHdr cl_info reg_rel _)
300 lbl = infoTableLabelFromCI cl_info
302 returnUs (\xs -> StAssign PtrRep (StInd PtrRep lhs) (StCLbl lbl) : xs)
310 gencode (CCheck macro args assts)
311 = gencode assts `thenUs` \assts_stix ->
312 checkCode macro args assts_stix
316 Assignment, the curse of von Neumann, is the center of the code we
317 produce. In most cases, the type of the assignment is determined
318 by the type of the destination. However, when the destination can
319 have mixed types, the type of the assignment is ``StgWord'' (we use
320 PtrRep for lack of anything better). Think: do we also want a cast
321 of the source? Be careful about floats/doubles.
325 gencode (CAssign lhs rhs)
326 | getAmodeRep lhs == VoidRep = returnUs id
328 = let pk = getAmodeRep lhs
329 pk' = if mixedTypeLocn lhs && not (isFloatingRep pk) then IntRep else pk
333 returnUs (\xs -> StAssign pk' lhs' rhs' : xs)
337 Unconditional jumps, including the special ``enter closure'' operation.
338 Note that the new entry convention requires that we load the InfoPtr (R2)
339 with the address of the info table before jumping to the entry code for Node.
341 For a vectored return, we must subtract the size of the info table to
342 get at the return vector. This depends on the size of the info table,
343 which varies depending on whether we're profiling etc.
348 = returnUs (\xs -> StJump NoDestInfo (a2stix dest) : xs)
350 gencode (CFallThrough (CLbl lbl _))
351 = returnUs (\xs -> StFallThrough lbl : xs)
353 gencode (CReturn dest DirectReturn)
354 = returnUs (\xs -> StJump NoDestInfo (a2stix dest) : xs)
356 gencode (CReturn table (StaticVectoredReturn n))
357 = returnUs (\xs -> StJump NoDestInfo dest : xs)
359 dest = StInd PtrRep (StIndex PtrRep (a2stix table)
360 (StInt (toInteger (-n-fixedItblSize-1))))
362 gencode (CReturn table (DynamicVectoredReturn am))
363 = returnUs (\xs -> StJump NoDestInfo dest : xs)
365 dest = StInd PtrRep (StIndex PtrRep (a2stix table) dyn_off)
366 dyn_off = StPrim IntSubOp [StPrim IntNegOp [a2stix am],
367 StInt (toInteger (fixedItblSize+1))]
371 Now the PrimOps, some of which may need caller-saves register wrappers.
374 gencode (COpStmt results (StgFCallOp fcall _) args vols)
375 = ASSERT( null vols )
376 foreignCallCode (nonVoid results) fcall (nonVoid args)
378 gencode (COpStmt results (StgPrimOp op) args vols)
379 -- ToDo (ADR?): use that liveness mask
380 | primOpNeedsWrapper op
382 saves = volsaves vols
383 restores = volrestores vols
385 p2stix (nonVoid results) op (nonVoid args)
387 returnUs (\xs -> saves ++ code (restores ++ xs))
389 | otherwise = p2stix (nonVoid results) op (nonVoid args)
391 nonVoid = filter ((/= VoidRep) . getAmodeRep)
394 Now the dreaded conditional jump.
396 Now the if statement. Almost *all* flow of control are of this form.
398 if (am==lit) { absC } else { absCdef }
412 gencode (CSwitch discrim alts deflt)
416 [(tag,alt_code)] -> case maybe_empty_deflt of
417 Nothing -> gencode alt_code
418 Just dc -> mkIfThenElse discrim tag alt_code dc
420 [(tag1@(MachInt i1), alt_code1),
421 (tag2@(MachInt i2), alt_code2)]
422 | deflt_is_empty && i1 == 0 && i2 == 1
423 -> mkIfThenElse discrim tag1 alt_code1 alt_code2
424 | deflt_is_empty && i1 == 1 && i2 == 0
425 -> mkIfThenElse discrim tag2 alt_code2 alt_code1
427 -- If the @discrim@ is simple, then this unfolding is safe.
428 other | simple_discrim -> mkSimpleSwitches discrim alts deflt
430 -- Otherwise, we need to do a bit of work.
431 other -> getUniqueUs `thenUs` \ u ->
433 (CAssign (CTemp u pk) discrim)
434 (CSwitch (CTemp u pk) alts deflt))
437 maybe_empty_deflt = nonemptyAbsC deflt
438 deflt_is_empty = case maybe_empty_deflt of
442 pk = getAmodeRep discrim
444 simple_discrim = case discrim of
452 Finally, all of the disgusting AbstractC macros.
456 gencode (CMacroStmt macro args) = macro_code macro args
458 gencode (CCallProfCtrMacro macro _)
459 = returnUs (\xs -> StComment macro : xs)
461 gencode (CCallProfCCMacro macro _)
462 = returnUs (\xs -> StComment macro : xs)
465 = pprPanic "AbsCStixGen.gencode" (dumpRealC other)
468 Here, we generate a jump table if there are more than four (integer)
469 alternatives and the jump table occupancy is greater than 50%.
470 Otherwise, we generate a binary comparison tree. (Perhaps this could
475 intTag :: Literal -> Integer
476 intTag (MachChar c) = toInteger c
477 intTag (MachInt i) = i
478 intTag (MachWord w) = intTag (word2IntLit (MachWord w))
479 intTag _ = panic "intTag"
481 fltTag :: Literal -> Rational
483 fltTag (MachFloat f) = f
484 fltTag (MachDouble d) = d
485 fltTag x = pprPanic "fltTag" (ppr x)
489 :: CAddrMode -> [(Literal,AbstractC)] -> AbstractC
490 -> UniqSM StixTreeList
492 mkSimpleSwitches am alts absC
493 = getUniqLabelNCG `thenUs` \ udlbl ->
494 getUniqLabelNCG `thenUs` \ ujlbl ->
496 joinedAlts = map (\ (tag,code) -> (tag, mkJoin code ujlbl)) alts
497 sortedAlts = naturalMergeSortLe leAlt joinedAlts
498 -- naturalMergeSortLe, because we often get sorted alts to begin with
500 lowTag = intTag (fst (head sortedAlts))
501 highTag = intTag (fst (last sortedAlts))
503 -- lowest and highest possible values the discriminant could take
504 lowest = if floating then targetMinDouble else targetMinInt
505 highest = if floating then targetMaxDouble else targetMaxInt
508 if not floating && choices > 4
509 && highTag - lowTag < toInteger (2 * choices)
511 mkJumpTable am' sortedAlts lowTag highTag udlbl
513 mkBinaryTree am' floating sortedAlts choices lowest highest udlbl
515 `thenUs` \ alt_code ->
516 gencode absC `thenUs` \ dflt_code ->
518 returnUs (\xs -> alt_code (StLabel udlbl : dflt_code (StLabel ujlbl : xs)))
521 floating = isFloatingRep (getAmodeRep am)
522 choices = length alts
524 (x@(MachChar _),_) `leAlt` (y,_) = intTag x <= intTag y
525 (x@(MachInt _), _) `leAlt` (y,_) = intTag x <= intTag y
526 (x@(MachWord _), _) `leAlt` (y,_) = intTag x <= intTag y
527 (x,_) `leAlt` (y,_) = fltTag x <= fltTag y
531 We use jump tables when doing an integer switch on a relatively dense
532 list of alternatives. We expect to be given a list of alternatives,
533 sorted by tag, and a range of values for which we are to generate a
534 table. Of course, the tags of the alternatives should lie within the
535 indicated range. The alternatives need not cover the range; a default
536 target is provided for the missing alternatives.
538 If a join is necessary after the switch, the alternatives should
539 already finish with a jump to the join point.
544 :: StixTree -- discriminant
545 -> [(Literal, AbstractC)] -- alternatives
546 -> Integer -- low tag
547 -> Integer -- high tag
548 -> CLabel -- default label
549 -> UniqSM StixTreeList
552 mkJumpTable am alts lowTag highTag dflt
553 = getUniqLabelNCG `thenUs` \ utlbl ->
554 mapUs genLabel alts `thenUs` \ branches ->
555 let cjmpLo = StCondJump dflt (StPrim IntLtOp [am, StInt (toInteger lowTag)])
556 cjmpHi = StCondJump dflt (StPrim IntGtOp [am, StInt (toInteger highTag)])
558 offset = StPrim IntSubOp [am, StInt lowTag]
559 dsts = DestInfo (dflt : map fst branches)
561 jump = StJump dsts (StInd PtrRep (StIndex PtrRep (StCLbl utlbl) offset))
563 table = StData PtrRep (mkTable branches [lowTag..highTag] [])
565 mapUs mkBranch branches `thenUs` \ alts ->
567 returnUs (\xs -> cjmpLo : cjmpHi : jump :
568 StSegment DataSegment : tlbl : table :
569 StSegment TextSegment : foldr1 (.) alts xs)
572 genLabel x = getUniqLabelNCG `thenUs` \ lbl -> returnUs (lbl, x)
574 mkBranch (lbl,(_,alt)) =
575 gencode alt `thenUs` \ alt_code ->
576 returnUs (\xs -> StLabel lbl : alt_code xs)
578 mkTable _ [] tbl = reverse tbl
579 mkTable [] (x:xs) tbl = mkTable [] xs (StCLbl dflt : tbl)
580 mkTable alts@((lbl,(tag,_)):rest) (x:xs) tbl
581 | intTag tag == x = mkTable rest xs (StCLbl lbl : tbl)
582 | otherwise = mkTable alts xs (StCLbl dflt : tbl)
586 We generate binary comparison trees when a jump table is inappropriate.
587 We expect to be given a list of alternatives, sorted by tag, and for
588 convenience, the length of the alternative list. We recursively break
589 the list in half and do a comparison on the first tag of the second half
590 of the list. (Odd lists are broken so that the second half of the list
591 is longer.) We can handle either integer or floating kind alternatives,
592 so long as they are not mixed. (We assume that the type of the discriminant
593 determines the type of the alternatives.)
595 As with the jump table approach, if a join is necessary after the switch, the
596 alternatives should already finish with a jump to the join point.
601 :: StixTree -- discriminant
602 -> Bool -- floating point?
603 -> [(Literal, AbstractC)] -- alternatives
604 -> Int -- number of choices
605 -> Literal -- low tag
606 -> Literal -- high tag
607 -> CLabel -- default code label
608 -> UniqSM StixTreeList
611 mkBinaryTree am floating [(tag,alt)] _ lowTag highTag udlbl
612 | rangeOfOne = gencode alt
614 = let tag' = a2stix (CLit tag)
615 cmpOp = if floating then DoubleNeOp else IntNeOp
616 test = StPrim cmpOp [am, tag']
617 cjmp = StCondJump udlbl test
619 gencode alt `thenUs` \ alt_code ->
620 returnUs (\xs -> cjmp : alt_code xs)
623 rangeOfOne = not floating && intTag lowTag + 1 >= intTag highTag
624 -- When there is only one possible tag left in range, we skip the comparison
626 mkBinaryTree am floating alts choices lowTag highTag udlbl
627 = getUniqLabelNCG `thenUs` \ uhlbl ->
628 let tag' = a2stix (CLit splitTag)
629 cmpOp = if floating then DoubleGeOp else IntGeOp
630 test = StPrim cmpOp [am, tag']
631 cjmp = StCondJump uhlbl test
633 mkBinaryTree am floating alts_lo half lowTag splitTag udlbl
634 `thenUs` \ lo_code ->
635 mkBinaryTree am floating alts_hi (choices - half) splitTag highTag udlbl
636 `thenUs` \ hi_code ->
638 returnUs (\xs -> cjmp : lo_code (StLabel uhlbl : hi_code xs))
641 half = choices `div` 2
642 (alts_lo, alts_hi) = splitAt half alts
643 splitTag = fst (head alts_hi)
650 :: CAddrMode -- discriminant
652 -> AbstractC -- if-part
653 -> AbstractC -- else-part
654 -> UniqSM StixTreeList
657 mkIfThenElse discrim tag alt deflt
658 = getUniqLabelNCG `thenUs` \ ujlbl ->
659 getUniqLabelNCG `thenUs` \ utlbl ->
660 let discrim' = a2stix discrim
661 tag' = a2stix (CLit tag)
662 cmpOp = if (isFloatingRep (getAmodeRep discrim)) then DoubleNeOp else IntNeOp
663 test = StPrim cmpOp [discrim', tag']
664 cjmp = StCondJump utlbl test
668 gencode (mkJoin alt ujlbl) `thenUs` \ alt_code ->
669 gencode deflt `thenUs` \ dflt_code ->
670 returnUs (\xs -> cjmp : alt_code (dest : dflt_code (join : xs)))
672 mkJoin :: AbstractC -> CLabel -> AbstractC
675 | mightFallThrough code = mkAbsCStmts code (CJump (CLbl lbl PtrRep))
679 %---------------------------------------------------------------------------
681 This answers the question: Can the code fall through to the next
682 line(s) of code? This errs towards saying True if it can't choose,
683 because it is used for eliminating needless jumps. In other words, if
684 you might possibly {\em not} jump, then say yes to falling through.
687 mightFallThrough :: AbstractC -> Bool
689 mightFallThrough absC = ft absC True
691 ft AbsCNop if_empty = if_empty
693 ft (CJump _) if_empty = False
694 ft (CReturn _ _) if_empty = False
695 ft (CSwitch _ alts deflt) if_empty
696 = ft deflt if_empty ||
697 or [ft alt if_empty | (_,alt) <- alts]
699 ft (AbsCStmts c1 c2) if_empty = ft c2 (ft c1 if_empty)
700 ft _ if_empty = if_empty
702 {- Old algorithm, which called nonemptyAbsC for every subexpression! =========
703 fallThroughAbsC (AbsCStmts c1 c2)
704 = case nonemptyAbsC c2 of
705 Nothing -> fallThroughAbsC c1
706 Just x -> fallThroughAbsC x
707 fallThroughAbsC (CJump _) = False
708 fallThroughAbsC (CReturn _ _) = False
709 fallThroughAbsC (CSwitch _ choices deflt)
710 = (not (isEmptyAbsC deflt) && fallThroughAbsC deflt)
711 || or (map (fallThroughAbsC . snd) choices)
712 fallThroughAbsC other = True
714 isEmptyAbsC :: AbstractC -> Bool
715 isEmptyAbsC = not . maybeToBool . nonemptyAbsC
716 ================= End of old, quadratic, algorithm -}