packages feed

sbv-14.8: Data/SBV/Compilers/C/Tuple.hs

-----------------------------------------------------------------------------
-- |
-- Module    : Data.SBV.Compilers.C.Tuple
-- Copyright : (c) Levent Erkok
-- License   : BSD3
-- Maintainer: erkokl@gmail.com
-- Stability : experimental
--
-- Structural tuple lowering for the SBV-to-C compiler.
-----------------------------------------------------------------------------

{-# OPTIONS_GHC -Wall -Werror #-}

module Data.SBV.Compilers.C.Tuple
  ( tupleKinds
  , tupleCType
  , tupleFieldName
  , tupleForwardTypeDecls
  , tupleTypeDecls
  , tupleOwnershipTypeDecls
  , tupleOwnedInitName
  , tupleOwnedSetName
  , tupleOwnedCloneName
  , tupleOwnedReleaseName
  , tupleDriverInit
  , tupleValue
  , tupleConst
  , tupleExpr
  , tupleUsesExact
  , tupleNeedsOwnership
  , elementCType
  , kindTag
  ) where

import Data.List                       (nub, sortOn)
import qualified Data.Set as Set

import Text.PrettyPrint.HughesPJ
import qualified Text.PrettyPrint.HughesPJ as P ((<>))

import Data.SBV.Compilers.C.Array      (arrayStoredLoad, arrayStoredValue)
import Data.SBV.Compilers.C.GMP        (gmpFunctionName, gmpOutputType, isExactGMPKind)
import Data.SBV.Compilers.C.List       (listClone, listRelease)
import Data.SBV.Compilers.C.Lowering   (CLowering, expressionLowering)
import Data.SBV.Compilers.C.Set        (setClone, setRelease)
import Data.SBV.Compilers.C.Syntax     (cCommentText)
import Data.SBV.Compilers.C.Types      ( elementCType, kindTag, tupleCType, tupleFieldName
                                     , tupleOwnedInitName, tupleOwnedSetName, tupleOwnedCloneName, tupleOwnedReleaseName
                                     , textCloneName, textReleaseName
                                     )
import Data.SBV.Compilers.C.Value      (managedValueClone, managedValueRelease, valueDriverInit, valueNeedsOwnership)
import Data.SBV.Compilers.CodeGen      (CgConfig)
import Data.SBV.Core.Data
import Data.SBV.Core.Kind              (expandKinds)

-- | Return the distinct tuple kinds used by a program, with nested tuple
-- declarations ordered before the structures that contain them.
tupleKinds :: Set.Set Kind -> [Kind]
tupleKinds = sortOn tupleDepth . nub . concatMap (filter isTuple . expandKinds) . Set.toAscList
 where tupleDepth :: Kind -> Int
       tupleDepth (KTuple fields) = 1 + maximum (0 : map tupleDepth fields)
       tupleDepth _               = 0

-- | Emit forward declarations that permit collection descriptors to refer to
-- tuple element types before their layouts are complete.
tupleForwardTypeDecls :: [Kind] -> Doc
tupleForwardTypeDecls []     = empty
tupleForwardTypeDecls tuples = text . unlines $ "/* Forward declarations for structural tuples. */" : concatMap declaration tuples
 where declaration kind@KTuple{} =
         [ "#ifndef " ++ tupleForwardGuard kind
         , "#define " ++ tupleForwardGuard kind
         , "typedef struct " ++ tupleCType kind ++ " " ++ tupleCType kind ++ ";"
         , "#endif"
         , ""
         ]
       declaration kind = error $ "SBV->C: Expected a tuple kind, received " ++ show kind

-- | Emit public structure definitions for all tuple kinds used by a program.
-- The unit tuple carries a private byte because ISO C does not permit empty
-- structures. Each definition also provides its own guarded forward
-- declaration so this function remains independently usable.
tupleTypeDecls :: [Kind] -> Doc
tupleTypeDecls []     = empty
tupleTypeDecls tuples = text . unlines $ "/* Structural tuple values. */" : concatMap declaration tuples
 where declaration kind@(KTuple fields) =
            [ "#ifndef " ++ tupleGuard kind
            , "#define " ++ tupleGuard kind
            , "#ifndef " ++ tupleForwardGuard kind
            , "#define " ++ tupleForwardGuard kind
            , "typedef struct " ++ tupleCType kind ++ " " ++ tupleCType kind ++ ";"
            , "#endif"
            , "struct " ++ tupleCType kind ++ " {"
            ]
         ++ (case fields of
               [] -> ["  uint8_t unit;"]
               _  -> zipWith fieldDeclaration [1 :: Int ..] fields)
         ++ [ "};"
            , "#endif"
            , ""
            ]
       declaration kind = error $ "SBV->C: Expected a tuple kind, received " ++ show kind

       fieldDeclaration index kind = "  " ++ elementCType kind ++ " " ++ tupleFieldName index ++ ";"

-- | Emit public ownership helpers for tuples containing exact GMP-backed,
-- string, collection, retained-array, or concrete ADT fields. Inputs may
-- borrow an ordinary tuple value.
-- Cloned values own their recursively managed fields and must be released
-- with 'tupleOwnedReleaseName'.
tupleOwnershipTypeDecls :: CgConfig -> [Kind] -> Doc
tupleOwnershipTypeDecls cfg tuples
  | null owned = empty
  | True       = text . unlines $ concatMap declaration owned
 where owned = filter (tupleNeedsOwnership cfg) tuples

       declaration kind@(KTuple fields) =
          [ "#ifndef " ++ ownershipGuard
          , "#define " ++ ownershipGuard
          , "/* Recursive ownership helpers for " ++ tupleCType kind ++ ". */"
          , "/* Owned values have unique ownership; clone before copying and release every owner. */"
          , "static inline SBV_CGEN_UNUSED void " ++ tupleOwnedInitName kind ++ "(" ++ tupleCType kind ++ " *value)"
          , "{"
          , "  if (value == NULL) abort();"
          , "  memset(value, 0, sizeof *value);"
          ]
          ++ concat (zipWith initializeField [1 :: Int ..] fields)
          ++ [ "}"
          , ""
          , "static inline SBV_CGEN_UNUSED void " ++ tupleOwnedSetName kind ++ "(" ++ tupleCType kind ++ " *target, " ++ tupleCType kind ++ " source)"
          , "{"
          ]
          ++ concat (zipWith setField [1 :: Int ..] fields)
          ++ [ "}"
          , ""
          , "static inline SBV_CGEN_UNUSED " ++ tupleCType kind ++ " " ++ tupleOwnedCloneName kind ++ "(" ++ tupleCType kind ++ " source)"
          , "{"
          , "  " ++ tupleCType kind ++ " result;"
          , "  " ++ tupleOwnedInitName kind ++ "(&result);"
          , "  " ++ tupleOwnedSetName kind ++ "(&result, source);"
          , "  return result;"
          , "}"
          , ""
          , "static inline SBV_CGEN_UNUSED void " ++ tupleOwnedReleaseName kind ++ "(" ++ tupleCType kind ++ " *value)"
          , "{"
          , "  if (value == NULL) return;"
          ]
          ++ concat (zipWith releaseField [1 :: Int ..] fields)
          ++ [ "  memset(value, 0, sizeof *value);"
          , "}"
          , "#endif"
          , ""
         ]
         where ownershipGuard = tupleCType kind ++ "_OWNERSHIP_DEFINED"

               initializeField index fieldKind
                  | isExactGMPKind cfg fieldKind
                  = let access  = "value->" ++ tupleFieldName index
                        mutable = gmpOutputType fieldKind
                        local   = "field" ++ show index
                    in [ "  " ++ mutable ++ " " ++ local ++ " = (" ++ mutable ++ ") malloc(sizeof(*" ++ local ++ "));"
                       , "  if (" ++ local ++ " == NULL) abort();"
                       , "  " ++ gmpFunctionName fieldKind "init" ++ "(" ++ local ++ ");"
                       , "  " ++ access ++ " = " ++ local ++ ";"
                       ]
                  | fieldKind == KString
                  = ["  value->" ++ tupleFieldName index ++ " = (SString) {NULL, 0, 0};"]
                  | isList fieldKind
                  = ["  value->" ++ tupleFieldName index ++ " = (" ++ elementCType fieldKind ++ ") {NULL, 0};"]
                  | isSet fieldKind
                  = ["  value->" ++ tupleFieldName index ++ " = (" ++ elementCType fieldKind ++ ") {NULL, 0, false};"]
                  | isArray fieldKind
                  = ["  value->" ++ tupleFieldName index ++ " = NULL;"]
                  | isTuple fieldKind && tupleNeedsOwnership cfg fieldKind
                  = ["  " ++ tupleOwnedInitName fieldKind ++ "(&value->" ++ tupleFieldName index ++ ");"]
                  | True
                  = []

               setField index fieldKind
                  | isExactGMPKind cfg fieldKind
                  = ["  " ++ gmpFunctionName fieldKind "set" ++ "((" ++ gmpOutputType fieldKind ++ ") target->" ++ field ++ ", source." ++ field ++ ");"]
                  | fieldKind == KString
                  = [ "  const SString " ++ field ++ "_copy = " ++ textCloneName ++ "(source." ++ field ++ ");"
                    , "  " ++ textReleaseName ++ "(&target->" ++ field ++ ");"
                    , "  target->" ++ field ++ " = " ++ field ++ "_copy;"
                    ]
                  | isList fieldKind
                  = [ "  const " ++ elementCType fieldKind ++ " " ++ field ++ "_copy = " ++ render (listClone fieldKind (text ("source." ++ field))) ++ ";"
                    , "  " ++ render (listRelease fieldKind (text ("target->" ++ field)))
                    , "  target->" ++ field ++ " = " ++ field ++ "_copy;"
                    ]
                  | isSet fieldKind
                  = [ "  const " ++ elementCType fieldKind ++ " " ++ field ++ "_copy = " ++ render (setClone fieldKind (text ("source." ++ field))) ++ ";"
                    , "  " ++ render (setRelease fieldKind (text ("target->" ++ field)))
                    , "  target->" ++ field ++ " = " ++ field ++ "_copy;"
                    ]
                  | isArray fieldKind
                  = [ "  " ++ elementCType fieldKind ++ " " ++ field ++ "_copy = " ++ render (managedValueClone fieldKind (text ("source." ++ field))) ++ ";"
                    , "  " ++ render (managedValueRelease fieldKind (text ("&target->" ++ field)))
                    , "  target->" ++ field ++ " = " ++ field ++ "_copy;"
                    ]
                  | isTuple fieldKind && tupleNeedsOwnership cfg fieldKind
                  = ["  " ++ tupleOwnedSetName fieldKind ++ "(&target->" ++ field ++ ", source." ++ field ++ ");"]
                  | valueNeedsOwnership cfg fieldKind
                  = [ "  " ++ elementCType fieldKind ++ " " ++ field ++ "_copy = " ++ render (managedValueClone fieldKind (text ("source." ++ field))) ++ ";"
                    , "  " ++ render (managedValueRelease fieldKind (text ("&target->" ++ field)))
                    , "  target->" ++ field ++ " = " ++ field ++ "_copy;"
                    ]
                  | True
                  = ["  target->" ++ field ++ " = source." ++ field ++ ";"]
                  where field = tupleFieldName index

               releaseField index fieldKind
                  | isExactGMPKind cfg fieldKind
                  = [ "  if (value->" ++ field ++ " != NULL) {"
                    , "    " ++ gmpFunctionName fieldKind "clear" ++ "((" ++ gmpOutputType fieldKind ++ ") value->" ++ field ++ ");"
                    , "    free((void *) value->" ++ field ++ ");"
                    , "  }"
                    ]
                  | fieldKind == KString
                  = ["  " ++ textReleaseName ++ "(&value->" ++ field ++ ");"]
                  | isList fieldKind
                  = ["  " ++ render (listRelease fieldKind (text ("value->" ++ field)))]
                  | isSet fieldKind
                  = ["  " ++ render (setRelease fieldKind (text ("value->" ++ field)))]
                  | isArray fieldKind
                  = ["  " ++ render (managedValueRelease fieldKind (text ("&value->" ++ field)))]
                  | isTuple fieldKind && tupleNeedsOwnership cfg fieldKind
                  = ["  " ++ tupleOwnedReleaseName fieldKind ++ "(&value->" ++ field ++ ");"]
                  | valueNeedsOwnership cfg fieldKind
                  = ["  " ++ render (managedValueRelease fieldKind (text ("&value->" ++ field)))]
                  | True
                  = []
                  where field = tupleFieldName index
       declaration kind = error $ "SBV->C: Expected a tuple kind, received " ++ show kind

-- | Initialize a generated-driver tuple and populate its fields from a seed.
-- Recursively owned fields use the public owned-tuple storage protocol; other
-- fields use the supplied scalar renderer. Array and ADT fields delegate to
-- the supplied statement initializer.
tupleDriverInit :: CgConfig -> (Kind -> Integer -> Doc) -> (Kind -> String -> Integer -> Doc) -> Kind -> String -> Integer -> Doc
tupleDriverInit cfg renderValue initializeValue kind@KTuple{} externalName seed = valueDriverInit cfg renderValue initializeValue kind externalName seed
tupleDriverInit _   _           _               kind         _            _    = error $ "SBV->C: Expected a tuple kind, received " ++ show kind

-- | Render a concrete tuple value as a C99 compound literal.
tupleConst :: (CV -> Doc) -> CV -> Maybe Doc
tupleConst renderValue (CV kind@(KTuple fieldKinds) (CTuple fieldValues))
  | length fieldKinds == length fieldValues
  = Just $ tupleValue kind (zipWith (\fieldKind fieldValue -> arrayStoredValue fieldKind (renderValue (CV fieldKind fieldValue))) fieldKinds fieldValues)
  | True
  = error $ "SBV->C: Malformed tuple constant " ++ show (CV kind (CTuple fieldValues))
tupleConst _ _ = Nothing

-- | Lower tuple construction, projection, conditionals, and labels. Other
-- operators are left to the scalar pipeline; SBV normally expands structural
-- comparisons into field operations before code generation.
tupleExpr :: Op -> [SV] -> SV -> [Doc] -> Maybe CLowering
tupleExpr op svs resultSV args
  | not (isTuple resultKind || any isTuple svs)
  = Nothing
  | True
  = case (op, svs, args) of
      (TupleConstructor arity, fields, renderedFields)
        | KTuple fieldKinds <- resultKind
        , arity == length fieldKinds
        , map kindOf fields == fieldKinds
        -> lower $ tupleValue resultKind (zipWith arrayStoredValue fieldKinds renderedFields)
      (TupleAccess fieldIndex arity, [tupleSV], [renderedTuple])
        | KTuple fieldKinds <- kindOf tupleSV
        , arity == length fieldKinds
        , fieldIndex >= 1
        , fieldIndex <= arity
        , resultKind == fieldKinds !! (fieldIndex - 1)
        -> let field = parens renderedTuple P.<> text "." P.<> text (tupleFieldName fieldIndex)
           in if isArray resultKind then Just (arrayStoredLoad resultSV field) else lower field
      (Label label, [_], [renderedTuple])
        -> lower $ renderedTuple <+> text "/*" <+> cCommentText label <+> text "*/"
      _ -> Nothing
 where resultKind = kindOf resultSV

       lower = Just . expressionLowering []

-- | Test whether a tuple contains an exact GMP-backed integer, real, or
-- rational at any nesting depth under the active code-generation configuration.
tupleUsesExact :: CgConfig -> Kind -> Bool
tupleUsesExact cfg kind@KTuple{} = any (isExactGMPKind cfg) (expandKinds kind)
tupleUsesExact _   _             = False

-- | Test whether a tuple contains a field that must be cloned when it crosses
-- the generated function's ownership boundary.
tupleNeedsOwnership :: CgConfig -> Kind -> Bool
tupleNeedsOwnership cfg kind@KTuple{} = valueNeedsOwnership cfg kind
tupleNeedsOwnership _   _             = False

-- | Render a tuple expression from its field expressions.
tupleValue :: Kind -> [Doc] -> Doc
tupleValue kind@(KTuple []) [] = parens (text (tupleCType kind)) P.<> braces (text "0")
tupleValue kind@(KTuple fieldKinds) fields
  | length fieldKinds == length fields
  = parens (text (tupleCType kind)) P.<> braces (fsep (punctuate comma designatedFields))
  | True
  = error $ "SBV->C: Tuple literal field mismatch for " ++ show kind
 where designatedFields = zipWith (\index field -> text "." P.<> text (tupleFieldName index) <+> text "=" <+> field) [1 :: Int ..] fields
tupleValue kind _ = error $ "SBV->C: Expected a tuple kind, received " ++ show kind

-- | Return the preprocessor guard protecting one tuple definition.
tupleGuard :: Kind -> String
tupleGuard kind = tupleCType kind ++ "_DEFINED"

-- | Return the preprocessor guard protecting one tuple forward declaration.
tupleForwardGuard :: Kind -> String
tupleForwardGuard kind = tupleCType kind ++ "_FORWARD_DEFINED"