Struct.hs 22.9 KB
Newer Older
1 2 3
{- sv2v
 - Author: Zachary Snow <zach@zachjs.com>
 -
4
 - Conversion for `struct packed` and `union packed`
5 6 7 8
 -}

module Convert.Struct (convert) where

9 10
import Control.Monad.State
import Control.Monad.Writer
11
import Data.List (elemIndex, sortOn)
12
import Data.Maybe (fromJust, isJust)
13 14
import Data.Tuple (swap)
import qualified Data.Map.Strict as Map
15
import qualified Data.Set as Set
16 17 18 19 20 21 22 23

import Convert.Traverse
import Language.SystemVerilog.AST

type TypeFunc = [Range] -> Type
type StructInfo = (Type, Map.Map Identifier (Range, Expr))
type Structs = Map.Map TypeFunc StructInfo
type Types = Map.Map Identifier Type
24
type Idents = Set.Set Identifier
25

26 27
convert :: [AST] -> [AST]
convert = map $ traverseDescriptions convertDescription
28 29

convertDescription :: Description -> Description
30
convertDescription (description @ Part{}) =
31
    traverseModuleItems (traverseTypes $ convertType structs) $
32
    Part attrs extern kw lifetime name ports (items ++ funcs)
33
    where
34
        description' @ (Part attrs extern kw lifetime name ports items) =
35 36
            scopedConversion (traverseDeclM structs) traverseModuleItemM
                traverseStmtM tfArgTypes description
37
        -- collect information about this description
38
        structs = execWriter $ collectModuleItemsM
39
            (collectTypesM collectStructM) description
40
        tfArgTypes = execWriter $ collectModuleItemsM collectTFArgsM description
41 42
        -- determine which of the packer functions we actually need
        calledFuncs = execWriter $ collectModuleItemsM
43
            (collectExprsM $ collectNestedExprsM collectCallsM) description'
44 45
        packerFuncs = Set.map packerFnName $ Map.keysSet structs
        calledPackedFuncs = Set.intersection calledFuncs packerFuncs
46
        funcs = map packerFn $ filter isNeeded $ Map.keys structs
47
        isNeeded tf = Set.member (packerFnName tf) calledPackedFuncs
48 49 50
        -- helpers for the scoped traversal
        traverseModuleItemM :: ModuleItem -> State Types ModuleItem
        traverseModuleItemM item =
51 52
            traverseLHSsM  traverseLHSM  item >>=
            traverseExprsM traverseExprM      >>=
53 54
            traverseAsgnsM traverseAsgnM
        traverseStmtM :: Stmt -> State Types Stmt
55
        traverseStmtM (Subroutine Nothing f args) = do
56
            stateTypes <- get
57 58
            return $ uncurry (Subroutine Nothing) $
                convertCall structs stateTypes f args
59
        traverseStmtM stmt =
60 61
            traverseStmtLHSsM  traverseLHSM  stmt >>=
            traverseStmtExprsM traverseExprM      >>=
62
            traverseStmtAsgnsM traverseAsgnM
63 64 65 66 67 68
        traverseExprM =
            traverseNestedExprsM $ stately converter
            where
                converter :: Types -> Expr -> Expr
                converter types expr =
                    snd $ convertAsgn structs types (LHSIdent "", expr)
69 70 71 72 73 74
        traverseLHSM =
            traverseNestedLHSsM $ stately converter
            where
                converter :: Types -> LHS -> LHS
                converter types lhs =
                    fst $ convertAsgn structs types (lhs, Ident "")
75
        traverseAsgnM = stately $ convertAsgn structs
76
convertDescription other = other
77

78 79
-- write down unstructured versions of packed struct types
collectStructM :: Type -> Writer Structs ()
80
collectStructM (Struct (Packed sg) fields _) =
81
    collectStructM' (Struct $ Packed sg) True  sg fields
82
collectStructM (Union  (Packed sg) fields _) =
83
    collectStructM' (Union  $ Packed sg) False sg fields
84 85 86
collectStructM _ = return ()

collectStructM'
87
    :: ([Field] -> [Range] -> Type)
88 89
    -> Bool -> Signing -> [Field] -> Writer Structs ()
