diff --git a/src/Convert/Scoper.hs b/src/Convert/Scoper.hs index eaa45a6..6cd1ed4 100644 --- a/src/Convert/Scoper.hs +++ b/src/Convert/Scoper.hs @@ -40,6 +40,7 @@ module Convert.Scoper , withinProcedure , withinProcedureM , scopeModuleItemT + , Replacements ) where import Control.Monad.State.Strict diff --git a/src/Convert/TypeOf.hs b/src/Convert/TypeOf.hs index baed014..551cd5c 100644 --- a/src/Convert/TypeOf.hs +++ b/src/Convert/TypeOf.hs @@ -3,6 +3,14 @@ - Author: Zachary Snow - - 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. -} module Convert.TypeOf (convert) where @@ -19,9 +27,15 @@ convert :: [AST] -> [AST] convert = map $ traverseDescriptions $ partScoper traverseDeclM traverseModuleItemM traverseGenItemM traverseStmtM +-- internal representation of a fully implicit type pattern UnknownType :: Type pattern UnknownType = Implicit Unspecified [] +-- single bit 4-state `logic` type +pattern UnitType :: Type +pattern UnitType = IntegerVector TLogic Unspecified [] + +-- insert the given declaration into the scope, and convert an TypeOfs within traverseDeclM :: Decl -> Scoper Type Decl traverseDeclM decl = do item <- traverseModuleItemM (MIPackageItem $ Decl decl) @@ -29,7 +43,7 @@ traverseDeclM decl = do case decl' of Variable Local UnknownType ident [] Nil -> do -- functions with no return type implicitly return a single bit - insertElem ident $ IntegerVector TLogic Unspecified [] + insertElem ident UnitType return decl' Variable d t ident a e -> do let t' = injectRanges t a @@ -46,6 +60,7 @@ traverseDeclM decl = do ParamType{} -> return decl' CommentDecl{} -> return decl' +-- convert TypeOf in a ModuleItem traverseModuleItemM :: ModuleItem -> Scoper Type ModuleItem traverseModuleItemM (Genvar x) = do insertElem x $ IntegerAtom TInteger Unspecified @@ -53,12 +68,15 @@ traverseModuleItemM (Genvar x) = do traverseModuleItemM item = traverseTypesM (traverseNestedTypesM traverseTypeM) item +-- convert TypeOf in a GenItem traverseGenItemM :: GenItem -> Scoper Type GenItem traverseGenItemM = traverseGenItemExprsM traverseExprM +-- convert TypeOf in a Stmt traverseStmtM :: Stmt -> Scoper Type Stmt traverseStmtM = traverseStmtExprsM traverseExprM +-- convert TypeOf in a Expr traverseExprM :: Expr -> Scoper Type Expr traverseExprM = traverseNestedExprsM $ traverseExprTypesM $ traverseNestedTypesM traverseTypeM @@ -67,6 +85,8 @@ traverseTypeM :: Type -> Scoper Type Type traverseTypeM (TypeOf expr) = typeof expr traverseTypeM other = return other +-- attempts to find the given (potentially hierarchical or generate-scoped) +-- expression in the available scope information lookupTypeOf :: Expr -> Scoper Type Type lookupTypeOf expr = do details <- lookupElemM expr @@ -75,15 +95,18 @@ lookupTypeOf expr = do Just (_, replacements, typ) -> return $ if Map.null replacements then typ - else rewriteType typ - where - rewriteType = traverseNestedTypes $ traverseTypeExprs $ - traverseNestedExprs replace - replace :: Expr -> Expr - replace (Ident x) = - Map.findWithDefault (Ident x) x replacements - replace other = other + else rewriteType replacements typ + where + rewriteType :: Replacements -> Type -> Type + rewriteType replacements = traverseNestedTypes $ traverseTypeExprs $ + traverseNestedExprs (replace replacements) + replace :: Replacements -> Expr -> Expr + replace replacements (Ident x) = + Map.findWithDefault (Ident x) x replacements + replace _ 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 -> Scoper Type Type typeof (Number n) = return $ IntegerVector TLogic sg [r] @@ -91,14 +114,7 @@ typeof (Number n) = r = (RawNum $ size - 1, RawNum 0) size = numberBitLength n sg = if numberIsSigned n then Signed else Unspecified -typeof (Call (Ident "$unsigned") (Args [e] [])) = - typeof e -typeof (Call (Ident "$signed") (Args [e] [])) = - typeof e -typeof (Call (Ident "$clog2") (Args [_] [])) = - return $ IntegerAtom TInteger Unspecified -typeof (Call (Ident x) _) = - typeof $ Ident x +typeof (Call (Ident x) args) = typeofCall x args typeof (orig @ (Bit e _)) = do t <- typeof e case t of @@ -120,10 +136,8 @@ typeof (orig @ (Range e mode r)) = do typeof (orig @ (Dot e x)) = do t <- typeof e case t of - Struct _ fields [] -> - return $ fieldsType fields - Union _ fields [] -> - return $ fieldsType fields + Struct _ fields [] -> return $ fieldsType fields + Union _ fields [] -> return $ fieldsType fields _ -> lookupTypeOf orig where fieldsType :: [Field] -> Type @@ -132,23 +146,8 @@ typeof (orig @ (Dot e x)) = do Just typ -> typ Nothing -> TypeOf orig typeof (Cast (Right s) _) = return $ typeOfSize s -typeof (UniOp UniSub e ) = typeof e -typeof (UniOp BitNot e ) = typeof e -typeof (UniOp LogNot _ ) = return $ IntegerVector TLogic Unspecified [] -typeof (BinOp Pow e _) = typeof e -typeof (BinOp ShiftL e _) = typeof e -typeof (BinOp ShiftR e _) = typeof e -typeof (BinOp ShiftAL e _) = typeof e -typeof (BinOp ShiftAR e _) = typeof e -typeof (BinOp Add a b) = largerSizeType a b -typeof (BinOp Sub a b) = largerSizeType a b -typeof (BinOp Mul a b) = largerSizeType a b -typeof (BinOp Div a b) = largerSizeType a b -typeof (BinOp Mod a b) = largerSizeType a b -typeof (BinOp BitAnd a b) = largerSizeType a b -typeof (BinOp BitXor a b) = largerSizeType a b -typeof (BinOp BitXnor a b) = largerSizeType a b -typeof (BinOp BitOr a b) = largerSizeType a b +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 $ concatSize exprs typeof (Repeat reps exprs) = return $ typeOfSize size @@ -158,12 +157,65 @@ typeof (String str) = where r = (RawNum $ len - 1, RawNum 0) len = if null str then 1 else 8 * unescapedLength str - unescapedLength :: String -> Integer - unescapedLength [] = 0 - unescapedLength ('\\' : _ : rest) = 1 + unescapedLength rest - unescapedLength (_ : rest) = 1 + unescapedLength rest 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 -> Scoper Type Type +typeofCall "$unsigned" (Args [e] []) = typeof e +typeofCall "$signed" (Args [e] []) = typeof e +typeofCall "$clog2" (Args [_] []) = + return $ IntegerAtom TInteger Unspecified +typeofCall fnName _ = typeof $ Ident fnName + +-- type of a unary operator expression +typeofUniOp :: UniOp -> Expr -> Scoper Type 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 -> Scoper Type 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 -> Scoper Type Type largerSizeType a (Number (Based 1 _ _ _ _)) = typeof a diff --git a/test/basic/typeof_op.sv b/test/basic/typeof_op.sv new file mode 100644 index 0000000..3e925fc --- /dev/null +++ b/test/basic/typeof_op.sv @@ -0,0 +1,55 @@ +`define TEST(expr) \ + $display(`"expr = %b; $bits(expr) = %0d`", (expr), $bits(expr)); + +module top; + initial begin + + `TEST(4'b1011) + `TEST(5'b01110) + + `TEST(! 3'b101) + `TEST(~ 3'b101) + `TEST(+ 3'b101) + `TEST(- 3'b101) + `TEST(& 3'b101) + `TEST(~& 3'b101) + `TEST(| 3'b101) + `TEST(~| 3'b101) + `TEST(^ 3'b101) + `TEST(~^ 3'b101) + + `TEST(4'b1011 && 5'b01110) + `TEST(4'b1011 || 5'b01110) + `TEST(4'b1011 & 5'b01110) + `TEST(4'b1011 ^ 5'b01110) + `TEST(4'b1011 ~^ 5'b01110) + `TEST(4'b1011 | 5'b01110) + `TEST(4'b1011 << 5'b01110) + `TEST(4'b1011 >> 5'b01110) + `TEST(4'b1011 <<< 5'b01110) + `TEST(4'b1011 >>> 5'b01110) + `TEST(4'b1011 == 5'b01110) + `TEST(4'b1011 != 5'b01110) + `TEST(4'b1011 === 5'b01110) + `TEST(4'b1011 !== 5'b01110) + `TEST(4'b1011 < 5'b01110) + `TEST(4'b1011 <= 5'b01110) + `TEST(4'b1011 > 5'b01110) + `TEST(4'b1011 >= 5'b01110) + + // TODO: iverilog incorrectly handles width of these + // `TEST(4'b1011 * 5'b01110) + // `TEST(4'b1011 / 5'b01110) + // `TEST(4'b1011 % 5'b01110) + // `TEST(4'b1011 + 5'b01110) + // `TEST(4'b1011 - 5'b01110) + // `TEST(4'b1011 ** 5'b01110) + + // TODO: not yet supported by iverilog + // `TEST(4'b1011 -> 5'b01110) + // `TEST(4'b1011 <-> 5'b01110) + // `TEST(4'b1011 ==? 5'b01110) + // `TEST(4'b1011 !=? 5'b01110) + + end +endmodule diff --git a/test/basic/typeof_op.v b/test/basic/typeof_op.v new file mode 100644 index 0000000..b798b77 --- /dev/null +++ b/test/basic/typeof_op.v @@ -0,0 +1 @@ +`include "typeof_op.sv"