597 lines
18 KiB
Haskell
597 lines
18 KiB
Haskell
{-# LANGUAGE DataKinds #-}
|
||
{-# LANGUAGE KindSignatures #-}
|
||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||
{-# LANGUAGE FlexibleInstances #-}
|
||
{-# LANGUAGE OverloadedStrings #-}
|
||
{-# LANGUAGE PatternSynonyms #-}
|
||
{-|
|
||
Module : Language.QBE
|
||
Description : Types and Pretty instances for the QBE IL
|
||
Copyright : (c) Francesco Gazzetta, 2022
|
||
License : BSD-3-Clause
|
||
Maintainer : Francesco Gazzetta <fgaz@fgaz.me>
|
||
|
||
This module contains datatypes representing the various structures of
|
||
the [intermediate language](https://c9x.me/compile/doc/il.html)
|
||
of the [QBE](https://c9x.me/compile/) compiler backend.
|
||
|
||
All datatypes also have 'Pretty' instances from
|
||
the [@prettyprinter@](https://hackage.haskell.org/package/prettyprinter)
|
||
library.
|
||
You can render QBE IL source files, or any part of them, with something like:
|
||
|
||
> render :: Pretty a => a -> Text
|
||
> render = renderStrict . layoutPretty defaultLayoutOptions . pretty
|
||
|
||
>>> render $ Ret $ Just $ ValTemporary "a"
|
||
"ret %a"
|
||
>>> Text.putStrLn $ render $ Program [] [] [FuncDef [] Nothing "main" …
|
||
function w $main () {
|
||
@start
|
||
⋮
|
||
-}
|
||
module Language.QBE
|
||
(
|
||
-- * Identifiers
|
||
RawIdent
|
||
, Sigil(..)
|
||
, Ident(..)
|
||
-- * Types
|
||
, BaseTy(..)
|
||
, ExtTy(..)
|
||
-- * Constants
|
||
, Const(..)
|
||
-- * Linkage
|
||
, Linkage(..)
|
||
-- * Definitions
|
||
, Alignment
|
||
, Size
|
||
, Amount
|
||
-- ** Aggregate types
|
||
, TypeDef(..)
|
||
, SubTy(..)
|
||
-- ** Data
|
||
, DataDef(..)
|
||
, DataItem(..)
|
||
, Field(..)
|
||
-- ** Functions
|
||
, FuncDef(..)
|
||
, AbiTy(..)
|
||
, Param(..)
|
||
, Variadic(..)
|
||
, prettyVariadic
|
||
-- * Control
|
||
, Val(..)
|
||
, Block(..)
|
||
, Jump(..)
|
||
-- * Instructions
|
||
, Phi(..)
|
||
, PhiArg(..)
|
||
, Inst(..)
|
||
, Assignment(..)
|
||
, pattern (:=)
|
||
, IntRepr(..)
|
||
, BinaryOp(..)
|
||
, Comparison(..)
|
||
, Arg(..)
|
||
-- * Program
|
||
, Program(..)
|
||
) where
|
||
|
||
import Data.Text (Text)
|
||
import Data.Text.Short (ShortText)
|
||
import qualified Data.Text.Short as TS
|
||
import Data.ByteString (ByteString)
|
||
import Data.Word (Word64)
|
||
import Data.List.NonEmpty (NonEmpty, toList)
|
||
import Data.Maybe (maybeToList)
|
||
import Prettyprinter
|
||
( Pretty(pretty), Doc, (<+>), vsep, hsep, hang, punctuate, group, flatAlt
|
||
, space, encloseSep, tupled, comma, equals, braces, lbrace, rbrace )
|
||
-- Instances
|
||
import Data.Hashable (Hashable)
|
||
import Control.DeepSeq (NFData)
|
||
import Data.String (IsString)
|
||
|
||
-- * Identifiers
|
||
----------------
|
||
|
||
-- | A raw identifier string, with no sigil information attached
|
||
type RawIdent = ShortText
|
||
|
||
-- | Sigils are used to differentiate the verious types of 'Ident'ifier.
|
||
data Sigil
|
||
= AggregateTy -- ^ @:@
|
||
| Global -- ^ @$@
|
||
| Temporary -- ^ @%@
|
||
| Label -- ^ @\@@
|
||
deriving (Show, Eq)
|
||
|
||
-- | QBE identifiers. The sigil is represented at the type level, so that
|
||
-- mixing incompatible identifiers is impossible.
|
||
--
|
||
-- >>> :set -XOverloadedStrings
|
||
-- >>> :set -XDataKinds
|
||
-- >>> :set -XTypeApplications
|
||
-- >>> pretty $ Jmp $ Ident @'Label "a"
|
||
-- jmp @a
|
||
-- >>> pretty $ Jmp $ Ident @'Global "a"
|
||
-- <interactive>:5:16: error:
|
||
-- • Couldn't match type ‘'Global’ with ‘'Label’
|
||
-- Expected: Ident 'Label
|
||
-- Actual: Ident 'Global
|
||
-- • In the second argument of ‘($)’, namely ‘Ident @'Global "a"’
|
||
-- In the second argument of ‘($)’, namely ‘Jmp $ Ident @'Global "a"’
|
||
-- In the expression: pretty $ Jmp $ Ident @'Global "a"
|
||
newtype Ident (t :: Sigil) = Ident RawIdent
|
||
deriving (Show, Eq, Ord, IsString, NFData, Hashable)
|
||
|
||
instance Pretty (Ident 'AggregateTy) where
|
||
pretty (Ident raw) = pretty ':' <> pretty (TS.toText raw)
|
||
instance Pretty (Ident 'Global) where
|
||
pretty (Ident raw) = pretty '$' <> pretty (TS.toText raw)
|
||
instance Pretty (Ident 'Temporary) where
|
||
pretty (Ident raw) = pretty '%' <> pretty (TS.toText raw)
|
||
instance Pretty (Ident 'Label) where
|
||
pretty (Ident raw) = pretty '@' <> pretty (TS.toText raw)
|
||
|
||
-- * Types
|
||
----------
|
||
|
||
-- | Base types
|
||
data BaseTy
|
||
= Word -- ^ @w@
|
||
| Long -- ^ @l@
|
||
| Single -- ^ @s@
|
||
| Double -- ^ @d@
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty BaseTy where
|
||
pretty Word = pretty 'w'
|
||
pretty Long = pretty 'l'
|
||
pretty Single = pretty 's'
|
||
pretty Double = pretty 'd'
|
||
|
||
-- | Extended types
|
||
data ExtTy
|
||
= BaseTy BaseTy
|
||
| Byte -- ^ @b@
|
||
| HalfWord -- ^ @h@
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty ExtTy where
|
||
pretty (BaseTy baseTy) = pretty baseTy
|
||
pretty Byte = pretty 'b'
|
||
pretty HalfWord = pretty 'h'
|
||
|
||
-- * Constants
|
||
--------------
|
||
|
||
-- | Constant/immediate
|
||
data Const
|
||
-- MAYBE just use a signed type
|
||
= CInt Bool Word64 -- ^ 64 bit integer. The 'Bool' is whether to negate.
|
||
| CSingle Float -- ^ Single-precision float
|
||
| CDouble Double -- ^ Double-precision float
|
||
| CGlobal (Ident 'Global) -- ^ Global symbol
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty Const where
|
||
pretty (CInt negative int) | negative = pretty '-' <> pretty int
|
||
| otherwise = pretty int
|
||
pretty (CSingle float) = "s_" <> pretty float
|
||
pretty (CDouble double) = "d_" <> pretty double
|
||
pretty (CGlobal ident) = pretty ident
|
||
|
||
-- * Linkage
|
||
------------
|
||
|
||
data Linkage
|
||
= Export -- ^ Marks the defined item as visible outside the current file's scope
|
||
| Section ShortText (Maybe Text) -- ^ Section name, with optional linker flags
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty Linkage where
|
||
pretty Export = "export"
|
||
pretty (Section secName Nothing) = "section" <+> pretty (TS.toText secName)
|
||
pretty (Section secName (Just secFlags)) =
|
||
"section" <+> pretty (TS.toText secName) <+> pretty secFlags
|
||
|
||
-- * Definitions
|
||
----------------
|
||
|
||
type Alignment = Word64
|
||
type Size = Word64
|
||
type Amount = Word64
|
||
|
||
-- ** Aggregate types
|
||
---------------------
|
||
|
||
-- | Aggregate type
|
||
data TypeDef
|
||
= TypeDef (Ident 'AggregateTy) (Maybe Alignment) [(SubTy, Maybe Amount)]
|
||
| Opaque (Ident 'AggregateTy) Alignment Size
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty TypeDef where
|
||
pretty (TypeDef ident alignment def) =
|
||
"type" <+> pretty ident <+> equals
|
||
<> maybe mempty (\x -> space <> pretty x) alignment
|
||
<+> braced (prettyItem <$> def)
|
||
where
|
||
prettyItem (subTy, Nothing ) = pretty subTy
|
||
prettyItem (subTy, Just amount) = pretty subTy <+> pretty amount
|
||
pretty (Opaque ident alignment size) =
|
||
"type" <+> pretty ident <+> equals
|
||
<+> "align" <+> pretty alignment <+> braces (pretty size)
|
||
|
||
-- | A type that can be part of an aggregate type
|
||
data SubTy
|
||
= SubExtTy ExtTy
|
||
| SubAggregateTy (Ident 'AggregateTy)
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty SubTy where
|
||
pretty (SubExtTy extTy) = pretty extTy
|
||
pretty (SubAggregateTy ident) = pretty ident
|
||
|
||
-- ** Data
|
||
----------
|
||
|
||
-- | Global object definition
|
||
data DataDef = DataDef [Linkage] (Ident 'Global) (Maybe Alignment) [Field]
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty DataDef where
|
||
pretty (DataDef linkage ident alignment fields) = vsep
|
||
[ vsep $ pretty <$> linkage
|
||
, hsep $ ("data" <+> pretty ident <+> equals)
|
||
: maybeToList (("align" <+>) . pretty <$> alignment)
|
||
, braced (pretty <$> fields)
|
||
]
|
||
|
||
data DataItem
|
||
= Symbol (Ident 'Global) (Maybe Alignment)
|
||
| String ByteString
|
||
| Const Const
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty DataItem where
|
||
pretty (Symbol ident alignment) =
|
||
hsep $ pretty ident : maybeToList ((pretty '+' <+>) . pretty <$> alignment)
|
||
pretty (String bs) = pretty $ show bs -- HACK: hoping that the escape sequences are the same...
|
||
pretty (Const c) = pretty c
|
||
|
||
data Field
|
||
= FieldExtTy ExtTy (NonEmpty DataItem)
|
||
| FieldZero Size
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty Field where
|
||
pretty (FieldExtTy extTy items) = pretty extTy <+> hsep (toList $ pretty <$> items)
|
||
pretty (FieldZero size) = pretty 'z' <+> pretty size
|
||
|
||
-- ** Functions
|
||
---------------
|
||
|
||
-- TODO use record syntax on long types like this one
|
||
-- | Function definition. The 'Maybe (Ident \'Temporary)' is the environment
|
||
data FuncDef = FuncDef [Linkage] (Maybe AbiTy) (Ident 'Global) (Maybe (Ident 'Temporary)) [Param] Variadic (NonEmpty Block)
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty FuncDef where
|
||
pretty (FuncDef linkage abiTy ident env params variadic blocks) = vsep
|
||
[ vsep $ pretty <$> linkage
|
||
, "function" <+> pretty abiTy <+> pretty ident <+> tupled (
|
||
maybeToList (("env" <+>) . pretty <$> env)
|
||
++ fmap pretty params
|
||
++ maybeToList (prettyVariadic variadic)
|
||
) <+> lbrace
|
||
, vsep $ toList $ pretty <$> blocks
|
||
, rbrace
|
||
]
|
||
|
||
data AbiTy = AbiBaseTy BaseTy | AbiAggregateTy (Ident 'AggregateTy)
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty AbiTy where
|
||
pretty (AbiBaseTy baseTy) = pretty baseTy
|
||
pretty (AbiAggregateTy ident) = pretty ident
|
||
|
||
-- | Function parameter
|
||
data Param = Param AbiTy (Ident 'Temporary)
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty Param where
|
||
pretty (Param abiTy ident) = pretty abiTy <+> pretty ident
|
||
|
||
-- | Indicates the presence or absence of a variadic marker
|
||
data Variadic = Variadic | NoVariadic
|
||
deriving (Show, Eq)
|
||
|
||
-- | 'Variadic' → @Just "..."@
|
||
-- 'NoVariadic' → @Nothing@
|
||
prettyVariadic :: Variadic -> Maybe (Doc a)
|
||
prettyVariadic Variadic = Just "..."
|
||
prettyVariadic NoVariadic = Nothing
|
||
|
||
-- * Control
|
||
------------
|
||
|
||
-- | Value, either an immediate or a global or temporary identifier.
|
||
data Val
|
||
= ValConst Const
|
||
| ValTemporary (Ident 'Temporary)
|
||
| ValGlobal (Ident 'Global)
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty Val where
|
||
pretty (ValConst c) = pretty c
|
||
pretty (ValTemporary ident) = pretty ident
|
||
pretty (ValGlobal ident) = pretty ident
|
||
|
||
-- | Block of instructions beginning with a label and ending with a jump
|
||
data Block = Block (Ident 'Label) [Phi] [Inst] Jump
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty Block where
|
||
pretty (Block ident phis insts jump) = hang 4 $ vsep $ concat
|
||
[ [pretty ident]
|
||
, pretty <$> phis
|
||
, pretty <$> insts
|
||
, [pretty jump]
|
||
]
|
||
|
||
-- | Jump instructions
|
||
data Jump
|
||
= Jmp (Ident 'Label) -- ^ Unconditional jump
|
||
| Jnz Val (Ident 'Label) (Ident 'Label) -- ^ Conditional jump
|
||
| Ret (Maybe Val) -- ^ Function return
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty Jump where
|
||
pretty (Jmp ident) = "jmp" <+> pretty ident
|
||
pretty (Jnz val label1 label2) =
|
||
"jnz" <+> pretty val <> comma
|
||
<+> pretty label1 <> comma
|
||
<+> pretty label2
|
||
pretty (Ret val) = "ret" <+> pretty val
|
||
|
||
-- * Instructions
|
||
-----------------
|
||
|
||
-- MAYBE change [PhiArg] to Map (Ident 'Label) Val
|
||
-- | Phi instruction
|
||
data Phi = Phi Assignment [PhiArg]
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty Phi where
|
||
pretty (Phi assignment args) =
|
||
pretty assignment <+> "phi" <+> hsep (punctuate comma $ pretty <$> args)
|
||
|
||
-- | Phi instruction argument, associating a 'Val' to a 'Label'
|
||
data PhiArg = PhiArg (Ident 'Label) Val
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty PhiArg where
|
||
pretty (PhiArg label val) = pretty label <+> pretty val
|
||
|
||
-- | Instruction
|
||
data Inst
|
||
-- Arithmetic and Bits
|
||
= BinaryOp Assignment BinaryOp Val Val -- ^ Binary arithmetic and bit operations
|
||
| Neg Assignment Val -- ^ @neg@
|
||
-- Memory
|
||
-- | @stored@/@stores@/@storel@/@storew@/@storeh@/@storeb@
|
||
| Store ExtTy Val Val
|
||
-- MAYBE collapse all the Loads in a single Load constructor and just discard
|
||
-- the intrepr when unused.
|
||
-- | @loadw@/@loadl@/@loads@/@loadd@
|
||
| Load Assignment BaseTy Val
|
||
-- | @loadsw@/@loaduw@
|
||
| LoadW Assignment IntRepr Val
|
||
-- | @loadsh@/@loaduh@
|
||
| LoadH Assignment IntRepr Val
|
||
-- | @loadsb@/@loadub@
|
||
| LoadB Assignment IntRepr Val
|
||
-- Comparisons
|
||
| Compare Assignment Comparison BaseTy Val Val
|
||
-- Conversions
|
||
-- | @extsw@/@extuw@
|
||
| ExtW Assignment IntRepr Val
|
||
-- | @extsh@/@extuh@
|
||
| ExtH Assignment IntRepr Val
|
||
-- | @extsb@/@extub@
|
||
| ExtB Assignment IntRepr Val
|
||
-- | @exts@. There is only one possible instruction type, so there's
|
||
-- only an 'Ident' instead of a full 'Assignment'
|
||
| ExtS (Ident 'Temporary) Val
|
||
-- | @truncd@. There is only one possible instruction type, so there's
|
||
-- only an 'Ident' instead of a full 'Assignment'
|
||
| TruncD (Ident 'Temporary) Val
|
||
-- | @stosi@/@stoui@
|
||
| StoI Assignment IntRepr Val
|
||
-- | @dtosi@/@dtoui@
|
||
| DtoI Assignment IntRepr Val
|
||
-- | @swtof@/@uwtof@
|
||
| WtoF Assignment IntRepr Val
|
||
-- | @sltof@/@ultof@
|
||
| LtoF Assignment IntRepr Val
|
||
-- Cast and Copy
|
||
-- | @cast@
|
||
| Cast Assignment Val
|
||
-- | @copy@
|
||
| Copy Assignment Val
|
||
-- Calls
|
||
-- | @call@. The fields are: assignment, function name, environment, arguments, variadic arguments
|
||
| Call (Maybe (Ident 'Temporary, AbiTy)) Val (Maybe Val) [Arg] [Arg]
|
||
-- Variadic
|
||
-- | @vastart@, initializes a variable argument list
|
||
| VaStart (Ident 'Temporary)
|
||
-- | @vaarg@, fetches the next argument from a variable argument list
|
||
| VaArg Assignment (Ident 'Temporary)
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty Inst where
|
||
pretty (BinaryOp assignment op v1 v2) =
|
||
pretty assignment <+> pretty op <+> pretty v1 <> comma <+> pretty v2
|
||
pretty (Neg assignment v) =
|
||
pretty assignment <+> "neg" <+> pretty v
|
||
pretty (Store ty v address) =
|
||
"store" <> pretty ty <+> pretty v <> comma <+> pretty address
|
||
pretty (Load assignment loadTy addr) =
|
||
pretty assignment <+> "load" <> pretty loadTy <+> pretty addr
|
||
pretty (LoadW assignment intRepr addr) =
|
||
pretty assignment <+> "load" <> pretty intRepr <> pretty 'w' <+> pretty addr
|
||
pretty (LoadH assignment intRepr addr) =
|
||
pretty assignment <+> "load" <> pretty intRepr <> pretty 'h' <+> pretty addr
|
||
pretty (LoadB assignment intRepr addr) =
|
||
pretty assignment <+> "load" <> pretty intRepr <> pretty 'b' <+> pretty addr
|
||
pretty (Compare assignment comp compTy v1 v2) =
|
||
pretty assignment <+> pretty 'c' <> pretty comp <> pretty compTy <+> pretty v1 <> comma <+> pretty v2
|
||
pretty (ExtW assignment intRepr v) =
|
||
pretty assignment <+> "ext" <> pretty intRepr <> pretty 'w' <+> pretty v
|
||
pretty (ExtH assignment intRepr v) =
|
||
pretty assignment <+> "ext" <> pretty intRepr <> pretty 'h' <+> pretty v
|
||
pretty (ExtB assignment intRepr v) =
|
||
pretty assignment <+> "ext" <> pretty intRepr <> pretty 'b' <+> pretty v
|
||
pretty (ExtS res v) = pretty res <+> equals <> pretty 'd' <+> "exts" <+> pretty v
|
||
pretty (TruncD res v) = pretty res <+> equals <> pretty 's' <+> "truncd" <+> pretty v
|
||
pretty (StoI assignment intRepr v) = pretty assignment <+> "sto" <> pretty intRepr <> pretty 'i' <+> pretty v
|
||
pretty (DtoI assignment intRepr v) = pretty assignment <+> "dto" <> pretty intRepr <> pretty 'i' <+> pretty v
|
||
pretty (WtoF assignment intRepr v) = pretty assignment <+> pretty intRepr <> "wtof" <+> pretty v
|
||
pretty (LtoF assignment intRepr v) = pretty assignment <+> pretty intRepr <> "ltof" <+> pretty v
|
||
pretty (Cast assignment v) = pretty assignment <+> "cast" <+> pretty v
|
||
pretty (Copy assignment v) = pretty assignment <+> "copy" <+> pretty v
|
||
pretty (Call assignment func env args variadics) = hsep $
|
||
maybeToList (prettyAssignment <$> assignment) ++
|
||
[ "call"
|
||
, pretty func
|
||
, tupled $ maybeToList (("env" <+>) . pretty <$> env)
|
||
++ fmap pretty args
|
||
++ variadics'
|
||
]
|
||
where
|
||
prettyAssignment (ident, ty) = pretty ident <+> equals <> pretty ty
|
||
variadics' = if null variadics then [] else "..." : fmap pretty variadics
|
||
pretty (VaStart argList) = "vastart" <+> pretty argList
|
||
pretty (VaArg assignment argList) = pretty assignment <+> "vaarg" <+> pretty argList
|
||
|
||
-- | Represents the @%x =t@ part of an instruction.
|
||
data Assignment = Assignment (Ident 'Temporary) BaseTy
|
||
deriving (Show, Eq)
|
||
|
||
-- | Infix synonym of 'Assignment'
|
||
pattern (:=) :: Ident 'Temporary -> BaseTy -> Assignment
|
||
pattern (:=) ident ty = Assignment ident ty
|
||
|
||
instance Pretty Assignment where
|
||
pretty (Assignment ident ty) = pretty ident <+> equals <> pretty ty
|
||
|
||
-- | Integer representation
|
||
data IntRepr = Signed | Unsigned
|
||
deriving (Show, Eq)
|
||
|
||
-- | Binary arithmetic and bit operations
|
||
data BinaryOp
|
||
-- | @add@
|
||
= Add
|
||
-- | @sub@
|
||
| Sub
|
||
-- | @div@/@udiv@. @Div Signed@ gets translated to @div@, so it will work
|
||
-- also on floats
|
||
| Div IntRepr
|
||
-- | @mul@
|
||
| Mul
|
||
-- | @rem@/@urem@
|
||
| Rem IntRepr
|
||
-- | @or@
|
||
| Or
|
||
-- | @xor@
|
||
| Xor
|
||
-- | @and@
|
||
| And
|
||
-- | @sar@
|
||
| Sar
|
||
-- | @shr@
|
||
| Shr
|
||
-- | @shl@
|
||
| Shl
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty BinaryOp where
|
||
pretty Add = "add"
|
||
pretty Sub = "sub"
|
||
pretty (Div Signed) = "div"
|
||
pretty (Div Unsigned) = "udiv"
|
||
pretty Mul = "mul"
|
||
pretty (Rem Signed) = "rem"
|
||
pretty (Rem Unsigned) = "rem"
|
||
pretty Or = "or"
|
||
pretty Xor = "xor"
|
||
pretty And = "and"
|
||
pretty Sar = "sar"
|
||
pretty Shr = "shr"
|
||
pretty Shl = "shl"
|
||
|
||
-- | Comparison operators.
|
||
-- Where there's a @'Maybe' 'IntRepr'@, 'Nothing' means floating point
|
||
-- (@le@, @lt@, @ge@, @gt@), while @'Just' r@ means integer
|
||
-- (@sle@, @ule@, @slt@, @ult@...)
|
||
data Comparison
|
||
-- Universal comparison
|
||
= Eq -- ^ equality
|
||
| Ne -- ^ inequality
|
||
| Le (Maybe IntRepr) -- ^ lower or equal
|
||
| Lt (Maybe IntRepr) -- ^ lower
|
||
| Ge (Maybe IntRepr) -- ^ greater or equal
|
||
| Gt (Maybe IntRepr) -- ^ greater
|
||
-- Floating point only comparison
|
||
| O -- ^ ordered (no operand is a NaN) (floating point only)
|
||
| Uo -- ^ unordered (at least one operand is a NaN) (floating point only)
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty Comparison where
|
||
pretty Eq = "eq"
|
||
pretty Ne = "ne"
|
||
pretty (Le intRepr) = pretty intRepr <> "le"
|
||
pretty (Lt intRepr) = pretty intRepr <> "lt"
|
||
pretty (Ge intRepr) = pretty intRepr <> "ge"
|
||
pretty (Gt intRepr) = pretty intRepr <> "gt"
|
||
pretty O = "o"
|
||
pretty Uo = "uo"
|
||
|
||
instance Pretty IntRepr where
|
||
pretty Signed = pretty 's'
|
||
pretty Unsigned = pretty 'u'
|
||
|
||
-- | Function argument
|
||
data Arg = Arg AbiTy Val
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty Arg where
|
||
pretty (Arg abiTy val) = pretty abiTy <+> pretty val
|
||
|
||
-- * Program
|
||
------------
|
||
|
||
-- | Datatypre representing a QBE IL source file
|
||
data Program = Program [TypeDef] [DataDef] [FuncDef]
|
||
deriving (Show, Eq)
|
||
|
||
instance Pretty Program where
|
||
pretty (Program typeDefs dataDefs funcDefs) = vsep $ concat
|
||
[ pretty <$> typeDefs
|
||
, pretty <$> dataDefs
|
||
, pretty <$> funcDefs
|
||
]
|
||
|
||
-- * Utilities
|
||
--------------
|
||
|
||
-- | Like 'list' and 'tupled', but with braces
|
||
braced :: [Doc ann] -> Doc ann
|
||
braced = group . encloseSep (flatAlt "{ " "{")
|
||
(flatAlt " }" "}")
|
||
", "
|