collectStructM' constructor isStruct sg fields = do
90 91
    if canUnstructure
        then tell $ Map.singleton
92
            (constructor fields)
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
            (unstructType, unstructFields)
        else return ()
    where
        zero = Number "0"
        typeRange :: Type -> Range
        typeRange t =
            if null ranges then (zero, zero) else head ranges
            where ranges = snd $ typeRanges t

        -- extract info about the fields
        fieldTypes = map fst fields
        fieldRanges = map typeRange fieldTypes
        fieldSizes = map rangeSize fieldRanges

        -- layout the fields into the unstructured type; note that `scanr` is
        -- used here because SystemVerilog structs are laid out backwards
109 110 111 112 113 114 115 116
        fieldLos =
            if isStruct
                then map simplify $ tail $ scanr (BinOp Add) (Number  "0") fieldSizes
                else map simplify $ repeat (Number "0")
        fieldHis =
            if isStruct
                then map simplify $ init $ scanr (BinOp Add) (Number "-1") fieldSizes
                else map simplify $ map (BinOp Add (Number "-1")) fieldSizes
117 118 119 120 121 122 123 124

        -- create the mapping structure for the unstructured fields
        unstructOffsets = map simplify $ map snd fieldRanges
        unstructRanges = zip fieldHis fieldLos
        keys = map snd fields
        vals = zip unstructRanges unstructOffsets
        unstructFields = Map.fromList $ zip keys vals

125 126
        -- create the unstructured type; result type takes on the signing of the
        -- struct itself to preserve behavior of operations on the whole struct
127 128 129 130
        structSize =
            if isStruct
                then foldl1 (BinOp Add) fieldSizes
                else head fieldSizes
131
        packedRange = (simplify $ BinOp Sub structSize (Number "1"), zero)
132
        unstructType = IntegerVector TLogic sg [packedRange]
133

134 135 136 137 138 139 140
        -- check if this struct can be packed into an integer vector; integer
        -- atoms and non-integers do not have a definitive size, and so cannot
        -- be packed; net types are not permitted as struct fields
        isIntVec :: Type -> Bool
        isIntVec (IntegerVector _ _ _) = True
        isIntVec _ = False
        canUnstructure = all isIntVec fieldTypes
141 142 143 144 145 146 147


-- convert a struct type to its unstructured equivalent
convertType :: Structs -> Type -> Type
convertType structs t1 =
    case Map.lookup tf1 structs of
        Nothing -> t1
148
        Just (t2, _) -> tf2 (rs1 ++ rs2)
149 150 151
            where (tf2, rs2) = typeRanges t2
    where (tf1, rs1) = typeRanges t1

152 153
-- writes down the names of called functions
collectCallsM :: Expr -> Writer Idents ()
154
collectCallsM (Call Nothing f _) = tell $ Set.singleton f
155
collectCallsM _ = return ()
156

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
collectTFArgsM :: ModuleItem -> Writer Types ()
collectTFArgsM (MIPackageItem item) = do
    _ <- case item of
        Function _ t f decls _ -> do
            tell $ Map.singleton f t
            mapM (collect f) (zip [0..] decls)
        Task     _   f decls _ ->
            mapM (collect f) (zip [0..] decls)
        _ -> return []
    return ()
    where
        collect :: Identifier -> (Int, Decl) -> Writer Types ()
        collect f (idx, (Variable _ t x _ _)) = do
            tell $ Map.singleton (f ++ ":" ++ show idx) t
            tell $ Map.singleton (f ++ ":" ++ x) t
        collect _ _ = return ()
collectTFArgsM _ = return ()

175
-- write down the types of declarations
176 177
traverseDeclM :: Structs -> Decl -> State Types Decl
traverseDeclM structs origDecl = do
178
    case origDecl of
179
        Variable d t x a me -> do
180
            let (tf, rs) = typeRanges t
181 182 183
            if isRangeable t
                then modify $ Map.insert x (tf $ a ++ rs)
                else return ()
