2 % (c) The AQUA Project, Glasgow University, 1993-1996
6 #include "HsVersions.h"
8 module StixPrim ( primCode, amodeToStix, amodeToStix' ) where
11 #if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ <= 201
12 IMPORT_DELOOPER(NcgLoop) -- paranoia checking only
19 import AbsCUtils ( getAmodeRep, mixedTypeLocn )
20 import Constants ( spARelToInt, spBRelToInt )
21 import CostCentre ( noCostCentreAttached )
22 import HeapOffs ( hpRelToInt, subOff )
23 import Literal ( Literal(..) )
24 import PrimOp ( PrimOp(..), isCompareOp, showPrimOp,
25 getPrimOpResultInfo, PrimOpResultInfo(..)
27 import PrimRep ( PrimRep(..), isFloatingRep )
28 import OrdList ( OrdList )
29 import Outputable ( PprStyle(..) )
30 import SMRep ( SMRep(..), SMSpecRepKind, SMUpdateKind )
32 import StixMacro ( heapCheck )
33 import StixInteger {- everything -}
34 import UniqSupply ( returnUs, thenUs, SYN_IE(UniqSM) )
35 import Pretty ( (<>), ptext, int )
38 #ifdef REALLY_HASKELL_1_3
39 ord = fromEnum :: Char -> Int
43 The main honcho here is primCode, which handles the guts of COpStmts.
46 arrayOfData_info = sStLitLbl SLIT("ArrayOfData_info") -- out here to avoid CAF (sigh)
47 imMutArrayOfPtrs_info = sStLitLbl SLIT("ImMutArrayOfPtrs_info")
50 :: [CAddrMode] -- results
52 -> [CAddrMode] -- args
53 -> UniqSM StixTreeList
56 First, the dreaded @ccall@. We can't handle @casm@s.
58 Usually, this compiles to an assignment, but when the left-hand side
59 is empty, we just perform the call and ignore the result.
61 ToDo ADR: modify this to handle ForeignObjs.
63 btw Why not let programmer use casm to provide assembly code instead
66 The (MP) integer operations are a true nightmare. Since we don't have
67 a convenient abstract way of allocating temporary variables on the (C)
68 stack, we use the space just below HpLim for the @MP_INT@ structures,
69 and modify our heap check accordingly.
72 -- NB: ordering of clauses somewhere driven by
73 -- the desire to getting sane patt-matching behavior
75 primCode res@[ar1,sr1,dr1, ar2,sr2,dr2]
77 args@[liveness, aa1,sa1,da1, aa2,sa2,da2]
78 = gmpTake2Return2 (ar1,sr1,dr1, ar2,sr2,dr2) SLIT("mpz_divmod") (liveness, aa1,sa1,da1, aa2,sa2,da2)
80 primCode res@[ar1,sr1,dr1, ar2,sr2,dr2]
82 args@[liveness, aa1,sa1,da1, aa2,sa2,da2]
83 = gmpTake2Return2 (ar1,sr1,dr1, ar2,sr2,dr2) SLIT("mpz_targetivmod") (liveness, aa1,sa1,da1, aa2,sa2,da2)
85 primCode res@[ar,sr,dr] IntegerAddOp args@[liveness, aa1,sa1,da1, aa2,sa2,da2]
86 = gmpTake2Return1 (ar,sr,dr) SLIT("mpz_add") (liveness, aa1,sa1,da1, aa2,sa2,da2)
87 primCode res@[ar,sr,dr] IntegerSubOp args@[liveness, aa1,sa1,da1, aa2,sa2,da2]
88 = gmpTake2Return1 (ar,sr,dr) SLIT("mpz_sub") (liveness, aa1,sa1,da1, aa2,sa2,da2)
89 primCode res@[ar,sr,dr] IntegerMulOp args@[liveness, aa1,sa1,da1, aa2,sa2,da2]
90 = gmpTake2Return1 (ar,sr,dr) SLIT("mpz_mul") (liveness, aa1,sa1,da1, aa2,sa2,da2)
92 primCode res@[ar,sr,dr] IntegerNegOp arg@[liveness,aa,sa,da]
93 = gmpTake1Return1 (ar,sr,dr) SLIT("mpz_neg") (liveness,aa,sa,da)
96 Since we are using the heap for intermediate @MP_INT@ structs, integer
97 comparison {\em does} require a heap check in the native code
101 primCode res@[exponr,ar,sr,dr] FloatDecodeOp args@[hp, arg]
102 = decodeFloatingKind FloatRep (exponr,ar,sr,dr) (hp, arg)
104 primCode res@[exponr,ar,sr,dr] DoubleDecodeOp args@[hp, arg]
105 = decodeFloatingKind DoubleRep (exponr,ar,sr,dr) (hp, arg)
107 primCode res@[ar,sr,dr] Int2IntegerOp args@[hp, n]
108 = gmpInt2Integer (ar,sr,dr) (hp, n)
110 primCode res@[ar,sr,dr] Addr2IntegerOp args@[liveness,str]
111 = gmpString2Integer (ar,sr,dr) (liveness,str)
113 primCode [res] IntegerCmpOp args@[hp, aa1,sa1,da1, aa2,sa2,da2]
114 = gmpCompare res (hp, aa1,sa1,da1, aa2,sa2,da2)
116 primCode [res] Integer2IntOp arg@[hp, aa,sa,da]
117 = gmpInteger2Int res (hp, aa,sa,da)
119 primCode [res] FloatEncodeOp args@[hp, aa,sa,da, expon]
120 = encodeFloatingKind FloatRep res (hp, aa,sa,da, expon)
122 primCode [res] DoubleEncodeOp args@[hp, aa,sa,da, expon]
123 = encodeFloatingKind DoubleRep res (hp, aa,sa,da, expon)
125 primCode [res] Int2AddrOp [arg]
126 = simpleCoercion AddrRep res arg
128 primCode [res] Addr2IntOp [arg]
129 = simpleCoercion IntRep res arg
131 primCode [res] Int2WordOp [arg]
132 = simpleCoercion IntRep{-WordRep?-} res arg
134 primCode [res] Word2IntOp [arg]
135 = simpleCoercion IntRep res arg
138 The @ErrorIO@ primitive is actually a bit weird...assign a new value
139 to the root closure, flush stdout and stderr, and jump to the
143 primCode [] ErrorIOPrimOp [rhs]
145 changeTop = StAssign PtrRep topClosure (amodeToStix rhs)
147 returnUs (\xs -> changeTop : flushStdout : flushStderr : errorIO : xs)
150 @newArray#@ ops allocate heap space.
153 primCode [res] NewArrayOp args
155 [liveness, n, initial] = map amodeToStix args
156 result = amodeToStix res
157 space = StPrim IntAddOp [n, mutHS]
158 loc = StIndex PtrRep stgHp
159 (StPrim IntNegOp [StPrim IntSubOp [space, StInt 1]])
160 assign = StAssign PtrRep result loc
161 initialise = StCall SLIT("newArrZh_init") VoidRep [result, n, initial]
163 heapCheck liveness space (StInt 0) `thenUs` \ heap_chk ->
165 returnUs (heap_chk . (\xs -> assign : initialise : xs))
167 primCode [res] (NewByteArrayOp pk) args
169 [liveness, count] = map amodeToStix args
170 result = amodeToStix res
171 n = StPrim IntMulOp [count, StInt (sizeOf pk)]
172 slop = StPrim IntAddOp [n, StInt (sizeOf IntRep - 1)]
173 words = StPrim IntQuotOp [slop, StInt (sizeOf IntRep)]
174 space = StPrim IntAddOp [n, StPrim IntAddOp [words, dataHS]]
175 loc = StIndex PtrRep stgHp
176 (StPrim IntNegOp [StPrim IntSubOp [space, StInt 1]])
177 assign = StAssign PtrRep result loc
178 init1 = StAssign PtrRep (StInd PtrRep loc) arrayOfData_info
179 init2 = StAssign IntRep
182 (StInt (toInteger fixedHdrSizeInWords))))
183 (StPrim IntAddOp [words,
184 StInt (toInteger (varHdrSizeInWords (DataRep 0)))])
186 heapCheck liveness space (StInt 0) `thenUs` \ heap_chk ->
188 returnUs (heap_chk . (\xs -> assign : init1 : init2 : xs))
190 primCode [res] SameMutableArrayOp args
192 compare = StPrim AddrEqOp (map amodeToStix args)
193 assign = StAssign IntRep (amodeToStix res) compare
195 returnUs (\xs -> assign : xs)
197 primCode res@[_] SameMutableByteArrayOp args
198 = primCode res SameMutableArrayOp args
201 Freezing an array of pointers is a double assignment. We fix the
202 header of the ``new'' closure because the lhs is probably a better
203 addressing mode for the indirection (most likely, it's a VanillaReg).
207 primCode [lhs] UnsafeFreezeArrayOp [rhs]
209 lhs' = amodeToStix lhs
210 rhs' = amodeToStix rhs
211 header = StInd PtrRep lhs'
212 assign = StAssign PtrRep lhs' rhs'
213 freeze = StAssign PtrRep header imMutArrayOfPtrs_info
215 returnUs (\xs -> assign : freeze : xs)
217 primCode [lhs] UnsafeFreezeByteArrayOp [rhs]
218 = simpleCoercion PtrRep lhs rhs
221 Most other array primitives translate to simple indexing.
225 primCode lhs@[_] IndexArrayOp args
226 = primCode lhs ReadArrayOp args
228 primCode [lhs] ReadArrayOp [obj, ix]
230 lhs' = amodeToStix lhs
231 obj' = amodeToStix obj
233 base = StIndex IntRep obj' mutHS
234 assign = StAssign PtrRep lhs' (StInd PtrRep (StIndex PtrRep base ix'))
236 returnUs (\xs -> assign : xs)
238 primCode [] WriteArrayOp [obj, ix, v]
240 obj' = amodeToStix obj
243 base = StIndex IntRep obj' mutHS
244 assign = StAssign PtrRep (StInd PtrRep (StIndex PtrRep base ix')) v'
246 returnUs (\xs -> assign : xs)
248 primCode lhs@[_] (IndexByteArrayOp pk) args
249 = primCode lhs (ReadByteArrayOp pk) args
251 -- NB: indexing in "pk" units, *not* in bytes (WDP 95/09)
253 primCode [lhs] (ReadByteArrayOp pk) [obj, ix]
255 lhs' = amodeToStix lhs
256 obj' = amodeToStix obj
258 base = StIndex IntRep obj' dataHS
259 assign = StAssign pk lhs' (StInd pk (StIndex pk base ix'))
261 returnUs (\xs -> assign : xs)
263 primCode [lhs] (IndexOffAddrOp pk) [obj, ix]
265 lhs' = amodeToStix lhs
266 obj' = amodeToStix obj
268 assign = StAssign pk lhs' (StInd pk (StIndex pk obj' ix'))
270 returnUs (\xs -> assign : xs)
272 primCode [lhs] (IndexOffForeignObjOp pk) [obj, ix]
274 lhs' = amodeToStix lhs
275 obj' = amodeToStix obj
277 obj'' = StIndex PtrRep obj' foHS
278 assign = StAssign pk lhs' (StInd pk (StIndex pk obj'' ix'))
280 returnUs (\xs -> assign : xs)
282 primCode [] (WriteByteArrayOp pk) [obj, ix, v]
284 obj' = amodeToStix obj
287 base = StIndex IntRep obj' dataHS
288 assign = StAssign pk (StInd pk (StIndex pk base ix')) v'
290 returnUs (\xs -> assign : xs)
293 Stable pointer operations.
298 primCode [lhs] DeRefStablePtrOp [sp]
300 lhs' = amodeToStix lhs
303 call = StCall SLIT("deRefStablePointer") pk [sp', smStablePtrTable]
304 assign = StAssign pk lhs' call
306 returnUs (\xs -> assign : xs)
309 Now the hard one. For comparison, here's the code from StgMacros:
312 #define makeStablePtrZh(stablePtr,liveness,unstablePtr) \
314 EXTDATA(MK_INFO_LBL(StablePointerTable)); \
316 StgStablePtr newSP; \
318 if (SPT_EMPTY(StorageMgrInfo.StablePointerTable)) { /* free stack is empty */ \
319 I_ OldNoPtrs = SPT_NoPTRS(StorageMgrInfo.StablePointerTable); \
321 /* any strictly increasing expression will do here */ \
322 I_ NewNoPtrs = OldNoPtrs * 2 + 100; \
324 I_ NewSize = DYN_VHS + NewNoPtrs + 1 + NewNoPtrs; \
327 HEAP_CHK(NO_LIVENESS, _FHS+NewSize, 0); \
328 CC_ALLOC(CCC, _FHS+NewSize, SPT_K); /* cc prof */ \
330 SPTable = Hp + 1 - (_FHS + NewSize); \
331 SET_DYN_HDR(SPTable,StablePointerTable,CCC,NewSize,NewNoPtrs); \
332 SAFESTGCALL2(void, (void *, P_, P_), enlargeSPTable, SPTable, StorageMgrInfo.StablePointerTable); \
333 StorageMgrInfo.StablePointerTable = SPTable; \
336 newSP = SPT_POP(StorageMgrInfo.StablePointerTable); \
337 SPT_SPTR(StorageMgrInfo.StablePointerTable, newSP) = unstablePtr; \
342 ToDo ADR: finish this. (Boy, this is hard work!)
345 trMumbles are now just StMumbles.
346 StInt 1 is how to write ``1''
347 temporaries are allocated at the end of the heap (see notes in StixInteger)
353 primCode [lhs] MakeStablePtrOp args
355 -- some useful abbreviations (I'm sure these must exist already)
356 add = trPrim . IntAddOp
357 sub = trPrim . IntSubOp
359 dec x = trAssign IntRep [x, sub [x, one]]
360 inc x = trAssign IntRep [x, add [x, one]]
362 -- tedious hardwiring in of closure layout offsets (from SMClosures)
363 dynHS = 2 + fixedHdrSizeInWords + varHdrSizeInWords DynamicRep
364 spt_SIZE c = trIndex PtrRep [c, trInt [fhs + gc_reserved] ]
365 spt_NoPTRS c = trIndex PtrRep [c, trInt [fhs + gc_reserved + 1] ]
366 spt_SPTR c i = trIndex PtrRep [c, add [trInt [dynHS], i]]
367 spt_TOP c = trIndex PtrRep [c, add [trInt [dynHS], spt_NoPTRS c]]
368 spt_FREE c i = trIndex PtrRep [c, add [trInt [dynHS], spt_NoPTRS c]]
370 -- tedious hardwiring in of stack manipulation macros (from SMClosures)
372 trCondJump lbl [trPrim IntEqOp [spt_TOP c, spt_NoPTRS c]]
374 trCondJump lbl [trPrim IntEqOp [spt_TOP c, trInt [0]]]
376 trAssign PtrRep [spt_FREE c (spt_TOP c), f],
380 trAssign PtrRep [x, spt_FREE c (spt_TOP c)]
383 -- now to get down to business
385 [liveness, unstable] = map amodeCode args
387 spt = smStablePtrTable
389 newSPT = -- a temporary (don't know how to allocate it)
390 newSP = -- another temporary
392 allocNewTable = -- some sort fo heap allocation needed
393 copyOldTable = trCall "enlargeSPTable" PtrRep [newSPT, spt]
398 trAssign PtrRep [spt, newSPT]
401 trAssign PtrRep [spt_SPTR spt newSP, unstable],
402 trAssign StablePtrRep [lhs', newSP]
406 getUniqLabelCTS `thenCTS` \ oklbl ->
408 (spt_EMPTY spt oklbl : (enlarge ++ (trLabel [oklbl] : allocate)))
412 primCode res Word2IntegerOp args = panic "primCode:Word2IntegerOp"
414 primCode lhs (CCallOp fn is_asm may_gc arg_tys result_ty) rhs
415 | is_asm = error "ERROR: Native code generator can't handle casm"
418 [] -> returnUs (\xs -> (StCall fn VoidRep args) : xs)
420 let lhs' = amodeToStix lhs
421 pk = if isFloatingRep (getAmodeRep lhs) then DoubleRep else IntRep
422 call = StAssign pk lhs' (StCall fn pk args)
424 returnUs (\xs -> call : xs)
426 args = map amodeCodeForCCall rhs
427 amodeCodeForCCall x =
428 let base = amodeToStix' x
430 case getAmodeRep x of
431 ArrayRep -> StIndex PtrRep base mutHS
432 ByteArrayRep -> StIndex IntRep base dataHS
433 ForeignObjRep -> StIndex PtrRep base foHS
434 {-error "ERROR: native-code generator can't handle ForeignObjs (yet): use -fvia-C!"-}
438 Now the more mundane operations.
443 lhs' = map amodeToStix lhs
444 rhs' = map amodeToStix' rhs
446 returnUs (\ xs -> simplePrim lhs' op rhs' : xs)
454 -> UniqSM StixTreeList
456 simpleCoercion pk lhs rhs
457 = returnUs (\xs -> StAssign pk (amodeToStix lhs) (amodeToStix rhs) : xs)
460 Here we try to rewrite primitives into a form the code generator can
461 understand. Any primitives not handled here must be handled at the
462 level of the specific code generator.
472 Now look for something more conventional.
475 simplePrim [lhs] op rest
476 = StAssign pk lhs (StPrim op rest)
478 pk = if isCompareOp op then
481 case getPrimOpResultInfo op of
483 _ -> simplePrim_error op
485 simplePrim as op bs = 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 do
496 an extra cast. This is reflected in amodeCode', which is for rhs
497 amodes that might possibly need the extra cast.
500 amodeToStix, amodeToStix' :: CAddrMode -> StixTree
502 amodeToStix'{-'-} am@(CVal rr CharRep)
503 | mixedTypeLocn am = StPrim ChrOp [amodeToStix am]
504 | otherwise = amodeToStix am
506 amodeToStix' am = amodeToStix am
509 amodeToStix am@(CVal rr CharRep)
511 = StInd IntRep (amodeToStix (CAddr rr))
513 amodeToStix (CVal rr pk) = StInd pk (amodeToStix (CAddr rr))
515 amodeToStix (CAddr (SpARel spA off))
516 = StIndex PtrRep stgSpA (StInt (toInteger (spARelToInt spA off)))
518 amodeToStix (CAddr (SpBRel spB off))
519 = StIndex IntRep stgSpB (StInt (toInteger (spBRelToInt spB off)))
521 amodeToStix (CAddr (HpRel hp off))
522 = StIndex IntRep stgHp (StInt (toInteger (-(hpRelToInt (hp `subOff` off)))))
524 amodeToStix (CAddr (NodeRel off))
525 = StIndex IntRep stgNode (StInt (toInteger (hpRelToInt off)))
527 amodeToStix (CReg magic) = StReg (StixMagicId magic)
528 amodeToStix (CTemp uniq pk) = StReg (StixTemp uniq pk)
530 amodeToStix (CLbl lbl _) = StCLbl lbl
531 amodeToStix (CUnVecLbl dir _) = StCLbl dir
533 amodeToStix (CTableEntry base off pk)
534 = StInd pk (StIndex pk (amodeToStix base) (amodeToStix off))
536 -- For CharLike and IntLike, we attempt some trivial constant-folding here.
538 amodeToStix (CCharLike (CLit (MachChar c)))
539 = StLitLbl ((<>) (ptext SLIT("CHARLIKE_closures+")) (int off))
541 off = charLikeSize * ord c
543 amodeToStix (CCharLike x)
544 = StPrim IntAddOp [charLike, off]
546 off = StPrim IntMulOp [amodeToStix x, StInt (toInteger charLikeSize)]
548 amodeToStix (CIntLike (CLit (MachInt i _)))
549 = StPrim IntAddOp [intLikePtr, StInt off]
551 off = toInteger intLikeSize * i
553 amodeToStix (CIntLike x)
554 = StPrim IntAddOp [intLikePtr, off]
556 off = StPrim IntMulOp [amodeToStix x, StInt (toInteger intLikeSize)]
558 -- A CString is just a (CLit . MachStr)
559 amodeToStix (CString s) = StString s
561 amodeToStix (CLit core)
563 MachChar c -> StInt (toInteger (ord c))
564 MachStr s -> StString s
565 MachAddr a -> StInt a
566 MachInt i _ -> StInt i
567 MachLitLit s _ -> StLitLit s
568 MachFloat d -> StDouble d
569 MachDouble d -> StDouble d
570 _ -> panic "amodeToStix:core literal"
572 -- A CLitLit is just a (CLit . MachLitLit)
573 amodeToStix (CLitLit s _) = StLitLit s
575 -- COffsets are in words, not bytes!
576 amodeToStix (COffset off) = StInt (toInteger (hpRelToInt off))
578 amodeToStix (CMacroExpr _ macro [arg])
580 INFO_PTR -> StInd PtrRep (amodeToStix arg)
581 ENTRY_CODE -> amodeToStix arg
583 EVAL_TAG -> StPrim IntGeOp [tag, StInt 0]
585 tag = StInd IntRep (StIndex IntRep (amodeToStix arg) (StInt (-2)))
586 -- That ``-2'' really bothers me. (JSM) (Replace w/ oTHER_TAG? [WDP])
588 amodeToStix (CCostCentre cc print_as_string)
589 = if noCostCentreAttached cc
590 then StComment SLIT("") -- sigh
591 else panic "amodeToStix:CCostCentre"
594 Sizes of the CharLike and IntLike closures that are arranged as arrays
595 in the data segment. (These are in bytes.)
598 -- The INTLIKE base pointer
600 intLikePtr :: StixTree
602 intLikePtr = StInd PtrRep (sStLitLbl SLIT("INTLIKE_closures"))
608 charLike = sStLitLbl SLIT("CHARLIKE_closures")
610 -- Trees for the ErrorIOPrimOp
612 topClosure, flushStdout, flushStderr, errorIO :: StixTree
614 topClosure = StInd PtrRep (sStLitLbl SLIT("TopClosure"))
615 flushStdout = StCall SLIT("fflush") VoidRep [StLitLit SLIT("stdout")]
616 flushStderr = StCall SLIT("fflush") VoidRep [StLitLit SLIT("stderr")]
617 errorIO = StJump (StInd PtrRep (sStLitLbl SLIT("ErrorIO_innards")))