ParamType.hs 14.3 KB
Newer Older
1
{-# LANGUAGE PatternSynonyms #-}
2
{-# LANGUAGE TupleSections #-}
3 4 5 6 7 8 9 10
{- sv2v
 - Author: Zachary Snow <zach@zachjs.com>
 -
 - Conversion for `parameter type` in module instantiations
 -}

module Convert.ParamType (convert) where

11
import Control.Monad.Writer.Strict
12
import Data.Either (isRight, lefts)
13
import qualified Data.Map.Strict as Map
14
import qualified Data.Set as Set
15 16 17 18

import Convert.Traverse
import Language.SystemVerilog.AST

19 20
type TypeMap = Map.Map Identifier Type
type Modules = Map.Map Identifier TypeMap
21

22
type Instance = Map.Map Identifier (Type, IdentSet)
23
type Instances = Map.Map String (Identifier, Instance)
24

25
type IdentSet = Set.Set Identifier
26
type DeclMap = Map.Map Identifier Decl
27 28
type UsageMap = [(Identifier, Set.Set Identifier)]

29 30
convert :: [AST] -> [AST]
convert files =
31
    files'''
32
    where
33
        modules = execWriter $
34
            mapM (collectDescriptionsM collectDescriptionM) files
35 36
        (files', instancesRaw) =
            runWriter $ mapM (mapM convertDescriptionM) files
37
        instances = Map.elems instancesRaw
38

39 40
        -- add type parameter instantiations
        files'' = map (concatMap explodeDescription) files'
41
        explodeDescription :: Description -> [Description]
42
        explodeDescription part@(Part _ _ _ _ name _ _) =
43 44 45
            (part :) $
            filter (not . alreadyExists) $
            map (rewriteModule part) theseInstances
46 47
            where
                theseInstances = map snd $ filter ((== name) . fst) instances
48
                alreadyExists = flip Map.member modules . moduleName
49
                moduleName :: Description -> Identifier
50
                moduleName = \(Part _ _ _ _ x _ _) -> x
51 52
        explodeDescription other = [other]

53 54 55 56 57 58 59
        -- remove or reduce source modules that are no longer needed
        files''' = map (map reduceTypeDefaults . filter keepDescription) files''
        -- produce a typed and untyped instantiation graph
        (usedUntypedModules, usedTypedModules) =
            both (Map.fromListWith Set.union) $
            execWriter $ mapM (mapM collectUsageM) files''
        collectUsageM :: Description -> Writer (UsageMap, UsageMap) ()
60
        collectUsageM part@(Part _ _ _ _ name _ _) =
61 62 63 64
            tell $ both makeList $ execWriter $
                (collectModuleItemsM collectModuleItemM) part
            where makeList s = zip (Set.toList s) (repeat $ Set.singleton name)
        collectUsageM _ = return ()
65
        collectModuleItemM :: ModuleItem -> Writer (IdentSet, IdentSet) ()
66 67 68 69
        collectModuleItemM (Instance m bindings _ _ _) =
            if all (isRight . snd) bindings
                then tell (Set.singleton m, Set.empty)
                else tell (Set.empty, Set.singleton m)
70
        collectModuleItemM _ = return ()
71 72 73
        both f (x, y) = (f x, f y) -- simple tuple map helper

        -- identify if a module is still in use
74 75
        keepDescription :: Description -> Bool
        keepDescription (Part _ _ _ _ name _ _) =
76 77 78 79
            isNewModule
            || isntTyped
            || isUsedAsUntyped
            || isUsedAsTyped && isInstantiatedViaNonTyped
80
            || allTypesHaveDefaults && notInstantiated && isntTemplateTagged
81 82 83 84 85 86 87 88 89 90
            where
                maybeTypeMap = Map.lookup name modules
                Just typeMap = maybeTypeMap
                isNewModule = maybeTypeMap == Nothing
                isntTyped = Map.null typeMap
                isUsedAsTyped = Map.member name usedTypedModules
                isUsedAsUntyped = Map.member name usedUntypedModules
                isInstantiatedViaNonTyped = untypedUsageSearch $ Set.singleton name
                allTypesHaveDefaults = all (/= UnknownType) (Map.elems typeMap)
                notInstantiated = lookup name instances == Nothing
91
                isntTemplateTagged = not $ isTemplateTagged name
92
        keepDescription _ = True
93

94 95
        -- instantiate the type parameters if this is a used default instance
        reduceTypeDefaults :: Description -> Description
96
        reduceTypeDefaults part@(Part _ _ _ _ name _ _) =
97 98 99 100 101 102
            if shouldntReduce
                then part
                else traverseModuleItems (traverseDecls rewriteDecl) part
            where
                shouldntReduce =
                    Map.notMember name modules || Map.null typeMap ||
103
                    any (== UnknownType) (Map.elems typeMap) ||
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
                    isTemplateTagged name
                typeMap = modules Map.! name
                rewriteDecl :: Decl -> Decl
                rewriteDecl (ParamType Parameter x t) =
                    ParamType Localparam x t
                rewriteDecl other = other
        reduceTypeDefaults other = other

        -- modules can be recursive; this checks if a typed module is not
        -- connected to any modules which are themselves used as typed modules
        untypedUsageSearch :: IdentSet -> Bool
        untypedUsageSearch visited =
            any (flip Map.notMember usedTypedModules) visited
            || Set.size visited /= Set.size visited'
                && untypedUsageSearch visited'
119
            where
120 121 122 123
                visited' =
                    Set.union visited $
                    Set.unions $
                    Set.map expandSet visited
124 125
                expandSet :: Identifier -> IdentSet
                expandSet ident =
126
                    Map.findWithDefault Set.empty ident usedTypedModules
127

128
        -- substitute in a particular instance's parameter types
129
        rewriteModule :: Description -> Instance -> Description
130
        rewriteModule part inst =
131
            Part attrs extern kw ml m' p (additionalParamItems ++ items')
132
            where
133
                Part attrs extern kw ml m p items = part
134
                m' = moduleInstanceName m inst
135
                items' = map rewriteModuleItem items
136 137
                rewriteModuleItem = traverseNestedModuleItems $ traverseNodes
                    rewriteExpr rewriteDecl rewriteType rewriteLHS rewriteStmt
138
                rewriteDecl :: Decl -> Decl
139
                rewriteDecl (ParamType Parameter x t) =
140 141
                    ParamType kind x $ rewriteType $
                    case Map.lookup x inst of
142 143
                        Nothing -> t
                        Just (t', _) -> t'
144 145 146
                    where kind = if Map.null inst
                                    then Parameter
                                    else Localparam
147
                rewriteDecl other =
148
                    traverseDeclNodes rewriteType rewriteExpr other
149
                additionalParamItems = concatMap makeAddedParams $
150
                    Map.toList $ Map.map snd inst
151
                rewriteExpr :: Expr -> Expr
152
                rewriteExpr orig@(Dot (Ident x) y) =
153 154 155 156 157 158
                    if x == m
                        then Dot (Ident m') y
                        else orig
                rewriteExpr other =
                    traverseExprTypes rewriteType $
                    traverseSinglyNestedExprs rewriteExpr other
159
                rewriteLHS :: LHS -> LHS
160
                rewriteLHS orig@(LHSDot (LHSIdent x) y) =
161 162 163 164 165 166
                    if x == m
                        then LHSDot (LHSIdent m') y
                        else orig
                rewriteLHS other =
                    traverseLHSExprs rewriteExpr $
                    traverseSinglyNestedLHSs rewriteLHS other
167 168
                rewriteType :: Type -> Type
                rewriteType =
169 170 171 172 173 174 175
                    traverseTypeExprs rewriteExpr .
                    traverseSinglyNestedTypes rewriteType
                rewriteStmt :: Stmt -> Stmt
                rewriteStmt =
                    traverseStmtLHSs rewriteLHS .
                    traverseStmtExprs rewriteExpr .
                    traverseSinglyNestedStmts rewriteStmt
176

177 178 179 180 181 182 183 184
        makeAddedParams :: (Identifier, IdentSet) -> [ModuleItem]
        makeAddedParams (paramName, identSet) =
            map (MIPackageItem . Decl) $
            map toTypeParam idents ++ map toParam idents
            where
                idents = Set.toList identSet
                toParam :: Identifier -> Decl
                toParam ident =
185
                    Param Parameter typ name (RawNum 0)
186 187 188 189
                    where
                        typ = Alias (addedParamTypeName paramName ident) []
                        name = addedParamName paramName ident
                toTypeParam :: Identifier -> Decl
190
                toTypeParam ident = ParamType Parameter name UnknownType
191 192
                    where name = addedParamTypeName paramName ident

193
-- write down module parameter names and type parameters
194
collectDescriptionM :: Description -> Writer Modules ()
195
collectDescriptionM part@(Part _ _ _ _ name _ _) =
196
    tell $ Map.singleton name typeMap
197
    where
198
        typeMap = Map.fromList $ execWriter $
199
            collectModuleItemsM (collectDeclsM collectDeclM) part
200 201
        collectDeclM :: Decl -> Writer [(Identifier, Type)] ()
        collectDeclM (ParamType Parameter x v) = tell [(x, v)]
202 203 204 205
        collectDeclM _ = return ()
collectDescriptionM _ = return ()

-- generate a "unique" name for a particular module type instance
206
moduleInstanceName :: Identifier -> Instance -> Identifier
207 208 209 210 211 212 213 214 215 216 217 218 219
moduleInstanceName (TemplateTag m) inst =
    moduleInstanceName m inst
moduleInstanceName m inst =
    if Map.null inst
        then TemplateTag m
        else m ++ "_" ++ shortHash (m, inst)

-- used to tag modules created for delayed type parameter instantiation
pattern TemplateTag :: Identifier -> Identifier
pattern TemplateTag x = '~' : x
isTemplateTagged :: Identifier -> Bool
isTemplateTagged TemplateTag{} = True
isTemplateTagged _ = False
220

221 222
-- checks where a type is sufficiently resolved to be substituted
isSimpleType :: Type -> Bool
223
isSimpleType typ =
224
    (not $ typeIsUnresolved typ) &&
225 226 227 228
    case typ of
        IntegerVector{} -> True
        IntegerAtom  {} -> True
        NonInteger   {} -> True
229
        Implicit     {} -> True
230 231
        Struct _ fields _ -> all (isSimpleType . fst) fields
        Union  _ fields _ -> all (isSimpleType . fst) fields
232 233
        _ -> False

234 235 236 237 238
-- returns whether a top-level type contains any dimension queries or
-- hierarchical references
typeIsUnresolved :: Type -> Bool
typeIsUnresolved =
    getAny . execWriter . collectTypeExprsM
239 240
    (collectNestedExprsM collectUnresolvedExprM)
    where
241 242 243 244
        collectUnresolvedExprM :: Expr -> Writer Any ()
        collectUnresolvedExprM DimsFn {} = tell $ Any True
        collectUnresolvedExprM DimFn  {} = tell $ Any True
        collectUnresolvedExprM Dot    {} = tell $ Any True
245
        collectUnresolvedExprM _ = return ()
246

247
prepareTypeExprs :: Identifier -> Identifier -> Type -> (Type, (IdentSet, DeclMap))
248
prepareTypeExprs instanceName paramName =
249
    runWriter . traverseNestedTypesM
250
        (traverseTypeExprsM $ traverseNestedExprsM prepareExpr)
251
    where
252
        prepareExpr :: Expr -> Writer (IdentSet, DeclMap) Expr
253
        prepareExpr e@Call{} = do
254
            tell (Set.empty, Map.singleton x decl)
255 256 257 258 259
            prepareExpr $ Ident x
            where
                decl = Param Localparam (TypeOf e) x e
                x = instanceName ++ "_sv2v_pfunc_" ++ shortHash e
        prepareExpr (Ident x) = do
260
            tell (Set.singleton x, Map.empty)
261 262
            return $ Ident $ paramName ++ '_' : x
        prepareExpr other = return other
263 264 265 266 267 268 269

addedParamName :: Identifier -> Identifier -> Identifier
addedParamName paramName var = paramName ++ '_' : var

addedParamTypeName :: Identifier -> Identifier -> Identifier
addedParamTypeName paramName var = paramName ++ '_' : var ++ "_type"

270 271 272 273 274 275 276 277 278 279 280 281
convertDescriptionM :: Description -> Writer Instances Description
convertDescriptionM (Part attrs extern kw liftetime name ports items) =
    mapM convertModuleItemM items >>=
        return . Part attrs extern kw liftetime name ports
convertDescriptionM other = return other

convertGenItemM :: GenItem -> Writer Instances GenItem
convertGenItemM (GenModuleItem item) =
    convertModuleItemM item >>= return . GenModuleItem
convertGenItemM other =
    traverseSinglyNestedGenItemsM convertGenItemM other

282
-- attempt to rewrite instantiations with type parameters
283
convertModuleItemM :: ModuleItem -> Writer Instances ModuleItem
284
convertModuleItemM orig@(Instance m bindings x r p) =
285
    if hasOnlyExprs then
286
        return orig
287
    else if not hasUnresolvedTypes then do
288
        let m' = moduleInstanceName m resolvedTypes
289
        tell $ Map.singleton m' (m, resolvedTypes)
290 291
        return $ Generate $ map GenModuleItem $
            map (MIPackageItem . Decl) addedDecls ++
292
            [Instance m' (additionalBindings ++ exprBindings) x r p]
293 294 295 296
    else if isTemplateTagged m then
        return orig
    else do
        let m' = TemplateTag m
297
        tell $ Map.singleton m' (m, Map.empty)
298
        return $ Instance m' bindings x r p
299
    where
300 301 302
        hasOnlyExprs = all (isRight . snd) bindings
        hasUnresolvedTypes = any (not . isSimpleType) (lefts $ map snd bindings)

303
        -- determine the types corresponding to each type parameter
304
        bindingsMap = Map.fromList bindings
305
        resolvedTypesWithDecls = Map.mapMaybeWithKey resolveType bindingsMap
306
        resolvedTypes = Map.map (\(a, (b, _)) -> (a, b)) resolvedTypesWithDecls
307 308 309
        addedDecls = Map.elems $ Map.unions $ map (snd . snd) $
            Map.elems resolvedTypesWithDecls
        resolveType :: Identifier -> TypeOrExpr -> Maybe (Type, (IdentSet, DeclMap))
310 311 312
        resolveType _ Right{} = Nothing
        resolveType paramName (Left t) =
            Just $ prepareTypeExprs x paramName t
313

314
        -- leave only the normal expression params behind
315
        exprBindings = filter (isRight . snd) bindings
316 317 318

        -- create additional parameters needed to specify existing type params
        additionalBindings = concatMap makeAddedParams $
319
            Map.toList $ Map.map snd resolvedTypes
320 321 322 323 324 325 326 327 328 329 330 331
        makeAddedParams :: (Identifier, IdentSet) -> [ParamBinding]
        makeAddedParams (paramName, identSet) =
            map toTypeParam idents ++ map toParam idents
            where
                idents = Set.toList identSet
                toParam :: Identifier -> ParamBinding
                toParam ident =
                    (addedParamName paramName ident, Right $ Ident ident)
                toTypeParam :: Identifier -> ParamBinding
                toTypeParam ident =
                    (addedParamTypeName paramName ident, Left $ TypeOf $ Ident ident)

332 333 334 335 336
convertModuleItemM (Generate items) =
    mapM convertGenItemM items >>= return . Generate
convertModuleItemM (MIAttr attr item) =
    convertModuleItemM item >>= return . MIAttr attr
convertModuleItemM other = return other