llvm-codegen-0.1.0.0: tests/Test/LLVM/Codegen/IRCombinatorsSpec.hs
{-# LANGUAGE QuasiQuotes, RecursiveDo, OverloadedLists #-}
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}
module Test.LLVM.Codegen.IRCombinatorsSpec
( module Test.LLVM.Codegen.IRCombinatorsSpec
) where
import Test.Hspec
import Data.Foldable (for_)
import LLVM.Codegen
import Data.Text (Text)
import NeatInterpolation
checkIR :: ModuleBuilder a -> Text -> IO ()
checkIR llvmModule expectedOutput = do
let ir = ppllvm $ runModuleBuilder llvmModule
ir `shouldBe` expectedOutput
spec :: Spec
spec = describe "IR builder combinators" $ parallel $ do
it "supports comparisons combinators" $ do
let scenarios :: [(Operand -> Operand -> IRBuilderT ModuleBuilder Operand, Text)]
scenarios = [ (eq, "eq"), (ne, "ne")
, (sge, "sge"), (sgt, "sgt"), (slt, "slt"), (sle, "sle")
, (uge, "uge"), (ugt, "ugt"), (ult, "ult"), (ule, "ule")
]
for_ scenarios $ \(f, op) -> do
let ir = do
function "func" [(i32, "a"), (i32, "b")] i1 $ \[a, b] -> do
c <- f a b
ret c
checkIR ir [text|
define external ccc i1 @func(i32 %a_0, i32 %b_0) {
start:
%0 = icmp $op i32 %a_0, %b_0
ret i1 %0
}
|]
it "supports 'one-sided if' combinator" $ do
let ir = do
function "func" [(i32, "a"), (i32, "b")] i32 $ \[a, b] -> mdo
isZero <- eq a (int32 0)
if' isZero $ do
_ <- add a b
ret $ int32 1000
ret b
checkIR ir [text|
define external ccc i32 @func(i32 %a_0, i32 %b_0) {
start:
%0 = icmp eq i32 %a_0, 0
br i1 %0, label %if_0, label %end_if_0
if_0:
%1 = add i32 %a_0, %b_0
ret i32 1000
end_if_0:
ret i32 %b_0
}
|]
it "supports 'loop' combinator" $ do
let ir = do
function "func" [] i32 $ \_ -> mdo
i <- allocate i32 (int32 0)
loop $ do
iValue <- load i 0
isEqual <- iValue `eq` int32 10
if' isEqual $ do
br end
end <- blockNamed "end"
ret $ int32 42
checkIR ir [text|
define external ccc i32 @func() {
start:
%stack.ptr_0 = alloca i32
store i32 0, ptr %stack.ptr_0
br label %loop_0
loop_0:
%0 = load i32, ptr %stack.ptr_0
%1 = icmp eq i32 %0, 10
br i1 %1, label %if_0, label %end_if_0
if_0:
br label %end_0
end_if_0:
br label %loop_0
end_0:
ret i32 42
}
|]
it "supports 'loopWhile' combinator" $ do
let ir = do
function "func" [] i32 $ \_ -> mdo
i <- allocate i32 (int32 10)
let notZero = do
iVal <- load i 0
iVal `ne` int32 0
loopWhile notZero $ do
iVal <- load i 0
iVal' <- sub iVal (int32 1)
store i 0 iVal'
ret $ int32 42
checkIR ir [text|
define external ccc i32 @func() {
start:
%stack.ptr_0 = alloca i32
store i32 10, ptr %stack.ptr_0
br label %while_begin_0
while_begin_0:
%0 = load i32, ptr %stack.ptr_0
%1 = icmp ne i32 %0, 0
br i1 %1, label %while_body_0, label %while_end_0
while_body_0:
%2 = load i32, ptr %stack.ptr_0
%3 = sub i32 %2, 1
store i32 %3, ptr %stack.ptr_0
br label %while_begin_0
while_end_0:
ret i32 42
}
|]
it "supports 'loopFor' combinator" $ do
let ir = do
function "func" [] i32 $ \_ -> mdo
x <- allocate i32 (int32 10)
loopFor (int32 0) (`ult` int32 10) (add (int32 1)) $ \i -> do
xVal <- load x 0
xVal' <- add i xVal
store x 0 xVal'
ret $ int32 42
checkIR ir [text|
define external ccc i32 @func() {
start:
%stack.ptr_0 = alloca i32
store i32 10, ptr %stack.ptr_0
br label %for_begin_0
for_begin_0:
%0 = phi i32 [0, %start], [%4, %for_body_0]
%1 = icmp ult i32 %0, 10
br i1 %1, label %for_body_0, label %for_end_0
for_body_0:
%2 = load i32, ptr %stack.ptr_0
%3 = add i32 %0, %2
store i32 %3, ptr %stack.ptr_0
%4 = add i32 1, %0
br label %for_begin_0
for_end_0:
ret i32 42
}
|]
it "supports 'pointer subtraction' combinator" $ do
let ir = do
function "func" [] i32 $ \_ -> mdo
array <- alloca i32 (Just $ int32 5) 0
ptr1 <- gep array [int32 0]
ptr2 <- gep array [int32 3]
_ <- pointerDiff i32 ptr1 ptr2
ret $ int32 42
checkIR ir [text|
define external ccc i32 @func() {
start:
%stack.ptr_0 = alloca i32, i32 5
%0 = getelementptr i32, ptr %stack.ptr_0, i32 0
%1 = getelementptr i32, ptr %stack.ptr_0, i32 3
%2 = ptrtoint ptr %0 to i64
%3 = ptrtoint ptr %1 to i64
%4 = sub i64 %2, %3
%5 = trunc i64 %4 to i32
ret i32 42
}
|]
it "supports logical not" $ do
let ir = do
function "func" [] i32 $ \_ -> mdo
_ <- not' $ bit 0
ret $ int32 42
checkIR ir [text|
define external ccc i32 @func() {
start:
%0 = select i1 0, i1 0, i1 1
ret i32 42
}
|]
it "supports computing the minimum of 2 values" $ do
let ir = do
function "func" [] i32 $ \_ -> mdo
_result1 <- minimum' Signed (int32 100) (int32 42)
_result2 <- minimum' Unsigned (int32 100) (int32 42)
ret $ int32 42
checkIR ir [text|
define external ccc i32 @func() {
start:
%0 = icmp slt i32 100, 42
%1 = select i1 %0, i32 100, i32 42
%2 = icmp ult i32 100, 42
%3 = select i1 %2, i32 100, i32 42
ret i32 42
}
|]
it "supports allocating and initializing a variable on the stack" $ do
let ir = do
function "func" [] i32 $ \_ -> mdo
_i <- allocate i32 (int32 0)
ret $ int32 42
checkIR ir [text|
define external ccc i32 @func() {
start:
%stack.ptr_0 = alloca i32
store i32 0, ptr %stack.ptr_0
ret i32 42
}
|]
it "supports composing Paths" $ do
let path = mkPath [int32 1, int32 2] ->> mkPath [int32 3]
path `shouldBe` Path [int32 0, int32 1, int32 2, int32 3]
it "supports computing the address based on a Path" $ do
let path = Path [int32 5]
ir = do
function "func" [] i32 $ \_ -> mdo
array <- alloca i32 (Just $ int32 5) 0
_address <- addr path array
ret $ int32 42
checkIR ir [text|
define external ccc i32 @func() {
start:
%stack.ptr_0 = alloca i32, i32 5
%0 = getelementptr i32, ptr %stack.ptr_0, i32 5
ret i32 42
}
|]
it "supports dereferencing an address based on a Path" $ do
let path = Path [int32 5]
ir = mdo
function "func" [] i32 $ \_ -> mdo
array <- alloca i32 (Just $ int32 5) 0
_value <- deref path array
ret $ int32 42
checkIR ir [text|
define external ccc i32 @func() {
start:
%stack.ptr_0 = alloca i32, i32 5
%0 = getelementptr i32, ptr %stack.ptr_0, i32 5
%1 = load i32, ptr %0
ret i32 42
}
|]
it "supports storing a value at an address based on a Path" $ do
let path = Path [int32 5]
ir = mdo
function "func" [] i32 $ \_ -> mdo
array <- alloca i32 (Just $ int32 5) 0
assign path array (int32 1000)
ret $ int32 42
checkIR ir [text|
define external ccc i32 @func() {
start:
%stack.ptr_0 = alloca i32, i32 5
%0 = getelementptr i32, ptr %stack.ptr_0, i32 5
store i32 1000, ptr %0
ret i32 42
}
|]
it "supports updating a value at an address based on a Path" $ do
let path = Path [int32 5]
ir = mdo
function "func" [] i32 $ \_ -> mdo
array <- alloca i32 (Just $ int32 5) 0
assign path array (int32 1000)
update path array (add (int32 10))
ret $ int32 42
checkIR ir [text|
define external ccc i32 @func() {
start:
%stack.ptr_0 = alloca i32, i32 5
%0 = getelementptr i32, ptr %stack.ptr_0, i32 5
store i32 1000, ptr %0
%1 = getelementptr i32, ptr %stack.ptr_0, i32 5
%2 = load i32, ptr %1
%3 = add i32 10, %2
store i32 %3, ptr %1
ret i32 42
}
|]
it "supports incrementing a value at an address based on a Path" $ do
let path = Path [int32 5]
ir = mdo
function "func" [] i32 $ \_ -> mdo
array <- alloca i32 (Just $ int32 5) 0
assign path array (int32 1000)
increment int32 path array
ret $ int32 42
checkIR ir [text|
define external ccc i32 @func() {
start:
%stack.ptr_0 = alloca i32, i32 5
%0 = getelementptr i32, ptr %stack.ptr_0, i32 5
store i32 1000, ptr %0
%1 = getelementptr i32, ptr %stack.ptr_0, i32 5
%2 = load i32, ptr %1
%3 = add i32 1, %2
store i32 %3, ptr %1
ret i32 42
}
|]
it "supports copying (part of) a type based on a Path" $ do
let path = Path [int32 5]
ir = mdo
function "func" [] i32 $ \_ -> mdo
array <- alloca i32 (Just $ int32 5) 0
assign path array (int32 1000)
array2 <- alloca i32 (Just $ int32 5) 0
copy path array array2
ret $ int32 42
checkIR ir [text|
define external ccc i32 @func() {
start:
%stack.ptr_0 = alloca i32, i32 5
%stack.ptr_1 = alloca i32, i32 5
%0 = getelementptr i32, ptr %stack.ptr_0, i32 5
store i32 1000, ptr %0
%1 = getelementptr i32, ptr %stack.ptr_0, i32 5
%2 = load i32, ptr %1
%3 = getelementptr i32, ptr %stack.ptr_1, i32 5
store i32 %2, ptr %3
ret i32 42
}
|]
it "supports swapping (part of) a type based on a Path" $ do
let path = Path [int32 5]
ir = mdo
function "func" [] i32 $ \_ -> mdo
array <- alloca i32 (Just $ int32 5) 0
assign path array (int32 1000)
array2 <- alloca i32 (Just $ int32 5) 0
swap path array array2
ret $ int32 42
checkIR ir [text|
define external ccc i32 @func() {
start:
%stack.ptr_0 = alloca i32, i32 5
%stack.ptr_1 = alloca i32, i32 5
%0 = getelementptr i32, ptr %stack.ptr_0, i32 5
store i32 1000, ptr %0
%1 = getelementptr i32, ptr %stack.ptr_0, i32 5
%2 = load i32, ptr %1
%3 = getelementptr i32, ptr %stack.ptr_1, i32 5
%4 = load i32, ptr %3
%5 = getelementptr i32, ptr %stack.ptr_0, i32 5
store i32 %4, ptr %5
%6 = getelementptr i32, ptr %stack.ptr_1, i32 5
store i32 %2, ptr %6
ret i32 42
}
|]