sv2v/src/Convert/ParamType.hs

336 lines
14 KiB
Haskell
Raw Normal View History

{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE TupleSections #-}
2019-09-11 09:27:18 +02:00
{- sv2v
- Author: Zachary Snow <zach@zachjs.com>
-
- Conversion for `parameter type` in module instantiations
-}
module Convert.ParamType (convert) where
2020-08-12 01:14:18 +02:00
import Control.Monad.Writer.Strict
import Data.Either (isRight, lefts)
2019-09-11 09:27:18 +02:00
import qualified Data.Map.Strict as Map
2019-09-11 17:58:03 +02:00
import qualified Data.Set as Set
2019-09-11 09:27:18 +02:00
import Convert.Traverse
import Language.SystemVerilog.AST
type TypeMap = Map.Map Identifier Type
type Modules = Map.Map Identifier TypeMap
2019-09-11 09:27:18 +02:00
type Instance = Map.Map Identifier (Type, IdentSet)
type Instances = Set.Set (Identifier, Instance)
2019-09-11 09:27:18 +02:00
2019-09-11 17:58:03 +02:00
type IdentSet = Set.Set Identifier
type UsageMap = [(Identifier, Set.Set Identifier)]
2019-09-11 09:27:18 +02:00
convert :: [AST] -> [AST]
convert files =
2019-09-11 17:58:03 +02:00
files'''
2019-09-11 09:27:18 +02:00
where
modules = execWriter $
2019-09-11 09:27:18 +02:00
mapM (collectDescriptionsM collectDescriptionM) files
(files', instancesRaw) =
runWriter $ mapM (mapM convertDescriptionM) files
instances = Set.toList instancesRaw
2019-09-11 09:27:18 +02:00
2019-09-11 17:58:03 +02:00
-- add type parameter instantiations
files'' = map (concatMap explodeDescription) files'
2019-09-11 09:27:18 +02:00
explodeDescription :: Description -> [Description]
explodeDescription (part @ (Part _ _ _ _ name _ _)) =
(part :) $
filter (not . alreadyExists) $
map (rewriteModule part) theseInstances
2019-09-11 17:58:03 +02:00
where
theseInstances = map snd $ filter ((== name) . fst) instances
alreadyExists = flip Map.member modules . moduleName
2019-09-11 17:58:03 +02:00
moduleName :: Description -> Identifier
moduleName = \(Part _ _ _ _ x _ _) -> x
2019-09-11 09:27:18 +02:00
explodeDescription other = [other]
-- 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) ()
collectUsageM (part @ (Part _ _ _ _ name _ _)) =
tell $ both makeList $ execWriter $
(collectModuleItemsM collectModuleItemM) part
where makeList s = zip (Set.toList s) (repeat $ Set.singleton name)
collectUsageM _ = return ()
2019-09-11 17:58:03 +02:00
collectModuleItemM :: ModuleItem -> Writer (IdentSet, IdentSet) ()
collectModuleItemM (Instance m bindings _ _ _) =
if all (isRight . snd) bindings
then tell (Set.singleton m, Set.empty)
else tell (Set.empty, Set.singleton m)
2019-09-11 17:58:03 +02:00
collectModuleItemM _ = return ()
both f (x, y) = (f x, f y) -- simple tuple map helper
-- identify if a module is still in use
2021-06-04 02:51:26 +02:00
keepDescription :: Description -> Bool
keepDescription (Part _ _ _ _ name _ _) =
isNewModule
|| isntTyped
|| isUsedAsUntyped
|| isUsedAsTyped && isInstantiatedViaNonTyped
|| allTypesHaveDefaults && notInstantiated
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
2021-06-04 02:51:26 +02:00
keepDescription _ = True
2019-09-11 17:58:03 +02:00
-- instantiate the type parameters if this is a used default instance
reduceTypeDefaults :: Description -> Description
reduceTypeDefaults (part @ (Part _ _ _ _ name _ _)) =
if shouldntReduce
then part
else traverseModuleItems (traverseDecls rewriteDecl) part
where
shouldntReduce =
Map.notMember name modules || Map.null typeMap ||
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'
2019-09-11 17:58:03 +02:00
where
visited' =
Set.union visited $
Set.unions $
Set.map expandSet visited
2019-09-11 17:58:03 +02:00
expandSet :: Identifier -> IdentSet
expandSet ident =
Map.findWithDefault Set.empty ident usedTypedModules
2019-09-11 09:27:18 +02:00
2019-09-11 17:58:03 +02:00
-- substitute in a particular instance's parameter types
2019-09-11 09:27:18 +02:00
rewriteModule :: Description -> Instance -> Description
rewriteModule part inst =
Part attrs extern kw ml m' p (additionalParamItems ++ items')
2019-09-11 09:27:18 +02:00
where
Part attrs extern kw ml m p items = part
m' = moduleInstanceName m inst
items' = map rewriteModuleItem items
rewriteModuleItem = traverseNestedModuleItems $ traverseNodes
rewriteExpr rewriteDecl rewriteType rewriteLHS rewriteStmt
2020-07-09 03:34:49 +02:00
rewriteDecl :: Decl -> Decl
2021-06-04 02:51:26 +02:00
rewriteDecl (ParamType Parameter x t) =
ParamType kind x $ rewriteType $
case Map.lookup x inst of
2021-06-04 02:51:26 +02:00
Nothing -> t
Just (t', _) -> t'
where kind = if Map.null inst
then Parameter
else Localparam
rewriteDecl other =
traverseDeclTypes rewriteType $
traverseDeclExprs rewriteExpr other
additionalParamItems = concatMap makeAddedParams $
Map.toList $ Map.map snd inst
rewriteExpr :: Expr -> Expr
rewriteExpr (orig @ (Dot (Ident x) y)) =
if x == m
then Dot (Ident m') y
else orig
rewriteExpr other =
traverseExprTypes rewriteType $
traverseSinglyNestedExprs rewriteExpr other
rewriteLHS :: LHS -> LHS
rewriteLHS (orig @ (LHSDot (LHSIdent x) y)) =
if x == m
then LHSDot (LHSIdent m') y
else orig
rewriteLHS other =
traverseLHSExprs rewriteExpr $
traverseSinglyNestedLHSs rewriteLHS other
rewriteType :: Type -> Type
rewriteType =
traverseTypeExprs rewriteExpr .
traverseSinglyNestedTypes rewriteType
rewriteStmt :: Stmt -> Stmt
rewriteStmt =
traverseStmtLHSs rewriteLHS .
traverseStmtExprs rewriteExpr .
traverseSinglyNestedStmts rewriteStmt
2019-09-11 09:27:18 +02:00
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 =
Param Parameter typ name (RawNum 0)
where
typ = Alias (addedParamTypeName paramName ident) []
name = addedParamName paramName ident
toTypeParam :: Identifier -> Decl
toTypeParam ident = ParamType Parameter name UnknownType
where name = addedParamTypeName paramName ident
2019-09-11 09:27:18 +02:00
-- write down module parameter names and type parameters
collectDescriptionM :: Description -> Writer Modules ()
collectDescriptionM (part @ (Part _ _ _ _ name _ _)) =
tell $ Map.singleton name typeMap
2019-09-11 09:27:18 +02:00
where
typeMap = Map.fromList $ execWriter $
2019-09-11 09:27:18 +02:00
collectModuleItemsM (collectDeclsM collectDeclM) part
collectDeclM :: Decl -> Writer [(Identifier, Type)] ()
collectDeclM (ParamType Parameter x v) = tell [(x, v)]
2019-09-11 09:27:18 +02:00
collectDeclM _ = return ()
collectDescriptionM _ = return ()
-- generate a "unique" name for a particular module type instance
2019-09-11 17:58:03 +02:00
moduleInstanceName :: Identifier -> Instance -> Identifier
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
2019-09-11 09:27:18 +02:00
2019-09-11 17:58:03 +02:00
-- checks where a type is sufficiently resolved to be substituted
isSimpleType :: Type -> Bool
isSimpleType typ =
(not $ typeIsUnresolved typ) &&
case typ of
IntegerVector{} -> True
IntegerAtom {} -> True
NonInteger {} -> True
Net {} -> True
Implicit {} -> True
Struct _ fields _ -> all (isSimpleType . fst) fields
Union _ fields _ -> all (isSimpleType . fst) fields
_ -> False
-- returns whether a top-level type contains any dimension queries or
-- hierarchical references
typeIsUnresolved :: Type -> Bool
typeIsUnresolved =
getAny . execWriter . collectTypeExprsM
(collectNestedExprsM collectUnresolvedExprM)
where
collectUnresolvedExprM :: Expr -> Writer Any ()
collectUnresolvedExprM DimsFn {} = tell $ Any True
collectUnresolvedExprM DimFn {} = tell $ Any True
collectUnresolvedExprM Dot {} = tell $ Any True
collectUnresolvedExprM _ = return ()
2019-09-11 17:58:03 +02:00
prepareTypeExprs :: Identifier -> Identifier -> Type -> (Type, (IdentSet, [Decl]))
prepareTypeExprs instanceName paramName =
2020-07-16 02:44:57 +02:00
runWriter . traverseNestedTypesM
(traverseTypeExprsM $ traverseNestedExprsM prepareExpr)
where
prepareExpr :: Expr -> Writer (IdentSet, [Decl]) Expr
prepareExpr (e @ Call{}) = do
tell (Set.empty, [decl])
prepareExpr $ Ident x
where
decl = Param Localparam (TypeOf e) x e
x = instanceName ++ "_sv2v_pfunc_" ++ shortHash e
prepareExpr (Ident x) = do
tell (Set.singleton x, [])
return $ Ident $ paramName ++ '_' : x
prepareExpr other = return other
addedParamName :: Identifier -> Identifier -> Identifier
addedParamName paramName var = paramName ++ '_' : var
addedParamTypeName :: Identifier -> Identifier -> Identifier
addedParamTypeName paramName var = paramName ++ '_' : var ++ "_type"
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
2019-09-11 17:58:03 +02:00
-- attempt to rewrite instantiations with type parameters
convertModuleItemM :: ModuleItem -> Writer Instances ModuleItem
convertModuleItemM (orig @ (Instance m bindings x r p)) =
if hasOnlyExprs then
2019-09-11 09:27:18 +02:00
return orig
else if not hasUnresolvedTypes then do
tell $ Set.singleton (m, resolvedTypes)
2019-09-11 17:58:03 +02:00
let m' = moduleInstanceName m resolvedTypes
return $ Generate $ map GenModuleItem $
map (MIPackageItem . Decl) addedDecls ++
2021-06-08 05:39:26 +02:00
[Instance m' (additionalBindings ++ exprBindings) x r p]
else if isTemplateTagged m then
return orig
else do
let m' = TemplateTag m
tell $ Set.singleton (m, Map.empty)
return $ Instance m' bindings x r p
2019-09-11 09:27:18 +02:00
where
hasOnlyExprs = all (isRight . snd) bindings
hasUnresolvedTypes = any (not . isSimpleType) (lefts $ map snd bindings)
2019-09-11 09:27:18 +02:00
-- determine the types corresponding to each type parameter
bindingsMap = Map.fromList bindings
2021-06-04 02:51:26 +02:00
resolvedTypesWithDecls = Map.mapMaybeWithKey resolveType bindingsMap
resolvedTypes = Map.map (\(a, (b, _)) -> (a, b)) resolvedTypesWithDecls
addedDecls = concatMap (snd . snd . snd) $
Map.toList resolvedTypesWithDecls
2021-06-04 02:51:26 +02:00
resolveType :: Identifier -> TypeOrExpr -> Maybe (Type, (IdentSet, [Decl]))
resolveType _ Right{} = Nothing
resolveType paramName (Left t) =
Just $ prepareTypeExprs x paramName t
2019-09-11 17:58:03 +02:00
2019-09-11 09:27:18 +02:00
-- leave only the normal expression params behind
2021-06-08 05:39:26 +02:00
exprBindings = filter (isRight . snd) bindings
-- create additional parameters needed to specify existing type params
additionalBindings = concatMap makeAddedParams $
Map.toList $ Map.map snd resolvedTypes
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)
convertModuleItemM (Generate items) =
mapM convertGenItemM items >>= return . Generate
convertModuleItemM (MIAttr attr item) =
convertModuleItemM item >>= return . MIAttr attr
convertModuleItemM other = return other