184 185 186 187 188
            case me of
                Nothing -> return origDecl
                Just e -> do
                    e' <- convertDeclExpr x e
                    return $ Variable d t x a (Just e')
189
        Param s t x e -> do
190 191
            modify $ Map.insert x t
            e' <- convertDeclExpr x e
192 193 194
            return $ Param s t x e'
        ParamType s x mt ->
            return $ ParamType s x mt
195 196 197 198 199 200
    where
        convertDeclExpr :: Identifier -> Expr -> State Types Expr
        convertDeclExpr x e = do
            types <- get
            let (LHSIdent _, e') = convertAsgn structs types (LHSIdent x, e)
            return e'
201 202 203 204
        isRangeable :: Type -> Bool
        isRangeable (IntegerAtom _ _) = False
        isRangeable (NonInteger  _  ) = False
        isRangeable _ = True
205

206 207 208 209 210 211 212 213 214 215 216 217
-- produces a function which packs the components of a struct literal
packerFn :: TypeFunc -> ModuleItem
packerFn structTf =
    MIPackageItem $
    Function Nothing (structTf []) fnName decls [retStmt]
    where
        Struct (Packed _) fields [] = structTf []
        toInput (t, x) = Variable Input t x [] Nothing
        decls = map toInput fields
        retStmt = Return $ Concat $ map (Ident . snd) fields
        fnName = packerFnName structTf

218 219 220
-- returns a "unique" name for the packer for a given struct type
packerFnName :: TypeFunc -> Identifier
packerFnName structTf =
221
    "sv2v_struct_" ++ shortHash structTf
222

223
-- This is where the magic happens. This is responsible for converting struct
224 225 226
-- accesses, assignments, and literals, given appropriate information about the
-- structs and the current declaration context. The general strategy involves
-- looking at the innermost type of a node to convert outer uses of fields, and
227 228
-- then using the outermost type to figure out the corresponding struct
-- definition for struct literals that are encountered.
229 230 231 232 233 234 235 236 237 238 239
convertAsgn :: Structs -> Types -> (LHS, Expr) -> (LHS, Expr)
convertAsgn structs types (lhs, expr) =
    (lhs', expr')
    where
        (typ, lhs') = convertLHS lhs
        expr' = snd $ convertSubExpr $ convertExpr typ expr

        -- converting LHSs by looking at the innermost types first
        convertLHS :: LHS -> (Type, LHS)
        convertLHS (LHSIdent  x) =
            case Map.lookup x types of
240
                Nothing -> (Implicit Unspecified [], LHSIdent x)
241 242
                Just t -> (t, LHSIdent x)
        convertLHS (LHSBit l e) =
243 244
            case l' of
                LHSRange lInner NonIndexed (_, loI) ->
245
                    (t', LHSBit lInner (simplify $ BinOp Add loI e))
246
                LHSRange lInner IndexedPlus (baseI, _) ->
247 248
                    (t', LHSBit lInner (simplify $ BinOp Add baseI e))
                _ -> (t', LHSBit l' e)
249 250
            where
                (t, l') = convertLHS l
251 252 253
                t' = case typeRanges t of
                    (_, []) -> Implicit Unspecified []
                    (tf, rs) -> tf $ tail rs
254
        convertLHS (LHSRange lOuter NonIndexed rOuter) =
255
            case lOuter' of
256
                LHSRange lInner NonIndexed (_, loI) ->
257
                    (t, LHSRange lInner NonIndexed (simplify hi, simplify lo))
258 259
                    where
                        lo = BinOp Add loI loO
260
                        hi = BinOp Add loI hiO
261 262 263 264 265 266
                LHSRange lInner IndexedPlus (baseI, _) ->
                    (t, LHSRange lInner IndexedPlus (simplify base, simplify len))
                    where
                        base = BinOp Add baseI loO
                        len = rangeSize rOuter
                _ -> (t, LHSRange lOuter' NonIndexed rOuter)
267
            where
268
                (hiO, loO) = rOuter
269 270
                (t, lOuter') = convertLHS lOuter
        convertLHS (LHSRange l m r) =
271
            (t', LHSRange l' m r)
272 273 274 275 276
            where
                (t, l') = convertLHS l
                t' = case typeRanges t of
                    (_, []) -> Implicit Unspecified []
                    (tf, rs) -> tf $ tail rs
277 278
        convertLHS (LHSDot    l x ) =
            case t of
279
                InterfaceT _ _ _ -> (Implicit Unspecified [], LHSDot l' x)
280 281 282 283 284 285 286
                Struct p fields [] -> undot (Struct p fields) fields
                Union  p fields [] -> undot (Union  p fields) fields
                Implicit sg _ -> (Implicit sg [], LHSDot l' x)
                _ -> error $ "convertLHS encountered dot for bad type: " ++ show (t, l, x)
            where
                (t, l') = convertLHS l
                undot structTf fields = case Map.lookup structTf structs of
287
                    Nothing -> (fieldType, LHSDot l' x)
288
                    Just (structT, m) -> (tf [tr], LHSRange l' NonIndexed r)
289 290 291 292 293 294
                        where
                            (tf, _) = typeRanges structT
                            (r @ (hi, lo), base) = m Map.! x
                            hi' = BinOp Add base $ BinOp Sub hi lo
                            lo' = base
                            tr = (simplify hi', simplify lo')
295 296
                    where
                        fieldType = lookupFieldType fields x
297
        convertLHS (LHSConcat lhss) =
298
            (Implicit Unspecified [], LHSConcat $ map (snd . convertLHS) lhss)
299 300
        convertLHS (LHSStream o e lhss) =
            (Implicit Unspecified [], LHSStream o e $ map (snd . convertLHS) lhss)
301

302 303
        defaultKey = Just "default"

304 305
        -- try expression conversion by looking at the *outermost* type first
        convertExpr :: Type -> Expr -> Expr
306 307 308 309 310
        -- TODO: This is really a conversion for using default patterns to
        -- populate arrays. Maybe this should be somewhere else?
        convertExpr (IntegerVector t sg (r:rs)) (Pattern [(Just "default", e)]) =
            Repeat (rangeSize r) [e']
            where e' = convertExpr (IntegerVector t sg rs) e
311 312
        convertExpr (Struct (Packed sg) fields (_:rs)) (Concat exprs) =
            Concat $ map (convertExpr (Struct (Packed sg) fields rs)) exprs
313 314
        convertExpr (Struct (Packed sg) fields (_:rs)) (Bit e _) =
            convertExpr (Struct (Packed sg) fields rs) e
315 316
        convertExpr (Struct (Packed sg) fields rs) (Pattern [(Just "default", e)]) =
            if Map.notMember structTf structs then
317
                Pattern [(defaultKey, e)]
318 319 320
            else if null rs then
                expanded
            else
321
                Repeat (dimensionsSize rs) [expanded]
322 323 324 325
            where
                structTf = Struct (Packed sg) fields
                expanded = convertExpr (structTf []) $ Pattern $
                    take (length fields) (repeat (Nothing, e))
326 327 328 329 330 331 332 333 334 335 336
        convertExpr (Struct (Packed sg) fields []) (Pattern itemsOrig) =
            if length items /= length fields then
                error $ "struct pattern " ++ show items ++
                    " doesn't have the same # of items as " ++ show structTf
            else if itemsFieldNames /= fieldNames then
                error $ "struct pattern " ++ show items ++ " has fields " ++
                    show itemsFieldNames ++ ", but struct type has fields " ++
                    show fieldNames
            else if Map.notMember structTf structs then
                Pattern items
            else
337
                Call Nothing fnName $ Args (map (Just . snd) items) []
338 339 340
            where
                subMap = \(Just ident, subExpr) ->
                    (Just ident, convertExpr (lookupFieldType fields ident) subExpr)
341
                structTf = Struct (Packed sg) fields
342
                itemsNamed =
343 344
                    -- if the pattern does not use identifiers, use the
                    -- identifiers from the struct type definition in order
345 346 347 348 349 350 351 352 353 354 355 356 357
                    if not (all (isJust . fst) itemsOrig) then
                        zip (map (Just. snd) fields) (map snd itemsOrig)
                    -- if the pattern has a default value, use that for any
                    -- missing fields
                    else if any ((== defaultKey) . fst) itemsOrig then
                        let origValueMap = Map.fromList itemsOrig
                            origValues = Map.delete defaultKey origValueMap
                            defaultValue = origValueMap Map.! defaultKey
                            defaultValues = Map.fromList $
                                zip (map Just fieldNames) (repeat defaultValue)
                        in Map.toList $ Map.union origValues defaultValues
                    else
                        itemsOrig
358
                items = sortOn itemPosition $ map subMap itemsNamed
359
                fieldNames = map snd fields
360
                itemsFieldNames = map (fromJust . fst) items
361
                itemPosition = \(Just x, _) -> fromJust $ elemIndex x fieldNames
362
                fnName = packerFnName structTf
363 364 365 366 367 368
        convertExpr _ other = other

        -- try expression conversion by looking at the *innermost* type first
        convertSubExpr :: Expr -> (Type, Expr)
        convertSubExpr (Ident x) =
            case Map.lookup x types of
369
                Nothing -> (Implicit Unspecified [], Ident x)
370
                Just t -> (t, Ident x)
371
        convertSubExpr (Dot e x) =
372
            case subExprType of
373 374
                Struct p fields [] -> undot (Struct p fields) fields
                Union  p fields [] -> undot (Union  p fields) fields
375
                _ -> (Implicit Unspecified [], Dot e' x)
376 377
            where
                (subExprType, e') = convertSubExpr e
378 379 380 381 382 383 384
                undot structTf fields =
                    if Map.notMember structTf structs
                        then (fieldType, Dot e' x)
                        else (fieldType, Range  e' NonIndexed r)
                    where
                        fieldType = lookupFieldType fields x
                        r = lookupUnstructRange structTf x
385
        convertSubExpr (Range eOuter NonIndexed (rOuter @ (hiO, loO))) =
386 387
            -- VCS doesn't allow ranges to be cascaded, so we need to combine
            -- nested Ranges into a single range. My understanding of the
388
            -- semantics are that a range returns a new, zero-indexed sub-range.
389
            case eOuter' of
390
                Range eInner NonIndexed (_, loI) ->
391
                    (t, Range eInner NonIndexed (simplify hi, simplify lo))
392 393
                    where
                        lo = BinOp Add loI loO
394
                        hi = BinOp Add loI hiO
395 396 397 398 399 400
                Range eInner IndexedPlus (baseI, _) ->
                    (t, Range eInner IndexedPlus (simplify base, simplify len))
                    where
                        base = BinOp Add baseI loO
                        len = rangeSize rOuter
                _ -> (t, Range eOuter' NonIndexed rOuter)
401
            where (t, eOuter') = convertSubExpr eOuter
402 403 404 405 406 407 408
        convertSubExpr (Range e m r) =
            (t', Range e' m r)
            where
                (t, e') = convertSubExpr e
                t' = case typeRanges t of
                    (_, []) -> Implicit Unspecified []
                    (tf, rs) -> tf $ tail rs
409
        convertSubExpr (Concat exprs) =
410
            (Implicit Unspecified [], Concat $ map (snd . convertSubExpr) exprs)
411 412 413 414 415
        convertSubExpr (Stream o e exprs) =
            (Implicit Unspecified [], Stream o e' exprs')
            where
                e' = (snd . convertSubExpr) e
                exprs' = map (snd . convertSubExpr) exprs
416
        convertSubExpr (BinOp op e1 e2) =
417
            (Implicit Unspecified [], BinOp op e1' e2')
418 419 420
            where
                (_, e1') = convertSubExpr e1
                (_, e2') = convertSubExpr e2
421
        convertSubExpr (Bit e i) =
422
            case e' of
423
                Range eInner NonIndexed (_, loI) ->
424
                    (t', Bit eInner (simplify $ BinOp Add loI i'))
425 426
                Range eInner IndexedPlus (baseI, _) ->
                    (t', Bit eInner (simplify $ BinOp Add baseI i'))
427
                _ -> (t', Bit e' i')
428 429 430 431 432 433
            where
                (t, e') = convertSubExpr e
                t' = case typeRanges t of
                    (_, []) -> Implicit Unspecified []
                    (tf, rs) -> tf $ tail rs
                (_, i') = convertSubExpr i
434 435
        convertSubExpr (Call Nothing f args) =
            (retType, uncurry (Call Nothing) $ convertCall structs types f args)
436 437 438 439
            where
                retType = case Map.lookup f types of
                    Nothing -> Implicit Unspecified []
                    Just t -> t
440 441 442 443
        convertSubExpr (Call (Just x) f args) =
            (Implicit Unspecified [], Call (Just x) f args)
        convertSubExpr (String s) = (Implicit Unspecified [], String s)
        convertSubExpr (Number n) = (Implicit Unspecified [], Number n)
444
        convertSubExpr (Time   n) = (Implicit Unspecified [], Time   n)
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
        convertSubExpr (PSIdent x y) = (Implicit Unspecified [], PSIdent x y)
        convertSubExpr (Repeat e es) =
            (Implicit Unspecified [], Repeat e' es')
            where
                (_, e') = convertSubExpr e
                es' = map (snd . convertSubExpr) es
        convertSubExpr (UniOp op e) =
            (Implicit Unspecified [], UniOp op e')
            where (_, e') = convertSubExpr e
        convertSubExpr (Mux a b c) =
            (t, Mux a' b' c')
            where
                (_, a') = convertSubExpr a
                (t, b') = convertSubExpr b
                (_, c') = convertSubExpr c
        convertSubExpr (Cast (Left t) sub) =
            (t, Cast (Left t) (snd $ convertSubExpr sub))
        convertSubExpr (Cast (Right e) sub) =
            (Implicit Unspecified [], Cast (Right e) (snd $ convertSubExpr sub))
464 465 466 467 468 469 470 471
        convertSubExpr (DimsFn f tore) =
            (Implicit Unspecified [], DimsFn f tore')
            where tore' = convertTypeOrExpr tore
        convertSubExpr (DimFn f tore e) =
            (Implicit Unspecified [], DimFn f tore' e')
            where
                tore' = convertTypeOrExpr tore
                e' = snd $ convertSubExpr e
472
        convertSubExpr (Pattern items) =
473 474 475
            if all (== Nothing) $ map fst items'
                then (Implicit Unspecified [], Concat $ map snd items')
                else (Implicit Unspecified [], Pattern items')
476 477 478
            where
                items' = map mapItem items
                mapItem (mx, e) = (mx, snd $ convertSubExpr e)
479 480 481 482 483 484
        convertSubExpr (MinTypMax a b c) =
            (t, MinTypMax a' b' c')
            where
                (_, a') = convertSubExpr a
                (t, b') = convertSubExpr b
                (_, c') = convertSubExpr c
485
        convertSubExpr Nil = (Implicit Unspecified [], Nil)
486

487 488 489 490
        convertTypeOrExpr :: TypeOrExpr -> TypeOrExpr
        convertTypeOrExpr (Left t) = Left t
        convertTypeOrExpr (Right e) = Right $ snd $ convertSubExpr e

491 492 493
        -- lookup the range of a field in its unstructured type
        lookupUnstructRange :: TypeFunc -> Identifier -> Range
        lookupUnstructRange structTf fieldName =
494 495 496 497
            case Map.lookup fieldName fieldRangeMap of
                Nothing -> error $ "field '" ++ fieldName ++
                    "' not found in struct: " ++ show structTf
                Just r -> r
498 499 500 501 502 503
            where fieldRangeMap = Map.map fst $ snd $ structs Map.! structTf

        -- lookup the type of a field in the given field list
        lookupFieldType :: [(Type, Identifier)] -> Identifier -> Type
        lookupFieldType fields fieldName = fieldMap Map.! fieldName
            where fieldMap = Map.fromList $ map swap fields
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520

-- attempts to convert based on the assignment-like contexts of TF arguments
convertCall :: Structs -> Types -> Identifier -> Args -> (Identifier, Args)
convertCall structs types f (Args pnArgs kwArgs) =
    (f, args)
    where
        idxs = map show ([0..] :: [Int])
        args = Args
            (map snd $ map convertArg $ zip idxs pnArgs)
            (map convertArg kwArgs)
        convertArg :: (Identifier, Maybe Expr) -> (Identifier, Maybe Expr)
        convertArg (x, Nothing) = (x, Nothing)
        convertArg (x, Just e ) = (x, Just e')
            where
                (_, e') = convertAsgn structs types
                    (LHSIdent $ f ++ ":" ++ x, e)