[project @ 1998-12-02 13:17:09 by simonm]
[ghc-hetmet.git] / ghc / compiler / codeGen / CgExpr.lhs
index 0fa8966..e6c9833 100644 (file)
@@ -1,5 +1,7 @@
 %
-% (c) The GRASP/AQUA Project, Glasgow University, 1992-1995
+% (c) The GRASP/AQUA Project, Glasgow University, 1992-1998
+%
+% $Id: CgExpr.lhs,v 1.15 1998/12/02 13:17:49 simonm Exp $
 %
 %********************************************************
 %*                                                     *
 %********************************************************
 
 \begin{code}
-module CgExpr ( cgExpr, getPrimOpArgAmodes ) where
+module CgExpr ( cgExpr ) where
 
 #include "HsVersions.h"
 
-import Constants       ( mAX_SPEC_SELECTEE_SIZE )
+import Constants       ( mAX_SPEC_SELECTEE_SIZE, mAX_SPEC_AP_SIZE )
 import StgSyn
 import CgMonad
 import AbsCSyn
-
-import AbsCUtils       ( mkAbsCStmts, mkAbstractCs )
-import CgBindery       ( getArgAmodes, getCAddrModeAndInfo, CgIdInfo )
-import CgCase          ( cgCase, saveVolatileVarsAndRegs )
-import CgClosure       ( cgRhsClosure )
+import CLabel          ( mkClosureTblLabel )
+
+import SMRep           ( fixedHdrSize )
+import CgBindery       ( getArgAmodes, CgIdInfo, nukeDeadBindings )
+import CgCase          ( cgCase, saveVolatileVarsAndRegs, 
+                         restoreCurrentCostCentre,
+                         splitAlgTyConAppThroughNewTypes )
+import CgClosure       ( cgRhsClosure, cgStdRhsClosure )
 import CgCon           ( buildDynCon, cgReturnDataCon )
-import CgHeapery       ( allocHeap )
 import CgLetNoEscape   ( cgLetNoEscapeClosure )
