{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ForeignFunctionInterface #-}
module LLVM.Core.Instructions(
    -- * ADT representation of IR
    BinOpDesc(..), InstrDesc(..), ArgDesc(..), getInstrDesc,
    -- * Terminator instructions
    ret,
    condBr,
    br,
    switch,
    invoke, invokeWithConv,
    invokeFromFunction, invokeWithConvFromFunction,
    unreachable,
    -- * Arithmetic binary operations
    -- | Arithmetic operations with the normal semantics.
    -- The u instructions are unsigned, the s instructions are signed.
    add, sub, mul, neg,
    iadd, isub, imul, ineg,
    iaddNoWrap, isubNoWrap, imulNoWrap, inegNoWrap,
    fadd, fsub, fmul, fneg,
    idiv, irem,
    udiv, sdiv, fdiv, urem, srem, frem,
    -- * Logical binary operations
    -- |Logical instructions with the normal semantics.
    shl, shr, lshr, ashr, and, or, xor, inv,
    -- * Vector operations
    extractelement,
    insertelement,
    shufflevector,
    -- * Aggregate operation
    extractvalue,
    insertvalue,
    -- * Memory access
    malloc, arrayMalloc,
    alloca, arrayAlloca,
    free,
    load,
    store,
    getElementPtr, getElementPtr0,
    -- * Conversions
    ValueCons,
    trunc, zext, sext, ext, zadapt, sadapt, adapt,
    fptrunc, fpext,
    fptoui, fptosi, fptoint,
    uitofp, sitofp, inttofp,
    ptrtoint, inttoptr,
    bitcast,
    -- * Comparison
    CmpPredicate(..), IntPredicate(..), FPPredicate(..),
    CmpRet, CmpResult, CmpValueResult,
    cmp, pcmp, icmp, fcmp,
    select,
    -- * Fast math
    setHasNoNaNs,
    setHasNoInfs,
    setHasNoSignedZeros,
    setHasAllowReciprocal,
    setFastMath,
    -- * Other
    phi, addPhiInputs,
    call, callWithConv,
    callFromFunction, callWithConvFromFunction,
    Call, applyCall, runCall,

    -- * Classes and types
    ValueCons2, BinOpValue,
    Terminate, Ret, CallArgs, CodeGen.IsConst,
    CodeGen.FunctionArgs, CodeGen.FunctionCodeGen, CodeGen.FunctionResult,
    AllocArg,
    GetElementPtr, ElementPtrType, IsIndexArg, IsIndexType,
    GetValue, ValueType,
    GetField, FieldType,
    ) where

import qualified LLVM.Core.Util as U
import qualified LLVM.Util.Proxy as LP
import qualified LLVM.Core.CodeGen as CodeGen
import LLVM.Core.Instructions.Private
            (ValueCons, unValue, convert, unop,
             FFIBinOp, FFIConstBinOp,
             GetField, FieldType, GetElementPtr, ElementPtrType,
             IsIndexArg, IsIndexType, getIxList, getArg,
             CmpPredicate(..), IntPredicate(..), FPPredicate(..),
             fromIntPredicate, fromFPPredicate,
             toIntPredicate, toFPPredicate,
             uintFromCmpPredicate, sintFromCmpPredicate, fpFromCmpPredicate)
import LLVM.Core.Data
import LLVM.Core.Type
import LLVM.Core.CodeGenMonad
import LLVM.Core.CodeGen
            (BasicBlock(BasicBlock), Function, withCurrentBuilder,
             ConstValue(ConstValue), zero,
             Value(Value), value, valueOf)

import qualified LLVM.FFI.Core as FFI

import qualified Type.Data.Num.Decimal.Number as Dec
import Type.Data.Num.Decimal.Literal (d1)
import Type.Data.Num.Decimal.Number ((:<:), (:>:))
import Type.Base.Proxy (Proxy)

import Foreign.Ptr (Ptr, FunPtr, )
import Foreign.C (CUInt)

import Control.Monad.IO.Class (liftIO)
import Control.Monad (liftM)

import Data.Int (Int8, Int16, Int32, Int64)
import Data.Word (Word8, Word16, Word32, Word64)
import Data.Map (fromList, (!))

import Prelude hiding (and, or)


-- TODO:
-- Add vector version of arithmetic
-- Add rest of instructions
-- Use Terminate to ensure bb termination (how?)
-- more intrinsics are needed to, e.g., create an empty vector

data ArgDesc = AV String | AI Int | AL String | AE

instance Show ArgDesc where
    -- show (AV s) = "V_" ++ s
    -- show (AI i) = "I_" ++ show i
    -- show (AL l) = "L_" ++ l
    show (AV s) = s
    show (AI i) = show i
    show (AL l) = l
    show AE = "voidarg?"

data BinOpDesc = BOAdd | BOAddNuw | BOAddNsw | BOAddNuwNsw | BOFAdd
               | BOSub | BOSubNuw | BOSubNsw | BOSubNuwNsw | BOFSub
               | BOMul | BOMulNuw | BOMulNsw | BOMulNuwNsw | BOFMul
               | BOUDiv | BOSDiv | BOSDivExact | BOFDiv | BOURem | BOSRem | BOFRem
               | BOShL | BOLShR | BOAShR | BOAnd | BOOr | BOXor
    deriving Show

-- FIXME: complete definitions for unimplemented instructions
data InstrDesc =
    -- terminators
    IDRet TypeDesc ArgDesc | IDRetVoid
  | IDBrCond ArgDesc ArgDesc ArgDesc | IDBrUncond ArgDesc
  | IDSwitch [(ArgDesc, ArgDesc)]
  | IDIndirectBr
  | IDInvoke
  | IDUnwind
  | IDUnreachable
    -- binary operators (including bitwise)
  | IDBinOp BinOpDesc TypeDesc ArgDesc ArgDesc
    -- memory access and addressing
  | IDAlloca TypeDesc Int Int | IDLoad TypeDesc ArgDesc | IDStore TypeDesc ArgDesc ArgDesc
  | IDGetElementPtr TypeDesc [ArgDesc]
    -- conversion
  | IDTrunc TypeDesc TypeDesc ArgDesc | IDZExt TypeDesc TypeDesc ArgDesc
  | IDSExt TypeDesc TypeDesc ArgDesc | IDFPtoUI TypeDesc TypeDesc ArgDesc
  | IDFPtoSI TypeDesc TypeDesc ArgDesc | IDUItoFP TypeDesc TypeDesc ArgDesc
  | IDSItoFP TypeDesc TypeDesc ArgDesc
  | IDFPTrunc TypeDesc TypeDesc ArgDesc | IDFPExt TypeDesc TypeDesc ArgDesc
  | IDPtrToInt TypeDesc TypeDesc ArgDesc | IDIntToPtr TypeDesc TypeDesc ArgDesc
  | IDBitcast TypeDesc TypeDesc ArgDesc
    -- other
  | IDICmp IntPredicate ArgDesc ArgDesc | IDFCmp FPPredicate ArgDesc ArgDesc
  | IDPhi TypeDesc [(ArgDesc, ArgDesc)] | IDCall TypeDesc ArgDesc [ArgDesc]
  | IDSelect TypeDesc ArgDesc ArgDesc | IDUserOp1 | IDUserOp2 | IDVAArg
    -- vector operators
  | IDExtractElement | IDInsertElement | IDShuffleVector
    -- aggregate operators
  | IDExtractValue | IDInsertValue
    -- invalid
  | IDInvalidOp
    deriving Show

-- TODO: overflow support for binary operations (add/sub/mul)
getInstrDesc :: FFI.ValueRef -> IO (String, InstrDesc)
getInstrDesc v = do
    valueName <- U.getValueNameU v
    opcode <- FFI.instGetOpcode v
    t <- FFI.typeOf v >>= typeDesc2
    -- FIXME: sizeof() does not work for types!
    --tsize <- FFI.typeOf v -- >>= FFI.sizeOf -- >>= FFI.constIntGetZExtValue >>= return . fromIntegral
    tsize <- return 1
    os <- U.getOperands v >>= mapM getArgDesc
    os0 <- if length os > 0 then return $ os !! 0 else return AE
    os1 <- if length os > 1 then return $ os !! 1 else return AE
    t2 <- (if not (null os) && (opcode >= 30 || opcode <= 41)
            then U.getOperands v >>= return . snd . head >>= FFI.typeOf >>= typeDesc2
            else return TDVoid)
    p <- if opcode `elem` [42, 43] then FFI.cmpInstGetPredicate v else return 0
    let instr =
            (if opcode >= 8 && opcode <= 25 -- binary arithmetic
             then IDBinOp (getBinOp opcode) t os0 os1
             else if opcode >= 30 && opcode <= 41 -- conversion
                  then (getConvOp opcode) t2 t os0
                  else case opcode of
                         { 1 -> if null os then IDRetVoid else IDRet t os0;
                           2 -> if length os == 1 then IDBrUncond os0 else IDBrCond os0 (os !! 2) os1;
                           3 -> IDSwitch $ toPairs os;
                           -- TODO (can skip for now)
                           -- 4 -> IndirectBr ; 5 -> Invoke ;
                           6 -> IDUnwind; 7 -> IDUnreachable;
                           26 -> IDAlloca (getPtrType t) tsize (getImmInt os0);
                           27 -> IDLoad t os0; 28 -> IDStore t os0 os1;
                           29 -> IDGetElementPtr t os;
                           42 -> IDICmp (toIntPredicate p) os0 os1;
                           43 -> IDFCmp (toFPPredicate p) os0 os1;
                           44 -> IDPhi t $ toPairs os;
                           -- FIXME: getelementptr arguments are not handled
                           45 -> IDCall t (last os) (init os);
                           46 -> IDSelect t os0 os1;
                           -- TODO (can skip for now)
                           -- 47 -> UserOp1 ; 48 -> UserOp2 ; 49 -> VAArg ;
                           -- 50 -> ExtractElement ; 51 -> InsertElement ; 52 -> ShuffleVector ;
                           -- 53 -> ExtractValue ; 54 -> InsertValue ;
                           _ -> IDInvalidOp })
    return (valueName, instr)
    --if instr /= InvalidOp then return instr else fail $ "Invalid opcode: " ++ show opcode
        where getBinOp o = fromList [(8, BOAdd), (9, BOFAdd), (10, BOSub), (11, BOFSub),
                                     (12, BOMul), (13, BOFMul), (14, BOUDiv), (15, BOSDiv),
                                     (16, BOFDiv), (17, BOURem), (18, BOSRem), (19, BOFRem),
                                     (20, BOShL), (21, BOLShR), (22, BOAShR), (23, BOAnd),
                                     (24, BOOr), (25, BOXor)] ! o
              getConvOp o = fromList [(30, IDTrunc), (31, IDZExt), (32, IDSExt), (33, IDFPtoUI),
                                      (34, IDFPtoSI), (35, IDUItoFP), (36, IDSItoFP), (37, IDFPTrunc),
                                      (38, IDFPExt), (39, IDPtrToInt), (40, IDIntToPtr), (41, IDBitcast)] ! o
              toPairs xs = zip (stride 2 xs) (stride 2 (drop 1 xs))
              stride _ [] = []
              stride n (x:xs) = x : stride n (drop (n-1) xs)
              getPtrType (TDPtr t) = t
              getPtrType _ = TDVoid
              getImmInt (AI i) = i
              getImmInt _ = 0

-- TODO: fix for non-int constants
getArgDesc :: (String, FFI.ValueRef) -> IO ArgDesc
getArgDesc (vname, v) = do
    isC <- U.isConstant v
    t <- FFI.typeOf v >>= typeDesc2
    if isC
      then case t of
             TDInt _ _ -> do
                          cV <- FFI.constIntGetSExtValue v
                          return $ AI $ fromIntegral cV
             _ -> return AE
      else case t of
             TDLabel -> return $ AL vname
             _ -> return $ AV vname

--------------------------------------

type Terminate = ()
terminate :: Terminate
terminate = ()

--------------------------------------

-- |Acceptable arguments to the 'ret' instruction.
class Ret a r where
    ret' :: a -> CodeGenFunction r Terminate

-- | Return from the current function with the given value.  Use () as the return value for what would be a void function in C.
ret :: (Ret a r) => a -> CodeGenFunction r Terminate
ret = ret'

-- overlaps with Ret () ()!
{-
instance (IsFirstClass a, IsConst a) => Ret a a where
    ret' = ret . valueOf
-}

instance Ret (Value a) a where
    ret' (Value a) = do
        withCurrentBuilder_ $ \ bldPtr -> FFI.buildRet bldPtr a
        return terminate

instance Ret () () where
    ret' _ = do
        withCurrentBuilder_ $ FFI.buildRetVoid
        return terminate

withCurrentBuilder_ :: (FFI.BuilderRef -> IO a) -> CodeGenFunction r ()
withCurrentBuilder_ p = withCurrentBuilder p >> return ()

--------------------------------------

-- | Branch to the first basic block if the boolean is true, otherwise to the second basic block.
condBr :: Value Bool -- ^ Boolean to branch upon.
       -> BasicBlock -- ^ Target for true.
       -> BasicBlock -- ^ Target for false.
       -> CodeGenFunction r Terminate
condBr (Value b) (BasicBlock t1) (BasicBlock t2) = do
    withCurrentBuilder_ $ \ bldPtr -> FFI.buildCondBr bldPtr b t1 t2
    return terminate

--------------------------------------

-- | Unconditionally branch to the given basic block.
br :: BasicBlock  -- ^ Branch target.
   -> CodeGenFunction r Terminate
br (BasicBlock t) = do
    withCurrentBuilder_ $ \ bldPtr -> FFI.buildBr bldPtr t
    return terminate

--------------------------------------

-- | Branch table instruction.
switch :: (IsInteger a)
       => Value a                        -- ^ Value to branch upon.
       -> BasicBlock                     -- ^ Default branch target.
       -> [(ConstValue a, BasicBlock)]   -- ^ Labels and corresponding branch targets.
       -> CodeGenFunction r Terminate
switch (Value val) (BasicBlock dflt) arms = do
    withCurrentBuilder_ $ \ bldPtr -> do
        inst <- FFI.buildSwitch bldPtr val dflt (fromIntegral $ length arms)
        sequence_ [ FFI.addCase inst c b | (ConstValue c, BasicBlock b) <- arms ]
    return terminate

--------------------------------------

-- |Inform the code generator that this code can never be reached.
unreachable :: CodeGenFunction r Terminate
unreachable = do
    withCurrentBuilder_ FFI.buildUnreachable
    return terminate

--------------------------------------


withArithmeticType ::
    (IsArithmetic c) =>
    (ArithmeticType c -> a -> CodeGenFunction r (v c)) ->
    (a -> CodeGenFunction r (v c))
withArithmeticType f = f arithmeticType


class (ValueCons value0, ValueCons value1) => ValueCons2 value0 value1 where
    type BinOpValue (value0 :: * -> *) (value1 :: * -> *) :: * -> *
    binop ::
        FFIConstBinOp -> FFIBinOp ->
        value0 a -> value1 a -> CodeGenFunction r (BinOpValue value0 value1 b)

instance ValueCons2 Value Value where
    type BinOpValue Value Value = Value
    binop _ op (Value a1) (Value a2) = buildBinOp op a1 a2

instance ValueCons2 Value ConstValue where
    type BinOpValue Value ConstValue = Value
    binop _ op (Value a1) (ConstValue a2) = buildBinOp op a1 a2

instance ValueCons2 ConstValue Value where
    type BinOpValue ConstValue Value = Value
    binop _ op (ConstValue a1) (Value a2) = buildBinOp op a1 a2

instance ValueCons2 ConstValue ConstValue where
    type BinOpValue ConstValue ConstValue = ConstValue
    binop cop _ (ConstValue a1) (ConstValue a2) =
        liftIO $ fmap ConstValue $ cop a1 a2


add, sub, mul ::
    (ValueCons2 value0 value1, IsArithmetic a) =>
    value0 a -> value1 a -> CodeGenFunction r (BinOpValue value0 value1 a)
add =
    curry $ withArithmeticType $ \typ -> uncurry $ case typ of
      IntegerType  -> binop FFI.constAdd  FFI.buildAdd
      FloatingType -> binop FFI.constFAdd FFI.buildFAdd

sub =
    curry $ withArithmeticType $ \typ -> uncurry $ case typ of
      IntegerType  -> binop FFI.constSub  FFI.buildSub
      FloatingType -> binop FFI.constFSub FFI.buildFSub

mul =
    curry $ withArithmeticType $ \typ -> uncurry $ case typ of
      IntegerType  -> binop FFI.constMul  FFI.buildMul
      FloatingType -> binop FFI.constFMul FFI.buildFMul

iadd, isub, imul ::
    (ValueCons2 value0 value1, IsInteger a) =>
    value0 a -> value1 a -> CodeGenFunction r (BinOpValue value0 value1 a)
iadd = binop FFI.constAdd FFI.buildAdd
isub = binop FFI.constSub FFI.buildSub
imul = binop FFI.constMul FFI.buildMul

iaddNoWrap, isubNoWrap, imulNoWrap ::
    (ValueCons2 value0 value1, IsInteger a) =>
    value0 a -> value1 a -> CodeGenFunction r (BinOpValue value0 value1 a)
iaddNoWrap =
    sbinop FFI.constNSWAdd FFI.buildNSWAdd FFI.constNUWAdd FFI.buildNUWAdd
isubNoWrap =
    sbinop FFI.constNSWSub FFI.buildNSWSub FFI.constNUWSub FFI.buildNUWSub
imulNoWrap =
    sbinop FFI.constNSWMul FFI.buildNSWMul FFI.constNUWMul FFI.buildNUWMul

-- | signed or unsigned integer division depending on the type
idiv ::
    (ValueCons2 value0 value1, IsInteger a) =>
    value0 a -> value1 a -> CodeGenFunction r (BinOpValue value0 value1 a)
idiv = sbinop FFI.constSDiv FFI.buildSDiv FFI.constUDiv FFI.buildUDiv
-- | signed or unsigned remainder depending on the type
irem ::
    (ValueCons2 value0 value1, IsInteger a) =>
    value0 a -> value1 a -> CodeGenFunction r (BinOpValue value0 value1 a)
irem = sbinop FFI.constSRem FFI.buildSRem FFI.constURem FFI.buildURem

{-# DEPRECATED udiv "use idiv instead" #-}
{-# DEPRECATED sdiv "use idiv instead" #-}
{-# DEPRECATED urem "use irem instead" #-}
{-# DEPRECATED srem "use irem instead" #-}
udiv, sdiv, urem, srem ::
    (ValueCons2 value0 value1, IsInteger a) =>
    value0 a -> value1 a -> CodeGenFunction r (BinOpValue value0 value1 a)
udiv = binop FFI.constUDiv FFI.buildUDiv
sdiv = binop FFI.constSDiv FFI.buildSDiv
urem = binop FFI.constURem FFI.buildURem
srem = binop FFI.constSRem FFI.buildSRem

fadd, fsub, fmul ::
    (ValueCons2 value0 value1, IsFloating a) =>
    value0 a -> value1 a -> CodeGenFunction r (BinOpValue value0 value1 a)
fadd = binop FFI.constFAdd FFI.buildFAdd
fsub = binop FFI.constFSub FFI.buildFSub
fmul = binop FFI.constFMul FFI.buildFMul

-- | Floating point division.
fdiv ::
    (ValueCons2 value0 value1, IsFloating a) =>
    value0 a -> value1 a -> CodeGenFunction r (BinOpValue value0 value1 a)
fdiv = binop FFI.constFDiv FFI.buildFDiv
-- | Floating point remainder.
frem ::
    (ValueCons2 value0 value1, IsFloating a) =>
    value0 a -> value1 a -> CodeGenFunction r (BinOpValue value0 value1 a)
frem = binop FFI.constFRem FFI.buildFRem

shl, lshr, ashr, and, or, xor ::
    (ValueCons2 value0 value1, IsInteger a) =>
    value0 a -> value1 a -> CodeGenFunction r (BinOpValue value0 value1 a)
shl  = binop FFI.constShl  FFI.buildShl
lshr = binop FFI.constLShr FFI.buildLShr
ashr = binop FFI.constAShr FFI.buildAShr
and  = binop FFI.constAnd  FFI.buildAnd
or   = binop FFI.constOr   FFI.buildOr
xor  = binop FFI.constXor  FFI.buildXor

shr ::
    (ValueCons2 value0 value1, IsInteger a) =>
    value0 a -> value1 a -> CodeGenFunction r (BinOpValue value0 value1 a)
shr = sbinop FFI.constAShr FFI.buildAShr FFI.constLShr FFI.buildLShr

sbinop ::
    forall value0 value1 a b r.
    (ValueCons2 value0 value1, IsInteger a) =>
    FFIConstBinOp -> FFIBinOp ->
    FFIConstBinOp -> FFIBinOp ->
    value0 a -> value1 a -> CodeGenFunction r (BinOpValue value0 value1 b)
sbinop scop sop ucop uop =
    if isSigned (LP.Proxy :: LP.Proxy a)
        then binop scop sop
        else binop ucop uop


buildBinOp ::
    FFIBinOp -> FFI.ValueRef -> FFI.ValueRef -> CodeGenFunction r (Value a)
buildBinOp op a1 a2 =
    liftM Value $
    withCurrentBuilder $ \ bld ->
      U.withEmptyCString $ op bld a1 a2

neg ::
    (ValueCons value, IsArithmetic a) =>
    value a -> CodeGenFunction r (value a)
neg =
    withArithmeticType $ \typ -> case typ of
      IntegerType  -> unop FFI.constNeg FFI.buildNeg
      FloatingType -> unop FFI.constFNeg FFI.buildFNeg

ineg ::
    (ValueCons value, IsInteger a) =>
    value a -> CodeGenFunction r (value a)
ineg = unop FFI.constNeg FFI.buildNeg

inegNoWrap ::
    forall value a r.
    (ValueCons value, IsInteger a) =>
    value a -> CodeGenFunction r (value a)
inegNoWrap =
   if isSigned (LP.Proxy :: LP.Proxy a)
     then unop FFI.constNSWNeg FFI.buildNSWNeg
     else unop FFI.constNUWNeg FFI.buildNUWNeg

fneg ::
    (ValueCons value, IsFloating a) =>
    value a -> CodeGenFunction r (value a)
fneg = unop FFI.constFNeg FFI.buildFNeg

inv ::
    (ValueCons value, IsInteger a) =>
    value a -> CodeGenFunction r (value a)
inv = unop FFI.constNot FFI.buildNot

--------------------------------------

-- | Get a value from a vector.
extractelement :: (Dec.Positive n, IsPrimitive a)
               => Value (Vector n a)               -- ^ Vector
               -> Value Word32                     -- ^ Index into the vector
               -> CodeGenFunction r (Value a)
extractelement (Value vec) (Value i) =
    liftM Value $
    withCurrentBuilder $ \ bldPtr ->
      U.withEmptyCString $ FFI.buildExtractElement bldPtr vec i

-- | Insert a value into a vector, nondestructive.
insertelement :: (Dec.Positive n, IsPrimitive a)
              => Value (Vector n a)                -- ^ Vector
              -> Value a                           -- ^ Value to insert
              -> Value Word32                      -- ^ Index into the vector
              -> CodeGenFunction r (Value (Vector n a))
insertelement (Value vec) (Value e) (Value i) =
    liftM Value $
    withCurrentBuilder $ \ bldPtr ->
      U.withEmptyCString $ FFI.buildInsertElement bldPtr vec e i

-- | Permute vector.
shufflevector :: (Dec.Positive n, Dec.Positive m, IsPrimitive a)
              => Value (Vector n a)
              -> Value (Vector n a)
              -> ConstValue (Vector m Word32)
              -> CodeGenFunction r (Value (Vector m a))
shufflevector (Value a) (Value b) (ConstValue mask) =
    liftM Value $
    withCurrentBuilder $ \ bldPtr ->
      U.withEmptyCString $ FFI.buildShuffleVector bldPtr a b mask


-- |Acceptable arguments to 'extractvalue' and 'insertvalue'.
class GetValue agg ix where
    type ValueType agg ix :: *
    getIx :: LP.Proxy agg -> ix -> CUInt

instance (GetField as i, Dec.Natural i) => GetValue (Struct as) (Proxy i) where
    type ValueType (Struct as) (Proxy i) = FieldType as i
    getIx _ n = Dec.integralFromProxy n

instance (IsFirstClass a, Dec.Natural n) => GetValue (Array n a) Word32 where
    type ValueType (Array n a) Word32 = a
    getIx _ n = fromIntegral n

instance (IsFirstClass a, Dec.Natural n) => GetValue (Array n a) Word64 where
    type ValueType (Array n a) Word64 = a
    getIx _ n = fromIntegral n


instance (IsFirstClass a, Dec.Natural n, Dec.Natural i, i :<: n) => GetValue (Array n a) (Proxy i) where
    type ValueType (Array n a) (Proxy i) = a
    getIx _ n = Dec.integralFromProxy n


-- | Get a value from an aggregate.
extractvalue :: forall r agg i.
                GetValue agg i
             => Value agg                   -- ^ Aggregate
             -> i                           -- ^ Index into the aggregate
             -> CodeGenFunction r (Value (ValueType agg i))
extractvalue (Value agg) i =
    liftM Value $
    withCurrentBuilder $ \ bldPtr ->
      U.withEmptyCString $
        FFI.buildExtractValue bldPtr agg (getIx (LP.Proxy :: LP.Proxy agg) i)

-- | Insert a value into an aggregate, nondestructive.
insertvalue :: forall r agg i.
               GetValue agg i
            => Value agg                   -- ^ Aggregate
            -> Value (ValueType agg i)     -- ^ Value to insert
            -> i                           -- ^ Index into the aggregate
            -> CodeGenFunction r (Value agg)
insertvalue (Value agg) (Value e) i =
    liftM Value $
    withCurrentBuilder $ \ bldPtr ->
      U.withEmptyCString $
        FFI.buildInsertValue bldPtr agg e (getIx (LP.Proxy :: LP.Proxy agg) i)


--------------------------------------

-- | Truncate a value to a shorter bit width.
trunc :: (ValueCons value, IsInteger a, IsInteger b, ShapeOf a ~ ShapeOf b, IsSized a, IsSized b, SizeOf a :>: SizeOf b)
      => value a -> CodeGenFunction r (value b)
trunc = convert FFI.constTrunc FFI.buildTrunc

-- | Zero extend a value to a wider width.
-- If possible, use 'ext' that chooses the right padding according to the types
zext :: (ValueCons value, IsInteger a, IsInteger b, ShapeOf a ~ ShapeOf b, IsSized a, IsSized b, SizeOf a :<: SizeOf b)
     => value a -> CodeGenFunction r (value b)
zext = convert FFI.constZExt FFI.buildZExt

-- | Sign extend a value to wider width.
-- If possible, use 'ext' that chooses the right padding according to the types
sext :: (ValueCons value, IsInteger a, IsInteger b, ShapeOf a ~ ShapeOf b, IsSized a, IsSized b, SizeOf a :<: SizeOf b)
     => value a -> CodeGenFunction r (value b)
sext = convert FFI.constSExt FFI.buildSExt

-- | Extend a value to wider width.
-- If the target type is signed, then preserve the sign,
-- If the target type is unsigned, then extended by zeros.
ext :: forall value a b r. (ValueCons value, IsInteger a, IsInteger b, ShapeOf a ~ ShapeOf b, Signed a ~ Signed b, IsSized a, IsSized b, SizeOf a :<: SizeOf b)
     => value a -> CodeGenFunction r (value b)
ext =
   if isSigned (LP.Proxy :: LP.Proxy b)
     then convert FFI.constSExt FFI.buildSExt
     else convert FFI.constZExt FFI.buildZExt

-- | It is 'zext', 'trunc' or nop depending on the relation of the sizes.
zadapt :: forall value a b r. (ValueCons value, IsInteger a, IsInteger b, ShapeOf a ~ ShapeOf b)
     => value a -> CodeGenFunction r (value b)
zadapt =
   case compare (sizeOf (typeDesc (LP.Proxy :: LP.Proxy a)))
                (sizeOf (typeDesc (LP.Proxy :: LP.Proxy b))) of
      LT -> convert FFI.constZExt FFI.buildZExt
      EQ -> convert FFI.constBitCast FFI.buildBitCast
      GT -> convert FFI.constTrunc FFI.buildTrunc

-- | It is 'sext', 'trunc' or nop depending on the relation of the sizes.
sadapt :: forall value a b r. (ValueCons value, IsInteger a, IsInteger b, ShapeOf a ~ ShapeOf b)
     => value a -> CodeGenFunction r (value b)
sadapt =
   case compare (sizeOf (typeDesc (LP.Proxy :: LP.Proxy a)))
                (sizeOf (typeDesc (LP.Proxy :: LP.Proxy b))) of
      LT -> convert FFI.constSExt FFI.buildSExt
      EQ -> convert FFI.constBitCast FFI.buildBitCast
      GT -> convert FFI.constTrunc FFI.buildTrunc

-- | It is 'sadapt' or 'zadapt' depending on the sign mode.
adapt :: forall value a b r. (ValueCons value, IsInteger a, IsInteger b, ShapeOf a ~ ShapeOf b, Signed a ~ Signed b)
     => value a -> CodeGenFunction r (value b)
adapt =
   case compare (sizeOf (typeDesc (LP.Proxy :: LP.Proxy a)))
                (sizeOf (typeDesc (LP.Proxy :: LP.Proxy b))) of
      LT ->
         if isSigned (LP.Proxy :: LP.Proxy b)
           then convert FFI.constSExt FFI.buildSExt
           else convert FFI.constZExt FFI.buildZExt
      EQ -> convert FFI.constBitCast FFI.buildBitCast
      GT -> convert FFI.constTrunc FFI.buildTrunc

-- | Truncate a floating point value.
fptrunc :: (ValueCons value, IsFloating a, IsFloating b, ShapeOf a ~ ShapeOf b, IsSized a, IsSized b, SizeOf a :>: SizeOf b)
        => value a -> CodeGenFunction r (value b)
fptrunc = convert FFI.constFPTrunc FFI.buildFPTrunc

-- | Extend a floating point value.
fpext :: (ValueCons value, IsFloating a, IsFloating b, ShapeOf a ~ ShapeOf b, IsSized a, IsSized b, SizeOf a :<: SizeOf b)
      => value a -> CodeGenFunction r (value b)
fpext = convert FFI.constFPExt FFI.buildFPExt

{-# DEPRECATED fptoui "use fptoint since it is type-safe with respect to signs" #-}
-- | Convert a floating point value to an unsigned integer.
fptoui :: (ValueCons value, IsFloating a, IsInteger b, ShapeOf a ~ ShapeOf b) => value a -> CodeGenFunction r (value b)
fptoui = convert FFI.constFPToUI FFI.buildFPToUI

{-# DEPRECATED fptosi "use fptoint since it is type-safe with respect to signs" #-}
-- | Convert a floating point value to a signed integer.
fptosi :: (ValueCons value, IsFloating a, IsInteger b, ShapeOf a ~ ShapeOf b) => value a -> CodeGenFunction r (value b)
fptosi = convert FFI.constFPToSI FFI.buildFPToSI

-- | Convert a floating point value to an integer.
-- It is mapped to @fptosi@ or @fptoui@ depending on the type @a@.
fptoint :: forall value a b r. (ValueCons value, IsFloating a, IsInteger b, ShapeOf a ~ ShapeOf b) => value a -> CodeGenFunction r (value b)
fptoint =
   if isSigned (LP.Proxy :: LP.Proxy b)
     then convert FFI.constFPToSI FFI.buildFPToSI
     else convert FFI.constFPToUI FFI.buildFPToUI


{- DEPRECATED uitofp "use inttofp since it is type-safe with respect to signs" -}
-- | Convert an unsigned integer to a floating point value.
-- Although 'inttofp' should be prefered, this function may be useful for conversion from Bool.
uitofp :: (ValueCons value, IsInteger a, IsFloating b, ShapeOf a ~ ShapeOf b) => value a -> CodeGenFunction r (value b)
uitofp = convert FFI.constUIToFP FFI.buildUIToFP

{- DEPRECATED sitofp "use inttofp since it is type-safe with respect to signs" -}
-- | Convert a signed integer to a floating point value.
-- Although 'inttofp' should be prefered, this function may be useful for conversion from Bool.
sitofp :: (ValueCons value, IsInteger a, IsFloating b, ShapeOf a ~ ShapeOf b) => value a -> CodeGenFunction r (value b)
sitofp = convert FFI.constSIToFP FFI.buildSIToFP

-- | Convert an integer to a floating point value.
-- It is mapped to @sitofp@ or @uitofp@ depending on the type @a@.
inttofp :: forall value a b r. (ValueCons value, IsInteger a, IsFloating b, ShapeOf a ~ ShapeOf b) => value a -> CodeGenFunction r (value b)
inttofp =
   if isSigned (LP.Proxy :: LP.Proxy a)
     then convert FFI.constSIToFP FFI.buildSIToFP
     else convert FFI.constUIToFP FFI.buildUIToFP


-- | Convert a pointer to an integer.
ptrtoint :: (ValueCons value, IsInteger b, IsPrimitive b) => value (Ptr a) -> CodeGenFunction r (value b)
ptrtoint = convert FFI.constPtrToInt FFI.buildPtrToInt

-- | Convert an integer to a pointer.
inttoptr :: (ValueCons value, IsInteger a, IsType b) => value a -> CodeGenFunction r (value (Ptr b))
inttoptr = convert FFI.constIntToPtr FFI.buildIntToPtr

-- | Convert between to values of the same size by just copying the bit pattern.
bitcast :: (ValueCons value, IsFirstClass a, IsFirstClass b, IsSized a, IsSized b, SizeOf a ~ SizeOf b)
        => value a -> CodeGenFunction r (value b)
bitcast = convert FFI.constBitCast FFI.buildBitCast


--------------------------------------

type CmpValueResult value0 value1 a = BinOpValue value0 value1 (CmpResult a)

type CmpResult c = ShapedType (ShapeOf c) Bool

class (IsFirstClass c) => CmpRet c where
    cmpBld :: LP.Proxy c -> CmpPredicate -> FFIBinOp
    cmpCnst :: LP.Proxy c -> CmpPredicate -> FFIConstBinOp

instance CmpRet Float   where cmpBld _ = fcmpBld ; cmpCnst _ = fcmpCnst
instance CmpRet Double  where cmpBld _ = fcmpBld ; cmpCnst _ = fcmpCnst
instance CmpRet FP128   where cmpBld _ = fcmpBld ; cmpCnst _ = fcmpCnst
instance CmpRet Bool    where cmpBld _ = ucmpBld ; cmpCnst _ = ucmpCnst
instance CmpRet Word8   where cmpBld _ = ucmpBld ; cmpCnst _ = ucmpCnst
instance CmpRet Word16  where cmpBld _ = ucmpBld ; cmpCnst _ = ucmpCnst
instance CmpRet Word32  where cmpBld _ = ucmpBld ; cmpCnst _ = ucmpCnst
instance CmpRet Word64  where cmpBld _ = ucmpBld ; cmpCnst _ = ucmpCnst
instance CmpRet Int8    where cmpBld _ = scmpBld ; cmpCnst _ = scmpCnst
instance CmpRet Int16   where cmpBld _ = scmpBld ; cmpCnst _ = scmpCnst
instance CmpRet Int32   where cmpBld _ = scmpBld ; cmpCnst _ = scmpCnst
instance CmpRet Int64   where cmpBld _ = scmpBld ; cmpCnst _ = scmpCnst
instance (IsType a) =>
         CmpRet (Ptr a) where cmpBld _ = ucmpBld ; cmpCnst _ = ucmpCnst

instance (Dec.Positive n) => CmpRet (WordN n) where
    cmpBld _ = ucmpBld ; cmpCnst _ = ucmpCnst
instance (Dec.Positive n) => CmpRet (IntN n) where
    cmpBld _ = scmpBld ; cmpCnst _ = scmpCnst

instance (CmpRet a, IsPrimitive a, Dec.Positive n) => CmpRet (Vector n a) where
    cmpBld _ = cmpBld (LP.Proxy :: LP.Proxy a)
    cmpCnst _ = cmpCnst (LP.Proxy :: LP.Proxy a)


{- |
Compare values of ordered types
and choose predicates according to the compared types.
Floating point numbers are compared in \"ordered\" mode,
that is @NaN@ operands yields 'False' as result.
Pointers are compared unsigned.
These choices are consistent with comparison in plain Haskell.
-}
cmp :: forall value0 value1 a r.
   (ValueCons2 value0 value1, CmpRet a) =>
   CmpPredicate -> value0 a -> value1 a ->
   CodeGenFunction r (CmpValueResult value0 value1 a)
cmp p =
    binop
        (cmpCnst (LP.Proxy :: LP.Proxy a) p)
        (cmpBld (LP.Proxy :: LP.Proxy a) p)

ucmpBld :: CmpPredicate -> FFIBinOp
ucmpBld p = flip FFI.buildICmp (fromIntPredicate (uintFromCmpPredicate p))

scmpBld :: CmpPredicate -> FFIBinOp
scmpBld p = flip FFI.buildICmp (fromIntPredicate (sintFromCmpPredicate p))

fcmpBld :: CmpPredicate -> FFIBinOp
fcmpBld p = flip FFI.buildFCmp (fromFPPredicate (fpFromCmpPredicate p))


ucmpCnst :: CmpPredicate -> FFIConstBinOp
ucmpCnst p = FFI.constICmp (fromIntPredicate (uintFromCmpPredicate p))

scmpCnst :: CmpPredicate -> FFIConstBinOp
scmpCnst p = FFI.constICmp (fromIntPredicate (sintFromCmpPredicate p))

fcmpCnst :: CmpPredicate -> FFIConstBinOp
fcmpCnst p = FFI.constFCmp (fromFPPredicate (fpFromCmpPredicate p))


_ucmp ::
    (ValueCons2 value0 value1, CmpRet a, IsInteger a) =>
    CmpPredicate -> value0 a -> value1 a ->
    CodeGenFunction r (CmpValueResult value0 value1 a)
_ucmp p = binop (ucmpCnst p) (ucmpBld p)

_scmp ::
    (ValueCons2 value0 value1, CmpRet a, IsInteger a) =>
    CmpPredicate -> value0 a -> value1 a ->
    CodeGenFunction r (CmpValueResult value0 value1 a)
_scmp p = binop (scmpCnst p) (scmpBld p)

pcmp ::
    (ValueCons2 value0 value1, IsType a) =>
    IntPredicate -> value0 (Ptr a) -> value1 (Ptr a) ->
    CodeGenFunction r (BinOpValue value0 value1 (Ptr a))
pcmp p =
    binop
        (FFI.constICmp (fromIntPredicate p))
        (flip FFI.buildICmp (fromIntPredicate p))


{-# DEPRECATED icmp "use cmp or pcmp instead" #-}
-- | Compare integers.
icmp ::
    (ValueCons2 value0 value1, CmpRet a, IsIntegerOrPointer a) =>
    IntPredicate -> value0 a -> value1 a ->
    CodeGenFunction r (CmpValueResult value0 value1 a)
icmp p =
    binop
        (FFI.constICmp (fromIntPredicate p))
        (flip FFI.buildICmp (fromIntPredicate p))

-- | Compare floating point values.
fcmp ::
    (ValueCons2 value0 value1, CmpRet a, IsFloating a) =>
    FPPredicate -> value0 a -> value1 a ->
    CodeGenFunction r (CmpValueResult value0 value1 a)
fcmp p =
    binop
        (FFI.constFCmp (fromFPPredicate p))
        (flip FFI.buildFCmp (fromFPPredicate p))

--------------------------------------

setHasNoNaNs, setHasNoInfs, setHasNoSignedZeros, setHasAllowReciprocal,
    setFastMath :: (IsFloating a) => Bool -> Value a -> CodeGenFunction r ()
setHasNoNaNs          = fastMath FFI.setHasNoNaNs
setHasNoInfs          = fastMath FFI.setHasNoInfs
setHasNoSignedZeros   = fastMath FFI.setHasNoSignedZeros
setHasAllowReciprocal = fastMath FFI.setHasAllowReciprocal
setFastMath           = fastMath FFI.setHasUnsafeAlgebra

fastMath ::
    (IsFloating a) =>
    (FFI.ValueRef -> FFI.Bool -> IO ()) ->
    Bool -> Value a -> CodeGenFunction r ()
fastMath setter b (Value v) = liftIO $ setter v $ FFI.consBool b


--------------------------------------

-- XXX could do const song and dance
-- | Select between two values depending on a boolean.
select :: (CmpRet a) => Value (CmpResult a) -> Value a -> Value a -> CodeGenFunction r (Value a)
select (Value cnd) (Value thn) (Value els) =
    liftM Value $
      withCurrentBuilder $ \ bldPtr ->
        U.withEmptyCString $
          FFI.buildSelect bldPtr cnd thn els

--------------------------------------

type Caller = FFI.BuilderRef -> [FFI.ValueRef] -> IO FFI.ValueRef

{-
Function (a -> b -> IO c)
Value a -> Value b -> CodeGenFunction r c
-}

-- |Acceptable arguments to 'call'.
class (f ~ CalledFunction g, r ~ CallerResult g, g ~ CallerFunction f r) =>
         CallArgs f g r where
    type CalledFunction g :: *
    type CallerResult g :: *
    type CallerFunction f r :: *
    doCall :: Call f -> g

instance (CallArgs b b' r) => CallArgs (a -> b) (Value a -> b') r where
    type CalledFunction (Value a -> b') = a -> CalledFunction b'
    type CallerResult (Value a -> b') = CallerResult b'
    type CallerFunction (a -> b) r = Value a -> CallerFunction b r
    doCall f a = doCall (applyCall f a)

--instance (CallArgs b b') => CallArgs (a -> b) (ConstValue a -> b') where
--    doCall mkCall args f (ConstValue arg) = doCall mkCall (arg : args) (f (LP.Proxy :: LP.Proxy a))

instance CallArgs (IO a) (CodeGenFunction r (Value a)) r where
    type CalledFunction (CodeGenFunction r (Value a)) = IO a
    type CallerResult (CodeGenFunction r (Value a)) = r
    type CallerFunction (IO a) r = CodeGenFunction r (Value a)
    doCall = runCall

doCallDef :: Caller -> [FFI.ValueRef] -> b -> CodeGenFunction r (Value a)
doCallDef mkCall args _ =
    withCurrentBuilder $ \ bld ->
      liftM Value $ mkCall bld (reverse args)

-- | Call a function with the given arguments.  The 'call' instruction is variadic, i.e., the number of arguments
-- it takes depends on the type of /f/.
call :: (CallArgs f g r) => Function f -> g
call = doCall . callFromFunction

data Call a = Call Caller [FFI.ValueRef]

callFromFunction :: Function a -> Call a
callFromFunction (Value f) = Call (U.makeCall f) []

-- like Applicative.<*>
infixl 4 `applyCall`

applyCall :: Call (a -> b) -> Value a -> Call b
applyCall (Call mkCall args) (Value arg) = Call mkCall (arg:args)

runCall :: Call (IO a) -> CodeGenFunction r (Value a)
runCall (Call mkCall args) = doCallDef mkCall args ()


invokeFromFunction ::
          BasicBlock         -- ^Normal return point.
       -> BasicBlock         -- ^Exception return point.
       -> Function f         -- ^Function to call.
       -> Call f
invokeFromFunction (BasicBlock norm) (BasicBlock expt) (Value f) =
    Call (U.makeInvoke norm expt f) []

-- | Call a function with exception handling.
invoke :: (CallArgs f g r)
       => BasicBlock         -- ^Normal return point.
       -> BasicBlock         -- ^Exception return point.
       -> Function f         -- ^Function to call.
       -> g
invoke norm expt f = doCall $ invokeFromFunction norm expt f

callWithConvFromFunction :: FFI.CallingConvention -> Function f -> Call f
callWithConvFromFunction cc (Value f) =
    Call (U.makeCallWithCc cc f) []

-- | Call a function with the given arguments.  The 'call' instruction
-- is variadic, i.e., the number of arguments it takes depends on the
-- type of /f/.
-- This also sets the calling convention of the call to the function.
-- As LLVM itself defines, if the calling conventions of the calling
-- /instruction/ and the function being /called/ are different, undefined
-- behavior results.
callWithConv :: (CallArgs f g r) => FFI.CallingConvention -> Function f -> g
callWithConv cc f = doCall $ callWithConvFromFunction cc f

invokeWithConvFromFunction ::
          FFI.CallingConvention -- ^Calling convention
       -> BasicBlock         -- ^Normal return point.
       -> BasicBlock         -- ^Exception return point.
       -> Function f         -- ^Function to call.
       -> Call f
invokeWithConvFromFunction cc (BasicBlock norm) (BasicBlock expt) (Value f) =
    Call (U.makeInvokeWithCc cc norm expt f) []

-- | Call a function with exception handling.
-- This also sets the calling convention of the call to the function.
-- As LLVM itself defines, if the calling conventions of the calling
-- /instruction/ and the function being /called/ are different, undefined
-- behavior results.
invokeWithConv :: (CallArgs f g r)
               => FFI.CallingConvention -- ^Calling convention
               -> BasicBlock         -- ^Normal return point.
               -> BasicBlock         -- ^Exception return point.
               -> Function f         -- ^Function to call.
               -> g
invokeWithConv cc norm expt f =
    doCall $ invokeWithConvFromFunction cc norm expt f

--------------------------------------

-- XXX could do const song and dance
-- |Join several variables (virtual registers) from different basic blocks into one.
-- All of the variables in the list are joined.  See also 'addPhiInputs'.
phi :: forall a r . (IsFirstClass a) => [(Value a, BasicBlock)] -> CodeGenFunction r (Value a)
phi incoming =
    liftM Value $
      withCurrentBuilder $ \ bldPtr -> do
        inst <- U.buildEmptyPhi bldPtr =<< typeRef (LP.Proxy :: LP.Proxy a)
        U.addPhiIns inst [ (v, b) | (Value v, BasicBlock b) <- incoming ]
        return inst

-- |Add additional inputs to an existing phi node.
-- The reason for this instruction is that sometimes the structure of the code
-- makes it impossible to have all variables in scope at the point where you need the phi node.
addPhiInputs :: forall a r . (IsFirstClass a)
             => Value a                      -- ^Must be a variable from a call to 'phi'.
             -> [(Value a, BasicBlock)]      -- ^Variables to add.
             -> CodeGenFunction r ()
addPhiInputs (Value inst) incoming =
    liftIO $ U.addPhiIns inst [ (v, b) | (Value v, BasicBlock b) <- incoming ]


--------------------------------------

-- | Acceptable argument to array memory allocation.
class AllocArg a where
    getAllocArg :: a -> Value Word32
instance AllocArg (Value Word32) where
    getAllocArg = id
instance AllocArg (ConstValue Word32) where
    getAllocArg = value
instance AllocArg Word32 where
    getAllocArg = valueOf

-- could be moved to Util.Memory
-- FFI.buildMalloc deprecated since LLVM-2.7
-- XXX What's the type returned by malloc
-- | Allocate heap memory.
malloc :: forall a r . (IsSized a) => CodeGenFunction r (Value (Ptr a))
malloc = arrayMalloc (1::Word32)

{-
I use a pointer type as size parameter of 'malloc'.
This way I hope that the parameter has always the correct size (32 or 64 bit).
A side effect is that we can convert the result of 'getelementptr' using 'bitcast',
that does not suffer from the slow assembly problem. (bug #8281)
-}
foreign import ccall "&aligned_malloc_sizeptr"
   alignedMalloc :: FunPtr (Ptr Word8 -> Ptr Word8 -> IO (Ptr Word8))

foreign import ccall "&aligned_free"
   alignedFree :: FunPtr (Ptr Word8 -> IO ())


{-
There is a bug in LLVM-2.7 and LLVM-2.8
(http://llvm.org/bugs/show_bug.cgi?id=8281)
that causes huge assembly times for expressions like
ptrtoint(getelementptr(zero,..)).
If you break those expressions into two statements
at separate lines, everything is fine.
But the C interface is too clever,
and rewrites two separate statements into a functional expression on a single line.
Such code is generated whenever you call
buildMalloc, buildArrayMalloc, sizeOf (called by buildMalloc), or alignOf.
One possible way is to write a getelementptr expression
containing a nullptr in a way
that hides the constant nature of nullptr.

    ptr <- alloca
    store (value zero) ptr
    z <- load ptr
    size <- bitcast =<<
       getElementPtr (z :: Value (Ptr a)) (getAllocArg s, ())

However, I found that bitcast on pointers causes no problems.
Thus I switched to using pointers for size quantities.
This still allows for optimizations involving pointers.
-}

-- XXX What's the type returned by arrayMalloc?
-- | Allocate heap (array) memory.
arrayMalloc :: forall a r s . (IsSized a, AllocArg s) =>
               s -> CodeGenFunction r (Value (Ptr a)) -- XXX
arrayMalloc s = do
    func <- CodeGen.staticNamedFunction "alignedMalloc" alignedMalloc
--    func <- externFunction "malloc"

    size <- sizeOfArray (LP.Proxy :: LP.Proxy a) (getAllocArg s)
    alignment <- alignOf (LP.Proxy :: LP.Proxy a)
    bitcast =<<
       call
          (func :: Function (Ptr Word8 -> Ptr Word8 -> IO (Ptr Word8)))
          size
          alignment

-- XXX What's the type returned by malloc
-- | Allocate stack memory.
alloca :: forall a r . (IsSized a) => CodeGenFunction r (Value (Ptr a))
alloca =
    liftM Value $
    withCurrentBuilder $ \ bldPtr -> do
      typ <- typeRef (LP.Proxy :: LP.Proxy a)
      U.withEmptyCString $ FFI.buildAlloca bldPtr typ

-- XXX What's the type returned by arrayAlloca?
-- | Allocate stack (array) memory.
arrayAlloca :: forall a r s . (IsSized a, AllocArg s) =>
               s -> CodeGenFunction r (Value (Ptr a))
arrayAlloca s =
    liftM Value $
    withCurrentBuilder $ \ bldPtr -> do
      typ <- typeRef (LP.Proxy :: LP.Proxy a)
      U.withEmptyCString $
        FFI.buildArrayAlloca bldPtr typ (case getAllocArg s of Value v -> v)

-- FFI.buildFree deprecated since LLVM-2.7
-- XXX What's the type of free?
-- | Free heap memory.
free :: (IsType a) => Value (Ptr a) -> CodeGenFunction r ()
free ptr = do
    func <- CodeGen.staticNamedFunction "alignedFree" alignedFree
--    func <- externFunction "free"
    _ <- call (func :: Function (Ptr Word8 -> IO ())) =<< bitcast ptr
    return ()


-- | If we want to export that, then we should have a Size type
-- This is the official implementation,
-- but it suffers from the ptrtoint(gep) bug.
_sizeOf ::
    forall a r.
    (IsSized a) => LP.Proxy a -> CodeGenFunction r (Value Word64)
_sizeOf a =
    liftIO $ liftM Value $
    FFI.sizeOf =<< typeRef a

_alignOf ::
    forall a r.
    (IsSized a) => LP.Proxy a -> CodeGenFunction r (Value Word64)
_alignOf a =
    liftIO $ liftM Value $
    FFI.alignOf =<< typeRef a


-- Here are reimplementation from Constants.cpp that avoid the ptrtoint(gep) bug #8281.
-- see ConstantExpr::getSizeOf
sizeOfArray ::
    forall a r . (IsSized a) =>
    LP.Proxy a -> Value Word32 -> CodeGenFunction r (Value (Ptr Word8))
sizeOfArray _ len =
    bitcast =<<
       getElementPtr (value zero :: Value (Ptr a)) (len, ())

-- see ConstantExpr::getAlignOf
alignOf ::
    forall a r . (IsSized a) =>
    LP.Proxy a -> CodeGenFunction r (Value (Ptr Word8))
alignOf _ =
    bitcast =<<
       getElementPtr0 (value zero :: Value (Ptr (Struct (Bool, (a, ()))))) (d1, ())


-- | Load a value from memory.
load :: Value (Ptr a)                   -- ^ Address to load from.
     -> CodeGenFunction r (Value a)
load (Value p) =
    liftM Value $
    withCurrentBuilder $ \ bldPtr ->
      U.withEmptyCString $ FFI.buildLoad bldPtr p

-- | Store a value in memory
store :: Value a                        -- ^ Value to store.
      -> Value (Ptr a)                  -- ^ Address to store to.
      -> CodeGenFunction r ()
store (Value v) (Value p) = do
    withCurrentBuilder_ $ \ bldPtr ->
      FFI.buildStore bldPtr v p
    return ()

-- | Address arithmetic.  See LLVM description.
-- (The type isn't as accurate as it should be.)
_getElementPtrDynamic :: (IsInteger i) =>
    Value (Ptr a) -> [Value i] -> CodeGenFunction r (Value (Ptr b))
_getElementPtrDynamic (Value ptr) ixs =
    liftM Value $
    withCurrentBuilder $ \ bldPtr ->
      U.withArrayLen [ v | Value v <- ixs ] $ \ idxLen idxPtr ->
        U.withEmptyCString $
          FFI.buildGEP bldPtr ptr idxPtr (fromIntegral idxLen)

-- | Address arithmetic.  See LLVM description.
-- The index is a nested tuple of the form @(i1,(i2,( ... ())))@.
-- (This is without a doubt the most confusing LLVM instruction, but the types help.)
getElementPtr :: forall a o i r . (GetElementPtr o i, IsIndexArg a) =>
                 Value (Ptr o) -> (a, i) -> CodeGenFunction r (Value (Ptr (ElementPtrType o i)))
getElementPtr (Value ptr) (a, ixs) =
    let ixl = getArg a : getIxList (LP.Proxy :: LP.Proxy o) ixs in
    liftM Value $
    withCurrentBuilder $ \ bldPtr ->
      U.withArrayLen ixl $ \ idxLen idxPtr ->
        U.withEmptyCString $
          FFI.buildGEP bldPtr ptr idxPtr (fromIntegral idxLen)

-- | Like getElementPtr, but with an initial index that is 0.
-- This is useful since any pointer first need to be indexed off the pointer, and then into
-- its actual value.  This first indexing is often with 0.
getElementPtr0 :: (GetElementPtr o i) =>
                  Value (Ptr o) -> i -> CodeGenFunction r (Value (Ptr (ElementPtrType o i)))
getElementPtr0 p i = getElementPtr p (0::Word32, i)

_getElementPtr :: forall value o i i0 r.
    (ValueCons value, GetElementPtr o i, IsIndexType i0) =>
    value (Ptr o) -> (value i0, i) ->
    CodeGenFunction r (value (Ptr (ElementPtrType o i)))
_getElementPtr vptr (a, ixs) =
    let withArgs act =
            U.withArrayLen
                (unValue a : getIxList (LP.Proxy :: LP.Proxy o) ixs) $
            \ idxLen idxPtr ->
                act idxPtr (fromIntegral idxLen)
    in  unop
            (\ptr -> withArgs $ FFI.constGEP ptr)
            (\bldPtr ptr cstr ->
                withArgs $ \idxPtr idxLen ->
                    FFI.buildGEP bldPtr ptr idxPtr idxLen cstr)
            vptr

--------------------------------------
{-
instance (IsConst a) => Show (ConstValue a) -- XXX
instance (IsConst a) => Eq (ConstValue a)

{-
instance (IsConst a) => Eq (ConstValue a) where
    ConstValue x == ConstValue y  =
        if isFloating x then ConstValue (FFI.constFCmp (fromFPPredicate  FPOEQ) x y)
                        else ConstValue (FFI.constICmp (fromIntPredicate IntEQ) x y)
    ConstValue x /= ConstValue y  =
        if isFloating x then ConstValue (FFI.constFCmp (fromFPPredicate  FPONE) x y)
                        else ConstValue (FFI.constICmp (fromIntPredicate IntNE) x y)

instance (IsConst a) => Ord (ConstValue a) where
    ConstValue x <  ConstValue y  =
        if isFloating x then ConstValue (FFI.constFCmp (fromFPPredicate  FPOLT) x y)
                        else ConstValue (FFI.constICmp (fromIntPredicate IntLT) x y)
    ConstValue x <= ConstValue y  =
        if isFloating x then ConstValue (FFI.constFCmp (fromFPPredicate  FPOLE) x y)
                        else ConstValue (FFI.constICmp (fromIntPredicate IntLE) x y)
    ConstValue x >  ConstValue y  =
        if isFloating x then ConstValue (FFI.constFCmp (fromFPPredicate  FPOGT) x y)
                        else ConstValue (FFI.constICmp (fromIntPredicate IntGT) x y)
    ConstValue x >= ConstValue y  =
        if isFloating x then ConstValue (FFI.constFCmp (fromFPPredicate  FPOGE) x y)
                        else ConstValue (FFI.constICmp (fromIntPredicate IntGE) x y)
-}

instance (Num a, IsConst a) => Num (ConstValue a) where
    ConstValue x + ConstValue y  =  ConstValue (FFI.constAdd x y)
    ConstValue x - ConstValue y  =  ConstValue (FFI.constSub x y)
    ConstValue x * ConstValue y  =  ConstValue (FFI.constMul x y)
    negate (ConstValue x)        =  ConstValue (FFI.constNeg x)
    fromInteger x                =  constOf (fromInteger x :: a)
-}