From ed09fe88cffa38f36372ff29b32e15cd463a7bfe Mon Sep 17 00:00:00 2001 From: Zachary Snow Date: Sat, 12 Mar 2022 23:57:17 +0100 Subject: [PATCH] support assignments within expressions --- CHANGELOG.md | 1 + src/Convert.hs | 2 + src/Convert/ExprAsgn.hs | 113 ++++++++++++++++++++++ src/Convert/Traverse.hs | 4 + src/Language/SystemVerilog/AST/Expr.hs | 2 + src/Language/SystemVerilog/Parser/Parse.y | 25 +++++ sv2v.cabal | 1 + test/core/asgn_expr.sv | 38 ++++++++ test/core/asgn_expr.v | 59 +++++++++++ test/error/asgn_expr_non_lhs.sv | 6 ++ 10 files changed, 251 insertions(+) create mode 100644 src/Convert/ExprAsgn.hs create mode 100644 test/core/asgn_expr.sv create mode 100644 test/core/asgn_expr.v create mode 100644 test/error/asgn_expr_non_lhs.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index b112a09..fe212a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### New Features +* Added support for assignments within expressions (e.g., `x = ++y;`) * Added support for excluding the conversion of unbased unsized literals (e.g., `'1`, `'x`) via `--exclude UnbasedUniszed` * Added support for enumerated type ranges (e.g., `enum { X[3:5] }`) diff --git a/src/Convert.hs b/src/Convert.hs index 12d2b1a..7f700c2 100644 --- a/src/Convert.hs +++ b/src/Convert.hs @@ -21,6 +21,7 @@ import qualified Convert.DoWhile import qualified Convert.DuplicateGenvar import qualified Convert.EmptyArgs import qualified Convert.Enum +import qualified Convert.ExprAsgn import qualified Convert.ForAsgn import qualified Convert.Foreach import qualified Convert.FuncRet @@ -101,6 +102,7 @@ initialPhases :: Selector -> [Phase] initialPhases selectExclude = [ Convert.ForAsgn.convert , Convert.Jump.convert + , Convert.ExprAsgn.convert , Convert.KWArgs.convert , Convert.Unique.convert , Convert.SenseEdge.convert diff --git a/src/Convert/ExprAsgn.hs b/src/Convert/ExprAsgn.hs new file mode 100644 index 0000000..1594de4 --- /dev/null +++ b/src/Convert/ExprAsgn.hs @@ -0,0 +1,113 @@ +{-# LANGUAGE PatternSynonyms #-} +{- sv2v + - Author: Zachary Snow + - + - Conversion for assignments within expressions + - + - IEEE 1800-2017 Section 11.3.6 states that assignments within expressions can + - only appear in procedural statements, though some tools support them in other + - contexts. We do not currently raise an error for assignments within + - expressions in unsupported contexts. + - + - Assignment expressions are replaced with the LHS, with the LHS being updated + - in a preceding statement. For post-increment operations, a pre-increment is + - performed and then the increment is reversed in the expression. + - + - This conversion occurs after the elaboration of `do` `while` loops and + - `foreach` loops, but before the elaboration of jumps and extra `for` loop + - initializations. + -} + +module Convert.ExprAsgn (convert) where + +import Control.Monad.Writer.Strict +import Data.Bitraversable (bimapM) + +import Convert.Traverse +import Language.SystemVerilog.AST + +convert :: [AST] -> [AST] +convert = map $ traverseDescriptions $ traverseModuleItems $ + traverseStmts convertStmt + +convertStmt :: Stmt -> Stmt + +-- assignment expressions in a loop guard +convertStmt (While cond stmt) = + if null add + then While cond stmt' + else block $ add ++ [While cond' $ injectLoopIter add stmt'] + where + (cond', add) = runWriter $ convertExpr cond + stmt' = convertStmt stmt + +-- assignment expressions can appear in any part of the for loop +convertStmt (For inits cond incrs stmt) = + if null initsAdd && null condAdd && null incrsAdd + then For inits cond incrs stmt' + else For initsCombined cond' incrs' $ + injectLoopIter (incrsAdd ++ condAdd) stmt' + where + (inits', initsAdd) = runWriter $ mapM convertInit inits + (cond', condAdd) = runWriter $ convertExpr cond + (incrs', incrsAdd) = runWriter $ mapM convertIncr incrs + stmt' = convertStmt stmt + initsCombined = map toInit initsAdd ++ inits' ++ map toInit condAdd + +-- assignment expressions in other statements are added in front +convertStmt stmt = + traverseSinglyNestedStmts convertStmt $ + if null add + then stmt + else block $ add ++ [stmt'] + where (stmt', add) = runWriter $ traverseStmtExprsM convertExpr stmt + +-- helper for creating simple blocks +block :: [Stmt] -> Stmt +block = Block Seq "" [] + +-- add statements before the loop guard is checked +injectLoopIter :: [Stmt] -> Stmt -> Stmt +injectLoopIter add stmt = block $ beforeContinue add stmt : add + +-- add statements before every `continue` in the loop +beforeContinue :: [Stmt] -> Stmt -> Stmt +beforeContinue _ stmt@While{} = stmt +beforeContinue _ stmt@For{} = stmt +beforeContinue add Continue = + block $ add ++ [Continue] +beforeContinue add stmt = + traverseSinglyNestedStmts (beforeContinue add) stmt + +-- reversible pattern to unwrap in for loops +pattern AsgnStmt :: LHS -> Expr -> Stmt +pattern AsgnStmt lhs expr = Asgn AsgnOpEq Nothing lhs expr + +toInit :: Stmt -> (LHS, Expr) +toInit stmt = (lhs, expr) + where AsgnStmt lhs expr = stmt + +-- functions which convert and collect assignment expressions +type Converter t = t -> Writer [Stmt] t + +convertExpr :: Converter Expr +convertExpr (ExprAsgn l r) = do + l' <- convertExpr l + r' <- convertExpr r + let Just lhs = exprToLHS l' -- checked by parser + tell [AsgnStmt lhs r'] + return l' +convertExpr expr = + traverseSinglyNestedExprsM convertExpr expr + +convertLHS :: Converter LHS +convertLHS = traverseNestedLHSsM $ traverseLHSExprsM convertExpr + +convertInit :: Converter (LHS, Expr) +convertInit = bimapM convertLHS convertExpr + +convertIncr :: Converter (LHS, AsgnOp, Expr) +convertIncr (lhs, op, expr) = do + lhs' <- convertLHS lhs + expr' <- convertExpr expr + return (lhs', op, expr') diff --git a/src/Convert/Traverse.hs b/src/Convert/Traverse.hs index 7957e3e..c480b77 100644 --- a/src/Convert/Traverse.hs +++ b/src/Convert/Traverse.hs @@ -497,6 +497,10 @@ traverseSinglyNestedExprsM exprMapper = em e2' <- exprMapper e2 e3' <- exprMapper e3 return $ MinTypMax e1' e2' e3' + em (ExprAsgn e1 e2) = do + e1' <- exprMapper e1 + e2' <- exprMapper e2 + return $ ExprAsgn e1' e2' em (Nil) = return Nil traverseSinglyNestedExprs :: Mapper Expr -> Mapper Expr diff --git a/src/Language/SystemVerilog/AST/Expr.hs b/src/Language/SystemVerilog/AST/Expr.hs index 15babb3..ec36adc 100644 --- a/src/Language/SystemVerilog/AST/Expr.hs +++ b/src/Language/SystemVerilog/AST/Expr.hs @@ -61,6 +61,7 @@ data Expr | Pattern [(TypeOrExpr, Expr)] | Inside Expr [Expr] | MinTypMax Expr Expr Expr + | ExprAsgn Expr Expr | Nil deriving Eq @@ -92,6 +93,7 @@ instance Show Expr where showPatternItem (Left t, v) = printf "%s: %s" tStr (show v) where tStr = if null (show t) then "default" else show t show (MinTypMax a b c) = printf "(%s : %s : %s)" (show a) (show b) (show c) + show (ExprAsgn l r) = printf "(%s = %s)" (show l) (show r) show e@UniOp{} = showsPrec 0 e "" show e@BinOp{} = showsPrec 0 e "" show e@Dot {} = showsPrec 0 e "" diff --git a/src/Language/SystemVerilog/Parser/Parse.y b/src/Language/SystemVerilog/Parser/Parse.y index fa3bb9d..15222c3 100644 --- a/src/Language/SystemVerilog/Parser/Parse.y +++ b/src/Language/SystemVerilog/Parser/Parse.y @@ -1385,6 +1385,11 @@ Expr :: { Expr } | "^" Expr %prec REDUCE_OP { UniOp RedXor $2 } | "~^" Expr %prec REDUCE_OP { UniOp RedXnor $2 } | "^~" Expr %prec REDUCE_OP { UniOp RedXnor $2 } + -- assignments within expressions + | "(" Expr "=" Expr ")" {% makeExprAsgn (tokenPosition $3, AsgnOpEq) $2 $4 } + | "(" Expr AsgnBinOpP Expr ")" {% makeExprAsgn $3 $2 $4 } + | Expr IncOrDecOperatorP {% makeIncrExprAsgn True $2 $1 } + | IncOrDecOperatorP Expr %prec "++" {% makeIncrExprAsgn False $1 $2 } ExprOrNil :: { Expr } : Expr { $1 } @@ -1786,4 +1791,24 @@ makeDPIImport :: String -> DPIImportProperty -> Identifier -> (Type, Identifier, [Decl]) -> PackageItem makeDPIImport a b c (d, e, f) = DPIImport a b c d e f +makeExprAsgn :: (Position, AsgnOp) -> Expr -> Expr -> ParseState Expr +makeExprAsgn (pos, AsgnOpEq) l r = do + case exprToLHS l of + Just{} -> return () + Nothing -> parseError pos $ "cannot convert expression to LHS: " ++ show l + return $ ExprAsgn l r +makeExprAsgn (pos, op) l r = + makeExprAsgn (pos, AsgnOpEq) l r' + where + AsgnOp binop = op + r' = BinOp binop l r + +makeIncrExprAsgn :: Bool -> (Position, BinOp) -> Expr -> ParseState Expr +makeIncrExprAsgn False (pos, op) expr = + makeExprAsgn (pos, AsgnOp op) expr (RawNum 1) +makeIncrExprAsgn True (pos, op) expr = do + expr' <- makeIncrExprAsgn False (pos, op) expr + return $ BinOp op expr' minusOne + where minusOne = Number $ Decimal 1 True 1 + } diff --git a/sv2v.cabal b/sv2v.cabal index 172286b..d644cd9 100644 --- a/sv2v.cabal +++ b/sv2v.cabal @@ -72,6 +72,7 @@ executable sv2v Convert.DuplicateGenvar Convert.EmptyArgs Convert.Enum + Convert.ExprAsgn Convert.ExprUtils Convert.ForAsgn Convert.Foreach diff --git a/test/core/asgn_expr.sv b/test/core/asgn_expr.sv new file mode 100644 index 0000000..99253a6 --- /dev/null +++ b/test/core/asgn_expr.sv @@ -0,0 +1,38 @@ +module top; + integer i; + byte b; + shortint s; + initial begin + $monitor("%2d %b %b %b", $time, i, b, s); + + #1 i = (b = (s = 0)); + #1 i = 1; + #1 i = {1'bx, b++, 1'bx}; + #1 i = {1'bx, b--, 1'bx}; + #1 i = {1'bx, ++b, 1'bx}; + #1 i = {1'bx, --b, 1'bx}; + + #1 i = 3; + while (--i) begin + if (i == 2) begin + s++; + #10; + end + else if (i == 1) begin + b++; + #3 continue; + $display("UNREACHABLE"); + end + b--; + #1; + end + + #1; + for (i[i++] = s--; b++ - 10 != s--; i[++i]++) begin + #1; + if (i & 1) + continue; + #10; + end + end +endmodule diff --git a/test/core/asgn_expr.v b/test/core/asgn_expr.v new file mode 100644 index 0000000..ce714fc --- /dev/null +++ b/test/core/asgn_expr.v @@ -0,0 +1,59 @@ +module top; + integer i; + reg signed [7:0] b; + reg signed [15:0] s; + initial begin + $monitor("%2d %b %b %b", $time, i, b, s); + + #1; + s = 0; + b = 0; + i = 0; + + #1 i = 1; + + #1; + b = b + 1; + i = {1'bx, b - 8'b1, 1'bx}; + + #1; + b = b - 1; + i = {1'bx, b + 8'b1, 1'bx}; + + #1; + b = b + 1; + i = {1'bx, b, 1'bx}; + + #1; + b = b - 1; + i = {1'bx, b, 1'bx}; + + #1; + i = 3; + i = i - 1; + s = s + 1; + #10; + b = b - 1; + #1; + i = i - 1; + b = b + 1; + #3; + i = i - 1; + + #1; + i = i + 1; + s = s - 1; + i[i] = s; + b = b + 1; + s = s - 1; + while (b - 1 - 10 != s + 1) begin + #1; + if (!(i & 1)) + #10; + i = i + 1; + i[i] = i[i] + 1; + b = b + 1; + s = s - 1; + end + end +endmodule diff --git a/test/error/asgn_expr_non_lhs.sv b/test/error/asgn_expr_non_lhs.sv new file mode 100644 index 0000000..051ed83 --- /dev/null +++ b/test/error/asgn_expr_non_lhs.sv @@ -0,0 +1,6 @@ +// pattern: cannot convert expression to LHS +// location: asgn_expr_non_lhs.sv:5:20 +module top; + integer x; + initial x = (1 = x); +endmodule