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 )
38 For each independent chunk of AbstractC code, we generate a list of
39 @StixTree@s, where each tree corresponds to a single Stix instruction.
40 We leave the chunks separated so that register allocation can be
41 performed locally within the chunk.
44 genCodeAbstractC :: AbstractC -> UniqSM [[StixTree]]
47 = mapUs gentopcode (mkAbsCStmtList absC) `thenUs` \ trees ->
48 returnUs ([StComment SLIT("Native Code")] : trees)
51 a2stix' = amodeToStix'
52 volsaves = volatileSaves
53 volrestores = volatileRestores
55 macro_code = macroCode
57 -- real code follows... ---------
60 Here we handle top-level things, like @CCodeBlock@s and
70 gentopcode (CCodeBlock label absC)
71 = gencode absC `thenUs` \ code ->
72 returnUs (StSegment TextSegment : StFunBegin label : code [StFunEnd label])
74 gentopcode stmt@(CStaticClosure label _ _ _)
75 = genCodeStaticClosure stmt `thenUs` \ code ->
76 returnUs (StSegment DataSegment : StLabel label : code [])
78 gentopcode stmt@(CRetUnVector _ _) = returnUs []
80 gentopcode stmt@(CFlatRetVector label _)
81 = genCodeVecTbl stmt `thenUs` \ code ->
82 returnUs (StSegment TextSegment : code [StLabel label])
84 gentopcode stmt@(CClosureInfoAndCode cl_info slow Nothing _ _ _)
87 = genCodeInfoTable stmt `thenUs` \ itbl ->
88 returnUs (StSegment TextSegment : itbl [])
91 = genCodeInfoTable stmt `thenUs` \ itbl ->
92 gencode slow `thenUs` \ slow_code ->
93 returnUs (StSegment TextSegment : itbl (StFunBegin slow_lbl :
94 slow_code [StFunEnd slow_lbl]))
96 slow_is_empty = not (maybeToBool (nonemptyAbsC slow))
97 slow_lbl = entryLabelFromCI cl_info
99 gentopcode stmt@(CClosureInfoAndCode cl_info slow (Just fast) _ _ _) =
100 -- ToDo: what if this is empty? ------------------------^^^^
101 genCodeInfoTable stmt `thenUs` \ itbl ->
102 gencode slow `thenUs` \ slow_code ->
103 gencode fast `thenUs` \ fast_code ->
104 returnUs (StSegment TextSegment : itbl (StFunBegin slow_lbl :
105 slow_code (StFunEnd slow_lbl : StFunBegin fast_lbl :
106 fast_code [StFunEnd fast_lbl])))
108 slow_lbl = entryLabelFromCI cl_info
109 fast_lbl = fastLabelFromCI cl_info
112 = gencode absC `thenUs` \ code ->
113 returnUs (StSegment TextSegment : code [])
117 Vector tables are trivial!
123 -> UniqSM StixTreeList
125 genCodeVecTbl (CFlatRetVector label amodes)
126 = returnUs (\xs -> vectbl : xs)
128 vectbl = StData PtrRep (reverse (map a2stix amodes))
132 Static closures are not so hard either.
138 -> UniqSM StixTreeList
140 genCodeStaticClosure (CStaticClosure _ cl_info cost_centre amodes)
141 = returnUs (\xs -> table : xs)
143 table = StData PtrRep (StCLbl info_lbl : body)
144 info_lbl = infoTableLabelFromCI cl_info
146 body = if closureUpdReqd cl_info then
147 take (max mIN_UPD_SIZE (length amodes')) (amodes' ++ zeros)
151 zeros = StInt 0 : zeros
153 amodes' = map amodeZeroVoid amodes
155 -- Watch out for VoidKinds...cf. PprAbsC
157 | getAmodeRep item == VoidRep = StInt 0
158 | otherwise = a2stix item
162 Now the individual AbstractC statements.
168 -> UniqSM StixTreeList
172 @AbsCNop@s just disappear.
176 gencode AbsCNop = returnUs id
180 Split markers are a NOP in this land.
184 gencode CSplitMarker = returnUs id
188 AbstractC instruction sequences are handled individually, and the
189 resulting StixTreeLists are joined together.
193 gencode (AbsCStmts c1 c2)
194 = gencode c1 `thenUs` \ b1 ->
195 gencode c2 `thenUs` \ b2 ->
200 Initialising closure headers in the heap...a fairly complex ordeal if
201 done properly. For now, we just set the info pointer, but we should
202 really take a peek at the flags to determine whether or not there are
203 other things to be done (setting cost centres, age headers, global
208 gencode (CInitHdr cl_info reg_rel _ _)
210 lhs = a2stix (CVal reg_rel PtrRep)
211 lbl = infoTableLabelFromCI cl_info
213 returnUs (\xs -> StAssign PtrRep lhs (StCLbl lbl) : xs)
217 Assignment, the curse of von Neumann, is the center of the code we
218 produce. In most cases, the type of the assignment is determined
219 by the type of the destination. However, when the destination can
220 have mixed types, the type of the assignment is ``StgWord'' (we use
221 PtrRep for lack of anything better). Think: do we also want a cast
222 of the source? Be careful about floats/doubles.
226 gencode (CAssign lhs rhs)
227 | getAmodeRep lhs == VoidRep = returnUs id
229 = let pk = getAmodeRep lhs
230 pk' = if mixedTypeLocn lhs && not (isFloatingRep pk) then IntRep else pk
234 returnUs (\xs -> StAssign pk' lhs' rhs' : xs)
238 Unconditional jumps, including the special ``enter closure'' operation.
239 Note that the new entry convention requires that we load the InfoPtr (R2)
240 with the address of the info table before jumping to the entry code for Node.
245 = returnUs (\xs -> StJump (a2stix dest) : xs)
247 gencode (CFallThrough (CLbl lbl _))
248 = returnUs (\xs -> StFallThrough lbl : xs)
250 gencode (CReturn dest DirectReturn)
251 = returnUs (\xs -> StJump (a2stix dest) : xs)
253 gencode (CReturn table (StaticVectoredReturn n))
254 = returnUs (\xs -> StJump dest : xs)
256 dest = StInd PtrRep (StIndex PtrRep (a2stix table)
257 (StInt (toInteger (-n-1))))
259 gencode (CReturn table (DynamicVectoredReturn am))
260 = returnUs (\xs -> StJump dest : xs)
262 dest = StInd PtrRep (StIndex PtrRep (a2stix table) dyn_off)
263 dyn_off = StPrim IntSubOp [StPrim IntNegOp [a2stix am], StInt 1]
267 Now the PrimOps, some of which may need caller-saves register wrappers.
271 gencode (COpStmt results op args liveness_mask vols)
272 -- ToDo (ADR?): use that liveness mask
273 | primOpNeedsWrapper op
275 saves = volsaves vols
276 restores = volrestores vols
278 p2stix (nonVoid results) op (nonVoid args)
280 returnUs (\xs -> saves ++ code (restores ++ xs))
282 | otherwise = p2stix (nonVoid results) op (nonVoid args)
284 nonVoid = filter ((/= VoidRep) . getAmodeRep)
288 Now the dreaded conditional jump.
290 Now the if statement. Almost *all* flow of control are of this form.
292 if (am==lit) { absC } else { absCdef }
306 gencode (CSwitch discrim alts deflt)
310 [(tag,alt_code)] -> case maybe_empty_deflt of
311 Nothing -> gencode alt_code
312 Just dc -> mkIfThenElse discrim tag alt_code dc
314 [(tag1@(MachInt i1 _), alt_code1),
315 (tag2@(MachInt i2 _), alt_code2)]
316 | deflt_is_empty && i1 == 0 && i2 == 1
317 -> mkIfThenElse discrim tag1 alt_code1 alt_code2
318 | deflt_is_empty && i1 == 1 && i2 == 0
319 -> mkIfThenElse discrim tag2 alt_code2 alt_code1
321 -- If the @discrim@ is simple, then this unfolding is safe.
322 other | simple_discrim -> mkSimpleSwitches discrim alts deflt
324 -- Otherwise, we need to do a bit of work.
325 other -> getUnique `thenUs` \ u ->
327 (CAssign (CTemp u pk) discrim)
328 (CSwitch (CTemp u pk) alts deflt))
331 maybe_empty_deflt = nonemptyAbsC deflt
332 deflt_is_empty = case maybe_empty_deflt of
336 pk = getAmodeRep discrim
338 simple_discrim = case discrim of
346 Finally, all of the disgusting AbstractC macros.
350 gencode (CMacroStmt macro args) = macro_code macro args
352 gencode (CCallProfCtrMacro macro _)
353 = returnUs (\xs -> StComment macro : xs)
355 gencode (CCallProfCCMacro macro _)
356 = returnUs (\xs -> StComment macro : xs)
360 Here, we generate a jump table if there are more than four (integer) alternatives and
361 the jump table occupancy is greater than 50%. Otherwise, we generate a binary
362 comparison tree. (Perhaps this could be tuned.)
366 intTag :: Literal -> Integer
367 intTag (MachChar c) = toInteger (ord c)
368 intTag (MachInt i _) = i
369 intTag _ = panic "intTag"
371 fltTag :: Literal -> Rational
373 fltTag (MachFloat f) = f
374 fltTag (MachDouble d) = d
375 fltTag _ = panic "fltTag"
379 :: CAddrMode -> [(Literal,AbstractC)] -> AbstractC
380 -> UniqSM StixTreeList
382 mkSimpleSwitches am alts absC
383 = getUniqLabelNCG `thenUs` \ udlbl ->
384 getUniqLabelNCG `thenUs` \ ujlbl ->
386 joinedAlts = map (\ (tag,code) -> (tag, mkJoin code ujlbl)) alts
387 sortedAlts = naturalMergeSortLe leAlt joinedAlts
388 -- naturalMergeSortLe, because we often get sorted alts to begin with
390 lowTag = intTag (fst (head sortedAlts))
391 highTag = intTag (fst (last sortedAlts))
393 -- lowest and highest possible values the discriminant could take
394 lowest = if floating then targetMinDouble else targetMinInt
395 highest = if floating then targetMaxDouble else targetMaxInt
398 if not floating && choices > 4 && highTag - lowTag < toInteger (2 * choices) then
399 mkJumpTable am' sortedAlts lowTag highTag udlbl
401 mkBinaryTree am' floating sortedAlts choices lowest highest udlbl
403 `thenUs` \ alt_code ->
404 gencode absC `thenUs` \ dflt_code ->
406 returnUs (\xs -> alt_code (StLabel udlbl : dflt_code (StLabel ujlbl : xs)))
409 floating = isFloatingRep (getAmodeRep am)
410 choices = length alts
412 (x@(MachChar _),_) `leAlt` (y,_) = intTag x <= intTag y
413 (x@(MachInt _ _),_) `leAlt` (y,_) = intTag x <= intTag y
414 (x,_) `leAlt` (y,_) = fltTag x <= fltTag y
418 We use jump tables when doing an integer switch on a relatively dense
419 list of alternatives. We expect to be given a list of alternatives,
420 sorted by tag, and a range of values for which we are to generate a
421 table. Of course, the tags of the alternatives should lie within the
422 indicated range. The alternatives need not cover the range; a default
423 target is provided for the missing alternatives.
425 If a join is necessary after the switch, the alternatives should
426 already finish with a jump to the join point.
431 :: StixTree -- discriminant
432 -> [(Literal, AbstractC)] -- alternatives
433 -> Integer -- low tag
434 -> Integer -- high tag
435 -> CLabel -- default label
436 -> UniqSM StixTreeList
439 mkJumpTable am alts lowTag highTag dflt
440 = getUniqLabelNCG `thenUs` \ utlbl ->
441 mapUs genLabel alts `thenUs` \ branches ->
442 let cjmpLo = StCondJump dflt (StPrim IntLtOp [am, StInt lowTag])
443 cjmpHi = StCondJump dflt (StPrim IntGtOp [am, StInt highTag])
445 offset = StPrim IntSubOp [am, StInt lowTag]
446 jump = StJump (StInd PtrRep (StIndex PtrRep (StCLbl utlbl) offset))
449 table = StData PtrRep (mkTable branches [lowTag..highTag] [])
451 mapUs mkBranch branches `thenUs` \ alts ->
453 returnUs (\xs -> cjmpLo : cjmpHi : jump :
454 StSegment DataSegment : tlbl : table :
455 StSegment TextSegment : foldr1 (.) alts xs)
458 genLabel x = getUniqLabelNCG `thenUs` \ lbl -> returnUs (lbl, x)
460 mkBranch (lbl,(_,alt)) =
461 gencode alt `thenUs` \ alt_code ->
462 returnUs (\xs -> StLabel lbl : alt_code xs)
464 mkTable _ [] tbl = reverse tbl
465 mkTable [] (x:xs) tbl = mkTable [] xs (StCLbl dflt : tbl)
466 mkTable alts@((lbl,(tag,_)):rest) (x:xs) tbl
467 | intTag tag == x = mkTable rest xs (StCLbl lbl : tbl)
468 | otherwise = mkTable alts xs (StCLbl dflt : tbl)
472 We generate binary comparison trees when a jump table is inappropriate.
473 We expect to be given a list of alternatives, sorted by tag, and for
474 convenience, the length of the alternative list. We recursively break
475 the list in half and do a comparison on the first tag of the second half
476 of the list. (Odd lists are broken so that the second half of the list
477 is longer.) We can handle either integer or floating kind alternatives,
478 so long as they are not mixed. (We assume that the type of the discriminant
479 determines the type of the alternatives.)
481 As with the jump table approach, if a join is necessary after the switch, the
482 alternatives should already finish with a jump to the join point.
487 :: StixTree -- discriminant
488 -> Bool -- floating point?
489 -> [(Literal, AbstractC)] -- alternatives
490 -> Int -- number of choices
491 -> Literal -- low tag
492 -> Literal -- high tag
493 -> CLabel -- default code label
494 -> UniqSM StixTreeList
497 mkBinaryTree am floating [(tag,alt)] _ lowTag highTag udlbl
498 | rangeOfOne = gencode alt
500 = let tag' = a2stix (CLit tag)
501 cmpOp = if floating then DoubleNeOp else IntNeOp
502 test = StPrim cmpOp [am, tag']
503 cjmp = StCondJump udlbl test
505 gencode alt `thenUs` \ alt_code ->
506 returnUs (\xs -> cjmp : alt_code xs)
509 rangeOfOne = not floating && intTag lowTag + 1 >= intTag highTag
510 -- When there is only one possible tag left in range, we skip the comparison
512 mkBinaryTree am floating alts choices lowTag highTag udlbl
513 = getUniqLabelNCG `thenUs` \ uhlbl ->
514 let tag' = a2stix (CLit splitTag)
515 cmpOp = if floating then DoubleGeOp else IntGeOp
516 test = StPrim cmpOp [am, tag']
517 cjmp = StCondJump uhlbl test
519 mkBinaryTree am floating alts_lo half lowTag splitTag udlbl
520 `thenUs` \ lo_code ->
521 mkBinaryTree am floating alts_hi (choices - half) splitTag highTag udlbl
522 `thenUs` \ hi_code ->
524 returnUs (\xs -> cjmp : lo_code (StLabel uhlbl : hi_code xs))
527 half = choices `div` 2
528 (alts_lo, alts_hi) = splitAt half alts
529 splitTag = fst (head alts_hi)
536 :: CAddrMode -- discriminant
538 -> AbstractC -- if-part
539 -> AbstractC -- else-part
540 -> UniqSM StixTreeList
543 mkIfThenElse discrim tag alt deflt
544 = getUniqLabelNCG `thenUs` \ ujlbl ->
545 getUniqLabelNCG `thenUs` \ utlbl ->
546 let discrim' = a2stix discrim
547 tag' = a2stix (CLit tag)
548 cmpOp = if (isFloatingRep (getAmodeRep discrim)) then DoubleNeOp else IntNeOp
549 test = StPrim cmpOp [discrim', tag']
550 cjmp = StCondJump utlbl test
554 gencode (mkJoin alt ujlbl) `thenUs` \ alt_code ->
555 gencode deflt `thenUs` \ dflt_code ->
556 returnUs (\xs -> cjmp : alt_code (dest : dflt_code (join : xs)))
558 mkJoin :: AbstractC -> CLabel -> AbstractC
561 | mightFallThrough code = mkAbsCStmts code (CJump (CLbl lbl PtrRep))
565 %---------------------------------------------------------------------------
567 This answers the question: Can the code fall through to the next
568 line(s) of code? This errs towards saying True if it can't choose,
569 because it is used for eliminating needless jumps. In other words, if
570 you might possibly {\em not} jump, then say yes to falling through.
573 mightFallThrough :: AbstractC -> Bool
575 mightFallThrough absC = ft absC True
577 ft AbsCNop if_empty = if_empty
579 ft (CJump _) if_empty = False
580 ft (CReturn _ _) if_empty = False
581 ft (CSwitch _ alts deflt) if_empty
582 = ft deflt if_empty ||
583 or [ft alt if_empty | (_,alt) <- alts]
585 ft (AbsCStmts c1 c2) if_empty = ft c2 (ft c1 if_empty)
586 ft _ if_empty = if_empty
588 {- Old algorithm, which called nonemptyAbsC for every subexpression! =========
589 fallThroughAbsC (AbsCStmts c1 c2)
590 = case nonemptyAbsC c2 of
591 Nothing -> fallThroughAbsC c1
592 Just x -> fallThroughAbsC x
593 fallThroughAbsC (CJump _) = False
594 fallThroughAbsC (CReturn _ _) = False
595 fallThroughAbsC (CSwitch _ choices deflt)
596 = (not (isEmptyAbsC deflt) && fallThroughAbsC deflt)
597 || or (map (fallThroughAbsC . snd) choices)
598 fallThroughAbsC other = True
600 isEmptyAbsC :: AbstractC -> Bool
601 isEmptyAbsC = not . maybeToBool . nonemptyAbsC
602 ================= End of old, quadratic, algorithm -}