Jump.hs 13.5 KB
Newer Older
1
{-# LANGUAGE PatternSynonyms #-}
2 3 4 5 6 7
{- sv2v
 - Author: Zachary Snow <zach@zachjs.com>
 -
 - Conversion for `return`, `break`, and `continue`
 -
 - Because Verilog-2005 has no jumping statements, this conversion ends up
8
 - producing significantly more verbose code to achieve the same control flow.
9 10 11 12
 -}

module Convert.Jump (convert) where

13 14
import Control.Monad.State.Strict
import Control.Monad.Writer.Strict
15 16 17 18 19

import Convert.Traverse
import Language.SystemVerilog.AST

data Info = Info
20 21 22 23 24 25 26 27 28 29 30 31
    { sLoopDepth :: Int
    , sHasJump :: Bool
    , sReturnAllowed :: Bool
    , sJumpAllowed :: Bool
    }

initialState :: Info
initialState = Info
    { sLoopDepth = 0
    , sHasJump = False
    , sReturnAllowed = False
    , sJumpAllowed = True
32 33
    }

34 35 36
initialStateTF :: Info
initialStateTF = initialState { sReturnAllowed = True }

37 38 39 40 41
convert :: [AST] -> [AST]
convert = map $ traverseDescriptions $ traverseModuleItems convertModuleItem

convertModuleItem :: ModuleItem -> ModuleItem
convertModuleItem (MIPackageItem (Function ml t f decls stmtsOrig)) =
42
    MIPackageItem $ Function ml t f decls' stmts''
43 44 45 46 47 48 49 50 51 52
    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
53 54
        stmts' = evalState (convertStmts stmts) initialStateTF
        (decls', stmts'') = addJumpStateDeclTF decls stmts'
55
convertModuleItem (MIPackageItem (Task ml f decls stmts)) =
56
    MIPackageItem $ Task ml f decls' stmts''
57
    where
58 59 60 61 62 63 64 65
        stmts' = evalState (convertStmts stmts) initialStateTF
        (decls', stmts'') = addJumpStateDeclTF decls stmts'
convertModuleItem (Initial    stmt) = convertMIStmt Initial      stmt
convertModuleItem (Final      stmt) = convertMIStmt Final        stmt
convertModuleItem (AlwaysC kw stmt) = convertMIStmt (AlwaysC kw) stmt
convertModuleItem other = other

convertMIStmt :: (Stmt -> ModuleItem) -> Stmt -> ModuleItem
66 67
convertMIStmt constructor (Timing c stmt) =
    convertMIStmt (constructor . Timing c) stmt
68 69
convertMIStmt constructor stmt =
    constructor stmt''
70
    where
71 72 73 74 75 76 77 78 79 80
        stmt' = evalState (convertStmt stmt) initialState
        stmt'' = addJumpStateDeclStmt stmt'

-- adds a declaration of the jump state variable if it is needed; if the jump
-- state is not used at all, then it is removed from the given statements
-- entirely
addJumpStateDeclTF :: [Decl] -> [Stmt] -> ([Decl], [Stmt])
addJumpStateDeclTF decls stmts =
    if uses && not declares then
        ( decls ++
Zachary Snow committed
81
            [Variable Local jumpStateType jumpState [] jsNone]
82 83 84 85 86
        , stmts )
    else if uses then
        (decls, stmts)
    else
        (decls, map (traverseNestedStmts removeJumpState) stmts)
87
    where
88 89 90 91 92 93
        dummyStmt = Block Seq "" decls stmts
        writesJumpState f = elem jumpState $ execWriter $
            collectNestedStmtsM f dummyStmt
        declares = writesJumpState $ collectStmtDeclsM collectVarM
        uses = writesJumpState $
            collectStmtExprsM $ collectNestedExprsM collectExprIdentM
94 95 96 97 98 99 100 101 102 103 104 105 106 107
        collectVarM :: Decl -> Writer [String] ()
        collectVarM (Variable Local _ ident _ _) = tell [ident]
        collectVarM _ = return ()
        collectExprIdentM :: Expr -> Writer [String] ()
        collectExprIdentM (Ident ident) = tell [ident]
        collectExprIdentM _ = return ()
addJumpStateDeclStmt :: Stmt -> Stmt
addJumpStateDeclStmt stmt =
    if null decls
        then stmt'
        else Block Seq "" decls [stmt']
    where (decls, [stmt']) = addJumpStateDeclTF [] [stmt]

removeJumpState :: Stmt -> Stmt
108
removeJumpState orig@(Asgn _ _ (LHSIdent ident) _) =
109 110 111 112
    if ident == jumpState
        then Null
        else orig
removeJumpState other = other
113 114 115 116 117 118 119 120

convertStmts :: [Stmt] -> State Info [Stmt]
convertStmts stmts = do
    res <- convertStmt $ Block Seq "" [] stmts
    let Block Seq "" [] stmts' = res
    return stmts'


121 122
pattern SimpleLoopInits :: Identifier -> [(LHS, Expr)]
pattern SimpleLoopInits var <- [(LHSIdent var, _)]
123

124 125
pattern SimpleLoopGuard :: Identifier -> Expr
pattern SimpleLoopGuard var <- BinOp _ (Ident var) _
126

127 128
pattern SimpleLoopIncrs :: Identifier -> [(LHS, AsgnOp, Expr)]
pattern SimpleLoopIncrs var <- [(LHSIdent var, _, _)]
129

130 131 132 133 134
-- 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
135
    jumpAllowed <- gets sJumpAllowed
136
    modify $ \s -> s { sJumpAllowed = False }
137
    stmts' <- mapM convertStmt stmts
138
    modify $ \s -> s { sJumpAllowed = jumpAllowed }
139 140
    return $ Block Par x decls stmts'

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
convertStmt (Block Seq ""
    decls@[CommentDecl{}, Variable Local _ var0 [] Nil]
    [ comment@CommentStmt{}
    , For
        inits@(SimpleLoopInits var1)
        comp@(SimpleLoopGuard var2)
        incr@(SimpleLoopIncrs var3)
        stmt
    ]) =
    convertLoop localInfo loop comp incr stmt
        >>= return . Block Seq "" decls . (comment :) . pure
    where
        loop c i s = For inits c i s
        localInfo = if var0 /= var1 || var1 /= var2 || var2 /= var3
                        then Nothing
                        else Just ""

158 159
convertStmt (Block Seq x decls stmts) =
    step stmts >>= return . Block Seq x decls
160 161 162 163
    where
        step :: [Stmt] -> State Info [Stmt]
        step [] = return []
        step (s : ss) = do
164 165 166 167 168 169 170 171 172 173 174 175 176 177
            hasJump <- gets sHasJump
            loopDepth <- gets sLoopDepth
            modify $ \st -> st { sHasJump = False }
            s' <- convertStmt s
            currHasJump <- gets sHasJump
            currLoopDepth <- gets sLoopDepth
            assertMsg (loopDepth == currLoopDepth) "loop depth invariant failed"
            modify $ \st -> st { sHasJump = hasJump || currHasJump }
            ss' <- step ss
            if currHasJump && not (null ss)
            then do
                let comp = BinOp Eq (Ident jumpState) jsNone
                let stmt = Block Seq "" [] ss'
                return [s', If NoCheck comp stmt Null]
178
            else do
179
                return $ s' : ss'
180 181

convertStmt (If unique expr thenStmt elseStmt) = do
182 183 184
    (thenStmt', thenHasJump) <- convertSubStmt thenStmt
    (elseStmt', elseHasJump) <- convertSubStmt elseStmt
    modify $ \s -> s { sHasJump = thenHasJump || elseHasJump }
185 186
    return $ If unique expr thenStmt' elseStmt'

187
convertStmt (Case unique kw expr cases) = do
188
    results <- mapM convertSubStmt $ map snd cases
189
    let (stmts', hasJumps) = unzip results
190
    let cases' = zip (map fst cases) stmts'
191 192
    let hasJump = foldl (||) False hasJumps
    modify $ \s -> s { sHasJump = hasJump }
193
    return $ Case unique kw expr cases'
194

195
convertStmt (For
196 197 198
    inits@(SimpleLoopInits var1)
    comp@(SimpleLoopGuard var2)
    incr@(SimpleLoopIncrs var3) stmt) =
199 200 201 202 203 204
    convertLoop localInfo loop comp incr stmt
    where
        loop c i s = For inits c i s
        localInfo = if var1 /= var2 || var2 /= var3
                        then Nothing
                        else Just var1
205
convertStmt (For inits comp incr stmt) =
206 207
    convertLoop Nothing loop comp incr stmt
    where loop c i s = For inits c i s
208
convertStmt (While comp stmt) =
209 210
    convertLoop Nothing loop comp [] stmt
    where loop c _ s = While c s
211
convertStmt (DoWhile comp stmt) =
212 213
    convertLoop Nothing loop comp [] stmt
    where loop c _ s = DoWhile c s
214 215

convertStmt (Continue) = do
216 217 218 219 220 221
    loopDepth <- gets sLoopDepth
    jumpAllowed <- gets sJumpAllowed
    assertMsg (loopDepth > 0) "encountered continue outside of loop"
    assertMsg jumpAllowed "encountered continue inside fork-join"
    modify $ \s -> s { sHasJump = True }
    return $ asgn jumpState jsContinue
222
convertStmt (Break) = do
223 224 225 226 227 228
    loopDepth <- gets sLoopDepth
    jumpAllowed <- gets sJumpAllowed
    assertMsg (loopDepth > 0) "encountered break outside of loop"
    assertMsg jumpAllowed "encountered break inside fork-join"
    modify $ \s -> s { sHasJump = True }
    return $ asgn jumpState jsBreak
229
convertStmt (Return Nil) = do
230 231 232 233 234 235
    jumpAllowed <- gets sJumpAllowed
    returnAllowed <- gets sReturnAllowed
    assertMsg jumpAllowed "encountered return inside fork-join"
    assertMsg returnAllowed "encountered return outside of task or function"
    modify $ \s -> s { sHasJump = True }
    return $ asgn jumpState jsReturn
236 237

convertStmt (RepeatL expr stmt) = do
238 239
    loopDepth <- gets sLoopDepth
    modify $ \s -> s { sLoopDepth = loopDepth + 1 }
240
    stmt' <- convertStmt stmt
241 242 243
    hasJump <- gets sHasJump
    assertMsg (not hasJump) "jumps not supported within repeat loops"
    modify $ \s -> s { sLoopDepth = loopDepth }
244 245
    return $ RepeatL expr stmt'
convertStmt (Forever stmt) = do
246 247
    loopDepth <- gets sLoopDepth
    modify $ \s -> s { sLoopDepth = loopDepth + 1 }
248
    stmt' <- convertStmt stmt
249 250 251
    hasJump <- gets sHasJump
    assertMsg (not hasJump) "jumps not supported within forever loops"
    modify $ \s -> s { sLoopDepth = loopDepth }
252 253 254 255 256 257 258 259 260 261 262 263 264 265
    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

266 267 268 269
-- convert a statement on its own without changing the state, but returning
-- whether or not the statement contains a jump; used to reconcile across
-- branching statements
convertSubStmt :: Stmt -> State Info (Stmt, Bool)
270 271 272
convertSubStmt stmt = do
    origState <- get
    stmt' <- convertStmt stmt
273
    hasJump <- gets sHasJump
274
    put origState
275
    return (stmt', hasJump)
276

277 278 279 280 281
type Incr = (LHS, AsgnOp, Expr)

convertLoop :: Maybe Identifier -> (Expr -> [Incr] -> Stmt -> Stmt) -> Expr
    -> [Incr] -> Stmt -> State Info Stmt
convertLoop localInfo loop comp incr stmt = do
282 283 284 285 286
    -- save the loop state and increment loop depth
    Info { sLoopDepth = origLoopDepth, sHasJump = origHasJump } <- get
    assertMsg (not origHasJump) "has jump invariant failed"
    modify $ \s -> s { sLoopDepth = origLoopDepth + 1 }
    -- convert the loop body
287
    stmt' <- convertStmt stmt
288 289 290 291 292
    -- restore the loop state
    Info { sLoopDepth = afterLoopDepth, sHasJump = afterHasJump } <- get
    assertMsg (origLoopDepth + 1 == afterLoopDepth) "loop depth invariant failed"
    modify $ \s -> s { sLoopDepth = origLoopDepth }

293 294 295 296 297
    let useBreakVar = local && not (null localVar)
    let breakVarDeclRaw = Variable Local (TypeOf $ Ident localVar) breakVar [] Nil
    let breakVarDecl = if useBreakVar then breakVarDeclRaw else CommentDecl "no-op"
    let updateBreakVar = if useBreakVar then asgn breakVar $ Ident localVar else Null

298
    let keepRunning = BinOp Lt (Ident jumpState) jsBreak
299 300 301 302
    let pushBreakVar = if useBreakVar
                        then If NoCheck (UniOp LogNot keepRunning)
                                (asgn localVar $ Ident breakVar) Null
                        else Null
303
    let comp' = if local then comp else BinOp LogAnd comp keepRunning
304
    let incr' = if local then incr else map (stubIncr keepRunning) incr
305 306 307 308
    let body = Block Seq "" [] $
                [ asgn jumpState jsNone
                , stmt'
                ]
309 310 311 312
    let body' = if local
                    then If NoCheck keepRunning
                            (Block Seq "" [] [body, updateBreakVar]) Null
                    else body
313 314
    let jsStackIdent = jumpState ++ "_" ++ show origLoopDepth
    let jsStackDecl = Variable Local jumpStateType jsStackIdent []
Zachary Snow committed
315
                        (Ident jumpState)
316 317 318 319
    let jsStackRestore = If NoCheck
                (BinOp Ne (Ident jumpState) jsReturn)
                (asgn jumpState (Ident jsStackIdent))
                Null
320 321 322 323
    let jsCheckReturn = If NoCheck
                (BinOp Ne (Ident jumpState) jsReturn)
                (asgn jumpState jsNone)
                Null
324 325 326

    return $
        if not afterHasJump then
327
            loop comp incr stmt'
328
        else if origLoopDepth == 0 then
329 330 331
            Block Seq "" [ breakVarDecl ]
                [ loop comp' incr' body'
                , pushBreakVar
332 333
                , jsCheckReturn
                ]
334 335
        else
            Block Seq ""
336 337 338
                [ breakVarDecl, jsStackDecl ]
                [ loop comp' incr' body'
                , pushBreakVar
339
                , jsStackRestore
340 341
                ]

342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
    where
        breakVar = "_sv2v_value_on_break"
        local = localInfo /= Nothing
        Just localVar = localInfo

stubIncr :: Expr -> Incr -> Incr
stubIncr keepRunning (lhs, AsgnOpEq, expr) =
    (lhs, AsgnOpEq, expr')
    where expr' = Mux keepRunning expr (lhsToExpr lhs)
stubIncr keepRunning (lhs, op, expr) =
    stubIncr keepRunning (lhs, AsgnOpEq, expr')
    where
        AsgnOp binop = op
        expr' = BinOp binop (lhsToExpr lhs) expr

357
jumpStateType :: Type
358
jumpStateType = IntegerVector TBit Unspecified [(RawNum 0, RawNum 1)]
359 360 361

jumpState :: String
jumpState = "_sv2v_jump"
362

363 364 365
jsVal :: Integer -> Expr
jsVal n = Number $ Based 2 False Binary n 0

366 367
-- keep running the loop/function normally
jsNone :: Expr
368
jsNone = jsVal 0
369
-- skip to the next iteration of the loop (continue)
370
jsContinue :: Expr
371
jsContinue = jsVal 1
372 373
-- stop running the loop immediately (break)
jsBreak :: Expr
374
jsBreak = jsVal 2
375 376
-- stop running the function immediately (return)
jsReturn :: Expr
377
jsReturn = jsVal 3
378 379 380 381 382 383 384


assertMsg :: Bool -> String -> State Info ()
assertMsg True _ = return ()
assertMsg False msg = error msg

asgn :: Identifier -> Expr -> Stmt
385
asgn x e = Asgn AsgnOpEq Nothing (LHSIdent x) e