2 % (c) The AQUA Project, Glasgow University, 1993-1995
6 #include "HsVersions.h"
9 genPrimCode, amodeCode, amodeCode',
11 Target, CAddrMode, StixTree, PrimOp, SplitUniqSupply
14 IMPORT_Trace -- ToDo: rm debugging
17 import AbsPrel ( PrimOp(..), PrimOpResultInfo(..), TyCon,
18 getPrimOpResultInfo, isCompareOp, showPrimOp
19 IF_ATTACK_PRAGMAS(COMMA tagOf_PrimOp)
20 IF_ATTACK_PRAGMAS(COMMA pprPrimOp)
22 import AbsUniType ( cmpTyCon ) -- pragmas only
23 import CgCompInfo ( spARelToInt, spBRelToInt )
26 import PrimKind ( isFloatingKind )
28 import SMRep ( SMRep(..), SMSpecRepKind(..), SMUpdateKind(..) )
30 import StixMacro ( smStablePtrTable )
31 import StixInteger {- everything -}
39 The main honcho here is genPrimCode, which handles the guts of COpStmts.
42 arrayOfData_info = sStLitLbl SLIT("ArrayOfData_info") -- out here to avoid CAF (sigh)
43 imMutArrayOfPtrs_info = sStLitLbl SLIT("ImMutArrayOfPtrs_info")
47 -> [CAddrMode] -- results
49 -> [CAddrMode] -- args
50 -> SUniqSM StixTreeList
54 First, the dreaded @ccall@. We can't handle @casm@s.
56 Usually, this compiles to an assignment, but when the left-hand side is
57 empty, we just perform the call and ignore the result.
59 ToDo ADR: modify this to handle Malloc Ptrs.
61 btw Why not let programmer use casm to provide assembly code instead
65 -- hacking with Uncle Will:
66 #define target_STRICT target@(Target _ _ _ _ _ _ _ _)
68 genPrimCode target_STRICT res op args
71 a2stix = amodeToStix target
72 a2stix' = amodeToStix' target
74 data_hs = dataHS target
75 heap_chkr = heapCheck target
76 size_of = sizeof target
77 fixed_hs = fixedHeaderSize target
78 var_hs = varHeaderSize target
80 --- real code will follow... -------------
83 The (MP) integer operations are a true nightmare. Since we don't have a
84 convenient abstract way of allocating temporary variables on the (C) stack,
85 we use the space just below HpLim for the @MP_INT@ structures, and modify our
86 heap check accordingly.
89 -- NB: ordering of clauses somewhere driven by
90 -- the desire to getting sane patt-matching behavior
92 genprim res@[ar1,sr1,dr1, ar2,sr2,dr2]
94 args@[liveness, aa1,sa1,da1, aa2,sa2,da2] =
95 gmpTake2Return2 target (ar1,sr1,dr1, ar2,sr2,dr2) SLIT("mpz_divmod") (liveness, aa1,sa1,da1, aa2,sa2,da2)
97 genprim res@[ar1,sr1,dr1, ar2,sr2,dr2]
99 args@[liveness, aa1,sa1,da1, aa2,sa2,da2] =
100 gmpTake2Return2 target (ar1,sr1,dr1, ar2,sr2,dr2) SLIT("mpz_targetivmod") (liveness, aa1,sa1,da1, aa2,sa2,da2)
102 genprim res@[ar,sr,dr] IntegerAddOp args@[liveness, aa1,sa1,da1, aa2,sa2,da2] =
103 gmpTake2Return1 target (ar,sr,dr) SLIT("mpz_add") (liveness, aa1,sa1,da1, aa2,sa2,da2)
104 genprim res@[ar,sr,dr] IntegerSubOp args@[liveness, aa1,sa1,da1, aa2,sa2,da2] =
105 gmpTake2Return1 target (ar,sr,dr) SLIT("mpz_sub") (liveness, aa1,sa1,da1, aa2,sa2,da2)
106 genprim res@[ar,sr,dr] IntegerMulOp args@[liveness, aa1,sa1,da1, aa2,sa2,da2] =
107 gmpTake2Return1 target (ar,sr,dr) SLIT("mpz_mul") (liveness, aa1,sa1,da1, aa2,sa2,da2)
109 genprim res@[ar,sr,dr] IntegerNegOp arg@[liveness,aa,sa,da] =
110 gmpTake1Return1 target (ar,sr,dr) SLIT("mpz_neg") (liveness,aa,sa,da)
113 Since we are using the heap for intermediate @MP_INT@ structs, integer comparison
114 {\em does} require a heap check in the native code implementation.
117 genprim res@[exponr,ar,sr,dr] FloatDecodeOp args@[hp, arg] =
118 decodeFloatingKind FloatKind target (exponr,ar,sr,dr) (hp, arg)
120 genprim res@[exponr,ar,sr,dr] DoubleDecodeOp args@[hp, arg] =
121 decodeFloatingKind DoubleKind target (exponr,ar,sr,dr) (hp, arg)
123 genprim res@[ar,sr,dr] Int2IntegerOp args@[hp, n]
124 = gmpInt2Integer target (ar,sr,dr) (hp, n)
126 genprim res@[ar,sr,dr] Addr2IntegerOp args@[liveness,str]
127 = gmpString2Integer target (ar,sr,dr) (liveness,str)
129 genprim [res] IntegerCmpOp args@[hp, aa1,sa1,da1, aa2,sa2,da2]
130 = gmpCompare target res (hp, aa1,sa1,da1, aa2,sa2,da2)
132 genprim [res] Integer2IntOp arg@[hp, aa,sa,da]
133 = gmpInteger2Int target res (hp, aa,sa,da)
135 genprim [res] FloatEncodeOp args@[hp, aa,sa,da, expon] =
136 encodeFloatingKind FloatKind target res (hp, aa,sa,da, expon)
138 genprim [res] DoubleEncodeOp args@[hp, aa,sa,da, expon] =
139 encodeFloatingKind DoubleKind target res (hp, aa,sa,da, expon)
141 genprim [res] Int2AddrOp [arg] =
142 simpleCoercion AddrKind res arg
144 genprim [res] Addr2IntOp [arg] =
145 simpleCoercion IntKind res arg
147 genprim [res] Int2WordOp [arg] =
148 simpleCoercion IntKind{-WordKind?-} res arg
150 genprim [res] Word2IntOp [arg] =
151 simpleCoercion IntKind res arg
155 The @ErrorIO@ primitive is actually a bit weird...assign a new value to the root
156 closure, flush stdout and stderr, and jump to the @ErrorIO_innards@.
160 genprim [] ErrorIOPrimOp [rhs] =
161 let changeTop = StAssign PtrKind topClosure (a2stix rhs)
163 returnSUs (\xs -> changeTop : flushStdout : flushStderr : errorIO : xs)
167 @newArray#@ ops allocate heap space.
170 genprim [res] NewArrayOp args =
171 let [liveness, n, initial] = map a2stix args
173 space = StPrim IntAddOp [n, mut_hs]
174 loc = StIndex PtrKind stgHp
175 (StPrim IntNegOp [StPrim IntSubOp [space, StInt 1]])
176 assign = StAssign PtrKind result loc
177 initialise = StCall SLIT("newArrZh_init") VoidKind [result, n, initial]
179 heap_chkr liveness space (StInt 0) `thenSUs` \ heap_chk ->
181 returnSUs (heap_chk . (\xs -> assign : initialise : xs))
183 genprim [res] (NewByteArrayOp pk) args =
184 let [liveness, count] = map a2stix args
186 n = StPrim IntMulOp [count, StInt (toInteger (size_of pk))]
187 slop = StPrim IntAddOp [n, StInt (toInteger (size_of IntKind - 1))]
188 words = StPrim IntQuotOp [slop, StInt (toInteger (size_of IntKind))]
189 space = StPrim IntAddOp [n, StPrim IntAddOp [words, data_hs]]
190 loc = StIndex PtrKind stgHp
191 (StPrim IntNegOp [StPrim IntSubOp [space, StInt 1]])
192 assign = StAssign PtrKind result loc
193 init1 = StAssign PtrKind (StInd PtrKind loc) arrayOfData_info
194 init2 = StAssign IntKind
197 (StInt (toInteger fixed_hs))))
198 (StPrim IntAddOp [words,
199 StInt (toInteger (var_hs (DataRep 0)))])
201 heap_chkr liveness space (StInt 0) `thenSUs` \ heap_chk ->
203 returnSUs (heap_chk . (\xs -> assign : init1 : init2 : xs))
205 genprim [res] SameMutableArrayOp args =
206 let compare = StPrim AddrEqOp (map a2stix args)
207 assign = StAssign IntKind (a2stix res) compare
209 returnSUs (\xs -> assign : xs)
211 genprim res@[_] SameMutableByteArrayOp args =
212 genprim res SameMutableArrayOp args
216 Freezing an array of pointers is a double assignment. We fix the header of
217 the ``new'' closure because the lhs is probably a better addressing mode for
218 the indirection (most likely, it's a VanillaReg).
222 genprim [lhs] UnsafeFreezeArrayOp [rhs] =
223 let lhs' = a2stix lhs
225 header = StInd PtrKind lhs'
226 assign = StAssign PtrKind lhs' rhs'
227 freeze = StAssign PtrKind header imMutArrayOfPtrs_info
229 returnSUs (\xs -> assign : freeze : xs)
231 genprim [lhs] UnsafeFreezeByteArrayOp [rhs] =
232 simpleCoercion PtrKind lhs rhs
236 Most other array primitives translate to simple indexing.
240 genprim lhs@[_] IndexArrayOp args =
241 genprim lhs ReadArrayOp args
243 genprim [lhs] ReadArrayOp [obj, ix] =
244 let lhs' = a2stix lhs
247 base = StIndex IntKind obj' mut_hs
248 assign = StAssign PtrKind lhs' (StInd PtrKind (StIndex PtrKind base ix'))
250 returnSUs (\xs -> assign : xs)
252 genprim [lhs] WriteArrayOp [obj, ix, v] =
253 let obj' = a2stix obj
256 base = StIndex IntKind obj' mut_hs
257 assign = StAssign PtrKind (StInd PtrKind (StIndex PtrKind base ix')) v'
259 returnSUs (\xs -> assign : xs)
261 genprim lhs@[_] (IndexByteArrayOp pk) args =
262 genprim lhs (ReadByteArrayOp pk) args
264 -- NB: indexing in "pk" units, *not* in bytes (WDP 95/09)
266 genprim [lhs] (ReadByteArrayOp pk) [obj, ix] =
267 let lhs' = a2stix lhs
270 base = StIndex IntKind obj' data_hs
271 assign = StAssign pk lhs' (StInd pk (StIndex pk base ix'))
273 returnSUs (\xs -> assign : xs)
275 genprim [lhs] (IndexOffAddrOp pk) [obj, ix] =
276 let lhs' = a2stix lhs
279 assign = StAssign pk lhs' (StInd pk (StIndex pk obj' ix'))
281 returnSUs (\xs -> assign : xs)
283 genprim [] (WriteByteArrayOp pk) [obj, ix, v] =
284 let obj' = a2stix obj
287 base = StIndex IntKind obj' data_hs
288 assign = StAssign pk (StInd pk (StIndex pk base ix')) v'
290 returnSUs (\xs -> assign : xs)
293 Stable pointer operations.
299 genprim [lhs] DeRefStablePtrOp [sp] =
300 let lhs' = a2stix lhs
301 pk = getAmodeKind lhs
303 call = StCall SLIT("deRefStablePointer") pk [sp', smStablePtrTable]
304 assign = StAssign pk lhs' call
306 returnSUs (\xs -> assign : xs)
310 Now the hard one. For comparison, here's the code from StgMacros:
313 #define makeStablePtrZh(stablePtr,liveness,unstablePtr) \
315 EXTDATA(MK_INFO_LBL(StablePointerTable)); \
317 StgStablePtr newSP; \
319 if (SPT_EMPTY(StorageMgrInfo.StablePointerTable)) { /* free stack is empty */ \
320 I_ OldNoPtrs = SPT_NoPTRS(StorageMgrInfo.StablePointerTable); \
322 /* any strictly increasing expression will do here */ \
323 I_ NewNoPtrs = OldNoPtrs * 2 + 100; \
325 I_ NewSize = DYN_VHS + NewNoPtrs + 1 + NewNoPtrs; \
328 HEAP_CHK(NO_LIVENESS, _FHS+NewSize, 0); \
329 CC_ALLOC(CCC, _FHS+NewSize, SPT_K); /* cc prof */ \
331 SPTable = Hp + 1 - (_FHS + NewSize); \
332 SET_DYN_HDR(SPTable,StablePointerTable,CCC,NewSize,NewNoPtrs); \
333 SAFESTGCALL2(void, (void *, P_, P_), enlargeSPTable, SPTable, StorageMgrInfo.StablePointerTable); \
334 StorageMgrInfo.StablePointerTable = SPTable; \
337 newSP = SPT_POP(StorageMgrInfo.StablePointerTable); \
338 SPT_SPTR(StorageMgrInfo.StablePointerTable, newSP) = unstablePtr; \
343 ToDo ADR: finish this. (Boy, this is hard work!)
346 trMumbles are now just StMumbles.
347 StInt 1 is how to write ``1''
348 temporaries are allocated at the end of the heap (see notes in StixInteger)
354 genprim [lhs] MakeStablePtrOp args =
356 -- some useful abbreviations (I'm sure these must exist already)
357 add = trPrim . IntAddOp
358 sub = trPrim . IntSubOp
360 dec x = trAssign IntKind [x, sub [x, one]]
361 inc x = trAssign IntKind [x, add [x, one]]
363 -- tedious hardwiring in of closure layout offsets (from SMClosures)
364 dynHS = 2 + fixedHeaderSize md sty + varHeaderSize md sty DynamicRep
365 spt_SIZE c = trIndex PtrKind [c, trInt [fhs + gc_reserved] ]
366 spt_NoPTRS c = trIndex PtrKind [c, trInt [fhs + gc_reserved + 1] ]
367 spt_SPTR c i = trIndex PtrKind [c, add [trInt [dynHS], i]]
368 spt_TOP c = trIndex PtrKind [c, add [trInt [dynHS], spt_NoPTRS c]]
369 spt_FREE c i = trIndex PtrKind [c, add [trInt [dynHS], spt_NoPTRS c]]
371 -- tedious hardwiring in of stack manipulation macros (from SMClosures)
373 trCondJump lbl [trPrim IntEqOp [spt_TOP c, spt_NoPTRS c]]
375 trCondJump lbl [trPrim IntEqOp [spt_TOP c, trInt [0]]]
377 trAssign PtrKind [spt_FREE c (spt_TOP c), f],
381 trAssign PtrKind [x, spt_FREE c (spt_TOP c)]
384 -- now to get down to business
385 lhs' = amodeCode sty md lhs
386 [liveness, unstable] = map (amodeCode sty md) args
388 spt = smStablePtrTable
390 newSPT = -- a temporary (don't know how to allocate it)
391 newSP = -- another temporary
393 allocNewTable = -- some sort fo heap allocation needed
394 copyOldTable = trCall "enlargeSPTable" PtrKind [newSPT, spt]
399 trAssign PtrKind [spt, newSPT]
402 trAssign PtrKind [spt_SPTR spt newSP, unstable],
403 trAssign StablePtrKind [lhs', newSP]
407 getUniqLabelCTS `thenCTS` \ oklbl ->
409 (spt_EMPTY spt oklbl : (enlarge ++ (trLabel [oklbl] : allocate)))
413 genprim res Word2IntegerOp args = panic "genPrimCode:Word2IntegerOp"
415 genprim lhs (CCallOp fn is_asm may_gc arg_tys result_ty) rhs
416 | is_asm = error "ERROR: Native code generator can't handle casm"
419 [] -> returnSUs (\xs -> (StCall fn VoidKind args) : xs)
421 let lhs' = a2stix lhs
422 pk = if isFloatingKind (getAmodeKind lhs) then DoubleKind else IntKind
423 call = StAssign pk lhs' (StCall fn pk args)
425 returnSUs (\xs -> call : xs)
427 args = map amodeCodeForCCall rhs
428 amodeCodeForCCall x =
431 case getAmodeKind x of
432 ArrayKind -> StIndex PtrKind base mut_hs
433 ByteArrayKind -> StIndex IntKind base data_hs
434 MallocPtrKind -> error "ERROR: native-code generator can't handle Malloc Ptrs (yet): use -fvia-C!"
438 Now the more mundane operations.
442 let lhs' = map a2stix lhs
443 rhs' = map a2stix' rhs
445 returnSUs (\ xs -> simplePrim lhs' op rhs' : xs)
453 -> SUniqSM StixTreeList
455 simpleCoercion pk lhs rhs =
456 returnSUs (\xs -> StAssign pk (a2stix lhs) (a2stix rhs) : xs)
460 Here we try to rewrite primitives into a form the code generator
461 can understand. Any primitives not handled here must be handled
462 at the level of the specific code generator.
475 Now look for something more conventional.
479 simplePrim [lhs] op rest = StAssign pk lhs (StPrim op rest)
480 where pk = if isCompareOp op then IntKind
481 else case getPrimOpResultInfo op of
483 _ -> simplePrim_error op
485 simplePrim _ op _ = simplePrim_error op
488 = error ("ERROR: primitive operation `"++showPrimOp PprDebug op++"'cannot be handled\nby the native-code generator. Workaround: use -fvia-C.\n(Perhaps you should report it as a GHC bug, also.)\n")
491 %---------------------------------------------------------------------
493 Here we generate the Stix code for CAddrModes.
495 When a character is fetched from a mixed type location, we have to
496 do an extra cast. This is reflected in amodeCode', which is for rhs
497 amodes that might possibly need the extra cast.
501 amodeCode, amodeCode'
506 amodeCode'{-'-} target_STRICT am@(CVal rr CharKind)
507 | mixedTypeLocn am = StPrim ChrOp [amodeToStix target am]
508 | otherwise = amodeToStix target am
510 amodeCode' target am = amodeToStix target am
512 amodeCode target_STRICT am
515 -- grab "target" things:
516 hp_rel = hpRel target
517 char_like = charLikeClosureSize target
518 int_like = intLikeClosureSize target
519 a2stix = amodeToStix target
521 -- real code: ----------------------------------
522 acode am@(CVal rr CharKind) | mixedTypeLocn am =
523 StInd IntKind (acode (CAddr rr))
525 acode (CVal rr pk) = StInd pk (acode (CAddr rr))
527 acode (CAddr r@(SpARel spA off)) =
528 StIndex PtrKind stgSpA (StInt (toInteger (spARelToInt r)))
530 acode (CAddr r@(SpBRel spB off)) =
531 StIndex IntKind stgSpB (StInt (toInteger (spBRelToInt r)))
533 acode (CAddr (HpRel hp off)) =
534 StIndex IntKind stgHp (StInt (toInteger (-(hp_rel (hp `subOff` off)))))
536 acode (CAddr (NodeRel off)) =
537 StIndex IntKind stgNode (StInt (toInteger (hp_rel off)))
539 acode (CReg magic) = StReg (StixMagicId magic)
540 acode (CTemp uniq pk) = StReg (StixTemp uniq pk)
542 acode (CLbl lbl _) = StCLbl lbl
544 acode (CUnVecLbl dir _) = StCLbl dir
546 acode (CTableEntry base off pk) =
547 StInd pk (StIndex pk (acode base) (acode off))
549 -- For CharLike and IntLike, we attempt some trivial constant-folding here.
551 acode (CCharLike (CLit (MachChar c))) =
552 StLitLbl (uppBeside (uppPStr SLIT("CHARLIKE_closures+")) (uppInt off))
553 where off = char_like * ord c
555 acode (CCharLike x) =
556 StPrim IntAddOp [charLike, off]
557 where off = StPrim IntMulOp [acode x,
558 StInt (toInteger (char_like))]
560 acode (CIntLike (CLit (MachInt i _))) =
561 StPrim IntAddOp [intLikePtr, StInt off]
562 where off = toInteger int_like * i
565 StPrim IntAddOp [intLikePtr, off]
566 where off = StPrim IntMulOp [acode x,
567 StInt (toInteger int_like)]
569 -- A CString is just a (CLit . MachStr)
570 acode (CString s) = StString s
572 acode (CLit core) = case core of
573 (MachChar c) -> StInt (toInteger (ord c))
574 (MachStr s) -> StString s
575 (MachAddr a) -> StInt a
576 (MachInt i _) -> StInt i
577 (MachLitLit s _) -> StLitLit s
578 (MachFloat d) -> StDouble d
579 (MachDouble d) -> StDouble d
580 _ -> panic "amodeCode:core literal"
582 -- A CLitLit is just a (CLit . MachLitLit)
583 acode (CLitLit s _) = StLitLit s
585 -- COffsets are in words, not bytes!
586 acode (COffset off) = StInt (toInteger (hp_rel off))
588 acode (CMacroExpr _ macro [arg]) =
590 INFO_PTR -> StInd PtrKind (a2stix arg)
591 ENTRY_CODE -> a2stix arg
593 EVAL_TAG -> StPrim IntGeOp [tag, StInt 0]
595 tag = StInd IntKind (StIndex IntKind (a2stix arg) (StInt (-2)))
596 -- That ``-2'' really bothers me. (JSM)
598 acode (CCostCentre cc print_as_string)
599 = if noCostCentreAttached cc
600 then StComment SLIT("") -- sigh
601 else panic "amodeCode:CCostCentre"
604 Sizes of the CharLike and IntLike closures that are arranged as arrays in the
605 data segment. (These are in bytes.)
609 -- The INTLIKE base pointer
611 intLikePtr :: StixTree
613 intLikePtr = StInd PtrKind (sStLitLbl SLIT("INTLIKE_closures"))
619 charLike = sStLitLbl SLIT("CHARLIKE_closures")
621 -- Trees for the ErrorIOPrimOp
623 topClosure, flushStdout, flushStderr, errorIO :: StixTree
625 topClosure = StInd PtrKind (sStLitLbl SLIT("TopClosure"))
626 flushStdout = StCall SLIT("fflush") VoidKind [StLitLit SLIT("stdout")]
627 flushStderr = StCall SLIT("fflush") VoidKind [StLitLit SLIT("stderr")]
628 errorIO = StJump (StInd PtrKind (sStLitLbl SLIT("ErrorIO_innards")))