From 8ae89a7bd46c4bfc4c7f466b8652852ab6f43ea6 Mon Sep 17 00:00:00 2001 From: Zachary Snow Date: Tue, 8 Oct 2019 22:13:05 -0400 Subject: [PATCH] support and convert jumps: break, continue, and return --- src/Convert.hs | 4 +- src/Convert/Jump.hs | 245 ++++++++++++++++++++++ src/Convert/Return.hs | 23 -- src/Convert/Traverse.hs | 11 + src/Language/SystemVerilog/AST/Stmt.hs | 4 + src/Language/SystemVerilog/Parser/Parse.y | 3 + sv2v.cabal | 2 +- test/basic/jump.sv | 81 +++++++ test/basic/jump.v | 67 ++++++ 9 files changed, 414 insertions(+), 26 deletions(-) create mode 100644 src/Convert/Jump.hs delete mode 100644 src/Convert/Return.hs create mode 100644 test/basic/jump.sv create mode 100644 test/basic/jump.v diff --git a/src/Convert.hs b/src/Convert.hs index 8af0834..6eba15c 100644 --- a/src/Convert.hs +++ b/src/Convert.hs @@ -21,6 +21,7 @@ import qualified Convert.Foreach import qualified Convert.FuncRet import qualified Convert.Interface import qualified Convert.IntTypes +import qualified Convert.Jump import qualified Convert.KWArgs import qualified Convert.Logic import qualified Convert.LogOp @@ -30,7 +31,6 @@ import qualified Convert.NestPI import qualified Convert.Package import qualified Convert.ParamType import qualified Convert.RemoveComments -import qualified Convert.Return import qualified Convert.Simplify import qualified Convert.SizeCast import qualified Convert.StarPort @@ -75,7 +75,7 @@ phases excludes = , Convert.Package.convert , Convert.Enum.convert , Convert.NestPI.convert - , Convert.Return.convert + , Convert.Jump.convert , Convert.Foreach.convert , selectExclude (Job.Interface, Convert.Interface.convert) , selectExclude (Job.Always , Convert.AlwaysKW.convert) diff --git a/src/Convert/Jump.hs b/src/Convert/Jump.hs new file mode 100644 index 0000000..5daba8c --- /dev/null +++ b/src/Convert/Jump.hs @@ -0,0 +1,245 @@ +{- sv2v + - Author: Zachary Snow + - + - Conversion for `return`, `break`, and `continue` + - + - Because Verilog-2005 has no jumping statements, this conversion ends up + - producing significantly more verbose code to acheive the same control flow. + -} + +module Convert.Jump (convert) where + +import Control.Monad.State + +import Convert.Traverse +import Language.SystemVerilog.AST + +data JumpType + = JTNone + | JTContinue + | JTBreak + | JTReturn + deriving (Eq, Ord, Show) + +data Info = Info + { sJumpType :: JumpType + , sLoopID :: Identifier + } + +convert :: [AST] -> [AST] +convert = map $ traverseDescriptions $ traverseModuleItems convertModuleItem + +convertModuleItem :: ModuleItem -> ModuleItem +convertModuleItem (MIPackageItem (Function ml t f decls stmtsOrig)) = + if sJumpType finalState == JTNone || sJumpType finalState == JTReturn + then MIPackageItem $ Function ml t f decls stmts' + else error "illegal jump statement within task" + where + stmts = map (traverseNestedStmts convertReturn) stmtsOrig + convertReturn :: Stmt -> Stmt + convertReturn (Return Nil) = Return Nil + convertReturn (Return e) = + Block Seq "" [] + [ asgn f e + , Return Nil + ] + convertReturn other = other + initialState = Info { sJumpType = JTNone, sLoopID = "" } + (stmts', finalState) = runState (convertStmts stmts) initialState +convertModuleItem (MIPackageItem (Task ml f decls stmts)) = + if sJumpType finalState == JTNone || sJumpType finalState == JTReturn + then MIPackageItem $ Task ml f decls $ stmts' + else error "illegal jump statement within task" + where + initialState = Info { sJumpType = JTNone, sLoopID = "" } + (stmts', finalState) = runState (convertStmts stmts) initialState +convertModuleItem (Initial stmt) = + if sJumpType finalState == JTNone + then Initial stmt' + else error "illegal jump statement within initial construct" + where + initialState = Info { sJumpType = JTNone, sLoopID = "" } + (stmt', finalState) = runState (convertStmt stmt) initialState +convertModuleItem (AlwaysC kw stmt) = + if sJumpType finalState == JTNone + then AlwaysC kw stmt' + else error "illegal jump statement within always construct" + where + initialState = Info { sJumpType = JTNone, sLoopID = "" } + (stmt', finalState) = runState (convertStmt stmt) initialState +convertModuleItem other = other + +convertStmts :: [Stmt] -> State Info [Stmt] +convertStmts stmts = do + res <- convertStmt $ Block Seq "" [] stmts + let Block Seq "" [] stmts' = res + return stmts' + + +-- rewrites the given statement, and returns the type of any unfinished jump +convertStmt :: Stmt -> State Info Stmt + +convertStmt (Block Par x decls stmts) = do + -- break, continue, and return disallowed in fork-join + modify $ \s -> s { sLoopID = "" } + loopID <- gets sLoopID + stmts' <- mapM convertStmt stmts + modify $ \s -> s { sLoopID = loopID } + return $ Block Par x decls stmts' + +convertStmt (Block Seq x decls stmts) = do + stmts' <- step stmts + return $ Block Seq x decls $ filter (/= Null) stmts' + where + step :: [Stmt] -> State Info [Stmt] + step [] = return [] + step (s : ss) = do + jt <- gets sJumpType + loopID <- gets sLoopID + if jt == JTNone then do + s' <- convertStmt s + jt' <- gets sJumpType + if jt' == JTNone || not (isBranch s) || null loopID then do + ss' <- step ss + return $ s' : ss' + else do + modify $ \t -> t { sJumpType = JTNone } + ss' <- step ss + let comp = BinOp Eq (Ident loopID) runLoop + let stmt = Block Seq "" [] ss' + modify $ \t -> t { sJumpType = jt' } + return [s', If Nothing comp stmt Null] + else do + return [Null] + isBranch :: Stmt -> Bool + isBranch (If{}) = True + isBranch (Case{}) = True + isBranch _ = False + +convertStmt (If unique expr thenStmt elseStmt) = do + (thenStmt', thenJT) <- convertSubStmt thenStmt + (elseStmt', elseJT) <- convertSubStmt elseStmt + let newJT = max thenJT elseJT + modify $ \s -> s { sJumpType = newJT } + return $ If unique expr thenStmt' elseStmt' + +convertStmt (Case unique kw expr cases mdef) = do + (mdef', mdefJT) <- + case mdef of + Nothing -> return (Nothing, JTNone) + Just stmt -> do + (stmt', jt) <- convertSubStmt stmt + return (Just stmt', jt) + results <- mapM convertSubStmt $ map snd cases + let (stmts', jts) = unzip results + let cases' = zip (map fst cases) stmts' + let newJT = foldl max mdefJT jts + modify $ \s -> s { sJumpType = newJT } + return $ Case unique kw expr cases' mdef' + +convertStmt (For inits comp incr stmt) = + convertLoop loop comp stmt + where loop c s = For inits c incr s +convertStmt (While comp stmt) = + convertLoop While comp stmt +convertStmt (DoWhile comp stmt) = + convertLoop DoWhile comp stmt + +convertStmt (Continue) = do + loopID <- gets sLoopID + modify $ \s -> s { sJumpType = JTContinue } + assertMsg (not $ null loopID) "encountered continue outside of loop" + return $ asgn loopID continueLoop +convertStmt (Break) = do + loopID <- gets sLoopID + modify $ \s -> s { sJumpType = JTBreak } + assertMsg (not $ null loopID) "encountered break outside of loop" + return $ asgn loopID exitLoop +convertStmt (Return Nil) = do + loopID <- gets sLoopID + modify $ \s -> s { sJumpType = JTReturn } + if null loopID + then return Null + else return $ asgn loopID exitLoop + +convertStmt (RepeatL expr stmt) = do + modify $ \s -> s { sLoopID = "repeat" } + stmt' <- convertStmt stmt + jt <- gets sJumpType + assertMsg (jt == JTNone) "jumps not supported within repeat loops" + return $ RepeatL expr stmt' +convertStmt (Forever stmt) = do + modify $ \s -> s { sLoopID = "forever" } + stmt' <- convertStmt stmt + jt <- gets sJumpType + assertMsg (jt == JTNone) "jumps not supported within forever loops" + return $ Forever stmt' + +convertStmt (Timing timing stmt) = + convertStmt stmt >>= return . Timing timing +convertStmt (StmtAttr attr stmt) = + convertStmt stmt >>= return . StmtAttr attr + +convertStmt (Return{}) = return $ + error "non-void return should have been elaborated already" +convertStmt (Foreach{}) = return $ + error "foreach should have been elaborated already" + +convertStmt other = return other + + +-- convert a statement on its own without changing the state, but returning the +-- resulting jump type; used to reconcile across branching statements +convertSubStmt :: Stmt -> State Info (Stmt, JumpType) +convertSubStmt stmt = do + origState <- get + stmt' <- convertStmt stmt + jt <- gets sJumpType + put origState + if sJumpType origState == JTNone + then return (stmt', jt) + else error $ "convertStmt invariant failed on: " ++ show stmt + +convertLoop :: (Expr -> Stmt -> Stmt) -> Expr -> Stmt -> State Info Stmt +convertLoop loop comp stmt = do + Info { sJumpType = origJT, sLoopID = origLoopID } <- get + let loopID = (++) "_sv2v_loop_" $ shortHash $ loop comp stmt + modify $ \s -> s { sLoopID = loopID } + stmt' <- convertStmt stmt + jt <- gets sJumpType + let afterJT = if jt == JTReturn then jt else origJT + put $ Info { sJumpType = afterJT, sLoopID = origLoopID } + let comp' = BinOp LogAnd (BinOp Ne (Ident loopID) exitLoop) comp + return $ if jt == JTNone + then loop comp stmt' + else Block Seq "" + [ Variable Local loopStateType loopID [] (Just runLoop) + ] + [ loop comp' $ Block Seq "" [] + [ asgn loopID runLoop + , stmt' + ] + , if afterJT == JTReturn && origLoopID /= "" + then asgn origLoopID exitLoop + else Null + ] + where loopStateType = IntegerVector TBit Unspecified [(Number "0", Number "1")] + + +-- stop running the loop immediately (break or return) +exitLoop :: Expr +exitLoop = Number "0" +-- keep running the loop normally +runLoop :: Expr +runLoop = Number "1" +-- skip to the next iteration of the loop (continue) +continueLoop :: Expr +continueLoop = Number "2" + + +assertMsg :: Bool -> String -> State Info () +assertMsg True _ = return () +assertMsg False msg = error msg + +asgn :: Identifier -> Expr -> Stmt +asgn x e = AsgnBlk AsgnOpEq (LHSIdent x) e diff --git a/src/Convert/Return.hs b/src/Convert/Return.hs deleted file mode 100644 index 26b9709..0000000 --- a/src/Convert/Return.hs +++ /dev/null @@ -1,23 +0,0 @@ -{- sv2v - - Author: Zachary Snow - - - - Conversion for `return` - -} - -module Convert.Return (convert) where - -import Convert.Traverse -import Language.SystemVerilog.AST - -convert :: [AST] -> [AST] -convert = map $ traverseDescriptions $ traverseModuleItems convertFunction - -convertFunction :: ModuleItem -> ModuleItem -convertFunction (MIPackageItem (Function ml t f decls stmts)) = - MIPackageItem $ Function ml t f decls $ - map (traverseNestedStmts convertStmt) stmts - where - convertStmt :: Stmt -> Stmt - convertStmt (Return e) = AsgnBlk AsgnOpEq (LHSIdent f) e - convertStmt other = other -convertFunction other = other diff --git a/src/Convert/Traverse.hs b/src/Convert/Traverse.hs index 26a3db8..70a8b10 100644 --- a/src/Convert/Traverse.hs +++ b/src/Convert/Traverse.hs @@ -229,6 +229,13 @@ traverseSinglyNestedStmtsM fullMapper = cs where cs (StmtAttr a stmt) = fullMapper stmt >>= return . StmtAttr a cs (Block _ "" [] []) = return Null + cs (Block Seq name decls stmts) = do + stmts' <- mapM fullMapper stmts + return $ Block Seq name decls $ concatMap explode stmts' + where + explode :: Stmt -> [Stmt] + explode (Block Seq "" [] ss) = ss + explode other = [other] cs (Block kw name decls stmts) = mapM fullMapper stmts >>= return . Block kw name decls cs (Case u kw expr cases def) = do @@ -254,6 +261,8 @@ traverseSinglyNestedStmtsM fullMapper = cs cs (Trigger blocks x) = return $ Trigger blocks x cs (Assertion a) = traverseAssertionStmtsM fullMapper a >>= return . Assertion + cs (Continue) = return Continue + cs (Break) = return Break cs (Null) = return Null traverseAssertionStmtsM :: Monad m => MapperM m Stmt -> MapperM m Assertion @@ -714,6 +723,8 @@ traverseStmtExprsM exprMapper = flatStmtMapper a' <- traverseAssertionStmtsM stmtMapper a a'' <- traverseAssertionExprsM exprMapper a' return $ Assertion a'' + flatStmtMapper (Continue) = return Continue + flatStmtMapper (Break) = return Break flatStmtMapper (Null) = return Null initsMapper (Left decls) = mapM declMapper decls >>= return . Left diff --git a/src/Language/SystemVerilog/AST/Stmt.hs b/src/Language/SystemVerilog/AST/Stmt.hs index ee60b31..16ab9a4 100644 --- a/src/Language/SystemVerilog/AST/Stmt.hs +++ b/src/Language/SystemVerilog/AST/Stmt.hs @@ -51,6 +51,8 @@ data Stmt | Subroutine (Maybe Identifier) Identifier Args | Trigger Bool Identifier | Assertion Assertion + | Continue + | Break | Null deriving Eq @@ -96,6 +98,8 @@ instance Show Stmt where show (Timing t s ) = printf "%s %s" (show t) (show s) show (Trigger b x) = printf "->%s %s;" (if b then "" else ">") x show (Assertion a) = show a + show (Continue ) = "continue;" + show (Break ) = "break;" show (Null ) = ";" data CaseKW diff --git a/src/Language/SystemVerilog/Parser/Parse.y b/src/Language/SystemVerilog/Parser/Parse.y index 86b9f54..5e92f6a 100644 --- a/src/Language/SystemVerilog/Parser/Parse.y +++ b/src/Language/SystemVerilog/Parser/Parse.y @@ -911,6 +911,9 @@ StmtNonBlock :: { Stmt } | Identifier "::" Identifier CallArgs ";" { Subroutine (Just $1) $3 $4 } | TimingControl Stmt { Timing $1 $2 } | "return" Expr ";" { Return $2 } + | "return" ";" { Return Nil } + | "break" ";" { Break } + | "continue" ";" { Continue } | "while" "(" Expr ")" Stmt { While $3 $5 } | "repeat" "(" Expr ")" Stmt { RepeatL $3 $5 } | "do" Stmt "while" "(" Expr ")" ";" { DoWhile $5 $2 } diff --git a/sv2v.cabal b/sv2v.cabal index c3d3179..ac07580 100644 --- a/sv2v.cabal +++ b/sv2v.cabal @@ -66,6 +66,7 @@ executable sv2v Convert.FuncRet Convert.Interface Convert.IntTypes + Convert.Jump Convert.KWArgs Convert.Logic Convert.LogOp @@ -75,7 +76,6 @@ executable sv2v Convert.Package Convert.ParamType Convert.RemoveComments - Convert.Return Convert.Simplify Convert.SizeCast Convert.StarPort diff --git a/test/basic/jump.sv b/test/basic/jump.sv new file mode 100644 index 0000000..58f6340 --- /dev/null +++ b/test/basic/jump.sv @@ -0,0 +1,81 @@ +module top; + + task skip1; + $display("HELLO skip1"); + return; + $display("UNREACHABLE"); + endtask + function void skip2; + $display("HELLO skip2"); + return; + $display("UNREACHABLE"); + endfunction + function int skip3; + $display("HELLO skip3"); + return 1; + $display("UNREACHABLE"); + endfunction + task skip4; + for (int i = 0; i < 10; ++i) begin + $display("HELLO skip4"); + return; + $display("UNREACHABLE"); + end + $display("UNREACHABLE"); + endtask + task skip5; + for (int i = 0; i < 10; ++i) begin + $display("HELLO skip5-1"); + for (int j = 0; j < 10; ++j) begin + $display("HELLO skip5-2"); + return; + $display("UNREACHABLE"); + end + $display("UNREACHABLE"); + end + $display("UNREACHABLE"); + endtask + initial begin + skip1; + skip2; + $display(skip3()); + skip4; + skip5; + end + + initial + for (int i = 0; i < 10; ++i) begin + $display("Loop Y:", i); + continue; + $display("UNREACHABLE"); + end + + initial + for (int i = 0; i < 10; ++i) begin + $display("Loop Z:", i); + break; + $display("UNREACHABLE"); + end + + initial + for (int i = 0; i < 10; ++i) + if (i < 5) + $display("Loop A:", i); + else + break; + + initial + for (int i = 0; i < 10; ++i) begin + if (i < 3) + $display("Loop B-1:", i); + else if (i < 7) + $display("Loop B-2:", i); + else begin + $display("Loop B-3:", i); + continue; + $display("UNREACHABLE"); + end + $display("Loop B:", i); + end + +endmodule diff --git a/test/basic/jump.v b/test/basic/jump.v new file mode 100644 index 0000000..01e96b1 --- /dev/null +++ b/test/basic/jump.v @@ -0,0 +1,67 @@ +module top; + + task skip1; + $display("HELLO skip1"); + endtask + task skip2; + $display("HELLO skip2"); + endtask + function integer skip3; + input x; + begin + $display("HELLO skip3"); + skip3 = 1; + end + endfunction + task skip4; + $display("HELLO skip4"); + endtask + task skip5; + begin + $display("HELLO skip5-1"); + $display("HELLO skip5-2"); + end + endtask + initial begin + skip1; + skip2; + $display(skip3(0)); + skip4; + skip5; + end + + initial begin : loop_y + integer i; + for (i = 0; i < 10; ++i) + $display("Loop Y:", i); + end + + initial begin : loop_z + integer i; + i = 0; + $display("Loop Z:", i); + end + + initial begin : loop_a + integer i; + for (i = 0; i < 5; ++i) + $display("Loop A:", i); + end + + initial begin : loop_b + integer i; + for (i = 0; i < 10; ++i) begin + if (i < 3) begin + $display("Loop B-1:", i); + $display("Loop B:", i); + end + else if (i < 7) begin + $display("Loop B-2:", i); + $display("Loop B:", i); + end + else + $display("Loop B-3:", i); + end + end + +endmodule