packages feed

accelerate-llvm-1.4.0.0: src/Data/Array/Accelerate/LLVM/CodeGen/Base.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE GADTs               #-}
{-# LANGUAGE LambdaCase          #-}
{-# LANGUAGE OverloadedStrings   #-}
{-# LANGUAGE RankNTypes          #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications    #-}
{-# OPTIONS_HADDOCK hide #-}
-- |
-- Module      : Data.Array.Accelerate.LLVM.CodeGen.Base
-- Copyright   : [2015..2020] The Accelerate Team
-- License     : BSD3
--
-- Maintainer  : Trevor L. McDonell <trevor.mcdonell@gmail.com>
-- Stability   : experimental
-- Portability : non-portable (GHC extensions)
--

module Data.Array.Accelerate.LLVM.CodeGen.Base (

  -- References
  Name(..),
  local, global,

  -- Arrays
  irArray,
  mutableArray,
  delayedArray,

  -- Functions & parameters
  call,
  parameter, scalarParameter, ptrParameter,
  envParam,
  arrayParam,

) where

import LLVM.AST.Type.Constant
import LLVM.AST.Type.Downcast
import LLVM.AST.Type.Function
import LLVM.AST.Type.Global
import LLVM.AST.Type.InlineAssembly
import LLVM.AST.Type.Instruction
import LLVM.AST.Type.Instruction.Volatile
import LLVM.AST.Type.Name
import LLVM.AST.Type.Operand
import LLVM.AST.Type.Representation

import Data.Array.Accelerate.Error
import Data.Array.Accelerate.LLVM.CodeGen.Environment
import Data.Array.Accelerate.LLVM.CodeGen.IR
import Data.Array.Accelerate.LLVM.CodeGen.Monad
import Data.Array.Accelerate.LLVM.CodeGen.Sugar
import Data.Array.Accelerate.Representation.Array                   ( Array, ArrayR(..) )
import Data.Array.Accelerate.Representation.Shape
import Data.Array.Accelerate.Representation.Type
import {-# SOURCE #-} Data.Array.Accelerate.LLVM.CodeGen.Exp

import qualified Data.Array.Accelerate.LLVM.Internal.LLVMPretty     as LP

import Data.Monoid
import Data.String
import Text.Printf
import qualified Data.IntMap                                        as IM
import Prelude                                                      as P


-- References
-- ----------

local :: TypeR a -> Name a -> Operands a
local  tp n = travTypeToOperands tp (\t i -> LocalReference (PrimType (ScalarPrimType t)) (rename n i))

global :: TypeR a -> Name a -> Operands a
global tp n = travTypeToOperands tp (\t i -> ConstantOperand (GlobalReference (PrimType (ScalarPrimType t)) (rename n i)))


-- Generating names for things
-- ---------------------------

-- | Names of array data components
--
arrayName :: Name (Array sh e) -> Int -> Name e'        -- for the i-th component of the ArrayData
arrayName (Name n)   i = Name (n <> fromString (printf   ".ad%d"   i))
arrayName (UnName n) i = Name (     fromString (printf "%d.ad%d" n i))

-- | Names of shape components
--
shapeName :: Name (Array sh e) -> Int -> Name sh'       -- for the i-th component of the shape structure
shapeName (Name n)   i = Name (n <> fromString (printf   ".sh%d"   i))
shapeName (UnName n) i = Name (     fromString (printf "%d.sh%d" n i))

-- | Names combined with traversing
--
rename :: Name t -> Int -> Name t'                      -- for the i-th component of the named variable
rename (Name   n) i = Name (n <> fromString (printf    "%d"   i))
rename (UnName n) i = Name (     fromString (printf "%d.%d" n i))


-- | Names of array data elements
--
{-# INLINEABLE irArray #-}
irArray
    :: ArrayR  (Array sh e)
    -> Name    (Array sh e)
    -> IRArray (Array sh e)
irArray repr@(ArrayR shr tp) n
  = IRArray repr
            (travTypeToOperands (shapeType shr) (\t i -> LocalReference (PrimType (ScalarPrimType t)) (shapeName n i)))
            (travTypeToOperands tp              (\t i -> LocalReference (PrimType (ScalarPrimType t)) (arrayName n i)))
            defaultAddrSpace
            NonVolatile

-- | Generate typed local names for array data components as well as function
-- parameters to bind those names
--
{-# INLINEABLE mutableArray #-}
mutableArray
    :: ArrayR (Array sh e)
    -> Name (Array sh e)
    -> (IRArray (Array sh e), [LP.Typed LP.Ident])
mutableArray repr name =
  ( irArray repr name
  , arrayParam repr name )

-- | Generate a delayed array representation for input arrays which come in
-- either delayed (fused) or manifest representation.
--
{-# INLINEABLE delayedArray #-}
delayedArray
    :: Name (Array sh e)
    -> MIRDelayed arch aenv (Array sh e)
    -> (IRDelayed arch aenv (Array sh e), [LP.Typed LP.Ident])
delayedArray name = \case
  IRDelayedJust a -> (a, [])
  IRDelayedNothing repr ->
    let (arr, param) = mutableArray repr name
    in ( IRDelayed { delayedRepr        = repr
                  , delayedExtent      = return (irArrayShape arr)
                  , delayedIndex       = IRFun1 (indexArray arr)
                  , delayedLinearIndex = IRFun1 (linearIndexArray arr)
                  }
      , param
      )

{-# INLINEABLE travTypeToList #-}
travTypeToList
    :: forall tp a.
       TypeR tp
    -> (forall s. ScalarType s -> Int -> a)
    -> [a]
travTypeToList tp f = snd $ go tp 0
  where
    -- DANGER: [1] must traverse in the same order as [2]
    go :: TypeR s -> Int -> (Int, [a])
    go TupRunit         i = (i,   [])
    go (TupRsingle t')  i = (i+1, [f t' i])
    go (TupRpair t2 t1) i = let (i1, r1) = go t1 i
                                (i2, r2) = go t2 i1
                            in
                            (i2, r2 ++ r1)

{-# INLINEABLE travTypeToOperands #-}
travTypeToOperands
    :: TypeR t
    -> (forall s. ScalarType s -> Int -> Operand s)
    -> Operands t
travTypeToOperands tp f = snd $ go tp 0
  where
    -- DANGER: [2] must traverse in the same order as [1]
    go :: TypeR s -> Int -> (Int, Operands s)
    go TupRunit         i = (i,   OP_Unit)
    go (TupRsingle t')  i = (i+1, ir t' $ f t' i)
    go (TupRpair t2 t1) i = let (i1, r1) = go t1 i
                                (i2, r2) = go t2 i1
                            in
                            (i2, OP_Pair r2 r1)

-- travTypeToOperandsPtr
--     :: forall t. Elt t
--     => AddrSpace
--     -> t {- dummy -}
--     -> (forall s. ScalarType s -> Int -> Operand (Ptr s))
--     -> Operands (Ptr t)
-- travTypeToOperandsPtr as t f = snd $ go (eltType @t) 0
--   where
--     -- DANGER: [2] must traverse in the same order as [1]
--     -- go :: TypeR s -> Int -> (Int, Operands (Ptr s))
--     go :: TypeR (EltRepr s) -> Int -> (Int, Operands (EltRepr (Ptr s)))   -- TLM: ugh ):
--     go TypeRunit         i = (i,   OP_Unit)
--     go (TypeRscalar t')  i = (i+1, ir (PtrPrimType t' as) $ f t' i)
--     go (TypeRpair t2 t1) i = let (i1, r1) = go t1 i
--                                  (i2, r2) = go t2 i1
--                              in
--                              (i2, OP_Pair r2 r1)


-- Function parameters
-- -------------------

-- | Call a global function.
--
-- If the function being called has a special prefix (see 'labelIsAccPrelude'),
-- the call instruction is generated as-is; otherwise (the common case), a
-- function declaration is also inserted into the symbol table, resulting in a
-- 'declare' statement in the LLVM module. A special prelude function is
-- currently not allowed to have any function attributes.
--
-- TODO: The original llvm-hs code put function attributes both on the external
-- function declaration and on the call instruction; this code puts them only
-- on the declaration. We should compare LLVM IR / benchmark to see if this is
-- an issue (@llvm-pretty@ does not yet support function attributes on call
-- instructions).
--
call :: GlobalFunction args t -> [FunctionAttribute] -> CodeGen arch (Operands t)
call f attrs = do
  let decl      = (downcast f) { LP.decAttrs = downcast attrs }
      --
      go :: GlobalFunction args t -> (Label, Function (Either InlineAssembly Label) args t)
      go (Body t k l) = (l, Body t k (Right l))
      go (Lam t x l)  = Lam t x <$> go l
  --
  let (lab, f') = go f
  if labelIsAccPrelude lab
    then case attrs of
           [] -> return ()
           _ -> internalError "Function attributes passed to a call to an acc prelude LLVM function"
    else declareExternFunc decl

  instr (Call f')


parameter :: TypeR t -> Name t -> [LP.Typed LP.Ident]
parameter tp n = travTypeToList tp (\s i -> LP.Typed (downcast s) (nameToPrettyI (rename n i)))

scalarParameter :: ScalarType t -> Name t -> LP.Typed LP.Ident
scalarParameter t x = downcast (Parameter (ScalarPrimType t) x)

ptrParameter :: ScalarType t -> Name (Ptr t) -> LP.Typed LP.Ident
ptrParameter t x = downcast (Parameter (PtrPrimType (ScalarPrimType t) defaultAddrSpace) x)


-- | Unpack the array environment into a set of input parameters to a function.
-- The environment here refers only to the actual free array variables that are
-- accessed by the function.
--
envParam :: forall aenv. Gamma aenv -> [LP.Typed LP.Ident]
envParam aenv = concatMap (\(Label n, Idx' repr _) -> arrayParam repr (Name n)) (IM.elems aenv)


-- | Generate function parameters for an Array with given base name.
--
{-# INLINEABLE arrayParam #-}
arrayParam
    :: ArrayR (Array sh e)
    -> Name (Array sh e)
    -> [LP.Typed LP.Ident]
arrayParam (ArrayR shr tp) name = ad ++ sh
  where
    ad = travTypeToList tp              (\t i -> ptrParameter    t (arrayName name i))
    sh = travTypeToList (shapeType shr) (\t i -> scalarParameter t (shapeName name i))