summaryrefslogtreecommitdiff
path: root/compiler/GHC/Builtin/PrimOps/Casts.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/GHC/Builtin/PrimOps/Casts.hs')
-rw-r--r--compiler/GHC/Builtin/PrimOps/Casts.hs212
1 files changed, 212 insertions, 0 deletions
diff --git a/compiler/GHC/Builtin/PrimOps/Casts.hs b/compiler/GHC/Builtin/PrimOps/Casts.hs
new file mode 100644
index 0000000000..ced3ace986
--- /dev/null
+++ b/compiler/GHC/Builtin/PrimOps/Casts.hs
@@ -0,0 +1,212 @@
+{-
+This module contains helpers to cast variables
+between different Int/WordReps in StgLand.
+
+-}
+
+module GHC.Builtin.PrimOps.Casts
+ ( getCasts )
+where
+
+import GHC.Prelude
+
+import GHC.Core.TyCon
+import GHC.Utils.Outputable
+import GHC.Utils.Panic
+import GHC.Utils.Panic.Plain
+import GHC.Types.RepType
+import GHC.Core.Type
+import GHC.Builtin.Types.Prim
+
+import GHC.Builtin.PrimOps
+import GHC.Plugins (HasDebugCallStack)
+
+{- Note [PrimRep based casting]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+This module contains a number of utility functions useful when
+converting between variables of differing PrimReps.
+
+The general pattern is:
+* We have two primReps `from_rep` and `to_rep`.
+* We want a list of PrimOps we can apply to a variable of rep `from_rep`.
+Applying the list of primOps in order takes us to `to_rep` from `from_rep` giving
+us a variable of the returned type at each step.
+
+E.g. we call `getCasts from_rep to_rep` and get back [(op1#,ty1),(op2#,ty2)].
+We can use this result to construct a function of type
+`StgExpr -> StgExpr` by construction an expression
+
+ case op1# <from> of (x' :: ty1) -> case op2# x' of x' -> <rhs_hole>
+
+Ideally backends will compile the sequence of PrimOps to a no-op. E.g. by reusing
+the same register but just relabeling it as another width.
+However this is might not always be possible or the required optimizations
+simply not implemented in the backend. This means currently many of these casts
+will be cheap but not all of them will be completely zero-cost.
+
+-}
+
+-- | `getCasts from_rep to_rep` gives us a list of primops which when applied in order convert from_rep to to_rep.
+-- See Note [PrimRep based casting]
+getCasts :: PrimRep -> PrimRep -> [(PrimOp,Type)]
+getCasts from_rep to_rep
+ -- No-op
+ | -- pprTrace "getCasts" (ppr (from_rep,to_rep)) $
+ to_rep == from_rep
+ = []
+
+ -- Float <-> Double
+ | to_rep == FloatRep =
+ assertPpr (from_rep == DoubleRep) (ppr from_rep <+> ppr to_rep) $
+ [(DoubleToFloatOp,floatPrimTy)]
+ | to_rep == DoubleRep =
+ assertPpr (from_rep == FloatRep) (ppr from_rep <+> ppr to_rep) $
+ [(FloatToDoubleOp,doublePrimTy)]
+
+ -- Addr <-> Word/Int
+ | to_rep == AddrRep = wordOrIntToAddrRep from_rep
+ | from_rep == AddrRep = addrToWordOrIntRep to_rep
+
+ -- Int* -> Int*
+ | primRepIsInt from_rep
+ , primRepIsInt to_rep
+ = sizedIntToSizedInt from_rep to_rep
+
+ -- Word* -> Word*
+ | primRepIsWord from_rep
+ , primRepIsWord to_rep
+ = sizedWordToSizedWord from_rep to_rep
+
+ -- Word* -> Int*
+ | primRepIsWord from_rep
+ , primRepIsInt to_rep
+ = let (op1,r1) = wordToIntRep from_rep
+ in (op1,primRepToType r1):sizedIntToSizedInt r1 to_rep
+
+ -- Int* -> Word*
+ | primRepIsInt from_rep
+ , primRepIsWord to_rep
+ = let (op1,r1) = intToWordRep from_rep
+ in (op1,primRepToType r1):sizedWordToSizedWord r1 to_rep
+
+ | otherwise = pprPanic "getCasts:Unexpect rep combination"
+ (ppr (from_rep,to_rep))
+
+wordOrIntToAddrRep :: HasDebugCallStack => PrimRep -> [(PrimOp,Type)]
+wordOrIntToAddrRep AddrRep = [] -- No-op argument is already AddrRep
+wordOrIntToAddrRep IntRep = [(IntToAddrOp, addrPrimTy)]
+wordOrIntToAddrRep WordRep = [(WordToIntOp,intPrimTy), (IntToAddrOp,addrPrimTy)]
+wordOrIntToAddrRep r
+ | primRepIsInt r = (intToMachineInt r,intPrimTy):[(IntToAddrOp,addrPrimTy)]
+ | primRepIsWord r =
+ let (op1,r1) = wordToIntRep r
+ in (op1, primRepToType r1):[(intToMachineInt r1,intPrimTy), (IntToAddrOp,addrPrimTy)]
+ | otherwise = pprPanic "Rep not word or int rep" (ppr r)
+
+addrToWordOrIntRep :: HasDebugCallStack => PrimRep -> [(PrimOp,Type)]
+-- Machine sizes
+addrToWordOrIntRep IntRep = [(AddrToIntOp, intPrimTy)]
+addrToWordOrIntRep WordRep = [(AddrToIntOp,intPrimTy), (IntToWordOp,wordPrimTy)]
+-- Explicitly sized reps
+addrToWordOrIntRep r
+ | primRepIsWord r = (AddrToIntOp,intPrimTy) : (IntToWordOp,wordPrimTy) : sizedWordToSizedWord WordRep r
+ | primRepIsInt r = (AddrToIntOp,intPrimTy) : sizedIntToSizedInt IntRep r
+ | otherwise = pprPanic "Target rep not word or int rep" (ppr r)
+
+
+-- WordX# -> IntX# (same size), argument is source rep
+wordToIntRep :: HasDebugCallStack => PrimRep -> (PrimOp,PrimRep)
+wordToIntRep rep
+ = case rep of
+ (WordRep) -> (WordToIntOp, IntRep)
+ (Word8Rep) -> (Word8ToInt8Op, Int8Rep)
+ (Word16Rep) -> (Word16ToInt16Op, Int16Rep)
+ (Word32Rep) -> (Word32ToInt32Op, Int32Rep)
+ (Word64Rep) -> (Word64ToInt64Op, Int64Rep)
+ _ -> pprPanic "Rep not a wordRep" (ppr rep)
+
+-- IntX# -> WordX#, argument is source rep
+intToWordRep :: HasDebugCallStack => PrimRep -> (PrimOp,PrimRep)
+intToWordRep rep
+ = case rep of
+ (IntRep) -> (IntToWordOp, WordRep)
+ (Int8Rep) -> (Int8ToWord8Op, Word8Rep)
+ (Int16Rep) -> (Int16ToWord16Op, Word16Rep)
+ (Int32Rep) -> (Int32ToWord32Op, Word32Rep)
+ (Int64Rep) -> (Int64ToWord64Op, Word64Rep)
+ _ -> pprPanic "Rep not a wordRep" (ppr rep)
+
+-- Casts between any size int to any other size of int
+sizedIntToSizedInt :: HasDebugCallStack => PrimRep -> PrimRep -> [(PrimOp,Type)]
+sizedIntToSizedInt r1 r2
+ | r1 == r2 = []
+-- Cast to Int#
+sizedIntToSizedInt r IntRep = [(intToMachineInt r,intPrimTy)]
+-- Cast from Int#
+sizedIntToSizedInt IntRep r = [(intFromMachineInt r,primRepToType r)]
+-- Sized to differently sized must go over machine word.
+sizedIntToSizedInt r1 r2 = (intToMachineInt r1,intPrimTy) : [(intFromMachineInt r2,primRepToType r2)]
+
+-- Casts between any size Word to any other size of Word
+sizedWordToSizedWord :: HasDebugCallStack => PrimRep -> PrimRep -> [(PrimOp,Type)]
+sizedWordToSizedWord r1 r2
+ | r1 == r2 = []
+-- Cast to Word#
+sizedWordToSizedWord r WordRep = [(wordToMachineWord r,wordPrimTy)]
+-- Cast from Word#
+sizedWordToSizedWord WordRep r = [(wordFromMachineWord r, primRepToType r)]
+-- Conversion between different non-machine sizes must go via machine word.
+sizedWordToSizedWord r1 r2 = (wordToMachineWord r1,wordPrimTy) : [(wordFromMachineWord r2, primRepToType r2)]
+
+
+-- Prefer the definitions above this line if possible
+----------------------
+
+
+-- Int*# to Int#
+{-# INLINE intToMachineInt #-}
+intToMachineInt :: HasDebugCallStack => PrimRep -> PrimOp
+intToMachineInt r =
+ assertPpr (primRepIsInt r) (ppr r) $
+ case r of
+ (Int8Rep) -> Int8ToIntOp
+ (Int16Rep) -> Int16ToIntOp
+ (Int32Rep) -> Int32ToIntOp
+ (Int64Rep) -> Int64ToIntOp
+ _ -> pprPanic "Source rep not int" $ ppr r
+
+-- Int# to Int*#
+{-# INLINE intFromMachineInt #-}
+intFromMachineInt :: HasDebugCallStack => PrimRep -> PrimOp
+intFromMachineInt r =
+ assertPpr (primRepIsInt r) (ppr r) $
+ case r of
+ Int8Rep -> IntToInt8Op
+ Int16Rep -> IntToInt16Op
+ Int32Rep -> IntToInt32Op
+ Int64Rep -> IntToInt64Op
+ _ -> pprPanic "Dest rep not sized int" $ ppr r
+
+-- Word# to Word*#
+{-# INLINE wordFromMachineWord #-}
+wordFromMachineWord :: HasDebugCallStack => PrimRep -> PrimOp
+wordFromMachineWord r =
+ assert (primRepIsWord r) $
+ case r of
+ Word8Rep -> WordToWord8Op
+ Word16Rep -> WordToWord16Op
+ Word32Rep -> WordToWord32Op
+ Word64Rep -> WordToWord64Op
+ _ -> pprPanic "Dest rep not sized word" $ ppr r
+
+-- Word*# to Word#
+{-# INLINE wordToMachineWord #-}
+wordToMachineWord :: HasDebugCallStack => PrimRep -> PrimOp
+wordToMachineWord r =
+ assertPpr (primRepIsWord r) (text "Not a word rep:" <> ppr r) $
+ case r of
+ Word8Rep -> Word8ToWordOp
+ Word16Rep -> Word16ToWordOp
+ Word32Rep -> Word32ToWordOp
+ Word64Rep -> Word64ToWordOp
+ _ -> pprPanic "Dest rep not sized word" $ ppr r \ No newline at end of file