{-# LANGUAGE PatternSynonyms #-} {- sv2v - Author: Zachary Snow <zach@zachjs.com> - - Conversion for the `type` operator - - This conversion is responsible for explicit type resolution throughout sv2v. - It uses Scoper to resolve hierarchical expressions in a scope-aware manner. - - Some other conversions, such as the dimension query and streaming - concatenation conversions, defer the resolution of type information to this - conversion pass by producing nodes with the `type` operator during - elaboration. - - This conversion also elaborates sign and size casts to their primitive types. - Sign casts take on the size of the underlying expression. Size casts take on - the sign of the underlying expression. This conversion incorporates this - elaboration as the canonical source for type information. It also enables the - removal of unnecessary casts often resulting from struct literals or casts in - the source intended to appease certain lint rules. -} module Convert.TypeOf (convert) where import Data.Tuple (swap) import Convert.ExprUtils (dimensionsSize, endianCondRange, simplify) import Convert.Scoper import Convert.Traverse import Language.SystemVerilog.AST convert :: [AST] -> [AST] convert = map $ traverseDescriptions $ partScoper traverseDeclM traverseModuleItemM traverseGenItemM traverseStmtM -- single bit 4-state `logic` type pattern UnitType :: Type pattern UnitType = IntegerVector TLogic Unspecified [] type ST = Scoper (Type, Bool) -- insert the given declaration into the scope, and convert an TypeOfs within traverseDeclM :: Decl -> ST Decl traverseDeclM decl = do decl' <- traverseDeclExprsM traverseExprM decl >>= traverseDeclTypesM traverseTypeM case decl' of Variable _ (Implicit sg rs) ident a _ -> -- implicit types, which are commonly found in function return -- types, are recast as logics to avoid outputting bare ranges insertType ident t' >> return decl' where t' = injectRanges (IntegerVector TLogic sg rs) a Variable d t ident a e -> do let t' = injectRanges t a insertType ident t' return $ case t' of UnpackedType t'' a' -> Variable d t'' ident a' e _ -> Variable d t' ident [] e Param _ UnknownType ident String{} -> insertType ident UnknownType >> return decl' Param _ UnknownType ident e -> typeof e >>= insertType ident >> return decl' Param _ (Implicit sg rs) ident _ -> insertType ident t' >> return decl' where t' = IntegerVector TLogic sg rs Param _ t ident _ -> insertType ident t >> return decl' ParamType{} -> return decl' CommentDecl{} -> return decl' -- rewrite and store a non-genvar data declaration's type information insertType :: Identifier -> Type -> ST () insertType ident typ = do typ' <- scopeType typ insertElem ident (typ', False) -- rewrite an expression so that any identifiers it contains unambiguously refer -- refer to currently visible declarations so it can be substituted elsewhere scopeExpr :: Expr -> ST Expr scopeExpr expr = do expr' <- traverseSinglyNestedExprsM scopeExpr expr >>= traverseExprTypesM scopeType details <- lookupElemM expr' case details of Just (accesses, _, (_, False)) -> return $ accessesToExpr accesses _ -> return expr' scopeType :: Type -> ST Type scopeType = traverseNestedTypesM $ traverseTypeExprsM scopeExpr -- convert TypeOf in a ModuleItem traverseModuleItemM :: ModuleItem -> ST ModuleItem traverseModuleItemM (Genvar x) = insertElem x (t, True) >> return (Genvar x) where t = IntegerAtom TInteger Unspecified traverseModuleItemM item = traverseNodesM traverseExprM return traverseTypeM traverseLHSM return item where traverseLHSM = traverseLHSExprsM traverseExprM -- convert TypeOf in a GenItem traverseGenItemM :: GenItem -> ST GenItem traverseGenItemM = traverseGenItemExprsM traverseExprM -- convert TypeOf in a Stmt traverseStmtM :: Stmt -> ST Stmt traverseStmtM = traverseStmtExprsM traverseExprM -- convert TypeOf in an Expr traverseExprM :: Expr -> ST Expr traverseExprM (Cast (Left (Implicit sg [])) expr) = -- `signed'(foo)` and `unsigned'(foo)` are syntactic sugar for the `$signed` -- and `$unsigned` system functions present in Verilog-2005 traverseExprM $ Call (Ident fn) $ Args [expr] [] where fn = if sg == Signed then "$signed" else "$unsigned" traverseExprM (Cast (Left t) (Number (UnbasedUnsized ch))) = -- defer until this expression becomes explicit return $ Cast (Left t) (Number (UnbasedUnsized ch)) traverseExprM (Cast (Left (t @ (IntegerAtom TInteger _))) expr) = -- convert to cast to an integer vector type traverseExprM $ Cast (Left t') expr where (tf, []) = typeRanges t t' = tf [(RawNum 1, RawNum 1)] traverseExprM (Cast (Left t1) expr) = do expr' <- traverseExprM expr t1' <- traverseTypeM t1 t2 <- typeof expr' if typeCastUnneeded t1' t2 then traverseExprM $ makeExplicit expr' else return $ Cast (Left t1') expr' traverseExprM (Cast (Right (Ident x)) expr) = do expr' <- traverseExprM expr details <- lookupElemM x if details == Nothing then return $ Cast (Left $ Alias x []) expr' else elaborateSizeCast (Ident x) expr' traverseExprM (Cast (Right size) expr) = do expr' <- traverseExprM expr size' <- traverseExprM size elaborateSizeCast size' expr' traverseExprM other = traverseSinglyNestedExprsM traverseExprM other >>= traverseExprTypesM traverseTypeM -- carry forward the signedness of the expression when cast to the given size elaborateSizeCast :: Expr -> Expr -> ST Expr elaborateSizeCast size value = do t <- typeof value case typeSignedness t of Unspecified -> return $ Cast (Right size) value sg -> traverseExprM $ Cast (Left $ typeOfSize sg size) value -- convert TypeOf in a Type traverseTypeM :: Type -> ST Type traverseTypeM (TypeOf expr) = traverseExprM expr >>= typeof traverseTypeM other = traverseSinglyNestedTypesM traverseTypeM other >>= traverseTypeExprsM traverseExprM -- attempts to find the given (potentially hierarchical or generate-scoped) -- expression in the available scope information lookupTypeOf :: Expr -> ST Type lookupTypeOf expr = do details <- lookupElemM expr case details of Nothing -> return $ TypeOf expr Just (_, replacements, (typ, _)) -> do let typ' = toVarType typ return $ replaceInType replacements typ' where toVarType :: Type -> Type toVarType (Net _ sg rs) = IntegerVector TLogic sg rs toVarType other = other -- determines the type of an expression based on the available scope information -- according the semantics defined in IEEE 1800-2017, especially Section 11.6 typeof :: Expr -> ST Type typeof (Number n) = return $ IntegerVector TLogic sg [r] where r = (RawNum $ size - 1, RawNum 0) size = numberBitLength n sg = if numberIsSigned n then Signed else Unspecified typeof (Call (Ident x) args) = typeofCall x args typeof (orig @ (Bit e _)) = do t <- typeof e let t' = popRange t case t of TypeOf{} -> lookupTypeOf orig Alias{} -> return $ TypeOf orig _ -> return $ typeSignednessOverride t' Unsigned t' typeof (orig @ (Range e NonIndexed r)) = do t <- typeof e let t' = replaceRange r t return $ case t of TypeOf{} -> TypeOf orig Alias{} -> TypeOf orig _ -> typeSignednessOverride t' Unsigned t' typeof (Range expr mode (base, len)) = typeof $ Range expr NonIndexed $ endianCondRange index (base, end) (end, base) where index = if mode == IndexedPlus then (boundR, boundL) else (boundL, boundR) boundL = DimFn FnLeft (Left $ TypeOf expr) (RawNum 1) boundR = DimFn FnRight (Left $ TypeOf expr) (RawNum 1) end = if mode == IndexedPlus then BinOp Sub (BinOp Add base len) (RawNum 1) else BinOp Add (BinOp Sub base len) (RawNum 1) typeof (orig @ (Dot e x)) = do t <- typeof e case t of Struct _ fields [] -> return $ fieldsType fields Union _ fields [] -> return $ fieldsType fields _ -> lookupTypeOf orig where fieldsType :: [Field] -> Type fieldsType fields = case lookup x $ map swap fields of Just typ -> typ Nothing -> TypeOf orig typeof (Cast (Left t) _) = traverseTypeM t typeof (UniOp op expr) = typeofUniOp op expr typeof (BinOp op a b) = typeofBinOp op a b typeof (Mux _ a b) = largerSizeType a b typeof (Concat exprs) = return $ typeOfSize Unsigned $ concatSize exprs typeof (Stream _ _ exprs) = return $ typeOfSize Unsigned $ concatSize exprs typeof (Repeat reps exprs) = return $ typeOfSize Unsigned size where size = BinOp Mul reps (concatSize exprs) typeof (String str) = return $ IntegerVector TBit Unspecified [r] where r = (RawNum $ len - 1, RawNum 0) len = if null str then 1 else 8 * unescapedLength str typeof other = lookupTypeOf other -- length of a string literal in characters unescapedLength :: String -> Integer unescapedLength [] = 0 unescapedLength ('\\' : _ : rest) = 1 + unescapedLength rest unescapedLength (_ : rest) = 1 + unescapedLength rest -- type of a standard (non-member) function call typeofCall :: String -> Args -> ST Type typeofCall "$unsigned" (Args [e] []) = return $ typeOfSize Unsigned $ sizeof e typeofCall "$signed" (Args [e] []) = return $ typeOfSize Signed $ sizeof e typeofCall "$clog2" (Args [_] []) = return $ IntegerAtom TInteger Unspecified typeofCall fnName _ = typeof $ Ident fnName -- replaces the signing of a type if possible typeSignednessOverride :: Type -> Signing -> Type -> Type typeSignednessOverride fallback sg t = case t of IntegerVector base _ rs -> IntegerVector base sg rs IntegerAtom base _ -> IntegerAtom base sg Net base _ rs -> Net base sg rs _ -> fallback -- type of a unary operator expression typeofUniOp :: UniOp -> Expr -> ST Type typeofUniOp UniAdd e = typeof e typeofUniOp UniSub e = typeof e typeofUniOp BitNot e = typeof e typeofUniOp _ _ = -- unary reductions and logical negation return UnitType -- type of a binary operator expression (Section 11.6.1) typeofBinOp :: BinOp -> Expr -> Expr -> ST Type typeofBinOp op a b = case op of LogAnd -> unitType LogOr -> unitType LogImp -> unitType LogEq -> unitType Eq -> unitType Ne -> unitType TEq -> unitType TNe -> unitType WEq -> unitType WNe -> unitType Lt -> unitType Le -> unitType Gt -> unitType Ge -> unitType Pow -> typeof a ShiftL -> typeof a ShiftR -> typeof a ShiftAL -> typeof a ShiftAR -> typeof a Add -> largerSizeType a b Sub -> largerSizeType a b Mul -> largerSizeType a b Div -> largerSizeType a b Mod -> largerSizeType a b BitAnd -> largerSizeType a b BitXor -> largerSizeType a b BitXnor -> largerSizeType a b BitOr -> largerSizeType a b where unitType = return UnitType -- produces a type large enough to hold either expression largerSizeType :: Expr -> Expr -> ST Type largerSizeType a (Number (Based 1 _ _ _ _)) = typeof a largerSizeType a b = do t <- typeof a u <- typeof b let sg = binopSignedness (typeSignedness t) (typeSignedness u) return $ if t == u then t else if sg == Unspecified then TypeOf $ BinOp Add a b else typeOfSize sg $ largerSizeOf a b -- returns the signedness of a traditional arithmetic binop, if possible binopSignedness :: Signing -> Signing -> Signing binopSignedness Unspecified _ = Unspecified binopSignedness _ Unspecified = Unspecified binopSignedness Unsigned _ = Unsigned binopSignedness _ Unsigned = Unsigned binopSignedness Signed Signed = Signed -- returns the signedness of the given type, if possible typeSignedness :: Type -> Signing typeSignedness (Net _ sg _) = signednessFallback Unsigned sg typeSignedness (IntegerVector _ sg _) = signednessFallback Unsigned sg typeSignedness (IntegerAtom t sg ) = signednessFallback fallback sg where fallback = if t == TTime then Unsigned else Signed typeSignedness _ = Unspecified -- helper for producing the former signing when the latter is unspecified signednessFallback :: Signing -> Signing -> Signing signednessFallback fallback Unspecified = fallback signednessFallback _ sg = sg -- returns the total size of concatenated list of expressions concatSize :: [Expr] -> Expr concatSize exprs = foldl (BinOp Add) (RawNum 0) $ map sizeof exprs -- returns the size of an expression, with the short-circuiting sizeof :: Expr -> Expr sizeof (Number n) = RawNum $ numberBitLength n sizeof (Mux _ a b) = largerSizeOf a b sizeof expr = DimsFn FnBits $ Left $ TypeOf expr -- returns the maximum size of the two given expressions largerSizeOf :: Expr -> Expr -> Expr largerSizeOf a b = simplify $ Mux cond (sizeof a) (sizeof b) where cond = BinOp Ge (sizeof a) (sizeof b) -- produces a generic type of the given size typeOfSize :: Signing -> Expr -> Type typeOfSize sg size = IntegerVector TLogic sg [(hi, RawNum 0)] where hi = simplify $ BinOp Sub size (RawNum 1) -- combines a type with unpacked ranges injectRanges :: Type -> [Range] -> Type injectRanges t [] = t injectRanges (UnpackedType t rs) unpacked = UnpackedType t $ unpacked ++ rs injectRanges t unpacked = UnpackedType t unpacked -- removes the most significant range of the given type popRange :: Type -> Type popRange (UnpackedType t [_]) = t popRange (IntegerAtom TInteger sg) = IntegerVector TLogic sg [] popRange t = tf rs where (tf, _ : rs) = typeRanges t -- replaces the most significant range of the given type replaceRange :: Range -> Type -> Type replaceRange r (UnpackedType t (_ : rs)) = UnpackedType t (r : rs) replaceRange r (IntegerAtom TInteger sg) = IntegerVector TLogic sg [r] replaceRange r t = tf (r : rs) where (tf, _ : rs) = typeRanges t -- checks for a cast type which already trivially matches the expression type typeCastUnneeded :: Type -> Type -> Bool typeCastUnneeded t1 t2 = sg1 == sg2 && sz1 == sz2 && sz1 /= Nothing && sz2 /= Nothing where sg1 = typeSignedness t1 sg2 = typeSignedness t2 sz1 = typeSize t1 sz2 = typeSize t2 typeSize :: Type -> Maybe Expr typeSize (Net _ _ rs) = Just $ dimensionsSize rs typeSize (IntegerVector _ _ rs) = Just $ dimensionsSize rs typeSize (t @ IntegerAtom{}) = typeSize $ tf [(RawNum 1, RawNum 1)] where (tf, []) = typeRanges t typeSize _ = Nothing -- explicitly sizes top level numbers used in arithmetic expressions makeExplicit :: Expr -> Expr makeExplicit (Number (Decimal size signed values)) = Number $ Decimal (abs size) signed values makeExplicit (Number (Based size base signed values kinds)) = Number $ Based (abs size) base signed values kinds makeExplicit (BinOp op e1 e2) = BinOp op (makeExplicit e1) (makeExplicit e2) makeExplicit (UniOp op e) = UniOp op $ makeExplicit e makeExplicit other = other