-
-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathTemplate.hs
152 lines (130 loc) · 6.32 KB
/
Template.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
{-# LANGUAGE UndecidableInstances, FunctionalDependencies #-}
module Eclair.LLVM.Template
( TemplateT
, Template
, HasSuffix(..)
, MonadTemplate(..)
, Suffix
, cmapParams
, instantiate
, partialInstantiate
, function
, typedef
) where
import Control.Monad.Morph
import LLVM.Codegen hiding (function, typedef)
import qualified LLVM.Codegen as CG
import qualified Control.Monad.State.Lazy as LazyState
import qualified Control.Monad.State.Strict as StrictState
import qualified Control.Monad.RWS.Lazy as LazyRWS
import qualified Control.Monad.RWS.Strict as StrictRWS
import Eclair.LLVM.Config
type Suffix = Text
-- | A MTL-like monad transformer that allows generating code in a way similar to C++ templates.
-- Instead of complicated machinery in the compiler, this transformer just adds a suffix to all generated functions and types.
-- It is up to the programmer to make sure all provided suffixes to one template are unique!
-- The type variable 'p' is short for "template parameters" and can be used to tweak (specialize) the code generation.
-- The type variable 'm' allows running this stack in a pure context, or in a stack on top of IO.
newtype TemplateT p m a
= TemplateT
{ unTemplateT :: ReaderT (Suffix, p) (ModuleBuilderT m) a
} deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadError e, MonadState s, MonadModuleBuilder)
via ReaderT (Suffix, p) (ModuleBuilderT m)
type Template p = TemplateT p Identity
instance MFunctor (TemplateT p) where
hoist nat = TemplateT . hoist (hoist nat) . unTemplateT
instance MonadReader r m => MonadReader r (TemplateT p m) where
ask = lift ask
local f (TemplateT m) =
TemplateT $ hoist (local f) m
class HasSuffix m where
getSuffix :: m Suffix
instance Monad m => HasSuffix (TemplateT p m) where
getSuffix = TemplateT $ asks (("_" <>) . fst)
-- The following instance makes 'function' behave the same as in llvm-codegen
instance Monad m => HasSuffix (ModuleBuilderT m) where
getSuffix = pure mempty
-- This allows getting the suffix inside a function body with llvm-codegen
instance (Monad m, HasSuffix m) => HasSuffix (IRBuilderT m) where
getSuffix = lift getSuffix
-- MTL boilerplate:
instance (Monad m, HasSuffix m) => HasSuffix (ReaderT r m) where
getSuffix = lift getSuffix
instance (Monad m, HasSuffix m, Monoid w) => HasSuffix (WriterT w m) where
getSuffix = lift getSuffix
instance (Monad m, HasSuffix m) => HasSuffix (LazyState.StateT w m) where
getSuffix = lift getSuffix
instance (Monad m, HasSuffix m) => HasSuffix (StrictState.StateT w m) where
getSuffix = lift getSuffix
instance (Monad m, HasSuffix m, Monoid w) => HasSuffix (LazyRWS.RWST r w s m) where
getSuffix = lift getSuffix
instance (Monad m, HasSuffix m, Monoid w) => HasSuffix (StrictRWS.RWST r w s m) where
getSuffix = lift getSuffix
instance (Monad m, HasSuffix m) => HasSuffix (ExceptT e m) where
getSuffix = lift getSuffix
instance (Monad m, HasSuffix m) => HasSuffix (ConfigT m) where
getSuffix = lift getSuffix
class MonadTemplate p m | m -> p where
getParams :: m p
instance Monad m => MonadTemplate p (TemplateT p m) where
getParams = TemplateT $ asks snd
instance (Monad m, MonadTemplate p m) => MonadTemplate p (ReaderT r m) where
getParams = lift getParams
instance (Monad m, MonadTemplate p m, Monoid w) => MonadTemplate p (WriterT w m) where
getParams = lift getParams
instance (Monad m, MonadTemplate p m, Monoid w) => MonadTemplate p (LazyState.StateT w m) where
getParams = lift getParams
instance (Monad m, MonadTemplate p m, Monoid w) => MonadTemplate p (StrictState.StateT w m) where
getParams = lift getParams
instance (Monad m, MonadTemplate p m, Monoid w) => MonadTemplate p (LazyRWS.RWST r w s m) where
getParams = lift getParams
instance (Monad m, MonadTemplate p m, Monoid w) => MonadTemplate p (StrictRWS.RWST r w s m) where
getParams = lift getParams
instance (Monad m, MonadTemplate p m) => MonadTemplate p (ExceptT e m) where
getParams = lift getParams
-- This allows getting the params inside a function body with llvm-codegen
instance (Monad m, MonadTemplate p m) => MonadTemplate p (IRBuilderT m) where
getParams = lift getParams
instance (Monad m, MonadTemplate p m) => MonadTemplate p (ConfigT m) where
getParams = lift getParams
instance MonadTrans (TemplateT p) where
lift m =
TemplateT $ ReaderT $ const $ lift m
-- "contramap" over the template params.
-- Useful if you only need access to part of the template data
-- and/or types of the params don't match.
cmapParams :: (p2 -> p1) -> TemplateT p1 m a -> TemplateT p2 m a
cmapParams f (TemplateT m) =
TemplateT $ flip withReaderT m $ second f
-- This instantiates a template, given a template name suffix and some template parameters.
-- The result is the underlying ModuleBuilerT which generates specialized code based on the parameters.
instantiate :: Suffix -> p -> TemplateT p m a -> ModuleBuilderT m a
instantiate suffix p (TemplateT t) =
runReaderT t (suffix, p)
-- This instantiates a template and wraps it into another template.
-- Useful for templated member functions on a templated object.
partialInstantiate :: Monad m => p1 -> TemplateT p1 m a -> TemplateT p2 m a
partialInstantiate p t = do
suffix <- getSuffix
embedIntoTemplate $ instantiate suffix p t
where
-- This embeds a plain ModuleBuilderT action into a template.
-- This action has no access to the actual template params from this point onwards.
embedIntoTemplate :: ModuleBuilderT m a -> TemplateT p m a
embedIntoTemplate m = TemplateT $ ReaderT $ const m
-- The next functions replace the corresponding functions defined in llvm-codegen.
-- The functions automatically add a suffix if needed, to guarantee unique function names.
-- In the actual codegen, you will still need to call 'getParams' to get access to the params,
-- to do the actual specialization based on them.
function :: (MonadModuleBuilder m, HasSuffix m)
=> Name -> [(Type, ParameterName)] -> Type -> ([Operand] -> IRBuilderT m a) -> m Operand
function (unName -> name) args retTy body = do
suffix <- getSuffix
let nameWithSuffix = Name $ name <> suffix
CG.function nameWithSuffix args retTy body
typedef :: (MonadModuleBuilder m, HasSuffix m)
=> Name -> Flag Packed -> [Type] -> m Type
typedef (unName -> name) packedFlag tys = do
suffix <- getSuffix
let nameWithSuffix = Name $ name <> suffix
CG.typedef nameWithSuffix packedFlag tys