llvm-pretty-bc-parser-0.1.2.0: src/Data/LLVM/BitCode/IR/Metadata.hs
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE RecursiveDo #-}
module Data.LLVM.BitCode.IR.Metadata (
parseMetadataBlock
, PartialUnnamedMd(..)
, finalizePartialUnnamedMd
) where
import Data.LLVM.BitCode.Bitstream
import Data.LLVM.BitCode.Match
import Data.LLVM.BitCode.Parse
import Data.LLVM.BitCode.Record
import Text.LLVM.AST
import Text.LLVM.Labels
import Control.Exception (throw)
import Control.Monad (foldM,guard)
import Data.Maybe (fromMaybe)
import qualified Data.Map as Map
import qualified Data.Traversable as T
-- Kind Tables -----------------------------------------------------------------
data KindTable = KindTable
{ ktNextId :: !Int
, ktNameMap :: Map.Map String Int
} deriving (Show)
emptyKindTable :: KindTable
emptyKindTable = KindTable
{ ktNextId = 5
, ktNameMap = Map.fromList
[ ("dbg", 0)
, ("tbaa", 1)
, ("prof", 2)
, ("fpmath", 3)
, ("range", 4)
]
}
getKindId :: String -> KindTable -> (Int,KindTable)
getKindId str kt = case Map.lookup str (ktNameMap kt) of
Just i -> (i,kt)
Nothing -> (ktNextId kt, kt')
where
kt' = kt
{ ktNextId = ktNextId kt + 1
, ktNameMap = Map.insert str (ktNextId kt) (ktNameMap kt)
}
-- Parsing State ---------------------------------------------------------------
data MetadataTable = MetadataTable
{ mtEntries :: MdTable
, mtNextNode :: !Int
, mtNodes :: Map.Map Int (Bool,Int)
} deriving (Show)
emptyMetadataTable :: MdTable -> MetadataTable
emptyMetadataTable es = MetadataTable
{ mtEntries = es
, mtNextNode = 0
, mtNodes = Map.empty
}
metadata :: PValMd -> Typed PValue
metadata = Typed (PrimType Metadata) . ValMd
addMetadata :: PValMd -> MetadataTable -> (Int,MetadataTable)
addMetadata val mt = (ix, mt { mtEntries = es' })
where
(ix,es') = addValue' (metadata val) (mtEntries mt)
nameNode :: Bool -> Int -> MetadataTable -> MetadataTable
nameNode fnLocal ix mt = mt
{ mtNodes = Map.insert ix (fnLocal,mtNextNode mt) (mtNodes mt)
, mtNextNode = mtNextNode mt + 1
}
addString :: String -> MetadataTable -> MetadataTable
addString str = snd . addMetadata (ValMdString str)
addNode :: Bool -> [Typed PValue] -> MetadataTable -> MetadataTable
addNode fnLocal vals mt = nameNode fnLocal ix mt'
where
(ix,mt') = addMetadata (ValMdNode vals) mt
mdForwardRef :: [String] -> MetadataTable -> Int -> Typed PValue
mdForwardRef cxt mt ix = fromMaybe fallback nodeRef
where
fallback = forwardRef cxt ix (mtEntries mt)
reference = metadata . ValMdRef . snd
nodeRef = reference `fmap` Map.lookup ix (mtNodes mt)
mdNodeRef :: [String] -> MetadataTable -> Int -> Int
mdNodeRef cxt mt ix =
maybe (throw (BadValueRef cxt ix)) snd (Map.lookup ix (mtNodes mt))
mkMdRefTable :: MetadataTable -> MdRefTable
mkMdRefTable mt = Map.mapMaybe step (mtNodes mt)
where
step (fnLocal,ix) = do
guard (not fnLocal)
return ix
type KindMap = Map.Map Int Int
data PartialMetadata = PartialMetadata
{ pmEntries :: MetadataTable
, pmNamedEntries :: Map.Map String [Int]
, pmKindMap :: KindMap
, pmKindTable :: KindTable
, pmNextName :: Maybe String
} deriving (Show)
emptyPartialMetadata :: MdTable -> PartialMetadata
emptyPartialMetadata es = PartialMetadata
{ pmEntries = emptyMetadataTable es
, pmNamedEntries = Map.empty
, pmKindMap = Map.empty
, pmKindTable = emptyKindTable
, pmNextName = Nothing
}
updateMetadataTable :: (MetadataTable -> MetadataTable)
-> (PartialMetadata -> PartialMetadata)
updateMetadataTable f pm = pm { pmEntries = f (pmEntries pm) }
addKind :: Int -> String -> PartialMetadata -> PartialMetadata
addKind ix str pm = pm
{ pmKindMap = Map.insert ix strId (pmKindMap pm)
, pmKindTable = kt'
}
where
(strId,kt') = getKindId str (pmKindTable pm)
setNextName :: String -> PartialMetadata -> PartialMetadata
setNextName name pm = pm { pmNextName = Just name }
nameMetadata :: [Int] -> PartialMetadata -> Parse PartialMetadata
nameMetadata val pm = case pmNextName pm of
Just name -> return $! pm
{ pmNextName = Nothing
, pmNamedEntries = Map.insert name val (pmNamedEntries pm)
}
Nothing -> fail "Expected a metadata name"
namedEntries :: PartialMetadata -> [NamedMd]
namedEntries = map (uncurry NamedMd)
. Map.toList
. pmNamedEntries
data PartialUnnamedMd = PartialUnnamedMd
{ pumIndex :: Int
, pumValues :: [Typed PValue]
} deriving (Show)
finalizePartialUnnamedMd :: PartialUnnamedMd -> Parse UnnamedMd
finalizePartialUnnamedMd pum = mkUnnamedMd `fmap` fixLabels (pumValues pum)
where
-- map through the list and typed PValue to change labels to textual ones
fixLabels = mapM (T.mapM (relabel (const requireBbEntryName)))
mkUnnamedMd vs = UnnamedMd
{ umIndex = pumIndex pum
, umValues = vs
}
unnamedEntries :: PartialMetadata -> ([PartialUnnamedMd],[PartialUnnamedMd])
unnamedEntries pm = foldl resolveNode ([],[]) (Map.toList (mtNodes mt))
where
mt = pmEntries pm
es = valueEntries (mtEntries mt)
resolveNode (gs,fs) (ref,(fnLocal,ix)) = case lookupNode ref ix of
Just pum | fnLocal -> (gs,pum:fs)
| otherwise -> (pum:gs,fs)
Nothing -> (gs,fs)
lookupNode ref ix = do
Typed { typedValue = ValMd (ValMdNode vs) } <- Map.lookup ref es
return PartialUnnamedMd
{ pumIndex = ix
, pumValues = vs
}
type ParsedMetadata = ([NamedMd],([PartialUnnamedMd],[PartialUnnamedMd]))
parsedMetadata :: PartialMetadata -> ParsedMetadata
parsedMetadata pm = (namedEntries pm, unnamedEntries pm)
-- Metadata Parsing ------------------------------------------------------------
parseMetadataBlock :: ValueTable -> [Entry] -> Parse ParsedMetadata
parseMetadataBlock vt es = label "METADATA_BLOCK" $ do
ms <- getMdTable
let pm0 = emptyPartialMetadata ms
rec pm <- foldM (parseMetadataEntry vt (pmEntries pm)) pm0 es
let entries = pmEntries pm
setMdTable (mtEntries entries)
setMdRefs (mkMdRefTable entries)
return (parsedMetadata pm)
-- | Parse an entry in the metadata block.
--
-- XXX this currently relies on the constant block having been parsed already.
-- Though most bitcode examples I've seen are ordered this way, it would be nice
-- to not have to rely on it.
parseMetadataEntry :: ValueTable -> MetadataTable -> PartialMetadata -> Entry
-> Parse PartialMetadata
parseMetadataEntry vt mt pm (fromEntry -> Just r) = case recordCode r of
-- [values]
1 -> label "METADATA_STRING" $ do
str <- parseFields r 0 char
return $! updateMetadataTable (addString str) pm
-- 2 is unused.
-- 3 is unused.
-- [values]
4 -> label "METADATA_NAME" $ do
name <- parseFields r 0 char
return $! setNextName name pm
-- 5 is unused.
-- [n x [id, name]]
6 -> label "METADATA_KIND" $ do
kind <- parseField r 0 numeric
name <- parseFields r 1 char
return $! addKind kind name pm
-- 7 is unused.
-- [n x (type num, value num)]
8 -> label "METADATA_NODE" (parseMetadataNode False vt mt r pm)
-- [n x (type num, value num)]
9 -> label "METADATA_FN_NODE" (parseMetadataNode True vt mt r pm)
-- [n x mdnodes]
10 -> label "METADATA_NAMED_NODE" $ do
mdIds <- parseFields r 0 numeric
cxt <- getContext
let ids = map (mdNodeRef cxt mt) mdIds
nameMetadata ids pm
-- [m x [value, [n x [id, mdnode]]]
11 -> label "METADATA_ATTACHMENT" $ do
fail "not implemented"
code -> fail ("unknown record code: " ++ show code)
parseMetadataEntry _ _ pm (abbrevDef -> Just _) =
return pm
parseMetadataEntry _ _ _ r =
fail ("unexpected: " ++ show r)
-- | Parse out a metadata node.
parseMetadataNode :: Bool -> ValueTable -> MetadataTable -> Record
-> PartialMetadata -> Parse PartialMetadata
parseMetadataNode fnLocal vt mt r pm = do
values <- loop =<< parseFields r 0 numeric
return $! updateMetadataTable (addNode fnLocal values) pm
where
loop fs = case fs of
tyId:valId:rest -> do
cxt <- getContext
ty <- getType' tyId
val <- case ty of
PrimType Metadata -> return (mdForwardRef cxt mt valId)
-- XXX need to check for a void type here
_ -> return (forwardRef cxt valId vt)
vals <- loop rest
return (val:vals)
[] -> return []
_ -> fail "Malformed metadata node"