Merging in the new codegen branch
[ghc-hetmet.git] / compiler / codeGen / StgCmmForeign.hs
diff --git a/compiler/codeGen/StgCmmForeign.hs b/compiler/codeGen/StgCmmForeign.hs
new file mode 100644 (file)
index 0000000..2d5d79e
--- /dev/null
@@ -0,0 +1,316 @@
+{-# OPTIONS -w #-}
+-- Lots of missing type sigs etc
+
+-----------------------------------------------------------------------------
+--
+-- Code generation for foreign calls.
+--
+-- (c) The University of Glasgow 2004-2006
+--
+-----------------------------------------------------------------------------
+
+module StgCmmForeign (
+  cgForeignCall,
+  emitPrimCall, emitCCall,
+  emitSaveThreadState, -- will be needed by the Cmm parser
+  emitLoadThreadState, -- ditto
+  emitCloseNursery,
+  emitOpenNursery,
+ ) where
+
+#include "HsVersions.h"
+
+import StgSyn
+import StgCmmProf
+import StgCmmEnv
+import StgCmmMonad
+import StgCmmUtils
+import StgCmmClosure
+
+import MkZipCfgCmm
+import Cmm
+import CmmUtils
+import Type
+import TysPrim
+import CLabel
+import SMRep
+import ForeignCall
+import Constants
+import StaticFlags
+import Maybes
+import Outputable
+
+import Control.Monad
+
+-----------------------------------------------------------------------------
+-- Code generation for Foreign Calls
+-----------------------------------------------------------------------------
+
+cgForeignCall :: [LocalReg]            -- r1,r2  where to put the results
+             -> [ForeignHint]
+             -> ForeignCall            -- the op
+             -> [StgArg]               -- x,y    arguments
+             -> FCode ()
+-- Emits code for an unsafe foreign call:      r1, r2 = foo( x, y, z )
+
+cgForeignCall results result_hints (CCall (CCallSpec target cconv safety)) stg_args
+  = do { cmm_args <- getFCallArgs stg_args
+       ; let (args, arg_hints) = unzip cmm_args
+             fc = ForeignConvention cconv arg_hints result_hints
+             (call_args, cmm_target)
+               = case target of
+                  StaticTarget lbl -> (args, CmmLit (CmmLabel 
+                                               (mkForeignLabel lbl (call_size args) False)))
+                  DynamicTarget    ->  case args of fn:rest -> (rest, fn)
+             call_target = ForeignTarget cmm_target fc
+       
+       ; srt <- getSRTInfo (panic "emitForeignCall")   -- SLPJ: Not sure what SRT 
+                                                       -- is right here
+       ; emitForeignCall safety results call_target call_args srt CmmMayReturn }
+  where
+       -- in the stdcall calling convention, the symbol needs @size appended
+       -- to it, where size is the total number of bytes of arguments.  We
+       -- attach this info to the CLabel here, and the CLabel pretty printer
+       -- will generate the suffix when the label is printed.
+      call_size args
+       | StdCallConv <- cconv = Just (sum (map arg_size args))
+       | otherwise            = Nothing
+
+       -- ToDo: this might not be correct for 64-bit API
+      arg_size arg = max (widthInBytes $ typeWidth $ cmmExprType arg) wORD_SIZE
+
+cgForeignCall _ _ (DNCall _) _
+  = panic "cgForeignCall: DNCall"
+
+emitCCall :: [(CmmFormal,ForeignHint)]
+         -> CmmExpr 
+         -> [(CmmActual,ForeignHint)]
+         -> FCode ()
+emitCCall hinted_results fn hinted_args
+  = emitForeignCall PlayRisky results (ForeignTarget fn fc) args 
+                   NoC_SRT -- No SRT b/c we PlayRisky
+                   CmmMayReturn
+  where
+    (args, arg_hints) = unzip hinted_args
+    (results, result_hints) = unzip hinted_results
+    target = ForeignTarget fn fc
+    fc = ForeignConvention CCallConv arg_hints result_hints
+    
+
+emitPrimCall :: CmmFormal -> CallishMachOp -> CmmActuals -> FCode ()
+emitPrimCall res op args
+  = emitForeignCall PlayRisky [res] (PrimTarget op) args NoC_SRT CmmMayReturn
+
+-- alternative entry point, used by CmmParse
+emitForeignCall
+       :: Safety
+       -> CmmFormals           -- where to put the results
+       -> MidCallTarget        -- the op
+       -> CmmActuals           -- arguments
+        -> C_SRT                -- the SRT of the calls continuation
+        -> CmmReturnInfo       -- This can say "never returns"
+                               --   only RTS procedures do this
+       -> FCode ()
+emitForeignCall safety results target args _srt _ret
+  | not (playSafe safety) = trace "emitForeignCall; ret is undone" $ do
+    let (caller_save, caller_load) = callerSaveVolatileRegs
+    emit caller_save
+    emit (mkUnsafeCall target results args)
+    emit caller_load
+
+  | otherwise = panic "ToDo: emitForeignCall'"
+
+{-
+  | otherwise = do
+    -- Both 'id' and 'new_base' are KindNonPtr because they're
+    -- RTS only objects and are not subject to garbage collection
+    id <- newTemp bWord
+    new_base <- newTemp (cmmRegType (CmmGlobal BaseReg))
+    temp_target <- load_target_into_temp target
+    let (caller_save, caller_load) = callerSaveVolatileRegs 
+    emitSaveThreadState
+    emit caller_save
+    -- The CmmUnsafe arguments are only correct because this part
+    -- of the code hasn't been moved into the CPS pass yet.
+    -- Once that happens, this function will just emit a (CmmSafe srt) call,
+    -- and the CPS will will be the one to convert that
+    -- to this sequence of three CmmUnsafe calls.
+    emit (mkCmmCall (CmmCallee suspendThread CCallConv)
+                       [ (id,AddrHint) ]
+                       [ (CmmReg (CmmGlobal BaseReg), AddrHint) ]
+                       CmmUnsafe
+                       ret)
+    emit (mkCmmCall temp_target results args CmmUnsafe ret)
+    emit (mkCmmCall (CmmCallee resumeThread CCallConv)
+                       [ (new_base, AddrHint) ]
+                       [ (CmmReg (CmmLocal id), AddrHint) ]
+                       CmmUnsafe
+                       ret )
+    -- Assign the result to BaseReg: we
+    -- might now have a different Capability!
+    emit (mkAssign (CmmGlobal BaseReg) (CmmReg (CmmLocal new_base)))
+    emit caller_load
+    emitLoadThreadState
+
+suspendThread = CmmLit (CmmLabel (mkRtsCodeLabel SLIT("suspendThread")))
+resumeThread  = CmmLit (CmmLabel (mkRtsCodeLabel SLIT("resumeThread")))
+-}
+
+
+{-
+--     THINK ABOUT THIS (used to happen)
+-- we might need to load arguments into temporaries before
+-- making the call, because certain global registers might
+-- overlap with registers that the C calling convention uses
+-- for passing arguments.
+--
+-- This is a HACK; really it should be done in the back end, but
+-- it's easier to generate the temporaries here.
+load_args_into_temps = mapM arg_assign_temp
+  where arg_assign_temp (e,hint) = do
+          tmp <- maybe_assign_temp e
+          return (tmp,hint)
+       
+load_target_into_temp (CmmCallee expr conv) = do 
+  tmp <- maybe_assign_temp expr
+  return (CmmCallee tmp conv)
+load_target_into_temp other_target =
+  return other_target
+
+maybe_assign_temp e
+  | hasNoGlobalRegs e = return e
+  | otherwise          = do 
+       -- don't use assignTemp, it uses its own notion of "trivial"
+       -- expressions, which are wrong here.
+        -- this is a NonPtr because it only duplicates an existing
+       reg <- newTemp (cmmExprType e) --TODO FIXME NOW
+       emit (mkAssign (CmmLocal reg) e)
+       return (CmmReg (CmmLocal reg))
+-}
+
+-- -----------------------------------------------------------------------------
+-- Save/restore the thread state in the TSO
+
+-- This stuff can't be done in suspendThread/resumeThread, because it
+-- refers to global registers which aren't available in the C world.
+
+emitSaveThreadState :: FCode ()
+emitSaveThreadState = do
+  -- CurrentTSO->sp = Sp;
+  emit $ mkStore (cmmOffset stgCurrentTSO tso_SP) stgSp
+  emitCloseNursery
+  -- and save the current cost centre stack in the TSO when profiling:
+  when opt_SccProfilingOn $
+       emit (mkStore (cmmOffset stgCurrentTSO tso_CCCS) curCCS)
+
+   -- CurrentNursery->free = Hp+1;
+emitCloseNursery :: FCode ()
+emitCloseNursery = emit $ mkStore nursery_bdescr_free (cmmOffsetW stgHp 1)
+
+emitLoadThreadState :: FCode ()
+emitLoadThreadState = do
+  tso <- newTemp gcWord -- TODO FIXME NOW
+  emit $ catAGraphs [
+       -- tso = CurrentTSO;
+       mkAssign (CmmLocal tso) stgCurrentTSO,
+       -- Sp = tso->sp;
+       mkAssign sp (CmmLoad (cmmOffset (CmmReg (CmmLocal tso)) tso_SP)
+                             bWord),
+       -- SpLim = tso->stack + RESERVED_STACK_WORDS;
+       mkAssign spLim (cmmOffsetW (cmmOffset (CmmReg (CmmLocal tso)) tso_STACK)
+                                   rESERVED_STACK_WORDS)
+    ]
+  emitOpenNursery
+  -- and load the current cost centre stack from the TSO when profiling:
+  when opt_SccProfilingOn $
+       emit (mkStore curCCSAddr 
+               (CmmLoad (cmmOffset (CmmReg (CmmLocal tso)) tso_CCCS) ccsType))
+
+emitOpenNursery :: FCode ()
+emitOpenNursery = emit $ catAGraphs [
+        -- Hp = CurrentNursery->free - 1;
+       mkAssign hp (cmmOffsetW (CmmLoad nursery_bdescr_free bWord) (-1)),
+
+        -- HpLim = CurrentNursery->start + 
+       --              CurrentNursery->blocks*BLOCK_SIZE_W - 1;
+       mkAssign hpLim
+           (cmmOffsetExpr
+               (CmmLoad nursery_bdescr_start bWord)
+               (cmmOffset
+                 (CmmMachOp mo_wordMul [
+                   CmmMachOp (MO_SS_Conv W32 wordWidth)
+                     [CmmLoad nursery_bdescr_blocks b32],
+                   CmmLit (mkIntCLit bLOCK_SIZE)
+                  ])
+                 (-1)
+               )
+           )
+   ]
+
+
+nursery_bdescr_free   = cmmOffset stgCurrentNursery oFFSET_bdescr_free
+nursery_bdescr_start  = cmmOffset stgCurrentNursery oFFSET_bdescr_start
+nursery_bdescr_blocks = cmmOffset stgCurrentNursery oFFSET_bdescr_blocks
+
+tso_SP    = tsoFieldB     oFFSET_StgTSO_sp
+tso_STACK = tsoFieldB     oFFSET_StgTSO_stack
+tso_CCCS  = tsoProfFieldB oFFSET_StgTSO_CCCS
+
+-- The TSO struct has a variable header, and an optional StgTSOProfInfo in
+-- the middle.  The fields we're interested in are after the StgTSOProfInfo.
+tsoFieldB :: ByteOff -> ByteOff
+tsoFieldB off
+  | opt_SccProfilingOn = off + sIZEOF_StgTSOProfInfo + fixedHdrSize * wORD_SIZE
+  | otherwise          = off + fixedHdrSize * wORD_SIZE
+
+tsoProfFieldB :: ByteOff -> ByteOff
+tsoProfFieldB off = off + fixedHdrSize * wORD_SIZE
+
+stgSp            = CmmReg sp
+stgHp            = CmmReg hp
+stgCurrentTSO    = CmmReg currentTSO
+stgCurrentNursery = CmmReg currentNursery
+
+sp               = CmmGlobal Sp
+spLim            = CmmGlobal SpLim
+hp               = CmmGlobal Hp
+hpLim            = CmmGlobal HpLim
+currentTSO       = CmmGlobal CurrentTSO
+currentNursery           = CmmGlobal CurrentNursery
+
+-- -----------------------------------------------------------------------------
+-- For certain types passed to foreign calls, we adjust the actual
+-- value passed to the call.  For ByteArray#/Array# we pass the
+-- address of the actual array, not the address of the heap object.
+
+getFCallArgs :: [StgArg] -> FCode [(CmmExpr, ForeignHint)]
+-- (a) Drop void args
+-- (b) Add foriegn-call shim code
+-- It's (b) that makes this differ from getNonVoidArgAmodes
+
+getFCallArgs args
+  = do { mb_cmms <- mapM get args
+       ; return (catMaybes mb_cmms) }
+  where
+    get arg | isVoidRep arg_rep 
+           = return Nothing
+           | otherwise
+           = do { cmm <- getArgAmode arg
+                ; return (Just (add_shim arg_ty cmm, hint)) }
+           where
+             arg_ty  = stgArgType arg
+             arg_rep = typePrimRep arg_ty
+             hint    = typeForeignHint arg_ty
+
+add_shim :: Type -> CmmExpr -> CmmExpr
+add_shim arg_ty expr
+  | tycon == arrayPrimTyCon || tycon == mutableArrayPrimTyCon
+  = cmmOffsetB expr arrPtrsHdrSize
+
+  | tycon == byteArrayPrimTyCon || tycon == mutableByteArrayPrimTyCon
+  = cmmOffsetB expr arrWordsHdrSize
+
+  | otherwise = expr
+  where        
+    tycon = tyConAppTyCon (repType arg_ty)
+       -- should be a tycon app, since this is a foreign call