mirror of https://github.com/zachjs/sv2v.git
constant folding extensions
- fold string literal comparisons - fold non-decimal bit shifts - fold non-decimal integer comparisons - fold decimal bitwise AND and OR - simplify cast expressions before elaboration - remove duplicate cast expression traversal - flatten concatenated numbers in a single pass
This commit is contained in:
parent
e09aea48e0
commit
96a108ded7
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
* Added elaboration for accesses to fields of struct constants, which can
|
||||
substantially improve performance on some designs
|
||||
* Added constant folding for comparisons involving string literals
|
||||
|
||||
## v0.0.10
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ traverseStmtM stmt = do
|
|||
|
||||
traverseExprM :: Expr -> SC Expr
|
||||
traverseExprM (Cast (Left (IntegerVector kw sg rs)) value) | kw /= TBit = do
|
||||
value' <- traverseExprM value
|
||||
value' <- fmap simplify $ traverseExprM value
|
||||
size' <- traverseExprM size
|
||||
convertCastM size' value' signed
|
||||
where
|
||||
|
|
@ -121,8 +121,9 @@ convertCastM (Number size) (Number value) signed =
|
|||
return $ Number $
|
||||
numberCast signed (fromIntegral size') value
|
||||
where Just size' = numberToInteger size
|
||||
convertCastM size@Number{} (String str) signed =
|
||||
convertCastM size (stringToNumber str) signed
|
||||
convertCastM size value signed = do
|
||||
value' <- traverseExprM value
|
||||
sizeUsesLocalVars <- embedScopes usesLocalVars size
|
||||
inProcedure <- withinProcedureM
|
||||
if not sizeUsesLocalVars || not inProcedure then do
|
||||
|
|
@ -135,12 +136,12 @@ convertCastM size value signed = do
|
|||
else do
|
||||
details <- lookupElemM name
|
||||
when (details == Nothing) (injectTopItem item)
|
||||
return $ Call (Ident name) (Args [value'] [])
|
||||
return $ Call (Ident name) (Args [value] [])
|
||||
else do
|
||||
name <- castDeclName 0
|
||||
insertElem name ()
|
||||
useVar <- withinStmt
|
||||
injectDecl $ castDecl useVar name value' size signed
|
||||
injectDecl $ castDecl useVar name value size signed
|
||||
return $ Ident name
|
||||
|
||||
-- checks if a cast size references any vars not defined at the top level scope
|
||||
|
|
|
|||
|
|
@ -13,9 +13,11 @@ module Convert.ExprUtils
|
|||
, endianCondExpr
|
||||
, endianCondRange
|
||||
, dimensionsSize
|
||||
, stringToNumber
|
||||
) where
|
||||
|
||||
import Data.Bits (shiftL, shiftR)
|
||||
import Data.Bits ((.&.), (.|.), shiftL, shiftR)
|
||||
import Data.Char (ord)
|
||||
|
||||
import Convert.Traverse
|
||||
import Language.SystemVerilog.AST
|
||||
|
|
@ -36,18 +38,13 @@ simplifyStep (UniOp LogNot (BinOp Ne a b)) = BinOp Eq a b
|
|||
simplifyStep (UniOp UniSub (UniOp UniSub e)) = e
|
||||
simplifyStep (UniOp UniSub (BinOp Sub e1 e2)) = BinOp Sub e2 e1
|
||||
|
||||
simplifyStep (Concat (Number n1 : Number n2 : rest)) =
|
||||
simplifyStep $ Concat $ Number n : rest
|
||||
where n = n1 <> n2
|
||||
|
||||
simplifyStep (Concat [Number (Decimal size _ value)]) =
|
||||
Number $ Decimal size False value
|
||||
simplifyStep (Concat [Number (Based size _ base value kinds)]) =
|
||||
Number $ Based size False base value kinds
|
||||
simplifyStep (Concat [e@Stream{}]) = e
|
||||
simplifyStep (Concat [e@Concat{}]) = e
|
||||
simplifyStep (Concat [e@Repeat{}]) = e
|
||||
simplifyStep (Concat es) = Concat $ filter (/= Concat []) es
|
||||
simplifyStep (Concat es) = Concat $ flattenConcat es
|
||||
simplifyStep (Repeat (Dec 0) _) = Concat []
|
||||
simplifyStep (Repeat (Dec 1) es) = Concat es
|
||||
simplifyStep (Mux (Number n) e1 e2) =
|
||||
|
|
@ -73,6 +70,16 @@ simplifyStep e@(BinOp _ (BinOp _ Number{} Number{}) Number{}) = e
|
|||
simplifyStep (BinOp op e1 e2) = simplifyBinOp op e1 e2
|
||||
simplifyStep other = other
|
||||
|
||||
-- flatten and coalesce concatenations
|
||||
flattenConcat :: [Expr] -> [Expr]
|
||||
flattenConcat (Number n1 : Number n2 : es) =
|
||||
flattenConcat $ Number (n1 <> n2) : es
|
||||
flattenConcat (Concat es1 : es2) =
|
||||
flattenConcat $ es1 ++ es2
|
||||
flattenConcat (e : es) =
|
||||
e : flattenConcat es
|
||||
flattenConcat [] = []
|
||||
|
||||
|
||||
simplifyBinOp :: BinOp -> Expr -> Expr -> Expr
|
||||
|
||||
|
|
@ -107,10 +114,50 @@ simplifyBinOp Add (BinOp Sub n1@Number{} e) n2@Number{} =
|
|||
BinOp Sub (BinOp Add n1 n2) e
|
||||
simplifyBinOp Ge (BinOp Sub e (Dec 1)) (Dec 0) = BinOp Ge e (toDec 1)
|
||||
|
||||
simplifyBinOp ShiftAL (Dec x) (Dec y) = toDec $ shiftL x (fromIntegral y)
|
||||
simplifyBinOp ShiftAR (Dec x) (Dec y) = toDec $ shiftR x (fromIntegral y)
|
||||
simplifyBinOp ShiftL (Dec x) (Dec y) = toDec $ shiftL x (fromIntegral y)
|
||||
simplifyBinOp ShiftR (Dec x) (Dec y) = toDec $ shiftR x (fromIntegral y)
|
||||
-- simplify bit shifts of decimal literals
|
||||
simplifyBinOp op (Dec x) (Number yRaw)
|
||||
| ShiftAL <- op = decShift shiftL
|
||||
| ShiftAR <- op = decShift shiftR
|
||||
| ShiftL <- op = decShift shiftL
|
||||
| ShiftR <- op = decShift shiftR
|
||||
where
|
||||
decShift shifter =
|
||||
case numberToInteger yRaw of
|
||||
Just y -> toDec $ shifter x (fromIntegral y)
|
||||
Nothing -> constantFold undefined Div undefined 0
|
||||
|
||||
-- simply comparisons with string literals
|
||||
simplifyBinOp op (Number n) (String s) | isCmpOp op =
|
||||
simplifyBinOp op (Number n) (sizeStringAs s n)
|
||||
simplifyBinOp op (String s) (Number n) | isCmpOp op =
|
||||
simplifyBinOp op (sizeStringAs s n) (Number n)
|
||||
simplifyBinOp op (String s1) (String s2) | isCmpOp op =
|
||||
simplifyBinOp op (stringToNumber s1) (stringToNumber s2)
|
||||
|
||||
-- simply basic arithmetic comparisons
|
||||
simplifyBinOp op (Number n1) (Number n2)
|
||||
| Eq <- op = cmp (==)
|
||||
| Ne <- op = cmp (/=)
|
||||
| Lt <- op = cmp (<)
|
||||
| Le <- op = cmp (<=)
|
||||
| Gt <- op = cmp (>)
|
||||
| Ge <- op = cmp (>=)
|
||||
where
|
||||
cmp :: (Integer -> Integer -> Bool) -> Expr
|
||||
cmp folder =
|
||||
case (numberToInteger n1', numberToInteger n2') of
|
||||
(Just i1, Just i2) -> bool $ folder i1 i2
|
||||
_ -> BinOp op (Number n1') (Number n2')
|
||||
sg = numberIsSigned n1 && numberIsSigned n2
|
||||
sz = fromIntegral $ max (numberBitLength n1) (numberBitLength n2)
|
||||
n1' = numberCast sg sz n1
|
||||
n2' = numberCast sg sz n2
|
||||
|
||||
-- simply comparisons with unbased unsized literals
|
||||
simplifyBinOp op (Number n) (ConvertedUU sz v k) | isCmpOp op =
|
||||
simplifyBinOp op (Number n) (uuExtend sz v k)
|
||||
simplifyBinOp op (ConvertedUU sz v k) (Number n) | isCmpOp op =
|
||||
simplifyBinOp op (uuExtend sz v k) (Number n)
|
||||
|
||||
simplifyBinOp op e1 e2 =
|
||||
case (e1, e2) of
|
||||
|
|
@ -142,6 +189,8 @@ constantFold _ Gt x y = bool $ x > y
|
|||
constantFold _ Ge x y = bool $ x >= y
|
||||
constantFold _ Lt x y = bool $ x < y
|
||||
constantFold _ Le x y = bool $ x <= y
|
||||
constantFold _ BitAnd x y = toDec $ x .&. y
|
||||
constantFold _ BitOr x y = toDec $ x .|. y
|
||||
constantFold fallback _ _ _ = fallback
|
||||
|
||||
|
||||
|
|
@ -224,3 +273,49 @@ pattern SizedRange expr = (BinOp Sub expr (RawNum 1), RawNum 0)
|
|||
-- similar to the above pattern, we assume E >= 1 for any range like [0:E-1]
|
||||
pattern RevSzRange :: Expr -> Range
|
||||
pattern RevSzRange expr = (RawNum 0, BinOp Sub expr (RawNum 1))
|
||||
|
||||
-- convert a string to decimal number
|
||||
stringToNumber :: String -> Expr
|
||||
stringToNumber str =
|
||||
Number $ Decimal size False value
|
||||
where
|
||||
size = 8 * length str
|
||||
value = stringToInteger str
|
||||
|
||||
-- convert a string to big integer
|
||||
stringToInteger :: String -> Integer
|
||||
stringToInteger [] = 0
|
||||
stringToInteger (x : xs) =
|
||||
fromIntegral (ord x) + (256 :: Integer) * stringToInteger xs
|
||||
|
||||
-- cast string to number at least as big as the width of the given number
|
||||
sizeStringAs :: String -> Number -> Expr
|
||||
sizeStringAs str num =
|
||||
Cast (Left typ) (stringToNumber str)
|
||||
where
|
||||
typ = IntegerVector TReg Unspecified [(RawNum size, RawNum 1)]
|
||||
size = max strSize numSize
|
||||
strSize = fromIntegral $ 8 * length str
|
||||
numSize = numberBitLength num
|
||||
|
||||
-- excludes wildcard and strict comparison operators
|
||||
isCmpOp :: BinOp -> Bool
|
||||
isCmpOp Eq = True
|
||||
isCmpOp Ne = True
|
||||
isCmpOp Lt = True
|
||||
isCmpOp Le = True
|
||||
isCmpOp Gt = True
|
||||
isCmpOp Ge = True
|
||||
isCmpOp _ = False
|
||||
|
||||
-- sign extend a converted unbased unsized literal into a based number
|
||||
uuExtend :: Integer -> Integer -> Integer -> Expr
|
||||
uuExtend sz v k =
|
||||
Number $
|
||||
numberCast False (fromIntegral sz) $
|
||||
Based 1 True Hex v k
|
||||
|
||||
pattern ConvertedUU :: Integer -> Integer -> Integer -> Expr
|
||||
pattern ConvertedUU sz v k <- Repeat
|
||||
(RawNum sz)
|
||||
[Number (Based 1 True Binary v k)]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
// This verifies that sv2v can evaluate certain constant expressions by
|
||||
// producing iverilog-incompatible code if the expression cannot be simplified
|
||||
// or is evaluated incorrectly.
|
||||
`define ASSERT_TRUE(expr) if (expr) begin end else begin shortreal x; end
|
||||
`define ASSERT_FALSE(expr) if (expr) begin shortreal x; end
|
||||
|
||||
module top;
|
||||
`ASSERT_TRUE(1)
|
||||
`ASSERT_FALSE(0)
|
||||
|
||||
`ASSERT_TRUE("inv" == "inv")
|
||||
`ASSERT_TRUE(32'("inv") == "inv")
|
||||
`ASSERT_TRUE("inv" == 32'("inv"))
|
||||
`ASSERT_TRUE(24'("inv") == "inv")
|
||||
`ASSERT_TRUE("inv" == 24'("inv"))
|
||||
|
||||
`ASSERT_FALSE("invv" == "inv")
|
||||
`ASSERT_FALSE("0inv" == "inv")
|
||||
`ASSERT_TRUE("invv" != "inv")
|
||||
`ASSERT_TRUE("0inv" != "inv")
|
||||
|
||||
`ASSERT_TRUE(24'("inv0") == "inv")
|
||||
`ASSERT_TRUE(24'("0inv") != "inv")
|
||||
`ASSERT_FALSE("inv" == 0)
|
||||
`ASSERT_FALSE("inv" == '0)
|
||||
`ASSERT_FALSE('0 == "inv")
|
||||
`ASSERT_FALSE("inv" == 1'b0)
|
||||
`ASSERT_FALSE("inv" == 2'd0)
|
||||
`ASSERT_FALSE("inv" == 1'sb0)
|
||||
|
||||
`ASSERT_TRUE(1'sb0 < 1'd1)
|
||||
`ASSERT_TRUE(1'sb0 <= 1'd0)
|
||||
`ASSERT_FALSE(1'sb0 > 1'd1)
|
||||
`ASSERT_TRUE(1'sb0 >= 1'd0)
|
||||
|
||||
`ASSERT_TRUE((1 | 2) == 3)
|
||||
`ASSERT_TRUE((13 & 7) == 5)
|
||||
|
||||
`ASSERT_TRUE((1 << 1) == 2)
|
||||
`ASSERT_TRUE((3 >> 1) == 1)
|
||||
`ASSERT_TRUE((1 <<< 1) == 2)
|
||||
`ASSERT_TRUE((3 >>> 1) == 1)
|
||||
|
||||
`ASSERT_TRUE(5'{4'hF, 3'{1'b1, 1'b1}} == 27)
|
||||
`ASSERT_TRUE(5'{{{1'b1, 1'b1}, {1'b1, 1'b1}}} == 15)
|
||||
endmodule
|
||||
|
|
@ -0,0 +1 @@
|
|||
module top; endmodule
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
// pattern: size cast width 'shxxxxxxxx is not an integer
|
||||
// location: size_cast_xpr_lit.sv:4:13
|
||||
module top;
|
||||
initial $display((1 << 'x)'(2));
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// pattern: size cast width 'shxxxxxxxx is not an integer
|
||||
// location: size_cast_xpr_var.sv:5:13
|
||||
module top;
|
||||
wire x = 0;
|
||||
initial $display((1 << 'x)'(x));
|
||||
endmodule
|
||||
Loading…
Reference in New Issue