{-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RebindableSyntax #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE ViewPatterns #-} {-# OPTIONS_HADDOCK hide #-} -- | -- Module : Data.Array.Accelerate.LLVM.CodeGen.Arithmetic -- Copyright : [2015..2020] The Accelerate Team -- License : BSD3 -- -- Maintainer : Trevor L. McDonell -- Stability : experimental -- Portability : non-portable (GHC extensions) -- module Data.Array.Accelerate.LLVM.CodeGen.Arithmetic where import Data.Array.Accelerate.AST ( PrimMaybe ) import Data.Array.Accelerate.Analysis.Match import Data.Array.Accelerate.Representation.Tag import Data.Array.Accelerate.Representation.Type import LLVM.AST.Type.Constant import LLVM.AST.Type.Function import LLVM.AST.Type.Instruction import LLVM.AST.Type.Instruction.Compare import LLVM.AST.Type.Name import LLVM.AST.Type.Operand import LLVM.AST.Type.Representation import Data.Array.Accelerate.LLVM.CodeGen.Base import Data.Array.Accelerate.LLVM.CodeGen.Constant import Data.Array.Accelerate.LLVM.CodeGen.IR import Data.Array.Accelerate.LLVM.CodeGen.Monad import Data.Array.Accelerate.LLVM.CodeGen.Type import Control.Applicative import Control.Monad import Data.Bits ( finiteBitSize ) import Data.Bool ( Bool(..), otherwise ) import Data.ByteString.Short ( ShortByteString ) import Data.Constraint ( Dict(..) ) import Data.Monoid import Data.String import Foreign.Storable ( sizeOf ) import Prelude ( Eq, Num, Maybe(..), ($), (==), (/), undefined, flip, fromInteger ) import Text.Printf import qualified Data.Ord as Ord import qualified Prelude as P -- Operations from Num -- ------------------- add :: NumType a -> Operands a -> Operands a -> CodeGen arch (Operands a) add = binop Add sub :: NumType a -> Operands a -> Operands a -> CodeGen arch (Operands a) sub = binop Sub mul :: NumType a -> Operands a -> Operands a -> CodeGen arch (Operands a) mul = binop Mul negate :: NumType a -> Operands a -> CodeGen arch (Operands a) negate t x = case t of IntegralNumType i | IntegralDict <- integralDict i -> mul t x (ir t (num t (P.negate 1))) FloatingNumType f | FloatingDict <- floatingDict f -> mul t x (ir t (num t (P.negate 1))) abs :: forall arch a. NumType a -> Operands a -> CodeGen arch (Operands a) abs n x = case n of FloatingNumType f -> mathf "fabs" f x IntegralNumType i | unsigned i -> return x | IntegralDict <- integralDict i -> let p = ScalarPrimType (SingleScalarType (NumSingleType n)) t = PrimType p in case finiteBitSize (undefined :: a) of 64 -> call (Lam p (op n x) (Body t Nothing "llabs")) [NoUnwind, ReadNone] _ -> call (Lam p (op n x) (Body t Nothing "abs")) [NoUnwind, ReadNone] signum :: forall arch a. NumType a -> Operands a -> CodeGen arch (Operands a) signum t x = case t of IntegralNumType i | IntegralDict <- integralDict i , unsigned i -> do z <- neq (NumSingleType t) x (ir t (num t 0)) s <- instr (BoolToInt i (unbool z)) return s -- -- http://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign | IntegralDict <- integralDict i -> do let wsib = finiteBitSize (undefined::a) z <- neq (NumSingleType t) x (ir t (num t 0)) l <- instr (BoolToInt i (unbool z)) r <- shiftRA i x (ir integralType (integral integralType (wsib P.- 1))) s <- bor i l r return s -- -- http://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign FloatingNumType f | FloatingDict <- floatingDict f -> do l <- gt (NumSingleType t) x (ir f (floating f 0)) r <- lt (NumSingleType t) x (ir f (floating f 0)) u <- instr (BoolToFP f (unbool l)) v <- instr (BoolToFP f (unbool r)) s <- sub t u v return s -- Operations from Integral and Bits -- --------------------------------- quot :: IntegralType a -> Operands a -> Operands a -> CodeGen arch (Operands a) quot = binop Quot rem :: IntegralType a -> Operands a -> Operands a -> CodeGen arch (Operands a) rem = binop Rem quotRem :: IntegralType a -> Operands a -> Operands a -> CodeGen arch (Operands (a,a)) quotRem t x y = do q <- quot t x y r <- rem t x y -- TLM: On x86 we can compute quotRem with a single [i]divq instruction. This -- is not evident from the generated LLVM IR, which will still list both -- div/rem operations. Note that this may not be true for other instruction -- sets, for example the NVPTX assembly contains both operations. It might be -- worthwhile to allow backends to specialise Exp code generation in the same -- way the other phases of compilation are handled. -- -- The following can be used to compute the remainder, and _may_ be better for -- architectures without a combined quotRem instruction. -- -- z <- mul (IntegralNumType t) y q -- r <- sub (IntegralNumType t) x z return $ pair q r idiv :: IntegralType a -> Operands a -> Operands a -> CodeGen arch (Operands a) idiv i x y | unsigned i = quot i x y -- | IntegralDict <- integralDict i , Dict <- integralElt i , zero <- ir i (integral i 0) , one <- ir i (integral i 1) , n <- IntegralNumType i , s <- NumSingleType n = if (tp, gt s x zero `land'` lt s y zero) then do a <- sub n x one b <- quot i a y c <- sub n b one return c else if (tp, lt s x zero `land'` gt s y zero) then do a <- add n x one b <- quot i a y c <- sub n b one return c else quot i x y where tp = TupRsingle $ SingleScalarType $ NumSingleType $ IntegralNumType i mod :: IntegralType a -> Operands a -> Operands a -> CodeGen arch (Operands a) mod i x y | unsigned i = rem i x y -- | IntegralDict <- integralDict i , Dict <- integralElt i , zero <- ir i (integral i 0) , n <- IntegralNumType i , s <- NumSingleType n = do r <- rem i x y if (tp, (gt s x zero `land'` lt s y zero) `lor'` (lt s x zero `land'` gt s y zero)) then if (tp, neq s r zero) then add n r y else return zero else return r where tp = TupRsingle $ SingleScalarType $ NumSingleType $ IntegralNumType i divMod :: IntegralType a -> Operands a -> Operands a -> CodeGen arch (Operands (a,a)) divMod i x y | unsigned i = quotRem i x y -- | IntegralDict <- integralDict i , Dict <- integralElt i , zero <- ir i (integral i 0) , one <- ir i (integral i 1) , n <- IntegralNumType i , s <- NumSingleType n = if (TupRpair tp tp, gt s x zero `land'` lt s y zero) then do a <- sub n x one b <- quotRem i a y c <- sub n (fst b) one d <- add n (snd b) y e <- add n d one return $ pair c e else if (TupRpair tp tp, lt s x zero `land'` gt s y zero) then do a <- add n x one b <- quotRem i a y c <- sub n (fst b) one d <- add n (snd b) y e <- sub n d one return $ pair c e else quotRem i x y where tp = TupRsingle $ SingleScalarType $ NumSingleType $ IntegralNumType i band :: IntegralType a -> Operands a -> Operands a -> CodeGen arch (Operands a) band = binop BAnd bor :: IntegralType a -> Operands a -> Operands a -> CodeGen arch (Operands a) bor = binop BOr xor :: IntegralType a -> Operands a -> Operands a -> CodeGen arch (Operands a) xor = binop BXor complement :: IntegralType a -> Operands a -> CodeGen arch (Operands a) complement t x | IntegralDict <- integralDict t = xor t x (ir t (integral t (P.negate 1))) shiftL :: IntegralType a -> Operands a -> Operands Int -> CodeGen arch (Operands a) shiftL t x i = do i' <- fromIntegral integralType (IntegralNumType t) i binop ShiftL t x i' shiftR :: IntegralType a -> Operands a -> Operands Int -> CodeGen arch (Operands a) shiftR t | signed t = shiftRA t | otherwise = shiftRL t shiftRL :: IntegralType a -> Operands a -> Operands Int -> CodeGen arch (Operands a) shiftRL t x i = do i' <- fromIntegral integralType (IntegralNumType t) i r <- binop ShiftRL t x i' return r shiftRA :: IntegralType a -> Operands a -> Operands Int -> CodeGen arch (Operands a) shiftRA t x i = do i' <- fromIntegral integralType (IntegralNumType t) i r <- binop ShiftRA t x i' return r rotateL :: forall arch a. IntegralType a -> Operands a -> Operands Int -> CodeGen arch (Operands a) rotateL t x i | IntegralDict <- integralDict t = do let wsib = finiteBitSize (undefined::a) i1 <- band integralType i (ir integralType (integral integralType (wsib P.- 1))) i2 <- sub numType (ir numType (integral integralType wsib)) i1 -- a <- shiftL t x i1 b <- shiftRL t x i2 c <- bor t a b return c rotateR :: IntegralType a -> Operands a -> Operands Int -> CodeGen arch (Operands a) rotateR t x i = do i' <- negate numType i r <- rotateL t x i' return r popCount :: forall arch a. IntegralType a -> Operands a -> CodeGen arch (Operands Int) popCount i x | IntegralDict <- integralDict i = do let ctpop = fromString $ printf "llvm.ctpop.i%d" (finiteBitSize (undefined::a)) p = ScalarPrimType (SingleScalarType (NumSingleType (IntegralNumType i))) t = PrimType p -- c <- call (Lam p (op i x) (Body t Nothing ctpop)) [NoUnwind, ReadNone] r <- fromIntegral i numType c return r countLeadingZeros :: forall arch a. IntegralType a -> Operands a -> CodeGen arch (Operands Int) countLeadingZeros i x | IntegralDict <- integralDict i = do let clz = fromString $ printf "llvm.ctlz.i%d" (finiteBitSize (undefined::a)) p = ScalarPrimType (SingleScalarType (NumSingleType (IntegralNumType i))) t = PrimType p -- c <- call (Lam p (op i x) (Lam primType (boolean False) (Body t Nothing clz))) [NoUnwind, ReadNone] r <- fromIntegral i numType c return r countTrailingZeros :: forall arch a. IntegralType a -> Operands a -> CodeGen arch (Operands Int) countTrailingZeros i x | IntegralDict <- integralDict i = do let clz = fromString $ printf "llvm.cttz.i%d" (finiteBitSize (undefined::a)) p = ScalarPrimType (SingleScalarType (NumSingleType (IntegralNumType i))) t = PrimType p -- c <- call (Lam p (op i x) (Lam primType (boolean False) (Body t Nothing clz))) [NoUnwind, ReadNone] r <- fromIntegral i numType c return r -- Operators from Fractional and Floating -- -------------------------------------- fdiv :: FloatingType a -> Operands a -> Operands a -> CodeGen arch (Operands a) fdiv = binop Div recip :: FloatingType a -> Operands a -> CodeGen arch (Operands a) recip t x | FloatingDict <- floatingDict t = fdiv t (ir t (floating t 1)) x sin :: FloatingType a -> Operands a -> CodeGen arch (Operands a) sin = mathf "sin" cos :: FloatingType a -> Operands a -> CodeGen arch (Operands a) cos = mathf "cos" tan :: FloatingType a -> Operands a -> CodeGen arch (Operands a) tan = mathf "tan" sinh :: FloatingType a -> Operands a -> CodeGen arch (Operands a) sinh = mathf "sinh" cosh :: FloatingType a -> Operands a -> CodeGen arch (Operands a) cosh = mathf "cosh" tanh :: FloatingType a -> Operands a -> CodeGen arch (Operands a) tanh = mathf "tanh" asin :: FloatingType a -> Operands a -> CodeGen arch (Operands a) asin = mathf "asin" acos :: FloatingType a -> Operands a -> CodeGen arch (Operands a) acos = mathf "acos" atan :: FloatingType a -> Operands a -> CodeGen arch (Operands a) atan = mathf "atan" asinh :: FloatingType a -> Operands a -> CodeGen arch (Operands a) asinh = mathf "asinh" acosh :: FloatingType a -> Operands a -> CodeGen arch (Operands a) acosh = mathf "acosh" atanh :: FloatingType a -> Operands a -> CodeGen arch (Operands a) atanh = mathf "atanh" atan2 :: FloatingType a -> Operands a -> Operands a -> CodeGen arch (Operands a) atan2 = mathf2 "atan2" exp :: FloatingType a -> Operands a -> CodeGen arch (Operands a) exp = mathf "exp" fpow :: FloatingType a -> Operands a -> Operands a -> CodeGen arch (Operands a) fpow = mathf2 "pow" sqrt :: FloatingType a -> Operands a -> CodeGen arch (Operands a) sqrt = mathf "sqrt" log :: FloatingType a -> Operands a -> CodeGen arch (Operands a) log = mathf "log" logBase :: forall arch a. FloatingType a -> Operands a -> Operands a -> CodeGen arch (Operands a) logBase t x@(op t -> base) y | FloatingDict <- floatingDict t = logBase' where match :: Eq t => Operand t -> Operand t -> Bool match (ConstantOperand (ScalarConstant _ u)) (ConstantOperand (ScalarConstant _ v)) = u == v match _ _ = False logBase' :: (Num a, Eq a) => CodeGen arch (Operands a) logBase' | match base (floating t 2) = mathf "log2" t y | match base (floating t 10) = mathf "log10" t y | otherwise = do x' <- log t x y' <- log t y fdiv t y' x' -- Operators from RealFloat -- ------------------------ isNaN :: FloatingType a -> Operands a -> CodeGen arch (Operands Bool) isNaN f (op f -> x) = instr (IsNaN f x) isInfinite :: forall arch a. FloatingType a -> Operands a -> CodeGen arch (Operands Bool) isInfinite f x = do x' <- abs n x eq (NumSingleType n) infinity x' where n :: NumType a n = FloatingNumType f infinity :: Operands a infinity | FloatingDict <- floatingDict f = ir f (floating f (1/0)) -- Operators from RealFrac -- ----------------------- truncate :: FloatingType a -> IntegralType b -> Operands a -> CodeGen arch (Operands b) truncate tf ti (op tf -> x) = instr (FPToInt tf ti x) round :: FloatingType a -> IntegralType b -> Operands a -> CodeGen arch (Operands b) round tf ti x = do i <- mathf "round" tf x truncate tf ti i floor :: FloatingType a -> IntegralType b -> Operands a -> CodeGen arch (Operands b) floor tf ti x = do i <- mathf "floor" tf x truncate tf ti i ceiling :: FloatingType a -> IntegralType b -> Operands a -> CodeGen arch (Operands b) ceiling tf ti x = do i <- mathf "ceil" tf x truncate tf ti i -- Relational and Equality operators -- --------------------------------- cmp :: Ordering -> SingleType a -> Operands a -> Operands a -> CodeGen arch (Operands Bool) cmp p dict (op dict -> x) (op dict -> y) = instr (Cmp dict p x y) lt :: SingleType a -> Operands a -> Operands a -> CodeGen arch (Operands Bool) lt = cmp LT gt :: SingleType a -> Operands a -> Operands a -> CodeGen arch (Operands Bool) gt = cmp GT lte :: SingleType a -> Operands a -> Operands a -> CodeGen arch (Operands Bool) lte = cmp LE gte :: SingleType a -> Operands a -> Operands a -> CodeGen arch (Operands Bool) gte = cmp GE eq :: SingleType a -> Operands a -> Operands a -> CodeGen arch (Operands Bool) eq = cmp EQ neq :: SingleType a -> Operands a -> Operands a -> CodeGen arch (Operands Bool) neq = cmp NE max :: SingleType a -> Operands a -> Operands a -> CodeGen arch (Operands a) max ty x y | NumSingleType (FloatingNumType f) <- ty = mathf2 "fmax" f x y | otherwise = do c <- unbool <$> gte ty x y binop (flip Select c) ty x y min :: SingleType a -> Operands a -> Operands a -> CodeGen arch (Operands a) min ty x y | NumSingleType (FloatingNumType f) <- ty = mathf2 "fmin" f x y | otherwise = do c <- unbool <$> lte ty x y binop (flip Select c) ty x y -- Logical operators -- ----------------- -- -- Note that these implementations are strict in both arguments. The short -- circuiting (&&) and (||) operators in the language are not evaluated -- using these functions, but defined in terms of if-then-else. -- land :: Operands Bool -> Operands Bool -> CodeGen arch (Operands Bool) land (OP_Bool x) (OP_Bool y) = instr (LAnd x y) lor :: Operands Bool -> Operands Bool -> CodeGen arch (Operands Bool) lor (OP_Bool x) (OP_Bool y) = instr (LOr x y) lnot :: Operands Bool -> CodeGen arch (Operands Bool) lnot (OP_Bool x) = instr (LNot x) -- Utilities for implementing bounds checks land' :: CodeGen arch (Operands Bool) -> CodeGen arch (Operands Bool) -> CodeGen arch (Operands Bool) land' x y = do a <- x b <- y land a b lor' :: CodeGen arch (Operands Bool) -> CodeGen arch (Operands Bool) -> CodeGen arch (Operands Bool) lor' x y = do a <- x b <- y lor a b -- Type conversions -- ---------------- fromIntegral :: forall arch a b. IntegralType a -> NumType b -> Operands a -> CodeGen arch (Operands b) fromIntegral i1 n (op i1 -> x) = case n of FloatingNumType f -> instr (IntToFP i1 f x) IntegralNumType (i2 :: IntegralType b) | IntegralDict <- integralDict i1 , IntegralDict <- integralDict i2 -> let bits_a = finiteBitSize (undefined::a) bits_b = finiteBitSize (undefined::b) in case Ord.compare bits_a bits_b of Ord.EQ -> instr (BitCast (SingleScalarType (NumSingleType n)) x) Ord.GT -> instr (Trunc (IntegralBoundedType i1) (IntegralBoundedType i2) x) Ord.LT -> instr (Ext (IntegralBoundedType i1) (IntegralBoundedType i2) x) toFloating :: forall arch a b. NumType a -> FloatingType b -> Operands a -> CodeGen arch (Operands b) toFloating n1 f2 (op n1 -> x) = case n1 of IntegralNumType i1 -> instr (IntToFP i1 f2 x) FloatingNumType (f1 :: FloatingType a) | FloatingDict <- floatingDict f1 , FloatingDict <- floatingDict f2 -> let bytes_a = sizeOf (undefined::a) bytes_b = sizeOf (undefined::b) in case Ord.compare bytes_a bytes_b of Ord.EQ -> instr (BitCast (SingleScalarType (NumSingleType (FloatingNumType f2))) x) Ord.GT -> instr (FTrunc f1 f2 x) Ord.LT -> instr (FExt f1 f2 x) bitcast :: ScalarType a -> ScalarType b -> Operands a -> CodeGen arch (Operands b) bitcast ta tb x | Just Refl <- matchScalarType ta tb = return x | otherwise = instr (BitCast tb (op ta x)) -- Utility functions -- ----------------- fst :: Operands (a, b) -> Operands a fst (OP_Pair x _) = x snd :: Operands (a, b) -> Operands b snd (OP_Pair _ y) = y pair :: Operands a -> Operands b -> Operands (a, b) pair x y = OP_Pair x y unpair :: Operands (a, b) -> (Operands a, Operands b) unpair (OP_Pair x y) = (x, y) uncurry :: (Operands a -> Operands b -> c) -> Operands (a, b) -> c uncurry f (OP_Pair x y) = f x y unbool :: Operands Bool -> Operand Bool unbool (OP_Bool x) = x binop :: IROP dict => (dict a -> Operand a -> Operand a -> Instruction a) -> dict a -> Operands a -> Operands a -> CodeGen arch (Operands a) binop f dict (op dict -> x) (op dict -> y) = instr (f dict x y) fst3 :: Operands (Tup3 a b c) -> Operands a fst3 (OP_Pair (OP_Pair (OP_Pair OP_Unit x) _) _) = x snd3 :: Operands (Tup3 a b c) -> Operands b snd3 (OP_Pair (OP_Pair _ y) _) = y thd3 :: Operands (Tup3 a b c) -> Operands c thd3 (OP_Pair _ z) = z trip :: Operands a -> Operands b -> Operands c -> Operands (Tup3 a b c) trip x y z = OP_Pair (OP_Pair (OP_Pair OP_Unit x) y) z untrip :: Operands (Tup3 a b c) -> (Operands a, Operands b, Operands c) untrip t = (fst3 t, snd3 t, thd3 t) -- | Lift a constant value into an constant in the intermediate representation. -- {-# INLINABLE lift #-} lift :: TypeR a -> a -> Operands a lift tp v = constant tp v {-# INLINE liftInt #-} liftInt :: Int -> Operands Int liftInt = lift $ TupRsingle scalarTypeInt {-# INLINE liftInt32 #-} liftInt32 :: Int32 -> Operands Int32 liftInt32 = lift $ TupRsingle scalarTypeInt32 {-# INLINE liftWord32 #-} liftWord32 :: Word32 -> Operands Word32 liftWord32 = lift $ TupRsingle scalarTypeWord32 {-# INLINE liftBool #-} liftBool :: Bool -> Operands Bool liftBool x = OP_Bool (boolean x) -- | Standard if-then-else expression -- ifThenElse :: (TypeR a, CodeGen arch (Operands Bool)) -> CodeGen arch (Operands a) -> CodeGen arch (Operands a) -> CodeGen arch (Operands a) ifThenElse (tp, test) yes no = do ifThen <- newBlock "if.then" ifElse <- newBlock "if.else" ifExit <- newBlock "if.exit" _ <- beginBlock "if.entry" p <- test _ <- cbr p ifThen ifElse setBlock ifThen tv <- yes tb <- br ifExit setBlock ifElse fv <- no fb <- br ifExit setBlock ifExit phi tp [(tv, tb), (fv, fb)] caseof :: TypeR a -> CodeGen arch (Operands TAG) -> [(TAG, CodeGen arch (Operands a))] -> Maybe (CodeGen arch (Operands a)) -> CodeGen arch (Operands a) caseof tR tag xs x = do exit <- newBlock "switch.exit" def <- newBlock "switch.default" cases <- forM xs (\(t,e) -> (t,e,) <$> newBlock (printf "switch.l%d" t)) _ <- beginBlock "switch.entry" p <- tag _ <- switch p def [(t,b) | (t,_,b) <- cases] -- Generate basic blocks for each equation vs <- forM cases $ \(_,body,label) -> do setBlock label r <- body b <- br exit return (r,b) -- Basic block for the default case v <- do setBlock def r <- case x of Nothing -> let go :: TypeR a -> Operands a go TupRunit = OP_Unit go (TupRsingle t) = ir t (undef t) go (TupRpair a b) = OP_Pair (go a) (go b) in return (go tR) Just default_ -> default_ b <- br exit return (r,b) setBlock exit phi tR (v:vs) -- Execute the body only if the first argument evaluates to True -- when :: CodeGen arch (Operands Bool) -> CodeGen arch () -> CodeGen arch () when test doit = do body <- newBlock "when.body" exit <- newBlock "when.exit" p <- test _ <- cbr p body exit setBlock body doit _ <- br exit setBlock exit -- Execute the body only if the first argument evaluates to False -- unless :: CodeGen arch (Operands Bool) -> CodeGen arch () -> CodeGen arch () unless test doit = do body <- newBlock "unless.body" exit <- newBlock "unless.exit" p <- test _ <- cbr p exit body setBlock body doit _ <- br exit setBlock exit -- Call a function from the standard C math library. This is a wrapper around -- the 'call' function from CodeGen.Base since: -- -- (1) The parameter and return types are all the same; and -- (2) We check if there is an intrinsic implementation of this function -- -- TLM: We should really be able to construct functions of any arity. -- mathf :: ShortByteString -> FloatingType t -> Operands t -> CodeGen arch (Operands t) mathf n f (op f -> x) = do let s = ScalarPrimType (SingleScalarType (NumSingleType (FloatingNumType f))) t = PrimType s -- name <- lm f n r <- call (Lam s x (Body t Nothing name)) [NoUnwind, ReadOnly] return r mathf2 :: ShortByteString -> FloatingType t -> Operands t -> Operands t -> CodeGen arch (Operands t) mathf2 n f (op f -> x) (op f -> y) = do let s = ScalarPrimType (SingleScalarType (NumSingleType (FloatingNumType f))) t = PrimType s -- name <- lm f n r <- call (Lam s x (Lam s y (Body t Nothing name))) [NoUnwind, ReadOnly] return r lm :: FloatingType t -> ShortByteString -> CodeGen arch Label lm t n = intrinsic $ case t of TypeHalf{} -> n<>"f" -- XXX: check TypeFloat{} -> n<>"f" TypeDouble{} -> n isJust :: Operands (PrimMaybe a) -> CodeGen arch (Operands Bool) isJust (OP_Pair l _) = instr (IntToBool integralType (op integralType l)) fromJust :: Operands (PrimMaybe a) -> CodeGen arch (Operands a) fromJust (OP_Pair _ (OP_Pair OP_Unit r)) = return r