620 likes | 645 Views
Explore the systematic compilation process from high-level source to source transformation in Real-Time Functional Reactive Programming (RT-FRP) to ensure bounded and known runtime cost. Learn about translating, compiling, and future work.
E N D
Compiling Real-Time Functional Reactive Programming (RT-FRP) Dana N. Xu and Siau-Cheng Khoo National University of Singapore
Compilation RT-FRP program Functional Code Partial evaluation Automata Code Tupling Tupled Automaton Code Contribution: Systematic compilation via high-level source to source transformation
Contents • Reactive System • Introduction to RT-FRP • Translating RT-FRP to Functional Code • Compiling Functional Code to Automaton Code • Conclusion and Future Work
Reactive System • Reactive systems have to react to an physical environment which cannot wait. • It requires cost (Time & Space) of running a program to be bounded and known before run-time.
Contents • Reactive System • Introduction to RT-FRP • Translating RT-FRP to Functional Code • Compiling Functional Code to Automata Code • Conclusion and Future Work
RT-FRP • Reactive Part • Captures the essential ingredients of FRP programs • Allows recursion and higher order functions • Resource-bounded (both time and space) • Base Language • Choose a terminating and resource-bounded language
RT-FRP Syntax e ::= x | c | () | (e1, e2) | e| | x.e | e1 e2 v ::= c | () | (v1, v2) | v| | x.e a. Base language syntax s, ev ::= input | time | ext e | delay v s | let snapshot x s1 in s2 | s1 switch on x ev in s2 | let continue {kj xj = uj } in s | u u ::= s until <evj kj> b. Reactive language syntax Figure 2. RT-FRP language syntax
x1 x2 when s Example 1 - The when operator when s= let snapshot x1 s in let snapshot x2 delay False s in ext (if x2x1 then () else )
Contents • Reactive System • Introduction to RT-FRP • Translating RT-FRP to Functional Code • Compiling Functional Code to Automata Code • Conclusion and Future Work
Event and Behavior in RT-FRP type Behavior a = [Time] [a] Event a Behavior (Maybe a) data Maybe a = Nothing | Just a type Event a = [Time] [Maybe a] time t1 t2 t3 t4 t5 t6 t7 … b v1 v2 v3 v4 v5 v6 v7 … … e j1 j4 j5 j7
Stream Based Implementation time :: Behavior Time time = \ts -> ts delay :: a -> Behavior a -> Behavior a delay v s = \ts -> v:(s ts) ==> :: Event a -> (a->b) -> Event b .|. :: Event a -> Event a -> Event a untilB :: Behavior a -> Event (Behavior a) -> Behavior a switcher :: Behavior a -> Event (Behavior a) -> Behavior a
Lifting Lifting values/operations at base level to stream of values/operations at reactive level. Example 1 lift0 = constantB constantB 5 = [5, 5, 5, …] Example 2 w1 = let snapshot x <- time in ext (x+1) w1 = lift1 (\x -> x+1) time
Translating RT-FRP to Functional Code []tr :: RExp -> VEnv -> BExp [ext e]tr = liftk (\v1..vk. e) w1..wk where ([v1..vk], [w1..wk]) =lookup e [delay v s]tr = delay v s’ where s’ = [s]
Compiling RT-FRP to Functional Code [let snapshot x s1 s2] = … [time] = time [s1 switch on x ev s2] = … [s until <evj => kj>j=1..n] = … [let continue {kj xj = uj}j=1..n in s] = …
Equivalent Functional Representation at Abstract Level z = let snapshot a delay True x in let snapshot b delay True z in let snapshot c input in ext (if a then b else ((a and b) or c)) z = lift3 (\a->\b->\c-> if a then b else ((a && b) || c)) (delay True x) (delay True z) input x = let snapshot a delay True x in let snapshot b z in ext (if a then False else b) x = lift2 (\a->\b->if a then False else b) (delay True x) z n = let snapshot a delay True x in let snapshot b delay 0 n in ext (if a then 0 else b+1) n = lift2 (\a->\b->if a then 0 else b+1) (delay True x) (delay 0 n) Translate to Translate to Translate to
Contents • Reactive System & Synchronous Language • Introduction to RT-FRP • Translating RT-FRP to Functional Code • Compiling Functional Code to Automata Code • Conclusion and Future Work
Our Algorithm • Generalize all constants that may result in infinite variations. • Apply a.Define configuration with state variables b.Instantiate signals c.Unfold & Unify d.Fold or goto 2a to the body of the definition of a signal
From Previous Example z = lift3 (\a->\b->\c-> if a then b else ((a && b) || c)) (delay True x) (delay True z) input x = lift2 (\a->\b->if a then False else b) (delay True x) z n = lift2 (\a->\b->if a then 0 else b+1) (delay True x) (delay 0 n)
Generalize Integer Constants n = lift2 (\a->\b->if a then 0 else b+1) (delay True x) (delay 0 n) nconfig-1[c1,c2,c3]: n = lift2 (\a->\b->if a then c1 else b+c2) (delay True x) (delay c3 n)
Instantiate signals Instantiate: x = delay x# x_ n = delay n# n_ [1] n = lift2 (\a->\b->if a then c1 else b+c2) (delay True x) (delay c3 n) n = lift2 (\a->\b->if a then c1 else b+c2) (delay True (delay x# x_)) (delay c3 (delay n# n_))
Unfold Once and Partial Evaluate The unfolding rule used is : lift2 (\a->\b -> e) (delay v1 v) (delay w1 w) => delay e[v1/a,w1/b] (lift2 (\a->\b -> e) v w) n = lift2 (\a->\b->if a then c1 else b+c2) (delay True (delay x# x_)) (delay c3 (delay n# n_)) n = delay c1 (lift2 (\a->\b->if a then c1 else b+c2) (delay x# x_) (delay n# n_)) [2]
Unification n = delay n# n_ [1] n = delay c1 (lift2 (\a->\b->if a then c1 else b+c2) (delay x# x_) (delay n# n_)) [2] n= delay c1 n_ n_ = (lift2 (\a->\b->if a then c1 else b+c2) (delay x# x_) (delay c1 n_))
Adjust Time Backward (Future Current) n_ = (lift2 (\a->\b->if a then c1 else b+c2) (delay x# x_) (delay c1 n_)) n = (lift2 (\a->\b->if a then c1 else b+c2) (delay xb x) (delay c1 n)) x# => xb x_ => x n_ => n
Try Folding n = (lift2 (\a->\b->if a then c1 else b+c2) (delay xb x) (delay c1 n)) nconfig-1[c1,c2,c3]: n = lift2 (\a->\b->if a then c1 else b+c2) (delay True x) (delay c3 n) Folding fails
Define a New Configuration n = (lift2 (\a->\b->if a then c1 else b+c2) (delay xb x) (delay c1 n)) n = delay c1nconfig-2[c1,c2,c1] nconfig-2[b1,b2,b3]: n = (lift2 (\a->\b->if a then b1 else b+b2) (delay xb x) (delay b3 n)) from n_ original n
Instantiate Instantiate: x = delay x# x_ n = delay n# n_ n =(lift2 (\a->\b->if a then c1 else b+c2) (delay xb x) (delay c3 n) n =(lift2 (\a->\b->if a then c1 else b+c2) (delay xb(delay x# x_)) (delay c3 (delay n# n_)))
Instantiate Unknown Boolean case xb == False: n =(lift2 (\a->\b->if a then c1 else b+c2) (delay False (delay x# x_)) (delay c3 (delay n# n_))) n = delay (c3+c2) (lift2 (\a->\b->if a then c1 else b+c2) (delay x# x_) (delay n# n_)) {unification with instantiation n#=c3+c2} n = delay (c3+c2) (lift2 (\a->\b->if a then c1 else b+c2) (delay x# x_) (delay (c3+c2) n_)) n = delay (c3+c2) nconfig-2[c1,c2,c3+c2]
(cont.) case xb == True: n =(lift2 (\a->\b->if a then c1 else b+c2) (delay True (delay x# x_)) (delay c3 (delay n# n_))) n = delay c1 (lift2 (\a->\b->if a then c1 else b+c2) (delay x# x_) (delay n# n_)) {unification with instantiation n#=c1} n = delay c1 (lift2 (\a->\b->if a then c1 else b+c2) (delay x# x_) (delay c1 n_)) n = delay c1 nconfig-2[c1,c2,c1]
Automaton for n nconfig-1[c1,c2,c3]: n=delay c1 nconfig-2[c1,c2,c1] nconfig-2[c1,c2,c3]: case xb = False n=delay (c3+c2) nconfig-2[c1,c2,c3+c2] case xb = True n=delay c1 nconfig-2[c1,c2,c1]
Tupling • To combine multiple automata into a single automaton • Benefits • More efficient • Less control logic • More specialization across automata
Tupling Combine three signals together mconfig-111[c1,c2,c3] (n,x,z)=(nconfig-1[c1,c2,c3],xconfig-1,zconfig-1) = delay (c1,False,True) (nconfig-2[c1,c2,c1],xconfig2,zconfig-2) = delay (c1,False,True) mconfig-222[c1,c2,c1]
Define, Unfold and Fold (twice) mconfig-222[c1,c2,c3] st xb =False, zb =True (n,x,z)=(nconfig-2[c1,c2,c3],xconfig-2,zconfig-2) : = delay (c3+c2,True,True) mconfig-233[c1,c2,c3+c2] mconfig-233[c1,c2,c3] st xb =True, zb =True (n,x,z) = (nconfig-2[c1,c2,c3],xconfig-3,zconfig-3) : = delay (c1,False,True) mconfig-223[c1,c2,c1]
Define, Unfold and Fold mconfig-223[c1,c2,c3] st xb = False, zb = True (n,x,z)= (nconfig-2[c1,c2,c3],xconfig-2,zconfig-3) : = delay (c3+c2,z#,input#) mconfig-233a[c1,c2,c3+c2]
Define, Unfold and Fold mconfig-233a[c1,c2,c3] st x#=z#=input# (n,x,z) = (nconfig-2[c1,c2,c3],xconfig-3,zconfig-3) case x#=z#=False = delay (c3+c2,z#,input#) (nconfig-2[c1,c2,c3+c2],xconfig-3,zconfig-3) =delay (c3+c2,z#,input#) mconfig-233a[c1,c2,c3+c2] case x#=z#=True = delay (c1,False,True) (nconfig-2[c1,c2,c1],xconfig-2,zconfig-3) =delay (c1,False,True) mconfig-223[c1,c2,c1]
Final Tupled Automaton mconfig-111[c1,c2,c3]: (n,x,z)= delay (c1,False,True) mconfig-222[c1,c2,c1] mconfig-222[c1,c2,c3]: (n,x,z)= delay (c3+c2,True,True) mconfig-233[c1,c2,c3+c2] mconfig-233[c1,c2,c3]: (n,x,z)= delay (c1,False,True) mconfig-223[c1,c2,c1] mconfig-223[c1,c2,c3]: (n,x,z)= delay (c3+c2,input#,input#) mconfig-233a[c1,c2,c3+c2] mconfig-233a[c1,c2,c3] st x#=z#=input# (n,x,z) = (nconfig-2[c1,c2,c3],xconfig-3,zconfig-3) case x#=z#=False = delay (c3+c2,input#,input#) mconfig-233a[c1,c2,c3+c2] case x#=z#=True =delay (c1,False,True) mconfig-223[c1,c2,c1]
Conclusion • Introduce an approach to building reactive systems based on RT-FRP • A two-stage compiler for RT-FRP • RT-FRP Functional Code • Functional Code Automaton • Partial Evaluation • Tupling
Future Work • Re-design RT-FRP • event-driven • deterministic • concurrent • compile to hardware
Unfolding Rules .|. :: Event a -> Event a -> Event a (delay v) .|. (delay w1 w) => delay w1 (v .|. w) (delay (Just a) v) .|. (delay w1 w) => delay (Just a) (v .|. w) ==> :: Event a -> (a->b) -> Event b (delay v) ==> f => (delay (v ==> f)) (delay (Just a) v) ==> f => (delay (Just (f a))) bot)
Unfolding Rules lift2 (\a->\b -> e) (delay v1 v) (delay w1 w) => delay e[v1/a,w1/b] (lift (\a b -> e) v w) switcher :: Behavior a->Event (Behavior a)->Behavior a switcher (delay v1 v) (delay w) => delay v1 (switcher v w) switcher (delay v1 v) (delay (Just k) w) => delay v1 (switcher k w) untilB :: Behavior a->Event (Behavior a)->Behavior a untilB (delay v1 v) (delay w) => delay v1 (untilB v w) untilB (delay v1 v) (delay (Just k) w) => delay v1 k
Compiling RT-FRP to Functional Code extr[s] = let v1..vn = freevar s - if (n=0) then [s] elselams v1..vn where lams = \ v1..vn ->[s]
time value t RT-FRP Example 1 s1 = let snapshot x time in ext (sin x) 0
FRP vs. RT-FRP FRP type Behavior a = Time a type Event a = [(Time, a)] RT-FRP Event a Behavior (Maybe a) data Maybe a = Nothing | Just a type Behavior a = [Time] [a] type Event a = [Time] [Maybe a]
Stream Based Implementation ==> :: Event a -> (a->b) -> Event b e1 ==> f = \ts loop ts (e1 ts) where loop (_:ts) (Nothing:ys) = Nothing:(loop ts ys) loop (_:ts) (Just a:ys) = (Just (f a)):(loop ts ys) fe ==> f = map (map f).fe
<Initialize Memory> foreachinput_event do <Compute Outputs> <Update Memory> end <Initialize Memory> foreachperiod do <Read Inputs> <Compute Outputs> <Update Memory> end a. “Event driven” b. “Sampling” Figure 1. Execution schemes for reactive systems Two Schemes for Reactive System Implementation
Stream Based Implementation time :: Behavior Time time = \ts -> ts delay :: a -> Behavior a -> Behavior a delay v s = \ts -> v:(s ts) ==> :: Event a -> (a->b) -> Event b e ==> f = map (map f) . e
Stream Based Implementation ==> :: Event a -> (a->b) -> Event b e1 ==> f = \ts loop ts (e1 ts) where loop (_:ts) (Nothing:es) = Nothing:(loop ts es) loop (_:ts) (Just a:es) = (Just (f a)):(loop ts es)