Skip to content
Snippets Groups Projects

CCall: Extend to test signed arguments and results

Merged Ben Gamari requested to merge T3 into master
Files
2
+ 61
36
@@ -2,9 +2,9 @@
@@ -2,9 +2,9 @@
module CCall
module CCall
( CCallDesc(..)
( CCallDesc(..)
, testCCall
, testCCall
 
, evalCCall
) where
) where
import Numeric.Natural
import System.FilePath
import System.FilePath
import System.IO.Temp
import System.IO.Temp
import Test.QuickCheck
import Test.QuickCheck
@@ -17,7 +17,8 @@ import Number
@@ -17,7 +17,8 @@ import Number
data CCallDesc
data CCallDesc
= CCallDesc { callRet :: SomeNumber
= CCallDesc { callRet :: SomeNumber
, callArgs :: [SomeNumber]
, callRetSignedness :: Signedness
 
, callArgs :: [(Signedness, SomeNumber)]
}
}
deriving (Show)
deriving (Show)
@@ -30,30 +31,40 @@ mAX_ARGS = 32
@@ -30,30 +31,40 @@ mAX_ARGS = 32
instance Arbitrary CCallDesc where
instance Arbitrary CCallDesc where
arbitrary = do
arbitrary = do
ret <- arbitrary
ret <- arbitrary
 
ret_signedness <- arbitrary
n <- chooseInt (0, mAX_ARGS)
n <- chooseInt (0, mAX_ARGS)
args <- vectorOf n arbitrary
args <- vectorOf n arbitrary
return $ CCallDesc ret args
return $ CCallDesc ret ret_signedness args
 
shrink (CCallDesc ret ret_s args) =
 
CCallDesc <$> shrink ret <*> pure ret_s <*> shrinkList shrink args
testCCall
evalCCall
:: Compiler
:: Compiler
-> CCallDesc
-> CCallDesc
-> Property
-> IO [Integer]
testCCall comp c =
evalCCall comp c = withTempDirectory "." "tmp" $ \tmpDir -> do
ioProperty $ withTempDirectory "." "tmp" $ \tmpDir -> do
writeFile (tmpDir </> "test_c.c") (cStub c)
writeFile (tmpDir </> "test_c.c") (cStub c)
writeFile (tmpDir </> "test.cmm") (cCallCmm c)
writeFile (tmpDir </> "test.cmm") (cCallCmm c)
compile comp tmpDir ["test_c.c", "test.cmm"] soName ["-shared", "-dynamic"]
compile comp tmpDir ["test_c.c", "test.cmm"] soName ["-shared", "-dynamic"]
out <- runIt comp (tmpDir </> soName)
out <- runIt comp (tmpDir </> soName)
let saw :: [Integer]
let saw :: [Natural]
saw = map read (lines out)
saw = map read (lines out)
return saw
expected :: [Natural]
expected = map (\(SomeNumber e) -> toUnsigned e) (callArgs c) ++ [ret]
ret = case callRet c of SomeNumber n -> toUnsigned n
return $ saw === expected
where
where
soName = "test.so"
soName = "test.so"
 
testCCall
 
:: Compiler
 
-> CCallDesc
 
-> Property
 
testCCall comp c = ioProperty $ do
 
saw <- evalCCall comp c
 
let expected :: [Integer]
 
expected = map (\(s, SomeNumber e) -> asInteger s e) (callArgs c) ++ [ret]
 
-- The wrapper zero extends the result so interpret it as unsigned.
 
ret = case callRet c of SomeNumber n -> asInteger Unsigned n
 
return $ saw === expected
 
cStub :: CCallDesc -> String
cStub :: CCallDesc -> String
cStub c
cStub c
= unlines
= unlines
@@ -65,11 +76,14 @@ cStub c
@@ -65,11 +76,14 @@ cStub c
]
]
where
where
argBndrs = [ "arg"++show i | (i,_) <- zip [0::Int ..] (callArgs c) ]
argBndrs = [ "arg"++show i | (i,_) <- zip [0::Int ..] (callArgs c) ]
argWidths = [ knownWidth @w | SomeNumber (_ :: Number w) <- callArgs c ]
argTypes =
 
