diff options
author | Erik de Castro Lopo <erikd@mega-nerd.com> | 2017-04-12 14:09:49 -0400 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2017-04-12 14:53:06 -0400 |
commit | aa206346e6f12c9f88fdf051185741761ea88fbb (patch) | |
tree | 3963e016c48662f00cf5c53cf3d75af05322092d /libraries | |
parent | bb3712bf772fecb965f56a356ccf61437d324dcf (diff) | |
download | haskell-aa206346e6f12c9f88fdf051185741761ea88fbb.tar.gz |
base: Implement bit casts between word and float types
Test Plan: Test on x86 and x86_64
Reviewers: duncan, trofi, simonmar, tibbe, hvr, austin, rwbarton,
bgamari
Reviewed By: duncan
Subscribers: Phyx, DemiMarie, rwbarton, thomie
Differential Revision: https://phabricator.haskell.org/D3358
Diffstat (limited to 'libraries')
-rw-r--r-- | libraries/base/GHC/Float.hs | 91 | ||||
-rw-r--r-- | libraries/base/cbits/CastFloatWord.cmm | 69 |
2 files changed, 159 insertions, 1 deletions
diff --git a/libraries/base/GHC/Float.hs b/libraries/base/GHC/Float.hs index 64467b338e..c534bafa07 100644 --- a/libraries/base/GHC/Float.hs +++ b/libraries/base/GHC/Float.hs @@ -1,8 +1,10 @@ {-# LANGUAGE Trustworthy #-} {-# LANGUAGE CPP + , GHCForeignImportPrim , NoImplicitPrelude , MagicHash , UnboxedTuples + , UnliftedFFITypes #-} {-# LANGUAGE CApiFFI #-} -- We believe we could deorphan this module, by moving lots of things @@ -21,11 +23,13 @@ -- Stability : internal -- Portability : non-portable (GHC Extensions) -- --- The types 'Float' and 'Double', and the classes 'Floating' and 'RealFloat'. +-- The types 'Float' and 'Double', the classes 'Floating' and 'RealFloat' and +-- casting between Word32 and Float and Word64 and Double. -- ----------------------------------------------------------------------------- #include "ieee-flpt.h" +#include "MachDeps.h" module GHC.Float ( module GHC.Float @@ -46,6 +50,7 @@ import GHC.Enum import GHC.Show import GHC.Num import GHC.Real +import GHC.Word import GHC.Arr import GHC.Float.RealFracMethods import GHC.Float.ConversionUtils @@ -1253,3 +1258,87 @@ exponents returned by decodeFloat. -} clamp :: Int -> Int -> Int clamp bd k = max (-bd) (min bd k) + + +{- +Note [Casting from integral to floating point types] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +To implement something like `reinterpret_cast` from C++ to go from a +floating-point type to an integral type one might niavely think that the +following should work: + + cast :: Float -> Word32 + cast (F# f#) = W32# (unsafeCoerce# f#) + +Unfortunately that is not the case, because all the `unsafeCoerce#` does is tell +the compiler that the types have changed. When one does the above cast and +tries to operate on the resulting `Word32` the code generator will generate code +that performs an integer/word operation on a floating-point register, which +results in a compile error. + +The correct way of implementing `reinterpret_cast` to implement a primpop, but +that requires a unique implementation for all supported archetectures. The next +best solution is to write the value from the source register to memory and then +read it from memory into the destination register and the best way to do that +is using CMM. +-} + +-- | @'castWord32ToFloat' w@ does a bit-for-bit copy from an integral value +-- to a floating-point value. +-- +-- @since 4.10.0.0 + +{-# INLINE castWord32ToFloat #-} +castWord32ToFloat :: Word32 -> Float +castWord32ToFloat (W32# w#) = F# (stgWord32ToFloat w#) + +foreign import prim "stg_word32ToFloatzh" + stgWord32ToFloat :: Word# -> Float# + + +-- | @'castFloatToWord32' f@ does a bit-for-bit copy from a floating-point value +-- to an integral value. +-- +-- @since 4.10.0.0 + +{-# INLINE castFloatToWord32 #-} +castFloatToWord32 :: Float -> Word32 +castFloatToWord32 (F# f#) = W32# (stgFloatToWord32 f#) + +foreign import prim "stg_floatToWord32zh" + stgFloatToWord32 :: Float# -> Word# + + + +-- | @'castWord64ToDouble' w@ does a bit-for-bit copy from an integral value +-- to a floating-point value. +-- +-- @since 4.10.0.0 + +{-# INLINE castWord64ToDouble #-} +castWord64ToDouble :: Word64 -> Double +castWord64ToDouble (W64# w) = D# (stgWord64ToDouble w) + +foreign import prim "stg_word64ToDoublezh" +#if WORD_SIZE_IN_BITS == 64 + stgWord64ToDouble :: Word# -> Double# +#else + stgWord64ToDouble :: Word64# -> Double# +#endif + + +-- | @'castFloatToWord32' f@ does a bit-for-bit copy from a floating-point value +-- to an integral value. +-- +-- @since 4.10.0.0 + +{-# INLINE castDoubleToWord64 #-} +castDoubleToWord64 :: Double -> Word64 +castDoubleToWord64 (D# d#) = W64# (stgDoubleToWord64 d#) + +foreign import prim "stg_doubleToWord64zh" +#if WORD_SIZE_IN_BITS == 64 + stgDoubleToWord64 :: Double# -> Word# +#else + stgDoubleToWord64 :: Double# -> Word64# +#endif diff --git a/libraries/base/cbits/CastFloatWord.cmm b/libraries/base/cbits/CastFloatWord.cmm new file mode 100644 index 0000000000..18d275f4af --- /dev/null +++ b/libraries/base/cbits/CastFloatWord.cmm @@ -0,0 +1,69 @@ +#include "Cmm.h" +#include "MachDeps.h" + +#if WORD_SIZE_IN_BITS == 64 +#define DOUBLE_SIZE_WDS 1 +#else +#define DOUBLE_SIZE_WDS 2 +#endif + +stg_word64ToDoublezh(I64 w) +{ + D_ d; + P_ ptr; + + STK_CHK_GEN_N (DOUBLE_SIZE_WDS); + + reserve DOUBLE_SIZE_WDS = ptr { + I64[ptr] = w; + d = D_[ptr]; + } + + return (d); +} + +stg_doubleToWord64zh(D_ d) +{ + I64 w; + P_ ptr; + + STK_CHK_GEN_N (DOUBLE_SIZE_WDS); + + reserve DOUBLE_SIZE_WDS = ptr { + D_[ptr] = d; + w = I64[ptr]; + } + + return (w); +} + +stg_word32ToFloatzh(W_ w) +{ + F_ f; + P_ ptr; + + STK_CHK_GEN_N (1); + + reserve 1 = ptr { + I32[ptr] = %lobits32(w); + f = F_[ptr]; + } + + return (f); +} + +stg_floatToWord32zh(F_ f) +{ + W_ w; + P_ ptr; + + STK_CHK_GEN_N (1); + + reserve 1 = ptr { + F_[ptr] = f; + w = TO_W_(I32[ptr]); + } + + return (w); +} + |