-import CgRetConv       ( dataReturnConvAlg, ctrlReturnConvAlg,
-                         DataReturnConvention(..), CtrlReturnConvention(..),
-                         assignPrimOpResultRegs, makePrimOpArgsRobust
+import CgRetConv       ( dataReturnConvPrim )
+import CgTailCall      ( cgTailCall, performReturn, performPrimReturn,
+                         mkDynamicAlgReturnCode, mkPrimReturnCode,
+                         tailCallPrimOp, returnUnboxedTuple
                        )
-import CgTailCall      ( cgTailCall, performReturn,
-                         mkDynamicAlgReturnCode, mkPrimReturnCode
-                       )
-import CLabel          ( mkPhantomInfoTableLabel, mkInfoTableVecTblLabel )
-import ClosureInfo     ( mkClosureLFInfo, mkSelectorLFInfo, mkVapLFInfo,
-                         layOutDynCon )
+import ClosureInfo     ( mkClosureLFInfo, mkSelectorLFInfo,
+                         mkApLFInfo, layOutDynCon )
 import CostCentre      ( sccAbleCostCentre, isDictCC, isSccCountCostCentre )
-import HeapOffs                ( VirtualSpBOffset, intOffsetIntoGoods )
-import Id              ( dataConTyCon, idPrimRep, getIdArity, 
-                         mkIdSet, unionIdSets, GenId{-instance Outputable-},
-                         Id
-                       )
+import Id              ( idPrimRep, idType, Id )
+import VarSet
+import DataCon         ( DataCon, dataConTyCon )
+import Const           ( Con(..) )
 import IdInfo          ( ArityInfo(..) )
-import Name            ( isLocallyDefined )
-import PrimOp          ( primOpCanTriggerGC, primOpHeapReq, HeapRequirement(..),
+import PrimOp          ( primOpOutOfLine, 
                          getPrimOpResultInfo, PrimOp(..), PrimOpResultInfo(..)
                        )
-import PrimRep         ( getPrimRepSize, PrimRep(..) )
-import TyCon           ( tyConDataCons, maybeTyConSingleCon  )
+import PrimRep         ( getPrimRepSize, PrimRep(..), isFollowableRep )
+import TyCon           ( maybeTyConSingleCon,
+                         isUnboxedTupleTyCon, isEnumerationTyCon )
+import Type            ( Type, typePrimRep )
 import Maybes          ( assocMaybe, maybeToBool )
-import Util            ( isIn )
+import Unique          ( mkBuiltinUnique )
+import BasicTypes      ( TopLevelFlag(..), RecFlag(..) )
 import Outputable
 \end{code}
 
@@ -72,7 +74,7 @@ cgExpr        :: StgExpr              -- input
 @(STGApp (StgLitArg 42) [])@.
 
 \begin{code}
-cgExpr (StgApp fun args live_vars) = cgTailCall fun args live_vars
+cgExpr (StgApp fun args) = cgTailCall fun args
 \end{code}
 
 %********************************************************
@@ -82,13 +84,24 @@ cgExpr (StgApp fun args live_vars) = cgTailCall fun args live_vars
 %********************************************************
 
 \begin{code}
-cgExpr (StgCon con args live_vars)
+cgExpr (StgCon (DataCon con) args res_ty)
   = getArgAmodes args `thenFC` \ amodes ->
-    cgReturnDataCon con amodes (all zero_size args) live_vars
+    cgReturnDataCon con amodes (all zero_size args)
   where
     zero_size atom = getPrimRepSize (getArgPrimRep atom) == 0
 \end{code}
 
+Literals are similar to constructors; they return by putting
+themselves in an appropriate register and returning to the address on
+top of the stack.
+
+\begin{code}
+cgExpr (StgCon (Literal lit) args res_ty)
+  = ASSERT( null args )
+    performPrimReturn (CLit lit)
+\end{code}
+
+
 %********************************************************
 %*                                                     *
 %*             STG PrimApps  (unboxed primitive ops)   *
@@ -97,116 +110,57 @@ cgExpr (StgCon con args live_vars)
 
 Here is where we insert real live machine instructions.
 
+NOTE about _ccall_GC_:
+
+A _ccall_GC_ is treated as an out-of-line primop for the case
+expression code, because we want a proper stack frame on the stack
+when we perform it.  When we get here, however, we need to actually
+perform the call, so we treat it an an inline primop.
+
 \begin{code}
-cgExpr x@(StgPrim op args live_vars)
+cgExpr (StgCon (PrimOp op@(CCallOp _ _ may_gc@True _)) args res_ty)
+  = primRetUnboxedTuple op args res_ty
+
+cgExpr x@(StgCon (PrimOp op) args res_ty)
+  | primOpOutOfLine op = tailCallPrimOp op args
+  | otherwise
   = ASSERT(op /= SeqOp) -- can't handle SeqOp
-    getPrimOpArgAmodes op args `thenFC` \ arg_amodes ->
-    let
-       result_regs   = assignPrimOpResultRegs op
-       result_amodes = map CReg result_regs
-       may_gc  = primOpCanTriggerGC op
-       dyn_tag = head result_amodes
-           -- The tag from a primitive op returning an algebraic data type
-           -- is returned in the first result_reg_amode
-    in
-    (if may_gc then
-       -- Use registers for args, and assign args to the regs
-       -- (Can-trigger-gc primops guarantee to have their args in regs)
-       let
-           (arg_robust_amodes, liveness_mask, arg_assts)
-             = makePrimOpArgsRobust op arg_amodes
-
-           liveness_arg = mkIntCLit liveness_mask
-       in
-       returnFC (
-           arg_assts,
-           COpStmt result_amodes op
-                   (pin_liveness op liveness_arg arg_robust_amodes)
-                   liveness_mask
-                   [{-no vol_regs-}]
-       )
-     else
-       -- Use args from their current amodes.
-       let
-         liveness_mask = panic "cgExpr: liveness of non-GC-ing primop touched\n"
-       in
-       returnFC (
-           COpStmt result_amodes op arg_amodes liveness_mask [{-no vol_regs-}],
-           AbsCNop
-       )
-    )                          `thenFC` \ (do_before_stack_cleanup,
-                                            do_just_before_jump) ->
+
+    getArgAmodes args  `thenFC` \ arg_amodes ->
 
     case (getPrimOpResultInfo op) of
 
        ReturnsPrim kind ->
-           performReturn do_before_stack_cleanup
-                         (\ sequel -> robustifySequel may_gc sequel
-                                                       `thenFC` \ (ret_asst, sequel') ->
-                          absC (ret_asst `mkAbsCStmts` do_just_before_jump)
-                                                       `thenC`
-                          mkPrimReturnCode sequel')
-                         live_vars
-
-       ReturnsAlg tycon ->
-           profCtrC SLIT("RET_NEW_IN_REGS") [num_of_fields]    `thenC`
-
-           performReturn do_before_stack_cleanup
-                         (\ sequel -> robustifySequel may_gc sequel
-                                                       `thenFC` \ (ret_asst, sequel') ->
-                          absC (mkAbstractCs [ret_asst,
-                                              do_just_before_jump,
-                                              info_ptr_assign])
-                       -- Must load info ptr here, not in do_just_before_stack_cleanup,
-                       -- because the info-ptr reg clashes with argument registers
-                       -- for the primop
-                                                               `thenC`
-                                     mkDynamicAlgReturnCode tycon dyn_tag sequel')
-                         live_vars
-           where
-
-           -- Here, the destination _can_ be an update frame, so we need to make sure that
-           -- infoptr (R2) is loaded with the constructor's info ptr.
-
-               info_ptr_assign = CAssign (CReg infoptr) info_lbl
-
-               info_lbl
-                 = case (ctrlReturnConvAlg tycon) of
-                     VectoredReturn   _ -> vec_lbl
-                     UnvectoredReturn _ -> dir_lbl
-
-               vec_lbl  = CTableEntry (CLbl (mkInfoTableVecTblLabel tycon) DataPtrRep)
-                               dyn_tag DataPtrRep
-
-               data_con = head (tyConDataCons tycon)
-
-               (dir_lbl, num_of_fields)
-                 = case (dataReturnConvAlg data_con) of
-                     ReturnInRegs rs
-                       -> (CLbl (mkPhantomInfoTableLabel data_con) DataPtrRep,
-                           mkIntCLit (length rs)) -- for ticky-ticky only
-
-                     ReturnInHeap
-                       -> pprPanic "CgExpr: can't return prim in heap:" (ppr data_con)
-                         -- Never used, and no point in generating
-                         -- the code for it!
-  where
-    -- for all PrimOps except ccalls, we pin the liveness info
-    -- on as the first "argument"
-    -- ToDo: un-duplicate?
-
-    pin_liveness (CCallOp _ _ _ _ _ _) _ args = args
-    pin_liveness other_op liveness_arg args
-      = liveness_arg :args
-
-    -- We only need to worry about the sequel when we may GC and the
-    -- sequel is OnStack.  If that's the case, arrange to pull the
-    -- sequel out into RetReg before performing the primOp.
-
-    robustifySequel True sequel@(OnStack _) =
-       sequelToAmode sequel                    `thenFC` \ amode ->
-       returnFC (CAssign (CReg RetReg) amode, InRetReg)
-    robustifySequel _ sequel = returnFC (AbsCNop, sequel)
+           let result_amode = CReg (dataReturnConvPrim kind) in
+           performReturn 
+             (COpStmt [result_amode] op arg_amodes [{-no vol_regs-}])
+                         (\ sequel -> mkPrimReturnCode sequel)
+                         
+       -- otherwise, must be returning an enumerated type (eg. Bool).
+       -- we've only got the tag in R2, so we have to load the constructor
+       -- itself into R1.
+
+       ReturnsAlg tycon
+           | isUnboxedTupleTyCon tycon -> primRetUnboxedTuple op args res_ty
+
+
+           | isEnumerationTyCon  tycon ->
+               performReturn
+                    (COpStmt [dyn_tag] op arg_amodes [{-no vol_regs-}])
+                         (\ sequel -> 
+                         absC (CAssign (CReg node) closure_lbl) `thenC`
+                         mkDynamicAlgReturnCode tycon dyn_tag sequel)
+
+            where
+              -- Pull a unique out of thin air to put the tag in.  
+              -- It shouldn't matter if this overlaps with anything - we're
+              -- about to return anyway.
+              dyn_tag = CTemp (mkBuiltinUnique 0) IntRep
+
+              closure_lbl = CTableEntry 
+                              (CLbl (mkClosureTblLabel tycon) PtrRep)
+                              dyn_tag PtrRep
+
 \end{code}
 
 %********************************************************
@@ -218,8 +172,8 @@ Case-expression conversion is complicated enough to have its own
 module, @CgCase@.
 \begin{code}
 
-cgExpr (StgCase expr live_vars save_vars uniq alts)
-  = cgCase expr live_vars save_vars uniq alts
+cgExpr (StgCase expr live_vars save_vars bndr srt alts)
+  = cgCase expr live_vars save_vars bndr srt alts
 \end{code}
 
 
@@ -251,8 +205,8 @@ cgExpr (StgLetNoEscape live_in_whole_let live_in_rhss bindings body)
     nukeDeadBindings live_in_whole_let `thenC`
     saveVolatileVarsAndRegs live_in_rhss
            `thenFC` \ (save_assts, rhs_eob_info, maybe_cc_slot) ->
-
-       -- ToDo: cost centre???
+    -- ToDo: cost centre???
+    restoreCurrentCostCentre maybe_cc_slot `thenFC` \ restore_cc ->
 
        -- Save those variables right now!
     absC save_assts                            `thenC`
@@ -271,12 +225,11 @@ cgExpr (StgLetNoEscape live_in_whole_let live_in_rhss bindings body)
 %*             SCC Expressions                         *
 %*                                                     *
 %********************************************************
-\subsection[scc-codegen]{Converting StgSCC}
 
 SCC expressions are treated specially. They set the current cost
 centre.
 \begin{code}
-cgExpr (StgSCC ty cc expr)
+cgExpr (StgSCC cc expr)
   = ASSERT(sccAbleCostCentre cc)
     costCentresC
        (if isDictCC cc then SLIT("SET_DICT_CCC") else SLIT("SET_CCC"))
@@ -309,110 +262,104 @@ cgRhs name (StgRhsCon maybe_cc con args)
   where
     zero_size atom = getPrimRepSize (getArgPrimRep atom) == 0
 
-cgRhs name (StgRhsClosure cc bi fvs upd_flag args body)
-  = cgRhsClosure name cc bi fvs args body lf_info
-  where
-    lf_info = mkRhsLFInfo fvs upd_flag args body
-    
+cgRhs name (StgRhsClosure cc bi srt@(NoSRT) fvs upd_flag args body)
+  = mkRhsClosure name cc bi srt fvs upd_flag args body
+cgRhs name (StgRhsClosure cc bi srt@(SRT _ _) fvs upd_flag args body)
+  = mkRhsClosure name cc bi srt fvs upd_flag args body
 \end{code}
 
-mkRhsLFInfo looks for two special forms of the right-hand side:
+mkRhsClosure looks for two special forms of the right-hand side:
        a) selector thunks.
-       b) VAP thunks
+       b) AP thunks
 
 If neither happens, it just calls mkClosureLFInfo.  You might think
-that mkClosureLFInfo should do all this, but
-
-       (a) it seems wrong for the latter to look at the structure 
-               of an expression
-
-       [March 97: item (b) is no longer true, but I've left mkRhsLFInfo here
-        anyway because of (a).]
-
-       (b) mkRhsLFInfo has to be in the monad since it looks up in
-               the environment, and it's very tiresome for mkClosureLFInfo to
-               be.  Apart from anything else it would make a loop between
-               CgBindery and ClosureInfo.
+that mkClosureLFInfo should do all this, but it seems wrong for the
+latter to look at the structure of an expression
 
 Selectors
 ~~~~~~~~~
 We look at the body of the closure to see if it's a selector---turgid,
 but nothing deep.  We are looking for a closure of {\em exactly} the
 form:
-\begin{verbatim}
+
 ...  = [the_fv] \ u [] ->
         case the_fv of
           con a_1 ... a_n -> a_i
-\end{verbatim}
+
 
 \begin{code}
-mkRhsLFInfo    [the_fv]                -- Just one free var
-               Updatable               -- Updatable thunk
+mkRhsClosure   bndr cc bi srt
+               [the_fv]                -- Just one free var
+               upd_flag                -- Updatable thunk
                []                      -- A thunk
-               (StgCase (StgApp (StgVarArg scrutinee) [{-no args-}] _)
-                     _ _ _   -- ignore live vars and uniq...
+               body@(StgCase (StgApp scrutinee [{-no args-}])
+                     _ _ _ _   -- ignore uniq, etc.
                      (StgAlgAlts case_ty
                         [(con, params, use_mask,
-                           (StgApp (StgVarArg selectee) [{-no args-}] _))]
+                           (StgApp selectee [{-no args-}]))]
                         StgNoDefault))
   |  the_fv == scrutinee                       -- Scrutinee is the only free variable
   && maybeToBool maybe_offset                  -- Selectee is a component of the tuple
-  && maybeToBool offset_into_int_maybe
   && offset_into_int <= mAX_SPEC_SELECTEE_SIZE -- Offset is small enough
-  = -- ASSERT(is_single_constructor)           -- Should be true, but causes error for SpecTyCon
-    mkSelectorLFInfo scrutinee con offset_into_int
+  = ASSERT(is_single_constructor)
+    cgStdRhsClosure bndr cc bi srt [the_fv] [] body lf_info [StgVarArg the_fv]
   where
+    lf_info              = mkSelectorLFInfo (idType bndr) offset_into_int 
+                               (isUpdatable upd_flag)
     (_, params_w_offsets) = layOutDynCon con idPrimRep params
     maybe_offset         = assocMaybe params_w_offsets selectee
     Just the_offset      = maybe_offset
-    offset_into_int_maybe = intOffsetIntoGoods the_offset
-    Just offset_into_int  = offset_into_int_maybe
+    offset_into_int       = the_offset - fixedHdrSize
     is_single_constructor = maybeToBool (maybeTyConSingleCon tycon)
     tycon                = dataConTyCon con
 \end{code}
 
 
-Vap thunks
-~~~~~~~~~~
-Same kind of thing, looking for vector-apply thunks, of the form:
+Ap thunks
+~~~~~~~~~
 
-       x = [...] \ .. [] -> f a1 .. an
+A more generic AP thunk of the form
 
-where f has arity n.  We rely on the arity info inside the Id being correct.
+       x = [ x_1...x_n ] \.. [] -> x_1 ... x_n
+
+A set of these is compiled statically into the RTS, so we just use
+those.  We could extend the idea to thunks where some of the x_i are
+global ids (and hence not free variables), but this would entail
+generating a larger thunk.  It might be an option for non-optimising
+compilation, though.
+
+We only generate an Ap thunk if all the free variables are pointers,
+for semi-obvious reasons.
 
 \begin{code}
-mkRhsLFInfo    fvs
+mkRhsClosure   bndr cc bi srt
+               fvs
                upd_flag
                []                      -- No args; a thunk
-               (StgApp (StgVarArg fun_id) args _)
-  | isLocallyDefined fun_id            -- Must be defined in this module
-  =    -- Get the arity of the fun_id.  It's guaranteed to be correct (by setStgVarInfo).
-     let
-       arity_maybe = case getIdArity fun_id of
-                       ArityExactly n  -> Just n
-                       other           -> Nothing
-     in
-     case arity_maybe of
-               Just arity
-                   | arity > 0 &&                      -- It'd better be a function!
-                     arity == length args              -- Saturated application
-                   ->          -- Ha!  A VAP thunk
-                       mkVapLFInfo fvs upd_flag fun_id args store_fun_in_vap
-
-               other -> mkClosureLFInfo False{-not top level-} fvs upd_flag []
-  where        
-       -- If the function is a free variable then it must be stored
-       -- in the thunk too; if it isn't a free variable it must be
-       -- because it's constant, so it doesn't need to be stored in the thunk
-    store_fun_in_vap = fun_id `is_elem` fvs
-    is_elem         = isIn "mkClosureLFInfo"
+               body@(StgApp fun_id args)
+
+  | length args + 1 == arity
+       && all isFollowableRep (map idPrimRep fvs) 
+       && isUpdatable upd_flag
+       && arity <= mAX_SPEC_AP_SIZE 
+
+                  -- Ha! an Ap thunk
+       = cgStdRhsClosure bndr cc bi srt fvs [] body lf_info payload
+
+   where
+       lf_info = mkApLFInfo (idType bndr) upd_flag arity
+       -- the payload has to be in the correct order, hence we can't
+       -- just use the fvs.
+       payload    = StgVarArg fun_id : args
+       arity      = length fvs
 \end{code}
 
 The default case
 ~~~~~~~~~~~~~~~~
 \begin{code}
-mkRhsLFInfo fvs upd_flag args body
-  = mkClosureLFInfo False{-not top level-} fvs upd_flag args
+mkRhsClosure bndr cc bi srt fvs upd_flag args body
+  = cgRhsClosure bndr cc bi srt fvs args body lf_info
+  where lf_info = mkClosureLFInfo bndr NotTopLevel fvs upd_flag args
 \end{code}
 
 
@@ -423,70 +370,65 @@ mkRhsLFInfo fvs upd_flag args body
 %********************************************************
 \begin{code}
 cgLetNoEscapeBindings live_in_rhss rhs_eob_info maybe_cc_slot (StgNonRec binder rhs)
-  = cgLetNoEscapeRhs live_in_rhss rhs_eob_info maybe_cc_slot binder rhs
+  = cgLetNoEscapeRhs live_in_rhss rhs_eob_info maybe_cc_slot   
+                       NonRecursive binder rhs 
                                `thenFC` \ (binder, info) ->
     addBindC binder info
 
 cgLetNoEscapeBindings live_in_rhss rhs_eob_info maybe_cc_slot (StgRec pairs)
   = fixC (\ new_bindings ->
                addBindsC new_bindings  `thenC`
-               listFCs [ cgLetNoEscapeRhs full_live_in_rhss rhs_eob_info
-                         maybe_cc_slot b e | (b,e) <- pairs ]
+               listFCs [ cgLetNoEscapeRhs full_live_in_rhss 
+                               rhs_eob_info maybe_cc_slot Recursive b e 
+                       | (b,e) <- pairs ]
     ) `thenFC` \ new_bindings ->
 
     addBindsC new_bindings
   where
     -- We add the binders to the live-in-rhss set so that we don't
     -- delete the bindings for the binder from the environment!
-    full_live_in_rhss = live_in_rhss `unionIdSets` (mkIdSet [b | (b,r) <- pairs])
+    full_live_in_rhss = live_in_rhss `unionVarSet` (mkVarSet [b | (b,r) <- pairs])
 
 cgLetNoEscapeRhs
     :: StgLiveVars     -- Live in rhss
     -> EndOfBlockInfo
-    -> Maybe VirtualSpBOffset
+    -> Maybe VirtualSpOffset
+    -> RecFlag
     -> Id
     -> StgRhs
     -> FCode (Id, CgIdInfo)
 
-cgLetNoEscapeRhs full_live_in_rhss rhs_eob_info maybe_cc_slot binder
-                (StgRhsClosure cc bi _ upd_flag args body)
+cgLetNoEscapeRhs full_live_in_rhss rhs_eob_info maybe_cc_slot rec binder
+                (StgRhsClosure cc bi srt _ upd_flag args body)
   = -- We could check the update flag, but currently we don't switch it off
     -- for let-no-escaped things, so we omit the check too!
     -- case upd_flag of
     --     Updatable -> panic "cgLetNoEscapeRhs"       -- Nothing to update!
     --     other     -> cgLetNoEscapeClosure binder cc bi live_in_whole_let live_in_rhss args body
-    cgLetNoEscapeClosure binder cc bi full_live_in_rhss rhs_eob_info maybe_cc_slot args body
+    cgLetNoEscapeClosure binder cc bi srt full_live_in_rhss rhs_eob_info maybe_cc_slot rec args body
 
 -- For a constructor RHS we want to generate a single chunk of code which
 -- can be jumped to from many places, which will return the constructor.
 -- It's easy; just behave as if it was an StgRhsClosure with a ConApp inside!
-cgLetNoEscapeRhs full_live_in_rhss rhs_eob_info maybe_cc_slot binder
+cgLetNoEscapeRhs full_live_in_rhss rhs_eob_info maybe_cc_slot rec binder
                 (StgRhsCon cc con args)
-  = cgLetNoEscapeClosure binder cc stgArgOcc{-safe-} full_live_in_rhss rhs_eob_info maybe_cc_slot
+  = cgLetNoEscapeClosure binder cc stgArgOcc{-safe-} NoSRT full_live_in_rhss rhs_eob_info maybe_cc_slot rec
        []      --No args; the binder is data structure, not a function
-       (StgCon con args full_live_in_rhss)
+       (StgCon (DataCon con) args (idType binder))
 \end{code}
 
-Some PrimOps require a {\em fixed} amount of heap allocation.  Rather
-than tidy away ready for GC and do a full heap check, we simply
-allocate a completely uninitialised block in-line, just like any other
-thunk/constructor allocation, and pass it to the PrimOp as its first
-argument.  Remember! The PrimOp is entirely responsible for
-initialising the object.  In particular, the PrimOp had better not
-trigger GC before it has filled it in, and even then it had better
-make sure that the GC can find the object somehow.
+Little helper for primitives that return unboxed tuples.
 
-Main current use: allocating SynchVars.
 
 \begin{code}
-getPrimOpArgAmodes op args
-  = getArgAmodes args          `thenFC` \ arg_amodes ->
-
-    case primOpHeapReq op of
-       FixedHeapRequired size -> allocHeap size `thenFC` \ amode ->
-                                 returnFC (amode : arg_amodes)
-
-       _                      -> returnFC arg_amodes
+primRetUnboxedTuple :: PrimOp -> [StgArg] -> Type -> Code
+primRetUnboxedTuple op args res_ty
+  = let Just (tc,ty_args) = splitAlgTyConAppThroughNewTypes res_ty
+       prim_reps         = map typePrimRep ty_args
+       temp_uniqs        = map mkBuiltinUnique [0..length ty_args]
+       temp_amodes       = zipWith CTemp temp_uniqs prim_reps
+    in
+    returnUnboxedTuple temp_amodes 
+       (getArgAmodes args  `thenFC` \ arg_amodes ->            
+        absC (COpStmt temp_amodes op arg_amodes []))
 \end{code}
-
-