From 44afcf5b2924a7a97b9529a68e7c378beeecd9a5 Mon Sep 17 00:00:00 2001 From: Zachary Snow Date: Wed, 14 Apr 2021 13:16:27 -0400 Subject: [PATCH] improved handling of break statement - fix preservation of loop variables when breaking - extend Yosys-compatible constant loop conversion to support loop variables declared outside of the looop --- src/Convert/Jump.hs | 85 +++++++++++++++++++++++++++------- test/basic/simple_loop_jump.sv | 43 ++++++++++++++++- test/basic/simple_loop_jump.v | 35 +++++++++++++- 3 files changed, 143 insertions(+), 20 deletions(-) diff --git a/src/Convert/Jump.hs b/src/Convert/Jump.hs index 6852a3e..a20d2f8 100644 --- a/src/Convert/Jump.hs +++ b/src/Convert/Jump.hs @@ -122,6 +122,9 @@ pattern SimpleLoopInits :: String -> Type -> Identifier -> Expr pattern SimpleLoopInits msg typ var expr = Left [CommentDecl msg, Variable Local typ var [] expr] +pattern SimpleLoopInitsAlt :: String -> Expr -> Either [Decl] [(LHS, Expr)] +pattern SimpleLoopInitsAlt var expr = Right [(LHSIdent var, expr)] + pattern SimpleLoopGuard :: BinOp -> Identifier -> Expr -> Expr pattern SimpleLoopGuard cmp var bound = BinOp cmp (Ident var) bound @@ -179,19 +182,32 @@ convertStmt (Case unique kw expr cases) = do convertStmt (For (inits @ (SimpleLoopInits _ _ var1 _)) (comp @ (SimpleLoopGuard _ var2 _)) - (incr @ (SimpleLoopIncrs var3 _ _)) - stmt) = - if var1 /= var2 || var2 /= var3 - then convertLoop False loop comp stmt - else convertLoop True loop comp stmt - where loop c s = For inits c incr s + (incr @ (SimpleLoopIncrs var3 _ _)) stmt) = + 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 "" +convertStmt (For + (inits @ (SimpleLoopInitsAlt var1 _)) + (comp @ (SimpleLoopGuard _ var2 _)) + (incr @ (SimpleLoopIncrs var3 _ _)) stmt) = + 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 convertStmt (For inits comp incr stmt) = - convertLoop False loop comp stmt - where loop c s = For inits c incr s + convertLoop Nothing loop comp incr stmt + where loop c i s = For inits c i s convertStmt (While comp stmt) = - convertLoop False While comp stmt + convertLoop Nothing loop comp [] stmt + where loop c _ s = While c s convertStmt (DoWhile comp stmt) = - convertLoop False DoWhile comp stmt + convertLoop Nothing loop comp [] stmt + where loop c _ s = DoWhile c s convertStmt (Continue) = do loopDepth <- gets sLoopDepth @@ -255,8 +271,11 @@ convertSubStmt stmt = do put origState return (stmt', hasJump) -convertLoop :: Bool -> (Expr -> Stmt -> Stmt) -> Expr -> Stmt -> State Info Stmt -convertLoop local loop comp stmt = do +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 -- save the loop state and increment loop depth Info { sLoopDepth = origLoopDepth, sHasJump = origHasJump } <- get assertMsg (not origHasJump) "has jump invariant failed" @@ -268,13 +287,26 @@ convertLoop local loop comp stmt = do assertMsg (origLoopDepth + 1 == afterLoopDepth) "loop depth invariant failed" modify $ \s -> s { sLoopDepth = origLoopDepth } + 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 + let keepRunning = BinOp Lt (Ident jumpState) jsBreak + let pushBreakVar = if useBreakVar + then If NoCheck (UniOp LogNot keepRunning) + (asgn localVar $ Ident breakVar) Null + else Null let comp' = if local then comp else BinOp LogAnd comp keepRunning + let incr' = if local then incr else map (stubIncr keepRunning) incr let body = Block Seq "" [] $ [ asgn jumpState jsNone , stmt' ] - let body' = if local then If NoCheck keepRunning body Null else body + let body' = if local + then If NoCheck keepRunning + (Block Seq "" [] [body, updateBreakVar]) Null + else body let jsStackIdent = jumpState ++ "_" ++ show origLoopDepth let jsStackDecl = Variable Local jumpStateType jsStackIdent [] (Ident jumpState) @@ -289,19 +321,36 @@ convertLoop local loop comp stmt = do return $ if not afterHasJump then - loop comp stmt' + loop comp incr stmt' else if origLoopDepth == 0 then - Block Seq "" [] - [ loop comp' body' + Block Seq "" [ breakVarDecl ] + [ loop comp' incr' body' + , pushBreakVar , jsCheckReturn ] else Block Seq "" - [ jsStackDecl ] - [ loop comp' body' + [ breakVarDecl, jsStackDecl ] + [ loop comp' incr' body' + , pushBreakVar , jsStackRestore ] + 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 + jumpStateType :: Type jumpStateType = IntegerVector TBit Unspecified [(RawNum 0, RawNum 1)] diff --git a/test/basic/simple_loop_jump.sv b/test/basic/simple_loop_jump.sv index 87cfd1d..f21395e 100644 --- a/test/basic/simple_loop_jump.sv +++ b/test/basic/simple_loop_jump.sv @@ -8,8 +8,49 @@ module top; f = f * 2; end endfunction + function automatic integer g; + input integer inp; + integer idx; + g = 1; + for (idx = 0; idx < inp; idx = idx + 1) begin + if (g == 32) + break; + g = g * 2; + end + g += idx; + endfunction + function automatic integer h; + input integer inp; + integer idx; + h = 1; + for (idx = 0; idx + 1 < 1 + inp; idx = idx + 1) begin + if (h == 32) + break; + h = h * 2; + end + h += idx + 1; + endfunction + function automatic integer j; + input integer inp; + for (j = 0; j < inp; j = j + 1) + if (j == 3) + return j; + j *= 2; + endfunction + function automatic integer k; + input integer inp; + for (k = 0; k + 1 < 1 + inp; k = k + 1) + if (k == 3) + return k * 3; + k = k * 2 + 1; + endfunction integer i; initial - for (i = 0; i < 10; i = i + 1) + for (i = 0; i < 10; i = i + 1) begin $display("f(%0d) = %0d", i, f(i)); + $display("g(%0d) = %0d", i, g(i)); + $display("h(%0d) = %0d", i, h(i)); + $display("j(%0d) = %0d", i, j(i)); + $display("k(%0d) = %0d", i, k(i)); + end endmodule diff --git a/test/basic/simple_loop_jump.v b/test/basic/simple_loop_jump.v index e1d90e2..3535809 100644 --- a/test/basic/simple_loop_jump.v +++ b/test/basic/simple_loop_jump.v @@ -6,8 +6,41 @@ module top; else f = 2 ** inp; endfunction + function automatic integer g; + input integer inp; + if (inp > 5) + g = 32 + 5; + else + g = 2 ** inp + inp; + endfunction + function automatic integer h; + input integer inp; + if (inp > 5) + h = 32 + 5 + 1; + else + h = 2 ** inp + inp + 1; + endfunction + function automatic integer j; + input integer inp; + if (inp > 3) + j = 3; + else + j = inp * 2; + endfunction + function automatic integer k; + input integer inp; + if (inp > 3) + k = 3 * 3; + else + k = inp * 2 + 1; + endfunction integer i; initial - for (i = 0; i < 10; i = i + 1) + for (i = 0; i < 10; i = i + 1) begin $display("f(%0d) = %0d", i, f(i)); + $display("g(%0d) = %0d", i, g(i)); + $display("h(%0d) = %0d", i, h(i)); + $display("j(%0d) = %0d", i, j(i)); + $display("k(%0d) = %0d", i, k(i)); + end endmodule