3758d614d24b6cac943b7e9767709c2b4494d81d
[ghc-hetmet.git] / ghc / compiler / deSugar / DsCCall.lhs
1 %
2 % (c) The AQUA Project, Glasgow University, 1994-1998
3 %
4 \section[DsCCall]{Desugaring \tr{_ccall_}s and \tr{_casm_}s}
5
6 \begin{code}
7 module DsCCall 
8         ( dsCCall
9         , mkFCall
10         , unboxArg
11         , boxResult
12         , resultWrapper
13         ) where
14
15 #include "HsVersions.h"
16
17 import CoreSyn
18
19 import DsMonad
20
21 import CoreUtils        ( exprType, mkCoerce )
22 import Id               ( Id, mkWildId, idType )
23 import MkId             ( mkFCallId, realWorldPrimId, mkPrimOpId )
24 import Maybes           ( maybeToBool )
25 import ForeignCall      ( ForeignCall(..), CCallSpec(..), CCallTarget(..), Safety, CCallConv(..) )
26 import DataCon          ( splitProductType_maybe, dataConSourceArity, dataConWrapId )
27 import ForeignCall      ( ForeignCall, CCallTarget(..) )
28 import Type             ( isUnLiftedType, splitAlgTyConApp_maybe, mkFunTys,
29                           splitTyConApp_maybe, tyVarsOfType, mkForAllTys, isPrimitiveType,
30                           isNewType, repType, isUnLiftedType, mkFunTy, mkTyConApp,
31                           Type
32                         )
33 import PrimOp           ( PrimOp(TouchOp) )
34 import TysPrim          ( realWorldStatePrimTy,
35                           byteArrayPrimTyCon, mutableByteArrayPrimTyCon,
36                           intPrimTy, foreignObjPrimTy
37                         )
38 import TysWiredIn       ( unitDataConId,
39                           unboxedSingletonDataCon, unboxedPairDataCon,
40                           unboxedSingletonTyCon, unboxedPairTyCon,
41                           boolTy, trueDataCon, falseDataCon, 
42                           trueDataConId, falseDataConId, unitTy
43                         )
44 import Literal          ( mkMachInt )
45 import CStrings         ( CLabelString )
46 import PrelNames        ( Unique, hasKey, ioTyConKey )
47 import VarSet           ( varSetElems )
48 import Outputable
49 \end{code}
50
51 Desugaring of @ccall@s consists of adding some state manipulation,
52 unboxing any boxed primitive arguments and boxing the result if
53 desired.
54
55 The state stuff just consists of adding in
56 @PrimIO (\ s -> case s of { S# s# -> ... })@ in an appropriate place.
57
58 The unboxing is straightforward, as all information needed to unbox is
59 available from the type.  For each boxed-primitive argument, we
60 transform:
61 \begin{verbatim}
62    _ccall_ foo [ r, t1, ... tm ] e1 ... em
63    |
64    |
65    V
66    case e1 of { T1# x1# ->
67    ...
68    case em of { Tm# xm# -> xm#
69    ccall# foo [ r, t1#, ... tm# ] x1# ... xm#
70    } ... }
71 \end{verbatim}
72
73 The reboxing of a @_ccall_@ result is a bit tricker: the types don't
74 contain information about the state-pairing functions so we have to
75 keep a list of \tr{(type, s-p-function)} pairs.  We transform as
76 follows:
77 \begin{verbatim}
78    ccall# foo [ r, t1#, ... tm# ] e1# ... em#
79    |
80    |
81    V
82    \ s# -> case (ccall# foo [ r, t1#, ... tm# ] s# e1# ... em#) of
83           (StateAnd<r># result# state#) -> (R# result#, realWorld#)
84 \end{verbatim}
85
86 \begin{code}
87 dsCCall :: CLabelString -- C routine to invoke
88         -> [CoreExpr]   -- Arguments (desugared)
89         -> Safety       -- Safety of the call
90         -> Bool         -- True <=> really a "_casm_"
91         -> Type         -- Type of the result: IO t
92         -> DsM CoreExpr
93
94 dsCCall lbl args may_gc is_asm result_ty
95   = mapAndUnzipDs unboxArg args `thenDs` \ (unboxed_args, arg_wrappers) ->
96     boxResult [] result_ty      `thenDs` \ (ccall_result_ty, res_wrapper) ->
97     getUniqueDs                 `thenDs` \ uniq ->
98     let
99         target | is_asm    = CasmTarget lbl
100                | otherwise = StaticTarget lbl
101         the_fcall    = CCall (CCallSpec target CCallConv may_gc)
102         the_prim_app = mkFCall uniq the_fcall unboxed_args ccall_result_ty
103     in
104     returnDs (foldr ($) (res_wrapper the_prim_app) arg_wrappers)
105
106 mkFCall :: Unique -> ForeignCall 
107         -> [CoreExpr]   -- Args
108         -> Type         -- Result type
109         -> CoreExpr
110 -- Construct the ccall.  The only tricky bit is that the ccall Id should have
111 -- no free vars, so if any of the arg tys do we must give it a polymorphic type.
112 --      [I forget *why* it should have no free vars!]
113 -- For example:
114 --      mkCCall ... [s::StablePtr (a->b), x::Addr, c::Char]
115 --
116 -- Here we build a ccall thus
117 --      (ccallid::(forall a b.  StablePtr (a -> b) -> Addr -> Char -> IO Addr))
118 --                      a b s x c
119 mkFCall uniq the_fcall val_args res_ty
120   = mkApps (mkVarApps (Var the_fcall_id) tyvars) val_args
121   where
122     arg_tys = map exprType val_args
123     body_ty = (mkFunTys arg_tys res_ty)
124     tyvars  = varSetElems (tyVarsOfType body_ty)
125     ty      = mkForAllTys tyvars body_ty
126     the_fcall_id = mkFCallId uniq the_fcall ty
127 \end{code}
128
129 \begin{code}
130 unboxArg :: CoreExpr                    -- The supplied argument
131          -> DsM (CoreExpr,              -- To pass as the actual argument
132                  CoreExpr -> CoreExpr   -- Wrapper to unbox the arg
133                 )
134 -- Example: if the arg is e::Int, unboxArg will return
135 --      (x#::Int#, \W. case x of I# x# -> W)
136 -- where W is a CoreExpr that probably mentions x#
137
138 unboxArg arg
139   -- Primtive types: nothing to unbox
140   | isPrimitiveType arg_ty
141   = returnDs (arg, \body -> body)
142
143   -- Newtypes
144   | isNewType arg_ty
145   = unboxArg (mkCoerce (repType arg_ty) arg_ty arg)
146       
147   -- Booleans
148   | arg_ty == boolTy
149   = newSysLocalDs intPrimTy             `thenDs` \ prim_arg ->
150     returnDs (Var prim_arg,
151               \ body -> Case (Case arg (mkWildId arg_ty)
152                                        [(DataAlt falseDataCon,[],mkIntLit 0),
153                                         (DataAlt trueDataCon, [],mkIntLit 1)])
154                              prim_arg 
155                              [(DEFAULT,[],body)])
156
157   -- Data types with a single constructor, which has a single, primitive-typed arg
158   -- This deals with Int, Float etc
159   | is_product_type && data_con_arity == 1 
160   = ASSERT(isUnLiftedType data_con_arg_ty1 )    -- Typechecker ensures this
161     newSysLocalDs arg_ty                `thenDs` \ case_bndr ->
162     newSysLocalDs data_con_arg_ty1      `thenDs` \ prim_arg ->
163     returnDs (Var prim_arg,
164               \ body -> Case arg case_bndr [(DataAlt data_con,[prim_arg],body)]
165     )
166
167   -- Byte-arrays, both mutable and otherwise; hack warning
168   | is_product_type &&
169     data_con_arity == 3 &&
170     maybeToBool maybe_arg3_tycon &&
171     (arg3_tycon ==  byteArrayPrimTyCon ||
172      arg3_tycon ==  mutableByteArrayPrimTyCon)
173     -- and, of course, it is an instance of CCallable
174   = newSysLocalDs arg_ty                `thenDs` \ case_bndr ->
175     newSysLocalsDs data_con_arg_tys     `thenDs` \ vars@[l_var, r_var, arr_cts_var] ->
176     returnDs (Var arr_cts_var,
177               \ body -> Case arg case_bndr [(DataAlt data_con,vars,body)]
178     )
179
180   | otherwise
181   = getSrcLocDs `thenDs` \ l ->
182     pprPanic "unboxArg: " (ppr l <+> ppr arg_ty)
183   where
184     arg_ty                                      = exprType arg
185     maybe_product_type                          = splitProductType_maybe arg_ty
186     is_product_type                             = maybeToBool maybe_product_type
187     Just (_, _, data_con, data_con_arg_tys)     = maybe_product_type
188     data_con_arity                              = dataConSourceArity data_con
189     (data_con_arg_ty1 : _)                      = data_con_arg_tys
190
191     (_ : _ : data_con_arg_ty3 : _) = data_con_arg_tys
192     maybe_arg3_tycon               = splitTyConApp_maybe data_con_arg_ty3
193     Just (arg3_tycon,_)            = maybe_arg3_tycon
194 \end{code}
195
196
197 \begin{code}
198 boxResult :: [Id] -> Type -> DsM (Type, CoreExpr -> CoreExpr)
199
200 -- Takes the result of the user-level ccall: 
201 --      either (IO t), 
202 --      or maybe just t for an side-effect-free call
203 -- Returns a wrapper for the primitive ccall itself, along with the
204 -- type of the result of the primitive ccall.  This result type
205 -- will be of the form  
206 --      State# RealWorld -> (# State# RealWorld, t' #)
207 -- where t' is the unwrapped form of t.  If t is simply (), then
208 -- the result type will be 
209 --      State# RealWorld -> (# State# RealWorld #)
210
211 -- Here is where we arrange that ForeignPtrs which are passed to a 'safe'
212 -- foreign import don't get finalized until the call returns.  For each
213 -- argument of type ForeignObj# we arrange to touch# the argument after
214 -- the call.  The arg_ids passed in are the Ids passed to the actual ccall.
215
216 boxResult arg_ids result_ty
217   = case splitAlgTyConApp_maybe result_ty of
218
219         -- The result is IO t, so wrap the result in an IO constructor
220         Just (io_tycon, [io_res_ty], [io_data_con]) | io_tycon `hasKey` ioTyConKey
221                 -> mk_alt return_result 
222                           (resultWrapper io_res_ty)     `thenDs` \ (ccall_res_ty, the_alt) ->
223                    newSysLocalDs  realWorldStatePrimTy   `thenDs` \ state_id ->
224                    let
225                         wrap = \ the_call -> 
226                                  mkApps (Var (dataConWrapId io_data_con))
227                                            [ Type io_res_ty, 
228                                              Lam state_id $
229                                               Case (App the_call (Var state_id))
230                                                    (mkWildId ccall_res_ty)
231                                                    [the_alt]
232                                            ]
233                    in
234                    returnDs (realWorldStatePrimTy `mkFunTy` ccall_res_ty, wrap)
235                 where
236                    return_result state ans = mkConApp unboxedPairDataCon 
237                                                       [Type realWorldStatePrimTy, Type io_res_ty, 
238                                                        state, ans]
239
240         -- It isn't, so do unsafePerformIO
241         -- It's not conveniently available, so we inline it
242         other -> mk_alt return_result
243                         (resultWrapper result_ty) `thenDs` \ (ccall_res_ty, the_alt) ->
244                  let
245                     wrap = \ the_call -> Case (App the_call (Var realWorldPrimId)) 
246                                               (mkWildId ccall_res_ty)
247                                               [the_alt]
248                  in
249                  returnDs (realWorldStatePrimTy `mkFunTy` ccall_res_ty, wrap)
250               where
251                  return_result state ans = ans
252   where
253     mk_alt return_result (Nothing, wrap_result)
254         =       -- The ccall returns ()
255           let
256                 rhs_fun state_id = return_result (Var state_id) 
257                                         (wrap_result (panic "boxResult"))
258           in
259           newSysLocalDs realWorldStatePrimTy    `thenDs` \ state_id ->
260           mkTouches arg_ids state_id rhs_fun    `thenDs` \ the_rhs ->
261           let
262                 ccall_res_ty = mkTyConApp unboxedSingletonTyCon [realWorldStatePrimTy]
263                 the_alt      = (DataAlt unboxedSingletonDataCon, [state_id], the_rhs)
264           in
265           returnDs (ccall_res_ty, the_alt)
266
267     mk_alt return_result (Just prim_res_ty, wrap_result)
268         =       -- The ccall returns a non-() value
269           newSysLocalDs prim_res_ty             `thenDs` \ result_id ->
270           let
271                 rhs_fun state_id = return_result (Var state_id) 
272                                         (wrap_result (Var result_id))
273           in
274           newSysLocalDs realWorldStatePrimTy    `thenDs` \ state_id ->
275           mkTouches arg_ids state_id rhs_fun    `thenDs` \ the_rhs ->
276           let
277                 ccall_res_ty = mkTyConApp unboxedPairTyCon [realWorldStatePrimTy, prim_res_ty]
278                 the_alt      = (DataAlt unboxedPairDataCon, [state_id, result_id], the_rhs)
279           in
280           returnDs (ccall_res_ty, the_alt)
281
282 touchzh = mkPrimOpId TouchOp
283
284 mkTouches []     s cont = returnDs (cont s)
285 mkTouches (v:vs) s cont
286   | idType v /= foreignObjPrimTy = mkTouches vs s cont
287   | otherwise = newSysLocalDs realWorldStatePrimTy `thenDs` \s' -> 
288                 mkTouches vs s' cont `thenDs` \ rest ->
289                 returnDs (Case (mkApps (Var touchzh) [Type foreignObjPrimTy, 
290                                                       Var v, Var s]) s' 
291                                 [(DEFAULT, [], rest)])
292
293 resultWrapper :: Type
294               -> (Maybe Type,           -- Type of the expected result, if any
295                   CoreExpr -> CoreExpr) -- Wrapper for the result 
296 resultWrapper result_ty
297   -- Base case 1: primitive types
298   | isPrimitiveType result_ty
299   = (Just result_ty, \e -> e)
300
301   -- Base case 1: the unit type ()
302   | result_ty == unitTy
303   = (Nothing, \e -> Var unitDataConId)
304
305   | result_ty == boolTy
306   = (Just intPrimTy, \e -> Case e (mkWildId intPrimTy)
307                                   [(LitAlt (mkMachInt 0),[],Var falseDataConId),
308                                    (DEFAULT             ,[],Var trueDataConId )])
309
310   -- Data types with a single constructor, which has a single arg
311   | is_product_type && data_con_arity == 1
312   = let
313         (maybe_ty, wrapper)    = resultWrapper unwrapped_res_ty
314         (unwrapped_res_ty : _) = data_con_arg_tys
315     in
316     (maybe_ty, \e -> mkApps (Var (dataConWrapId data_con)) 
317                             (map Type tycon_arg_tys ++ [wrapper e]))
318
319   -- newtypes
320   | isNewType result_ty
321   = let
322         rep_ty              = repType result_ty
323         (maybe_ty, wrapper) = resultWrapper rep_ty
324     in
325     (maybe_ty, \e -> mkCoerce result_ty rep_ty (wrapper e))
326
327   | otherwise
328   = pprPanic "resultWrapper" (ppr result_ty)
329   where
330     maybe_product_type                                  = splitProductType_maybe result_ty
331     is_product_type                                     = maybeToBool maybe_product_type
332     Just (_, tycon_arg_tys, data_con, data_con_arg_tys) = maybe_product_type
333     data_con_arity                                      = dataConSourceArity data_con
334 \end{code}