{- 
  MsgBuf.hs: Flounder stub generator for marshalling into / out of in-memory
      message buffers

  Part of Flounder: a message passing IDL for Barrelfish

  Copyright (c) 2007-2010, ETH Zurich.
  All rights reserved.

  This file is distributed under the terms in the attached LICENSE file.
  If you do not find this file, copies can be found by writing to:
  ETH Zurich D-INFK, Universit\"atstr. 6, CH-8092 Zurich. Attn: Systems Group.
-}

module MsgBuf where

import qualified CAbsSyntax as C
import qualified Backend
import BackendCommon hiding (errvar)
import Syntax

------------------------------------------------------------------------
-- Language mapping: C identifier names
------------------------------------------------------------------------

-- Name of the marshall functions
tx_fn_name ifn mn = idscope ifn mn "msgbuf_marshall"
rx_fn_name ifn = ifscope ifn "msgbuf_dispatch"

msgbuf_name = "_msg"
msgbuf_var = C.Variable msgbuf_name

errvar_name = "_err"
errvar = C.Variable errvar_name

------------------------------------------------------------------------
-- Language mapping: Create the header file for this interconnect driver
------------------------------------------------------------------------

header :: String -> String -> Interface -> String
header infile outfile intf =
    unlines $ C.pp_unit $ header_file intf (header_body infile intf)
    where
        header_file :: Interface -> [C.Unit] -> C.Unit
        header_file interface@(Interface name _ _) body =
            let sym = "__" ++ name ++ "_MSGBUF_STUB_H"
            in C.IfNDef sym ([ C.Define sym [] "1"] ++ body) []

header_body :: String -> Interface -> [C.Unit]
header_body infile interface@(Interface ifn descr decls) = [
    intf_preamble infile ifn descr,
    C.Blank,
    C.MultiComment [ "Generic message buffer marshalling header" ],
    C.Blank,
    C.Include C.Standard ("if/" ++ ifn ++ "_defs.h"),
    C.Blank,
    C.UnitList [ tx_fn_proto ifn m | m <- msgs ],
    C.Blank,
    rx_fn_proto ifn]
    where
        (types, msgs) = Backend.partitionTypesMessages decls

tx_fn_proto :: String -> MessageDef -> C.Unit
tx_fn_proto ifn msg =
    C.GVarDecl C.NoScope C.NonConst
         (C.Function C.NoScope (C.TypeName "errval_t") (tx_fn_params ifn msg))
         (tx_fn_name ifn (msg_name msg)) Nothing

tx_fn_params :: String -> MessageDef -> [C.Param]
tx_fn_params ifn (Message _ _ args _)
    = [ C.Param (C.Ptr $ C.Struct "msgbuf") msgbuf_name ]
      ++ concat [ msg_argdecl TX ifn a | a <- args ]

rx_fn_proto :: String -> C.Unit
rx_fn_proto ifn =
    C.GVarDecl C.NoScope C.NonConst
         (C.Function C.NoScope (C.TypeName "errval_t") (rx_fn_params ifn))
         (rx_fn_name ifn) Nothing

rx_fn_params :: String -> [C.Param]
rx_fn_params ifn =
    [C.Param (C.Ptr $ C.Struct $ intf_bind_type ifn) intf_bind_var,
     C.Param (C.Ptr $ C.Struct "msgbuf") msgbuf_name]

------------------------------------------------------------------------
-- Language mapping: Create the stub (implementation) for this interconnect driver
------------------------------------------------------------------------

stub :: String -> String -> Interface -> String
stub infile outfile intf = unlines $ C.pp_unit $ stub_body infile intf

stub_body :: String -> Interface -> C.Unit
stub_body infile intf@(Interface ifn descr decls) = C.UnitList [
    intf_preamble infile ifn descr,
    C.Blank,
    C.MultiComment [ "Generic message buffer marshalling stubs" ],
    C.Blank,

    C.Include C.Standard "barrelfish/barrelfish.h",
    C.Include C.Standard "barrelfish/msgbuf.h",
    C.Include C.Standard ("if/" ++ ifn ++ "_msgbuf_defs.h"),
    C.Blank,

    C.MultiComment [ "Marshalling functions" ],
    C.UnitList [ tx_fn ifn m | m <- msgs ],
    C.Blank,

    C.MultiComment [ "Demarshall/dispatch function" ],
    rx_fn ifn msgs
    ]
    where
        (types, msgs) = Backend.partitionTypesMessages decls