[ (signedness, knownWidth @w)
 
| (signedness, SomeNumber (_ :: Number w)) <- callArgs c
 
]
funcDef = unlines $
funcDef = unlines $
[ cType (retWidth c) <> " test_c(" <> argList <> ") {" ] ++
[ cType Unsigned (retWidth c) <> " test_c(" <> argList <> ") {" ] ++
zipWith printArg argWidths argBndrs ++
zipWith printArg argTypes argBndrs ++
[ " fflush(stdout);"
[ " fflush(stdout);"
, " return " ++ show (someNumberToUnsigned $ callRet c) ++ "ULL;"
, " return " ++ show (someNumberToUnsigned $ callRet c) ++ "ULL;"
, "}"
, "}"
@@ -77,39 +91,50 @@ cStub c
@@ -77,39 +91,50 @@ cStub c
argList =
argList =
commaList
commaList
[ unwords [cType w, bndr]
[ unwords [ty, bndr]
| (w, bndr) <- zip argWidths argBndrs
| (ty, bndr) <- zip (map (uncurry cType) argTypes) argBndrs
]
]
printArg w bndr =
printArg ty bndr =
" printf(" ++ quoted (formatStr w ++ "\\n") ++ ", " ++ bndr ++ ");"
" printf(" ++ quoted (formatStr ty ++ "\\n") ++ ", " ++ bndr ++ ");"
quoted :: String -> String
quoted :: String -> String
quoted s = "\"" ++ s ++ "\""
quoted s = "\"" ++ s ++ "\""
formatStr :: Width -> String
formatStr :: (Signedness, Width) -> String
formatStr w =
formatStr (signedness, w) =
"0x%" ++ quoted ("PRIx"++show n)
"%" ++ quoted ("PRI" ++ fmt ++ show n)
where
where
 
fmt = case signedness of
 
Signed -> "d"
 
Unsigned -> "u"
n = widthBits w
n = widthBits w
cType :: Width -> String
cType :: Signedness -> Width -> String
cType W8 = "uint8_t"
cType signedness width = prefix ++ "int" ++ show n ++ "_t"
cType W16 = "uint16_t"
where
cType W32 = "uint32_t"
n = widthBits width
cType W64 = "uint64_t"
prefix = case signedness of
 
Signed -> ""
 
Unsigned -> "u"
cCallCmm :: CCallDesc -> String
cCallCmm :: CCallDesc -> String
cCallCmm c = unlines
cCallCmm c = unlines
[ "test("++cmmWordType ++" buffer) {"
[ "test("++cmmWordType ++" buffer) {"
, " "++cmmType (retWidth c)++" ret;"
, " "++cmmType (retWidth c)++" ret;"
, " (ret) = foreign \"C\" test_c(" ++ argList ++ ");"
, " (" ++ retHint ++ "ret) = foreign \"C\" test_c(" ++ argList ++ ");"
, " return ("++widenOp++"(ret));"
, " return ("++widenOp++"(ret));"
, "}"
, "}"
]
]
where
where
 
retHint = case callRetSignedness c of
 
Signed -> "\"signed\" "
 
Unsigned -> ""
widenOp = "%zx" ++ show (widthBits wordSize)
widenOp = "%zx" ++ show (widthBits wordSize)
argList =
argList =
commaList
commaList
[ exprToCmm $ ELit e
[ exprToCmm (ELit e) ++ hint
| SomeNumber e <- callArgs c
| (signedness, SomeNumber e) <- callArgs c
 
, let hint = case signedness of
 
Signed -> " \"signed\""
 
Unsigned -> ""
]
]
Loading