Interruptible FFI calls with pthread_kill and CancelSynchronousIO. v4
[ghc-hetmet.git] / compiler / cmm / ZipCfgCmmRep.hs
index 05203e5..aa16f0b 100644 (file)
@@ -1,3 +1,8 @@
+#if __GLASGOW_HASKELL__ >= 611
+{-# OPTIONS_GHC -XNoMonoLocalBinds #-}
+#endif
+-- Norman likes local bindings
+
 -- This module is pure representation and should be imported only by
 -- clients that need to manipulate representation and know what
 -- they're doing.  Clients that need to create flow graphs should
@@ -29,17 +34,16 @@ import CmmTx
 import CLabel
 import FastString
 import ForeignCall
-import qualified ZipCfg as Z
 import qualified ZipDataflow as DF
 import ZipCfg 
 import MkZipCfg
 import Util
 
+import BasicTypes
 import Maybes
-import Monad
+import Control.Monad
 import Outputable
 import Prelude hiding (zip, unzip, last)
-import qualified Data.List as L
 import SMRep (ByteOff)
 import UniqSupply
 
@@ -49,8 +53,10 @@ import UniqSupply
 type CmmGraph                = LGraph Middle Last
 type CmmAGraph               = AGraph Middle Last
 type CmmBlock                = Block  Middle Last
-type CmmZ                    = GenCmm    CmmStatic CmmInfo CmmGraph
-type CmmTopZ                 = GenCmmTop CmmStatic CmmInfo CmmGraph
+type CmmStackInfo            = (ByteOff, Maybe ByteOff)
+  -- probably want a record; (SP offset on entry, update frame space)
+type CmmZ                    = GenCmm    CmmStatic CmmInfo (CmmStackInfo, CmmGraph)
+type CmmTopZ                 = GenCmmTop CmmStatic CmmInfo (CmmStackInfo, CmmGraph)
 type CmmBackwardFixedPoint a = DF.BackwardFixedPoint Middle Last a ()
 type CmmForwardFixedPoint  a = DF.ForwardFixedPoint  Middle Last a ()
 
@@ -64,7 +70,7 @@ data Middle
   | MidStore  CmmExpr CmmExpr    -- Assign to memory location.  Size is
                                  -- given by cmmExprType of the rhs.
 
-  | MidForeignCall               -- A foreign call;
+  | MidForeignCall               -- A foreign call; see Note [Foreign calls]
      ForeignSafety               -- Is it a safe or unsafe call?
      MidCallTarget               -- call target and convention
      CmmFormals                  -- zero or more results
@@ -84,37 +90,61 @@ data Last
         --      one  -> second block etc
         -- Undefined outside range, and when there's a Nothing
   | LastCall {                   -- A call (native or safe foreign)
-        cml_target  :: CmmExpr,  -- never a CmmPrim to a CallishMachOp!
-        cml_cont    :: Maybe BlockId,
+        cml_target :: CmmExpr,  -- never a CmmPrim to a CallishMachOp!
+
+        cml_cont :: Maybe BlockId,
             -- BlockId of continuation (Nothing for return or tail call)
-        cml_args    :: ByteOff,  -- bytes offset for youngest outgoing arg
-        cml_ret_off :: Maybe UpdFrameOffset}
-          -- stack offset for return (update frames);
-          -- The return offset should be Nothing only if we have to create
-          -- a new call, e.g. for a procpoint, in which case it's an invariant
-          -- that the call does not stand for a return or a tail call,
-          -- and the successor does not need an info table.
-
-data MidCallTarget     -- The target of a MidUnsafeCall
-  = ForeignTarget      -- A foreign procedure
-       CmmExpr                 -- Its address
-       ForeignConvention       -- Its calling convention
-
-  | PrimTarget         -- A possibly-side-effecting machine operation
-       CallishMachOp           -- Which one
+
+        cml_args :: ByteOff, 
+           -- Byte offset, from the *old* end of the Area associated with
+            -- the BlockId (if cml_cont = Nothing, then Old area), of
+            -- youngest outgoing arg.  Set the stack pointer to this before
+           -- transferring control.
+           -- (NB: an update frame might also have been stored in the Old
+           --      area, but it'll be in an older part than the args.)
+
+        cml_ret_args :: ByteOff,  
+           -- For calls *only*, the byte offset for youngest returned value
+           -- This is really needed at the *return* point rather than here
+           -- at the call, but in practice it's convenient to record it here.
+
+        cml_ret_off :: Maybe ByteOff
+          -- For calls *only*, the byte offset of the base of the frame that
+         -- must be described by the info table for the return point.  
+         -- The older words are an update frames, which have their own
+         -- info-table and layout information
+
+         -- From a liveness point of view, the stack words older than
+         -- cml_ret_off are treated as live, even if the sequel of
+         -- the call goes into a loop.
+       }
+
+data MidCallTarget        -- The target of a MidUnsafeCall
+  = ForeignTarget         -- A foreign procedure
+        CmmExpr                  -- Its address
+        ForeignConvention        -- Its calling convention
+
+  | PrimTarget            -- A possibly-side-effecting machine operation
+        CallishMachOp            -- Which one
   deriving Eq
 
 data Convention
-  = Native             -- Native C-- call/return
+  = NativeDirectCall -- Native C-- call skipping the node (closure) argument
+  
+  | NativeNodeCall   -- Native C-- call including the node argument
+
+  | NativeReturn     -- Native C-- return
+
+  | Slow             -- Slow entry points: all args pushed on the stack
 
-  | Slow               -- Slow entry points: all args pushed on the stack
+  | GC               -- Entry to the garbage collector: uses the node reg!
 
-  | GC                         -- Entry to the garbage collector: uses the node reg!
+  | PrimOpCall       -- Calling prim ops
 
-  | PrimOp             -- Calling prim ops
+  | PrimOpReturn     -- Returning from prim ops
 
-  | Foreign            -- Foreign call/return
-       ForeignConvention
+  | Foreign          -- Foreign call/return
+        ForeignConvention
 
   | Private
         -- Used for control transfers within a (pre-CPS) procedure All
@@ -135,12 +165,40 @@ data ForeignSafety
   = Unsafe              -- unsafe call
   | Safe BlockId        -- making infotable requires: 1. label 
          UpdFrameOffset --                            2. where the upd frame is
+         Bool           -- is the call interruptible?
   deriving Eq
 
 data ValueDirection = Arguments | Results
   -- Arguments go with procedure definitions, jumps, and arguments to calls
   -- Results go with returns and with results of calls.
   deriving Eq
+{- Note [Foreign calls]
+~~~~~~~~~~~~~~~~~~~~~~~
+A MidForeign call is used *all* foreign calls, both *unsafe* and *safe*.
+Unsafe ones are easy: think of them as a "fat machine instruction".
+
+Safe ones are trickier.  A safe foreign call 
+     r = f(x)
+ultimately expands to
+     push "return address"     -- Never used to return to; 
+                               -- just points an info table
+     save registers into TSO
+     call suspendThread
+     r = f(x)                  -- Make the call
+     call resumeThread
+     restore registers
+     pop "return address"
+We cannot "lower" a safe foreign call to this sequence of Cmms, because
+after we've saved Sp all the Cmm optimiser's assumptions are broken.
+Furthermore, currently the smart Cmm constructors know the calling
+conventions for Haskell, the garbage collector, etc, and "lower" them
+so that a LastCall passes no parameters or results.  But the smart 
+constructors do *not* (currently) know the foreign call conventions.
+
+For these reasons use MidForeignCall for all calls. The only annoying thing
+is that a safe foreign call needs an info table.
+-}
 
 ----------------------------------------------------------------------
 ----- Splicing between blocks
@@ -174,7 +232,7 @@ insertBetween b ms succId = insert $ goto_end $ unzip b
           panic "unimp: insertBetween after a call -- probably not a good idea"
         insert (_, LastExit) = panic "cannot insert after exit"
         newBlocks = do id <- liftM BlockId $ getUniqueM
-                       return $ (id, [Block id emptyStackInfo $
+                       return $ (id, [Block id $
                                    foldr ZTail (ZLast (LastOther (LastBranch succId))) ms])
         mbNewBlocks (Just k) = if k == succId then liftM lift newBlocks
                                else return (Just k, [])
@@ -196,18 +254,18 @@ instance LastNode Last where
     branchNodeTarget _ = panic "asked for target of non-branch"
 
 cmmSuccs :: Last -> [BlockId]
-cmmSuccs (LastBranch id)            = [id]
-cmmSuccs (LastCall _ Nothing _ _)   = []
-cmmSuccs (LastCall _ (Just id) _ _) = [id]
-cmmSuccs (LastCondBranch _ t f)     = [f, t]  -- meets layout constraint
-cmmSuccs (LastSwitch _ edges)       = catMaybes edges
+cmmSuccs (LastBranch id)              = [id]
+cmmSuccs (LastCall _ Nothing   _ _ _) = []
+cmmSuccs (LastCall _ (Just id) _ _ _) = [id]
+cmmSuccs (LastCondBranch _ t f)       = [f, t]  -- meets layout constraint
+cmmSuccs (LastSwitch _ edges)         = catMaybes edges
 
 fold_cmm_succs :: (BlockId -> a -> a) -> Last -> a -> a
-fold_cmm_succs  f (LastBranch id)            z = f id z
-fold_cmm_succs  _ (LastCall _ Nothing _ _)   z = z
-fold_cmm_succs  f (LastCall _ (Just id) _ _) z = f id z
-fold_cmm_succs  f (LastCondBranch _ te fe)   z = f te (f fe z)
-fold_cmm_succs  f (LastSwitch _ edges)       z = foldl (flip f) z $ catMaybes edges
+fold_cmm_succs  f (LastBranch id)              z = f id z
+fold_cmm_succs  _ (LastCall _ Nothing _ _ _)   z = z
+fold_cmm_succs  f (LastCall _ (Just id) _ _ _) z = f id z
+fold_cmm_succs  f (LastCondBranch _ te fe)     z = f te (f fe z)
+fold_cmm_succs  f (LastSwitch _ edges)         z = foldl (flip f) z $ catMaybes edges
 
 ----------------------------------------------------------------------
 ----- Instance declarations for register use
@@ -225,8 +283,8 @@ instance UserOfLocalRegs MidCallTarget where
   foldRegsUsed f  z (ForeignTarget e _) = foldRegsUsed f z e
 
 instance UserOfSlots MidCallTarget where
+  foldSlotsUsed  f z (ForeignTarget e _) = foldSlotsUsed f z e
   foldSlotsUsed _f z (PrimTarget _)      = z
-  foldSlotsUsed f  z (ForeignTarget e _) = foldSlotsUsed f z e
 
 instance (UserOfLocalRegs a) => UserOfLocalRegs (Maybe a) where
   foldRegsUsed f z (Just x) = foldRegsUsed f z x
@@ -239,16 +297,16 @@ instance (UserOfSlots a) => UserOfSlots (Maybe a) where
 instance UserOfLocalRegs Last where
     foldRegsUsed f z l = last l
       where last (LastBranch _id)       = z
-            last (LastCall tgt _ _ _)   = foldRegsUsed f z tgt
+            last (LastCall tgt _ _ _ _) = foldRegsUsed f z tgt
             last (LastCondBranch e _ _) = foldRegsUsed f z e
             last (LastSwitch e _tbl)    = foldRegsUsed f z e
 
 instance DefinerOfLocalRegs Middle where
     foldRegsDefd f z m = middle m
-      where middle (MidComment {})            = z
-            middle (MidAssign _lhs _)         = fold f z _lhs
-            middle (MidStore _ _)             = z
-            middle (MidForeignCall _ _ fs _)  = fold f z fs
+      where middle (MidComment {})           = z
+            middle (MidAssign lhs _)         = fold f z lhs
+            middle (MidStore _ _)            = z
+            middle (MidForeignCall _ _ fs _) = fold f z fs
             fold f z m = foldRegsDefd f z m  -- avoid monomorphism restriction
 
 instance DefinerOfLocalRegs Last where
@@ -269,7 +327,7 @@ instance UserOfSlots Middle where
 instance UserOfSlots Last where
     foldSlotsUsed f z l = last l
       where last (LastBranch _id)       = z
-            last (LastCall tgt _ _ _)   = foldSlotsUsed f z tgt
+            last (LastCall tgt _ _ _ _) = foldSlotsUsed f z tgt
             last (LastCondBranch e _ _) = foldSlotsUsed f z e
             last (LastSwitch e _tbl)    = foldSlotsUsed f z e
 
@@ -313,13 +371,13 @@ mapExpLast :: (CmmExpr -> CmmExpr) -> Last -> Last
 mapExpLast _   l@(LastBranch _)           = l
 mapExpLast exp (LastCondBranch e ti fi)   = LastCondBranch (exp e) ti fi
 mapExpLast exp (LastSwitch e tbl)         = LastSwitch (exp e) tbl
-mapExpLast exp (LastCall tgt mb_id u s) = LastCall (exp tgt) mb_id u s
+mapExpLast exp (LastCall tgt mb_id o i s) = LastCall (exp tgt) mb_id o i s
 
 foldExpLast :: (CmmExpr -> z -> z) -> Last -> z -> z
 foldExpLast _   (LastBranch _)         z = z
 foldExpLast exp (LastCondBranch e _ _) z = exp e z
 foldExpLast exp (LastSwitch e _)       z = exp e z
-foldExpLast exp (LastCall tgt _ _ _)   z = exp tgt z
+foldExpLast exp (LastCall tgt _ _ _ _) z = exp tgt z
 
 mapExpMidcall :: (CmmExpr -> CmmExpr) -> MidCallTarget -> MidCallTarget 
 mapExpMidcall exp   (ForeignTarget e c) = ForeignTarget (exp e) c
@@ -359,11 +417,11 @@ joinOuts lattice env l =
   let bot  = fact_bot lattice
       join x y = txVal $ fact_add_to lattice x y
   in case l of
-       (LastBranch id)           -> env id
-       (LastCall _ Nothing _ _)  -> bot
-       (LastCall _ (Just k) _ _) -> env k
-       (LastCondBranch _ t f)    -> join (env t) (env f)
-       (LastSwitch _ tbl)        -> foldr join bot (map env $ catMaybes tbl)
+       (LastBranch id)             -> env id
+       (LastCall _ Nothing _ _ _)  -> bot
+       (LastCall _ (Just k) _ _ _) -> env k
+       (LastCondBranch _ t f)      -> join (env t) (env f)
+       (LastSwitch _ tbl)          -> foldr join bot (map env $ catMaybes tbl)
 
 ----------------------------------------------------------------------
 ----- Instance declarations for prettyprinting (avoids recursive imports)
@@ -407,10 +465,9 @@ pprMiddle stmt = pp_stmt <+> pp_debug
        -- call "ccall" foo(x, y)[r1, r2];
        -- ToDo ppr volatile
        MidForeignCall safety target results args ->
-           hsep [ if null results
-                     then empty
-                     else parens (commafy $ map ppr results) <+> equals,
-                      ppr_safety safety,
+           hsep [ ppUnless (null results) $
+                     parens (commafy $ map ppr results) <+> equals,
+                   ppr_safety safety,
                   ptext $ sLit "call", 
                   ppr_call_target target <> parens (commafy $ map ppr args) <> semi]
 
@@ -424,15 +481,23 @@ pprMiddle stmt = pp_stmt <+> pp_debug
              MidForeignCall {} -> text "MidForeignCall"
 
 ppr_fc :: ForeignConvention -> SDoc
-ppr_fc (ForeignConvention c _ _) = doubleQuotes (ppr c)
+ppr_fc (ForeignConvention c args res) =
+  doubleQuotes (ppr c) <+> text "args: " <+> ppr args <+> text " results: " <+> ppr res
 
 ppr_safety :: ForeignSafety -> SDoc
-ppr_safety (Safe bid upd) = text "safe<" <> ppr bid <> text ", " <> ppr upd <> text ">"
+ppr_safety (Safe bid upd interruptible) =
+    text (if interruptible then "interruptible" else "safe") <>
+    text "<" <> ppr bid <> text ", " <> ppr upd <> text ">"
 ppr_safety Unsafe         = text "unsafe"
 
 ppr_call_target :: MidCallTarget -> SDoc
 ppr_call_target (ForeignTarget fn c) = ppr_fc c <+> ppr_target fn
-ppr_call_target (PrimTarget op)      = ppr (CmmLabel (mkForeignLabel (mkFastString (show op)) Nothing False))
+ppr_call_target (PrimTarget op) 
+ -- HACK: We're just using a ForeignLabel to get this printed, the label
+ --      might not really be foreign.
+ = ppr (CmmLabel (mkForeignLabel
+                       (mkFastString (show op)) 
+                       Nothing ForeignLabelInThisPackage IsFunction))
 
 ppr_target :: CmmExpr -> SDoc
 ppr_target t@(CmmLit _) = ppr t
@@ -447,10 +512,10 @@ pprLast :: Last -> SDoc
 pprLast stmt = pp_stmt <+> pp_debug
   where
     pp_stmt = case stmt of
-       LastBranch ident             -> ptext (sLit "goto") <+> ppr ident <> semi
-       LastCondBranch expr t f      -> genFullCondBranch expr t f
-       LastSwitch arg ids           -> ppr $ CmmSwitch arg ids
-       LastCall tgt k off updfr_off -> genBareCall tgt k off updfr_off
+       LastBranch ident                -> ptext (sLit "goto") <+> ppr ident <> semi
+       LastCondBranch expr t f         -> genFullCondBranch expr t f
+       LastSwitch arg ids              -> ppr $ CmmSwitch arg ids
+       LastCall tgt k out res updfr_off -> genBareCall tgt k out res updfr_off
 
     pp_debug = text " //" <+> case stmt of
            LastBranch {} -> text "LastBranch"
@@ -458,11 +523,13 @@ pprLast stmt = pp_stmt <+> pp_debug
            LastSwitch {} -> text "LastSwitch"
            LastCall {} -> text "LastCall"
 
-genBareCall :: CmmExpr -> Maybe BlockId -> Int -> Maybe UpdFrameOffset -> SDoc
-genBareCall fn k off updfr_off =
+genBareCall :: CmmExpr -> Maybe BlockId -> ByteOff -> ByteOff ->
+                          Maybe UpdFrameOffset -> SDoc
+genBareCall fn k out res updfr_off =
         hcat [ ptext (sLit "call"), space
              , pprFun fn, ptext (sLit "(...)"), space
-             , ptext (sLit "returns to") <+> ppr k <+> parens (ppr off)
+             , ptext (sLit "returns to") <+> ppr k <+> parens (ppr out)
+                                                   <+> parens (ppr res)
              , ptext (sLit " with update frame") <+> ppr updfr_off
              , semi ]
 
@@ -481,12 +548,15 @@ genFullCondBranch expr t f =
          ]
 
 pprConvention :: Convention -> SDoc
-pprConvention (Native {})  = text "<native-convention>"
-pprConvention  Slow        = text "<slow-convention>"
-pprConvention  GC          = text "<gc-convention>"
-pprConvention  PrimOp      = text "<primop-convention>"
-pprConvention (Foreign c)  = ppr c
-pprConvention (Private {}) = text "<private-convention>"
+pprConvention (NativeNodeCall   {}) = text "<native-node-call-convention>"
+pprConvention (NativeDirectCall {}) = text "<native-direct-call-convention>"
+pprConvention (NativeReturn {})     = text "<native-ret-convention>"
+pprConvention  Slow                 = text "<slow-convention>"
+pprConvention  GC                   = text "<gc-convention>"
+pprConvention  PrimOpCall           = text "<primop-call-convention>"
+pprConvention  PrimOpReturn         = text "<primop-ret-convention>"
+pprConvention (Foreign c)           = ppr c
+pprConvention (Private {})          = text "<private-convention>"
 
 pprForeignConvention :: ForeignConvention -> SDoc
 pprForeignConvention (ForeignConvention c as rs) = ppr c <> ppr as <> ppr rs