packages feed

hhlo-0.11.0.0: test/Test/Runtime/EndToEnd.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}

module Test.Runtime.EndToEnd where

import qualified Data.Text as T
import qualified Data.Vector.Storable as V
import Foreign.C
import Foreign.Ptr
import Foreign.Marshal.Alloc (alloca)
import Foreign.Storable (peek)
import Test.Tasty
import Test.Tasty.HUnit

import HHLO.Core.Types
import HHLO.EDSL.Ops
import HHLO.IR.AST (FuncArg(..), TensorType(..))
import HHLO.IR.Builder
import HHLO.IR.Pretty
import HHLO.Runtime.PJRT.FFI
import HHLO.Runtime.PJRT.Types
import HHLO.Runtime.PJRT.Error
import HHLO.Runtime.Compile
import HHLO.Runtime.Execute
import HHLO.Runtime.Buffer

tests :: TestTree
tests = testGroup "EndToEnd"
    [ testCase "add two tensors on CPU" $ do
        -- 1. Load plugin
        api <- withCString "deps/pjrt/libpjrt_cpu.so" $ \path -> do
            alloca $ \apiPtrPtr -> do
                checkError nullPtr $ c_pjrtLoadPlugin path apiPtrPtr
                PJRTApi <$> peek apiPtrPtr

        -- 2. Create client
        client <- alloca $ \clientPtrPtr -> do
            checkError (unApi api) $ c_pjrtCreateClient (unApi api) clientPtrPtr
            PJRTClient <$> peek clientPtrPtr

        -- 3. Build and compile a program using the EDSL
        let modu = moduleFromBuilder @'[2,2] @'F32 "main"
                [ FuncArg "arg0" (TensorType [Just 2, Just 2] F32)
                , FuncArg "arg1" (TensorType [Just 2, Just 2] F32)
                ]
                $ do
                    x <- arg
                    y <- arg
                    z <- add x y
                    return z
        let mlir = render modu
        exec <- compile api client mlir

        -- 4. Create input buffers
        let inputX = V.fromList [1.0, 2.0, 3.0, 4.0] :: V.Vector Float
            inputY = V.fromList [5.0, 6.0, 7.0, 8.0] :: V.Vector Float
        bufX <- toDeviceF32 api client inputX [2, 2]
        bufY <- toDeviceF32 api client inputY [2, 2]

        -- 5. Execute
        [bufZ] <- execute api exec [bufX, bufY]

        -- 6. Read back result
        result <- fromDeviceF32 api bufZ 4

        -- 7. Verify
        result @?= V.fromList [6.0, 8.0, 10.0, 12.0]

        -- 8. Cleanup (client only)
        checkError (unApi api) $ c_pjrtClientDestroy (unApi api) (unClient client)
    ]

unApi :: PJRTApi -> Ptr PJRTApi
unApi (PJRTApi p) = p

unClient :: PJRTClient -> Ptr PJRTClient
unClient (PJRTClient p) = p