FFI for SPARC [was: FFI testers wanted]
Sven Panne
Sven_Panne@BetaResearch.de
Tue, 23 Jul 2002 13:21:24 +0200
This is a multi-part message in MIME format.
Since your mail reader does not understand
this format, some or all of this message may not be legible.
--------------EB38FE144339F44779FA94C4
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Using cut-n-paste from GHC's sources (esp. Adjustor.c) quite generously,
I've hacked together the missing FFI parts for SPARC. Attached is a
torture test, too, which is again based on GHC stuff (ffi009.hs).
Cheers,
S.
--------------EB38FE144339F44779FA94C4
Content-Type: text/plain; charset=us-ascii;
name="sparc_ffi.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="sparc_ffi.patch"
diff -r -u hugs98-ffi-09072002c.orig/src/builtin.c hugs98-ffi-09072002c/src/builtin.c
--- hugs98-ffi-09072002c.orig/src/builtin.c Tue Jul 9 13:05:26 2002
+++ hugs98-ffi-09072002c/src/builtin.c Tue Jul 23 13:04:30 2002
@@ -2239,6 +2239,8 @@
HugsStablePtr stable;
#if defined(__ppc__)
char code[13*4];
+#elif defined(__sparc__) && defined(__GNUC__)
+ char code[44];
#else
char code[16];
#endif
@@ -2324,6 +2326,65 @@
}
__asm__ volatile ("sync\n\tisync");
}
+ }
+#elif defined(__sparc__) && defined(__GNUC__)
+ /* Mostly cut-n-pasted from GHC's Adjustor.c:
+
+ <00>: 9C23A008 sub %sp, 8, %sp ! make room for %o4/%o5 in caller's frame
+ <04>: DA23A060 st %o5, [%sp + 96] ! shift registers by 2 positions
+ <08>: D823A05C st %o4, [%sp + 92]
+ <0C>: 9A10000B mov %o3, %o5
+ <10>: 9810000A mov %o2, %o4
+ <14>: 96100009 mov %o1, %o3
+ <18>: 94100008 mov %o0, %o2
+ <1C>: 13000000 sethi %hi(app), %o1 ! load up app (1 of 2)
+ <20>: 11000000 sethi %hi(s), %o0 ! load up s (1 of 2)
+ <24>: 81C26000 jmp %o1 + %lo(app) ! jump to app (load 2 of 2)
+ <28>: 90122000 or %o0, %lo(), %o0 ! load up s (2 of 2, delay slot)
+
+ ccall'ing on SPARC is easy, because we are quite lucky to push a
+ multiple of 8 bytes (1 word stable pointer + 1 word dummy arg) in front
+ of the existing arguments (note that %sp must stay double-word aligned at
+ all times, see ABI spec at http://www.sparc.org/standards/psABI3rd.pdf).
+ To do this, we extend the *caller's* stack frame by 2 words and shift
+ the output registers used for argument passing (%o0 - %o5, we are a
+ *leaf* procedure because of the tail-jump) by 2 positions. This makes
+ room in %o0 and %o1 for the additinal arguments, namely the stable
+ pointer and a dummy (used for destination addr of jump on SPARC). This
+ shouldn't cause any problems for a C-like caller: alloca is implemented
+ similarly, and local variables should be accessed via %fp, not %sp. In
+ a nutshell: This should work! (Famous last words! :-)
+ */
+ {
+ unsigned long *adj_code = (unsigned long *)pc;
+ adj_code[ 0] = 0x9C23A008UL; /* sub %sp, 8, %sp */
+ adj_code[ 1] = 0xDA23A060UL; /* st %o5, [%sp + 96] */
+ adj_code[ 2] = 0xD823A05CUL; /* st %o4, [%sp + 92] */
+ adj_code[ 3] = 0x9A10000BUL; /* mov %o3, %o5 */
+ adj_code[ 4] = 0x9810000AUL; /* mov %o2, %o4 */
+ adj_code[ 5] = 0x96100009UL; /* mov %o1, %o3 */
+ adj_code[ 6] = 0x94100008UL; /* mov %o0, %o2 */
+ adj_code[ 7] = 0x13000000UL; /* sethi %hi(app), %o1 */
+ adj_code[ 7] |= ((unsigned long)app) >> 10;
+ adj_code[ 8] = 0x11000000UL; /* sethi %hi(s), %o0 */
+ adj_code[ 8] |= ((unsigned long)s) >> 10;
+ adj_code[ 9] = 0x81C26000UL; /* jmp %o1 + %lo(app) */
+ adj_code[ 9] |= ((unsigned long)app) & 0x000003FFUL;
+ adj_code[10] = 0x90122000UL; /* or %o0, %lo(s), %o0 */
+ adj_code[10] |= ((unsigned long)s) & 0x000003FFUL;
+
+ /* flush cache */
+ asm("flush %0" : : "r" (adj_code ));
+ asm("flush %0" : : "r" (adj_code + 2));
+ asm("flush %0" : : "r" (adj_code + 4));
+ asm("flush %0" : : "r" (adj_code + 6));
+ asm("flush %0" : : "r" (adj_code + 10));
+
+ /* max. 5 instructions latency, and we need at >= 1 for returning */
+ asm("nop");
+ asm("nop");
+ asm("nop");
+ asm("nop");
}
#else
ERRMSG(0) "Foreign import wrapper is not supported on this architecture"
diff -r -u hugs98-ffi-09072002c.orig/src/ffi.c hugs98-ffi-09072002c/src/ffi.c
--- hugs98-ffi-09072002c.orig/src/ffi.c Sat Jul 6 12:52:00 2002
+++ hugs98-ffi-09072002c/src/ffi.c Tue Jul 23 11:54:05 2002
@@ -412,7 +412,13 @@
}
fprintf(out,"(");
if (extraArg) {
+#ifdef __sparc__
+ /* On SPARC we need an additional dummy argument due to stack alignment
+ restrictions, see the comment in mkThunk in builtin.c. */
+ fprintf(out,"HugsStablePtr fun1, void* unusedArg");
+#else
fprintf(out,"HugsStablePtr fun1");
+#endif
if (nonNull(argTys)) {
fprintf(out,", ");
}
--------------EB38FE144339F44779FA94C4
Content-Type: text/plain; charset=us-ascii;
name="ManyArgs.hs"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="ManyArgs.hs"
import Foreign
import Random
--------------------------------------------------------------------------------
foreign import ccall "dynamic" callFun5I :: FunPtr (Int -> Int -> Int -> Int -> Int -> Int) -> (Int -> Int -> Int -> Int -> Int -> Int)
foreign import ccall "wrapper" mkFun5I :: (Int -> Int -> Int -> Int -> Int -> Int) -> IO (FunPtr (Int -> Int -> Int -> Int -> Int -> Int))
manyArgs5I :: (Int -> Int -> Int -> Int -> Int -> Int)
manyArgs5I a1 a2 a3 a4 a5 = (((a1 * 31 + a2) * 31 + a3) * 31 + a4) * 31 + a5
test5I :: IO ()
test5I = do
a1 <- randomIO
a2 <- randomIO
a3 <- randomIO
a4 <- randomIO
a5 <- randomIO
funAddr <- mkFun5I manyArgs5I
print (callFun5I funAddr a1 a2 a3 a4 a5 ==
manyArgs5I a1 a2 a3 a4 a5)
freeHaskellFunPtr funAddr
--------------------------------------------------------------------------------
foreign import ccall "dynamic" callFun6D :: FunPtr (Double -> Double -> Double -> Double -> Double -> Double -> Double) -> (Double -> Double -> Double -> Double -> Double -> Double -> Double)
foreign import ccall "wrapper" mkFun6D :: (Double -> Double -> Double -> Double -> Double -> Double -> Double) -> IO (FunPtr (Double -> Double -> Double -> Double -> Double -> Double -> Double))
manyArgs6D :: Double -> Double -> Double -> Double -> Double -> Double -> Double
manyArgs6D a1 a2 a3 a4 a5 a6 =
((((a1 * 31 + a2) * 31 + a3) * 31 + a4) * 31 + a5) * 31 + a6
test6D :: IO ()
test6D = do
a1 <- randomIO
a2 <- randomIO
a3 <- randomIO
a4 <- randomIO
a5 <- randomIO
a6 <- randomIO
funAddr <- mkFun6D manyArgs6D
print (callFun6D funAddr a1 a2 a3 a4 a5 a6 ==
manyArgs6D a1 a2 a3 a4 a5 a6)
freeHaskellFunPtr funAddr
--------------------------------------------------------------------------------
foreign import ccall "dynamic" callFun11M :: FunPtr (Int -> Double -> Float -> Char -> Int -> Int -> Float -> Int -> Char -> Double -> Int -> Double) -> (Int -> Double -> Float -> Char -> Int -> Int -> Float -> Int -> Char -> Double -> Int -> Double)
foreign import ccall "wrapper" mkFun11M :: (Int -> Double -> Float -> Char -> Int -> Int -> Float -> Int -> Char -> Double -> Int -> Double) -> IO (FunPtr (Int -> Double -> Float -> Char -> Int -> Int -> Float -> Int -> Char -> Double -> Int -> Double))
manyArgs11M :: Int -> Double -> Float -> Char -> Int -> Int -> Float -> Int -> Char -> Double -> Int -> Double
manyArgs11M a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 =
(((((((((fromIntegral a1 * 31 + a2) * 31 +
realToFrac a3) * 31 + fromIntegral (fromEnum a4)) * 31 +
fromIntegral a5) * 31 + fromIntegral a6) * 31 +
realToFrac a7) * 31 + fromIntegral a8) * 31 +
fromIntegral (fromEnum a9)) * 31 + a10) * 31 +
fromIntegral a11
test11M :: IO ()
test11M = do
a1 <- randomIO
a2 <- randomIO
a3 <- randomIO
a4 <- randomIO
a5 <- randomIO
a6 <- randomIO
a7 <- randomIO
a8 <- randomIO
a9 <- randomIO
a10 <- randomIO
a11 <- randomIO
funAddr <- mkFun11M manyArgs11M
print (callFun11M funAddr a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 ==
manyArgs11M a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11)
freeHaskellFunPtr funAddr
--------------------------------------------------------------------------------
rep :: String -> IO () -> IO ()
rep msg tst = do
putStrLn ("Testing " ++ msg ++ "...")
sequence_ (replicate 10 tst)
main :: IO ()
main = do
setStdGen (mkStdGen 4711)
rep "5 Int arguments" test5I
rep "6 Double arguments" test6D
rep "11 mixed arguments" test11M
--------------EB38FE144339F44779FA94C4--