packages feed

synthesizer-llvm-0.8: src/Synthesizer/LLVM/Alloc.hs

{- |
Provide allocation functions with correct alignment.
GHC's functions are wrong until at least GHC-6.12.3.
-}
module Synthesizer.LLVM.Alloc (
   malloc, free,
   mallocArray, freeArray,
   F.alloca, F.with,
   ) where
{- reexport alloca and with, since they work correctly in GHC-6.12.3 -}

import qualified Foreign as F
import Foreign.Storable.FixedArray (sizeOfArray, roundUp, )
import Foreign.Storable (Storable, sizeOf, alignment, )
import Foreign.Ptr (Ptr, minusPtr, plusPtr, nullPtr, )

import qualified Synthesizer.LLVM.Debug.Storable as Debug


{-
diffClip :: Int -> Int -> Int
-- this implementation also works for unsigned types
diffClip x y = x - min x y
-- diffClip x y = max 0 (x-y)
-}

defaultMallocAlign :: Int
defaultMallocAlign = 8


seekPointer :: Int -> Ptr a -> Ptr b
seekPointer size ptr =
   alignPointer (alignment nullPtr) (plusPtr ptr size)

alignPointer :: Int -> Ptr a -> Ptr b
alignPointer align ptr =
   plusPtr nullPtr $
   roundUp align $
   minusPtr ptr nullPtr

{- |
> mallocAligned align size
-}
mallocAligned :: Int -> Int -> IO (Ptr a)
mallocAligned align size =
   if mod defaultMallocAlign align == 0
     then F.mallocBytes size
     else do
        let {- pessimistic but safe -}
            ptrOffset = size + alignment nullPtr
            {- This should be optimal and I think it is also correct,
               but better safe than sorry.
            common = gcd align (alignment nullPtr)
            ptrOffset = roundUp common size + alignment nullPtr - common
            -}
            padSize = align - gcd defaultMallocAlign align
        allocPtr <- F.mallocBytes (padSize + ptrOffset + sizeOf nullPtr)
        let alignedPtr = alignPointer align allocPtr
        F.poke (seekPointer size alignedPtr) allocPtr
        return alignedPtr

{- |
> mallocBytes align size
-}
mallocBytes :: Storable a => a -> Int -> IO (Ptr a)
mallocBytes a size =
   Debug.traceMalloc a size =<< mallocAligned (alignment a) size

peekMock :: Ptr a -> a
peekMock _ = error "auxiliary object for free functions"

freeAligned :: Int -> Int -> Ptr a -> IO ()
freeAligned align size ptr =
   F.free =<<
   if mod defaultMallocAlign align == 0
     then return ptr
     else F.peek $ seekPointer size ptr

freeBytes :: Storable a => a -> Int -> Ptr a -> IO ()
freeBytes a size ptr = freeAligned (alignment a) size ptr

malloc :: (Storable a) => IO (Ptr a)
malloc =
   case error "auxiliary object for LLVM.Alloc.malloc" of
      mock -> mallocBytes mock (sizeOf mock)

free :: (Storable a) => Ptr a -> IO ()
free ptr =
   freeBytes (peekMock ptr) (sizeOf (peekMock ptr)) ptr


mallocArray :: (Storable a) => Int -> IO (Ptr a)
mallocArray n =
   case error "auxiliary object for LLVM.Alloc.mallocArray" of
      mock -> mallocBytes mock (sizeOfArray n mock)

freeArray :: (Storable a) => Int -> Ptr a -> IO ()
freeArray n ptr =
   freeBytes (peekMock ptr) (sizeOfArray n (peekMock ptr)) ptr