diff options
author | David Terei <davidterei@gmail.com> | 2013-01-16 20:16:28 -0800 |
---|---|---|
committer | David Terei <davidterei@gmail.com> | 2013-01-17 00:30:47 -0800 |
commit | 25f8d040a6e151237c84be380179b3c6ffb9a34c (patch) | |
tree | 2ed8a7f10060258c63fc4a72624e61c4209ebae6 /compiler/llvmGen | |
parent | 5cca0b443ed2b0328e58fb7881cb393c300f64ed (diff) | |
download | haskell-25f8d040a6e151237c84be380179b3c6ffb9a34c.tar.gz |
Fix floating point constants in LLVM backend (#7600).
Diffstat (limited to 'compiler/llvmGen')
-rw-r--r-- | compiler/llvmGen/Llvm/Types.hs | 38 |
1 files changed, 30 insertions, 8 deletions
diff --git a/compiler/llvmGen/Llvm/Types.hs b/compiler/llvmGen/Llvm/Types.hs index 9e77990160..c4d9995e47 100644 --- a/compiler/llvmGen/Llvm/Types.hs +++ b/compiler/llvmGen/Llvm/Types.hs @@ -18,6 +18,8 @@ import Unique -- from NCG import PprBase +import GHC.Float + -- ----------------------------------------------------------------------------- -- * LLVM Basic Types and Variables -- @@ -227,7 +229,8 @@ getLit :: LlvmLit -> String getLit (LMIntLit i (LMInt 32)) = show (fromInteger i :: Int32) getLit (LMIntLit i (LMInt 64)) = show (fromInteger i :: Int64) getLit (LMIntLit i _ ) = show (fromInteger i :: Int) -getLit (LMFloatLit r LMFloat ) = fToStr $ realToFrac r +-- See Note [LLVM Float Types]. +getLit (LMFloatLit r LMFloat ) = (dToStr . widenFp . narrowFp) r getLit (LMFloatLit r LMDouble) = dToStr r getLit f@(LMFloatLit _ _) = error $ "Can't print this float literal!" ++ show f getLit (LMNullLit _ ) = "null" @@ -792,6 +795,8 @@ instance Show LlvmCastOp where -- | Convert a Haskell Double to an LLVM hex encoded floating point form. In -- Llvm float literals can be printed in a big-endian hexadecimal format, -- regardless of underlying architecture. +-- +-- See Note [LLVM Float Types]. dToStr :: Double -> String dToStr d = let bs = doubleToBytes d @@ -804,13 +809,30 @@ dToStr d str = map toUpper $ concat . fixEndian . (map hex) $ bs in "0x" ++ str --- | Convert a Haskell Float to an LLVM hex encoded floating point form. --- LLVM uses the same encoding for both floats and doubles (16 digit hex --- string) but floats must have the last half all zeroes so it can fit into --- a float size type. -{-# NOINLINE fToStr #-} -fToStr :: Float -> String -fToStr = (dToStr . realToFrac) +-- Note [LLVM Float Types] +-- ~~~~~~~~~~~~~~~~~~~~~~~ +-- We use 'dToStr' for both printing Float and Double floating point types. This is +-- as LLVM expects all floating point constants (single & double) to be in IEEE +-- 754 Double precision format. However, for single precision numbers (Float) +-- they should be *representable* in IEEE 754 Single precision format. So the +-- easiest way to do this is to narrow and widen again. +-- (i.e., Double -> Float -> Double). We must be careful doing this that GHC +-- doesn't optimize that away. + +-- Note [narrowFp & widenFp] +-- ~~~~~~~~~~~~~~~~~~~~~~~~~ +-- NOTE: we use float2Double & co directly as GHC likes to optimize away +-- successive calls of 'realToFrac', defeating the narrowing. (Bug #7600). +-- 'realToFrac' has inconsistent behaviour with optimisation as well that can +-- also cause issues, these methods don't. + +narrowFp :: Double -> Float +{-# NOINLINE narrowFp #-} +narrowFp = double2Float + +widenFp :: Float -> Double +{-# NOINLINE widenFp #-} +widenFp = float2Double -- | Reverse or leave byte data alone to fix endianness on this target. fixEndian :: [a] -> [a] |