tx_fn :: String -> MessageDef -> C.Unit
tx_fn ifn msg@(Message _ mn args _) =
    C.FunctionDef C.NoScope (C.TypeName "errval_t") (tx_fn_name ifn mn) (tx_fn_params ifn msg)
    [
        localvar (C.TypeName "errval_t") errvar_name Nothing,
        C.SBlank,
        C.StmtList $ concat [ handle_marshall $ marshall_arg a | a <- msgnum_arg:args ],
        C.Return $ C.Variable "SYS_ERR_OK"
    ] where
        msgnum_arg = Arg (Builtin msg_code_type) (Name $ msg_enum_elem_name ifn mn)

        handle_marshall :: C.Expr -> [C.Stmt]
        handle_marshall marshall_expr =
            [C.Ex $ C.Assignment errvar marshall_expr,
             C.If (C.Call "err_is_fail" [errvar])
                [C.Return errvar] []]

        marshall_arg :: MessageArgument -> C.Expr
        marshall_arg (Arg (TypeAlias _ b) v) = marshall_arg (Arg (Builtin b) v)
        marshall_arg (Arg (Builtin b) (Name n))
            = C.Call ("msgbuf_marshall_" ++ (show b)) [msgbuf_var, C.Variable n]
        marshall_arg (Arg (Builtin b) (DynamicArray n l _))
            | b `elem` [Int8, UInt8, Char]
                = C.Call "msgbuf_marshall_buffer" [msgbuf_var, C.Variable n, C.Variable l]
            | otherwise = error "dynamic arrays are NYI for MsgBuf backend"
        marshall_arg a = error $ "complex types are NYI for MsgBuf backend: " ++ show a

rx_fn :: String -> [MessageDef] -> C.Unit
rx_fn ifn msgs =
    C.FunctionDef C.NoScope (C.TypeName "errval_t") (rx_fn_name ifn) (rx_fn_params ifn)
    [
        localvar (C.TypeName "errval_t") errvar_name Nothing,
        localvar (C.TypeName msg_code_ctype) "msgnum" Nothing,
        C.SBlank,
        C.SComment "unmarshall message code",
        C.Ex $ C.Assignment errvar (C.Call msg_code_unmarshall_func
                                        [msgbuf_var,
                                         C.AddressOf $ C.Variable "msgnum"]),
        C.If (C.Call "err_is_fail" [errvar]) [C.Return errvar] [],
        C.SBlank,
        C.Switch (C.Variable "msgnum") cases
            [C.Return $ C.Variable "FLOUNDER_ERR_RX_INVALID_MSGNUM"],
        C.SBlank,
        C.Return $ C.Variable "SYS_ERR_OK"
    ] where
        cases = [C.Case (C.Variable $ msg_enum_elem_name ifn mn) [handle_msg mn args]
                 | Message _ mn args _ <- msgs]

        handle_msg :: String -> [MessageArgument] -> C.Stmt
        handle_msg mn args = C.Block [
            localvar (C.Struct $ msg_argstruct_name RX ifn mn) "args" Nothing,
            C.SBlank,
            C.StmtList $ concat $ map (handle_unmarshall.unmarshall_arg) args,
            C.Ex $ C.Call "assert" [C.Binary C.NotEquals rx_handler (C.Variable "NULL")],
            C.Ex $ C.CallInd rx_handler rx_handler_args,
            C.Break
            ] where
                rx_handler = C.DerefField bindvar "rx_vtbl" `C.FieldOf` mn
                rx_handler_args = [bindvar] ++ (map arg_field $ concat $ map mkargs args)

                mkargs (Arg _ (Name n)) = [n]
                mkargs (Arg _ (DynamicArray n l _)) = [n, l]

        handle_unmarshall :: C.Expr -> [C.Stmt]
        handle_unmarshall unmarshall_expr =
            [C.Ex $ C.Assignment errvar unmarshall_expr,
             C.If (C.Call "err_is_fail" [errvar])
                [C.Return errvar] []]

        unmarshall_arg :: MessageArgument -> C.Expr
        unmarshall_arg (Arg (TypeAlias _ b) v) = unmarshall_arg (Arg (Builtin b) v)
        unmarshall_arg (Arg (Builtin b) (Name n))
            = C.Call ("msgbuf_unmarshall_" ++ (show b))
                        [msgbuf_var, C.AddressOf $ arg_field n]
        unmarshall_arg (Arg (Builtin b) (DynamicArray n l _))
            | b `elem` [Int8, UInt8, Char]
                = C.Call "msgbuf_unmarshall_buffer"
                    [msgbuf_var, C.AddressOf $ arg_field l,
                     C.Cast (C.Ptr $ C.Ptr C.Void) $ C.AddressOf $ arg_field n]
            | otherwise = error "dynamic arrays are NYI for MsgBuf backend"
        unmarshall_arg a = error $ "complex types are NYI for MsgBuf backend: " ++ show a

        arg_field n = (C.Variable "args") `C.FieldOf` n


msg_code_type = UInt16
msg_code_ctype = "uint16_t"
msg_code_unmarshall_func = "msgbuf_unmarshall_uint16"
