-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Monomorphisation [do not merge yet] #76
base: master
Are you sure you want to change the base?
Conversation
I'm gonna try to inspect the procedure body first, as that seems like the least amount of work. Adding source location information to procedures is also nice but it would become more code to maintain. If we do write a plugin do insert that information, that plugin could potentially be outdated every now and then, as GHC changes. Ideally, we would do this with just software. |
…odies are different. I also included a file with some example programs
I wrote a prototype implementation where procedure bodies are inspected. The synthesized names are a bit iffy now but that's easy to change. I piggybacked on the name-generating machinery I already had in place. fun1 :: Ref Int32 -> SSM ()
fun1 = box "fun1" ["x"] $ \x -> do
fork [fun x]
where
fun :: Ref Int32 -> SSM ()
fun = box "fun" ["x"] $ \x -> do
x <~ (0 :: Exp Int32)
fun2 :: Ref Int32 -> SSM ()
fun2 = box "fun2" ["x"] $ \x -> do
fork [fun x]
where
fun :: Ref Int32 -> SSM ()
fun = box "fun" ["x"] $ \x -> do
x <~ (1 :: Exp Int32)
testprogram :: SSM ()
testprogram = boxNullary "testprogram" $ do
int32 <- var (5 :: Exp Int32)
fork [fun1 int32, fun2 int32] produces
Which seems correct. |
If there are arguments provided at the host-language level, this is also recognized and procedures are specialized. E.g in the following program the first argument fun3
:: forall a
. (Num a, Ord a, SSMType a, DefSSMExp a, FromLiteral a)
=> a
-> Ref a
-> SSM ()
fun3 n = box "fun3" ["r"] $ \r -> do
if n < 2 then r <~ defaultValue @a else r <~ (defaultValue @a + 1)
testprogram2 :: SSM ()
testprogram2 = boxNullary "testprogram2" $ do
r <- var (5 :: Exp Int32)
fork [fun3 1 r, fun3 2 r, fun3 3 r] becomes
Of course, in this case, it makes more sense to embed the parameter in the embedded language and leverage the conditional execution mechanisms in the target language to produce just one procedure. If you don't care about code size I guess this code would save you a few cycles though! |
The following encodes a supervisor process. The intention is that the supervisor allocates some resources, hands them to the consumers and forks them, and then returns (which deallocates the resource). -- | supervisor to allocate resource and apply 'consumers'
supervisor :: forall a . DefSSMExp a => [(Ref a -> SSM ())] -> SSM ()
supervisor procs = boxNullary "supervisor" $ do
r <- var $ defaultValue @a
fork $ map ($ r) procs
-- 3 different consumer processes, that uses the resource
client1 :: Ref Int32 -> SSM ()
client1 = box "client1" ["r"] $ \r -> do
r <~ int32 5
client2 :: Ref Int32 -> SSM ()
client2 = box "client2" ["r"] $ \r -> do
r <~ int32 10
client3 :: Ref Bool -> SSM ()
client3 = box "client3" ["r"] $ \r -> do
r <~ true'
testprogram3 :: SSM ()
testprogram3 = boxNullary "testprogram3" $ do
fork [supervisor [client1, client2], supervisor [client3]] Generated code:
|
Writing a polymorphic function that alternates the value of a reference between two predetermined values at a set interval can look like this: alternate :: Ref a -> Exp a -> Exp a -> Exp Word64 -> SSM ()
alternate r e1 e2 d = fork [ alternateProcess r e1 e2 d ]
where
alternateProcess :: Ref a -> Exp a -> Exp a -> Exp Word64 -> SSM ()
alternateProcess = box "alternateProcess" ["r","e1","e2","d"] $ \r e1 e2 d -> do
while' true' $ do
r <~ e1
delay d
r <~ e2
delay d
delay :: Exp Word64 -> SSM ()
delay t = fork [delayProcedure t]
where
delayProcedure :: Exp Word64 -> SSM ()
delayProcedure = box "delayProcedure" ["delay"] $ \delay -> do
x <- var event'
after delay x event'
wait [x]
testprogram4 :: SSM ()
testprogram4 = boxNullary "testprogram4" $ do
r <- var (1 :: Exp Int32)
x <- var true'
alternate r 2 3 50
alternate x false' true' 50 And then becomes
So for all of these examples, we do get different, specialized procedures for different types. We can write nice polymorphic functions in the EDSL. I am certain that @koengit can find a counterexample that breaks this though, so I don't think this issue is complete yet lol. |
The initial commit 5341984 can turn the following code
into the pretty printed code
Which is a nice start.
The following program, where we have two different local functions that define a procedure
fun
Produce this pretty printed code
Right now the machinery only relies on the name & types of a procedure to specialize it, so the above example produces wrong code. There are two ways forward - either we add source information to make the compiler understand that these are two different definitions, or we inspect the procedure body to determine if they're the same or not.