diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,24 @@
+# Change Log
+
+Notable changes to the project will be documented in this file.
+
+The format is based on [Keep a Changelog](http://keepachangelog.com/) and the
+project adheres to the [Haskell Package Versioning
+Policy (PVP)](https://pvp.haskell.org)
+
+## [1.1.0.0] - 2017-09-21
+### Added
+ * support for GHC-8.2
+ * support for LLVM-5.0
+
+### Changed
+ * internal restructuring of compile/link phases
+
+
+## [1.0.0.0] - 2017-03-31
+  * initial release
+
+
+[1.1.0.0]:    https://github.com/AccelerateHS/accelerate-llvm/compare/1.0.0.0...1.1.0.0
+[1.0.0.0]:    https://github.com/AccelerateHS/accelerate-llvm/compare/be7f91295f77434b2103c70aa1cabb6a4f2b09a8...1.0.0.0
+
diff --git a/Control/Parallel/Meta.hs b/Control/Parallel/Meta.hs
--- a/Control/Parallel/Meta.hs
+++ b/Control/Parallel/Meta.hs
@@ -22,13 +22,14 @@
 
 import Control.Monad
 import Control.Parallel.Meta.Worker
+import Data.ByteString.Short                                        ( ShortByteString )
 import Data.Concurrent.Deque.Class
 import Data.Monoid
-import Data.Sequence                                            ( Seq )
-import Data.Range.Range                                         as R
-import qualified Data.Vector                                    as V
+import Data.Sequence                                                ( Seq )
+import Data.Range.Range                                             as R
+import qualified Data.Vector                                        as V
 
-import GHC.Base                                                 ( quotInt, remInt )
+import GHC.Base                                                     ( quotInt, remInt )
 
 
 -- | The 'Startup' component of a 'Resource' is a callback that implements
@@ -95,10 +96,10 @@
 --
 data Executable = Executable {
     runExecutable
-        :: String       -- Function name
-        -> Int          -- Profitable parallelism threshold (PPT)
-        -> Range        -- The range to execute over
-        -> Action       -- The main function to execute
+        :: ShortByteString    -- Function name
+        -> Int                -- Profitable parallelism threshold (PPT)
+        -> Range              -- The range to execute over
+        -> Action             -- The main function to execute
         -> IO ()
   }
 
@@ -213,9 +214,7 @@
             -- make sure all the work queues are exhausted before exiting.
             _             -> do
               done <- exhausted workers
-              if done
-                 then return ()
-                 else loop
+              unless done loop
 
     when (end > start) $ pushL (workpool me) (IE start end)
     loop
diff --git a/Control/Parallel/Meta/Resource/Backoff.hs b/Control/Parallel/Meta/Resource/Backoff.hs
--- a/Control/Parallel/Meta/Resource/Backoff.hs
+++ b/Control/Parallel/Meta/Resource/Backoff.hs
@@ -1,5 +1,4 @@
 {-# LANGUAGE RecordWildCards #-}
-{-# LANGUAGE ViewPatterns    #-}
 {-# OPTIONS_HADDOCK hide #-}
 -- |
 -- Module      : Control.Parallel.Meta.Resource.Backoff
@@ -27,11 +26,11 @@
 ) where
 
 import Control.Concurrent
-import Data.IORef
 import Text.Printf
 
 import Control.Parallel.Meta
 import Control.Parallel.Meta.Worker
+import Data.IORef.Storable
 import Data.Range.Range                                         as R
 import qualified Data.Vector                                    as V
 import qualified Data.Array.Accelerate.Debug                    as Debug
diff --git a/Control/Parallel/Meta/Resource/SMP.hs b/Control/Parallel/Meta/Resource/SMP.hs
--- a/Control/Parallel/Meta/Resource/SMP.hs
+++ b/Control/Parallel/Meta/Resource/SMP.hs
@@ -1,5 +1,4 @@
-{-# LANGUAGE BangPatterns    #-}
-{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE BangPatterns #-}
 {-# OPTIONS_HADDOCK hide #-}
 -- |
 -- Module      : Control.Parallel.Meta.Resource.SMP
@@ -27,15 +26,14 @@
 ) where
 
 -- accelerate
-import Data.Range.Range
 import Control.Parallel.Meta
 import Control.Parallel.Meta.Worker
-
+import Data.IORef.Storable
+import Data.Range.Range
 import qualified Data.Array.Accelerate.Debug            as Debug
 
 -- standard library
 import Data.Concurrent.Deque.Class
-import Data.IORef
 import System.Random.MWC
 import Text.Printf
 import qualified Data.Vector                            as V
@@ -77,7 +75,7 @@
 
           loop 0      = do
             message myId "work search failed"
-            modifyIORef' (consecutiveFailures me) (+1)
+            modifyIORef (consecutiveFailures me) (+1)
             return Nothing
 
           loop n      = do
diff --git a/Control/Parallel/Meta/Trans/LBS.hs b/Control/Parallel/Meta/Trans/LBS.hs
--- a/Control/Parallel/Meta/Trans/LBS.hs
+++ b/Control/Parallel/Meta/Trans/LBS.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE BangPatterns    #-}
 {-# LANGUAGE RecordWildCards #-}
 {-# OPTIONS_HADDOCK hide #-}
 -- |
diff --git a/Control/Parallel/Meta/Worker.hs b/Control/Parallel/Meta/Worker.hs
--- a/Control/Parallel/Meta/Worker.hs
+++ b/Control/Parallel/Meta/Worker.hs
@@ -21,6 +21,7 @@
 ) where
 
 -- accelerate
+import Data.IORef.Storable
 import qualified Data.Array.Accelerate.Debug                    as Debug
 
 -- standard library
@@ -28,7 +29,6 @@
 import Control.Concurrent
 import Control.Exception
 import Control.Monad
-import Data.IORef
 import Data.Range.Range
 import Data.Vector                                              ( Vector )
 import System.IO.Unsafe
@@ -77,7 +77,7 @@
   -- Work scheduling
   , workpool            :: {-# UNPACK #-} !(WSDeque Range)
   , consecutiveFailures :: {-# UNPACK #-} !(IORef Int)
-  , rngState            :: {-# UNPACK #-} !GenIO        -- don't unpack: too large?
+  , rngState            :: {-# UNPACK #-} !GenIO            -- TLM: don't unpack: too large?
 
   -- TODO: debug/work statistics
   }
@@ -102,13 +102,16 @@
 -- threadId the worker is spawned on, because we might have multiple work groups
 -- (i.e. for CPUs and GPUs)
 --
+-- TLM: This isn't a bottleneck, but it would have been better to use something
+-- like 'Data.Atomic' as in the base Accelerate package.
+--
 {-# NOINLINE uniqueSupply #-}
-uniqueSupply :: IORef Int
-uniqueSupply = unsafePerformIO $ newIORef 0
+uniqueSupply :: MVar Int
+uniqueSupply = unsafePerformIO $ newMVar 0
 
 -- Generate  a fresh identifier. Note that the bang pattern is important.
 freshId :: IO Int
-freshId = atomicModifyIORef uniqueSupply (\n -> let !n' = n+1 in (n', n))
+freshId = modifyMVar uniqueSupply (\n -> let !n' = n+1 in return (n', n))
 
 
 -- | Create a set of workers. This is a somewhat expensive function, so it is
diff --git a/Data/Array/Accelerate/LLVM/AST.hs b/Data/Array/Accelerate/LLVM/AST.hs
new file mode 100644
--- /dev/null
+++ b/Data/Array/Accelerate/LLVM/AST.hs
@@ -0,0 +1,174 @@
+{-# LANGUAGE ConstraintKinds  #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE GADTs            #-}
+{-# LANGUAGE TypeOperators    #-}
+{-# OPTIONS_HADDOCK hide #-}
+-- |
+-- Module      : Data.Array.Accelerate.LLVM.AST
+-- Copyright   : [2017] Trevor L. McDonell
+-- License     : BSD3
+--
+-- Maintainer  : Trevor L. McDonell <tmcdonell@cse.unsw.edu.au>
+-- Stability   : experimental
+-- Portability : non-portable (GHC extensions)
+--
+
+module Data.Array.Accelerate.LLVM.AST (
+
+  PreOpenAccCommand(..),
+  PreOpenAccSkeleton(..),
+
+  PreAfun, PreOpenAfun(..),
+  PreFun,  PreOpenFun(..),
+  PreExp,  PreOpenExp(..),
+  Idx(..), Val(..), prj,
+
+) where
+
+import Data.Array.Accelerate.Array.Sugar
+import Data.Array.Accelerate.Product
+import Data.Array.Accelerate.AST
+    ( PreOpenAfun(..), PreOpenExp(..), PreOpenFun(..), Idx(..), Val(..), PreAfun, PreFun, PreExp, prj )
+
+
+-- | Non-computational array program operations, parameterised over array
+-- variables represented as de Bruijn indices.
+--
+data PreOpenAccCommand acc aenv a where
+
+  Avar        :: Arrays arrs
+              => Idx                   aenv arrs
+              -> PreOpenAccCommand acc aenv arrs
+
+  Alet        :: (Arrays bnd, Arrays body)
+              => acc                   aenv        bnd
+              -> acc                   (aenv, bnd) body
+              -> PreOpenAccCommand acc aenv        body
+
+  Use         :: Arrays arrs
+              => ArrRepr arrs
+              -> PreOpenAccCommand acc aenv arrs
+
+  Unit        :: Elt e
+              => PreExp            acc aenv e
+              -> PreOpenAccCommand acc aenv (Scalar e)
+
+  Atuple      :: (Arrays arrs, IsAtuple arrs)
+              => Atuple           (acc aenv) (TupleRepr arrs)
+              -> PreOpenAccCommand acc aenv  arrs
+
+  Aprj        :: (Arrays arrs, IsAtuple arrs, Arrays a)
+              => TupleIdx (TupleRepr arrs) a
+              -> acc                   aenv arrs
+              -> PreOpenAccCommand acc aenv a
+
+  Apply       :: (Arrays as, Arrays bs)
+              => PreOpenAfun       acc aenv (as -> bs)
+              -> acc                   aenv as
+              -> PreOpenAccCommand acc aenv bs
+
+  Aforeign    :: (Arrays as, Arrays bs, Foreign asm)
+              => asm                       (as -> bs)
+              -> acc                   aenv as
+              -> PreOpenAccCommand acc aenv bs
+
+  Acond       :: Arrays arrs
+              => PreExp            acc aenv Bool
+              -> acc                   aenv arrs
+              -> acc                   aenv arrs
+              -> PreOpenAccCommand acc aenv arrs
+
+  Awhile      :: Arrays arrs
+              => PreOpenAfun       acc aenv (arrs -> Scalar Bool)
+              -> PreOpenAfun       acc aenv (arrs -> arrs)
+              -> acc                   aenv arrs
+              -> PreOpenAccCommand acc aenv arrs
+
+  Reshape     :: (Shape sh, Shape sh', Elt e)
+              => PreExp            acc aenv sh
+              -> Idx                   aenv (Array sh' e)
+              -> PreOpenAccCommand acc aenv (Array sh  e)
+
+  Unzip       :: (Elt tup, Elt e)
+              => TupleIdx (TupleRepr tup) e
+              -> Idx                   aenv (Array sh tup)
+              -> PreOpenAccCommand acc aenv (Array sh e)
+
+
+-- | Collective array computations parameterised over array variables
+-- represented as de Bruijn indices.
+--
+data PreOpenAccSkeleton acc aenv a where
+
+  Map         :: (Shape sh, Elt e)
+              => PreExp             acc aenv sh
+              -> PreOpenAccSkeleton acc aenv (Array sh e)
+
+  Generate    :: (Shape sh, Elt e)
+              => PreExp             acc aenv sh
+              -> PreOpenAccSkeleton acc aenv (Array sh e)
+
+  Transform   :: (Shape sh, Elt e)
+              => PreExp             acc aenv sh
+              -> PreOpenAccSkeleton acc aenv (Array sh e)
+
+  Backpermute :: (Shape sh, Elt e)
+              => PreExp             acc aenv sh
+              -> PreOpenAccSkeleton acc aenv (Array sh e)
+
+  Fold        :: (Shape sh, Elt e)
+              => PreExp             acc aenv (sh :. Int)
+              -> PreOpenAccSkeleton acc aenv (Array sh e)
+
+  Fold1       :: (Shape sh, Elt e)
+              => PreExp             acc aenv (sh :. Int)
+              -> PreOpenAccSkeleton acc aenv (Array sh e)
+
+  FoldSeg     :: (Shape sh, Elt e)
+              => PreExp             acc aenv (sh :. Int)
+              -> PreExp             acc aenv (Z  :. Int)
+              -> PreOpenAccSkeleton acc aenv (Array (sh:.Int) e)
+
+  Fold1Seg    :: (Shape sh, Elt e)
+              => PreExp             acc aenv (sh :. Int)
+              -> PreExp             acc aenv (Z  :. Int)
+              -> PreOpenAccSkeleton acc aenv (Array (sh:.Int) e)
+
+  Scanl       :: (Shape sh, Elt e)
+              => PreExp             acc aenv (sh :. Int)
+              -> PreOpenAccSkeleton acc aenv (Array (sh:.Int) e)
+
+  Scanl1      :: (Shape sh, Elt e)
+              => PreExp             acc aenv (sh :. Int)
+              -> PreOpenAccSkeleton acc aenv (Array (sh:.Int) e)
+
+  Scanl'      :: (Shape sh, Elt e)
+              => PreExp             acc aenv (sh :. Int)
+              -> PreOpenAccSkeleton acc aenv (Array (sh:.Int) e, Array sh e)
+
+  Scanr       :: (Shape sh, Elt e)
+              => PreExp             acc aenv (sh :. Int)
+              -> PreOpenAccSkeleton acc aenv (Array (sh:.Int) e)
+
+  Scanr1      :: (Shape sh, Elt e)
+              => PreExp             acc aenv (sh :. Int)
+              -> PreOpenAccSkeleton acc aenv (Array (sh:.Int) e)
+
+  Scanr'      :: (Shape sh, Elt e)
+              => PreExp             acc aenv (sh :. Int)
+              -> PreOpenAccSkeleton acc aenv (Array (sh:.Int) e, Array sh e)
+
+  Permute     :: (Shape sh, Shape sh', Elt e)
+              => PreExp             acc aenv sh               -- source
+              -> acc                    aenv (Array sh' e)    -- default values
+              -> PreOpenAccSkeleton acc aenv (Array sh' e)
+
+  Stencil     :: (Shape sh, Elt a, Elt b)
+              => Idx                    aenv (Array sh a)
+              -> PreOpenAccSkeleton acc aenv (Array sh b)
+
+  Stencil2    :: (Shape sh, Elt a, Elt b, Elt c)
+              => Idx                    aenv (Array sh a)
+              -> Idx                    aenv (Array sh b)
+              -> PreOpenAccSkeleton acc aenv (Array sh c)
+
diff --git a/Data/Array/Accelerate/LLVM/Array/Data.hs b/Data/Array/Accelerate/LLVM/Array/Data.hs
--- a/Data/Array/Accelerate/LLVM/Array/Data.hs
+++ b/Data/Array/Accelerate/LLVM/Array/Data.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE BangPatterns        #-}
 {-# LANGUAGE GADTs               #-}
 {-# LANGUAGE RankNTypes          #-}
 {-# LANGUAGE ScopedTypeVariables #-}
diff --git a/Data/Array/Accelerate/LLVM/CodeGen.hs b/Data/Array/Accelerate/LLVM/CodeGen.hs
--- a/Data/Array/Accelerate/LLVM/CodeGen.hs
+++ b/Data/Array/Accelerate/LLVM/CodeGen.hs
@@ -24,11 +24,10 @@
 ) where
 
 -- accelerate
-import Data.Array.Accelerate.AST                                hiding ( Val(..), prj, stencil )
-import Data.Array.Accelerate.Array.Sugar                        hiding ( Foreign )
+import Data.Array.Accelerate.AST                                    hiding ( Val(..), prj, stencil )
+import Data.Array.Accelerate.Array.Sugar                            hiding ( Foreign )
 import Data.Array.Accelerate.Error
 import Data.Array.Accelerate.Trafo
-import Data.Array.Accelerate.Type
 
 import Data.Array.Accelerate.LLVM.Target
 
@@ -42,10 +41,11 @@
 import Data.Array.Accelerate.LLVM.CodeGen.Permute
 import Data.Array.Accelerate.LLVM.CodeGen.Skeleton
 import Data.Array.Accelerate.LLVM.CodeGen.Sugar
+import Data.Array.Accelerate.LLVM.Compile.Cache
 import Data.Array.Accelerate.LLVM.Foreign
 
 -- standard library
-import Prelude                                                  hiding ( map, scanl, scanl1, scanr, scanr1 )
+import Prelude                                                      hiding ( map, scanl, scanl1, scanr, scanr1 )
 
 
 -- | Generate code for a given target architecture.
@@ -54,32 +54,33 @@
 llvmOfOpenAcc
     :: forall arch aenv arrs. (Target arch, Skeleton arch, Intrinsic arch, Foreign arch)
     => arch
+    -> UID
     -> DelayedOpenAcc aenv arrs
     -> Gamma aenv
     -> Module arch aenv arrs
-llvmOfOpenAcc _    Delayed{}       _    = $internalError "llvmOfOpenAcc" "expected manifest array"
-llvmOfOpenAcc arch (Manifest pacc) aenv = runLLVM $
+llvmOfOpenAcc _    _    Delayed{}      _    = $internalError "llvmOfOpenAcc" "expected manifest array"
+llvmOfOpenAcc arch uid (Manifest pacc) aenv = runLLVM $
   case pacc of
     -- Producers
-    Map f a                 -> map arch aenv (travF1 f) (travD a)
-    Generate _ f            -> generate arch aenv (travF1 f)
-    Transform _ p f a       -> transform arch aenv (travF1 p) (travF1 f) (travD a)
-    Backpermute _ p a       -> backpermute arch aenv (travF1 p) (travD a)
+    Map f a                 -> map arch uid aenv (travF1 f) (travD a)
+    Generate _ f            -> generate arch uid aenv (travF1 f)
+    Transform _ p f a       -> transform arch uid aenv (travF1 p) (travF1 f) (travD a)
+    Backpermute _ p a       -> backpermute arch uid aenv (travF1 p) (travD a)
 
     -- Consumers
-    Fold f z a              -> fold arch aenv (travF2 f) (travE z) (travD a)
-    Fold1 f a               -> fold1 arch aenv (travF2 f) (travD a)
-    FoldSeg f z a s         -> foldSeg arch aenv (travF2 f) (travE z) (travD a) (travD s)
-    Fold1Seg f a s          -> fold1Seg arch aenv (travF2 f) (travD a) (travD s)
-    Scanl f z a             -> scanl arch aenv (travF2 f) (travE z) (travD a)
-    Scanl' f z a            -> scanl' arch aenv (travF2 f) (travE z) (travD a)
-    Scanl1 f a              -> scanl1 arch aenv (travF2 f) (travD a)
-    Scanr f z a             -> scanr arch aenv (travF2 f) (travE z) (travD a)
-    Scanr' f z a            -> scanr' arch aenv (travF2 f) (travE z) (travD a)
-    Scanr1 f a              -> scanr1 arch aenv (travF2 f) (travD a)
-    Permute f _ p a         -> permute arch aenv (travPF f) (travF1 p) (travD a)
-    Stencil f b a           -> stencil arch aenv (travF1 f) (travB a b) (travM a)
-    Stencil2 f b1 a1 b2 a2  -> stencil2 arch aenv (travF2 f) (travB a1 b1) (travM a1) (travB a2 b2) (travM a2)
+    Fold f z a              -> fold arch uid aenv (travF2 f) (travE z) (travD a)
+    Fold1 f a               -> fold1 arch uid aenv (travF2 f) (travD a)
+    FoldSeg f z a s         -> foldSeg arch uid aenv (travF2 f) (travE z) (travD a) (travD s)
+    Fold1Seg f a s          -> fold1Seg arch uid aenv (travF2 f) (travD a) (travD s)
+    Scanl f z a             -> scanl arch uid aenv (travF2 f) (travE z) (travD a)
+    Scanl' f z a            -> scanl' arch uid aenv (travF2 f) (travE z) (travD a)
+    Scanl1 f a              -> scanl1 arch uid aenv (travF2 f) (travD a)
+    Scanr f z a             -> scanr arch uid aenv (travF2 f) (travE z) (travD a)
+    Scanr' f z a            -> scanr' arch uid aenv (travF2 f) (travE z) (travD a)
+    Scanr1 f a              -> scanr1 arch uid aenv (travF2 f) (travD a)
+    Permute f _ p a         -> permute arch uid aenv (travPF f) (travF1 p) (travD a)
+    Stencil f b a           -> stencil arch uid aenv (travF1 f) (travB b) (travM a)
+    Stencil2 f b1 a1 b2 a2  -> stencil2 arch uid aenv (travF2 f) (travB b1) (travM a1) (travB b2) (travM a2)
 
     -- Non-computation forms: sadness
     Alet{}                  -> unexpectedError
@@ -121,16 +122,14 @@
     travE :: DelayedExp aenv t -> IRExp arch aenv t
     travE e = llvmOfOpenExp arch e Empty aenv
 
-    travB :: forall sh e. Elt e
-          => DelayedOpenAcc aenv (Array sh e)
-          -> Boundary (EltRepr e)
-          -> Boundary (IR e)
-    travB _ Clamp        = Clamp
-    travB _ Mirror       = Mirror
-    travB _ Wrap         = Wrap
-    travB _ (Constant c)
-      = Constant
-      $ IR (constant (eltType (undefined::e)) c)
+    travB :: forall sh e.
+             PreBoundary DelayedOpenAcc aenv (Array sh e)
+          -> IRBoundary arch aenv (Array sh e)
+    travB Clamp        = IRClamp
+    travB Mirror       = IRMirror
+    travB Wrap         = IRWrap
+    travB (Constant c) = IRConstant $ IR (constant (eltType (undefined::e)) c)
+    travB (Function f) = IRFunction $ travF1 f
 
     -- sadness
     fusionError, unexpectedError :: error
diff --git a/Data/Array/Accelerate/LLVM/CodeGen/Arithmetic.hs b/Data/Array/Accelerate/LLVM/CodeGen/Arithmetic.hs
--- a/Data/Array/Accelerate/LLVM/CodeGen/Arithmetic.hs
+++ b/Data/Array/Accelerate/LLVM/CodeGen/Arithmetic.hs
@@ -19,15 +19,17 @@
   where
 
 -- standard/external libraries
-import Prelude                                                  ( Eq, Num, Either(..), ($), (++), (==), undefined, otherwise, flip, fromInteger )
+import Prelude                                                      ( Eq, Num, Either(..), ($), (==), undefined, otherwise, flip, fromInteger )
 import Control.Applicative
 import Control.Monad
-import Data.Bits                                                ( finiteBitSize )
+import Data.Bits                                                    ( finiteBitSize )
+import Data.ByteString.Short                                        ( ShortByteString )
+import Data.Monoid
 import Data.String
+import Foreign.Storable                                             ( sizeOf )
 import Text.Printf
-import Foreign.Storable                                         ( sizeOf )
-import qualified Prelude                                        as P
-import qualified Data.Ord                                       as Ord
+import qualified Data.Ord                                           as Ord
+import qualified Prelude                                            as P
 
 -- accelerate
 import Data.Array.Accelerate.Error
@@ -269,7 +271,7 @@
 popCount :: forall a. IntegralType a -> IR a -> CodeGen (IR Int)
 popCount i x
   | IntegralDict <- integralDict i
-  = do let ctpop = Label $ printf "llvm.ctpop.i%d" (finiteBitSize (undefined::a))
+  = do let ctpop = fromString $ printf "llvm.ctpop.i%d" (finiteBitSize (undefined::a))
            p     = ScalarPrimType (NumScalarType (IntegralNumType i))
            t     = PrimType p
        --
@@ -280,7 +282,7 @@
 countLeadingZeros :: forall a. IntegralType a -> IR a -> CodeGen (IR Int)
 countLeadingZeros i x
   | IntegralDict <- integralDict i
-  = do let clz = Label $ printf "llvm.ctlz.i%d" (finiteBitSize (undefined::a))
+  = do let clz = fromString $ printf "llvm.ctlz.i%d" (finiteBitSize (undefined::a))
            p   = ScalarPrimType (NumScalarType (IntegralNumType i))
            t   = PrimType p
        --
@@ -291,7 +293,7 @@
 countTrailingZeros :: forall a. IntegralType a -> IR a -> CodeGen (IR Int)
 countTrailingZeros i x
   | IntegralDict <- integralDict i
-  = do let clz = Label $ printf "llvm.cttz.i%d" (finiteBitSize (undefined::a))
+  = do let clz = fromString $ printf "llvm.cttz.i%d" (finiteBitSize (undefined::a))
            p   = ScalarPrimType (NumScalarType (IntegralNumType i))
            t   = PrimType p
        --
@@ -384,11 +386,29 @@
 isNaN f (op f -> x) = do
   let p = ScalarPrimType (NumScalarType (FloatingNumType f))
       t = type'
-  name <- intrinsic "isnan"
+  name <- intrinsic
+        $ case f of
+            TypeFloat{}   -> "isnanf"
+            TypeCFloat{}  -> "isnanf"
+            TypeDouble{}  -> "isnand"
+            TypeCDouble{} -> "isnand"
   r    <- call (Lam p x (Body t name)) [NoUnwind, ReadOnly]
   return r
 
+isInfinite :: FloatingType a -> IR a -> CodeGen (IR Bool)
+isInfinite f (op f -> x) = do
+  let p = ScalarPrimType (NumScalarType (FloatingNumType f))
+      t = type'
+  name <- intrinsic
+        $ case f of
+            TypeFloat{}   -> "isinff"
+            TypeCFloat{}  -> "isinff"
+            TypeDouble{}  -> "isinfd"
+            TypeCDouble{} -> "isinfd"
+  r    <- call (Lam p x (Body t name)) [NoUnwind, ReadOnly]
+  return r
 
+
 -- Operators from RealFrac
 -- -----------------------
 
@@ -653,7 +673,7 @@
 --
 -- TLM: We should really be able to construct functions of any arity.
 --
-mathf :: String -> FloatingType t -> IR t -> CodeGen (IR t)
+mathf :: ShortByteString -> FloatingType t -> IR t -> CodeGen (IR t)
 mathf n f (op f -> x) = do
   let s = ScalarPrimType (NumScalarType (FloatingNumType f))
       t = PrimType s
@@ -663,7 +683,7 @@
   return r
 
 
-mathf2 :: String -> FloatingType t -> IR t -> IR t -> CodeGen (IR t)
+mathf2 :: ShortByteString -> FloatingType t -> IR t -> IR t -> CodeGen (IR t)
 mathf2 n f (op f -> x) (op f -> y) = do
   let s = ScalarPrimType (NumScalarType (FloatingNumType f))
       t = PrimType s
@@ -672,12 +692,12 @@
   r    <- call (Lam s x (Lam s y (Body t name))) [NoUnwind, ReadOnly]
   return r
 
-lm :: FloatingType t -> String -> CodeGen Label
+lm :: FloatingType t -> ShortByteString -> CodeGen Label
 lm t n
   = intrinsic
   $ case t of
-      TypeFloat{}   -> n++"f"
-      TypeCFloat{}  -> n++"f"
+      TypeFloat{}   -> n<>"f"
+      TypeCFloat{}  -> n<>"f"
       TypeDouble{}  -> n
       TypeCDouble{} -> n
 
diff --git a/Data/Array/Accelerate/LLVM/CodeGen/Array.hs b/Data/Array/Accelerate/LLVM/CodeGen/Array.hs
--- a/Data/Array/Accelerate/LLVM/CodeGen/Array.hs
+++ b/Data/Array/Accelerate/LLVM/CodeGen/Array.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE GADTs               #-}
 {-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE TemplateHaskell     #-}
 {-# LANGUAGE ViewPatterns        #-}
 {-# OPTIONS_HADDOCK hide #-}
 -- |
@@ -77,6 +76,6 @@
 writeArrayPrim :: Volatility -> Operand (Ptr e) -> Operand int -> Operand e -> CodeGen ()
 writeArrayPrim v arr i x = do
   p <- instr' $ GetElementPtr arr [i]
-  _ <- instr' $ Store v p x
+  _ <- do_    $ Store v p x
   return ()
 
diff --git a/Data/Array/Accelerate/LLVM/CodeGen/Base.hs b/Data/Array/Accelerate/LLVM/CodeGen/Base.hs
--- a/Data/Array/Accelerate/LLVM/CodeGen/Base.hs
+++ b/Data/Array/Accelerate/LLVM/CodeGen/Base.hs
@@ -50,6 +50,9 @@
 
 import qualified LLVM.AST.Global                                    as LLVM
 
+import Data.Monoid
+import Data.String
+import Text.Printf
 import qualified Data.IntMap                                        as IM
 
 
@@ -69,14 +72,14 @@
 -- | 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 ++ ".ad" ++ show i)
-arrayName (UnName n) i = arrayName (Name (show n)) i
+arrayName (Name n)   i = Name (n <> fromString (printf ".ad%d" i))
+arrayName (UnName n) i = arrayName (fromString (show 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 ++ ".sh" ++ show i)
-shapeName (UnName n) i = shapeName (Name (show n)) i
+shapeName (Name n)   i = Name (n <> fromString (printf ".sh%d" i))
+shapeName (UnName n) i = shapeName (fromString (show n)) i
 
 -- | Names of array data elements
 --
diff --git a/Data/Array/Accelerate/LLVM/CodeGen/Constant.hs b/Data/Array/Accelerate/LLVM/CodeGen/Constant.hs
--- a/Data/Array/Accelerate/LLVM/CodeGen/Constant.hs
+++ b/Data/Array/Accelerate/LLVM/CodeGen/Constant.hs
@@ -1,5 +1,4 @@
-{-# LANGUAGE GADTs             #-}
-{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE GADTs #-}
 {-# OPTIONS_HADDOCK hide #-}
 -- |
 -- Module      : Data.Array.Accelerate.LLVM.CodeGen.Constant
diff --git a/Data/Array/Accelerate/LLVM/CodeGen/Downcast.hs b/Data/Array/Accelerate/LLVM/CodeGen/Downcast.hs
--- a/Data/Array/Accelerate/LLVM/CodeGen/Downcast.hs
+++ b/Data/Array/Accelerate/LLVM/CodeGen/Downcast.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP                   #-}
 {-# LANGUAGE FlexibleContexts      #-}
 {-# LANGUAGE FlexibleInstances     #-}
 {-# LANGUAGE GADTs                 #-}
@@ -46,6 +47,7 @@
 import qualified LLVM.AST.Type.Instruction.RMW                      as RMW
 
 import qualified LLVM.AST.Attribute                                 as L
+import qualified LLVM.AST.AddrSpace                                 as L
 import qualified LLVM.AST.CallingConvention                         as L
 import qualified LLVM.AST.Constant                                  as LC
 import qualified LLVM.AST.Float                                     as L
@@ -222,7 +224,11 @@
 
 instance Downcast Synchronisation L.SynchronizationScope where
   downcast SingleThread = L.SingleThread
+#if MIN_VERSION_llvm_hs_pure(5,0,0)
+  downcast CrossThread  = L.System
+#else
   downcast CrossThread  = L.CrossThread
+#endif
 
 instance Downcast MemoryOrdering L.MemoryOrdering where
   downcast Unordered              = L.Unordered
@@ -303,9 +309,9 @@
   downcast = L.MetadataOperand . downcast
 
 instance Downcast Metadata L.Metadata where
-  downcast (MetadataStringOperand s) = L.MDString s
-  downcast (MetadataNodeOperand n)   = L.MDNode (downcast n)
-  downcast (MetadataOperand o)       = L.MDValue (downcast o)
+  downcast (MetadataStringOperand s)   = L.MDString s
+  downcast (MetadataConstantOperand o) = L.MDValue (L.ConstantOperand o)
+  downcast (MetadataNodeOperand n)     = L.MDNode (downcast n)
 
 instance Downcast MetadataNode L.MetadataNode where
   downcast (MetadataNode n)          = L.MetadataNode (downcast n)
@@ -349,7 +355,7 @@
                              in  (downcast t : t', r, n)
 
           (args, result, name)  = trav f
-          ty                    = L.FunctionType result args False
+          ty                    = L.PointerType (L.FunctionType result args False) (L.AddrSpace 0)
       in
       Right (L.ConstantOperand (LC.GlobalReference ty name))
 
@@ -421,35 +427,35 @@
   downcast (FloatingNumType t) = downcast t
 
 instance Downcast (IntegralType a) L.Type where
-  downcast (TypeInt     _) = L.IntegerType $( [| fromIntegral (finiteBitSize (undefined :: Int)) |] )
-  downcast (TypeInt8    _) = L.IntegerType 8
-  downcast (TypeInt16   _) = L.IntegerType 16
-  downcast (TypeInt32   _) = L.IntegerType 32
-  downcast (TypeInt64   _) = L.IntegerType 64
-  downcast (TypeWord    _) = L.IntegerType $( [| fromIntegral (finiteBitSize (undefined :: Word)) |] )
-  downcast (TypeWord8   _) = L.IntegerType 8
-  downcast (TypeWord16  _) = L.IntegerType 16
-  downcast (TypeWord32  _) = L.IntegerType 32
-  downcast (TypeWord64  _) = L.IntegerType 64
-  downcast (TypeCShort  _) = L.IntegerType 16
-  downcast (TypeCUShort _) = L.IntegerType 16
-  downcast (TypeCInt    _) = L.IntegerType 32
-  downcast (TypeCUInt   _) = L.IntegerType 32
-  downcast (TypeCLong   _) = L.IntegerType $( [| fromIntegral (finiteBitSize (undefined :: CLong)) |] )
-  downcast (TypeCULong  _) = L.IntegerType $( [| fromIntegral (finiteBitSize (undefined :: CULong)) |] )
-  downcast (TypeCLLong  _) = L.IntegerType 64
-  downcast (TypeCULLong _) = L.IntegerType 64
+  downcast TypeInt{}     = L.IntegerType $( [| fromIntegral (finiteBitSize (undefined :: Int)) |] )
+  downcast TypeInt8{}    = L.IntegerType 8
+  downcast TypeInt16{}   = L.IntegerType 16
+  downcast TypeInt32{}   = L.IntegerType 32
+  downcast TypeInt64{}   = L.IntegerType 64
+  downcast TypeWord{}    = L.IntegerType $( [| fromIntegral (finiteBitSize (undefined :: Word)) |] )
+  downcast TypeWord8{}   = L.IntegerType 8
+  downcast TypeWord16{}  = L.IntegerType 16
+  downcast TypeWord32{}  = L.IntegerType 32
+  downcast TypeWord64{}  = L.IntegerType 64
+  downcast TypeCShort{}  = L.IntegerType 16
+  downcast TypeCUShort{} = L.IntegerType 16
+  downcast TypeCInt{}    = L.IntegerType 32
+  downcast TypeCUInt{}   = L.IntegerType 32
+  downcast TypeCLong{}   = L.IntegerType $( [| fromIntegral (finiteBitSize (undefined :: CLong)) |] )
+  downcast TypeCULong{}  = L.IntegerType $( [| fromIntegral (finiteBitSize (undefined :: CULong)) |] )
+  downcast TypeCLLong{}  = L.IntegerType 64
+  downcast TypeCULLong{} = L.IntegerType 64
 
 instance Downcast (FloatingType a) L.Type where
-  downcast (TypeFloat   _) = L.FloatingPointType 32 L.IEEE
-  downcast (TypeDouble  _) = L.FloatingPointType 64 L.IEEE
-  downcast (TypeCFloat  _) = L.FloatingPointType 32 L.IEEE
-  downcast (TypeCDouble _) = L.FloatingPointType 64 L.IEEE
+  downcast TypeFloat{}   = L.FloatingPointType L.FloatFP
+  downcast TypeDouble{}  = L.FloatingPointType L.DoubleFP
+  downcast TypeCFloat{}  = L.FloatingPointType L.FloatFP
+  downcast TypeCDouble{} = L.FloatingPointType L.DoubleFP
 
 instance Downcast (NonNumType a) L.Type where
-  downcast (TypeBool   _) = L.IntegerType 1
-  downcast (TypeChar   _) = L.IntegerType 32
-  downcast (TypeCChar  _) = L.IntegerType 8
-  downcast (TypeCSChar _) = L.IntegerType 8
-  downcast (TypeCUChar _) = L.IntegerType 8
+  downcast TypeBool{}   = L.IntegerType 1
+  downcast TypeChar{}   = L.IntegerType 32
+  downcast TypeCChar{}  = L.IntegerType 8
+  downcast TypeCSChar{} = L.IntegerType 8
+  downcast TypeCUChar{} = L.IntegerType 8
 
diff --git a/Data/Array/Accelerate/LLVM/CodeGen/Environment.hs b/Data/Array/Accelerate/LLVM/CodeGen/Environment.hs
--- a/Data/Array/Accelerate/LLVM/CodeGen/Environment.hs
+++ b/Data/Array/Accelerate/LLVM/CodeGen/Environment.hs
@@ -16,6 +16,8 @@
   where
 
 import Data.IntMap                                              ( IntMap )
+import Data.String
+import Text.Printf
 import qualified Data.IntMap                                    as IM
 
 import Data.Array.Accelerate.AST                                ( Idx(..), idxToInt )
@@ -82,7 +84,7 @@
 makeGamma = snd . IM.mapAccum (\n ix -> (n+1, toAval n ix)) 0
   where
     toAval :: Int -> Idx' aenv -> (Label, Idx' aenv)
-    toAval n ix = (Label ("fv" ++ show n), ix)
+    toAval n ix = (fromString (printf "fv%d" n), ix)
 
 -- | A free variable
 --
diff --git a/Data/Array/Accelerate/LLVM/CodeGen/Exp.hs b/Data/Array/Accelerate/LLVM/CodeGen/Exp.hs
--- a/Data/Array/Accelerate/LLVM/CodeGen/Exp.hs
+++ b/Data/Array/Accelerate/LLVM/CodeGen/Exp.hs
@@ -165,8 +165,8 @@
         go ZeroTupIdx (PairTuple _ t) (OP_Pair _ v)
           | Just Refl <- matchTupleType t (eltType (undefined :: e))
           = v
-        go (SuccTupIdx ix) (PairTuple t _) (OP_Pair tup _)      = go ix t tup
-        go _ _ _                                                = $internalError "prjT" "inconsistent valuation"
+        go (SuccTupIdx ix) (PairTuple t _) (OP_Pair tup _) = go ix t tup
+        go _ _ _                                           = $internalError "prjT" "inconsistent valuation"
 
     cvtT :: forall t. (Elt t, IsTuple t) => Tuple (DelayedOpenExp env aenv) (TupleRepr t) -> CodeGen (IR t)
     cvtT tup = IR <$> go (eltType (undefined::t)) tup
@@ -182,7 +182,9 @@
           = do a'    <- go ta a
                IR b' <- cvtE b
                return $ OP_Pair a' b'
-        go _ _ = $internalError "cvtT" "impossible evaluation"
+        go _ _ = $internalError "cvtT"
+               $ unlines [ "impossible evaluation"
+                         , "  possible solution: ensure that the 'EltRepr' and 'ProdRepr' instances of your data type are consistent." ]
 
     linearIndex :: (Shape sh, Elt e) => IRManifest arch aenv (Array sh e) -> IR Int -> CodeGen (IR e)
     linearIndex (IRManifest v) ix =
@@ -343,6 +345,7 @@
         PrimFloor ta tb           -> A.floor ta tb           =<< cvtE x
         PrimCeiling ta tb         -> A.ceiling ta tb         =<< cvtE x
         PrimIsNaN t               -> A.isNaN t               =<< cvtE x
+        PrimIsInfinite t          -> A.isInfinite t          =<< cvtE x
         PrimLt t                  -> A.uncurry (A.lt t)      =<< cvtE x
         PrimGt t                  -> A.uncurry (A.gt t)      =<< cvtE x
         PrimLtEq t                -> A.uncurry (A.lte t)     =<< cvtE x
diff --git a/Data/Array/Accelerate/LLVM/CodeGen/IR.hs b/Data/Array/Accelerate/LLVM/CodeGen/IR.hs
--- a/Data/Array/Accelerate/LLVM/CodeGen/IR.hs
+++ b/Data/Array/Accelerate/LLVM/CodeGen/IR.hs
@@ -27,7 +27,9 @@
 import Data.Array.Accelerate.Error
 import Data.Array.Accelerate.Array.Sugar
 
+import qualified Data.ByteString.Short                              as B
 
+
 -- | The datatype 'IR' represents the LLVM IR producing a value of type 'a'.
 -- Note that the operands comprising this value are stored in representation
 -- type.
@@ -154,7 +156,7 @@
   ir' :: dict a -> Operand a -> Operands a
 
 instance IROP Type where
-  op VoidType     _  = LocalReference VoidType (Name []) -- TLM: ???
+  op VoidType     _  = LocalReference VoidType (Name B.empty) -- TLM: ???
   op (PrimType t) x  = op t x
 
   ir VoidType     _  = IR OP_Unit
@@ -163,7 +165,7 @@
   ir' VoidType     _ = OP_Unit
   ir' (PrimType t) x = ir' t x
 
-  op' VoidType     _ = LocalReference VoidType (Name [])  -- TLM: ???
+  op' VoidType     _ = LocalReference VoidType (Name B.empty)  -- TLM: ???
   op' (PrimType t) x = op' t x
 
 instance IROP PrimType where
diff --git a/Data/Array/Accelerate/LLVM/CodeGen/Intrinsic.hs b/Data/Array/Accelerate/LLVM/CodeGen/Intrinsic.hs
--- a/Data/Array/Accelerate/LLVM/CodeGen/Intrinsic.hs
+++ b/Data/Array/Accelerate/LLVM/CodeGen/Intrinsic.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE OverloadedStrings #-}
 {-# OPTIONS_HADDOCK hide #-}
 -- |
 -- Module      : Data.Array.Accelerate.LLVM.CodeGen.Intrinsic
@@ -19,6 +20,8 @@
 import LLVM.AST.Type.Name
 
 -- libraries
+import Data.Monoid
+import Data.ByteString.Short                                    ( ShortByteString )
 import Data.HashMap.Strict                                      ( HashMap )
 import qualified Data.HashMap.Strict                            as HashMap
 
@@ -35,19 +38,26 @@
 --   sqrt       -> llvm.sqrt.f64
 --
 class Intrinsic arch where
-  intrinsicForTarget :: arch -> HashMap String Label
+  intrinsicForTarget :: arch -> HashMap ShortByteString Label
   intrinsicForTarget _ = llvmIntrinsic
 
 
-llvmIntrinsic :: HashMap String Label
+llvmIntrinsic :: HashMap ShortByteString Label
 llvmIntrinsic =
   let floating base rest
-          = (base,        Label ("llvm." ++ base ++ ".f64"))
-          : (base ++ "f", Label ("llvm." ++ base ++ ".f32"))
-          : (base ++ "l", Label ("llvm." ++ base ++ ".f128"))
+          = (base,        Label ("llvm." <> base <> ".f64"))
+          : (base <> "f", Label ("llvm." <> base <> ".f32"))
+          : (base <> "l", Label ("llvm." <> base <> ".f128"))
           : rest
+
+      others
+          = ("isnanf", "isnan")
+          : ("isnand", "isnan")
+          : ("isinff", "isinf")
+          : ("isinfd", "isinf")
+          : []
   in
-  HashMap.fromList $ foldr floating []
+  HashMap.fromList $ foldr floating others
     [ "sqrt"
     , "powi"
     , "sin"
diff --git a/Data/Array/Accelerate/LLVM/CodeGen/Monad.hs b/Data/Array/Accelerate/LLVM/CodeGen/Monad.hs
--- a/Data/Array/Accelerate/LLVM/CodeGen/Monad.hs
+++ b/Data/Array/Accelerate/LLVM/CodeGen/Monad.hs
@@ -40,23 +40,26 @@
 
 -- standard library
 import Control.Applicative
-import Control.Monad.State.Strict
+import Control.Monad.State
+import Data.ByteString.Short                                        ( ShortByteString )
 import Data.Function
-import Data.HashMap.Strict                                              ( HashMap )
-import Data.Map                                                         ( Map )
-import Data.Sequence                                                    ( Seq )
+import Data.HashMap.Strict                                          ( HashMap )
+import Data.Map                                                     ( Map )
+import Data.Sequence                                                ( Seq )
+import Data.String
 import Data.Word
-import Text.Printf
 import Prelude
-import qualified Data.Foldable                                          as F
-import qualified Data.HashMap.Strict                                    as HashMap
-import qualified Data.Map                                               as Map
-import qualified Data.Sequence                                          as Seq
+import Text.Printf
+import qualified Data.Foldable                                      as F
+import qualified Data.HashMap.Strict                                as HashMap
+import qualified Data.Map                                           as Map
+import qualified Data.Sequence                                      as Seq
+import qualified Data.ByteString.Short                              as B
 
 -- accelerate
 import Data.Array.Accelerate.Error
-import Data.Array.Accelerate.Array.Sugar                                ( Elt, eltType )
-import qualified Data.Array.Accelerate.Debug                            as Debug
+import Data.Array.Accelerate.Array.Sugar                            ( Elt, eltType )
+import qualified Data.Array.Accelerate.Debug                        as Debug
 
 -- accelerate-llvm
 import LLVM.AST.Type.Instruction
@@ -73,11 +76,11 @@
 import Data.Array.Accelerate.LLVM.CodeGen.Module
 import Data.Array.Accelerate.LLVM.CodeGen.Type
 
-import Data.Array.Accelerate.LLVM.CodeGen.Sugar                         ( IROpenAcc(..) )
+import Data.Array.Accelerate.LLVM.CodeGen.Sugar                     ( IROpenAcc(..) )
 
 -- llvm-hs
-import qualified LLVM.AST                                               as LLVM
-import qualified LLVM.AST.Global                                        as LLVM
+import qualified LLVM.AST                                           as LLVM
+import qualified LLVM.AST.Global                                    as LLVM
 
 
 -- Code generation
@@ -89,17 +92,17 @@
 -- AST, and one for each of the basic blocks that are generated during the walk.
 --
 data CodeGenState = CodeGenState
-  { blockChain          :: Seq Block                                    -- blocks for this function
-  , symbolTable         :: Map Label LLVM.Global                        -- global (external) function declarations
-  , metadataTable       :: HashMap String (Seq [Maybe Metadata])        -- module metadata to be collected
-  , intrinsicTable      :: HashMap String Label                         -- standard math intrinsic functions
-  , next                :: {-# UNPACK #-} !Word                         -- a name supply
+  { blockChain          :: Seq Block                                      -- blocks for this function
+  , symbolTable         :: Map Label LLVM.Global                          -- global (external) function declarations
+  , metadataTable       :: HashMap ShortByteString (Seq [Maybe Metadata]) -- module metadata to be collected
+  , intrinsicTable      :: HashMap ShortByteString Label                  -- standard math intrinsic functions
+  , next                :: {-# UNPACK #-} !Word                           -- a name supply
   }
 
 data Block = Block
-  { blockLabel          :: Label                                        -- block label
-  , instructions        :: Seq (LLVM.Named LLVM.Instruction)            -- stack of instructions
-  , terminator          :: LLVM.Terminator                              -- block terminator
+  { blockLabel          :: {-# UNPACK #-} !Label                          -- block label
+  , instructions        :: Seq (LLVM.Named LLVM.Instruction)              -- stack of instructions
+  , terminator          :: LLVM.Terminator                                -- block terminator
   }
 
 newtype CodeGen a = CodeGen { runCodeGen :: State CodeGenState a }
@@ -111,7 +114,7 @@
     :: forall arch aenv a. (Target arch, Intrinsic arch)
     => CodeGen (IROpenAcc arch aenv a)
     -> Module arch aenv a
-runLLVM  ll =
+runLLVM ll =
   let
       initialState      = CodeGenState
                             { blockChain        = initBlockChain
@@ -137,7 +140,7 @@
   Module { moduleMetadata = md
          , unModule       = LLVM.Module
                           { LLVM.moduleName           = name
-                          , LLVM.moduleSourceFileName = []
+                          , LLVM.moduleSourceFileName = B.empty
                           , LLVM.moduleDataLayout     = targetDataLayout (undefined::arch)
                           , LLVM.moduleTargetTriple   = targetTriple (undefined::arch)
                           , LLVM.moduleDefinitions    = definitions
@@ -187,7 +190,7 @@
   state $ \s ->
     let idx     = Seq.length (blockChain s)
         label   = let (h,t) = break (== '.') nm in (h ++ shows idx t)
-        next    = Block (Label label) Seq.empty err
+        next    = Block (fromString label) Seq.empty err
         err     = $internalError label "Block has no terminator"
     in
     ( next, s )
@@ -256,10 +259,17 @@
 instr ins = ir (typeOf ins) <$> instr' ins
 
 instr' :: Instruction a -> CodeGen (Operand a)
-instr' ins = do
-  name <- freshName
-  instr_ $ downcast (name := ins)
-  return $ LocalReference (typeOf ins) name
+instr' ins =
+  -- LLVM-5 does not allow instructions of type void to have a name.
+  case typeOf ins of
+    VoidType -> do
+      do_ ins
+      return $ LocalReference VoidType (Name B.empty)
+    --
+    ty -> do
+      name <- freshName
+      instr_ $ downcast (name := ins)
+      return $ LocalReference ty name
 
 -- | Execute an unnamed instruction
 --
@@ -361,7 +371,7 @@
 
       name = case LLVM.name g of
                LLVM.Name n      -> Label n
-               LLVM.UnName n    -> Label (show n)
+               LLVM.UnName n    -> Label (fromString (show n))
   in
   modify (\s -> s { symbolTable = Map.alter unique name (symbolTable s) })
 
@@ -369,7 +379,7 @@
 -- | Get name of the corresponding intrinsic function implementing a given C
 -- function. If there is no mapping, the C function name is used.
 --
-intrinsic :: String -> CodeGen Label
+intrinsic :: ShortByteString -> CodeGen Label
 intrinsic key =
   state $ \s ->
     let name = HashMap.lookupDefault (Label key) key (intrinsicTable s)
@@ -382,7 +392,7 @@
 
 -- | Insert a metadata key/value pair into the current module.
 --
-addMetadata :: String -> [Maybe Metadata] -> CodeGen ()
+addMetadata :: ShortByteString -> [Maybe Metadata] -> CodeGen ()
 addMetadata key val =
   modify $ \s ->
     s { metadataTable = HashMap.insertWith (flip (Seq.><)) key (Seq.singleton val) (metadataTable s) }
@@ -393,10 +403,10 @@
 -- represent the metadata node definitions that will be attached to that
 -- definition.
 --
-createMetadata :: HashMap String (Seq [Maybe Metadata]) -> [LLVM.Definition]
+createMetadata :: HashMap ShortByteString (Seq [Maybe Metadata]) -> [LLVM.Definition]
 createMetadata md = build (HashMap.toList md) (Seq.empty, Seq.empty)
   where
-    build :: [(String, Seq [Maybe Metadata])]
+    build :: [(ShortByteString, Seq [Maybe Metadata])]
           -> (Seq LLVM.Definition, Seq LLVM.Definition) -- accumulator of (names, metadata)
           -> [LLVM.Definition]
     build []     (k,d) = F.toList (k Seq.>< d)
@@ -405,7 +415,7 @@
       in  build xs (k Seq.|> k', d Seq.>< d')
 
     meta :: Int                                         -- number of metadata node definitions so far
-         -> (String, Seq [Maybe Metadata])              -- current assoc of the metadata map
+         -> (ShortByteString, Seq [Maybe Metadata])     -- current assoc of the metadata map
          -> (LLVM.Definition, Seq LLVM.Definition)
     meta n (key, vals)
       = let node i      = LLVM.MetadataNodeID (fromIntegral (i+n))
diff --git a/Data/Array/Accelerate/LLVM/CodeGen/Monad.hs-boot b/Data/Array/Accelerate/LLVM/CodeGen/Monad.hs-boot
--- a/Data/Array/Accelerate/LLVM/CodeGen/Monad.hs-boot
+++ b/Data/Array/Accelerate/LLVM/CodeGen/Monad.hs-boot
@@ -11,7 +11,7 @@
 module Data.Array.Accelerate.LLVM.CodeGen.Monad (CodeGen)
   where
 
-import Control.Monad.State.Strict
+import Control.Monad.State
 
 data CodeGenState
 newtype CodeGen a = CodeGen { runCodeGen :: State CodeGenState a }
diff --git a/Data/Array/Accelerate/LLVM/CodeGen/Permute.hs b/Data/Array/Accelerate/LLVM/CodeGen/Permute.hs
--- a/Data/Array/Accelerate/LLVM/CodeGen/Permute.hs
+++ b/Data/Array/Accelerate/LLVM/CodeGen/Permute.hs
@@ -1,7 +1,6 @@
 {-# LANGUAGE GADTs               #-}
 {-# LANGUAGE RecordWildCards     #-}
 {-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE TemplateHaskell     #-}
 {-# LANGUAGE TupleSections       #-}
 {-# LANGUAGE TypeOperators       #-}
 {-# OPTIONS_HADDOCK hide #-}
diff --git a/Data/Array/Accelerate/LLVM/CodeGen/Skeleton.hs b/Data/Array/Accelerate/LLVM/CodeGen/Skeleton.hs
--- a/Data/Array/Accelerate/LLVM/CodeGen/Skeleton.hs
+++ b/Data/Array/Accelerate/LLVM/CodeGen/Skeleton.hs
@@ -22,17 +22,17 @@
 import Prelude                                                  hiding ( id )
 
 -- accelerate
-import Data.Array.Accelerate.AST                                hiding ( Val(..), prj, stencil, stencilAccess )
+import Data.Array.Accelerate.AST                                hiding ( Val(..), PreBoundary(..), prj, stencil )
 import Data.Array.Accelerate.Array.Sugar
 import Data.Array.Accelerate.Type
 
 import Data.Array.Accelerate.LLVM.CodeGen.Base
 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.Permute
 import Data.Array.Accelerate.LLVM.CodeGen.Stencil
 import Data.Array.Accelerate.LLVM.CodeGen.Sugar
+import Data.Array.Accelerate.LLVM.Compile.Cache
 
 
 -- | A class covering code generation for all of the primitive array operations.
@@ -50,12 +50,14 @@
 
   generate      :: (Shape sh, Elt e)
                 => arch
+                -> UID
                 -> Gamma       aenv
                 -> IRFun1 arch aenv (sh -> e)
                 -> CodeGen (IROpenAcc arch aenv (Array sh e))
 
   transform     :: (Shape sh, Shape sh', Elt a, Elt b)
                 => arch
+                -> UID
                 -> Gamma          aenv
                 -> IRFun1    arch aenv (sh' -> sh)
                 -> IRFun1    arch aenv (a -> b)
@@ -64,6 +66,7 @@
 
   map           :: (Shape sh, Elt a, Elt b)
                 => arch
+                -> UID
                 -> Gamma          aenv
                 -> IRFun1    arch aenv (a -> b)
                 -> IRDelayed arch aenv (Array sh a)
@@ -71,6 +74,7 @@
 
   fold          :: (Shape sh, Elt e)
                 => arch
+                -> UID
                 -> Gamma          aenv
                 -> IRFun2    arch aenv (e -> e -> e)
                 -> IRExp     arch aenv e
@@ -79,6 +83,7 @@
 
   fold1         :: (Shape sh, Elt e)
                 => arch
+                -> UID
                 -> Gamma          aenv
                 -> IRFun2    arch aenv (e -> e -> e)
                 -> IRDelayed arch aenv (Array (sh:.Int) e)
@@ -86,6 +91,7 @@
 
   foldSeg       :: (Shape sh, Elt e, Elt i, IsIntegral i)
                 => arch
+                -> UID
                 -> Gamma          aenv
                 -> IRFun2    arch aenv (e -> e -> e)
                 -> IRExp     arch aenv e
@@ -95,6 +101,7 @@
 
   fold1Seg      :: (Shape sh, Elt e, Elt i, IsIntegral i)
                 => arch
+                -> UID
                 -> Gamma          aenv
                 -> IRFun2    arch aenv (e -> e -> e)
                 -> IRDelayed arch aenv (Array (sh:.Int) e)
@@ -103,6 +110,7 @@
 
   scanl         :: (Shape sh, Elt e)
                 => arch
+                -> UID
                 -> Gamma          aenv
                 -> IRFun2    arch aenv (e -> e -> e)
                 -> IRExp     arch aenv e
@@ -111,6 +119,7 @@
 
   scanl'        :: (Shape sh, Elt e)
                 => arch
+                -> UID
                 -> Gamma          aenv
                 -> IRFun2    arch aenv (e -> e -> e)
                 -> IRExp     arch aenv e
@@ -119,6 +128,7 @@
 
   scanl1        :: (Shape sh, Elt e)
                 => arch
+                -> UID
                 -> Gamma          aenv
                 -> IRFun2    arch aenv (e -> e -> e)
                 -> IRDelayed arch aenv (Array (sh:.Int) e)
@@ -126,6 +136,7 @@
 
   scanr         :: (Shape sh, Elt e)
                 => arch
+                -> UID
                 -> Gamma          aenv
                 -> IRFun2    arch aenv (e -> e -> e)
                 -> IRExp     arch aenv e
@@ -134,6 +145,7 @@
 
   scanr'        :: (Shape sh, Elt e)
                 => arch
+                -> UID
                 -> Gamma          aenv
                 -> IRFun2    arch aenv (e -> e -> e)
                 -> IRExp     arch aenv e
@@ -142,6 +154,7 @@
 
   scanr1        :: (Shape sh, Elt e)
                 => arch
+                -> UID
                 -> Gamma          aenv
                 -> IRFun2    arch aenv (e -> e -> e)
                 -> IRDelayed arch aenv (Array (sh:.Int) e)
@@ -149,6 +162,7 @@
 
   permute       :: (Shape sh, Shape sh', Elt e)
                 => arch
+                -> UID
                 -> Gamma             aenv
                 -> IRPermuteFun arch aenv (e -> e -> e)
                 -> IRFun1       arch aenv (sh -> sh')
@@ -157,6 +171,7 @@
 
   backpermute   :: (Shape sh, Shape sh', Elt e)
                 => arch
+                -> UID
                 -> Gamma          aenv
                 -> IRFun1    arch aenv (sh' -> sh)
                 -> IRDelayed arch aenv (Array sh e)
@@ -164,19 +179,21 @@
 
   stencil       :: (Stencil sh a stencil, Elt b)
                 => arch
+                -> UID
                 -> Gamma aenv
                 -> IRFun1 arch aenv (stencil -> b)
-                -> Boundary (IR a)
+                -> IRBoundary arch aenv (Array sh a)
                 -> IRManifest arch aenv (Array sh a)
                 -> CodeGen (IROpenAcc arch aenv (Array sh b))
 
   stencil2      :: (Stencil sh a stencil1, Stencil sh b stencil2, Elt c)
                 => arch
+                -> UID
                 -> Gamma aenv
                 -> IRFun2 arch aenv (stencil1 -> stencil2 -> c)
-                -> Boundary (IR a)
+                -> IRBoundary arch aenv (Array sh a)
                 -> IRManifest arch aenv (Array sh a)
-                -> Boundary (IR b)
+                -> IRBoundary arch aenv (Array sh b)
                 -> IRManifest arch aenv (Array sh b)
                 -> CodeGen (IROpenAcc arch aenv (Array sh c))
 
@@ -197,35 +214,38 @@
 defaultMap
     :: (Skeleton arch, Shape sh, Elt a, Elt b)
     => arch
+    -> UID
     -> Gamma          aenv
     -> IRFun1    arch aenv (a -> b)
     -> IRDelayed arch aenv (Array sh a)
     -> CodeGen (IROpenAcc arch aenv (Array sh b))
-defaultMap arch aenv f a
-  = transform arch aenv id f a
+defaultMap arch uid aenv f a
+  = transform arch uid aenv id f a
 
 {-# INLINEABLE defaultBackpermute #-}
 defaultBackpermute
     :: (Skeleton arch, Shape sh, Shape sh', Elt e)
     => arch
+    -> UID
     -> Gamma          aenv
     -> IRFun1    arch aenv (sh' -> sh)
     -> IRDelayed arch aenv (Array sh e)
     -> CodeGen (IROpenAcc arch aenv (Array sh' e))
-defaultBackpermute arch aenv p a
-  = transform arch aenv p id a
+defaultBackpermute arch uid aenv p a
+  = transform arch uid aenv p id a
 
 {-# INLINEABLE defaultTransform #-}
 defaultTransform
     :: (Skeleton arch, Shape sh', Elt b)
     => arch
+    -> UID
     -> Gamma          aenv
     -> IRFun1    arch aenv (sh' -> sh)
     -> IRFun1    arch aenv (a -> b)
     -> IRDelayed arch aenv (Array sh a)
     -> CodeGen (IROpenAcc arch aenv (Array sh' b))
-defaultTransform arch aenv p f IRDelayed{..}
-  = generate arch aenv . IRFun1 $ \ix -> do
+defaultTransform arch uid aenv p f IRDelayed{..}
+  = generate arch uid aenv . IRFun1 $ \ix -> do
       ix' <- app1 p ix
       a   <- app1 delayedIndex ix'
       app1 f a
@@ -234,13 +254,14 @@
 defaultStencil1
     :: (Skeleton arch, Stencil sh a stencil, Elt b)
     => arch
+    -> UID
     -> Gamma aenv
     -> IRFun1 arch aenv (stencil -> b)
-    -> Boundary (IR a)
+    -> IRBoundary arch aenv (Array sh a)
     -> IRManifest arch aenv (Array sh a)
     -> CodeGen (IROpenAcc arch aenv (Array sh b))
-defaultStencil1 arch aenv f boundary (IRManifest v)
-  = generate arch aenv . IRFun1 $ \ix -> do
+defaultStencil1 arch uid aenv f boundary (IRManifest v)
+  = generate arch uid aenv . IRFun1 $ \ix -> do
       sten <- stencilAccess boundary (irArray (aprj v aenv)) ix
       app1 f sten
 
@@ -248,15 +269,16 @@
 defaultStencil2
     :: (Skeleton arch, Stencil sh a stencil1, Stencil sh b stencil2, Elt c)
     => arch
+    -> UID
     -> Gamma aenv
     -> IRFun2 arch aenv (stencil1 -> stencil2 -> c)
-    -> Boundary (IR a)
+    -> IRBoundary arch aenv (Array sh a)
     -> IRManifest arch aenv (Array sh a)
-    -> Boundary (IR b)
+    -> IRBoundary arch aenv (Array sh b)
     -> IRManifest arch aenv (Array sh b)
     -> CodeGen (IROpenAcc arch aenv (Array sh c))
-defaultStencil2 arch aenv f boundary1 (IRManifest v1) boundary2 (IRManifest v2)
-  = generate arch aenv . IRFun1 $ \ix -> do
+defaultStencil2 arch uid aenv f boundary1 (IRManifest v1) boundary2 (IRManifest v2)
+  = generate arch uid aenv . IRFun1 $ \ix -> do
       sten1 <- stencilAccess boundary1 (irArray (aprj v1 aenv)) ix
       sten2 <- stencilAccess boundary2 (irArray (aprj v2 aenv)) ix
       app2 f sten1 sten2
diff --git a/Data/Array/Accelerate/LLVM/CodeGen/Stencil.hs b/Data/Array/Accelerate/LLVM/CodeGen/Stencil.hs
--- a/Data/Array/Accelerate/LLVM/CodeGen/Stencil.hs
+++ b/Data/Array/Accelerate/LLVM/CodeGen/Stencil.hs
@@ -19,9 +19,9 @@
   where
 
 -- accelerate
-import Data.Array.Accelerate.AST                                hiding ( Val(..), prj, stencilAccess )
+import Data.Array.Accelerate.AST                                hiding ( Val(..), PreBoundary(..), prj )
 import Data.Array.Accelerate.Analysis.Match
-import Data.Array.Accelerate.Array.Sugar                        hiding ( bound )
+import Data.Array.Accelerate.Array.Sugar
 import Data.Array.Accelerate.Type
 import Data.Array.Accelerate.Error
 
@@ -42,7 +42,7 @@
 --
 stencilAccess
     :: Stencil sh e stencil
-    => Boundary (IR e)
+    => IRBoundary arch aenv (Array sh e)
     -> IRArray (Array sh e)
     -> IR sh
     -> CodeGen (IR stencil)
@@ -103,7 +103,7 @@
     -- Recursive cases. Note that because the stencil pattern is defined with
     -- a cons ordering, whereas shapes (indices) are defined as a snoc list,
     -- when we recurse on the stencil structure we must manipulate the
-    -- _innermost_ index component
+    -- _left-most_ index component
     --
     goR (StencilRtup3 s1 s2 s3) rf ix =
       let (i, ix') = uncons ix
@@ -162,18 +162,21 @@
 --
 bounded
     :: (Shape sh, Elt e)
-    => Boundary (IR e)
+    => IRBoundary arch aenv (Array sh e)
     -> IRArray (Array sh e)
     -> IR sh
     -> CodeGen (IR e)
 bounded bndy arr@IRArray{..} ix =
   case bndy of
-    Constant v ->
+    IRConstant v ->
       if inside irArrayShape ix
-        then do i <- intOfIndex irArrayShape ix
-                readArray arr i
+        then readArray arr =<< intOfIndex irArrayShape ix
         else return v
-    _          -> do
+    IRFunction f ->
+      if inside irArrayShape ix
+        then readArray arr =<< intOfIndex irArrayShape ix
+        else app1 f ix
+    _            -> do
       ix' <- bound irArrayShape ix
       i   <- intOfIndex irArrayShape ix'
       readArray arr i
@@ -199,22 +202,22 @@
                IR i' <- if A.lt t (IR iz) (int 0)
                           then
                             case bndy of
-                              Clamp      -> return (int 0)
-                              Mirror     -> A.negate numType (IR iz)
-                              Wrap       -> A.add    numType (IR sz) (IR iz)
-                              Constant _ -> $internalError "bound" "unexpected boundary condition"
+                              IRClamp  -> return (int 0)
+                              IRMirror -> A.negate numType (IR iz)
+                              IRWrap   -> A.add    numType (IR sz) (IR iz)
+                              _        -> $internalError "bound" "unexpected boundary condition"
                           else
                             if A.gte t (IR iz) (IR sz)
                               then
                                 case bndy of
-                                  Clamp      -> A.sub numType (IR sz) (int 1)
-                                  Mirror     -> do
+                                  IRClamp  -> A.sub numType (IR sz) (int 1)
+                                  IRWrap   -> A.sub numType (IR iz) (IR sz)
+                                  IRMirror -> do
                                     a <- A.add numType (IR sz) (int 2)
                                     b <- A.sub numType (IR iz) a
                                     c <- A.sub numType (IR sz) b
                                     return c
-                                  Wrap       -> A.sub numType (IR iz) (IR sz)
-                                  Constant _ -> $internalError "bound" "unexpected boundary condition"
+                                  _        -> $internalError "bound" "unexpected boundary condition"
                               else
                                 return (IR iz)
                return i'
@@ -235,12 +238,9 @@
               else return (bool False)
         go (SingleTuple t) sz iz
           | Just Refl <- matchScalarType t (scalarType :: ScalarType Int)
-          = if A.lt t (IR iz) (int 0)
+          = if A.lt t (IR iz) (int 0) `A.lor` A.gte t (IR iz) (IR sz)
               then return (bool False)
-              else
-                if A.gte t (IR iz) (IR sz)
-                  then return (bool False)
-                  else return (bool True)
+              else return (bool True)
           --
           | otherwise
           = $internalError "bound" "expected shape with Int components"
@@ -277,7 +277,7 @@
   IR $ OP_Pair (OP_Pair (OP_Pair (OP_Pair (OP_Pair (OP_Pair (OP_Pair (OP_Pair (OP_Pair OP_Unit a) b) c) d) e) f) g) h) i
 
 
--- Add an _innermost_ dimension to a shape
+-- Add a _left-most_ dimension to a shape
 --
 cons :: forall sh. Shape sh => IR Int -> IR sh -> IR (sh :. Int)
 cons (IR ix) (IR extent) = IR $ go (eltType (undefined::sh)) extent
@@ -292,7 +292,7 @@
       = $internalError "cons" "expected shape with Int components"
 
 
--- Remove the _innermost_ dimension to a shape, and return the remainder
+-- Remove the _left-most_ index to a shape, and return the remainder
 --
 uncons :: forall sh. Shape sh => IR (sh :. Int) -> (IR Int, IR sh)
 uncons (IR extent) = let (ix, extent') = go (eltType (undefined::(sh :. Int))) extent
diff --git a/Data/Array/Accelerate/LLVM/CodeGen/Sugar.hs b/Data/Array/Accelerate/LLVM/CodeGen/Sugar.hs
--- a/Data/Array/Accelerate/LLVM/CodeGen/Sugar.hs
+++ b/Data/Array/Accelerate/LLVM/CodeGen/Sugar.hs
@@ -18,6 +18,8 @@
   IROpenExp, IROpenFun1(..), IROpenFun2(..),
   IROpenAcc(..), IRDelayed(..), IRManifest(..),
 
+  IRBoundary(..),
+
   IRArray(..),
 
 ) where
@@ -52,6 +54,17 @@
 data IROpenFun2 arch env aenv t where
   IRFun2 :: { app2 :: IR a -> IR b -> IROpenExp arch ((env,a),b) aenv c }
          -> IROpenFun2 arch env aenv (a -> b -> c)
+
+
+-- Stencil
+-- -------
+
+data IRBoundary arch aenv t where
+  IRClamp     :: IRBoundary arch aenv t
+  IRMirror    :: IRBoundary arch aenv t
+  IRWrap      :: IRBoundary arch aenv t
+  IRConstant  :: Elt e => IR e -> IRBoundary arch aenv (Array sh e)
+  IRFunction  :: (Shape sh, Elt e) => IRFun1 arch aenv (sh -> e) -> IRBoundary arch aenv (Array sh e)
 
 
 -- Arrays
diff --git a/Data/Array/Accelerate/LLVM/CodeGen/Type.hs b/Data/Array/Accelerate/LLVM/CodeGen/Type.hs
--- a/Data/Array/Accelerate/LLVM/CodeGen/Type.hs
+++ b/Data/Array/Accelerate/LLVM/CodeGen/Type.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE GADTs               #-}
 {-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE TemplateHaskell     #-}
 {-# OPTIONS_HADDOCK hide #-}
 -- |
 -- Module      : Data.Array.Accelerate.LLVM.CodeGen.Type
diff --git a/Data/Array/Accelerate/LLVM/Compile.hs b/Data/Array/Accelerate/LLVM/Compile.hs
--- a/Data/Array/Accelerate/LLVM/Compile.hs
+++ b/Data/Array/Accelerate/LLVM/Compile.hs
@@ -22,113 +22,104 @@
   Compile(..),
   compileAcc, compileAfun,
 
-  ExecOpenAcc(..), ExecOpenAfun,
-  ExecAcc, ExecAfun,
-  ExecExp, ExecOpenExp,
-  ExecFun, ExecOpenFun
+  CompiledOpenAcc(..), CompiledOpenAfun,
+  CompiledAcc, CompiledAfun,
+  CompiledExp, CompiledOpenExp,
+  CompiledFun, CompiledOpenFun
 
 ) where
 
 -- accelerate
 import Data.Array.Accelerate.AST
-import Data.Array.Accelerate.Array.Sugar                        hiding ( Foreign )
+import Data.Array.Accelerate.Array.Sugar                            hiding ( Foreign )
 import Data.Array.Accelerate.Error
 import Data.Array.Accelerate.Product
 import Data.Array.Accelerate.Trafo
-import qualified Data.Array.Accelerate.Array.Sugar              as A
+import qualified Data.Array.Accelerate.Array.Sugar                  as A
 
-import Data.Array.Accelerate.LLVM.Array.Data
 import Data.Array.Accelerate.LLVM.CodeGen.Environment
 import Data.Array.Accelerate.LLVM.Foreign
 import Data.Array.Accelerate.LLVM.State
+import qualified Data.Array.Accelerate.LLVM.AST                     as AST
 
 -- standard library
-import Data.IntMap                                              ( IntMap )
-import Data.Monoid
-import Control.Applicative                                      hiding ( Const )
-import Prelude                                                  hiding ( exp, unzip )
+import Data.IntMap                                                  ( IntMap )
+import Control.Applicative                                          hiding ( Const )
+import Prelude                                                      hiding ( map, unzip, zipWith, scanl, scanl1, scanr, scanr1, exp )
 
 
 class Foreign arch => Compile arch where
-  data ExecutableR arch
+  data ObjectR arch
+  -- TODO: Provide serialisation facilities, for on-disk caching etc.
 
-  -- | Compile an accelerate computation into some backend-specific executable format
+  -- | Compile an accelerate computation into some backend-specific code that
+  -- will be used to execute the given array expression. The code is not yet
+  -- linked into the running executable.
   --
   compileForTarget
       :: DelayedOpenAcc aenv a
       -> Gamma aenv
-      -> LLVM arch (ExecutableR arch)
+      -> LLVM arch (ObjectR arch)
 
 
--- | Annotate an open array expression with the information necessary to execute
--- each node directly.
---
-data ExecOpenAcc arch aenv a where
-  ExecAcc  :: ExecutableR arch
-           -> Gamma aenv
-           -> PreOpenAcc (ExecOpenAcc arch) aenv a
-           -> ExecOpenAcc arch aenv a
-
-  EmbedAcc :: (Shape sh, Elt e)
-           => PreExp (ExecOpenAcc arch) aenv sh
-           -> ExecOpenAcc arch aenv (Array sh e)
+data CompiledOpenAcc arch aenv a where
+  BuildAcc  :: Gamma aenv
+            -> ObjectR arch
+            -> AST.PreOpenAccSkeleton (CompiledOpenAcc arch) aenv a
+            -> CompiledOpenAcc arch aenv a
 
-  UnzipAcc :: (Elt t, Elt e)
-           => TupleIdx (TupleRepr t) e
-           -> Idx aenv (Array sh t)
-           -> ExecOpenAcc arch aenv (Array sh e)
+  PlainAcc  :: Arrays a
+            => AST.PreOpenAccCommand  (CompiledOpenAcc arch) aenv a
+            -> CompiledOpenAcc arch aenv a
 
 
--- An annotated AST suitable for execution
+-- An annotated AST with embedded build products
 --
-type ExecAcc arch a     = ExecOpenAcc arch () a
-type ExecAfun arch a    = PreAfun (ExecOpenAcc arch) a
+type CompiledOpenAfun arch  = PreOpenAfun (CompiledOpenAcc arch)
+type CompiledOpenExp arch   = PreOpenExp (CompiledOpenAcc arch)
+type CompiledOpenFun arch   = PreOpenFun (CompiledOpenAcc arch)
 
-type ExecOpenAfun arch  = PreOpenAfun (ExecOpenAcc arch)
-type ExecOpenExp arch   = PreOpenExp (ExecOpenAcc arch)
-type ExecOpenFun arch   = PreOpenFun (ExecOpenAcc arch)
+type CompiledAcc arch a     = CompiledOpenAcc arch () a
+type CompiledAfun arch a    = CompiledOpenAfun arch () a
 
-type ExecExp arch       = ExecOpenExp arch ()
-type ExecFun arch       = ExecOpenFun arch ()
+type CompiledExp arch       = CompiledOpenExp arch ()
+type CompiledFun arch       = CompiledOpenFun arch ()
 
 
--- | Initialise code generation, compilation, and data transfer (if required)
--- for an array expression. The returned array computation is annotated to be
--- suitable for execution on the target:
---
---   * A list of the array variables embedded within scalar expressions
---
---   * The compiled LLVM code required to execute the kernel
+-- | Generate and compile code for an array expression. The returned expression
+-- is annotated with the compilation products required to executed each
+-- operation on the given target, together with the list of array variables
+-- referenced from the embedded scalar expressions.
 --
 {-# INLINEABLE compileAcc #-}
 compileAcc
-    :: (Compile arch, Remote arch)
+    :: Compile arch
     => DelayedAcc a
-    -> LLVM arch (ExecAcc arch a)
+    -> LLVM arch (CompiledAcc arch a)
 compileAcc = compileOpenAcc
 
 {-# INLINEABLE compileAfun #-}
 compileAfun
-    :: (Compile arch, Remote arch)
+    :: Compile arch
     => DelayedAfun f
-    -> LLVM arch (ExecAfun arch f)
+    -> LLVM arch (CompiledAfun arch f)
 compileAfun = compileOpenAfun
 
 
 {-# INLINEABLE compileOpenAfun #-}
 compileOpenAfun
-    :: (Compile arch, Remote arch)
+    :: Compile arch
     => DelayedOpenAfun aenv f
-    -> LLVM arch (PreOpenAfun (ExecOpenAcc arch) aenv f)
+    -> LLVM arch (CompiledOpenAfun arch aenv f)
 compileOpenAfun (Alam l)  = Alam  <$> compileOpenAfun l
 compileOpenAfun (Abody b) = Abody <$> compileOpenAcc b
 
 
 {-# INLINEABLE compileOpenAcc #-}
 compileOpenAcc
-    :: forall arch _aenv _a. (Compile arch, Remote arch)
+    :: forall arch _aenv _a. Compile arch
     => DelayedOpenAcc _aenv _a
-    -> LLVM arch (ExecOpenAcc arch _aenv _a)
+    -> LLVM arch (CompiledOpenAcc arch _aenv _a)
 compileOpenAcc = traverseAcc
   where
     -- Traverse an open array expression in depth-first order. The top-level
@@ -140,104 +131,135 @@
     -- array variables that were referred to within scalar sub-expressions.
     -- These will be required during code generation and execution.
     --
-    traverseAcc :: forall aenv arrs. DelayedOpenAcc aenv arrs -> LLVM arch (ExecOpenAcc arch aenv arrs)
+    traverseAcc :: forall aenv arrs. DelayedOpenAcc aenv arrs -> LLVM arch (CompiledOpenAcc arch aenv arrs)
     traverseAcc Delayed{}              = $internalError "compileOpenAcc" "unexpected delayed array"
     traverseAcc topAcc@(Manifest pacc) =
       case pacc of
         -- Environment and control flow
-        Avar ix                 -> node $ pure (Avar ix)
-        Alet a b                -> node . pure =<< Alet         <$> traverseAcc a <*> traverseAcc b
-        Apply f a               -> node =<< liftA2 Apply        <$> travAF f <*> travA a
-        Awhile p f a            -> node =<< liftA3 Awhile       <$> travAF p <*> travAF f <*> travA a
-        Acond p t e             -> node =<< liftA3 Acond        <$> travE  p <*> travA  t <*> travA e
-        Atuple tup              -> node =<< liftA  Atuple       <$> travAtup tup
-        Aprj ix tup             -> node =<< liftA (Aprj ix)     <$> travA    tup
-
-        -- Foreign
-        Aforeign ff afun a      -> foreignA ff afun a
+        Avar ix                     -> plain $ pure (AST.Avar ix)
+        Alet a b                    -> plain . pure =<< AST.Alet      <$> traverseAcc a <*> traverseAcc b
+        Apply f a                   -> plain =<< liftA2 AST.Apply     <$> travAF f <*> travA a
+        Awhile p f a                -> plain =<< liftA3 AST.Awhile    <$> travAF p <*> travAF f <*> travA a
+        Acond p t e                 -> plain =<< liftA3 AST.Acond     <$> travE  p <*> travA  t <*> travA e
+        Atuple tup                  -> plain =<< liftA  AST.Atuple    <$> travAtup tup
+        Aprj ix tup                 -> plain =<< liftA (AST.Aprj ix)  <$> travA    tup
 
-        -- Array injection
-        Unit e                  -> node =<< liftA  Unit         <$> travE e
-        Use arrs                -> useRemote (toArr arrs::arrs) >> node (pure (Use arrs))
+        -- Foreign arrays operations
+        Aforeign ff afun a          -> foreignA ff afun a
 
-        -- Index space transforms
-        Reshape s a             -> node =<< liftA2 Reshape              <$> travE s <*> travA a
-        Replicate slix e a      -> exec =<< liftA2 (Replicate slix)     <$> travE e <*> travA a
-        Slice slix a e          -> exec =<< liftA2 (Slice slix)         <$> travA a <*> travE e
-        Backpermute e f a       -> exec =<< liftA3 Backpermute          <$> travE e <*> travF f <*> travA a
+        -- Array injection & manipulation
+        Reshape sh a                -> plain =<< liftA2 AST.Reshape   <$> travE sh <*> travM a
+        Unit e                      -> plain =<< liftA  AST.Unit      <$> travE e
+        Use arrs                    -> plain $ pure (AST.Use arrs)
+        Map f a
+          | Just (t,x) <- unzip f a -> plain $ pure (AST.Unzip t x)
 
+        -- Skeleton operations resulting in compiled code
         -- Producers
-        Generate e f            -> exec =<< liftA2 Generate             <$> travE e <*> travF f
-        Map f a
-          | Just b <- unzip f a -> return b
-          | otherwise           -> exec =<< liftA2 Map                  <$> travF f <*> travA a
-        ZipWith f a b           -> exec =<< liftA3 ZipWith              <$> travF f <*> travA a <*> travA b
-        Transform e p f a       -> exec =<< liftA4 Transform            <$> travE e <*> travF p <*> travF f <*> travA a
+        Map f a                     -> build =<< liftA2 map           <$> travF f  <*> travD a
+        Generate sh f               -> build =<< liftA2 generate      <$> travE sh <*> travF f
+        Transform sh p f a          -> build =<< liftA4 transform     <$> travE sh <*> travF p <*> travF f <*> travD a
+        Backpermute sh f a          -> build =<< liftA3 backpermute   <$> travE sh <*> travF f <*> travD a
 
         -- Consumers
-        Fold f z a              -> exec =<< liftA3 Fold                 <$> travF f <*> travE z <*> travA a
-        Fold1 f a               -> exec =<< liftA2 Fold1                <$> travF f <*> travA a
-        FoldSeg f e a s         -> exec =<< liftA4 FoldSeg              <$> travF f <*> travE e <*> travA a <*> travA s
-        Fold1Seg f a s          -> exec =<< liftA3 Fold1Seg             <$> travF f <*> travA a <*> travA s
-        Scanl f e a             -> exec =<< liftA3 Scanl                <$> travF f <*> travE e <*> travA a
-        Scanl' f e a            -> exec =<< liftA3 Scanl'               <$> travF f <*> travE e <*> travA a
-        Scanl1 f a              -> exec =<< liftA2 Scanl1               <$> travF f <*> travA a
-        Scanr f e a             -> exec =<< liftA3 Scanr                <$> travF f <*> travE e <*> travA a
-        Scanr' f e a            -> exec =<< liftA3 Scanr'               <$> travF f <*> travE e <*> travA a
-        Scanr1 f a              -> exec =<< liftA2 Scanr1               <$> travF f <*> travA a
-        Permute f d g a         -> exec =<< liftA4 Permute              <$> travF f <*> travA d <*> travF g <*> travA a
-        Stencil f b a           -> exec =<< liftA2 (flip Stencil b)     <$> travF f <*> travM a
-        Stencil2 f b1 a1 b2 a2  -> exec =<< liftA3 stencil2             <$> travF f <*> travM a1 <*> travM a2
-          where stencil2 f' a1' a2' = Stencil2 f' b1 a1' b2 a2'
+        Fold f z a                  -> build =<< liftA3 fold          <$> travF f <*> travE z <*> travD a
+        Fold1 f a                   -> build =<< liftA2 fold1         <$> travF f <*> travD a
+        FoldSeg f z a s             -> build =<< liftA4 foldSeg       <$> travF f <*> travE z <*> travD a <*> travD s
+        Fold1Seg f a s              -> build =<< liftA3 fold1Seg      <$> travF f <*> travD a <*> travD s
+        Scanl f z a                 -> build =<< liftA3 scanl         <$> travF f <*> travE z <*> travD a
+        Scanl' f z a                -> build =<< liftA3 scanl'        <$> travF f <*> travE z <*> travD a
+        Scanl1 f a                  -> build =<< liftA2 scanl1        <$> travF f <*> travD a
+        Scanr f z a                 -> build =<< liftA3 scanr         <$> travF f <*> travE z <*> travD a
+        Scanr' f z a                -> build =<< liftA3 scanr'        <$> travF f <*> travE z <*> travD a
+        Scanr1 f a                  -> build =<< liftA2 scanr1        <$> travF f <*> travD a
+        Permute f d g a             -> build =<< liftA4 permute       <$> travF f <*> travA d <*> travF g <*> travD a
+        Stencil f x a               -> build =<< liftA3 stencil1      <$> travF f <*> travB x <*> travM a
+        Stencil2 f x a y b          -> build =<< liftA5 stencil2      <$> travF f <*> travB x <*> travM a <*> travB y <*> travM b
 
+        -- Removed by fusion
+        Replicate{}                 -> fusionError
+        Slice{}                     -> fusionError
+        ZipWith{}                   -> fusionError
+
       where
-        travA :: DelayedOpenAcc aenv a -> LLVM arch (IntMap (Idx' aenv), ExecOpenAcc arch aenv a)
-        travA acc = case acc of
-          Manifest{}    -> pure                    <$> traverseAcc acc
-          Delayed{..}   -> liftA2 (const EmbedAcc) <$> travF indexD <*> travE extentD
+        map _ a             = AST.Map a
+        generate sh _       = AST.Generate sh
+        transform sh _ _ _  = AST.Transform sh
+        backpermute sh _ _  = AST.Backpermute sh
+        fold _ _ a          = AST.Fold a
+        fold1 _ a           = AST.Fold1 a
+        foldSeg _ _ a s     = AST.FoldSeg a s
+        fold1Seg _ a s      = AST.Fold1Seg a s
+        scanl _ _ a         = AST.Scanl a
+        scanl1 _ a          = AST.Scanl1 a
+        scanl' _ _ a        = AST.Scanl' a
+        scanr _ _ a         = AST.Scanr a
+        scanr1 _ a          = AST.Scanr1 a
+        scanr' _ _ a        = AST.Scanr' a
+        permute _ d _ a     = AST.Permute a d
+        stencil1 _ _ a      = AST.Stencil a
+        stencil2 _ _ a _ b  = AST.Stencil2 a b
 
+        fusionError :: error
+        fusionError = $internalError "execute" $ "unexpected fusible material: " ++ showPreAccOp pacc
+
+        travA :: DelayedOpenAcc aenv a -> LLVM arch (IntMap (Idx' aenv), CompiledOpenAcc arch aenv a)
+        travA acc = pure <$> traverseAcc acc
+
+        travD :: (Shape sh, Elt e)
+              => DelayedOpenAcc aenv (Array sh e)
+              -> LLVM arch (IntMap (Idx' aenv), PreExp (CompiledOpenAcc arch) aenv sh)
+        travD Manifest{}  = $internalError "compileOpenAcc" "expected delayed array"
+        travD Delayed{..} = liftA2 (flip const) <$> travF indexD <*> travE extentD
+
         travM :: (Shape sh, Elt e)
-              => DelayedOpenAcc aenv (Array sh e) -> LLVM arch (IntMap (Idx' aenv), ExecOpenAcc arch aenv (Array sh e))
-        travM acc = case acc of
-          Manifest (Avar ix) -> (freevar ix,) <$> traverseAcc acc
-          _                  -> $internalError "compileOpenAcc" "expected array variable"
+              => DelayedOpenAcc aenv (Array sh e)
+              -> LLVM arch (IntMap (Idx' aenv), Idx aenv (Array sh e))
+        travM (Manifest (Avar ix)) = return (freevar ix, ix)
+        travM _                    = $internalError "compileOpenAcc" "expected array variable"
 
         travAF :: DelayedOpenAfun aenv f
-               -> LLVM arch (IntMap (Idx' aenv), PreOpenAfun (ExecOpenAcc arch) aenv f)
+               -> LLVM arch (IntMap (Idx' aenv), CompiledOpenAfun arch aenv f)
         travAF afun = pure <$> compileOpenAfun afun
 
         travAtup :: Atuple (DelayedOpenAcc aenv) a
-                 -> LLVM arch (IntMap (Idx' aenv), Atuple (ExecOpenAcc arch aenv) a)
+                 -> LLVM arch (IntMap (Idx' aenv), Atuple (CompiledOpenAcc arch aenv) a)
         travAtup NilAtup        = return (pure NilAtup)
         travAtup (SnocAtup t a) = liftA2 SnocAtup <$> travAtup t <*> travA a
 
         travF :: DelayedOpenFun env aenv t
-              -> LLVM arch (IntMap (Idx' aenv), PreOpenFun (ExecOpenAcc arch) env aenv t)
+              -> LLVM arch (IntMap (Idx' aenv), CompiledOpenFun arch env aenv t)
         travF (Body b)  = liftA Body <$> travE b
         travF (Lam  f)  = liftA Lam  <$> travF f
 
-        exec :: (IntMap (Idx' aenv), PreOpenAcc (ExecOpenAcc arch) aenv arrs)
-             -> LLVM arch (ExecOpenAcc arch aenv arrs)
-        exec (aenv, eacc) = do
-          let aval = makeGamma aenv
-          kernel <- build topAcc aval
-          return $! ExecAcc kernel aval eacc
+        travB :: PreBoundary DelayedOpenAcc aenv t
+              -> LLVM arch (IntMap (Idx' aenv), PreBoundary (CompiledOpenAcc arch) aenv t)
+        travB Clamp        = return $ pure Clamp
+        travB Mirror       = return $ pure Mirror
+        travB Wrap         = return $ pure Wrap
+        travB (Constant c) = return $ pure (Constant c)
+        travB (Function f) = liftA Function <$> travF f
 
-        node :: (IntMap (Idx' aenv'), PreOpenAcc (ExecOpenAcc arch) aenv' arrs')
-             -> LLVM arch (ExecOpenAcc arch aenv' arrs')
-        node = fmap snd . wrap
+        build :: (IntMap (Idx' aenv), AST.PreOpenAccSkeleton (CompiledOpenAcc arch) aenv arrs)
+              -> LLVM arch (CompiledOpenAcc arch aenv arrs)
+        build (aenv, eacc) = do
+          let aval = makeGamma aenv
+          kernel <- compileForTarget topAcc aval
+          return $! BuildAcc aval kernel eacc
 
-        wrap :: (IntMap (Idx' aenv'), PreOpenAcc (ExecOpenAcc arch) aenv' arrs')
-             -> LLVM arch (IntMap (Idx' aenv'), ExecOpenAcc arch aenv' arrs')
-        wrap = return . liftA (ExecAcc noKernel mempty)
+        plain :: Arrays arrs'
+              => (IntMap (Idx' aenv'), AST.PreOpenAccCommand (CompiledOpenAcc arch) aenv' arrs')
+              -> LLVM arch (CompiledOpenAcc arch aenv' arrs')
+        plain (_, eacc) = return (PlainAcc eacc)
 
         -- Unzips of manifest array data can be done in constant time without
         -- executing any array programs. We split them out here into a separate
-        -- case of 'ExecAcc' so that the execution phase does not have to
-        -- continually perform the below check (in particular; 'run1').
+        -- case so that the execution phase does not have to continually perform
+        -- the below check.
+        --
         unzip :: PreFun DelayedOpenAcc aenv (a -> b)
               -> DelayedOpenAcc aenv (Array sh a)
-              -> Maybe (ExecOpenAcc arch aenv (Array sh b))
+              -> Maybe (TupleIdx (TupleRepr a) b, Idx aenv (Array sh a))
         unzip f a
           | Lam (Body (Prj tix (Var ZeroIdx)))  <- f
           , Delayed sh index _                  <- a
@@ -245,7 +267,7 @@
           , Manifest (Avar ix)                  <- u
           , Lam (Body (Index v (Var ZeroIdx)))  <- index
           , Just Refl                           <- match u v
-          = Just (UnzipAcc tix ix)
+          = Just (tix, ix)
         unzip _ _
           = Nothing
 
@@ -254,28 +276,24 @@
         -- Subsequent phases, if they encounter a foreign node, can assume that
         -- it is for them. Otherwise, drop this term and continue walking down
         -- the list of alternate implementations.
+        --
         foreignA :: (Arrays a, Arrays b, A.Foreign asm)
                  => asm         (a -> b)
                  -> DelayedAfun (a -> b)
                  -> DelayedOpenAcc aenv a
-                 -> LLVM arch (ExecOpenAcc arch aenv b)
+                 -> LLVM arch (CompiledOpenAcc arch aenv b)
         foreignA asm f a =
           case foreignAcc (undefined :: arch) asm of
-            Just{}  -> node =<< liftA (Aforeign asm err) <$> travA a
+            Just{}  -> plain =<< liftA (AST.Aforeign asm) <$> travA a
             Nothing -> traverseAcc $ Manifest (Apply (weaken absurd f) a)
             where
               absurd :: Idx () t -> Idx aenv t
-              absurd = absurd
-              err    = $internalError "compile" "attempt to use fallback in foreign function"
-
-        -- sadness
-        noKernel  = $internalError "compile" "no kernel module for this node"
-
+              absurd = error "complicated stuff in simple words"
 
     -- Traverse a scalar expression
     --
     travE :: DelayedOpenExp env aenv e
-          -> LLVM arch (IntMap (Idx' aenv), PreOpenExp (ExecOpenAcc arch) env aenv e)
+          -> LLVM arch (IntMap (Idx' aenv), PreOpenExp (CompiledOpenAcc arch) env aenv e)
     travE exp =
       case exp of
         Var ix                  -> return $ pure (Var ix)
@@ -285,53 +303,53 @@
         IndexNil                -> return $ pure IndexNil
         Foreign ff f x          -> foreignE ff f x
         --
-        Let a b                 -> liftA2 Let                   <$> travE a <*> travE b
-        IndexCons t h           -> liftA2 IndexCons             <$> travE t <*> travE h
-        IndexHead h             -> liftA  IndexHead             <$> travE h
-        IndexTail t             -> liftA  IndexTail             <$> travE t
-        IndexSlice slix x s     -> liftA2 (IndexSlice slix)     <$> travE x <*> travE s
-        IndexFull slix x s      -> liftA2 (IndexFull slix)      <$> travE x <*> travE s
-        ToIndex s i             -> liftA2 ToIndex               <$> travE s <*> travE i
-        FromIndex s i           -> liftA2 FromIndex             <$> travE s <*> travE i
-        Tuple t                 -> liftA  Tuple                 <$> travT t
-        Prj ix e                -> liftA  (Prj ix)              <$> travE e
-        Cond p t e              -> liftA3 Cond                  <$> travE p <*> travE t <*> travE e
-        While p f x             -> liftA3 While                 <$> travF p <*> travF f <*> travE x
-        PrimApp f e             -> liftA  (PrimApp f)           <$> travE e
-        Index a e               -> liftA2 Index                 <$> travA a <*> travE e
-        LinearIndex a e         -> liftA2 LinearIndex           <$> travA a <*> travE e
-        Shape a                 -> liftA  Shape                 <$> travA a
-        ShapeSize e             -> liftA  ShapeSize             <$> travE e
-        Intersect x y           -> liftA2 Intersect             <$> travE x <*> travE y
-        Union x y               -> liftA2 Union                 <$> travE x <*> travE y
+        Let a b                 -> liftA2 Let               <$> travE a <*> travE b
+        IndexCons t h           -> liftA2 IndexCons         <$> travE t <*> travE h
+        IndexHead h             -> liftA  IndexHead         <$> travE h
+        IndexTail t             -> liftA  IndexTail         <$> travE t
+        IndexSlice slix x s     -> liftA2 (IndexSlice slix) <$> travE x <*> travE s
+        IndexFull slix x s      -> liftA2 (IndexFull slix)  <$> travE x <*> travE s
+        ToIndex s i             -> liftA2 ToIndex           <$> travE s <*> travE i
+        FromIndex s i           -> liftA2 FromIndex         <$> travE s <*> travE i
+        Tuple t                 -> liftA  Tuple             <$> travT t
+        Prj ix e                -> liftA  (Prj ix)          <$> travE e
+        Cond p t e              -> liftA3 Cond              <$> travE p <*> travE t <*> travE e
+        While p f x             -> liftA3 While             <$> travF p <*> travF f <*> travE x
+        PrimApp f e             -> liftA  (PrimApp f)       <$> travE e
+        Index a e               -> liftA2 Index             <$> travA a <*> travE e
+        LinearIndex a e         -> liftA2 LinearIndex       <$> travA a <*> travE e
+        Shape a                 -> liftA  Shape             <$> travA a
+        ShapeSize e             -> liftA  ShapeSize         <$> travE e
+        Intersect x y           -> liftA2 Intersect         <$> travE x <*> travE y
+        Union x y               -> liftA2 Union             <$> travE x <*> travE y
 
       where
         travA :: (Shape sh, Elt e)
               => DelayedOpenAcc aenv (Array sh e)
-              -> LLVM arch (IntMap (Idx' aenv), ExecOpenAcc arch aenv (Array sh e))
+              -> LLVM arch (IntMap (Idx' aenv), CompiledOpenAcc arch aenv (Array sh e))
         travA a = do
           a'    <- traverseAcc a
           return $ (bind a', a')
 
         travT :: Tuple (DelayedOpenExp env aenv) t
-              -> LLVM arch (IntMap (Idx' aenv), Tuple (PreOpenExp (ExecOpenAcc arch) env aenv) t)
+              -> LLVM arch (IntMap (Idx' aenv), Tuple (PreOpenExp (CompiledOpenAcc arch) env aenv) t)
         travT NilTup        = return (pure NilTup)
         travT (SnocTup t e) = liftA2 SnocTup <$> travT t <*> travE e
 
         travF :: DelayedOpenFun env aenv t
-              -> LLVM arch (IntMap (Idx' aenv), PreOpenFun (ExecOpenAcc arch) env aenv t)
+              -> LLVM arch (IntMap (Idx' aenv), PreOpenFun (CompiledOpenAcc arch) env aenv t)
         travF (Body b)  = liftA Body <$> travE b
         travF (Lam  f)  = liftA Lam  <$> travF f
 
-        bind :: (Shape sh, Elt e) => ExecOpenAcc arch aenv (Array sh e) -> IntMap (Idx' aenv)
-        bind (ExecAcc _ _ (Avar ix)) = freevar ix
-        bind _                       = $internalError "bind" "expected array variable"
+        bind :: (Shape sh, Elt e) => CompiledOpenAcc arch aenv (Array sh e) -> IntMap (Idx' aenv)
+        bind (PlainAcc (AST.Avar ix)) = freevar ix
+        bind _                        = $internalError "bind" "expected array variable"
 
         foreignE :: (Elt a, Elt b, A.Foreign asm)
                  => asm           (a -> b)
                  -> DelayedFun () (a -> b)
                  -> DelayedOpenExp env aenv a
-                 -> LLVM arch (IntMap (Idx' aenv), PreOpenExp (ExecOpenAcc arch) env aenv b)
+                 -> LLVM arch (IntMap (Idx' aenv), PreOpenExp (CompiledOpenAcc arch) env aenv b)
         foreignE asm f x =
           case foreignExp (undefined :: arch) asm of
             Just{}                      -> liftA (Foreign asm err) <$> travE x
@@ -339,32 +357,14 @@
             _                           -> error "the slow regard of silent things"
           where
             absurd :: Idx () t -> Idx aenv t
-            absurd = absurd
-            err    = $internalError "foreignE" "attempt to use fallback in foreign expression"
+            absurd = error "Look to my coming, at first light, on the fifth day. At dawn, look to the East."
 
             zero :: Idx ((), a) t -> Idx (env,a) t
             zero ZeroIdx = ZeroIdx
-            zero notzero = zero notzero
-
-
--- Compilation
--- -----------
+            zero _       = error "There are three things all wise men fear: the sea in storm, a night with no moon, and the anger of a gentle man."
 
--- | Generate code that will be used to evaluate an array computation. Pass the
--- generated code to the appropriate backend handler, which may then, for
--- example, compile and link the code into the running executable.
---
--- TODO:
---  * asynchronous compilation
---  * kernel caching
---
-{-# INLINEABLE build #-}
-build :: forall arch aenv a. Compile arch
-      => DelayedOpenAcc aenv a
-      -> Gamma aenv
-      -> LLVM arch (ExecutableR arch)
-build acc aenv =
-  compileForTarget acc aenv
+            err :: CompiledFun arch () (a -> b)
+            err = $internalError "foreignE" "attempt to use fallback in foreign expression"
 
 
 -- Applicative
@@ -372,4 +372,7 @@
 --
 liftA4 :: Applicative f => (a -> b -> c -> d -> e) -> f a -> f b -> f c -> f d -> f e
 liftA4 f a b c d = f <$> a <*> b <*> c <*> d
+
+liftA5 :: Applicative f => (a -> b -> c -> d -> e -> g) -> f a -> f b -> f c -> f d -> f e -> f g
+liftA5 f a b c d g = f <$> a <*> b <*> c <*> d <*> g
 
diff --git a/Data/Array/Accelerate/LLVM/Compile/Cache.hs b/Data/Array/Accelerate/LLVM/Compile/Cache.hs
new file mode 100644
--- /dev/null
+++ b/Data/Array/Accelerate/LLVM/Compile/Cache.hs
@@ -0,0 +1,101 @@
+{-# OPTIONS_HADDOCK hide #-}
+-- |
+-- Module      : Data.Array.Accelerate.LLVM.Compile.Cache
+-- Copyright   : [2017] Trevor L. McDonell
+-- License     : BSD3
+--
+-- Maintainer  : Trevor L. McDonell <tmcdonell@cse.unsw.edu.au>
+-- Stability   : experimental
+-- Portability : non-portable (GHC extensions)
+--
+
+module Data.Array.Accelerate.LLVM.Compile.Cache (
+
+  Persistent(..), UID,
+  cacheOfUID,
+  cacheOfOpenAcc,
+  removeCacheDirectory,
+
+) where
+
+import Data.Array.Accelerate.Debug
+import Data.Array.Accelerate.Trafo
+
+import Data.Array.Accelerate.LLVM.State
+
+import Control.Monad.Trans
+import Data.Version
+import System.Directory
+import System.FilePath
+import Text.Printf
+
+import Paths_accelerate_llvm
+
+
+-- TODO:
+--  * Remove old files which have not been accessed in some time
+--  * Reuse old cache files when upgrading to new versions (ala stack)
+
+class Persistent arch where
+  -- | Specify a filename template which can be used to cache files for a given
+  -- backend. This should also include something to distinguish this
+  -- particular backend/target from another.
+  --
+  targetCacheTemplate :: LLVM arch FilePath
+
+
+-- | Unique identifier for an accelerate computation
+--
+type UID = Int
+
+
+-- | Return the unique cache file path corresponding to a given accelerate
+-- computation.
+--
+{-# INLINEABLE cacheOfOpenAcc #-}
+cacheOfOpenAcc
+    :: Persistent arch
+    => DelayedOpenAcc aenv a
+    -> LLVM arch (UID, FilePath)
+cacheOfOpenAcc acc = do
+  let uid = hashDelayedOpenAcc acc
+  cacheFile <- cacheOfUID uid
+  return (uid, cacheFile)
+
+
+-- | Return the unique cache file path corresponding to the unique identifier of
+-- an accelerate computation.
+--
+{-# INLINEABLE cacheOfUID #-}
+cacheOfUID
+    :: Persistent arch
+    => UID
+    -> LLVM arch FilePath
+cacheOfUID uid = do
+  dbg       <- liftIO $ queryFlag debug_cc
+  appdir    <- liftIO $ getAppUserDataDirectory "accelerate"
+  template  <- targetCacheTemplate
+  let
+      (base, file)  = splitFileName template
+      (name, ext)   = splitExtensions file
+      --
+      cachepath     = appdir </> "accelerate-llvm-" ++ showVersion version </> base </> if dbg then "dbg" else "rel"
+      cachefile     = cachepath </> printf "%s%016X" name uid <.> ext
+  --
+  liftIO $ createDirectoryIfMissing True cachepath
+  return cachefile
+
+
+-- | Remove the cache directory
+--
+{-# INLINEABLE removeCacheDirectory #-}
+removeCacheDirectory :: Persistent arch => LLVM arch ()
+removeCacheDirectory = do
+  appdir    <- liftIO $ getAppUserDataDirectory "accelerate"
+  template  <- targetCacheTemplate
+  let
+      (base, _)     = splitFileName template
+      cachepath     = appdir </> "accelerate-llvm-" ++ showVersion version </> base
+  --
+  liftIO $ removeDirectoryRecursive cachepath
+
diff --git a/Data/Array/Accelerate/LLVM/Compile/Cache.hs-boot b/Data/Array/Accelerate/LLVM/Compile/Cache.hs-boot
new file mode 100644
--- /dev/null
+++ b/Data/Array/Accelerate/LLVM/Compile/Cache.hs-boot
@@ -0,0 +1,19 @@
+-- |
+-- Module      : Data.Array.Accelerate.LLVM.Compile.Cache-boot
+-- Copyright   : [2017] Trevor L. McDonell
+-- License     : BSD3
+--
+-- Maintainer  : Trevor L. McDonell <tmcdonell@cse.unsw.edu.au>
+-- Stability   : experimental
+-- Portability : non-portable (GHC extensions)
+--
+
+module Data.Array.Accelerate.LLVM.Compile.Cache
+  where
+
+import {-# SOURCE #-} Data.Array.Accelerate.LLVM.State
+
+class Persistent arch
+
+removeCacheDirectory :: Persistent arch => LLVM arch ()
+
diff --git a/Data/Array/Accelerate/LLVM/Embed.hs b/Data/Array/Accelerate/LLVM/Embed.hs
new file mode 100644
--- /dev/null
+++ b/Data/Array/Accelerate/LLVM/Embed.hs
@@ -0,0 +1,280 @@
+{-# LANGUAGE CPP                 #-}
+{-# LANGUAGE GADTs               #-}
+{-# LANGUAGE QuasiQuotes         #-}
+{-# LANGUAGE RankNTypes          #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TemplateHaskell     #-}
+{-# OPTIONS_HADDOCK hide #-}
+-- |
+-- Module      : Data.Array.Accelerate.LLVM.Embed
+-- Copyright   : [2017] Trevor L. McDonell
+-- License     : BSD3
+--
+-- Maintainer  : Trevor L. McDonell <tmcdonell@cse.unsw.edu.au>
+-- Stability   : experimental
+-- Portability : non-portable (GHC extensions)
+--
+
+module Data.Array.Accelerate.LLVM.Embed (
+
+  Embed(..),
+  embedAfun, embedOpenAfun,
+  embedOpenAcc,
+
+) where
+
+import LLVM.AST.Type.Name
+
+import Data.Array.Accelerate.AST                                    ( liftIdx, liftTupleIdx, liftArrays, liftConst, liftSliceIndex, liftPrimConst, liftPrimFun )
+import Data.Array.Accelerate.Array.Sugar
+
+import Data.Array.Accelerate.LLVM.AST
+import Data.Array.Accelerate.LLVM.CodeGen.Environment
+import Data.Array.Accelerate.LLVM.Compile
+import Data.Array.Accelerate.LLVM.Link
+
+import Data.Typeable
+import Data.ByteString.Short                                        ( ShortByteString )
+import GHC.Ptr                                                      ( Ptr(..) )
+import Language.Haskell.TH                                          ( Q, TExp )
+import System.IO.Unsafe
+import qualified Data.ByteString.Short.Internal                     as BS
+import qualified Language.Haskell.TH                                as TH
+import qualified Language.Haskell.TH.Syntax                         as TH
+
+#if MIN_VERSION_containers(0,5,9)
+import qualified Data.IntMap.Internal                               as IM
+#elif MIN_VERSION_containers(0,5,8)
+import qualified Data.IntMap.Base                                   as IM
+#else
+import qualified Data.IntMap                                        as IM
+#endif
+
+
+class Embed arch where
+
+  -- | Turn the compiled object into a TemplateHaskell expression, suitable for
+  -- use in a splice. The splice should evaluate into the backend-specific
+  -- executable representation.
+  --
+  embedForTarget
+      :: arch
+      -> ObjectR arch
+      -> Q (TExp (ExecutableR arch))
+
+
+-- | Embed the compiled array function into a TemplateHaskell expression,
+-- suitable for use in a splice.
+--
+{-# INLINEABLE embedAfun #-}
+embedAfun
+    :: (Embed arch, Typeable arch)
+    => arch
+    -> CompiledAfun arch f
+    -> Q (TExp (ExecAfun arch f))
+embedAfun = embedOpenAfun
+
+{-# INLINEABLE embedOpenAfun #-}
+embedOpenAfun
+    :: (Embed arch, Typeable arch, Typeable aenv)
+    => arch
+    -> CompiledOpenAfun arch aenv f
+    -> Q (TExp (ExecOpenAfun arch aenv f))
+embedOpenAfun arch (Alam l)  = [|| Alam $$(embedOpenAfun arch l) ||]
+embedOpenAfun arch (Abody b) = [|| Abody $$(embedOpenAcc arch b) ||]
+
+{-# INLINEABLE embedOpenAcc #-}
+embedOpenAcc
+    :: forall arch aenv arrs. (Embed arch, Typeable arch, Typeable aenv, Typeable arrs)
+    => arch
+    -> CompiledOpenAcc arch aenv arrs
+    -> Q (TExp (ExecOpenAcc arch aenv arrs))
+embedOpenAcc arch = liftA
+  where
+    liftA :: (Typeable aenv', Typeable arrs') => CompiledOpenAcc arch aenv' arrs' -> Q (TExp (ExecOpenAcc arch aenv' arrs'))
+    liftA (PlainAcc pacc)          = withSigE [|| EvalAcc $$(liftPreOpenAccCommand arch pacc) ||]
+    liftA (BuildAcc aenv obj pacc) = withSigE [|| ExecAcc $$(liftGamma aenv) $$(embedForTarget arch obj) $$(liftPreOpenAccSkeleton arch pacc) ||]
+
+    liftGamma :: Gamma aenv' -> Q (TExp (Gamma aenv'))
+#if MIN_VERSION_containers(0,5,8)
+    liftGamma IM.Nil           = [|| IM.Nil ||]
+    liftGamma (IM.Bin p m l r) = [|| IM.Bin p m $$(liftGamma l) $$(liftGamma r) ||]
+    liftGamma (IM.Tip k v)     = [|| IM.Tip k $$(liftV v) ||]
+#else
+    -- O(n) at runtime to reconstruct the set
+    liftGamma aenv             = [|| IM.fromAscList $$(liftIM (IM.toAscList aenv)) ||]
+      where
+        liftIM :: [(Int, (Label, Idx' aenv'))] -> Q (TExp [(Int, (Label, Idx' aenv'))])
+        liftIM im = do
+          im' <- mapM (\(k,v) -> TH.unTypeQ [|| (k, $$(liftV v)) ||]) im
+          TH.unsafeTExpCoerce (return $ TH.ListE im')
+#endif
+    liftV :: (Label, Idx' aenv') -> Q (TExp (Label, Idx' aenv'))
+    liftV (Label n, Idx' ix) = [|| (Label $$(liftSBS n), Idx' $$(liftIdx ix)) ||]
+
+    -- O(n) at runtime to copy from the Addr# to the ByteArray#. We should
+    -- be able to do this without copying, but I don't think the definition of
+    -- ByteArray# is exported (or it is deeply magical).
+    liftSBS :: ShortByteString -> Q (TExp ShortByteString)
+    liftSBS bs =
+      let bytes = BS.unpack bs
+          len   = BS.length bs
+      in
+      [|| unsafePerformIO $ BS.createFromPtr $$( TH.unsafeTExpCoerce [| Ptr $(TH.litE (TH.StringPrimL bytes)) |]) len ||]
+
+
+{-# INLINEABLE liftPreOpenAfun #-}
+liftPreOpenAfun
+    :: (Embed arch, Typeable arch, Typeable aenv)
+    => arch
+    -> PreOpenAfun (CompiledOpenAcc arch) aenv t
+    -> Q (TExp (PreOpenAfun (ExecOpenAcc arch) aenv t))
+liftPreOpenAfun arch (Alam f)  = [|| Alam  $$(liftPreOpenAfun arch f) ||]
+liftPreOpenAfun arch (Abody b) = [|| Abody $$(embedOpenAcc arch b) ||]
+
+{-# INLINEABLE liftPreOpenAccCommand #-}
+liftPreOpenAccCommand
+    :: forall arch aenv a. (Embed arch, Typeable arch, Typeable aenv)
+    => arch
+    -> PreOpenAccCommand (CompiledOpenAcc arch) aenv a
+    -> Q (TExp (PreOpenAccCommand (ExecOpenAcc arch) aenv a))
+liftPreOpenAccCommand arch pacc =
+  let
+      liftA :: (Typeable aenv', Typeable arrs) => CompiledOpenAcc arch aenv' arrs -> Q (TExp (ExecOpenAcc arch aenv' arrs))
+      liftA = embedOpenAcc arch
+
+      liftE :: PreExp (CompiledOpenAcc arch) aenv t -> Q (TExp (PreExp (ExecOpenAcc arch) aenv t))
+      liftE = liftPreOpenExp arch
+
+      liftAF :: PreOpenAfun (CompiledOpenAcc arch) aenv f -> Q (TExp (PreOpenAfun (ExecOpenAcc arch) aenv f))
+      liftAF = liftPreOpenAfun arch
+
+      liftAtuple :: Atuple (CompiledOpenAcc arch aenv) t -> Q (TExp (Atuple (ExecOpenAcc arch aenv) t))
+      liftAtuple NilAtup          = [|| NilAtup ||]
+      liftAtuple (SnocAtup tup a) = [|| SnocAtup $$(liftAtuple tup) $$(liftA a) ||]
+  in
+  case pacc of
+    Avar ix           -> [|| Avar $$(liftIdx ix) ||]
+    Alet bnd body     -> [|| Alet $$(liftA bnd) $$(liftA body) ||]
+    Use a             -> [|| Use $$(liftArrays (arrays (undefined::a)) a) ||]
+    Unit e            -> [|| Unit $$(liftE e) ||]
+    Atuple tup        -> [|| Atuple $$(liftAtuple tup) ||]
+    Aprj tix a        -> [|| Aprj $$(liftTupleIdx tix) $$(liftA a) ||]
+    Apply f a         -> [|| Apply $$(liftAF f) $$(liftA a) ||]
+    Aforeign asm a    -> [|| Aforeign $$(liftForeign asm) $$(liftA a) ||]
+    Acond p t e       -> [|| Acond $$(liftE p) $$(liftA t) $$(liftA e) ||]
+    Awhile p f a      -> [|| Awhile $$(liftAF p) $$(liftAF f) $$(liftA a) ||]
+    Reshape sh ix     -> [|| Reshape $$(liftE sh) $$(liftIdx ix) ||]
+    Unzip tix ix      -> [|| Unzip $$(liftTupleIdx tix) $$(liftIdx ix) ||]
+
+{-# INLINEABLE liftPreOpenAccSkeleton #-}
+liftPreOpenAccSkeleton
+    :: forall arch aenv a. (Embed arch, Typeable arch, Typeable aenv)
+    => arch
+    -> PreOpenAccSkeleton (CompiledOpenAcc arch) aenv a
+    -> Q (TExp (PreOpenAccSkeleton (ExecOpenAcc arch) aenv a))
+liftPreOpenAccSkeleton arch pacc =
+  let
+      liftA :: Typeable arrs => CompiledOpenAcc arch aenv arrs -> Q (TExp (ExecOpenAcc arch aenv arrs))
+      liftA = embedOpenAcc arch
+
+      liftE :: PreExp (CompiledOpenAcc arch) aenv t -> Q (TExp (PreExp (ExecOpenAcc arch) aenv t))
+      liftE = liftPreOpenExp arch
+  in
+  case pacc of
+    Map sh            -> [|| Map $$(liftE sh) ||]
+    Generate sh       -> [|| Generate $$(liftE sh) ||]
+    Transform sh      -> [|| Transform $$(liftE sh) ||]
+    Backpermute sh    -> [|| Backpermute $$(liftE sh) ||]
+    Fold sh           -> [|| Fold $$(liftE sh) ||]
+    Fold1 sh          -> [|| Fold1 $$(liftE sh) ||]
+    FoldSeg sa ss     -> [|| FoldSeg $$(liftE sa) $$(liftE ss) ||]
+    Fold1Seg sa ss    -> [|| Fold1Seg $$(liftE sa) $$(liftE ss) ||]
+    Scanl sh          -> [|| Scanl $$(liftE sh) ||]
+    Scanl1 sh         -> [|| Scanl1 $$(liftE sh) ||]
+    Scanl' sh         -> [|| Scanl' $$(liftE sh) ||]
+    Scanr sh          -> [|| Scanr $$(liftE sh) ||]
+    Scanr1 sh         -> [|| Scanr1 $$(liftE sh) ||]
+    Scanr' sh         -> [|| Scanr' $$(liftE sh) ||]
+    Permute sh a      -> [|| Permute $$(liftE sh) $$(liftA a) ||]
+    Stencil a         -> [|| Stencil $$(liftIdx a) ||]
+    Stencil2 a1 a2    -> [|| Stencil2 $$(liftIdx a1) $$(liftIdx a2) ||]
+
+{-# INLINEABLE liftPreOpenFun #-}
+liftPreOpenFun
+    :: (Embed arch, Typeable arch, Typeable env, Typeable aenv)
+    => arch
+    -> PreOpenFun (CompiledOpenAcc arch) env aenv t
+    -> Q (TExp (PreOpenFun (ExecOpenAcc arch) env aenv t))
+liftPreOpenFun arch (Lam f)  = [|| Lam  $$(liftPreOpenFun arch f) ||]
+liftPreOpenFun arch (Body b) = [|| Body $$(liftPreOpenExp arch b) ||]
+
+{-# INLINEABLE liftPreOpenExp #-}
+liftPreOpenExp
+    :: forall arch env aenv t. (Embed arch, Typeable arch, Typeable env, Typeable aenv)
+    => arch
+    -> PreOpenExp (CompiledOpenAcc arch) env aenv t
+    -> Q (TExp (PreOpenExp (ExecOpenAcc arch) env aenv t))
+liftPreOpenExp arch pexp =
+  let
+      liftA :: Typeable arrs => CompiledOpenAcc arch aenv arrs -> Q (TExp (ExecOpenAcc arch aenv arrs))
+      liftA = embedOpenAcc arch
+
+      liftE :: PreOpenExp (CompiledOpenAcc arch) env aenv e -> Q (TExp (PreOpenExp (ExecOpenAcc arch) env aenv e))
+      liftE = liftPreOpenExp arch
+
+      liftF :: PreOpenFun (CompiledOpenAcc arch) env aenv f -> Q (TExp (PreOpenFun (ExecOpenAcc arch) env aenv f))
+      liftF = liftPreOpenFun arch
+
+      liftT :: Tuple (PreOpenExp (CompiledOpenAcc arch) env aenv) e -> Q (TExp (Tuple (PreOpenExp (ExecOpenAcc arch) env aenv) e))
+      liftT NilTup          = [|| NilTup ||]
+      liftT (SnocTup tup e) = [|| SnocTup $$(liftT tup) $$(liftE e) ||]
+  in
+  case pexp of
+    Let bnd body              -> [|| Let $$(liftPreOpenExp arch bnd) $$(liftPreOpenExp arch body) ||]
+    Var ix                    -> [|| Var $$(liftIdx ix) ||]
+    Foreign asm f x           -> [|| Foreign $$(liftForeign asm) $$(liftPreOpenFun arch f) $$(liftE x) ||]
+    Const c                   -> [|| Const $$(liftConst (eltType (undefined::t)) c) ||]
+    Tuple tup                 -> [|| Tuple $$(liftT tup) ||]
+    Prj tix e                 -> [|| Prj $$(liftTupleIdx tix) $$(liftE e) ||]
+    IndexNil                  -> [|| IndexNil ||]
+    IndexCons sh sz           -> [|| IndexCons $$(liftE sh) $$(liftE sz) ||]
+    IndexHead sh              -> [|| IndexHead $$(liftE sh) ||]
+    IndexTail sh              -> [|| IndexTail $$(liftE sh) ||]
+    IndexAny                  -> [|| IndexAny ||]
+    IndexSlice slice slix sh  -> [|| IndexSlice $$(liftSliceIndex slice) $$(liftE slix) $$(liftE sh) ||]
+    IndexFull slice slix sl   -> [|| IndexFull $$(liftSliceIndex slice) $$(liftE slix) $$(liftE sl) ||]
+    ToIndex sh ix             -> [|| ToIndex $$(liftE sh) $$(liftE ix) ||]
+    FromIndex sh ix           -> [|| FromIndex $$(liftE sh) $$(liftE ix) ||]
+    Cond p t e                -> [|| Cond $$(liftE p) $$(liftE t) $$(liftE e) ||]
+    While p f x               -> [|| While $$(liftF p) $$(liftF f) $$(liftE x) ||]
+    PrimConst t               -> [|| PrimConst $$(liftPrimConst t) ||]
+    PrimApp f x               -> [|| PrimApp $$(liftPrimFun f) $$(liftE x) ||]
+    Index a ix                -> [|| Index $$(liftA a) $$(liftE ix) ||]
+    LinearIndex a ix          -> [|| LinearIndex $$(liftA a) $$(liftE ix) ||]
+    Shape a                   -> [|| Shape $$(liftA a) ||]
+    ShapeSize ix              -> [|| ShapeSize $$(liftE ix) ||]
+    Intersect sh1 sh2         -> [|| Intersect $$(liftE sh1) $$(liftE sh2) ||]
+    Union sh1 sh2             -> [|| Union $$(liftE sh1) $$(liftE sh2) ||]
+
+
+-- Utilities
+-- ---------
+
+withSigE :: forall e. Typeable e => Q (TExp e) -> Q (TExp e)
+withSigE e = e `sigE` typeRepToType (typeOf (undefined::e))
+
+sigE :: Q (TExp t) -> Q TH.Type -> Q (TExp t)
+sigE e t = TH.unsafeTExpCoerce $ TH.sigE (TH.unTypeQ e) t
+
+typeRepToType :: TypeRep -> Q TH.Type
+typeRepToType trep = do
+  let (con, args)     = splitTyConApp trep
+      name            = TH.Name (TH.OccName (tyConName con)) (TH.NameG TH.TcClsName (TH.PkgName (tyConPackage con)) (TH.ModName (tyConModule con)))
+      --
+      appsT x []      = x
+      appsT x (y:xs)  = appsT (TH.AppT x y) xs
+      --
+  resultArgs <- mapM typeRepToType args
+  return (appsT (TH.ConT name) resultArgs)
+
diff --git a/Data/Array/Accelerate/LLVM/Execute.hs b/Data/Array/Accelerate/LLVM/Execute.hs
--- a/Data/Array/Accelerate/LLVM/Execute.hs
+++ b/Data/Array/Accelerate/LLVM/Execute.hs
@@ -1,9 +1,12 @@
-{-# LANGUAGE CPP                 #-}
-{-# LANGUAGE GADTs               #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE TemplateHaskell     #-}
-{-# LANGUAGE TypeFamilies        #-}
-{-# LANGUAGE TypeOperators       #-}
+{-# LANGUAGE BangPatterns          #-}
+{-# LANGUAGE CPP                   #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE GADTs                 #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE ScopedTypeVariables   #-}
+{-# LANGUAGE TemplateHaskell       #-}
+{-# LANGUAGE TypeFamilies          #-}
+{-# LANGUAGE TypeOperators         #-}
 {-# OPTIONS_HADDOCK hide #-}
 -- |
 -- Module      : Data.Array.Accelerate.LLVM.Execute
@@ -19,12 +22,12 @@
 module Data.Array.Accelerate.LLVM.Execute (
 
   Execute(..), Gamma,
-  executeAcc, executeAfun1,
+  executeAcc, executeAfun,
+  executeOpenAcc,
 
 ) where
 
 -- accelerate
-import Data.Array.Accelerate.AST
 import Data.Array.Accelerate.Analysis.Match
 import Data.Array.Accelerate.Array.Representation               ( SliceIndex(..) )
 import Data.Array.Accelerate.Array.Sugar                        hiding ( Foreign )
@@ -35,9 +38,10 @@
 import qualified Data.Array.Accelerate.Array.Sugar              as S
 import qualified Data.Array.Accelerate.Array.Representation     as R
 
+import Data.Array.Accelerate.LLVM.AST
 import Data.Array.Accelerate.LLVM.Array.Data
-import Data.Array.Accelerate.LLVM.Compile
 import Data.Array.Accelerate.LLVM.Foreign
+import Data.Array.Accelerate.LLVM.Link
 import Data.Array.Accelerate.LLVM.State
 
 import Data.Array.Accelerate.LLVM.CodeGen.Environment           ( Gamma )
@@ -200,7 +204,8 @@
 -- Computations are evaluated by traversing the AST bottom up, and for each node
 -- distinguishing between three cases:
 --
---  1. If it is a Use node, we return a reference to the array data.
+--  1. If it is a Use node, asynchronously transfer the data to the remote
+--     device (if necessary).
 --
 --  2. If it is a non-skeleton node, such as a let-binding or shape conversion,
 --     then execute directly by updating the environment or similar.
@@ -213,31 +218,73 @@
     :: forall arch a. Execute arch
     => ExecAcc arch a
     -> LLVM arch a
-executeAcc acc =
+executeAcc !acc =
   get =<< async (executeOpenAcc acc Aempty)
 
-{-# INLINEABLE executeAfun1 #-}
-executeAfun1
-    :: forall arch a b. (Execute arch, Arrays a)
-    => ExecAfun arch (a -> b)
-    -> a
-    -> LLVM arch b
-executeAfun1 afun arrs = do
-  AsyncR _ a <- async (useRemoteAsync arrs)
-  executeOpenAfun1 afun Aempty a
 
+-- Execute a variadic array function
+--
+{-# INLINEABLE executeAfun #-}
+executeAfun
+    :: ExecuteAfun arch f
+    => ExecAfun arch (ExecAfunR arch f)
+    -> f
+executeAfun f = executeOpenAfun f (return Aempty)
 
--- Execute an open array function of one argument
+class ExecuteAfun arch f where
+  type ExecAfunR arch f
+  executeOpenAfun :: ExecOpenAfun arch aenv (ExecAfunR arch f) -> LLVM arch (AvalR arch aenv) -> f
+
+instance (Remote arch, ExecuteAfun arch b) => ExecuteAfun arch (a -> b) where
+  type ExecAfunR arch (a -> b) = a -> ExecAfunR arch b
+  {-# INLINEABLE executeOpenAfun #-}
+  executeOpenAfun Abody{}  _ _    = $internalError "executeOpenAfun" "malformed array function"
+  executeOpenAfun (Alam f) k arrs =
+    let k' = do aenv       <- k
+                AsyncR _ a <- async (useRemoteAsync arrs)
+                return (aenv `Apush` a)
+    in
+    executeOpenAfun f k'
+
+instance Execute arch => ExecuteAfun arch (LLVM arch b) where
+  type ExecAfunR arch (LLVM arch b) = b
+  {-# INLINEABLE executeOpenAfun #-}
+  executeOpenAfun Alam{}    _ = $internalError "executeOpenAfun" "function not fully applied"
+  executeOpenAfun (Abody b) k = do
+    aenv <- k
+    get =<< async (executeOpenAcc b aenv)
+
+
+-- NOTE: [ExecuteAfun and closed type families]
 --
-{-# INLINEABLE executeOpenAfun1 #-}
-executeOpenAfun1
-    :: Execute arch
-    => ExecOpenAfun arch aenv (a -> b)
-    -> AvalR arch aenv
-    -> AsyncR arch a
-    -> LLVM arch b
-executeOpenAfun1 (Alam (Abody f)) aenv a = get =<< async (executeOpenAcc f (aenv `Apush` a))
-executeOpenAfun1 _                _    _ = error "boop!"
+-- It would be nice to use something like the following closed type family
+-- instance, and implement 'executeOpenAfun' as a regular recursive function,
+-- rather than as a class function.
+--
+-- > type family ExecAfunR arch r :: * where
+-- >   ExecAfunR arch (a -> b) = a -> ExecAfunR arch b
+-- >   ExecAfunR arch r        = LLVM arch r
+-- >
+-- > executeOpenAfun
+-- >     :: Execute arch
+-- >     => ExecOpenAfun arch aenv f
+-- >     -> LLVM arch (AvalR arch aenv)
+-- >     -> ExecAfunR arch f
+-- > executeOpenAfun (Alam f)  k = \arrs -> ...
+-- > executeOpenAfun (Abody b) k = do ...
+--
+-- However, closed type families don't quite work the way that we might think.
+-- It seems that they rely on some notion of type inequality, or at least types
+-- which don't have a unifier.
+--
+-- When we match of the `Abody` constructor, we expose a constraint of the form
+-- `Arrays a, T a ~ a0`. For the type checker to figure out that
+-- `a0 ~ LLVM arch a`, it needs to know that it _can not_ match on the first
+-- case of the type family; i.e., that `a` can't unify with `b -> c`. Since it
+-- doesn't have constraints to figure that out, it doesn't proceed and fall
+-- through to the case that we want. If we had something like `a ~ Array sh e`,
+-- then it could.
+--
 
 
 -- Execute an open array computation
@@ -249,66 +296,50 @@
     -> AvalR arch aenv
     -> StreamR arch
     -> LLVM arch arrs
-executeOpenAcc EmbedAcc{} _ _ =
-  $internalError "execute" "unexpected delayed array"
-executeOpenAcc (ExecAcc kernel gamma pacc) aenv stream =
-  case pacc of
-
-    -- Array introduction
-    Use arr                     -> return (toArr arr)
-    Unit x                      -> newRemote Z . const =<< travE x
-
-    -- Environment manipulation
-    Avar ix                     -> do let AsyncR event arr = aprj ix aenv
-                                      after stream event
-                                      return arr
-    Alet bnd body               -> do bnd'  <- async (executeOpenAcc bnd aenv)
-                                      body' <- executeOpenAcc body (aenv `Apush` bnd') stream
-                                      return body'
-    Apply f a                   -> executeOpenAfun1 f aenv =<< async (executeOpenAcc a aenv)
-    Atuple tup                  -> toAtuple <$> travT tup
-    Aprj ix tup                 -> evalPrj ix . fromAtuple <$> travA tup
-    Acond p t e                 -> acond t e =<< travE p
-    Awhile p f a                -> awhile p f =<< travA a
-
-    -- Foreign function
-    Aforeign asm _ a            -> foreignA asm =<< travA a
-
-    -- Producers
-    Map _ a                     -> map kernel gamma aenv stream         =<< extent a
-    Generate sh _               -> generate kernel gamma aenv stream    =<< travE sh
-    Transform sh _ _ _          -> transform kernel gamma aenv stream   =<< travE sh
-    Backpermute sh _ _          -> backpermute kernel gamma aenv stream =<< travE sh
-    Reshape sh a                -> reshape <$> travE sh <*> travA a
-
-    -- Consumers
-    Fold _ _ a                  -> fold  kernel gamma aenv stream =<< extent a
-    Fold1 _ a                   -> fold1 kernel gamma aenv stream =<< extent a
-    FoldSeg _ _ a s             -> join $ foldSeg  kernel gamma aenv stream <$> extent a <*> extent s
-    Fold1Seg _ a s              -> join $ fold1Seg kernel gamma aenv stream <$> extent a <*> extent s
-    Scanl _ _ a                 -> scanl kernel gamma aenv stream =<< extent a
-    Scanr _ _ a                 -> scanr kernel gamma aenv stream =<< extent a
-    Scanl1 _ a                  -> scanl1 kernel gamma aenv stream =<< extent a
-    Scanr1 _ a                  -> scanr1 kernel gamma aenv stream =<< extent a
-    Scanl' _ _ a                -> scanl' kernel gamma aenv stream =<< extent a
-    Scanr' _ _ a                -> scanr' kernel gamma aenv stream =<< extent a
-    Permute _ d _ a             -> join $ permute kernel gamma aenv stream (inplace d) <$> extent a <*> travA d
-    Stencil _ _ a               -> stencil1 kernel gamma aenv stream =<< travA a
-    Stencil2 _ _ a _ b          -> join $ stencil2 kernel gamma aenv stream <$> travA a <*> travA b
+executeOpenAcc !topAcc !aenv !stream = travA topAcc
+  where
+    travA :: ExecOpenAcc arch aenv a -> LLVM arch a
+    travA (EvalAcc pacc) =
+      case pacc of
+        Use arrs        -> get =<< useRemoteAsync (toArr arrs) stream
+        Unit x          -> newRemote Z . const =<< travE x
+        Avar ix         -> avar ix
+        Alet bnd body   -> alet bnd body
+        Apply f a       -> travAF f =<< async (executeOpenAcc a aenv)
+        Atuple tup      -> toAtuple <$> travT tup
+        Aprj ix tup     -> evalPrj ix . fromAtuple <$> travA tup
+        Acond p t e     -> acond t e =<< travE p
+        Awhile p f a    -> awhile p f =<< travA a
+        Reshape sh ix   -> reshape <$> travE sh <*> avar ix
+        Unzip tix ix    -> unzip tix <$> avar ix
+        Aforeign asm a  -> aforeign asm =<< travA a
 
-    -- Removed by fusion
-    Replicate{}                 -> fusionError
-    Slice{}                     -> fusionError
-    ZipWith{}                   -> fusionError
+    travA (ExecAcc !gamma !kernel pacc) =
+      case pacc of
+        -- Producers
+        Map sh          -> map kernel gamma aenv stream =<< travE sh
+        Generate sh     -> generate kernel gamma aenv stream =<< travE sh
+        Transform sh    -> transform kernel gamma aenv stream =<< travE sh
+        Backpermute sh  -> backpermute kernel gamma aenv stream =<< travE sh
 
-  where
-    fusionError :: error
-    fusionError = $internalError "execute" $ "unexpected fusible material: " ++ showPreAccOp pacc
+        -- Consumers
+        Fold sh         -> fold  kernel gamma aenv stream =<< travE sh
+        Fold1 sh        -> fold1 kernel gamma aenv stream =<< travE sh
+        FoldSeg sa ss   -> id =<< foldSeg  kernel gamma aenv stream <$> travE sa <*> travE ss
+        Fold1Seg sa ss  -> id =<< fold1Seg kernel gamma aenv stream <$> travE sa <*> travE ss
+        Scanl sh        -> scanl  kernel gamma aenv stream =<< travE sh
+        Scanr sh        -> scanr  kernel gamma aenv stream =<< travE sh
+        Scanl1 sh       -> scanl1 kernel gamma aenv stream =<< travE sh
+        Scanr1 sh       -> scanr1 kernel gamma aenv stream =<< travE sh
+        Scanl' sh       -> scanl' kernel gamma aenv stream =<< travE sh
+        Scanr' sh       -> scanr' kernel gamma aenv stream =<< travE sh
+        Permute sh d    -> id =<< permute kernel gamma aenv stream (inplace d) <$> travE sh <*> travA d
+        Stencil2 a b    -> id =<< stencil2 kernel gamma aenv stream <$> avar a <*> avar b
+        Stencil a       -> stencil1 kernel gamma aenv stream =<< avar a
 
-    -- Term traversals
-    -- ---------------
-    travA :: ExecOpenAcc arch aenv a -> LLVM arch a
-    travA acc = executeOpenAcc acc aenv stream
+    travAF :: ExecOpenAfun arch aenv (a -> b) -> AsyncR arch a -> LLVM arch b
+    travAF (Alam (Abody f)) a = get =<< async (executeOpenAcc f (aenv `Apush` a))
+    travAF _                _ = error "boop!"
 
     travE :: ExecExp arch aenv t -> LLVM arch t
     travE exp = executeExp exp aenv stream
@@ -317,28 +348,25 @@
     travT NilAtup        = return ()
     travT (SnocAtup t a) = (,) <$> travT t <*> travA a
 
-    -- get the extent of an embedded array
-    extent :: Shape sh => ExecOpenAcc arch aenv (Array sh e) -> LLVM arch sh
-    extent ExecAcc{}       = $internalError "executeOpenAcc" "expected delayed array"
-    extent (EmbedAcc sh)   = travE sh
-    extent (UnzipAcc _ ix) = let AsyncR _ a = aprj ix aenv
-                             in  return $ shape a
-
-    inplace :: ExecOpenAcc arch aenv a -> Bool
-    inplace (ExecAcc _ _ Avar{}) = False
-    inplace _                    = True
-
-    -- Skeleton implementation
-    -- -----------------------
+    -- Bound terms. Let-bound input arrays (Use nodes) are copied to the device
+    -- asynchronously, so that they may overlap other computations if possible.
+    alet :: ExecOpenAcc arch aenv bnd -> ExecOpenAcc arch (aenv, bnd) body -> LLVM arch body
+    alet bnd body = do
+      bnd'  <- case bnd of
+                 EvalAcc (Use arrs) -> do AsyncR _ bnd' <- async (useRemoteAsync (toArr arrs))
+                                          return bnd'
+                 _                  -> async (executeOpenAcc bnd aenv)
+      body' <- executeOpenAcc body (aenv `Apush` bnd') stream
+      return body'
 
-    -- Change the shape of an array without altering its contents. This does not
-    -- execute any kernel programs.
-    reshape :: Shape sh => sh -> Array sh' e -> Array sh e
-    reshape sh (Array sh' adata)
-      = $boundsCheck "reshape" "shape mismatch" (size sh == R.size sh')
-      $ Array (fromElt sh) adata
+    -- Access bound variables
+    avar :: Idx aenv a -> LLVM arch a
+    avar ix = do
+      let AsyncR event arr = aprj ix aenv
+      after stream event
+      return arr
 
-    -- Array level conditional
+    -- Array level conditionals
     acond :: ExecOpenAcc arch aenv a -> ExecOpenAcc arch aenv a -> Bool -> LLVM arch a
     acond yes _  True  = travA yes
     acond _   no False = travA no
@@ -350,26 +378,18 @@
            -> LLVM arch a
     awhile p f a = do
       e   <- checkpoint stream
-      r   <- executeOpenAfun1 p aenv (AsyncR e a)
+      r   <- travAF p (AsyncR e a)
       ok  <- indexRemote r 0
-      if ok then awhile p f =<< executeOpenAfun1 f aenv (AsyncR e a)
+      if ok then awhile p f =<< travAF f (AsyncR e a)
             else return a
 
-    -- Foreign functions
-    foreignA :: (Arrays a, Arrays b, Foreign arch, S.Foreign asm)
-             => asm (a -> b)
-             -> a
-             -> LLVM arch b
-    foreignA asm a =
-      case foreignAcc (undefined :: arch) asm of
-        Just f  -> f stream a
-        Nothing -> $internalError "foreignA" "failed to recover foreign function the second time"
+    -- Change the shape of an array without altering its contents
+    reshape :: Shape sh => sh -> Array sh' e -> Array sh e
+    reshape sh (Array sh' adata)
+      = $boundsCheck "reshape" "shape mismatch" (size sh == R.size sh')
+      $ Array (fromElt sh) adata
 
-executeOpenAcc (UnzipAcc tup v) aenv stream = do
-  let AsyncR event arr = aprj v aenv
-  after stream event
-  return $ unzip tup arr
-  where
+    -- Pull apart the unzipped struct-of-array representation
     unzip :: forall t sh e. (Elt t, Elt e) => TupleIdx (TupleRepr t) e -> Array sh t -> Array sh e
     unzip tix (Array sh adata) = Array sh $ go tix (eltType (undefined::t)) adata
       where
@@ -378,6 +398,21 @@
         go ZeroTupIdx      (PairTuple _ t) (AD_Pair _ x)
           | Just Refl <- matchTupleType t (eltType (undefined::e)) = x
         go _ _ _                                                   = $internalError "unzip" "inconsistent valuation"
+
+    -- Foreign functions
+    aforeign :: (Arrays a, Arrays b, Foreign arch, S.Foreign asm)
+             => asm (a -> b)
+             -> a
+             -> LLVM arch b
+    aforeign asm a =
+      case foreignAcc (undefined :: arch) asm of
+        Just f  -> f stream a
+        Nothing -> $internalError "foreignA" "failed to recover foreign function the second time"
+
+    -- Can the permutation function write directly into the results array?
+    inplace :: ExecOpenAcc arch aenv a -> Bool
+    inplace (EvalAcc Avar{}) = False
+    inplace _                = True
 
 
 -- Scalar expression evaluation
diff --git a/Data/Array/Accelerate/LLVM/Execute/Async.hs b/Data/Array/Accelerate/LLVM/Execute/Async.hs
--- a/Data/Array/Accelerate/LLVM/Execute/Async.hs
+++ b/Data/Array/Accelerate/LLVM/Execute/Async.hs
@@ -68,11 +68,13 @@
 
 -- | Wait for an asynchronous operation to complete, then return it.
 --
+{-# INLINEABLE get #-}
 get :: Async arch => AsyncR arch a -> LLVM arch a
 get (AsyncR e a) = block e >> return a
 
 -- | Execute the given operation asynchronously in a new execution stream.
 --
+{-# INLINEABLE async #-}
 async :: Async arch
       => (StreamR arch -> LLVM arch a)
       -> LLVM arch (AsyncR arch a)
diff --git a/Data/Array/Accelerate/LLVM/Link.hs b/Data/Array/Accelerate/LLVM/Link.hs
new file mode 100644
--- /dev/null
+++ b/Data/Array/Accelerate/LLVM/Link.hs
@@ -0,0 +1,203 @@
+{-# LANGUAGE GADTs               #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TemplateHaskell     #-}
+{-# LANGUAGE TypeFamilies        #-}
+{-# OPTIONS_HADDOCK hide #-}
+-- |
+-- Module      : Data.Array.Accelerate.LLVM.Link
+-- Copyright   : [2017] Trevor L. McDonell
+-- License     : BSD3
+--
+-- Maintainer  : Trevor L. McDonell <tmcdonell@cse.unsw.edu.au>
+-- Stability   : experimental
+-- Portability : non-portable (GHC extensions)
+--
+
+module Data.Array.Accelerate.LLVM.Link (
+
+  Link(..),
+  linkAcc, linkAfun,
+
+  ExecOpenAcc(..), ExecOpenAfun,
+  ExecAcc, ExecAfun,
+  ExecExp, ExecOpenExp,
+  ExecFun, ExecOpenFun
+
+) where
+
+-- accelerate
+import Data.Array.Accelerate.Array.Sugar                            hiding ( Foreign )
+import Data.Array.Accelerate.Error
+
+import Data.Array.Accelerate.LLVM.AST
+import Data.Array.Accelerate.LLVM.CodeGen.Environment
+import Data.Array.Accelerate.LLVM.Compile
+import Data.Array.Accelerate.LLVM.State
+
+import Control.Applicative                                          hiding ( Const )
+import Control.DeepSeq
+import Prelude                                                      hiding ( exp )
+
+
+class Link arch where
+  data ExecutableR arch
+
+  -- | Link a target-specific object file into the current context to produce an
+  -- executable functions.
+  --
+  linkForTarget
+      :: ObjectR arch
+      -> LLVM arch (ExecutableR arch)
+
+
+-- | Annotate an open array expression with executable code loaded into the
+-- target address space, suitable for execution.
+--
+data ExecOpenAcc arch aenv a where
+  ExecAcc   :: Gamma aenv
+            -> ExecutableR arch
+            -> PreOpenAccSkeleton (ExecOpenAcc arch) aenv a
+            -> ExecOpenAcc arch aenv a
+
+  EvalAcc   :: Arrays a
+            => PreOpenAccCommand (ExecOpenAcc arch) aenv a
+            -> ExecOpenAcc arch aenv a
+
+-- An AST annotated with compiled and linked functions in the target address
+-- space, suitable for execution.
+--
+type ExecOpenAfun arch  = PreOpenAfun (ExecOpenAcc arch)
+type ExecOpenExp arch   = PreOpenExp (ExecOpenAcc arch)
+type ExecOpenFun arch   = PreOpenFun (ExecOpenAcc arch)
+
+type ExecAcc arch a     = ExecOpenAcc arch () a
+type ExecAfun arch a    = ExecOpenAfun arch () a
+
+type ExecExp arch       = ExecOpenExp arch ()
+type ExecFun arch       = ExecOpenFun arch ()
+
+
+-- | Link the compiled code for an array expression into the target address
+-- space. Additionally, copy input array data into the target address space.
+--
+{-# INLINEABLE linkAcc #-}
+linkAcc
+    :: Link arch
+    => CompiledAcc arch a
+    -> LLVM arch (ExecAcc arch a)
+linkAcc = linkOpenAcc
+
+{-# INLINEABLE linkAfun #-}
+linkAfun
+    :: Link arch
+    => CompiledAfun arch f
+    -> LLVM arch (ExecAfun arch f)
+linkAfun = linkOpenAfun
+
+
+{-# INLINEABLE linkOpenAfun #-}
+linkOpenAfun
+    :: Link arch
+    => CompiledOpenAfun arch aenv f
+    -> LLVM arch (ExecOpenAfun arch aenv f)
+linkOpenAfun (Alam l)  = Alam  <$> linkOpenAfun l
+linkOpenAfun (Abody b) = Abody <$> linkOpenAcc b
+
+{-# INLINEABLE linkOpenAcc #-}
+linkOpenAcc
+    :: forall arch _aenv _a. Link arch
+    => CompiledOpenAcc arch _aenv _a
+    -> LLVM arch (ExecOpenAcc arch _aenv _a)
+linkOpenAcc = travA
+  where
+    travA :: forall aenv arrs. CompiledOpenAcc arch aenv arrs -> LLVM arch (ExecOpenAcc arch aenv arrs)
+    travA (PlainAcc pacc) = EvalAcc <$>
+      case pacc of
+        Unzip tix ix            -> return (Unzip tix ix)
+        Avar ix                 -> return (Avar ix)
+        Use arrs                -> rnfArrs (arrays (undefined::arrs)) arrs `seq` return (Use arrs)
+        Unit e                  -> Unit         <$> travE e
+        Alet a b                -> Alet         <$> travA a  <*> travA b
+        Apply f a               -> Apply        <$> travAF f <*> travA a
+        Awhile p f a            -> Awhile       <$> travAF p <*> travAF f <*> travA a
+        Acond p t e             -> Acond        <$> travE p  <*> travA t  <*> travA e
+        Atuple tup              -> Atuple       <$> travAtup tup
+        Aprj ix tup             -> Aprj ix      <$> travA tup
+        Reshape s ix            -> Reshape      <$> travE s <*> pure ix
+        Aforeign asm a          -> Aforeign asm <$> travA a
+
+    travA (BuildAcc aenv obj pacc) = ExecAcc aenv <$> linkForTarget obj <*>
+      case pacc of
+        Map sh                  -> Map          <$> travE sh
+        Generate sh             -> Generate     <$> travE sh
+        Transform sh            -> Transform    <$> travE sh
+        Backpermute sh          -> Backpermute  <$> travE sh
+        Fold sh                 -> Fold         <$> travE sh
+        Fold1 sh                -> Fold1        <$> travE sh
+        FoldSeg sa ss           -> FoldSeg      <$> travE sa <*> travE ss
+        Fold1Seg sa ss          -> Fold1Seg     <$> travE sa <*> travE ss
+        Scanl sh                -> Scanl        <$> travE sh
+        Scanl1 sh               -> Scanl1       <$> travE sh
+        Scanl' sh               -> Scanl'       <$> travE sh
+        Scanr sh                -> Scanr        <$> travE sh
+        Scanr1 sh               -> Scanr1       <$> travE sh
+        Scanr' sh               -> Scanr'       <$> travE sh
+        Permute sh d            -> Permute      <$> travE sh <*> travA d
+        Stencil a               -> return (Stencil a)
+        Stencil2 a b            -> return (Stencil2 a b)
+
+    travAF :: CompiledOpenAfun arch aenv f
+           -> LLVM arch (ExecOpenAfun arch aenv f)
+    travAF = linkOpenAfun
+
+    travAtup :: Atuple (CompiledOpenAcc arch aenv) a
+             -> LLVM arch (Atuple (ExecOpenAcc arch aenv) a)
+    travAtup NilAtup        = return NilAtup
+    travAtup (SnocAtup t a) = SnocAtup <$> travAtup t <*> travA a
+
+    travF :: CompiledOpenFun arch env aenv t
+          -> LLVM arch (ExecOpenFun arch env aenv t)
+    travF (Body b) = Body <$> travE b
+    travF (Lam  f) = Lam  <$> travF f
+
+    travE :: CompiledOpenExp arch env aenv t
+          -> LLVM arch (ExecOpenExp arch env aenv t)
+    travE exp =
+      case exp of
+        Var ix                  -> return (Var ix)
+        Const c                 -> return (Const c)
+        PrimConst c             -> return (PrimConst c)
+        IndexAny                -> return IndexAny
+        IndexNil                -> return IndexNil
+        Let a b                 -> Let                <$> travE a <*> travE b
+        IndexCons t h           -> IndexCons          <$> travE t <*> travE h
+        IndexHead h             -> IndexHead          <$> travE h
+        IndexTail t             -> IndexTail          <$> travE t
+        IndexSlice slix x s     -> (IndexSlice slix)  <$> travE x <*> travE s
+        IndexFull slix x s      -> (IndexFull slix)   <$> travE x <*> travE s
+        ToIndex s i             -> ToIndex            <$> travE s <*> travE i
+        FromIndex s i           -> FromIndex          <$> travE s <*> travE i
+        Tuple t                 -> Tuple              <$> travT t
+        Prj ix e                -> (Prj ix)           <$> travE e
+        Cond p t e              -> Cond               <$> travE p <*> travE t <*> travE e
+        While p f x             -> While              <$> travF p <*> travF f <*> travE x
+        PrimApp f e             -> (PrimApp f)        <$> travE e
+        Index a e               -> Index              <$> travA a <*> travE e
+        LinearIndex a e         -> LinearIndex        <$> travA a <*> travE e
+        Shape a                 -> Shape              <$> travA a
+        ShapeSize e             -> ShapeSize          <$> travE e
+        Intersect x y           -> Intersect          <$> travE x <*> travE y
+        Union x y               -> Union              <$> travE x <*> travE y
+        Foreign asm _ x         -> Foreign asm err    <$> travE x
+          where err = $internalError "link" "attempt to use fallback foreign expression"
+
+    travT :: Tuple (CompiledOpenExp arch env aenv) t
+          -> LLVM arch (Tuple (ExecOpenExp arch env aenv) t)
+    travT NilTup        = return NilTup
+    travT (SnocTup t e) = SnocTup <$> travT t <*> travE e
+
+    rnfArrs :: ArraysR a -> a -> ()
+    rnfArrs (ArraysRpair ar1 ar2) (a1, a2) = rnfArrs ar1 a1 `seq` rnfArrs ar2 a2
+    rnfArrs ArraysRarray arr               = rnf arr
+    rnfArrs ArraysRunit ()                 = ()
+
diff --git a/Data/Array/Accelerate/LLVM/Link/Cache.hs b/Data/Array/Accelerate/LLVM/Link/Cache.hs
new file mode 100644
--- /dev/null
+++ b/Data/Array/Accelerate/LLVM/Link/Cache.hs
@@ -0,0 +1,114 @@
+{-# OPTIONS_HADDOCK hide #-}
+-- |
+-- Module      : Data.Array.Accelerate.LLVM.Link.Cache
+-- Copyright   : [2017] Trevor L. McDonell
+-- License     : BSD3
+--
+-- Maintainer  : Trevor L. McDonell <tmcdonell@cse.unsw.edu.au>
+-- Stability   : experimental
+-- Portability : non-portable (GHC extensions)
+--
+
+module Data.Array.Accelerate.LLVM.Link.Cache (
+
+  LinkCache,
+  new, dlsym,
+
+) where
+
+import Data.Array.Accelerate.Lifetime
+import Data.Array.Accelerate.Debug
+
+import Control.Monad
+import Control.Concurrent.MVar
+import Data.IntMap.Strict                                           ( IntMap )
+import Prelude                                                      hiding ( lookup )
+import Text.Printf
+import qualified Data.IntMap.Strict                                 as IM
+
+
+-- Simple reference-counted linker cache for function tables 'f' implemented by
+-- object code 'o'.
+--
+data LinkCache f o = LinkCache {-# UNPACK #-} !(MVar (IntMap (Entry f o)))
+data Entry f o     = Entry {-# UNPACK #-} !Int !f !o
+
+
+-- Create a new linker cache
+--
+new :: IO (LinkCache f o)
+new = LinkCache `liftM` newMVar IM.empty
+
+
+-- Return the binding addresses for the given kernel functions (by key). If the
+-- functions do not already exist in the cache, the supplied continuation will
+-- be run in order to generate them. This happens as a single atomic step; thus
+-- the cache is thread safe.
+--
+dlsym :: Int -> LinkCache f o -> IO (f,o) -> IO (Lifetime f)
+dlsym key cache@(LinkCache var) k = do
+  modifyMVar var $ \im ->
+    case IM.lookup key im of
+      -- Run the supplied function to generate the object code and add to the cache
+      Nothing -> do
+        (f,o)  <- k
+        ticket <- issue key f cache
+        return ( IM.insert key (Entry 1 f o) im, ticket )
+
+      -- Return the existing object code
+      Just (Entry c f o) -> do
+        ticket <- issue key f cache
+        return ( IM.insert key (Entry (c+1) f o) im, ticket )
+
+
+{--
+-- Insert the given function table and object code into the cache. The returned
+-- value must be kept alive for as long as you need the object code to live;
+-- linker table entries are removed once all tickets referring to them are
+-- GC'ed.
+--
+-- NOTE: It is an error if the entry already exists in the table. Thus, there is
+-- a potential race condition between 'lookup' and 'insert'. On collision, it
+-- would be fine to return a reference to the existing implementation instead
+-- and discard the input values, but 'dlsym' solves this anyway.
+--
+insert :: Int -> f -> o -> LinkCache f o -> IO (Lifetime f)
+insert key functionTable objectCode cache@(LinkCache var) = do
+  ticket <- issue key functionTable cache
+  modifyMVar_ var $ \im ->
+    let collision = $internalError "insert" "duplicate entry"
+    in  return $! IM.insertWith collision key (Entry 1 functionTable objectCode) im
+  --
+  return ticket
+
+
+-- Check the linker cache for the given functions; if found return the
+-- corresponding function table.
+--
+lookup :: Int -> LinkCache f o -> IO (Maybe (Lifetime f))
+lookup key cache@(LinkCache var) = do
+  modifyMVar var $ \im ->
+    case IM.lookup key im of
+      Nothing             -> return (im, Nothing)
+      Just (Entry c f o)  -> do
+        ticket <- issue key f cache
+        return ( IM.insert key (Entry (c+1) f o) im, Just ticket )
+--}
+
+
+-- Issue a new ticket for the given table key/function table. When the returned
+-- lifetime is GC'ed it decreasing the reference count of the corresponding
+-- entry, and removes it from the table entirely once the count drops to zero.
+--
+issue :: Int -> f -> LinkCache f o -> IO (Lifetime f)
+issue key fun (LinkCache var) = do
+  ticket <- newLifetime fun
+  addFinalizer ticket $
+    let refcount (Entry c f o)
+          | c <= 1    = trace dump_ld (printf "ld: remove object code %016x" key) Nothing
+          | otherwise = Just (Entry (c-1) f o)
+    in
+    modifyMVar_ var $ \im -> return $! IM.update refcount key im
+  --
+  return ticket
+
diff --git a/Data/Array/Accelerate/LLVM/State.hs b/Data/Array/Accelerate/LLVM/State.hs
--- a/Data/Array/Accelerate/LLVM/State.hs
+++ b/Data/Array/Accelerate/LLVM/State.hs
@@ -18,8 +18,8 @@
 -- library
 import Control.Applicative                              ( Applicative )
 import Control.Concurrent                               ( forkIO, threadDelay )
-import Control.Monad.State                              ( StateT, MonadState, evalStateT )
 import Control.Monad.Catch                              ( MonadCatch, MonadThrow, MonadMask )
+import Control.Monad.State                              ( StateT, MonadState, evalStateT )
 import Control.Monad.Trans                              ( MonadIO )
 import Prelude
 
diff --git a/Data/Array/Accelerate/LLVM/State.hs-boot b/Data/Array/Accelerate/LLVM/State.hs-boot
new file mode 100644
--- /dev/null
+++ b/Data/Array/Accelerate/LLVM/State.hs-boot
@@ -0,0 +1,17 @@
+-- |
+-- Module      : Data.Array.Accelerate.LLVM.State-boot
+-- Copyright   : [2017] Trevor L. McDonell
+-- License     : BSD3
+--
+-- Maintainer  : Trevor L. McDonell <tmcdonell@cse.unsw.edu.au>
+-- Stability   : experimental
+-- Portability : non-portable (GHC extensions)
+--
+
+module Data.Array.Accelerate.LLVM.State
+  where
+
+import Control.Monad.State
+
+newtype LLVM target a = LLVM { runLLVM :: StateT target IO a }
+
diff --git a/Data/Array/Accelerate/LLVM/Target.hs b/Data/Array/Accelerate/LLVM/Target.hs
--- a/Data/Array/Accelerate/LLVM/Target.hs
+++ b/Data/Array/Accelerate/LLVM/Target.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE EmptyDataDecls            #-}
 {-# LANGUAGE ExistentialQuantification #-}
 {-# LANGUAGE ScopedTypeVariables       #-}
 {-# LANGUAGE TypeFamilies              #-}
@@ -17,13 +16,13 @@
 module Data.Array.Accelerate.LLVM.Target
   where
 
--- llvm-hs
 import LLVM.AST.DataLayout                                ( DataLayout )
+import Data.ByteString.Short                              ( ShortByteString )
 
 
 -- | Describes some target specific information needed for code generation
 --
 class Target t where
-  targetTriple          :: t {- dummy -} -> Maybe String
+  targetTriple          :: t {- dummy -} -> Maybe ShortByteString
   targetDataLayout      :: t {- dummy -} -> Maybe DataLayout
 
diff --git a/Data/ByteString/Short/Char8.hs b/Data/ByteString/Short/Char8.hs
new file mode 100644
--- /dev/null
+++ b/Data/ByteString/Short/Char8.hs
@@ -0,0 +1,46 @@
+{-# OPTIONS_HADDOCK hide #-}
+-- |
+-- Module      : Data.ByteString.Short.Char8
+-- Copyright   : [2017] Trevor L. McDonell
+-- License     : BSD3
+--
+-- Maintainer  : Trevor L. McDonell <tmcdonell@cse.unsw.edu.au>
+-- Stability   : experimental
+-- Portability : non-portable (GHC extensions)
+--
+
+module Data.ByteString.Short.Char8 (
+
+  ShortByteString,
+  pack,
+  unpack,
+  takeWhile,
+
+) where
+
+import Data.ByteString.Short                                        ( ShortByteString )
+import Prelude                                                      as P hiding ( takeWhile )
+import qualified Data.ByteString.Internal                           as BI
+import qualified Data.ByteString.Short                              as BS
+import qualified Data.ByteString.Short.Extra                        as BS
+
+
+-- | /O(n)/ Convert a 'ShortByteString' into a list.
+--
+{-# INLINEABLE unpack #-}
+unpack :: ShortByteString -> [Char]
+unpack = P.map BI.w2c . BS.unpack
+
+-- | /O(n)/ Convert a 'String' into a 'ShortByteString'.
+--
+{-# INLINEABLE pack #-}
+pack :: String -> ShortByteString
+pack = BS.pack . P.map BI.c2w
+
+-- | 'takeWhile', applied to a predicate @p@ and a ShortByteString @xs@, returns
+-- the longest prefix (possibly empty) of @xs@ of elements that satisfy @p@.
+--
+{-# INLINEABLE takeWhile #-}
+takeWhile :: (Char -> Bool) -> ShortByteString -> ShortByteString
+takeWhile f = BS.takeWhile (f . BI.w2c)
+
diff --git a/Data/ByteString/Short/Extra.hs b/Data/ByteString/Short/Extra.hs
new file mode 100644
--- /dev/null
+++ b/Data/ByteString/Short/Extra.hs
@@ -0,0 +1,103 @@
+{-# LANGUAGE BangPatterns  #-}
+{-# LANGUAGE MagicHash     #-}
+{-# LANGUAGE UnboxedTuples #-}
+{-# OPTIONS_HADDOCK hide #-}
+-- |
+-- Module      : Data.ByteString.Short.Extra
+-- Copyright   : [2017] Trevor L. McDonell
+-- License     : BSD3
+--
+-- Maintainer  : Trevor L. McDonell <tmcdonell@cse.unsw.edu.au>
+-- Stability   : experimental
+-- Portability : non-portable (GHC extensions)
+--
+
+module Data.ByteString.Short.Extra (
+
+  ShortByteString,
+  take,
+  takeWhile,
+
+) where
+
+import Data.ByteString.Short                                        ( ShortByteString )
+import qualified Data.ByteString.Short                              as BS
+import qualified Data.ByteString.Short.Internal                     as BI
+import Prelude                                                      hiding ( take, takeWhile )
+
+import GHC.ST
+import GHC.Exts
+import GHC.Word
+
+
+-- | /O(n)/ @'take' n@ applied to the ShortByteString @xs@, returns the prefix
+-- of @xs@ of length @n@ as a new ShortByteString, or @xs@ itself if
+-- @n > 'length' xs@
+--
+{-# INLINEABLE take #-}
+take :: Int -> ShortByteString -> ShortByteString
+take n xs
+  | n >= BS.length xs = xs
+  | otherwise         = runST $ do
+      mba <- newByteArray n
+      copyByteArray (asBA xs) 0 mba 0 n
+      ba  <- unsafeFreezeByteArray mba
+      return (asSBS ba)
+
+-- | 'takeWhile', applied to a predicate @p@ and a ShortByteString @xs@, returns
+-- the longest prefix (possibly empty) of @xs@ of elements that satisfy @p@.
+--
+{-# INLINEABLE takeWhile #-}
+takeWhile :: (Word8 -> Bool) -> ShortByteString -> ShortByteString
+takeWhile f ps = take (findIndexOrEnd (not . f) ps) ps
+
+
+-- | Return the index of the first element satisfying the predicate, otherwise
+-- return the length of the string if no such element is found.
+--
+{-# INLINEABLE findIndexOrEnd #-}
+findIndexOrEnd :: (Word8 -> Bool) -> ShortByteString -> Int
+findIndexOrEnd p xs = go 0
+  where
+    !ba = asBA xs
+    !n  = BS.length xs
+    --
+    go !i | i >= n                   = i
+          | p (indexWord8Array ba i) = i
+          | otherwise                = go (i+1)
+
+
+------------------------------------------------------------------------
+-- Internal utils
+
+asBA :: ShortByteString -> BA
+asBA (BI.SBS ba#) = BA# ba#
+
+asSBS :: BA -> ShortByteString
+asSBS (BA# ba#) = BI.SBS ba#
+
+
+------------------------------------------------------------------------
+-- Primop wrappers
+
+data BA    = BA# ByteArray#
+data MBA s = MBA# (MutableByteArray# s)
+
+indexWord8Array :: BA -> Int -> Word8
+indexWord8Array (BA# ba#) (I# i#) = W8# (indexWord8Array# ba# i#)
+
+newByteArray :: Int -> ST s (MBA s)
+newByteArray (I# len#) =
+    ST $ \s -> case newByteArray# len# s of
+                 (# s', mba# #) -> (# s', MBA# mba# #)
+
+unsafeFreezeByteArray :: MBA s -> ST s BA
+unsafeFreezeByteArray (MBA# mba#) =
+    ST $ \s -> case unsafeFreezeByteArray# mba# s of
+                 (# s', ba# #) -> (# s', BA# ba# #)
+
+copyByteArray :: BA -> Int -> MBA s -> Int -> Int -> ST s ()
+copyByteArray (BA# src#) (I# src_off#) (MBA# dst#) (I# dst_off#) (I# len#) =
+    ST $ \s -> case copyByteArray# src# src_off# dst# dst_off# len# s of
+                 s' -> (# s', () #)
+
diff --git a/Data/IORef/Storable.hs b/Data/IORef/Storable.hs
new file mode 100644
--- /dev/null
+++ b/Data/IORef/Storable.hs
@@ -0,0 +1,59 @@
+{-# OPTIONS_HADDOCK hide #-}
+-- |
+-- Module      : Data.IORef.Storable
+-- Copyright   : [2017] Trevor L. McDonell
+-- License     : BSD3
+--
+-- Maintainer  : Trevor L. McDonell <tmcdonell@cse.unsw.edu.au>
+-- Stability   : experimental
+-- Portability : non-portable (GHC extensions)
+--
+-- A mutable reference in the 'IO' monad.
+--
+
+module Data.IORef.Storable (
+
+  IORef,
+
+  newIORef,
+  readIORef,
+  writeIORef,
+  modifyIORef,
+
+) where
+
+import Foreign.ForeignPtr
+import Foreign.Storable
+import GHC.ForeignPtr
+
+
+-- | A pointer to a mutable object. Unlike 'Data.IORef.IORef', this is just
+-- a pointer to the value, rather than a reference containing a boxed object.
+-- This means that the value is stored strictly, and is only one pointer away.
+--
+data IORef a = IORef {-# UNPACK #-} !(ForeignPtr a)
+
+
+-- | Build a new 'IORef'
+--
+newIORef :: Storable a => a -> IO (IORef a)
+newIORef v = do
+  fp <- mallocPlainForeignPtr
+  withForeignPtr fp $ \p -> poke p v
+  return (IORef fp)
+
+-- | Read the value of the 'IORef'
+--
+readIORef :: Storable a => IORef a -> IO a
+readIORef (IORef fp) = withForeignPtr fp peek
+
+-- | Write a new value into the 'IORef'
+--
+writeIORef :: Storable a => IORef a -> a -> IO ()
+writeIORef (IORef fp) v = withForeignPtr fp $ \p -> poke p v
+
+-- | Mutate the contents of an 'IORef'
+--
+modifyIORef :: Storable a => IORef a -> (a -> a) -> IO ()
+modifyIORef ref f = readIORef ref >>= writeIORef ref . f
+
diff --git a/LLVM/AST/Type/Metadata.hs b/LLVM/AST/Type/Metadata.hs
--- a/LLVM/AST/Type/Metadata.hs
+++ b/LLVM/AST/Type/Metadata.hs
@@ -13,20 +13,22 @@
 module LLVM.AST.Type.Metadata
   where
 
-import LLVM.AST.Type.Operand
+import qualified LLVM.AST.Constant                        as LLVM
 import qualified LLVM.AST.Operand                         as LLVM
 
+import Data.ByteString.Short                              ( ShortByteString )
 
--- | <http://llvm.org/docs/LangRef.html#metadata>
+
+-- | Metadata does not have a type, and is not a value.
 --
--- Metadata does not have a type, and is not a value.
+-- <http://llvm.org/docs/LangRef.html#metadata>
 --
 data MetadataNode
-  = MetadataNode [Maybe Metadata]
-  | MetadataNodeReference LLVM.MetadataNodeID
+  = MetadataNode ![Maybe Metadata]
+  | MetadataNodeReference {-# UNPACK #-} !LLVM.MetadataNodeID
 
-data Metadata where
-  MetadataStringOperand :: String -> Metadata
-  MetadataOperand       :: Operand a -> Metadata
-  MetadataNodeOperand   :: MetadataNode -> Metadata
+data Metadata
+  = MetadataStringOperand {-# UNPACK #-} !ShortByteString
+  | MetadataConstantOperand !LLVM.Constant
+  | MetadataNodeOperand !MetadataNode
 
diff --git a/LLVM/AST/Type/Name.hs b/LLVM/AST/Type/Name.hs
--- a/LLVM/AST/Type/Name.hs
+++ b/LLVM/AST/Type/Name.hs
@@ -14,9 +14,10 @@
 module LLVM.AST.Type.Name
   where
 
+import Data.ByteString.Short                                        ( ShortByteString )
 import Data.Data
-import Data.Word
 import Data.String
+import Data.Word
 import Prelude
 
 
@@ -46,12 +47,12 @@
 --
 type role Name representational
 data Name a
-  = Name String         -- ^ a string name
-  | UnName Word         -- ^ a number for a nameless thing
+  = Name   {-# UNPACK #-} !ShortByteString    -- ^ a string name
+  | UnName {-# UNPACK #-} !Word               -- ^ a number for a nameless thing
   deriving (Eq, Ord, Read, Show, Typeable, Data)
 
 instance IsString (Name a) where
-  fromString = Name
+  fromString = Name . fromString
 
 
 -- TLM: 'Name' is used a lot over the place, to refer to things like variables
@@ -64,9 +65,13 @@
 --      unsatisfactory... )
 --
 
-data Label = Label String
+data Label = Label {-# UNPACK #-} !ShortByteString
   deriving (Eq, Ord, Read, Show, Typeable, Data)
 
 instance IsString Label where
-  fromString = Label
+  fromString = Label . fromString
+
+instance Monoid Label where
+  mempty                      = Label mempty
+  mappend (Label x) (Label y) = Label (mappend x y)
 
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,188 @@
+An LLVM backend for the Accelerate Array Language
+=================================================
+
+[![Build Status](https://travis-ci.org/AccelerateHS/accelerate-llvm.svg)](https://travis-ci.org/AccelerateHS/accelerate-llvm)
+[![Hackage](https://img.shields.io/hackage/v/accelerate-llvm.svg)](https://hackage.haskell.org/package/accelerate-llvm)
+[![Docker Automated build](https://img.shields.io/docker/automated/tmcdonell/accelerate-llvm.svg)](https://hub.docker.com/r/tmcdonell/accelerate-llvm/)
+[![Docker status](https://images.microbadger.com/badges/image/tmcdonell/accelerate-llvm.svg)](https://microbadger.com/images/tmcdonell/accelerate-llvm)
+
+This package compiles Accelerate code to LLVM IR, and executes that code on
+multicore CPUs as well as NVIDIA GPUs. This avoids the need to go through `nvcc`
+or `clang`. For details on Accelerate, refer to the [main repository][GitHub].
+
+We love all kinds of contributions, so feel free to open issues for missing
+features as well as report (or fix!) bugs on the [issue tracker][Issues].
+
+  [GitHub]:  https://github.com/AccelerateHS/accelerate
+  [Issues]:  https://github.com/AccelerateHS/accelerate/issues
+
+
+ * [Dependencies](#dependencies)
+ * [Docker](#docker)
+ * [Installing LLVM](#installing-llvm)
+   * [Homebrew](#homebrew)
+   * [Debian/Ubuntu](#debianubuntu)
+   * [Building from source](#building-from-source)
+ * [Installing Accelerate-LLVM](#installing-accelerate-llvm)
+   * [libNVVM](#libNVVM)
+
+
+Dependencies
+------------
+
+Haskell dependencies are available from Hackage, but there are several external
+library dependencies that you will need to install as well:
+
+ * [`LLVM`](http://llvm.org)
+ * [`libFFI`](http://sourceware.org/libffi/) (if using the `accelerate-llvm-native` backend for multicore CPUs)
+ * [`CUDA`](https://developer.nvidia.com/cuda-downloads) (if using the `accelerate-llvm-ptx` backend for NVIDIA GPUs)
+
+
+Docker
+------
+
+A [docker](https://www.docker.com) container is provided with this package
+preinstalled (via stack) at `/opt/accelerate-llvm`. Note that if you wish to use
+the `accelerate-llvm-ptx` GPU backend, you will need to install the [NVIDIA
+docker](https://github.com/NVIDIA/nvidia-docker) plugin; see that page for more
+information.
+
+```sh
+$ docker run -it tmcdonell/accelerate-llvm
+```
+
+
+Installing LLVM
+---------------
+
+When installing LLVM, make sure that it includes the `libLLVM` shared library.
+If you want to use the GPU targeting `accelerate-llvm-ptx` backend, make sure
+you install (or build) LLVM with the 'nvptx' target.
+
+## Homebrew
+
+Example using [Homebrew](http://brew.sh) on macOS:
+
+```sh
+$ brew install llvm-hs/homebrew-llvm/llvm-4.0
+```
+
+## Debian/Ubuntu
+
+For Debian/Ubuntu based Linux distributions, the LLVM.org website provides
+binary distribution packages. Check [apt.llvm.org](http://apt.llvm.org) for
+instructions for adding the correct package database for your OS version, and
+then:
+
+```sh
+$ apt-get install llvm-4.0-dev
+```
+
+## Building from source
+
+If your OS does not have an appropriate LLVM distribution available, you can also build from source. Detailed build instructions are available on the [LLVM.org website](http://releases.llvm.org/4.0.0/docs/CMake.html). Note that you will require at least [CMake 3.4.3](http://www.cmake.org/cmake/resources/software.html) and a recent C++ compiler; at least Clang 3.1, GCC 4.8, or Visual Studio 2015 (update 3).
+
+  1. Download and unpack the [LLVM-4.0 source code](http://releases.llvm.org/4.0.0/llvm-4.0.0.src.tar.xz). We'll refer to
+     the path that the source tree was unpacked to as `LLVM_SRC`. Only the main
+     LLVM source tree is required, but you can optionally add other components
+     such as the Clang compiler or Polly loop optimiser. See the [LLVM releases](http://releases.llvm.org/download.html#4.0.0)
+     page for the complete list.
+
+  2. Create a temporary build directory and `cd` into it, for example:
+     ```sh
+     $ mkdir /tmp/build
+     $ cd /tmp/build
+     ```
+
+  3. Execute the following to configure the build. Here `INSTALL_PREFIX` is
+     where LLVM is to be installed, for example `/usr/local` or
+     `$HOME/opt/llvm`:
+     ```sh
+     $ cmake $LLVM_SRC -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_BUILD_LLVM_DYLIB=ON -DLLVM_LINK_LLVM_DYLIB=ON
+     ```
+     See [options and variables](http://llvm.org/docs/CMake.html#options-and-variables)
+     for a list of additional build parameters you can specify.
+
+  4. Build and install:
+     ```sh
+     $ cmake --build .
+     $ cmake --build . --target install
+     ```
+
+  5. For macOS only, some additional steps are useful to work around issues related
+     to [System Integrity Protection](https://en.wikipedia.org/wiki/System_Integrity_Protection):
+     ```sh
+     cd $INSTALL_PREFIX/lib
+     ln -s libLLVM.dylib libLLVM-4.0.dylib
+     install_name_tool -id $PWD/libLTO.dylib libLTO.dylib
+     install_name_tool -id $PWD/libLLVM.dylib libLLVM.dylib
+     install_name_tool -change '@rpath/libLLVM.dylib' $PWD/libLLVM.dylib libLTO.dylib
+     ```
+
+
+Installing Accelerate-LLVM
+--------------------------
+
+Once the dependencies are installed, we are ready to install `accelerate-llvm`.
+
+For example, installation using [`stack`](http://docs.haskellstack.org/en/stable/README.html)
+just requires you to point it to the appropriate configuration file:
+```sh
+$ ln -s stack-8.0.yaml stack.yaml
+$ stack setup
+$ stack install
+```
+
+Note that the version of [`llvm-hs`](https://hackage.haskell.org/package/llvm-hs)
+used must match the installed version of LLVM, which is currently 4.0.
+
+
+## libNVVM
+
+The `accelerate-llvm-ptx` backend can optionally be compiled to generate GPU
+code using the `libNVVM` library, rather than LLVM's inbuilt NVPTX code
+generator. `libNVVM` is a closed-source library distributed as part of the
+NVIDIA CUDA toolkit, and is what the `nvcc` compiler itself uses internally when
+compiling CUDA C code.
+
+Using `libNVVM` _may_ improve GPU performance compared to the code generator
+built in to LLVM. One difficulty with using it however is that since `libNVVM`
+is also based on LLVM, and typically lags LLVM by several releases, you must
+install `accelerate-llvm` with a "compatible" version of LLVM, which will depend
+on the version of the CUDA toolkit you have installed. The following table shows
+some combinations:
+
+|              | LLVM-3.3 | LLVM-3.4 | LLVM-3.5 | LLVM-3.8 | LLVM-3.9 | LLVM-4.0 |
+|:------------:|:--------:|:--------:|:--------:|:--------:|:--------:|:--------:|
+| **CUDA-7.0** |     ⭕    |     ❌    |          |          |          |          |
+| **CUDA-7.5** |          |     ⭕    |     ⭕    |     ❌    |          |          |
+| **CUDA-8.0** |          |          |     ⭕    |     ⭕    |     ❌    |     ❌    |
+
+Where ⭕ = Works, and ❌ = Does not work.
+
+Note that the above restrictions on CUDA and LLVM version exist _only_ if you
+want to use the NVVM component. Otherwise, you should be free to use any
+combination of CUDA and LLVM.
+
+Also note that `accelerate-llvm-ptx` itself currently requires at least LLVM-3.5.
+
+Using `stack`, either edit the `stack.yaml` and add the following section:
+
+```yaml
+flags:
+  accelerate-llvm-ptx:
+    nvvm: true
+```
+
+Or install using the following option on the command line:
+
+```sh
+$ stack install accelerate-llvm-ptx --flag accelerate-llvm-ptx:nvvm
+```
+
+If installing via `cabal`:
+
+```sh
+$ cabal install accelerate-llvm-ptx -fnvvm
+```
+
diff --git a/accelerate-llvm.cabal b/accelerate-llvm.cabal
--- a/accelerate-llvm.cabal
+++ b/accelerate-llvm.cabal
@@ -1,14 +1,72 @@
 name:                   accelerate-llvm
-version:                1.0.0.0
+version:                1.1.0.0
 cabal-version:          >= 1.10
-tested-with:            GHC == 7.8.*
+tested-with:            GHC >= 7.10
 build-type:             Simple
 
-synopsis:               Accelerate backend generating LLVM
+synopsis:               Accelerate backend component generating LLVM IR
 description:
     This library implements direct LLVM IR generation for the /Accelerate/
-    language. For further information, refer to the main /Accelerate/ package:
-    <http://hackage.haskell.org/package/accelerate>
+    language. For further information, refer to the main
+    <http://hackage.haskell.org/package/accelerate accelerate> package.
+    .
+    [/Dependencies/]
+    .
+    Haskell dependencies are available from Hackage. The following external
+    libraries are alse required:
+    .
+      * <http://llvm.org LLVM>
+    .
+      * <http://sourceware.org/libffi/ libFFI> (if using <http://hackage.haskell.org/package/accelerate-llvm-native accelerate-llvm-native>)
+    .
+      * <https://developer.nvidia.com/cuda-downloads CUDA> (if using <http://hackage.haskell.org/package/accelerate-llvm-ptx accelerate-llvm-ptx>)
+    .
+    [/Installing LLVM/]
+    .
+    /Homebrew/
+    .
+    Example using Homebrew on macOS:
+    .
+    > brew install llvm-hs/homebrew-llvm/llvm-5.0
+    .
+    /Debian & Ubuntu/
+    .
+    For Debian/Ubuntu based Linux distributions, the LLVM.org website provides
+    binary distribution packages. Check <http://apt.llvm.org apt.llvm.org> for
+    instructions for adding the correct package database for your OS version,
+    and then:
+    .
+    > apt-get install llvm-5.0-dev
+    .
+    /Building from source/
+    .
+    If your OS does not have an appropriate LLVM distribution available, you can
+    also build from source. Detailed build instructions are available on
+    <http://releases.llvm.org/5.0.0/docs/CMake.html LLVM.org>. Make sure to
+    include the cmake build options
+    @-DLLVM_BUILD_LLVM_DYLIB=ON -DLLVM_LINK_LLVM_DYLIB=ON@ so that the @libLLVM@
+    shared library will be built.
+    .
+    If using the @accelerate-llvm-ptx@ backend, also ensure that the
+    @LLVM_TARGETS_TO_BUILD@ option includes the @NVPTX@ target (if not
+    specified, all targets are built).
+    .
+    [/Installing accelerate-llvm/]
+    .
+    To use @accelerate-llvm@ it is important that the @llvm-hs@ package is
+    installed against the @libLLVM@ shared library, rather than statically
+    linked, so that we can use LLVM from GHCi and Template Haskell. This is the
+    default configuration, but you can also enforce this explicitly by adding
+    the following to your @stack.yaml@ file:
+    .
+    > flags:
+    >   llvm-hs:
+    >     shared-llvm: true
+    .
+    Or by specifying the @shared-llvm@ flag to cabal:
+    .
+    > cabal install llvm-hs -fshared-llvm
+    .
 
 license:                BSD3
 license-file:           LICENSE
@@ -17,12 +75,16 @@
 bug-reports:            https://github.com/AccelerateHS/accelerate/issues
 category:               Compilers/Interpreters, Concurrency, Data, Parallelism
 
+extra-source-files:
+    CHANGELOG.md
+    README.md
 
+
 -- Configuration flags
 -- -------------------
 
 Flag debug
-  Default:              True
+  Default:              False
   Description:
     Enable debug tracing message flags. Note that 'debug' must be enabled in the
     base 'accelerate' package as well. See the 'accelerate' package for usage
@@ -33,11 +95,11 @@
   Description:          Enable bounds checking
 
 Flag unsafe-checks
-  Default:              True
+  Default:              False
   Description:          Enable bounds checking in unsafe operations
 
 Flag internal-checks
-  Default:              True
+  Default:              False
   Description:          Enable internal consistency checks
 
 Flag chase-lev
@@ -51,6 +113,7 @@
 Library
   exposed-modules:
     -- Accelerate-LLVM middle-end
+    Data.Array.Accelerate.LLVM.AST
     Data.Array.Accelerate.LLVM.Analysis.Match
     Data.Array.Accelerate.LLVM.Array.Data
     Data.Array.Accelerate.LLVM.CodeGen
@@ -73,11 +136,15 @@
     Data.Array.Accelerate.LLVM.CodeGen.Sugar
     Data.Array.Accelerate.LLVM.CodeGen.Type
     Data.Array.Accelerate.LLVM.Compile
+    Data.Array.Accelerate.LLVM.Compile.Cache
+    Data.Array.Accelerate.LLVM.Embed
     Data.Array.Accelerate.LLVM.Execute
     Data.Array.Accelerate.LLVM.Execute.Async
     Data.Array.Accelerate.LLVM.Execute.Environment
     Data.Array.Accelerate.LLVM.Execute.Marshal
     Data.Array.Accelerate.LLVM.Foreign
+    Data.Array.Accelerate.LLVM.Link
+    Data.Array.Accelerate.LLVM.Link.Cache
     Data.Array.Accelerate.LLVM.State
     Data.Array.Accelerate.LLVM.Target
     Data.Array.Accelerate.LLVM.Util
@@ -105,21 +172,34 @@
     Control.Parallel.Meta.Resource.Single
     Control.Parallel.Meta.Resource.SMP
     Control.Parallel.Meta.Trans.LBS
+    Data.IORef.Storable
     Data.Range.Range
 
+    -- Extras
+    Data.ByteString.Short.Char8
+    Data.ByteString.Short.Extra
+
+  other-modules:
+    Paths_accelerate_llvm
+
   build-depends:
-          base                          >= 4.7 && < 4.10
+          base                          >= 4.7 && < 4.11
         , abstract-deque                >= 0.3
-        , accelerate                    == 1.0.*
-        , containers                    >= 0.5 && < 0.6
+        , accelerate                    == 1.1.*
+        , bytestring                    >= 0.10.4
+        , containers                    >= 0.5
         , data-default-class            >= 0.0.1
+        , deepseq                       >= 1.3
+        , directory                     >= 1.0
         , dlist                         >= 0.6
         , exceptions                    >= 0.6
         , fclabels                      >= 2.0
-        , llvm-hs                       >= 3.9
-        , llvm-hs-pure                  >= 3.9
+        , filepath                      >= 1.0
+        , llvm-hs                       >= 4.1 && < 5.1
+        , llvm-hs-pure                  >= 4.1 && < 5.1
         , mtl                           >= 2.0
         , mwc-random                    >= 0.13
+        , template-haskell
         , unordered-containers          >= 0.2
         , vector                        >= 0.10
 
@@ -154,7 +234,7 @@
 
 source-repository this
   type:                 git
-  tag:                  1.0.0.0
+  tag:                  1.1.0.0
   location:             https://github.com/AccelerateHS/accelerate-llvm.git
 
 -- vim: nospell
