x86-64bit 0.1.3 → 0.1.4
raw patch · 7 files changed
+279/−222 lines, 7 files
Files
- CHANGELOG.md +7/−0
- CodeGen/X86.hs +0/−4
- CodeGen/X86/Asm.hs +5/−1
- CodeGen/X86/CallConv.hs +112/−95
- CodeGen/X86/FFI.hs +152/−119
- CodeGen/X86/Utils.hs +2/−2
- x86-64bit.cabal +1/−1
CHANGELOG.md view
@@ -1,3 +1,10 @@+# Version 0.1.4 + +- OS X operating system support (Balázs Kőműves) +- better show for db +- bugfix + - save flags in traceReg + # Version 0.1.3 - jmpq instruction support (George Stelle)
CodeGen/X86.hs view
@@ -1,4 +1,3 @@-{-# language CPP #-} {-# language PatternSynonyms #-} module CodeGen.X86 ( @@ -58,9 +57,6 @@ , runTests , CString (..) , traceReg -#ifdef linux_HOST_OS - , memalign -#endif , printf ) where
CodeGen/X86/Asm.hs view
@@ -538,7 +538,11 @@ Std -> showOp0 "std" Align s -> codeLine $ ".align " ++ show s- Data (Bytes x) -> showOp "db" $ intercalate ", " (showByte <$> x) ++ " ; " ++ show (toEnum . fromIntegral <$> x :: String)+ Data (Bytes x)+ | 2 * length (filter isPrint x) > length x -> showOp "db" $ show (toEnum . fromIntegral <$> x :: String)+ | otherwise -> showOp "db" $ intercalate ", " (show <$> x)+ where+ isPrint c = c >= 32 && c <= 126 showOp0 s = codeLine s showOp s a = showOp0 $ s ++ " " ++ a
CodeGen/X86/CallConv.hs view
@@ -1,95 +1,112 @@- --- | Calling conventions. There are basically only two: System V (Linux, OSX, BSD) and Win64\/fastcall - -{-# language CPP #-} -{-# language BangPatterns #-} -{-# language DataKinds #-} -module CodeGen.X86.CallConv where - -import Foreign -import Data.Monoid -import CodeGen.X86.Asm - ------------------------------------------------------------------------------- - -#if defined (mingw32_HOST_OS) || defined (mingw64_HOST_OS) - --- On Win64 the caller have to reserve 32 byte "shadow space" on the stack (and clean up after) -callFun :: Operand S64 RW -> FunPtr a -> Code -callFun r p - = Sub rsp (imm 32) - <> Mov r (imm $ fromIntegral $ ptrToIntPtr $ castFunPtrToPtr p) - <> Call r - <> Add rsp (imm 32) - -#else - --- helper to call a function -callFun :: Operand S64 RW -> FunPtr a -> Code -callFun r p = Mov r (imm $ fromIntegral $ ptrToIntPtr $ castFunPtrToPtr p) <> Call r - -#endif - ------------------------------------------------------------------------------- - --- | Save the non-volatile registers --- --- Note: R12..R15 should be preserved on both Windows and Linux. --- This is the responsability of the user (this won't save them). --- -saveNonVolatile :: Code -> Code -saveNonVolatile code = prologue <> code <> epilogue <> Ret - ------------------------------------------------------------------------------- --- calling conventions - -#if defined (mingw32_HOST_OS) || defined (mingw64_HOST_OS) - ----------- Win64 calling convention ---------- - -arg1 = rcx -arg2 = rdx -arg3 = r8 -arg4 = r9 --- rest of the arguments on the stack - -result = rax - -prologue - = Push rbp - <> Push rbx - <> Push rdi - <> Push rsi - -epilogue - = Pop rsi - <> Pop rdi - <> Pop rbx - <> Pop rbp - -#elif defined (linux_HOST_OS) - ----------- System V calling convention ---------- - -arg1 = rdi -arg2 = rsi -arg3 = rdx -arg4 = rcx -arg5 = r8 -arg6 = r9 --- rest of the arguments on the stack - -result = rax - -prologue - = Push rbp - <> Push rbx - -epilogue - = Pop rbx - <> Pop rbp - -#endif - ------------------------------------------------------------------------------- - ++-- | Calling conventions. There are basically only two: System V (Linux, OSX, BSD) and Win64\/fastcall++{-# language CPP #-}+{-# language BangPatterns #-}+{-# language DataKinds #-}+module CodeGen.X86.CallConv where++import Foreign+import Data.Monoid+import CodeGen.X86.Asm++------------------------------------------------------------------------------ ++#if defined (mingw32_HOST_OS) || defined (mingw64_HOST_OS)++-- On Win64 the caller have to reserve 32 byte "shadow space" on the stack (and clean up after)+callFun :: Operand S64 RW -> FunPtr a -> Code+callFun r p + = Sub rsp (imm 32) + <> Mov r (imm $ fromIntegral $ ptrToIntPtr $ castFunPtrToPtr p) + <> Call r + <> Add rsp (imm 32)++#elif defined (darwin_HOST_OS)++-- OSX requires 16 byte alignment of the stack...+callFun :: Operand S64 RW -> FunPtr a -> Code+callFun r p + = Push r15 -- we will use r15 (non-volatile) to store old rsp+ <> Mov r15 (imm 15) -- 0xf+ <> Not r15 -- 0xffff ... fff0+ <> And r15 rsp -- align rsp into r15+ <> Xchg r15 rsp -- new rsp = aligned, r15 = old rsp+ <> Mov r (imm $ fromIntegral $ ptrToIntPtr $ castFunPtrToPtr p) + <> Call r + <> Mov rsp r15 -- restore rsp+ <> Pop r15 -- restore r15++#else++-- helper to call a function+callFun :: Operand S64 RW -> FunPtr a -> Code+callFun r p + = Mov r (imm $ fromIntegral $ ptrToIntPtr $ castFunPtrToPtr p) + <> Call r++#endif++------------------------------------------------------------------------------ ++-- | Save the non-volatile registers +--+-- Note: R12..R15 should be preserved on both Windows and Linux.+-- This is the responsability of the user (this won't save them).+--+saveNonVolatile :: Code -> Code+saveNonVolatile code = prologue <> code <> epilogue <> Ret++------------------------------------------------------------------------------ +-- calling conventions++#if defined (mingw32_HOST_OS) || defined (mingw64_HOST_OS)++---------- Win64 calling convention ----------++arg1 = rcx+arg2 = rdx+arg3 = r8+arg4 = r9+-- rest of the arguments on the stack++result = rax++prologue + = Push rbp+ <> Push rbx+ <> Push rdi+ <> Push rsi++epilogue+ = Pop rsi+ <> Pop rdi+ <> Pop rbx+ <> Pop rbp ++#else++---------- System V calling convention ----------++arg1 = rdi+arg2 = rsi+arg3 = rdx+arg4 = rcx+arg5 = r8+arg6 = r9+-- rest of the arguments on the stack++result = rax++prologue + = Push rbp+ <> Push rbx++epilogue+ = Pop rbx+ <> Pop rbp ++#endif++------------------------------------------------------------------------------ +
CodeGen/X86/FFI.hs view
@@ -1,119 +1,152 @@-{-# language CPP #-} -{-# language ForeignFunctionInterface #-} -{-# language BangPatterns #-} -{-# language ViewPatterns #-} -{-# language FlexibleInstances #-} -module CodeGen.X86.FFI where - -------------------------------------------------------- - -import Control.Monad -import Control.Exception (evaluate) -import Foreign -import Foreign.C.Types -import Foreign.ForeignPtr -import Foreign.ForeignPtr.Unsafe -import System.IO.Unsafe - -import CodeGen.X86.Asm -import CodeGen.X86.CodeGen - -------------------------------------------------------- - -#define PAGE_SIZE 4096 - -#if defined (mingw32_HOST_OS) || defined (mingw64_HOST_OS) - -import System.Win32.Types -import System.Win32.Mem -import Foreign.Marshal.Alloc - -#endif - -------------------------------------------------------- - -foreign import ccall "dynamic" callWord64 :: FunPtr Word64 -> Word64 -foreign import ccall "dynamic" callWord32 :: FunPtr Word32 -> Word32 -foreign import ccall "dynamic" callWord16 :: FunPtr Word16 -> Word16 -foreign import ccall "dynamic" callWord8 :: FunPtr Word8 -> Word8 -foreign import ccall "dynamic" callBool :: FunPtr Bool -> Bool -foreign import ccall "dynamic" callIOUnit :: FunPtr (IO ()) -> IO () -foreign import ccall "dynamic" callWord64_Word64 :: FunPtr (Word64 -> Word64) -> Word64 -> Word64 -foreign import ccall "dynamic" callPtr_Word64 :: FunPtr (Ptr a -> Word64) -> Ptr a -> Word64 - -unsafeCallForeignPtr0 call p = unsafePerformIO $ evaluate (call (castPtrToFunPtr $ unsafeForeignPtrToPtr p)) <* touchForeignPtr p - -unsafeCallForeignPtr1 call p a = unsafePerformIO $ evaluate (call (castPtrToFunPtr $ unsafeForeignPtrToPtr p) a) <* touchForeignPtr p - -unsafeCallForeignPtrIO0 call p = call (castPtrToFunPtr $ unsafeForeignPtrToPtr p) <* touchForeignPtr p - - -class Callable a where unsafeCallForeignPtr :: ForeignPtr a -> a - -instance Callable Word64 where unsafeCallForeignPtr = unsafeCallForeignPtr0 callWord64 -instance Callable Word32 where unsafeCallForeignPtr = unsafeCallForeignPtr0 callWord32 -instance Callable Word16 where unsafeCallForeignPtr = unsafeCallForeignPtr0 callWord16 -instance Callable Word8 where unsafeCallForeignPtr = unsafeCallForeignPtr0 callWord8 -instance Callable Bool where unsafeCallForeignPtr = unsafeCallForeignPtr0 callBool -instance Callable (IO ()) where unsafeCallForeignPtr = unsafeCallForeignPtrIO0 callIOUnit -instance Callable (Word64 -> Word64) where unsafeCallForeignPtr = unsafeCallForeignPtr1 callWord64_Word64 -instance Callable (Ptr a -> Word64) where unsafeCallForeignPtr = unsafeCallForeignPtr1 callPtr_Word64 - -------------------------------------------------------- - -#if defined (mingw32_HOST_OS) || defined (mingw64_HOST_OS) --- note: GHC 64 bit also defines mingw32 ... - -foreign import ccall "static malloc.h _aligned_malloc" c_aligned_malloc :: CSize -> CSize -> IO (Ptr a) -foreign import ccall "static malloc.h _aligned_free" c_aligned_free :: Ptr a -> IO () -foreign import ccall "static malloc.h &_aligned_free" ptr_aligned_free :: FunPtr (Ptr a -> IO ()) - -#elif defined linux_HOST_OS - -foreign import ccall "static stdlib.h memalign" memalign :: CUInt -> CUInt -> IO (Ptr a) -foreign import ccall "static stdlib.h &free" stdfree :: FunPtr (Ptr a -> IO ()) -foreign import ccall "static sys/mman.h mprotect" mprotect :: Ptr a -> CUInt -> Int -> IO Int - -#endif - -------------------------------------------------------- - -#if defined (mingw32_HOST_OS) || defined (mingw64_HOST_OS) - -flag_PAGE_EXECUTE_READWRITE :: Word32 -flag_PAGE_EXECUTE_READWRITE = 0x40 - -{-# NOINLINE compile #-} -compile :: Callable a => Code -> a -compile x = unsafeCallForeignPtr $ unsafePerformIO $ do - let (bytes, fromIntegral -> size) = buildTheCode x - arr <- c_aligned_malloc (fromIntegral size) PAGE_SIZE - _ <- virtualProtect (castPtr arr) (fromIntegral size) flag_PAGE_EXECUTE_READWRITE - forM_ [p | Right p <- bytes] $ uncurry $ pokeByteOff arr - newForeignPtr ptr_aligned_free arr - -#elif defined linux_HOST_OS - -{-# NOINLINE compile #-} -compile :: Callable a => Code -> a -compile x = unsafeCallForeignPtr $ unsafePerformIO $ do - let (bytes, fromIntegral -> size) = buildTheCode x - arr <- memalign PAGE_SIZE size - _ <- mprotect arr size 0x7 -- READ, WRITE, EXEC - forM_ [p | Right p <- bytes] $ uncurry $ pokeByteOff arr - newForeignPtr stdfree arr - -#endif - -------------------------------------------------------- - -foreign import ccall "wrapper" createPtrWord64_Word64 :: (Word64 -> Word64) -> IO (FunPtr (Word64 -> Word64)) - -class CallableHs a where createHsPtr :: a -> IO (FunPtr a) - -instance CallableHs (Word64 -> Word64) where createHsPtr = createPtrWord64_Word64 - -hsPtr :: CallableHs a => a -> FunPtr a -hsPtr x = unsafePerformIO $ createHsPtr x - - +{-# language CPP #-}+{-# language ForeignFunctionInterface #-}+{-# language BangPatterns #-}+{-# language ViewPatterns #-}+{-# language FlexibleInstances #-}+module CodeGen.X86.FFI where++-------------------------------------------------------++import Control.Monad+import Control.Exception (evaluate)+import Foreign+import Foreign.C.Types+import Foreign.ForeignPtr+import Foreign.ForeignPtr.Unsafe+import System.IO.Unsafe++import CodeGen.X86.Asm+import CodeGen.X86.CodeGen++-------------------------------------------------------++-- this should be queried from the OS dynamically...+#define PAGE_SIZE 4096++#if defined (mingw32_HOST_OS) || defined (mingw64_HOST_OS) ++import System.Win32.Types+import System.Win32.Mem+import Foreign.Marshal.Alloc++#endif++-------------------------------------------------------++foreign import ccall "dynamic" callWord64 :: FunPtr Word64 -> Word64+foreign import ccall "dynamic" callWord32 :: FunPtr Word32 -> Word32+foreign import ccall "dynamic" callWord16 :: FunPtr Word16 -> Word16+foreign import ccall "dynamic" callWord8 :: FunPtr Word8 -> Word8+foreign import ccall "dynamic" callBool :: FunPtr Bool -> Bool+foreign import ccall "dynamic" callIOUnit :: FunPtr (IO ()) -> IO ()+foreign import ccall "dynamic" callWord64_Word64 :: FunPtr (Word64 -> Word64) -> Word64 -> Word64+foreign import ccall "dynamic" callPtr_Word64 :: FunPtr (Ptr a -> Word64) -> Ptr a -> Word64++unsafeCallForeignPtr0 call p = unsafePerformIO $ evaluate (call (castPtrToFunPtr $ unsafeForeignPtrToPtr p)) <* touchForeignPtr p++unsafeCallForeignPtr1 call p a = unsafePerformIO $ evaluate (call (castPtrToFunPtr $ unsafeForeignPtrToPtr p) a) <* touchForeignPtr p++unsafeCallForeignPtrIO0 call p = call (castPtrToFunPtr $ unsafeForeignPtrToPtr p) <* touchForeignPtr p+++class Callable a where unsafeCallForeignPtr :: ForeignPtr a -> a++instance Callable Word64 where unsafeCallForeignPtr = unsafeCallForeignPtr0 callWord64+instance Callable Word32 where unsafeCallForeignPtr = unsafeCallForeignPtr0 callWord32+instance Callable Word16 where unsafeCallForeignPtr = unsafeCallForeignPtr0 callWord16+instance Callable Word8 where unsafeCallForeignPtr = unsafeCallForeignPtr0 callWord8+instance Callable Bool where unsafeCallForeignPtr = unsafeCallForeignPtr0 callBool+instance Callable (IO ()) where unsafeCallForeignPtr = unsafeCallForeignPtrIO0 callIOUnit+instance Callable (Word64 -> Word64) where unsafeCallForeignPtr = unsafeCallForeignPtr1 callWord64_Word64+instance Callable (Ptr a -> Word64) where unsafeCallForeignPtr = unsafeCallForeignPtr1 callPtr_Word64++-------------------------------------------------------++#if defined (mingw32_HOST_OS) || defined (mingw64_HOST_OS) +-- note: GHC 64 bit also defines mingw32 ...++foreign import ccall "static malloc.h _aligned_malloc" c_aligned_malloc :: CSize -> CSize -> IO (Ptr a)+foreign import ccall "static malloc.h _aligned_free" c_aligned_free :: Ptr a -> IO ()+foreign import ccall "static malloc.h &_aligned_free" ptr_aligned_free :: FunPtr (Ptr a -> IO ())++#elif defined (linux_HOST_OS)++-- on Linux too, we should use posix_memalign...+foreign import ccall "static stdlib.h memalign" memalign :: CSize -> CSize -> IO (Ptr a)+foreign import ccall "static stdlib.h &free" stdfree :: FunPtr (Ptr a -> IO ())+foreign import ccall "static sys/mman.h mprotect" mprotect :: Ptr a -> CSize -> CInt -> IO CInt++#elif defined (darwin_HOST_OS) || defined (freebsd_HOST_OS) || defined (openbsd_HOST_OS) || defined (netbsd_HOST_OS) ++foreign import ccall "static stdlib.h posix_memalign" posix_memalign :: Ptr (Ptr a) -> CSize -> CSize -> IO CInt+foreign import ccall "static stdlib.h &free" stdfree :: FunPtr (Ptr a -> IO ())+foreign import ccall "static sys/mman.h mprotect" mprotect :: Ptr a -> CSize -> CInt -> IO CInt++#endif++-------------------------------------------------------++#if defined (mingw32_HOST_OS) || defined (mingw64_HOST_OS) ++flag_PAGE_EXECUTE_READWRITE :: Word32+flag_PAGE_EXECUTE_READWRITE = 0x40 ++{-# NOINLINE compile #-}+compile :: Callable a => Code -> a+compile x = unsafeCallForeignPtr $ unsafePerformIO $ do+ let (bytes, fromIntegral -> size) = buildTheCode x+ arr <- c_aligned_malloc (fromIntegral size) PAGE_SIZE+ _ <- virtualProtect (castPtr arr) (fromIntegral size) flag_PAGE_EXECUTE_READWRITE+ forM_ [p | Right p <- bytes] $ uncurry $ pokeByteOff arr + newForeignPtr ptr_aligned_free arr ++#elif defined linux_HOST_OS++{-# NOINLINE compile #-}+compile :: Callable a => Code -> a+compile x = unsafeCallForeignPtr $ unsafePerformIO $ do+ let (bytes, fromIntegral -> size) = buildTheCode x+ arr <- memalign PAGE_SIZE size+ _ <- mprotect arr size 0x7 -- READ, WRITE, EXEC+ forM_ [p | Right p <- bytes] $ uncurry $ pokeByteOff arr+ newForeignPtr stdfree arr++#elif defined (darwin_HOST_OS) || defined (freebsd_HOST_OS) || defined (openbsd_HOST_OS) || defined (netbsd_HOST_OS) ++-- | This calls @posix_memalign()@+posixMemAlign + :: CSize -- ^ alignment+ -> CSize -- ^ size+ -> IO (Ptr a)+posixMemAlign alignment size0 =+ alloca $ \pp -> do+ let a = max alignment 8+ size = mod (size0 + a - 1) a -- size *must* be a multiple of both alignment and sizeof(void*)+ res <- posix_memalign pp alignment size+ case res of+ 0 -> peek pp+ _ -> error "posix_memalign failed"+ +{-# NOINLINE compile #-}+compile :: Callable a => Code -> a+compile x = unsafeCallForeignPtr $ unsafePerformIO $ do+ let (bytes, fromIntegral -> size) = buildTheCode x+ arr <- posixMemAlign PAGE_SIZE size+ _ <- mprotect arr size 0x7 -- READ, WRITE, EXEC+ forM_ [p | Right p <- bytes] $ uncurry $ pokeByteOff arr+ newForeignPtr stdfree arr++#endif++-------------------------------------------------------++foreign import ccall "wrapper" createPtrWord64_Word64 :: (Word64 -> Word64) -> IO (FunPtr (Word64 -> Word64))++class CallableHs a where createHsPtr :: a -> IO (FunPtr a)++instance CallableHs (Word64 -> Word64) where createHsPtr = createPtrWord64_Word64++hsPtr :: CallableHs a => a -> FunPtr a+hsPtr x = unsafePerformIO $ createHsPtr x++
CodeGen/X86/Utils.hs view
@@ -74,9 +74,9 @@ traceReg :: IsSize s => String -> Operand s RW -> Code traceReg d r = - push_all + PushF <> push_all <> mov' arg2 r <> leaData arg1 (CString $ show r ++ " = %" ++ s ++ d ++ "\n") <> Xor rax rax <> callFun r11 printf - <> pop_all + <> pop_all <> PopF where s = case size r of S8 -> "hh"
x86-64bit.cabal view
@@ -1,5 +1,5 @@ name: x86-64bit -version: 0.1.3 +version: 0.1.4 homepage: https://github.com/divipp/x86-64 synopsis: Runtime code generation for x86 64 bit machine code description: The primary goal of x86-64bit is to provide a lightweight assembler for machine generated 64 bit x86 assembly instructions. See README.md for further details.