Skip to content

Commit b619adb

Browse files
committed
Initial commit; revisions for H.S. publication
0 parents  commit b619adb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+5832
-0
lines changed

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Version 1.0
2+
===========
3+
4+
* Initial release

LICENSE

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Copyright (c) 2015, Richard Eisenberg
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are met:
6+
7+
1. Redistributions of source code must retain the above copyright notice, this
8+
list of conditions and the following disclaimer.
9+
10+
2. Redistributions in binary form must reproduce the above copyright notice,
11+
this list of conditions and the following disclaimer in the documentation
12+
and/or other materials provided with the distribution.
13+
14+
3. Neither the name of the author nor the names of its contributors may be
15+
used to endorse or promote products derived from this software without
16+
specific prior written permission.
17+
18+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

cabal.project

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
-- This file allows local project settings, overriding global
2+
-- settings. In our case, these settings speed up compilation.
3+
4+
packages: ./*.cabal
5+
6+
profiling: false
7+
documentation: false
8+
optimization: 0

main/Main.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module Main where
2+
3+
import qualified Language.Stitch.Repl as Repl ( main )
4+
5+
main :: IO ()
6+
main = Repl.main

src/Language/Stitch/CSE.hs

Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
{-# LANGUAGE TypeInType, GADTs, MultiParamTypeClasses, LambdaCase,
2+
TypeFamilies, TypeOperators, RankNTypes, AllowAmbiguousTypes,
3+
ScopedTypeVariables, TypeApplications #-}
4+
5+
-----------------------------------------------------------------------------
6+
-- |
7+
-- Module : Language.Stitch.CSE
8+
-- Copyright : (C) 2018 Richard Eisenberg
9+
-- License : BSD-style (see LICENSE)
10+
-- Maintainer : Richard Eisenberg ([email protected])
11+
-- Stability : experimental
12+
--
13+
-- Eliminate common sub-expressions from a Stitch program
14+
--
15+
----------------------------------------------------------------------------
16+
17+
module Language.Stitch.CSE ( cse ) where
18+
19+
{- GENERAL APPROACH
20+
21+
We do CSE in three steps:
22+
23+
1. Recur through an expression. At every point in the AST with more than
24+
one subexpression (e.g., App, Cond, etc.), check to see if the same
25+
subsubexpression appears in more than subexpression. For example,
26+
App (... blah ...) (... blah ...). If so, add a new let-binding for
27+
the common subsubexpression. This is done by insertLets.
28+
29+
2. Recur through an expression. For every Let, remember the let-bound
30+
variable's RHS in a map. If that expression is seen, replace the expression
31+
with the let-bound variable. This is done by replaceExprs.
32+
33+
3. Recur through an expression. For every Let, do two things:
34+
a. If the RHS of the let-bound variable is just a variable, map the LHS to
35+
the RHS, applying the mapping to the body of the let.
36+
b. If the let-bound variable is not used in its body, remove the Let entirely.
37+
This is done in zapLets.
38+
39+
This last step is important because step 1 inserts a new Let for every common
40+
subsubexpression, even if there is a larger subsubexpression to be commoned
41+
up. Zapping the lets removes the cruft that step 1 might insert. Step 1 also
42+
will insert a Let at every interior node where multiple subexpressions have
43+
a subsubexpression in common; if a subsubexpression appears in *three* places,
44+
then we'll get two Lets, one at the top join and one at the lower join. (Indeed,
45+
this duplication of Lets is why we need part (a) of step 3.)
46+
-}
47+
48+
import Language.Stitch.Type
49+
import Language.Stitch.Exp
50+
import Language.Stitch.Shift
51+
import Language.Stitch.Util
52+
53+
import Language.Stitch.Data.Vec
54+
import Language.Stitch.Data.Exists
55+
56+
import Data.Type.Equality
57+
58+
import qualified Language.Stitch.Data.IHashMap.Lazy as M
59+
import qualified Language.Stitch.Data.IHashSet as S
60+
import Language.Stitch.Data.IHashable
61+
62+
import Data.Kind
63+
64+
-- | Perform common-subexpression elimination (CSE)
65+
cse :: KnownLength ctx => Exp ctx ty -> Exp ctx ty
66+
cse = zapLets . replaceExprs . insertLets
67+
68+
---------------------------------------------------------------------
69+
-- The main types for the data structures used in this file
70+
71+
-- | A mapping from expressions in a certain context to a type indexed
72+
-- by the type of expression it is stored with
73+
type ExpMap ctx a = M.IHashMap (Exp ctx) a
74+
75+
-- | A set of expressions in a certain context
76+
type ExpSet ctx = S.IHashSet (Exp ctx)
77+
78+
---------------------------------------------------------------------
79+
-- Step 1. insertLets
80+
81+
-- | Implements Step 1 from the general description of CSE, above
82+
insertLets :: KnownLength ctx => Exp ctx ty -> Exp ctx ty
83+
insertLets = fst . go
84+
where
85+
go :: forall ctx ty. KnownLength ctx => Exp ctx ty -> (Exp ctx ty, ExpSet ctx)
86+
go e@(Var {}) = (e, S.empty)
87+
go (Lam arg_ty e1)
88+
= let (e1', all_exprs) = go e1
89+
e' = Lam arg_ty e1' in
90+
mk_result e' [unshiftSet all_exprs]
91+
92+
go (App e1 e2)
93+
= let (e1', all_exprs1) = go e1
94+
(e2', all_exprs2) = go e2
95+
e' = App e1' e2' in
96+
mk_result e' [all_exprs1, all_exprs2]
97+
98+
go (Let e1 e2)
99+
= let (e1', all_exprs1) = go e1
100+
(e2', all_exprs2) = go e2
101+
e' = Let e1' e2'
102+
103+
all_exprs2' = unshiftSet all_exprs2 in
104+
mk_result e' [all_exprs1, all_exprs2']
105+
106+
go (Arith e1 op e2)
107+
= let (e1', all_exprs1) = go e1
108+
(e2', all_exprs2) = go e2
109+
e' = Arith e1' op e2' in
110+
mk_result e' [all_exprs1, all_exprs2]
111+
112+
go (Cond e1 e2 e3)
113+
= let (e1', all_exprs1) = go e1
114+
(e2', all_exprs2) = go e2
115+
(e3', all_exprs3) = go e3
116+
e' = Cond e1' e2' e3' in
117+
mk_result e' [all_exprs1, all_exprs2, all_exprs3]
118+
119+
go (Fix e1)
120+
= let (e1', all_exprs) = go e1
121+
e' = Fix e1' in
122+
mk_result e' [all_exprs]
123+
124+
go e@(IntE {}) = (e, S.empty)
125+
go e@(BoolE {}) = (e, S.empty)
126+
127+
mk_result :: KnownLength ctx => Exp ctx ty -> [ExpSet ctx] -> (Exp ctx ty, ExpSet ctx)
128+
mk_result e all_exprss
129+
= (mkLets common_exprs e, S.insert e all_exprs)
130+
where
131+
pairs = allPairs all_exprss
132+
inters = map (uncurry S.intersection) pairs
133+
common_exprs = S.toList (S.unions inters)
134+
all_exprs = S.unions all_exprss
135+
136+
-- | A 'ShiftedExp' represents an expression that's been shifted into a deeper
137+
-- context. Note the non-prenex kind, necessary so that Lets can be parameterized
138+
-- by a partial application of this type.
139+
newtype ShiftedExp base_ctx :: forall n. Ctx n -> Ty -> Type where
140+
ShiftedExp :: Exp (prefix +++ base_ctx) ty
141+
-> ShiftedExp base_ctx prefix ty
142+
143+
-- | A snoc-list (the "nil" is at the left) of expressions, where later elements
144+
-- are in a deeper context than earlier ones.
145+
data Lets :: forall n. (forall m. Ctx m -> Ty -> Type) -> Ctx n -> Type where
146+
LNil :: forall (a :: forall m. Ctx m -> Ty -> Type). Lets a VNil
147+
(:<:) :: forall (a :: forall m. Ctx m -> Ty -> Type) ctx ty.
148+
Lets a ctx -> a ctx ty -> Lets a (ty :> ctx)
149+
infixl 5 :<:
150+
151+
-- | Convert a list of expressions (of a variety of types) into a 'Lets' construct,
152+
-- in CPS-style.
153+
expsToLets :: [Ex (Exp ctx)]
154+
-> (forall n (prefix :: Ctx n). Lets (ShiftedExp ctx) prefix -> Length prefix -> r)
155+
-> r
156+
expsToLets [] k = k LNil LZ
157+
expsToLets (Ex exp : rest) k = expsToLets rest $ \ lets len ->
158+
k (lets :<: ShiftedExp (shifts len exp)) (LS len)
159+
160+
-- | Wrap an expression in nested Lets. This version is efficient, shifting expressions
161+
-- by many levels at once.
162+
mkLets :: forall ctx ty. [Ex (Exp ctx)] -> Exp ctx ty -> Exp ctx ty
163+
mkLets exps body = expsToLets exps $ \ lets len -> go lets (shifts len body)
164+
where
165+
go :: forall n (prefix :: Ctx n) ty.
166+
Lets (ShiftedExp ctx) prefix -> Exp (prefix +++ ctx) ty -> Exp ctx ty
167+
go LNil body = body
168+
go (rest :<: ShiftedExp exp) body = go rest (Let exp body)
169+
170+
-- | Wrap an expression in nested Lets. This version is very inefficient, doing
171+
-- lots of single shifts.
172+
_mkLetsSimple :: forall ctx ty. [Ex (Exp ctx)] -> Exp ctx ty -> Exp ctx ty
173+
_mkLetsSimple [] body = body
174+
_mkLetsSimple (Ex exp : rest) body = Let exp (shift (_mkLetsSimple rest body))
175+
176+
---------------------------------------------------------------------
177+
-- Step 2. replaceExprs
178+
179+
-- | Implements Step 2 from the general description of CSE, above
180+
replaceExprs :: KnownLength ctx => Exp ctx ty -> Exp ctx ty
181+
replaceExprs = go M.empty
182+
where
183+
go :: forall n (ctx :: Ctx n) ty.
184+
KnownLength ctx => ExpMap ctx (Elem ctx) -> Exp ctx ty -> Exp ctx ty
185+
go m e
186+
| Just v <- M.lookup e m
187+
= Var v
188+
189+
go m (Let e1 e2)
190+
= let e1' = go m e1
191+
m' = add_mapping (shift e1) $ add_mapping (shift e1') (shiftMap m) in
192+
Let e1' (go m' e2)
193+
194+
go _ e@(Var {}) = e
195+
go m (Lam arg_ty e) = Lam arg_ty (go (shiftMap m) e)
196+
go m (App e1 e2) = App (go m e1) (go m e2)
197+
go m (Arith e1 op e2) = Arith (go m e1) op (go m e2)
198+
go m (Cond e1 e2 e3) = Cond (go m e1) (go m e2) (go m e3)
199+
go m (Fix e) = Fix (go m e)
200+
go _ e@(IntE {}) = e
201+
go _ e@(BoolE {}) = e
202+
203+
add_mapping e m = insertIfAbsent e EZ m
204+
205+
insertIfAbsent :: (TestEquality k, IHashable k)
206+
=> k i -> v i -> M.IHashMap k v -> M.IHashMap k v
207+
insertIfAbsent k v m = M.alter (\case Just v0 -> Just v0
208+
Nothing -> Just v)
209+
k m
210+
---------------------------------------------------------------------
211+
-- Step 3. zapLets
212+
213+
-- | Implements Step 3 from the general description of CSE, above
214+
zapLets :: KnownLength ctx => Exp ctx ty -> Exp ctx ty
215+
zapLets = fst . go M.empty
216+
where
217+
go :: forall n (ctx :: Ctx n) ty.
218+
KnownLength ctx
219+
=> M.IHashMap (Elem ctx) (Elem ctx)
220+
-> Exp ctx ty
221+
-> (Exp ctx ty, S.IHashSet (Elem ctx))
222+
223+
go m (Let e1 e2)
224+
| Var v <- e1
225+
= let (e2', used_vars) = go (M.insert EZ (shift v) (shiftMap m)) e2
226+
e2'' = uncheckedUnshift e2' in
227+
(e2'', unshiftSet used_vars)
228+
229+
| otherwise
230+
= let (e1', used_vars1) = go m e1
231+
(e2', used_vars2) = go (shiftMap m) e2
232+
used_vars2' = unshiftSet used_vars2 in
233+
234+
if S.member EZ used_vars2
235+
then (Let e1' e2', S.unions [used_vars1, used_vars2'])
236+
else (uncheckedUnshift e2', used_vars2')
237+
238+
go m e@(Var v)
239+
| Just v' <- M.lookup v m
240+
= (Var v', S.singleton v')
241+
242+
| otherwise
243+
= (e, S.singleton v)
244+
245+
go m (Lam arg_ty e)
246+
= let (e', used_vars) = go (shiftMap m) e in
247+
(Lam arg_ty e', unshiftSet used_vars)
248+
249+
go m (App e1 e2)
250+
= let (e1', used_vars1) = go m e1
251+
(e2', used_vars2) = go m e2 in
252+
(App e1' e2', used_vars1 `S.union` used_vars2)
253+
254+
go m (Arith e1 op e2)
255+
= let (e1', used_vars1) = go m e1
256+
(e2', used_vars2) = go m e2 in
257+
(Arith e1' op e2', used_vars1 `S.union` used_vars2)
258+
259+
go m (Cond e1 e2 e3)
260+
= let (e1', used_vars1) = go m e1
261+
(e2', used_vars2) = go m e2
262+
(e3', used_vars3) = go m e3 in
263+
(Cond e1' e2' e3', S.unions [used_vars1, used_vars2, used_vars3])
264+
265+
go m (Fix e)
266+
= let (e', used_vars) = go m e in
267+
(Fix e', used_vars)
268+
269+
go _ e@(IntE {}) = (e, S.empty)
270+
go _ e@(BoolE {}) = (e, S.empty)
271+
272+
---------------------------------------------------------
273+
-- Shifting utilities
274+
275+
shiftMap :: forall (a :: forall n. Ctx n -> Ty -> Type)
276+
(b :: forall n. Ctx n -> Ty -> Type)
277+
ctx ty.
278+
( IHashable (a (ty :> ctx)), TestEquality (a (ty :> ctx))
279+
, Shiftable a, Shiftable b )
280+
=> M.IHashMap (a ctx) (b ctx) -> M.IHashMap (a (ty :> ctx)) (b (ty :> ctx))
281+
shiftMap = M.foldrWithKey go M.empty
282+
where go exp el m = M.insert (shift exp) (shift el) m
283+
284+
unshiftSet :: forall (a :: forall n. Ctx n -> Ty -> Type) ty ctx.
285+
(Shiftable a, TestEquality (a ctx), IHashable (a ctx))
286+
=> S.IHashSet (a (ty :> ctx))
287+
-> S.IHashSet (a ctx)
288+
unshiftSet = S.foldr go S.empty
289+
where
290+
go exp set
291+
| Just exp' <- unshift exp
292+
= S.insert exp' set
293+
| otherwise
294+
= set
295+
296+
---------------------------------------------------------
297+
-- Examples for testing
298+
299+
_testExp :: Exp VNil ((TInt :-> TInt) :-> TInt :-> TInt)
300+
_testExp = Lam (SInt ::-> SInt) $
301+
Lam SInt $
302+
App (Lam SInt $
303+
App (Var (ES (ES EZ)))
304+
(Var (ES EZ)))
305+
(App (Var (ES EZ))
306+
(Var EZ))
307+
308+
_biggerExp :: Exp VNil ((TInt :-> TInt) :-> TInt :-> TInt)
309+
_biggerExp = Lam (SInt ::-> SInt) $
310+
Lam SInt $
311+
App (Lam SInt $
312+
App (Lam SInt $
313+
App (Var (ES (ES (ES EZ))))
314+
(Var (ES (ES EZ))))
315+
(App (Var (ES (ES EZ)))
316+
(Var (ES EZ))))
317+
(App (Var (ES EZ))
318+
(Var EZ))

0 commit comments

Comments
 (0)