tock-mirror/backends/BackendPassesTest.hs
Adam Sampson 36e7353ee7 Take NameType out of NameDef.
NameType is only really needed in the parser, so this takes it out of
NameDef, meaning that later passes defining names no longer need to
set an arbitrary NameType for them. The parser gets slightly more
complicated (because some productions now have to return a SpecType
and a NameType too), but lots of other code gets simpler.

The code that removed free names was the only thing outside the parser
using NameType, and it now makes a more sensible decision based on the
SpecType. Since unscoped names previously didn't have a SpecType at
all, I've added an Unscoped constructor to it and arranged matters
such that unscoped names now get a proper entry in csNames.

Fixes #61.
2008-06-02 10:13:14 +00:00

423 lines
20 KiB
Haskell

{-
Tock: a compiler for parallel languages
Copyright (C) 2007 University of Kent
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
-}
-- #ignore-exports
-- | Currently contains tests just for the transformWaitFor pass that is run for the C backend.
module BackendPassesTest (qcTests) where
import Control.Monad.State
import Data.Generics
import qualified Data.Map as Map
import Test.HUnit hiding (State)
import Test.QuickCheck
import qualified AST as A
import BackendPasses
import CompState
import Metadata
import Pattern
import TagAST
import TestFramework
import TestUtils
import TreeUtils
import Types
import Utils
m :: Meta
m = emptyMeta
waitFor :: A.Expression -> A.Process -> A.Alternative
waitFor e body = A.Alternative emptyMeta (A.True emptyMeta) (A.Variable emptyMeta $ simpleName
(ghostVarPrefix ++ "raintimer" ++ ghostVarSuffix)) (A.InputTimerFor emptyMeta e)
body
waitUntil :: A.Expression -> A.Process -> A.Alternative
waitUntil e body = A.Alternative emptyMeta (A.True emptyMeta) (A.Variable emptyMeta $ simpleName
(ghostVarPrefix ++ "raintimer" ++ ghostVarSuffix)) (A.InputTimerAfter emptyMeta e)
body
mWaitUntil :: (Data a, Data b) => a -> b -> Pattern
mWaitUntil e body = mAlternative (A.True emptyMeta) (mVariable $ simpleName (ghostVarPrefix ++ "raintimer"
++ ghostVarSuffix)) (mInputTimerAfter e) body
mGetTime :: Pattern -> Pattern
mGetTime v = mInput (mVariable $ simpleName (ghostVarPrefix ++ "raintimer"
++ ghostVarSuffix)) (mInputTimerRead $ mInVariable v)
-- | Test WaitUntil guard (should be unchanged)
testTransformWaitFor0 :: Test
testTransformWaitFor0 = TestCase $ testPass "testTransformWaitFor0" orig (transformWaitFor orig) (return ())
where
orig = A.Alt m True $ A.Only m $ waitUntil (exprVariable "t") (A.Skip m)
-- | Test pulling out a single WaitFor:
testTransformWaitFor1 :: Test
testTransformWaitFor1 = TestCase $ testPass "testTransformWaitFor1" exp (transformWaitFor orig) (return ())
where
orig = A.Alt m True $ A.Only m $ waitFor (exprVariable "t") (A.Skip m)
exp = tag2 A.Seq DontCare $ mSpecP (tag3 A.Specification DontCare varName $ A.Declaration m A.Time) $
mSeveralP
[
mOnlyP $ mGetTime var
,mOnlyP $ tag3 A.Assign DontCare [var] $ tag2 A.ExpressionList DontCare [tag4 A.Dyadic DontCare A.Plus evar (exprVariablePattern "t")]
,mOnlyP $ tag3 A.Alt DontCare True $ mOnlyA $ mWaitUntil evar (A.Skip m)
]
varName = (tag2 A.Name DontCare $ Named "nowt" DontCare)
var = tag2 A.Variable DontCare varName
evar = tag2 A.ExprVariable DontCare var
-- | Test pulling out two WaitFors:
testTransformWaitFor2 :: Test
testTransformWaitFor2 = TestCase $ testPass "testTransformWaitFor2" exp (transformWaitFor orig) (return ())
where
orig = A.Alt m True $ A.Several m [A.Only m $ waitFor (exprVariable "t0") (A.Skip m),
A.Only m $ waitFor (exprVariable "t1") (A.Skip m)]
exp = tag2 A.Seq DontCare $ mSpecP (tag3 A.Specification DontCare varName0 $ A.Declaration m A.Time) $
mSpecP (tag3 A.Specification DontCare varName1 $ A.Declaration m A.Time) $
mSeveralP
[
mOnlyP $ mGetTime var0
,mOnlyP $ tag3 A.Assign DontCare [var0] $ tag2 A.ExpressionList DontCare [tag4 A.Dyadic DontCare A.Plus evar0 (exprVariablePattern "t0")]
,mOnlyP $ mGetTime var1
,mOnlyP $ tag3 A.Assign DontCare [var1] $ tag2 A.ExpressionList DontCare [tag4 A.Dyadic DontCare A.Plus evar1 (exprVariablePattern "t1")]
,mOnlyP $ tag3 A.Alt DontCare True $ mSeveralA
[mOnlyA $ mWaitUntil evar0 (A.Skip m)
,mOnlyA $ mWaitUntil evar1 (A.Skip m)]
]
varName0 = (tag2 A.Name DontCare $ Named "nowt0" DontCare)
var0 = tag2 A.Variable DontCare varName0
evar0 = tag2 A.ExprVariable DontCare var0
varName1 = (tag2 A.Name DontCare $ Named "nowt1" DontCare)
var1 = tag2 A.Variable DontCare varName1
evar1 = tag2 A.ExprVariable DontCare var1
-- | Test pulling out a single WaitFor with an expression:
testTransformWaitFor3 :: Test
testTransformWaitFor3 = TestCase $ testPass "testTransformWaitFor3" exp (transformWaitFor orig) (return ())
where
orig = A.Alt m True $ A.Only m $ waitFor (A.Dyadic m A.Plus (exprVariable "t0") (exprVariable "t1")) (A.Skip m)
exp = tag2 A.Seq DontCare $ mSpecP (tag3 A.Specification DontCare varName $ A.Declaration m A.Time) $
mSeveralP
[
mOnlyP $ mGetTime var
,mOnlyP $ tag3 A.Assign DontCare [var] $ tag2 A.ExpressionList DontCare [tag4 A.Dyadic DontCare A.Plus evar
(A.Dyadic m A.Plus (exprVariable "t0") (exprVariable "t1"))]
,mOnlyP $ tag3 A.Alt DontCare True $ mOnlyA $ mWaitUntil evar (A.Skip m)
]
varName = (tag2 A.Name DontCare $ Named "nowt" DontCare)
var = tag2 A.Variable DontCare varName
evar = tag2 A.ExprVariable DontCare var
-- | Test pulling out a single WaitFor with some slight nesting in the ALT:
testTransformWaitFor4 :: Test
testTransformWaitFor4 = TestCase $ testPass "testTransformWaitFor4" exp (transformWaitFor orig) (return ())
where
orig = A.Alt m True $ A.Several m [A.Only m $ waitFor (exprVariable "t") (A.Skip m)]
exp = tag2 A.Seq DontCare $ mSpecP (tag3 A.Specification DontCare varName $ A.Declaration m A.Time) $
mSeveralP
[
mOnlyP $ mGetTime var
,mOnlyP $ tag3 A.Assign DontCare [var] $ tag2 A.ExpressionList DontCare [tag4 A.Dyadic DontCare A.Plus evar (exprVariablePattern "t")]
,mOnlyP $ tag3 A.Alt DontCare True $ mSeveralA
[mOnlyA $ mWaitUntil evar (A.Skip m)]
]
varName = (tag2 A.Name DontCare $ Named "nowt" DontCare)
var = tag2 A.Variable DontCare varName
evar = tag2 A.ExprVariable DontCare var
-- | Test pulling out two WaitFors that use the same variable:
testTransformWaitFor5 :: Test
testTransformWaitFor5 = TestCase $ testPass "testTransformWaitFor5" exp (transformWaitFor orig) (return ())
where
orig = A.Alt m True $ A.Several m [A.Only m $ waitFor (exprVariable "t") (A.Skip m),
A.Only m $ waitFor (exprVariable "t") (A.Skip m)]
exp = tag2 A.Seq DontCare $ mSpecP (tag3 A.Specification DontCare varName0 $ A.Declaration m A.Time) $
mSpecP (tag3 A.Specification DontCare varName1 $ A.Declaration m A.Time) $
mSeveralP
[
mOnlyP $ mGetTime var0
,mOnlyP $ tag3 A.Assign DontCare [var0] $ tag2 A.ExpressionList DontCare [tag4 A.Dyadic DontCare A.Plus evar0 (exprVariablePattern "t")]
,mOnlyP $ mGetTime var1
,mOnlyP $ tag3 A.Assign DontCare [var1] $ tag2 A.ExpressionList DontCare [tag4 A.Dyadic DontCare A.Plus evar1 (exprVariablePattern "t")]
,mOnlyP $ tag3 A.Alt DontCare True $ mSeveralA
[mOnlyA $ mWaitUntil evar0 (A.Skip m)
,mOnlyA $ mWaitUntil evar1 (A.Skip m)]
]
varName0 = (tag2 A.Name DontCare $ Named "nowt0" DontCare)
var0 = tag2 A.Variable DontCare varName0
evar0 = tag2 A.ExprVariable DontCare var0
varName1 = (tag2 A.Name DontCare $ Named "nowt1" DontCare)
var1 = tag2 A.Variable DontCare varName1
evar1 = tag2 A.ExprVariable DontCare var1
newtype PosInts = PosInts [Int] deriving (Show)
instance Arbitrary PosInts where
arbitrary = do len <- choose (1, 10)
replicateM len (choose (1,1000)) >>* PosInts
newtype PosInt = PosInt Int deriving (Show)
instance Arbitrary PosInt where
arbitrary = choose (1,20) >>* PosInt
newtype StaticTypeList = StaticTypeList [A.Type] deriving (Show)
instance Arbitrary StaticTypeList where
arbitrary = do len <- choose (1,10)
tl <- replicateM len $ frequency
[ (10, return A.Int)
, (10, return A.Byte)
, (20, do len <- choose (1,5)
ns <- replicateM len $ choose (1,1000)
t <- oneof [return A.Int, return A.Byte]
return $ A.Array (map dimension ns) t)
]
return $ StaticTypeList tl
newtype DynTypeList = DynTypeList [A.Type] deriving (Show)
instance Arbitrary DynTypeList where
arbitrary = do len <- choose (1,10)
tl <- replicateM len $ frequency
[ (10, return A.Int)
, (10, return A.Byte)
, (20, do len <- choose (1,5)
ds <- replicateM len $ oneof
[choose (1,1000) >>* dimension
,return A.UnknownDimension]
t <- oneof [return A.Int, return A.Byte]
return $ A.Array ds t)
]
return $ DynTypeList tl
-- types of thing being abbreviated, types of abbreviation, subscripts
newtype AbbrevTypesIs = AbbrevTypesIs ([A.Dimension], [A.Dimension], [A.Subscript]) deriving (Show)
instance Arbitrary AbbrevTypesIs where
arbitrary = do lenSrc <- choose (1,10)
lenDest <- choose (1, lenSrc)
srcDims <- replicateM lenSrc $ oneof [return A.UnknownDimension, choose (1,1000) >>* dimension]
destDims <- flip mapM (take lenDest srcDims) $ \d ->
case d of
A.UnknownDimension -> return A.UnknownDimension
_ -> oneof [return d, return A.UnknownDimension]
subs <- replicateM (length srcDims - length destDims) $ return $ A.Subscript emptyMeta A.NoCheck (A.True emptyMeta)
return $ AbbrevTypesIs (srcDims, destDims, subs)
qcTestDeclareSizes :: [LabelledQuickCheckTest]
qcTestDeclareSizes =
[
("Test Adding _sizes For Declarations", scaleQC (20, 100, 500, 1000) (runQCTest . testFoo 0 . declFoo . \(PosInts xs) -> xs))
,("Test Adding _sizes For IsChannelArray", scaleQC (20, 100, 500, 1000) (runQCTest . testFoo 1 . isChanArrFoo . \(PosInt x) -> x))
,("Test Adding _sizes For RecordType", scaleQC (20, 100, 500, 1000) (runQCTest . testRecordFoo 2 . \(StaticTypeList ts) -> ts))
,("Test Adding _sizes For Is", scaleQC (20, 100, 500, 1000)
(\(AbbrevTypesIs dds@(_,dds',_)) -> A.UnknownDimension `elem` dds' ==> (runQCTest $ testFoo 3 $ isIsFoo dds)))
,("Test Adding _sizes For IsExpr (static)", scaleQC (20, 100, 500, 1000) (runQCTest . testFoo 4 . isExprStaticFoo . \(PosInts xs) -> xs))
--TODO add tests for dynamic IsExpr
--TODO test reshapes/retypes abbreviations (and add checks)
]
where
-- spectype of foo, spectype of foo_sizes
testFoo :: TestMonad m r => Int -> (A.SpecType, A.SpecType, State CompState ()) -> m ()
testFoo n (fooSpec, fooSizesSpec, st) = test n
(strFooSizes $ strFoo term)
(strFoo term) st (checkFooSizes fooSizesSpec)
where
strFoo :: Data a => A.Structured a -> A.Structured a
strFoo = A.Spec emptyMeta (A.Specification emptyMeta (simpleName "foo") fooSpec)
strFooSizes :: Data a => A.Structured a -> A.Structured a
strFooSizes = A.Spec emptyMeta (A.Specification emptyMeta (simpleName "foo_sizes") fooSizesSpec)
isChanArrFoo :: Int -> (A.SpecType, A.SpecType, State CompState ())
isChanArrFoo n = (A.IsChannelArray emptyMeta (A.Array [dimension n] $ A.Chan A.DirUnknown (A.ChanAttributes False False) A.Byte) (replicate n $ variable "c")
,valSize [makeConstant emptyMeta n], return ())
isIsFoo :: ([A.Dimension], [A.Dimension], [A.Subscript]) -> (A.SpecType, A.SpecType, State CompState ())
isIsFoo (srcDims, destDims, subs)
= (A.Is emptyMeta A.Abbrev (A.Array destDims A.Byte)
(foldr (A.SubscriptedVariable emptyMeta) (variable "src") subs)
,specSizes, defSrc)
where
specSizes = A.IsExpr emptyMeta A.ValAbbrev (A.Array [dimension $ length destDims] A.Int) $
A.ExprVariable m $
A.SubscriptedVariable emptyMeta (A.SubscriptFromFor emptyMeta
A.NoCheck
(intLiteral $ toInteger $ length srcDims - length destDims)
(intLiteral $ toInteger $ length destDims)
) (variable "src_sizes")
defSrc = do defineTestName "src" (A.Declaration emptyMeta (A.Array srcDims A.Byte)) A.Original
defineTestName "src_sizes" (A.IsExpr emptyMeta A.ValAbbrev (A.Array srcDims A.Byte) dummyExpr) A.ValAbbrev
dummyExpr = A.True emptyMeta
testRecordFoo :: forall m r. TestMonad m r => Int -> [A.Type] -> m ()
-- Give fields arbitrary names (for testing), then check that all ones that are array types
-- do get _sizes array (concat of array name, field name and _sizes)
testRecordFoo n ts = test n
(declRecord fields $ flip (foldr declSizeItems) (reverse fields) term)
(declRecord fields term) (return ()) (sequence_ . flip applyAll (map checkSizeItems fields))
where
fields = (zip ["x_" ++ show n | n <- [(0::Integer)..]] ts)
declRecord :: Data a => [(String, A.Type)] -> A.Structured a -> A.Structured a
declRecord fields = A.Spec emptyMeta (A.Specification emptyMeta (simpleName "foo") fooSpec)
where
fooSpec = A.RecordType emptyMeta False (map (\(n,t) -> (simpleName n, t)) fields)
declSizeItems :: Data a => (String, A.Type) -> A.Structured a -> A.Structured a
declSizeItems (n, A.Array ds _) = A.Spec emptyMeta (A.Specification emptyMeta (simpleName $ "foo" ++ n) $
valSize $ map (\(A.Dimension n) -> n) ds)
declSizeItems _ = id
checkSizeItems :: (String, A.Type) -> CompState -> m ()
checkSizeItems (n, A.Array ds _) = checkName ("foo" ++ n) (valSize $ map (\(A.Dimension n) -> n) ds) A.ValAbbrev
checkSizeItems _ = const (return ())
isExprStaticFoo :: [Int] -> (A.SpecType, A.SpecType, State CompState ())
isExprStaticFoo ns = (A.IsExpr emptyMeta A.ValAbbrev t (A.True emptyMeta), valSize (map (makeConstant emptyMeta) ns), return ())
where
t = A.Array (map dimension ns) A.Byte
declFoo :: [Int] -> (A.SpecType, A.SpecType, State CompState ())
declFoo ns = (A.Declaration emptyMeta t, valSize (map (makeConstant emptyMeta) ns), return ())
where
t = A.Array (map dimension ns) A.Byte
valSize :: [A.Expression] -> A.SpecType
valSize ds = A.IsExpr emptyMeta A.ValAbbrev (A.Array [dimension $ length ds] A.Int) $ makeSizesLiteral ds
makeSizesLiteral :: [A.Expression] -> A.Expression
makeSizesLiteral xs = A.Literal emptyMeta (A.Array [dimension $ length xs] A.Int) $ A.ArrayLiteral emptyMeta $
map A.ArrayElemExpr xs
checkFooSizes :: TestMonad m r => A.SpecType -> CompState -> m ()
checkFooSizes sp = checkName "foo_sizes" sp A.ValAbbrev
term = A.Only emptyMeta ()
test :: TestMonad m r => Int -> A.Structured () -> A.Structured () -> State CompState () -> (CompState -> m ()) -> m ()
test n exp inp st chk = testPassWithStateCheck label exp (declareSizesArray inp) st chk
where
label = "testDeclareSizes " ++ show n
defineTestName :: String -> A.SpecType -> A.AbbrevMode -> State CompState ()
defineTestName n sp am
= defineName (simpleName n) $ A.NameDef {
A.ndMeta = emptyMeta
,A.ndName = n
,A.ndOrigName = n
,A.ndSpecType = sp
,A.ndAbbrevMode = am
,A.ndPlacement = A.Unplaced
}
checkName :: TestMonad m r => String -> A.SpecType -> A.AbbrevMode -> CompState -> m ()
checkName n spec am cs
= do nd <- case Map.lookup n (csNames cs) of
Just nd -> return nd
Nothing -> testFailure ("Could not find " ++ n) >> return undefined
testEqual "ndName" n (A.ndName nd)
testEqual "ndOrigName" n (A.ndOrigName nd)
testEqual "ndSpecType" spec (A.ndSpecType nd)
testEqual "ndAbbrevMode" am (A.ndAbbrevMode nd)
qcTestSizeParameters :: [LabelledQuickCheckTest]
qcTestSizeParameters =
[
("Test Adding _sizes parameters to PROC formals (static)", scaleQC (20, 100, 500, 1000) (runQCTest . testFormal . \(StaticTypeList ts) -> ts))
,("Test Adding _sizes parameters to PROC actuals (static)", scaleQC (20, 100, 500, 1000) (runQCTest . testActual . \(StaticTypeList ts) -> ts))
,("Test Adding _sizes parameters to PROC formals (dynamic)", scaleQC (20, 100, 500, 1000) (runQCTest . testFormal . \(DynTypeList ts) -> ts))
,("Test Adding _sizes parameters to PROC actuals (dynamic)", scaleQC (20, 100, 500, 1000) (runQCTest . testActual . \(DynTypeList ts) -> ts))
]
where
-- TODO need to test both with dynamically sized arrays
testActual :: TestMonad m r => [A.Type] -> m ()
testActual ts = testPassWithStateCheck "qcTestSizeParameters Actual"
(procCall "p" argsWithSizes)
(addSizesActualParameters $ procCall "p" args)
(do recordProcDef args
recordProcFormals args)
(const $ return ())
where
args = [("x" ++ show n, t, A.Abbrev) | (n, t) <- zip [(0::Integer)..] ts]
argsWithSizes = concat [
case t of
(A.Array ds _) -> [("x" ++ show n, t, A.Abbrev), ("x" ++ show n ++ "_sizes", A.Array [dimension $ length ds] A.Int, A.ValAbbrev)]
_ -> [("x" ++ show n, t, A.Abbrev)]
| (n, t) <- zip [(0::Integer)..] ts]
testFormal :: TestMonad m r => [A.Type] -> m ()
testFormal ts = testPassWithStateCheck "qcTestSizeParameters Formal"
(wrapSpec "p" $ makeProcDef argsWithSizes)
(addSizesFormalParameters $ wrapSpec "p" $ makeProcDef args)
(do recordProcDef args
recordProcFormals args)
(\x -> do checkProcDef argsWithSizes x
checkProcFormals argsWithSizes x)
where
args = [("x" ++ show n, t, A.Abbrev) | (n, t) <- zip [(0::Integer)..] ts]
argsWithSizes = concat [
case t of
(A.Array ds _) -> [("x" ++ show n, t, A.Abbrev), ("x" ++ show n ++ "_sizes", A.Array [dimension $ length ds] A.Int, A.ValAbbrev)]
_ -> [("x" ++ show n, t, A.Abbrev)]
| (n, t) <- zip [(0::Integer)..] ts]
makeProcDef :: [(String, A.Type, A.AbbrevMode)] -> A.SpecType
makeProcDef nts = A.Proc emptyMeta A.PlainSpec [A.Formal am t (simpleName n) | (n, t, am) <- nts] (A.Skip emptyMeta)
recordProcDef :: [(String, A.Type, A.AbbrevMode)] -> State CompState ()
recordProcDef nts = defineTestName "p" (makeProcDef nts) A.Original
recordProcFormals :: [(String, A.Type, A.AbbrevMode)] -> State CompState ()
recordProcFormals = mapM_ rec
where
rec :: (String, A.Type, A.AbbrevMode) -> State CompState ()
rec (n, t, am) = defineTestName n (A.Declaration emptyMeta t) am
checkProcDef :: TestMonad m r => [(String, A.Type, A.AbbrevMode)] -> CompState -> m ()
checkProcDef nts cs = checkName "p" (makeProcDef nts) A.Original cs
checkProcFormals :: TestMonad m r => [(String, A.Type, A.AbbrevMode)] -> CompState -> m ()
checkProcFormals nts cs = mapM_ (\(n,t,am) -> checkName n (A.Declaration emptyMeta t) am cs) nts
wrapSpec :: String -> A.SpecType -> A.Structured ()
wrapSpec n spec = A.Spec emptyMeta (A.Specification emptyMeta (simpleName n) spec) (A.Only emptyMeta ())
procCall :: String -> [(String, A.Type, A.AbbrevMode)] -> A.Process
procCall p nts = A.ProcCall emptyMeta (simpleName p) [A.ActualVariable (variable n) | (n, _, _) <- nts]
---Returns the list of tests:
qcTests :: (Test, [LabelledQuickCheckTest])
qcTests = (TestLabel "BackendPassesTest" $ TestList
[
testTransformWaitFor0
,testTransformWaitFor1
,testTransformWaitFor2
,testTransformWaitFor3
,testTransformWaitFor4
,testTransformWaitFor5
]
,qcTestDeclareSizes ++ qcTestSizeParameters)