futhask-1.0.0: src/CodeGen.hs
{-# LANGUAGE OverloadedStrings #-}
--{-# LANGUAGE PatternSynonyms #-}
--{-# LANGUAGE ViewPatterns #-}
module CodeGen where
import Futhark.Manifest
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.IO as T
import qualified Data.Text.Encoding as T
import qualified Data.ByteString as B
import qualified Data.Char as C
import qualified Data.Map.Strict as M
import Text.ParserCombinators.ReadP as P
import Debug.Trace
readManifest :: FilePath -> IO Manifest
readManifest path = B.readFile path >>= \bs -> case (manifestFromJSON $ T.decodeUtf8 bs) of
Just manifest -> return manifest
Nothing -> error "Invalid manifest file"
writeText path = B.writeFile path . T.encodeUtf8
putText = T.putStrLn
--Futhark to Haskell
futhaskTypeMap :: M.Map Text Text
futhaskTypeMap = M.fromList
[ ("f16", "F16")
, ("f32", "F32")
, ("f64", "F64")
, ("bool", "CBool")
, ("i8" , "I8")
, ("i16", "I16")
, ("i32", "I32")
, ("i64", "I64")
, ("u8" , "U8")
, ("u16", "U16")
, ("u32", "U32")
, ("u64", "U64") ]
primitive =
[ "f16"
, "f32"
, "f64"
, "bool"
, "i8"
, "i16"
, "i32"
, "i64"
, "u8"
, "u16"
, "u32"
, "u64" ]
isPrimitive a = elem a primitive
isBool a = a == "bool"
haskellElemType :: TypeName -> Text
haskellElemType ft = case M.lookup ft futhaskTypeMap of
Just ht -> ht
Nothing -> error (T.unpack ft <> " is not a valid element type")
haskellType :: TypeName -> Text
haskellType ft = case M.lookup ft futhaskTypeMap of
Just ht -> ht
Nothing -> reformatTypeName ft
rawHaskellType :: TypeName -> Text
rawHaskellType ft = case M.lookup ft futhaskTypeMap of
Just ht -> ht
Nothing -> pointer (raw $ reformatTypeName ft)
rawOutputType output = if M.member (outputType output) futhaskTypeMap
then pointer (haskellType $ outputType output)
else pointer $ pointer (raw $ haskellType (outputType output))
rawInputType input = if M.member (inputType input) futhaskTypeMap
then (haskellType $ inputType input)
else pointer (raw $ haskellType (inputType input))
--C to Haskell
chaskTypeMap = M.fromList
[ ("int", "Int")
, ("float", "Float")
, ("double", "Double")
, ("char", "CChar")
, ("unsigned char", "CUChar")
, ("bool", "CBool")
, ("void", "()")
, ("int8_t" , "Int8")
, ("int16_t", "Int16")
, ("int32_t", "Int32")
, ("int64_t", "Int64")
, ("uint8_t" , "Word8")
, ("uint16_t", "Word16")
, ("uint32_t", "Word32")
, ("uint64_t", "Word64")
, ("size_t", "CSize")
, ("FILE", "CFile")
, ("cl_mem", "CLMem")
, ("cl_command_queue", "CLCommandQueue")
, ("CUdeviceptr", "DevicePtr ()") ]
csize = "CSize"
cbyte = "CChar"
capitalize t = case T.uncons t of
Nothing -> t
Just (i, r) -> T.cons (C.toUpper i) r
decapitalize t = case T.uncons t of
Nothing -> t
Just (i, r) -> T.cons (C.toLower i) r
-- bad conversion, needs improvement
markdown2haddock t = T.replace "`" "@" $ t
comment t = case T.lines (markdown2haddock t) of
[] -> ""
(l:ls) -> T.unlines $ map ("-- " <>) $ ("|" <> l : ls)
hcomment t = "-- *" <> t <> "\n"
codecomment t = "-- | @" <> t <> "@\n"
indent :: Text -> Text
indent = T.unlines . (map (" " <>)) . T.lines
commaSep = T.intercalate ", "
arrowSep = T.intercalate " -> "
wrap t = "(" <> t <> ")"
tuple :: [Text] -> Text
tuple elements = wrap (commaSep elements)
strictTuple = tuple . map ("!"<>)
wrapIfNotOneWord t = if length (T.words t) == 1 then t else wrap t
wrapIfNotTupleOrPrimitive t s = if isPrimitive t then s else if isTuple t then s else wrap s
wrapIfNotWrappedOrSingleton s = if (T.head s == '(' && T.last s == ')') || length (T.words s) == 1 then s else wrap s
raw t = "Raw." <> t
wrapped t = "Wrapped." <> t
(<->) a b = a <> "_" <> b
(<~>) a b = a <> " " <> b
(<=>) a b = a <> " = " <> b
(</>) a b = a <> "\n" <> b
(</~>) a b = a <> "\n " <> b
pointer t = "Ptr " <> wrapIfNotOneWord t
void = "()"
unpacked t = "{-# UNPACK #-}!(" <> t <> ")"
data CompositeType = SimpleType Text | ArrayType Int CompositeType | TupleType [CompositeType] | RecordType [(String, CompositeType)] | SumType [(String, [CompositeType])] deriving Show
formatCompositeType :: CompositeType -> Text
formatCompositeType (SimpleType name) = capitalize name
formatCompositeType (ArrayType rank elem) = "Arr" <> T.pack (show rank) <-> formatCompositeType elem
formatCompositeType (TupleType fields) = if length fields == 0 then "Unit" else
"Tup" <> T.pack (show (length fields)) <-> T.intercalate "_" (map formatCompositeType fields)
formatCompositeType (RecordType fields) =
"Rec" <> T.pack (show (length fields)) <-> T.intercalate "_" (map (\(n, t) -> T.pack n <-> formatCompositeType t) fields)
formatCompositeType (SumType variants) = "Sum" <> T.pack (show (length variants)) <-> T.intercalate "_" (map (\(c, ps) -> T.pack c <-> T.intercalate "_" (map formatCompositeType ps)) variants)
haskellEquivType :: CompositeType -> Text
haskellEquivType (SimpleType t) = haskellType t
haskellEquivType (ArrayType rank elem) = if rank > 0 then "Array " <> wrapIfNotWrappedOrSingleton (haskellEquivType (ArrayType (rank-1) elem)) else haskellEquivType elem
haskellEquivType (TupleType fields) = tuple (map haskellEquivType fields)
haskellEquivType (RecordType fields) = formatCompositeType(RecordType fields)
haskellEquivType (SumType variants) = formatCompositeType (SumType variants)
readDigit = fmap C.digitToInt (P.satisfy C.isDigit)
readTuple readElem = do
P.char '('
elems <- P.sepBy readElem (skipSpaces >> P.char ',' >> skipSpaces)
P.char ')'
pure elems
readRecord readFieldType = do
P.char '{'
fields <- P.sepBy readField (skipSpaces >> P.char ',' >> skipSpaces)
P.char '}'
pure fields
where
readField = do
skipSpaces
n <- P.munch1 (/= ':')
skipSpaces
P.char ':'
skipSpaces
t <- readFieldType
pure (n, t)
readCompositeType :: P.ReadP CompositeType
readCompositeType = do
skipSpaces
get >>= \c -> case c of
'(' -> P.sepBy readCompositeType (P.char ',' >> skipSpaces) >>= \m -> char ')' >> pure (if length m == 1 then head m else TupleType m)
'{' -> P.sepBy
(P.munch (/=':') >>= \n -> char ':' >> P.skipSpaces >> readCompositeType >>= \t -> pure (n, t))
(P.char ',' >> P.skipSpaces)
>>= \m -> char '}' >> pure (RecordType m)
'[' -> P.munch (\c -> elem c ("[]" :: String)) >>= \p -> readCompositeType >>= \elem -> pure (ArrayType (div (length p + 1) 2) elem)
'#' -> P.sepBy
(P.munch (/=' ') >>= \cons -> P.sepBy readCompositeType (P.char ' ') >>= \payloads -> pure (cons, payloads))
(P.string " | #")
>>= \cps -> pure (SumType cps)
_ -> P.munch (\c -> not $ elem c (" ()[]{},#|"::String)) >>= \cs -> pure (SimpleType (T.pack (c:cs)))
where
char c = P.char c >>= \_ -> pure ()
parseCompositeType :: TypeName -> CompositeType
parseCompositeType n = case (P.readP_to_S (readCompositeType >>= \t -> P.eof >> pure t) $ T.unpack n) of
[] -> error (T.unpack ("unable to parse type: " <> n))
(a:_) -> fst a
parseCompositeType' = fst . head . P.readP_to_S readCompositeType
reformatTypeName = formatCompositeType . parseCompositeType
reformatTypeName' = T.unpack . formatCompositeType . parseCompositeType . T.pack
nativeType :: TypeName -> Text
nativeType = haskellEquivType . parseCompositeType
isTuple t = case parseCompositeType t of
(TupleType _) -> True
_ -> False
type Constraint = Text
assignment :: Text -> Text -> Text
assignment a b = a <> " = " <> b <> "\n"
sumFormat :: TypeName -> [(Text, [TypeName])] -> Text
sumFormat name constructors
= "data " <> name <> "\n"
<> indent ("= " <> T.intercalate "\n| " (map (\(c, as) -> T.unwords (c:as)) constructors) <> "\n")
<> "\n"
sumFormat' :: TypeName -> [(Text, [TypeName])] -> Text
sumFormat' name constructors
= "data " <> name <> "\n"
<> indent ("= " <> T.intercalate "\n| " (map (\(c, as) -> T.unwords (c:as)) constructors) <> "\n")
<> " deriving Show\n"
<> "\n"
recFormat :: TypeName -> [(Text, TypeName)] -> Text
recFormat name fields
= "data " <> name <> " = " <> name <> "\n"
<> indent ("{ " <> T.intercalate "\n, " (map (\(f, t) -> f <> " :: " <> t) fields) <> " }\n")
<> "\n"
recFormat' :: TypeName -> [(Text, TypeName)] -> Text
recFormat' name fields
= "data " <> name <> " = " <> name <> "\n"
<> indent ("{ " <> T.intercalate "\n, " (map (\(f, t) -> f <> " :: " <> t) fields) <> " } deriving Show\n")
<> "\n"
qrecFormat :: TypeName -> [(Text, TypeName)] -> Text
qrecFormat name fields
= "data " <> name <> " c = " <> name <> "\n"
<> indent ("{ " <> T.intercalate "\n, " (map (\(f, t) -> f <> " :: " <> t) fields) <> " }\n")
<> "\n"
typeFormat :: TypeName -> TypeName -> Text
typeFormat nt ot
= "type " <> nt <> " = " <> ot <> "\n"
qtypeFormat :: TypeName -> TypeName -> Text
qtypeFormat nt ot
= "type " <> nt <> " c = " <> ot <> "\n"
newtypeFormat :: TypeName -> TypeName -> Text
newtypeFormat nt ot
= "newtype " <> nt <> " = " <> nt <~> ot <> "\n"
newtypeFormat' :: TypeName -> TypeName -> Text
newtypeFormat' nt ot
= "newtype " <> nt <> " = " <> nt <~> ot <~> "deriving Show\n"
qnewtypeFormat :: TypeName -> TypeName -> Text
qnewtypeFormat nt ot
= "newtype " <> nt <> " c = " <> nt <~> ot <> "\n"
caseFormat :: Text -> [(Text, Text)] -> Text
caseFormat expression cases
= "case " <> expression <> " of\n"
<> indent (mconcat (map (\(pattern, result) -> pattern <~> "->" <~> result <> "\n") cases))
functionFormat :: Text -> TypeName -> [(Text, TypeName, [Constraint])] -> Text -> Text
functionFormat name outputType arguments body
= name
<> " :: "
<> tuple (concatMap (\(_, t, cs) -> (map (\c -> c <~> t) cs) ) arguments)
<> " => "
<> arrowSep ( (map (\(_, t, _) -> t) arguments) <> [outputType])
<> "\n"
<> name
<> mconcat (map (\(n, _, _) -> " " <> n) arguments)
<> " = \n"
<> indent body
<> "\n"
classFormat :: Text -> [(TypeName, [Constraint])] -> [([Text], Text)] -> [(Text, TypeName, [TypeName])] -> Text
classFormat name parameters dependencies methods
= "class "
<> tuple (concatMap (\(t, cs) -> (map (\c -> c <> " " <> t) cs) ) parameters)
<> " => "
<> name
<> " "
<> T.unwords (map fst parameters)
<> if dependencies == []
then ""
else " | " <> commaSep (map (\(ts, t) -> T.unwords ts <> " -> " <> t) dependencies)
<> " where"
<> "\n"
<> indent (mconcat $ map (\(n,t,ats) -> n <> " :: " <> arrowSep (ats <> [t]) <> "\n") methods)
<> "\n"
instanceFormat :: Text -> [(TypeName, [Constraint])] -> [Text] -> Text
instanceFormat className instanceTypes methods
= "instance "
<> className
<> " "
<> T.unwords (map fst instanceTypes)
<> " where"
<> "\n"
<> indent (mconcat methods)
<> "\n"
cFFICall :: Text -> CFuncName -> [Text] -> Text -> Text
cFFICall name cfunc argTypes returnType
= "foreign import ccall unsafe \"" <> cfunc <> "\""
<> "\n "
<> decapitalize name
<> "\n :: "
<> mconcat (map (<> "\n -> ") argTypes)
<> "IO " <> wrapIfNotOneWord returnType
<> "\n"
withCtxCall outformat fun inputTypes outputTypes = ""
<> mconcat (zipWith (\i t -> if isPrimitive t
then ""
else " -> withRawObject in" <> i <> " $ \\in" <> i <> "'\n") inputNums inputTypes)
<> mconcat (map (\i -> " -> alloca $ \\outptr" <> i <> "\n") outputNums)
<> " -> inContextWithError ctx (\\ctx'\n"
<> " -> " <> fun <> " ctx' "
<> T.unwords (map ("outptr" <>) outputNums)
<> " "
<> T.unwords (zipWith (\i t -> if isPrimitive t
then "in" <> i
else "in" <> i <> "'") inputNums inputTypes) <> ")\n"
<> " >> "
<> mconcat (zipWith (\i t -> "peek outptr" <> i <> " >>= wrap" <> (if isPrimitive t then "" else "FO") <~> "ctx >>= \\out" <> i <> "\n -> ") outputNums outputTypes)
<> "pure" <~> outformat (map ("out" <>) outputNums) <> "\n"
where
inputNums = zipWith (\i _ -> T.pack (show (i::Int))) [0..] inputTypes
outputNums = zipWith (\i _ -> T.pack (show (i::Int))) [0..] outputTypes
futCall fun inputTypes outputTypes = "unsafeLiftFromIO $ \\ctx\n" <> withCtxCall tuple fun inputTypes outputTypes
rawContextType :: Text
rawContextType = "C.Context"
contextPointer = pointer rawContextType
rawEntryCall :: Text -> EntryPoint -> Text
rawEntryCall name entry = cFFICall name (entryPointCFun entry) argTypes "Int"
where
argTypes = contextPointer
: (rawOutputType $ entryPointOutput entry)
: (map rawInputType $ entryPointInputs entry)
packTuple manifest t = pack t
where
inContext f args = "inContextWithError ctx (\\ctx -> " <> f <> " ctx " <> T.unwords args <> ")"
outputInContext f args = "alloca (\\ptr -> " <> inContext f ("ptr":args) <> " >> peek ptr)"
subtypes t = case M.lookup t (manifestTypes manifest) of
Just (TypeOpaque _ _ (Just (OpaqueRecord ops)) _) -> map recordFieldType (recordFields ops)
_ -> error "nontuple type"
subargs t = zipWith (\i _ -> "t" <> T.pack (show i)) [0..] (subtypes t)
pack t = let as = subargs t
in "(\\" <> tuple as <> "\n"
<> indent ( " -> "
<> mconcat (zipWith (\st a -> if isPrimitive st
then ""
else if isTuple st
then pack st <~> a <> " >>= \\" <> a <> "\n -> "
else "withRawObject " <> a <> " $ \\" <> a <> "\n -> ")
(subtypes t) as)
<> outputInContext ("Raw.new_" <> reformatTypeName t) as <> " >>= \\t\n -> "
<> mconcat (zipWith (\t a -> if isTuple t
then inContext ("Raw.free_" <> reformatTypeName t) [a] <> "\n >> "
else "")
(subtypes t) as)
<> "pure t) " )
unpackTuple manifest t = unpack t
where
inContext f args = "inContextWithError ctx (\\ctx -> " <> f <> " ctx " <> T.unwords args <> ")"
outputInContext f args = "alloca (\\ptr -> " <> inContext f ("ptr":args) <> " >> peek ptr)"
subtypes t = case M.lookup t (manifestTypes manifest) of
Just (TypeOpaque _ _ (Just (OpaqueRecord ops)) _) -> map recordFieldType (recordFields ops)
_ -> error "nontuple type"
subargs t = zipWith (\i _ -> "t" <> T.pack (show i)) [0..] (subtypes t)
unpack t = let as = subargs t
in "(\\t\n"
<> indent ( " -> "
<> mconcat (zipWith3 (\a st i -> outputInContext ("Raw.project" <-> reformatTypeName t <-> T.pack (show i)) ["t"]
<> (if isTuple st
then "\n >>= " <> unpack st
else (if isPrimitive st then "" else " >>= wrapFO ctx"))
<> " >>= \\" <> a <> "\n -> ")
as (subtypes t) [0..])
<> inContext ("Raw.free" <-> reformatTypeName t) ["t"] <> "\n >> pure " <> tuple as <> ") " )
packTuple' manifest t = pack t
where
inContext f args = "C.inContextWithError ctx (\\ctx -> " <> f <> " ctx " <> T.unwords args <> ")"
outputInContext f args = "F.alloca (\\ptr -> " <> inContext f ("ptr":args) <> " >> F.peek ptr)"
subtypes t = case M.lookup t (manifestTypes manifest) of
Just (TypeOpaque _ _ (Just (OpaqueRecord ops)) _) -> map recordFieldType (recordFields ops)
_ -> error "nontuple type"
subargs t = zipWith (\i _ -> "t" <> T.pack (show i)) [0..] (subtypes t)
pack t = let as = subargs t
in "(\\" <> tuple as <> "\n"
<> indent ( " -> "
<> mconcat (zipWith (\st a -> if isPrimitive st
then if isBool st
then "pure (F.fromBool " <> a <> ") >>= \\" <> a <> "\n -> "
else ""
else if isTuple st
then pack st <~> a <> " >>= \\" <> a <> "\n -> "
else "O.withRawObject " <> a <> " $ \\" <> a <> "\n -> ")
(subtypes t) as)
<> outputInContext ("Raw.new_" <> reformatTypeName t) as <> " >>= \\t\n -> "
<> mconcat (zipWith (\t a -> if isTuple t
then inContext ("Raw.free_" <> reformatTypeName t) [a] <> "\n >> "
else "")
(subtypes t) as)
<> "pure t) " )
unpackTuple' manifest t = unpack t
where
inContext f args = "C.inContextWithError ctx (\\ctx -> " <> f <> " ctx " <> T.unwords args <> ")"
outputInContext f args = "F.alloca (\\ptr -> " <> inContext f ("ptr":args) <> " >> F.peek ptr)"
subtypes t = case M.lookup t (manifestTypes manifest) of
Just (TypeOpaque _ _ (Just (OpaqueRecord ops)) _) -> map recordFieldType (recordFields ops)
_ -> error "nontuple type"
subargs t = zipWith (\i _ -> "t" <> T.pack (show i)) [0..] (subtypes t)
unpack t = let as = subargs t
in "(\\t\n"
<> indent ( " -> "
<> mconcat (zipWith3 (\a st i -> outputInContext ("Raw.project" <-> reformatTypeName t <-> T.pack (show i)) ["t"]
<> (if isTuple st
then "\n >>= " <> unpack st
else (if isPrimitive st then if isBool st then ">>= pure . F.toBool" else "" else " >>= O.wrapFO ctx"))
<> " >>= \\" <> a <> "\n -> ")
as (subtypes t) [0..])
<> inContext ("Raw.free" <-> reformatTypeName t) ["t"] <> "\n >> pure " <> tuple as <> ") " )
wrappedEntryCall manifest name entry
= (doc <> typeDec <> "\n", pragma <> callDec <> "\n")
where
doc = comment $ "`" <> futtype <> "`" <> case entryPointDoc entry of
Nothing -> ""
Just d -> "\n\n" <> d
inputs = entryPointInputs entry
output = entryPointOutput entry
subtypes t = case M.lookup t (manifestTypes manifest) of
Just (TypeOpaque _ _ (Just (OpaqueRecord ops)) _) -> map recordFieldType (recordFields ops)
_ -> error "nontuple type"
subargs t = zipWith (\i _ -> "t" <> T.pack (show i)) [0..] (subtypes t)
name' = decapitalize name
futtype = name
<~> T.unwords (map (\i -> "(" <> inputName i <> " : " <> (if inputUnique i then "*" else "") <> inputType i <> ")") inputs)
<> " : " <> outputType output
tuningDec = let ps = entryPointTuningParams entry in if length ps == 0
then ""
else comment $ "tuning parameters:\n" <> mconcat (map (\t -> " - " <> t <> "\n") ps)
attributeDec = let as = entryPointAttrs entry in if length as == 0
then ""
else comment $ "attributes:\n" <> mconcat (map (\t -> " - " <> t <> "\n") as)
typeDec = name' <> " :: " <> (if any inputUnique inputs then "MonadIO" else "Monad") <> " m\n => "
<> mconcat (map (\i -> typeDec' (inputType i) <> " -- ^" <> inputName i <> "\n -> " ) inputs)
<> "M.FutT c m "
<> (let ot = outputType output in if isTuple ot
then typeDec' ot
else wrapIfNotOneWord (typeDec' ot))
<> "\n"
typeDec' t = if isTuple t
then tuple (map typeDec' (subtypes t))
else reformatTypeName t <> (if isPrimitive t then "" else " c")
pragma = "{-# NOINLINE " <> name <> " #-}\n"
callDec = name' <~> T.unwords args <> " = M.unsafeLiftFromIO (\\ctx\n -> "
<> mconcat (zipWith prepareInput inputs args)
<> callRaw
<> processOutput
<> cleanup
<> "C.sync ctx >> pure res)"
<> if any inputUnique inputs
then " >>= \\res\n -> " <> freeuniqueinputs <> "pure res\n"
else "\n"
args = zipWith (\i _ -> "arg" <> T.pack (show i)) [0..] inputs
prepareInput i a = let t = inputType i in if isPrimitive t
then if isBool t
then "pure (F.fromBool " <> a <> ") >>= \\" <> a <> "\n -> "
else ""
else if isTuple t
then packTuple' manifest t <~> a <> " >>= \\" <> a <> "\n -> "
else "O.withRawObject" <~> a <~> "$ \\" <> a <> "\n -> "
processOutput = let t = outputType output in if isPrimitive t
then if isBool t
then " >>= pure . F.toBool >>= \\res\n -> "
else " >>= \\res\n -> "
else if isTuple t
then "\n >>= " <> unpackTuple' manifest t <> " >>= \\res\n -> "
else "\n >>= O.wrapFO ctx >>= \\res\n -> "
inContext f args = "C.inContextWithError ctx (\\ctx -> " <> f <> " ctx " <> T.unwords args <> ")"
outputInContext f args = "F.alloca (\\ptr -> " <> inContext f ("ptr":args) <> " >> F.peek ptr)"
cleanup = mconcat (zipWith (\i a -> if isTuple (inputType i)
then inContext ("Raw.free_" <> reformatTypeName (inputType i)) [a] <> "\n >> " else "") inputs args)
freeuniqueinputs = mconcat (zipWith (\i a -> if inputUnique i
then finalizeValue a (inputType i)
else "" ) inputs args)
finalizeValue n t = if isPrimitive t
then ""
else if isTuple t
then
let as = subargs t
in "(\\" <> tuple as <> "\n"
<> indent ( " -> "
<> mconcat (zipWith finalizeValue as (subtypes t))
<> "pure () ")
<> ") " <> n <> "\n >> "
else "O.finalizeFO " <> n <> "\n >> "
callRaw = outputInContext ("RawEntry." <> name) args
rawTypeDec :: Text -> Type -> Text
rawTypeDec name _ = codecomment name <> "\n" <> "data" <~> reformatTypeName name <> "\n\n"
wrappedTypeDec :: Text -> Type -> Text
wrappedTypeDec name _ = if isTuple name then "" else
let name' = reformatTypeName name
in codecomment name <> "\n"
<> "data " <> name' <> " c = " <> name' <~> unpacked "Futhask.ReferenceCounter" <~> unpacked ("F.ForeignPtr" <~> raw name') <> "\n"
<> "\n"
unpackedType manifest t = if isPrimitive t
then reformatTypeName t
else if isTuple t
then tuple (map (unpackedType manifest) (subtypes t))
else "Packed." <> reformatTypeName t <~> "c"
where
subtypes t = case M.lookup t (manifestTypes manifest) of
Just (TypeOpaque _ _ (Just (OpaqueRecord ops)) _) -> map recordFieldType (recordFields ops)
_ -> error "nontuple type"
unpackedTypeDec manifest name (TypeOpaque ctype ops mExtraOps doc) = dec
where
dec = case mExtraOps of
Just (OpaqueSum ops) -> unpackedSumDec manifest name ops
Just (OpaqueRecord ops) -> unpackedRecordDec manifest name ops
Just (OpaqueRecordArray ops) -> unpackedRecordArrayDec manifest name ops
_ -> ""
unpackedTypeDec manifest name _ = ""
unpackedSumDec manifest name ops = codecomment name <> sumFormat (reformatTypeName name <~> "c") (map variant $ sumVariants ops)
where
variant v = (reformatTypeName name <-> sumVariantName v, map (\p -> wrapIfNotTupleOrPrimitive p (unpackedType manifest p)) $ sumVariantPayload v)
unpackedRecordDec manifest name ops = if length fields == 0 then "" else if isTuple'
then if isTuple name
then ""
else codecomment name <> qnewtypeFormat name' (tuple (map snd fields)) <> "\n"
else codecomment name <> qrecFormat name' fields
where
name' = reformatTypeName name
isTuple' = fst (fields !! 0) == "0"
fields = map (\f -> (recordFieldName f, unpackedType manifest (recordFieldType f))) $ recordFields ops
unpackedRecordArrayDec manifest name ops = if isTuple'
then codecomment name <> qnewtypeFormat name' (tuple (map snd fields)) <> "\n"
else codecomment name <> qrecFormat name' fields <> "\n"
where
isTuple' = fst (fields !! 0) == "0"
name' = reformatTypeName name
fields = map (\f -> (recordFieldName f, unpackedType manifest (recordFieldType f))) $ recordArrayFields ops
objectInstance name = instanceFormat "FutharkObject" [(wname,[])] [rawDec, contextDec, freeDec, wrapDec, pointerDec, counterDec]
where
name' = reformatTypeName name
wname = wrapped name'
constructor = wname
rawDec = typeFormat ("RawObject" <~> wname) (raw name')
contextDec = typeFormat ("ObjectContext" <~> wname) "C.Context"
freeDec = assignment "freeRawObject" (raw "free" <-> name')
wrapDec = assignment "wrapRawObject" constructor
pointerDec = assignment ("objectPointer (" <> constructor <> " _ ptr)") "ptr"
counterDec = assignment ("objectCounter (" <> constructor <> " ctr _)") "ctr"
sumCompositeInstance manifest name ops = instanceFormat "FutharkComposite" [(wname,[])] [unpackedDec, packDec, unpackDec]
where
name' = reformatTypeName name
wname = wrapped name'
uname = "Unpacked." <> name'
unpackedDec = typeFormat ("Unpacked" <~> wname) uname
payloadid variant = zipWith (\i _ -> "p" <> T.pack (show i)) [0..] (sumVariantPayload variant)
payloadtypes variant = sumVariantPayload variant
packDec = "pack unpacked = unsafeLiftFromIO $ \\ctx\n -> case unpacked of\n"
<> indent (mconcat (map packVariant (sumVariants ops)))
unpackDec = "unpack sum = unsafeLiftFromIO $ \\ctx\n -> "
<> "withRawObject sum $ \\sum -> "
<> "inContext ctx (\\ctx -> Raw.variant_" <> name' <> " ctx sum)" <> " >>= \\variant -> case variant of\n"
<> indent (mconcat (zipWith unpackVariant [0..] (sumVariants ops)))
packVariant variant = "Unpacked." <> name' <-> sumVariantName variant <~> T.unwords (payloadid variant) <> " ->\n"
<> indent ( ""
<> mconcat (zipWith prepareInput (payloadtypes variant) (payloadid variant))
<> outputInContext ("Raw.construct" <-> name' <-> sumVariantName variant) (payloadid variant)
<> "\n >>= wrapFO ctx >>= \\packed\n -> "
<> mconcat (zipWith (\t id -> if isTuple t
then inContext ("Raw.free_" <> reformatTypeName t) [id] <> "\n >> "
else "") (payloadtypes variant) (payloadid variant))
<> "pure packed\n"
)
unpackVariant i variant = T.pack (show i) <> " -> "
<> mconcat (map (\a -> "alloca $ \\" <> a <> "\n -> ") (payloadid variant))
<> inContext ("Raw.destruct" <-> name' <-> sumVariantName variant) (payloadid variant ++ ["sum"]) <> "\n >> "
<> mconcat (zipWith (\t a -> "peek " <> a <> processOutput t a) (payloadtypes variant) (payloadid variant))
<> "pure (" <> "Unpacked." <> name' <-> sumVariantName variant <~> T.unwords (payloadid variant) <> ")\n"
inContext f args = "inContextWithError ctx (\\ctx -> " <> f <> " ctx " <> T.unwords args <> ")"
outputInContext f args = "alloca (\\ptr -> " <> inContext f ("ptr":args) <> " >> peek ptr)"
prepareInput t a = if isPrimitive t
then ""
else if isTuple t
then packTuple manifest t <~> a <> " >>= \\" <> a <> "\n -> "
else "withRawObject" <~> a <~> "$ \\" <> a <> "\n -> "
processOutput t a = if isPrimitive t
then " >>= \\" <> a <> "\n -> "
else if isTuple t
then "\n >>= " <> unpackTuple manifest t <> " >>= \\" <> a <> "\n -> "
else "\n >>= wrapFO ctx >>= \\"<> a <>"\n -> "
recordCompositeInstance manifest name fields isArr = instanceFormat "FutharkComposite" [(wname,[])] [unpackedDec, packDec, unpackDec]
where
name' = reformatTypeName name
wname = wrapped name'
uname = "Unpacked." <> name'
packfun = if isArr then "zip" else "new"
unpackedDec = typeFormat ("Unpacked" <~> wname) uname
isTuple' = recordFieldName (fields !! 0) == "0"
nums = zipWith (\i _ -> T.pack (show (i::Int))) [0..] fields
fieldid = map ("f"<>) nums
fieldtypes = map recordFieldType fields
fieldnames = map recordFieldName fields
pattern = if isTuple' then tuple fieldid else T.unwords fieldid
packDec = "pack (" <> uname <~> pattern <> ") = unsafeLiftFromIO $ \\ctx\n -> "
<> mconcat (zipWith prepareInput fieldtypes fieldid)
<> outputInContext ("Raw." <> packfun <-> name') fieldid
<> "\n >>= wrapFO ctx >>= \\packed\n -> "
<> mconcat (zipWith (\t id -> if isTuple t
then inContext ("Raw.free_" <> reformatTypeName t) [id] <> "\n >> "
else "") fieldtypes fieldid )
<> "pure packed\n"
unpackDec = "unpack packed = unsafeLiftFromIO $ \\ctx\n -> "
<> "withRawObject packed" <~> "$ \\packed\n -> "
<> mconcat (zipWith3 (\n t id -> outputInContext ("Raw.project" <-> name' <-> n) ["packed"] <> processOutput t id)
fieldnames fieldtypes fieldid)
<> "pure (" <> uname <~> pattern <> ")\n"
inContext f args = "inContextWithError ctx (\\ctx -> " <> f <> " ctx " <> T.unwords args <> ")"
outputInContext f args = "alloca (\\ptr -> " <> inContext f ("ptr":args) <> " >> peek ptr)"
prepareInput t a = if isPrimitive t
then ""
else if isTuple t
then packTuple manifest t <~> a <> " >>= \\" <> a <> "\n -> "
else "withRawObject" <~> a <~> "$ \\" <> a <> "\n -> "
processOutput t a = if isPrimitive t
then " >>= \\" <> a <> "\n -> "
else if isTuple t
then "\n >>= " <> unpackTuple manifest t <> " >>= \\" <> a <> "\n -> "
else "\n >>= wrapFO ctx >>= \\"<> a <>"\n -> "
arrayInstance name futelem rank
= instanceFormat "FutIndexable" [(wname,[])] [rankDec]
<> instanceFormat "FutharkPrimArray'" [(wname,[]), ("C.Context",[]), (rank',[]), (elem',[])] [elemDec, shapeDec, valuesDec, newDec, indexDec]
where
name' = reformatTypeName name
wname = wrapped name'
rank' = "R" <> T.pack (show rank)
elem' = haskellElemType futelem
rankDec = typeFormat ("FutRank" <~> wname) rank'
elemDec = typeFormat ("PrimElem" <~> wname) elem'
newDec = assignment "newFPA" (raw "newArray" <-> name')
shapeDec = assignment "shapeFPA" (raw "shape" <-> name')
valuesDec = assignment "valuesFPA" (raw "values" <-> name')
indexDec = assignment "indexFPA" (raw "index" <-> name')
opaqueInstance name = instanceFormat "FutharkOpaque" [(wname,[])] [storeDec, restoreDec]
where
name' = reformatTypeName name
wname = wrapped name'
storeDec = assignment "storeOpaque" (raw "store" <-> name')
restoreDec = assignment "restoreOpaque" (raw "restore" <-> name')
--wrappedTypeOps :: Text -> Type -> Text
wrappedTypeOps manifest name (TypeArray ctype futelem rank ops) = objectInstance name <> arrayInstance name futelem rank
wrappedTypeOps manifest name (TypeOpaque ctype ops mExtraOps doc) = if isTuple name
then ""
else "-- " <> name </> objectInstance name <> opaqueInstance name <> otherInstances
where
otherInstances = case mExtraOps of
Just (OpaqueSum ops) -> sumCompositeInstance manifest name ops
Just (OpaqueRecord ops) -> recordCompositeInstance manifest name (recordFields ops) False
Just (OpaqueRecordArray ops) -> recordCompositeInstance manifest name (recordArrayFields ops) True
_ -> ""
rawTypeOps :: Text -> Type -> Text
rawTypeOps name (TypeArray ctype futelem rank ops) = freeDec <> shapeDec <> indexDec <> valuesDec <> newDec <> newRawDec <> valuesRawDec
where
name' = reformatTypeName name
elem = haskellElemType futelem
freeDec = cFFICall ("free" <-> name') (arrayFree ops)
[contextPointer, pointer (raw name')] "Int"
shapeDec = cFFICall ("shape" <-> name') (arrayShape ops)
[contextPointer, pointer (raw name')] (pointer "I64")
indexDec = cFFICall ("index" <-> name') (arrayIndex ops)
(contextPointer : pointer elem : pointer (raw name') : replicate rank "I64") "Int"
valuesDec = cFFICall ("values" <-> name') (arrayValues ops)
[contextPointer, pointer (raw name'), pointer elem] "Int"
valuesRawDec = cFFICall ("valuesRaw" <-> name') (arrayValuesRaw ops)
[contextPointer, pointer (raw name')] (pointer cbyte)
newDec = cFFICall ("newArray" <-> name') (arrayNew ops)
(contextPointer : pointer elem : replicate rank "I64") (pointer $ raw name')
newRawDec = cFFICall ("newArrayRaw" <-> name') (arrayNewRaw ops)
(contextPointer : pointer cbyte : replicate rank "I64") (pointer $ raw name')
rawTypeOps name (TypeOpaque ctype ops mExtraOps doc) = freeDec <> storeDec <> restoreDec <> extraOpsDec
where
name' = reformatTypeName name
freeDec = cFFICall ("free" <-> name') (opaqueFree ops)
[contextPointer, pointer (raw name')] "Int"
storeDec = cFFICall ("store" <-> name') (opaqueStore ops)
[contextPointer, pointer (raw name'), pointer (pointer cbyte), pointer csize] "Int"
restoreDec = cFFICall ("restore" <-> name') (opaqueRestore ops)
[contextPointer, pointer cbyte] (pointer (raw name'))
extraOpsDec = case mExtraOps of
Nothing -> ""
Just (OpaqueSum ops) -> rawOpaqueSumDec name ops
Just (OpaqueRecord ops) -> rawOpaqueRecordDec name ops
Just (OpaqueArray ops) -> rawOpaqueArrayDec name ops
Just (OpaqueRecordArray ops) -> rawOpaqueRecordArrayDec name ops
_ -> error ("unknown kind of opaque:" <> T.unpack name)
rawOpaqueSumDec name ops
= cFFICall ("variant" <-> name') (sumVariant ops) [contextPointer, rawHaskellType name] "Int"
<> mconcat (map variantDec $ sumVariants ops)
where
name' = reformatTypeName name
variantDec variant
= cFFICall ("construct" <-> name' <-> sumVariantName variant) (sumVariantConstruct variant)
(contextPointer : pointer (rawHaskellType name) : map rawHaskellType (sumVariantPayload variant)) "Int"
<> cFFICall ("destruct" <-> name' <-> sumVariantName variant) (sumVariantDestruct variant)
(contextPointer : map (pointer . rawHaskellType) (sumVariantPayload variant) <> [rawHaskellType name]) "Int"
rawOpaqueRecordDec name ops
= newDec (recordNew ops) (recordFields ops)
<> mconcat (map fieldDec $ recordFields ops)
where
name' = reformatTypeName name
newDec cfunc fields = cFFICall ("new" <-> name') cfunc
(contextPointer : pointer (rawHaskellType name) : map (rawHaskellType . recordFieldType) fields) "Int"
fieldDec field = cFFICall ("project" <-> name' <-> recordFieldName field) (recordFieldProject field)
[contextPointer, pointer (rawHaskellType $ recordFieldType field), rawHaskellType name] "Int"
rawOpaqueArrayDec name ops = newDec <> setDec <> shapeDec <> indexDec
where
name' = reformatTypeName name
rank = opaqueArrayRank ops
elem = rawHaskellType (opaqueArrayElemType ops)
newDec = cFFICall ("new" <-> name') (opaqueArrayNew ops)
(contextPointer : pointer (rawHaskellType name) : pointer elem : replicate rank "I64") "Int"
setDec = cFFICall ("set" <-> name') (opaqueArraySet ops)
(contextPointer : pointer (rawHaskellType name) : elem : replicate rank "I64") "Int"
shapeDec = cFFICall ("shape" <-> name') (opaqueArrayShape ops)
[contextPointer, pointer (rawHaskellType name)] (pointer "I64")
indexDec = cFFICall ("index" <-> name') (opaqueArrayIndex ops)
(contextPointer : elem : rawHaskellType name : replicate rank "I64") "Int"
rawOpaqueRecordArrayDec name ops = newDec <> setDec <> shapeDec <> indexDec <> zipDec <> mconcat (map projectDec fields)
where
name' = reformatTypeName name
rank = recordArrayRank ops
elem = rawHaskellType (recordArrayElemType ops)
fields = recordArrayFields ops
newDec = cFFICall ("new" <-> name') (recordArrayNew ops)
(contextPointer : pointer (rawHaskellType name) : pointer elem : replicate rank "I64") "Int"
setDec = cFFICall ("set" <-> name') (recordArraySet ops)
(contextPointer : pointer (rawHaskellType name) : elem : replicate rank "I64") "Int"
shapeDec = cFFICall ("shape" <-> name') (recordArrayShape ops)
[contextPointer, pointer (rawHaskellType name)] (pointer "I64")
indexDec = cFFICall ("index" <-> name') (recordArrayIndex ops)
(contextPointer : elem : rawHaskellType name : replicate rank "I64") "Int"
zipDec = cFFICall ("zip" <-> name') (recordArrayZip ops)
(contextPointer : pointer (rawHaskellType name) : map (rawHaskellType . recordFieldType) fields) "Int"
projectDec field = cFFICall ("project" <-> name' <-> recordFieldName field) (recordFieldProject field)
[contextPointer, pointer (rawHaskellType $ recordFieldType field), rawHaskellType name] "Int"
isConvertible manifest t = case M.lookup t (manifestTypes manifest) of
Nothing -> True --Primitive
Just t' -> case t' of
(TypeArray _ _ _ _) -> True
(TypeOpaque _ _ ops _) -> case ops of
Nothing -> False
Just ops' -> case ops' of
(OpaqueArray _) -> False --fix eventually
(OpaqueRecordArray o) -> and (map (check . recordFieldType) (recordArrayFields o))
(OpaqueRecord o) -> and (map (check . recordFieldType) (recordFields o))
(OpaqueSum o) -> and (map (and . map check . sumVariantPayload) (sumVariants o))
where
check = isConvertible manifest
mutable t = "Mutable" <> t
nativeSumDec manifest name ops =
(codecomment name <> sumFormat' name' (map variant $ sumVariants ops), elementDec)
where
variant v = (name' <-> sumVariantName v, map nativeType $ sumVariantPayload v)
name' = reformatTypeName name
elementDec = instanceFormat ("Element") [(name',[])]
[ typeFormat ("Array " <> name') ("BArray " <> name')
, typeFormat ("MArray " <> name') ("MBArray " <> name')
, "size = sizeB\n"
, "msize = msizeB\n"
, "unsafeRead = unsafeReadB\n"
, "unsafePeek = unsafePeekB\n"
, "unsafePoke = unsafePokeB\n"
, "unsafeThaw = unsafeThawB\n"
, "unsafeFreeze = unsafeFreezeB\n"
, "unsafeSlice = unsafeSliceB\n"
, "scratch = scratchB\n"
]
nativeRecordDec manifest name ops = if isTuple name
then ("","")
else if isTuple'
then (codecomment name <> newtypeFormat' (reformatTypeName name) (tuple (map snd fields)) <> "\n", elementDec)
else (codecomment name <> recFormat' (reformatTypeName name) fields, elementDec)
where
elementDec = idec <> mdec <> arrdec <> elemdec <> zipdec <> unzipdec <> showdec :: Text
name' = reformatTypeName name
isTuple' = fst (fields !! 0) == "0"
fields = map (\f -> (recordFieldName f, nativeType (recordFieldType f))) $ recordFields ops
icon' = "Array_" <> name'
mcon' = "MArray_" <> name'
array a = "Array " <> wrapIfNotWrappedOrSingleton a
marray a = "M" <> array a
econ vars = if isTuple'
then name' <~> tuple vars
else name' <~> T.unwords (map wrapIfNotWrappedOrSingleton vars)
icon vars = icon' <~> T.unwords (map wrapIfNotWrappedOrSingleton vars)
mcon vars = mcon' <~> T.unwords (map wrapIfNotWrappedOrSingleton vars)
nums = map (T.pack . show) [0..length fields-1]
vars = map ("a"<>) nums
elems = map ("e"<>) nums
idec = "data" <~> icon' <=> icon (map (wrap.array.snd) fields) <> "\n"
mdec = "data" <~> mcon' <=> mcon (map (wrap.marray.snd) fields) <> "\n"
arrdec = instanceFormat "Element"
[(name', [])]
[ typeFormat ("Array " <> name' ) icon'
, typeFormat ("MArray " <> name' ) mcon'
, "size (" <> icon vars <> ") = size a0\n"
, "msize (" <> mcon vars <> ") = msize a0\n"
, "unsafeRead (" <> icon vars <> ") idx = " <> econ (map (\v -> "unsafeRead " <> v <> " idx") vars) <> "\n"
, "unsafePeek (" <> mcon vars <> ") idx = do\n"
<> mconcat (zipWith (\v e -> " " <> e <> " <- unsafePeek " <> v <> " idx\n") vars elems)
<> " pure (" <> econ elems <> ")\n"
, "unsafePoke (" <> mcon vars <> ") idx (" <> econ elems <> ") = do\n"
<> mconcat (zipWith (\v e -> " unsafePoke " <> v <> " idx " <> e <>"\n") vars elems)
--, "copy (" <> mcon vars <> ") = do\n"
-- <> mconcat (map (\v -> " " <> v <> "' <- copy " <> v <> "\n") vars)
-- <> " pure (" <> mcon (map (<>"'") vars) <> ")\n"
, "unsafeThaw (" <> icon vars <> ") = do\n"
<> mconcat (map (\v -> " " <> v <> "' <- unsafeThaw " <> v <> "\n") vars)
<> " pure (" <> mcon (map (<>"'") vars) <> ")\n"
, "unsafeFreeze (" <> mcon vars <> ") = do\n"
<> mconcat (map (\v -> " " <> v <> "' <- unsafeFreeze " <> v <> "\n") vars)
<> " pure (" <> icon (map (<>"'") vars) <> ")\n"
, "unsafeSlice (" <> icon vars <> ") i s = " <> icon (map (\v -> "(unsafeSlice " <> v <> " i s)") vars)
<> "\n"
, "scratch s = do\n"
<> mconcat (map (\v -> " " <> v <> " <- scratch s\n") vars)
<> " pure (" <> mcon vars <> ")\n"
]
elemdec = instanceFormat ("Element") [(icon',[])]
[ typeFormat ("Array " <> icon') ("BArray " <> icon')
, typeFormat ("MArray " <> icon') ("MBArray " <> icon')
, "size = sizeB\n"
, "msize = msizeB\n"
, "unsafeRead = unsafeReadB\n"
, "unsafePeek = unsafePeekB\n"
, "unsafePoke = unsafePokeB\n"
, "unsafeThaw = unsafeThawB\n"
, "unsafeFreeze = unsafeFreezeB\n"
, "unsafeSlice = unsafeSliceB\n"
, "scratch = scratchB\n"
]
zipdec = ""
<> "zip_" <> name' <> " :: "
<> mconcat (map (\f -> array (snd f) <> " -> ") fields)
<> "Array " <> name' <> "\n"
<> "zip_" <> name' <~> T.unwords vars <=> (if length vars > 1
then "if " <> T.intercalate " && " (zipWith (\v v' -> "size " <> v <> " == " <> "size " <> v') vars (tail vars)) <> "\n"
<> " then " <> icon vars <> "\n"
<> " else error \"sizes of arguments do not match\"\n"
else icon vars <> "\n")
unzipdec = ""
<> "unzip_" <> name' <> " :: "
<> "Array " <> name' <> " -> " <> tuple (map (array.snd) fields) <> "\n"
<> "unzip_" <> name' <~> wrap (icon vars) <=> tuple vars <> "\n"
showdec = instanceFormat
"Show"
[(icon',[])]
[ "show = show . toList\n" ]
nativeOpaqueArrayDec manifest name ops = (codecomment name <> if isArr
then typeFormat name' (nativeType name)
<> "\n"
else newtypeFormat' name' (wrap innerType)
<> "\n"
, "" )
where
innerType = nativeType (mconcat (replicate (opaqueArrayRank ops) "[]") <> opaqueArrayElemType ops)
isArr = take 2 (T.unpack name) == "[]"
name' = reformatTypeName name
nativeRecordArrayDec manifest name ops = (codecomment name <> if isArr
then typeFormat name' (nativeType name)
<> "\n"
else newtypeFormat' name' (wrap innerType)
<> "\n"
, "" )
where
innerType = nativeType (mconcat (replicate (recordArrayRank ops) "[]") <> recordArrayElemType ops)
isArr = take 2 (T.unpack name) == "[]"
name' = reformatTypeName name
nativeTypeDec manifest name (TypeArray ctype futelem rank ops) = (codecomment name <> staticDec <> "\n","")
where
name' = reformatTypeName name
elem = capitalize futelem
staticDec = typeFormat name' (arrayType rank)
arrayType rank = if rank == 1 then "Array " <> elem else "Array (" <> arrayType (rank-1) <> ")"
nativeTypeDec manifest name (TypeOpaque ctype ops mExtraOps _) = case mExtraOps of
Nothing -> ("","")
Just (OpaqueSum ops) -> nativeSumDec manifest name ops
Just (OpaqueRecord ops) -> nativeRecordDec manifest name ops
Just (OpaqueArray ops) -> nativeOpaqueArrayDec manifest name ops
Just (OpaqueRecordArray ops) -> nativeRecordArrayDec manifest name ops
_ -> error ("unknown kind of opaque:" <> T.unpack name)
conversionDec manifest name (TypeArray ctype futelem rank ops) = instanceFormat "Convertible" [(wname,[])]
[native, toFuthark, fromFuthark]
where
name' = reformatTypeName name
wname = "Futhark." <> name'
nname = "Haskell." <> name'
native = typeFormat ("Native" <~> wname) nname
szs = map (\i -> "sz"<> T.pack (show i)) [0..rank-1]
idxs = map (\i -> "idx"<> T.pack (show i)) [0..rank-1]
toFuthark = if rank == 1
then "toFuthark (SArray sz ptr) = unsafeLiftFromIO $ \\ctx"
</~> "-> inContext ctx $ \\cp"
</~> "-> F.withForeignPtr ptr $ \\ap"
</~> "-> Raw.newArray_" <> name' <> " cp ap (fromIntegral sz) >>= wrapFO ctx\n"
else "toFuthark arr = let " <> tuple szs <=> tuple (map (\r -> "size (arr " <> mconcat (replicate r "! 0") <> ")" ) [0..rank-1]) <> " in unsafeLiftFromIO $ \\ctx"
</~> "-> inContext ctx $ \\cp"
</~> "-> F.allocaArray (" <> T.intercalate "*" szs <> ") $ \\tmp"
</~> "-> sequence ((\\" <> T.unwords (init idxs) <> " -> pure (arr" <> mconcat (map (" ! "<>) (init idxs)) <> ") >>= \\(SArray sz ptr) -> if sz == " <> last szs
</~> " then F.withForeignPtr ptr (\\ap -> F.copyArray ap (F.advancePtr tmp (" <> T.intercalate "+" (zipWith (\a b -> a<>"*"<>b) (tail szs) (init idxs)) <> ")) sz)"
</~> " else error \"Uneven array can not be converted\") <$> " <> T.intercalate " <*> " (map (\sz -> "[0.." <> sz <> "-1]") $ init szs) <> ")"
</~> ">> Raw.newArray_" <> name' <> " cp tmp " <> T.unwords (map (wrap.("fromIntegral " <>)) szs) <>" >>= wrapFO ctx\n"
fromFuthark = "fromFuthark arr = unsafeLiftFromIO $ \\ctx"
</~> "-> inContext ctx $ \\cp"
</~> "-> withRawObject arr $ \\fp"
</~> "-> Raw.shape_" <> name' <> " cp fp >>= \\sp"
<> mconcat (zipWith (\i sz -> "\n -> fmap fromIntegral (F.peekElemOff sp " <> T.pack (show i) <> ") >>= \\" <> sz <> "") [0..] szs)
</~> "-> pure (" <> T.intercalate "*" szs <> ") >>= \\sz"
</~> "-> F.mallocForeignPtrArray sz >>= \\ptr"
</~> "-> F.withForeignPtr ptr (\\ap -> Raw.values_" <> name' <> " cp fp ap)"
</~> ">> sync ctx"
</~> ">> pure (SArray sz ptr)" <> if rank == 1
then "\n"
else " >>= \\arr "
</~> "-> pure ("
<> mconcat (zipWith (\i sz -> "A.tabulate " <> sz <> " $ \\" <> i <> " -> ") (init idxs) szs)
<> "A.unsafeSlice arr ("<> T.intercalate "*" (last szs:init idxs) <> ") " <> last szs <> ")"
conversionDec manifest name (TypeOpaque ctype ops mExtraOps doc) = if not (isConvertible manifest name)
then ""
else case mExtraOps of
Nothing -> ""
Just (OpaqueSum ops) -> conversionSumDec manifest name ops
Just (OpaqueRecord ops) -> conversionRecordDec manifest name ops
Just (OpaqueArray ops) -> conversionArrayDec manifest name ops
Just (OpaqueRecordArray ops) -> conversionRecordArrayDec manifest name ops
_ -> error ("unknown kind of opaque:" <> T.unpack name)
conversionSumDec manifest name ops = instanceFormat "Convertible" [(wname,[])] [native, toFuthark, fromFuthark]
where
name' = reformatTypeName name
wname = "Futhark." <> name'
uname = "Unpacked." <> name'
nname = "Haskell." <> name'
native = typeFormat ("Native" <~> wname) nname
fieldtype t = if isTuple t
then mconcat $ map fieldtype (subtypes t)
else [t]
fieldid prefix t = if isTuple t
then mconcat $ zipWith (\i -> fieldid (prefix <-> T.pack (show i))) [0..] (subtypes t)
else [prefix]
tuplePattern prefix t = if isTuple t
then tuple $ zipWith (\i -> tuplePattern (prefix <-> T.pack (show i))) [0..] (subtypes t)
else prefix
payloadtypes variant = mconcat $ map fieldtype (sumVariantPayload variant)
payloadids variant = mconcat $ zipWith (fieldid) (map (\i -> "p" <> T.pack (show i)) [0..]) (sumVariantPayload variant)
pattern variant =
let items = zipWith tuplePattern (map (\i -> "p" <> T.pack (show i)) [0..]) (sumVariantPayload variant)
in T.unwords items
subtypes t = case M.lookup t (manifestTypes manifest) of
Just (TypeOpaque _ _ (Just (OpaqueRecord ops)) _) -> map recordFieldType (recordFields ops)
_ -> error "nontuple type"
subargs t = zipWith (\i _ -> "t" <> T.pack (show i)) [0..] (subtypes t)
fromFuthark = "fromFuthark packed = unsafeFromFutIO $ do\n" <> indent (""
<> "unpacked <- unpack packed\n"
<> "case unpacked of\n"
<> indent (mconcat (map fromFutharkVariant (sumVariants ops)))
)
toFuthark = "toFuthark native = unsafeFromFutIO $ do\n" <> indent (""
<> "case native of\n"
<> indent (mconcat (map toFutharkVariant (sumVariants ops)))
)
fromFutharkVariant variant = uname <-> sumVariantName variant <~> pattern variant <> " -> do\n" <> indent (""
<> mconcat (zipWith (\t i -> if isPrimitive t
then ""
else i <> " <- fromFuthark " <> i <> " >>= \\" <> i <> "' -> finalizeFO " <> i <> " >> pure " <> i <> "'\n")
(payloadtypes variant) (payloadids variant))
<> "pure (" <> nname <-> sumVariantName variant <~> pattern variant <> ")\n"
)
toFutharkVariant variant = nname <-> sumVariantName variant <~> pattern variant <> " -> do\n" <> indent (""
<> mconcat (zipWith (\t i -> if isPrimitive t
then ""
else i <> " <- toFuthark " <> i <> "\n")
(payloadtypes variant) (payloadids variant))
<> "packed <- pack $ " <> uname <-> sumVariantName variant <~> (pattern variant) <> "\n"
<> mconcat (zipWith (\t i -> if isPrimitive t
then ""
else "finalizeFO " <> i <> "\n")
(payloadtypes variant) (payloadids variant))
<> "pure packed\n"
)
conversionRecordDec manifest name ops = if isTuple name
then ""
else instanceFormat "Convertible" [(wname,[])] [native, toFuthark, fromFuthark]
where
name' = reformatTypeName name
fields = recordFields ops
isTuple' = recordFieldName (fields !! 0) == "0"
wname = "Futhark." <> name'
uname = "Unpacked." <> name'
nname = "Haskell." <> name'
fieldtype t = if isTuple t
then mconcat $ map fieldtype (subtypes t)
else [t]
fieldid prefix t = if isTuple t
then mconcat $ zipWith (\i -> fieldid (prefix <-> T.pack (show i))) [0..] (subtypes t)
else [prefix]
tuplePattern prefix t = if isTuple t
then tuple $ zipWith (\i -> tuplePattern (prefix <-> T.pack (show i))) [0..] (subtypes t)
else prefix
fieldtypes = mconcat $ map (fieldtype . recordFieldType) fields
fieldids = mconcat $ zipWith (fieldid) (map (\i -> "f" <> T.pack (show i)) [0..]) (map recordFieldType fields)
pattern = let items = zipWith tuplePattern (map (\i -> "f" <> T.pack (show i)) [0..]) (map recordFieldType fields)
in if isTuple' then tuple items else T.unwords items
native = typeFormat ("Native" <~> wname) nname
subtypes t = case M.lookup t (manifestTypes manifest) of
Just (TypeOpaque _ _ (Just (OpaqueRecord ops)) _) -> map recordFieldType (recordFields ops)
_ -> error "nontuple type"
subargs t = zipWith (\i _ -> "t" <> T.pack (show i)) [0..] (subtypes t)
fromFuthark = "fromFuthark packed = unsafeFromFutIO $ do\n" <> indent (""
<> uname <~> pattern <> " <- unpack packed\n"
<> mconcat (zipWith (\t a -> if isPrimitive t
then ""
else a <> " <- fromFuthark " <> a <> " >>= \\" <> a <> "' -> finalizeFO " <> a <> " >> pure " <> a <> "'\n")
fieldtypes fieldids)
<> "pure (" <> nname <~> pattern <> ")\n"
)
toFuthark = "toFuthark (" <> nname <~> pattern <> ") = unsafeFromFutIO $ do\n" <> indent (""
<> mconcat (zipWith (\t a -> if isPrimitive t
then ""
else a <> " <- toFuthark " <> a <> "\n")
fieldtypes fieldids)
<> "packed <- pack (" <> uname <~> pattern <> ")\n"
<> mconcat (zipWith (\t a -> if isPrimitive t
then ""
else "finalizeFO " <> a <> "\n")
fieldtypes fieldids)
<> "pure packed\n"
)
conversionRecordArrayDec manifest name ops = instanceFormat "Convertible" [(wname,[])] [native, toFuthark, fromFuthark]
where
name' = reformatTypeName name
fields = recordArrayFields ops
ename = reformatTypeName $ recordArrayElemType ops
fieldN = T.pack (show (length fields))
rank = recordArrayRank ops
(zipFun, unzipFun) = if isTuple (recordArrayElemType ops)
then ("A.zip" <> fieldN, "A.unzip" <> fieldN)
else ("Haskell.zip_" <> ename, "Haskell.unzip_" <> ename)
unzipArr rank = if rank > 1
then "A.unzip" <> fieldN <> " . A.map " <> wrapIfNotOneWord (unzipArr (rank-1))
else unzipFun
zipArr rank = if rank > 1
then "A.map" <> fieldN <~> wrapIfNotOneWord (zipArr (rank-1))
else zipFun
isTuple' = recordFieldName (fields !! 0) == "0"
isArr = take 2 (T.unpack name) == "[]"
deArr = T.dropWhile (\c -> c == '[' || c == ']')
isTupleArr = isTuple . deArr
tupleArr = "T" <> T.pack (show (length fields)) <> "Array"
recordArr = "Haskell.Array_" <> reformatTypeName (recordArrayElemType ops)
wname = "Futhark." <> name'
uname = "Unpacked." <> name'
nname = "Haskell." <> name'
fieldtypes = map recordFieldType fields
fieldids = zipWith (\i _ -> "f" <> T.pack (show i)) [0..] fields
upattern = uname <~> if isTuple' then tuple fieldids else T.unwords fieldids
native = typeFormat ("Native" <~> wname) nname
fromFuthark = "fromFuthark packed = unsafeFromFutIO $ do\n" <> indent (""
<> upattern <> " <- unpack packed\n"
<> mconcat (zipWith (\t a -> if isPrimitive t
then ""
else a <> " <- fromFuthark " <> a <> " >>= \\" <> a <> "' -> finalizeFO " <> a <> " >> pure " <> a <> "'\n")
fieldtypes fieldids)
<> "pure $ " <> (if isArr then "" else nname <> " $ ") <> zipArr rank <~> T.unwords fieldids
)
toFuthark = "toFuthark "<> (if isArr then "arr" else wrap (nname <> " arr")) <>" = unsafeFromFutIO $ do\n" <> indent (""
<> tuple fieldids <> " <- pure . " <> unzipArr rank <> " $ arr\n"
<> mconcat (zipWith (\t a -> if isPrimitive t
then ""
else a <> " <- toFuthark " <> a <> "\n")
fieldtypes fieldids)
<> "packed <- pack (" <> upattern <> ")\n"
<> mconcat (zipWith (\t a -> if isPrimitive t
then ""
else "finalizeFO " <> a <> "\n")
fieldtypes fieldids)
<> "pure packed\n"
)
conversionArrayDec manifest name ops = instanceFormat "Convertible" [(wname,[])] [native, toFuthark, fromFuthark]
where
name' = reformatTypeName name
wname = "Futhark." <> name'
uname = "Unpacked." <> name'
nname = "Haskell." <> name'
native = typeFormat ("Native" <~> wname) nname
fromFuthark = "fromFuthark = undefined\n"
toFuthark = "toFuthark = undefined\n"
rawTypeUtil name _ = error $ "unknown type: " <> T.unpack name
typeDec :: Text -> Type -> Text
typeDec name (TypeArray ctype futelem rank ops) = undefined
typeDec name (TypeOpaque ctype ops mExtraOps doc) = undefined
typeDec name _ = error $ "unknown type: " <> T.unpack name
rawEntryCalls = M.foldr (<>) "" . M.mapWithKey rawEntryCall . manifestEntryPoints
rawTypeOpss = M.foldr (<>) "" . M.mapWithKey rawTypeOps . manifestTypes
rawTypeDecs = M.foldr (<>) "" . M.mapWithKey rawTypeDec . manifestTypes
wrappedTypeDecs = M.foldr (<>) "" . M.mapWithKey wrappedTypeDec . manifestTypes
wrappedTypeOpss manifest = M.foldr (<>) "" . M.mapWithKey (wrappedTypeOps manifest) . manifestTypes $ manifest
unpackedTypeDecs manifest = M.foldr (<>) "" . M.mapWithKey (unpackedTypeDec manifest) . manifestTypes $ manifest
conversionDecs manifest = M.foldr (<>) "" . M.mapWithKey (conversionDec manifest) . manifestTypes $ manifest
wrappedEntryCalls manifest = (\(docs, calls) -> docs <> calls)
$ M.foldr (\(doc1, call1) (doc2, call2) -> (doc1 <> doc2, call1 <> call2)) ("", "")
. M.mapWithKey (wrappedEntryCall manifest) . manifestEntryPoints $ manifest
nativeTypeDecs manifest = (\(decs, instances) -> hcomment "Types mirroring those in the manifest" <> "\n" <> decs <> "\n" <> hcomment "Element instances and functions" <> "\n" <> instances)
$ M.foldr (\(dec1, inst1) (dec2, inst2) -> (dec1 <> dec2, inst1 <> inst2)) ("", "")
. M.mapWithKey (nativeTypeDec manifest) . manifestTypes $ manifest
typetest = do
manifest <- readManifest "test.json"
putText $ rawEntryCalls manifest
putText $ rawTypeOpss manifest
putText $ rawTypeDecs manifest
putText $ wrappedTypeDecs manifest
putText $ wrappedTypeOpss manifest
putText $ nativeTypeDecs manifest
putText $ conversionDecs manifest
pure ()
--generate mapN for array module
arrMapN :: Int -> Text
arrMapN n = typedec <> mapdec
where
n' = T.pack (show n)
elems = take (n+1) $ map (\i -> "e" <> T.pack (show i)) [0..]
vars = take n $ map (\i -> "a" <> T.pack (show i)) [0..]
typedec = "map" <> n'
<> " :: " <> tuple (map ("Element "<>) elems)
<> " => " <> wrap (T.intercalate " -> " elems)
<> mconcat (map (" -> Array "<>) elems)
<> "\n"
mapdec = "map" <> n' <> " f " <> T.unwords vars
<=> "if " <> T.intercalate " && " (zipWith (\a b -> "size " <> a <> " == size " <> b) vars (tail vars))
</~> "then tabulate (size a0) (\\idx -> f " <> T.unwords (map (\v -> "(unsafeRead " <> v <> " idx)") vars) <> ")"
</~> "else error \"sizes of arguments do not match\""
</> "\n"
--generate tuple arrays
tupleArray :: Int -> Text
tupleArray i = idec <> mdec <> arrdec <> elemdec <> showdec <> zipdec <> unzipdec
where
icon' = "T" <> T.pack (show i) <> "Array"
mcon' = "M" <> icon'
icon vars = icon' <~> T.unwords vars
mcon vars = mcon' <~> T.unwords vars
nums = map (T.pack . show) [0..i-1]
vars = map ("a"<>) nums
elems = map ("e"<>) nums
idec = "data" <~> icon vars <> " where\n "
<> icon' <> " :: "
<> tuple (map ("Element " <>) vars) <> " => "
<> mconcat (map (\v -> "Array " <> v <> " -> ") vars)
<> icon vars <> "\n"
mdec = "data" <~> mcon vars <> " where\n "
<> mcon' <> " :: "
<> tuple (map ("Element " <>) vars) <> " => "
<> mconcat (map (\v -> "MArray " <> v <> " -> ") vars)
<> mcon vars <> "\n"
arrdec = instanceFormat ( tuple ( map ("Element" <~>) vars) <> " => Element" )
[(tuple vars, [])]
[ typeFormat ("Array " <> tuple vars ) (icon vars)
, typeFormat ("MArray " <> tuple vars ) (mcon vars)
, "size (" <> icon vars <> ") = size a0\n"
, "msize (" <> mcon vars <> ") = msize a0\n"
, "unsafeRead (" <> icon vars <> ") idx = " <> tuple (map (\v -> "unsafeRead " <> v <> " idx") vars) <> "\n"
, "unsafePeek (" <> mcon vars <> ") idx = do\n"
<> mconcat (zipWith (\v e -> " " <> e <> " <- unsafePeek " <> v <> " idx\n") vars elems)
<> " pure " <> tuple elems <> "\n"
, "unsafePoke (" <> mcon vars <> ") idx " <> tuple elems <> " = do\n"
<> mconcat (zipWith (\v e -> " unsafePoke " <> v <> " idx " <> e <>"\n") vars elems)
--, "copy (" <> mcon vars <> ") = do\n"
-- <> mconcat (map (\v -> " " <> v <> "' <- copy " <> v <> "\n") vars)
-- <> " pure (" <> mcon (map (<>"'") vars) <> ")\n"
, "unsafeThaw (" <> icon vars <> ") = do\n"
<> mconcat (map (\v -> " " <> v <> "' <- unsafeThaw " <> v <> "\n") vars)
<> " pure (" <> mcon (map (<>"'") vars) <> ")\n"
, "unsafeFreeze (" <> mcon vars <> ") = do\n"
<> mconcat (map (\v -> " " <> v <> "' <- unsafeFreeze " <> v <> "\n") vars)
<> " pure (" <> icon (map (<>"'") vars) <> ")\n"
, "unsafeSlice (" <> icon vars <> ") i s = " <> icon (map (\v -> "(unsafeSlice " <> v <> " i s)") vars)
<> "\n"
, "scratch s = do\n"
<> mconcat (map (\v -> " " <> v <> " <- scratch s\n") vars)
<> " pure (" <> mcon vars <> ")\n"
]
elemdec = instanceFormat ("Element") [(wrap (icon vars),[])]
[ typeFormat ("Array " <> wrap (icon vars)) ("BArray " <> wrap (icon vars))
, typeFormat ("MArray " <> wrap (icon vars)) ("MBArray " <> wrap (icon vars))
, "size = sizeB\n"
, "msize = msizeB\n"
, "unsafeRead = unsafeReadB\n"
, "unsafePeek = unsafePeekB\n"
, "unsafePoke = unsafePokeB\n"
, "unsafeThaw = unsafeThawB\n"
, "unsafeFreeze = unsafeFreezeB\n"
, "unsafeSlice = unsafeSliceB\n"
, "scratch = scratchB\n"
]
zipdec = ""
<> "zip" <> T.pack (show i) <> " :: " <> tuple (map ("Element " <> ) vars) <> " => "
<> mconcat (map (\v -> "Array " <> v <> " -> ") vars)
<> "Array " <> tuple vars <> "\n"
<> "zip" <> T.pack (show i) <~> T.unwords vars <=> "if " <> T.intercalate " && " (zipWith (\v v' -> "size " <> v <> " == " <> "size " <> v') vars (tail vars)) <> "\n"
<> " then " <> icon vars <> "\n"
<> " else error \"sizes of arguments do not match\"\n"
unzipdec = ""
<> "unzip" <> T.pack (show i) <> " :: " <> tuple (map ("Element " <> ) vars) <> " => "
<> "Array " <> tuple vars <> " -> " <> tuple (map ("Array " <>) vars) <> "\n"
<> "unzip" <> T.pack (show i) <~> wrap (icon vars) <=> tuple vars <> "\n"
showdec = instanceFormat
(tuple ( map ("Show " <>) vars ++ map ("Element " <>) vars ) <> " => Show")
[(wrap (icon vars),[])]
[ "show = show . toList\n" ]