ddc-core-llvm-0.4.3.1: DDC/Core/Llvm/Convert/Exp/Case.hs
module DDC.Core.Llvm.Convert.Exp.Case
(convertCase)
where
import DDC.Core.Llvm.Convert.Exp.Atom
import DDC.Core.Llvm.Convert.Context
import DDC.Core.Llvm.Convert.Base
import DDC.Llvm.Syntax
import DDC.Core.Salt.Platform
import DDC.Data.ListUtils
import Control.Monad
import Data.Maybe
import Data.Sequence (Seq, (<|), (|>), (><))
import qualified DDC.Core.Salt as A
import qualified DDC.Core.Exp as C
import qualified Data.Sequence as Seq
-- Case -------------------------------------------------------------------------------------------
convertCase
:: Context -- ^ Context of the conversion.
-> ExpContext -- ^ Expression context.
-> Label -- ^ Label of current block
-> Seq AnnotInstr -- ^ Instructions to prepend to initial block.
-> A.Exp -- ^ Scrutinee of case expression.
-> [A.Alt] -- ^ Alternatives of case expression.
-> ConvertM (Seq Block)
convertCase ctx ectx label instrs xScrut alts
| Just mVar <- takeLocalV ctx xScrut
= do
vScrut' <- mVar
-- Convert all the alternatives.
-- If we're in a nested context we'll also get a block to join the
-- results of each alternative.
(alts', blocksJoin)
<- convertAlts ctx ectx alts
-- Determine what default alternative to use for the instruction.
(lDefault, blocksDefault)
<- case last alts' of
AltDefault l bs -> return (l, bs)
AltCase _ l bs -> return (l, bs)
-- Get the alternatives before the default one.
-- This will fail if there are no alternatives at all.
altsTable
<- case takeInit alts' of
Nothing -> throw $ ErrorInvalidExp (A.XCase xScrut alts) Nothing
Just as -> return as
-- Build the jump table of non-default alts.
let table = mapMaybe takeAltCase altsTable
let blocksTable = join $ fmap altResultBlocks $ Seq.fromList altsTable
let switchBlock
= Block label
$ instrs
|> (annotNil $ ISwitch (XVar vScrut') lDefault table)
return $ switchBlock
<| (blocksTable >< blocksDefault >< blocksJoin)
| otherwise
= throw $ ErrorInvalidExp (A.XCase xScrut alts) Nothing
-- Alts -------------------------------------------------------------------------------------------
-- | Convert some case alternatives to LLVM.
convertAlts
:: Context -> ExpContext
-> [A.Alt]
-> ConvertM ([AltResult], Seq Block)
-- Alternatives are at top level.
convertAlts ctx ectx@ExpTop{} alts
= do
alts' <- mapM (convertAlt ctx ectx) alts
return (alts', Seq.empty)
-- If we're doing a branch inside a let-binding we need to add a join
-- point to collect the results from each altenative before continuing
-- on to evaluate the rest.
convertAlts ctx (ExpNest ectx vDst lCont) alts
= do
-- Label of the block that does the join.
lJoin <- newUniqueLabel "join"
-- Convert all the alternatives,
-- assiging their results into separate vars.
(vDstAlts, alts'@(_:_))
<- liftM unzip
$ mapM (\alt -> do
vDst' <- newUniqueNamedVar "alt" (typeOfVar vDst)
alt' <- convertAlt ctx (ExpNest ectx vDst' lJoin) alt
lAlt <- return (altResultLabel alt')
return ((XVar vDst', lAlt), alt'))
$ alts
-- A block to join the result from each alternative.
let blockJoin
= Block lJoin
$ Seq.fromList $ map annotNil
[ IPhi vDst vDstAlts
, IBranch lCont ]
return (alts', Seq.singleton blockJoin)
-- Cannot convert alternative in this context.
convertAlts _ ExpAssign{} alts
= throw $ ErrorInvalidAlt alts
$ Just "Cannot convert alternative in this context."
-- Alt --------------------------------------------------------------------------------------------
-- | Convert a case alternative to LLVM.
--
-- This only works for zero-arity constructors.
-- The client should extract the fields of algebraic data objects manually.
convertAlt
:: Context -> ExpContext
-> A.Alt
-> ConvertM AltResult
convertAlt ctx ectx aa
= let pp = contextPlatform ctx
convBodyM = contextConvertBody ctx
in case aa of
A.AAlt A.PDefault x
-> do label <- newUniqueLabel "default"
blocks <- convBodyM ctx ectx Seq.empty label Seq.empty x
return $ AltDefault label blocks
A.AAlt (A.PData C.DaConUnit []) x
-> do label <- newUniqueLabel "alt"
blocks <- convBodyM ctx ectx Seq.empty label Seq.empty x
return $ AltDefault label blocks
A.AAlt (A.PData dc []) x
| Just n <- A.takeNameOfDaCon dc
, Just lit <- convPatName pp n
-> do label <- newUniqueLabel "alt"
blocks <- convBodyM ctx ectx Seq.empty label Seq.empty x
return $ AltCase lit label blocks
_ -> throw $ ErrorInvalidAlt [aa] Nothing
-- | Convert a constructor name from a pattern to a LLVM literal.
--
-- Only integral-ish types can be used as patterns, for others
-- such as Floats we rely on the Lite transform to have expanded
-- cases on float literals into a sequence of boolean checks.
convPatName :: Platform -> A.Name -> Maybe Lit
convPatName pp (A.NamePrimLit lit)
= case lit of
A.PrimLitBool True
-> Just $ LitInt (TInt 1) 1
A.PrimLitBool False
-> Just $ LitInt (TInt 1) 0
A.PrimLitNat i
-> Just $ LitInt (TInt (8 * platformAddrBytes pp)) i
A.PrimLitInt i
-> Just $ LitInt (TInt (8 * platformAddrBytes pp)) i
A.PrimLitWord i bits
| elem bits [8, 16, 32, 64]
-> Just $ LitInt (TInt $ fromIntegral bits) i
A.PrimLitTag i
-> Just $ LitInt (TInt (8 * platformTagBytes pp)) i
_ -> Nothing
convPatName _ _
= Nothing
-- | Take the label from an `AltResult`.
altResultLabel :: AltResult -> Label
altResultLabel aa
= case aa of
AltDefault label _ -> label
AltCase _ label _ -> label
-- | Take the blocks from an `AltResult`.
altResultBlocks :: AltResult -> Seq Block
altResultBlocks aa
= case aa of
AltDefault _ blocks -> blocks
AltCase _ _ blocks -> blocks
-- | Take the `Lit` and `Label` from an `AltResult`
takeAltCase :: AltResult -> Maybe (Lit, Label)
takeAltCase ac
= case ac of
AltCase lit label _ -> Just (lit, label)
_ -> Nothing