ghc-9.12.1: GHC/CmmToAsm/Format.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ViewPatterns #-}
-- | Formats on this architecture
-- A Format is a combination of width and class
--
-- TODO: Signed vs unsigned?
--
-- TODO: This module is currently shared by all architectures because
-- NCGMonad need to know about it to make a VReg. It would be better
-- to have architecture specific formats, and do the overloading
-- properly. eg SPARC doesn't care about FF80.
--
module GHC.CmmToAsm.Format (
Format(.., IntegerFormat),
ScalarFormat(..),
intFormat,
floatFormat,
isIntFormat,
isIntScalarFormat,
intScalarFormat,
isFloatFormat,
vecFormat,
isVecFormat,
cmmTypeFormat,
formatToWidth,
scalarWidth,
formatInBytes,
isFloatScalarFormat,
isFloatOrFloatVecFormat,
floatScalarFormat,
scalarFormatFormat,
VirtualRegWithFormat(..),
RegWithFormat(..),
takeVirtualRegs,
takeRealRegs,
)
where
import GHC.Prelude
import GHC.Cmm
import GHC.Platform.Reg ( Reg(..), RealReg, VirtualReg )
import GHC.Types.Unique ( Uniquable(..) )
import GHC.Types.Unique.Set
import GHC.Utils.Outputable
import GHC.Utils.Panic
{- Note [GHC's data format representations]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GHC has severals types that represent various aspects of data format.
These include:
* 'CmmType.CmmType': The data classification used throughout the C--
pipeline. This is a pair of a CmmCat and a Width.
* 'CmmType.CmmCat': What the bits in a C-- value mean (e.g. a pointer, integer, or floating-point value)
* 'CmmType.Width': The width of a C-- value.
* 'CmmType.Length': The width (measured in number of scalars) of a vector value.
* 'Format.Format': The data format representation used by much of the backend.
* 'Format.ScalarFormat': The format of a 'Format.VecFormat'\'s scalar.
* 'RegClass.RegClass': Whether a register is an integer or a floating point/vector register.
-}
-- It looks very like the old MachRep, but it's now of purely local
-- significance, here in the native code generator. You can change it
-- without global consequences.
--
-- A major use is as an opcode qualifier; thus the opcode
-- mov.l a b
-- might be encoded
-- MOV II32 a b
-- where the Format field encodes the ".l" part.
-- ToDo: it's not clear to me that we need separate signed-vs-unsigned formats
-- here. I've removed them from the x86 version, we'll see what happens --SDM
-- ToDo: quite a few occurrences of Format could usefully be replaced by Width
data Format
= II8
| II16
| II32
| II64
| FF32
| FF64
| VecFormat !Length -- ^ number of elements (always at least 2)
!ScalarFormat -- ^ format of each element
deriving (Show, Eq, Ord)
pattern IntegerFormat :: Format
pattern IntegerFormat <- ( isIntegerFormat -> True )
{-# COMPLETE IntegerFormat, FF32, FF64, VecFormat #-}
isIntegerFormat :: Format -> Bool
isIntegerFormat = \case
II8 -> True
II16 -> True
II32 -> True
II64 -> True
_ -> False
instance Outputable Format where
ppr fmt = text (show fmt)
data ScalarFormat
= FmtInt8
| FmtInt16
| FmtInt32
| FmtInt64
| FmtFloat
| FmtDouble
deriving (Show, Eq, Ord)
scalarFormatFormat :: ScalarFormat -> Format
scalarFormatFormat = \case
FmtInt8 -> II8
FmtInt16 -> II16
FmtInt32 -> II32
FmtInt64 -> II64
FmtFloat -> FF32
FmtDouble -> FF64
isFloatScalarFormat :: ScalarFormat -> Bool
isFloatScalarFormat = \case
FmtFloat -> True
FmtDouble -> True
_ -> False
isFloatOrFloatVecFormat :: Format -> Bool
isFloatOrFloatVecFormat = \case
VecFormat _ sFmt -> isFloatScalarFormat sFmt
fmt -> isFloatFormat fmt
floatScalarFormat :: Width -> ScalarFormat
floatScalarFormat W32 = FmtFloat
floatScalarFormat W64 = FmtDouble
floatScalarFormat w = pprPanic "floatScalarFormat" (ppr w)
isIntScalarFormat :: ScalarFormat -> Bool
isIntScalarFormat = not . isFloatScalarFormat
intScalarFormat :: Width -> ScalarFormat
intScalarFormat = \case
W8 -> FmtInt8
W16 -> FmtInt16
W32 -> FmtInt32
W64 -> FmtInt64
w -> pprPanic "intScalarFormat" (ppr w)
-- | Get the integer format of this width.
intFormat :: Width -> Format
intFormat width
= case width of
W8 -> II8
W16 -> II16
W32 -> II32
W64 -> II64
other -> sorry $ "The native code generator cannot " ++
"produce code for Format.intFormat " ++ show other
++ "\n\tConsider using the llvm backend with -fllvm"
-- | Check if a format represent an integer value.
isIntFormat :: Format -> Bool
isIntFormat format =
case format of
II8 -> True
II16 -> True
II32 -> True
II64 -> True
_ -> False
-- | Get the float format of this width.
floatFormat :: Width -> Format
floatFormat width
= case width of
W32 -> FF32
W64 -> FF64
other -> pprPanic "Format.floatFormat" (ppr other)
-- | Check if a format represents a floating point value.
isFloatFormat :: Format -> Bool
isFloatFormat format
= case format of
FF32 -> True
FF64 -> True
_ -> False
vecFormat :: CmmType -> Format
vecFormat ty =
let l = vecLength ty
elemTy = vecElemType ty
in if isFloatType elemTy
then case typeWidth elemTy of
W32 -> VecFormat l FmtFloat
W64 -> VecFormat l FmtDouble
_ -> pprPanic "Incorrect vector element width" (ppr elemTy)
else case typeWidth elemTy of
W8 -> VecFormat l FmtInt8
W16 -> VecFormat l FmtInt16
W32 -> VecFormat l FmtInt32
W64 -> VecFormat l FmtInt64
_ -> pprPanic "Incorrect vector element width" (ppr elemTy)
-- | Check if a format represents a vector
isVecFormat :: Format -> Bool
isVecFormat (VecFormat {}) = True
isVecFormat _ = False
-- | Convert a Cmm type to a Format.
cmmTypeFormat :: CmmType -> Format
cmmTypeFormat ty
| isFloatType ty = floatFormat (typeWidth ty)
| isVecType ty = vecFormat ty
| otherwise = intFormat (typeWidth ty)
-- | Get the Width of a Format.
formatToWidth :: Format -> Width
formatToWidth format
= case format of
II8 -> W8
II16 -> W16
II32 -> W32
II64 -> W64
FF32 -> W32
FF64 -> W64
VecFormat l s ->
widthFromBytes (l * widthInBytes (scalarWidth s))
scalarWidth :: ScalarFormat -> Width
scalarWidth = \case
FmtInt8 -> W8
FmtInt16 -> W16
FmtInt32 -> W32
FmtInt64 -> W64
FmtFloat -> W32
FmtDouble -> W64
formatInBytes :: Format -> Int
formatInBytes = widthInBytes . formatToWidth
--------------------------------------------------------------------------------
-- | A typed virtual register: a virtual register, together with the specific
-- format we are using it at.
data VirtualRegWithFormat
= VirtualRegWithFormat
{ virtualRegWithFormat_reg :: {-# UNPACK #-} !VirtualReg
, virtualRegWithFormat_format :: !Format
}
-- | A typed register: a register, together with the specific format we
-- are using it at.
data RegWithFormat
= RegWithFormat
{ regWithFormat_reg :: {-# UNPACK #-} !Reg
, regWithFormat_format :: !Format
}
instance Show RegWithFormat where
show (RegWithFormat reg fmt) = show reg ++ "::" ++ show fmt
instance Uniquable RegWithFormat where
getUnique = getUnique . regWithFormat_reg
instance Outputable VirtualRegWithFormat where
ppr (VirtualRegWithFormat reg fmt) = ppr reg <+> dcolon <+> ppr fmt
instance Outputable RegWithFormat where
ppr (RegWithFormat reg fmt) = ppr reg <+> dcolon <+> ppr fmt
-- | Take all the virtual registers from this set.
takeVirtualRegs :: UniqSet RegWithFormat -> UniqSet VirtualReg
takeVirtualRegs = mapMaybeUniqSet_sameUnique $
\ case { RegWithFormat { regWithFormat_reg = RegVirtual vr } -> Just vr; _ -> Nothing }
-- See Note [Unique Determinism and code generation]
-- | Take all the real registers from this set.
takeRealRegs :: UniqSet RegWithFormat -> UniqSet RealReg
takeRealRegs = mapMaybeUniqSet_sameUnique $
\ case { RegWithFormat { regWithFormat_reg = RegReal rr } -> Just rr; _ -> Nothing }
-- See Note [Unique Determinism and code generation]