aboutsummaryrefslogtreecommitdiffstats
path: root/src/VeriFuzz/Reduce.hs
blob: 95c5bef4ebe5bd1480220903f8a68e9ac25205aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
{-|
Module      : VeriFuzz.Reduce
Description : Test case reducer implementation.
Copyright   : (c) 2019, Yann Herklotz Grave
License     : GPL-3
Maintainer  : ymherklotz [at] gmail [dot] com
Stability   : experimental
Portability : POSIX

Test case reducer implementation.
-}

module VeriFuzz.Reduce
    ( reduce
    )
where

import           Control.Lens
import           VeriFuzz.AST
import           VeriFuzz.CodeGen
import           VeriFuzz.Internal
import           VeriFuzz.Mutate

data Replacement a = Dual a a
                   | Single a
                   | None
                   deriving (Eq, Show)

instance Functor Replacement where
    fmap f (Dual a b) = Dual (f a) $ f b
    fmap f (Single a) = Single $ f a
    fmap _ None       = None

instance Foldable Replacement where
    foldMap _ None       = mempty
    foldMap f (Single a) = f a
    foldMap f (Dual a b) = f a <> f b

instance Traversable Replacement where
    traverse _ None       = pure None
    traverse f (Single a) = Single <$> f a
    traverse f (Dual a b) = Dual <$> f a <*> f b

-- | Split a list in two halves.
halve :: [a] -> ([a], [a])
halve l = splitAt (length l `div` 2) l

filterExpr :: [Identifier] -> Expr -> Expr
filterExpr ids (Id i) = if i `notElem` ids then Number 1 0 else Id i
filterExpr _ e        = e

filterDecl :: [Identifier] -> ModItem -> Bool
filterDecl ids (Decl Nothing (Port _ _ _ i)) = i `elem` ids
filterDecl _   _                             = True

filterAssigns :: [Port] -> ModItem -> Bool
filterAssigns out (ModCA (ContAssign i _)) =
    notElem i $ out ^.. traverse . portName
filterAssigns _ _ = False

cleanUndefined :: [Identifier] -> [ModItem] -> [ModItem]
cleanUndefined ids mis =
    filter (filterDecl usedWires) mis
        &  traverse
        .  modContAssign
        .  contAssignExpr
        %~ transform (filterExpr usedWires)
    where usedWires = mis ^.. traverse . modContAssign . contAssignNetLVal <> ids

halveModAssign :: (([ModItem], [ModItem]) -> [ModItem]) -> ModDecl -> ModDecl
halveModAssign choose m = m & modItems %~ assigns
  where
    assigns l =
        cleanUndefined (m ^.. modInPorts . traverse . portName)
            . combineAssigns (head $ m ^. modOutPorts)
            . (filter (not . filterAssigns []) l <>)
            . choose
            . halve
            . filter (filterAssigns $ m ^. modOutPorts)
            $ l

-- | Split a module declaration in half by trying to remove assign statements.
halveAssigns :: VerilogSrc -> Replacement VerilogSrc
halveAssigns vsrc =
    Dual (modified fst) (modified snd)
    where
        modified f = vsrc & getModule %~ halveModAssign f

--halveExpr :: Expr -> Replacement Expr
--halveExpr (Concat (x:xs)) = Dual x $ Concat xs
--halveExpr (BinOp e1 _ e2) = Dual e1 e2
--halveExpr (Cond _ e1 e2)  = Dual e1 e2
--halveExpr (UnOp _ e)      = Single e
--halveExpr (Func _ e)      = Single e
--halveExpr _               = None

-- | Reduce an input to a minimal representation.
reduce
    :: (SourceInfo -> IO Bool) -- ^ Failed or not.
    -> SourceInfo              -- ^ Input verilog source to be reduced.
    -> IO SourceInfo           -- ^ Reduced output.
reduce eval srcInfo@(SourceInfo top src) = do
    replAnswer <- sequenceA $ evalIfNotEmpty <$> replacement
    case (replacement, replAnswer) of
        (Single s, Single False) ->
            reduce eval $ srcTop s
        (Dual _ l, Dual True False) ->
            reduce eval $ srcTop l
        (Dual r _, Dual False True) ->
            reduce eval $ srcTop r
        (Dual r l, Dual False False) -> do
            lreduced <- reduce eval $ srcTop l
            rreduced <- reduce eval $ srcTop r
            if runSource lreduced < runSource rreduced
            then return lreduced
            else return rreduced
        _ -> return srcInfo
  where
    replacement = halveAssigns src
    srcTop = SourceInfo top
    evalIfNotEmpty m = do
        print $ GenVerilog <$> m ^.. getModule . modItems . traverse . modContAssign
        eval $ srcTop m