llvm-pretty-bc-parser-0.2.0.0: src/Data/LLVM/BitCode/IR/Metadata.hs
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE RecursiveDo #-}
module Data.LLVM.BitCode.IR.Metadata (
parseMetadataBlock
, PartialUnnamedMd(..)
, finalizePartialUnnamedMd
, MetadataAttachments
) 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,mplus,unless,when)
import Data.Maybe (fromMaybe)
import qualified Data.Map as Map
import qualified Data.Traversable as T
-- Parsing State ---------------------------------------------------------------
data MetadataTable = MetadataTable
{ mtEntries :: MdTable
, mtNextNode :: !Int
, mtNodes :: Map.Map Int (Bool,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)
addMdValue :: Typed PValue -> MetadataTable -> MetadataTable
addMdValue tv mt = mt { mtEntries = addValue tv (mtEntries mt) }
nameNode :: Bool -> Bool -> Int -> MetadataTable -> MetadataTable
nameNode fnLocal isDistinct ix mt = mt
{ mtNodes = Map.insert ix (fnLocal,isDistinct,mtNextNode mt) (mtNodes mt)
, mtNextNode = mtNextNode mt + 1
}
addString :: String -> MetadataTable -> MetadataTable
addString str = snd . addMetadata (ValMdString str)
-- | Add a new node, that might be distinct.
addNode :: Bool -> [Maybe PValMd] -> MetadataTable -> MetadataTable
addNode isDistinct vals mt = nameNode False isDistinct ix mt'
where
(ix,mt') = addMetadata (ValMdNode vals) mt
addOldNode :: Bool -> [Typed PValue] -> MetadataTable -> MetadataTable
addOldNode fnLocal vals mt = nameNode fnLocal False ix mt'
where
(ix,mt') = addMetadata (ValMdNode [ Just (ValMdValue tv) | tv <- vals ]) mt
mdForwardRef :: [String] -> MetadataTable -> Int -> PValMd
mdForwardRef cxt mt ix = fromMaybe fallback nodeRef
where
fallback = case forwardRef cxt ix (mtEntries mt) of
Typed { typedValue = ValMd md } -> md
tv -> ValMdValue tv
reference (_,_,r) = ValMdRef r
nodeRef = reference `fmap` Map.lookup ix (mtNodes mt)
mdNodeRef :: [String] -> MetadataTable -> Int -> Int
mdNodeRef cxt mt ix =
maybe (throw (BadValueRef cxt ix)) prj (Map.lookup ix (mtNodes mt))
where
prj (_,_,x) = x
mkMdRefTable :: MetadataTable -> MdRefTable
mkMdRefTable mt = Map.mapMaybe step (mtNodes mt)
where
step (fnLocal,_,ix) = do
guard (not fnLocal)
return ix
data PartialMetadata = PartialMetadata
{ pmEntries :: MetadataTable
, pmNamedEntries :: Map.Map String [Int]
, pmNextName :: Maybe String
, pmAttachments :: MetadataAttachments
} deriving (Show)
emptyPartialMetadata :: MdTable -> PartialMetadata
emptyPartialMetadata es = PartialMetadata
{ pmEntries = emptyMetadataTable es
, pmNamedEntries = Map.empty
, pmNextName = Nothing
, pmAttachments = Map.empty
}
updateMetadataTable :: (MetadataTable -> MetadataTable)
-> (PartialMetadata -> PartialMetadata)
updateMetadataTable f pm = pm { pmEntries = f (pmEntries pm) }
setNextName :: String -> PartialMetadata -> PartialMetadata
setNextName name pm = pm { pmNextName = Just name }
addAttachment :: Int -> [(String,PValMd)] -> PartialMetadata -> PartialMetadata
addAttachment instr md pm =
pm { pmAttachments = Map.insert instr md (pmAttachments pm) }
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 :: [Maybe PValMd]
, pumDistinct :: Bool
} 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 = T.mapM (T.mapM (relabel (const requireBbEntryName)))
mkUnnamedMd vs = UnnamedMd
{ umIndex = pumIndex pum
, umValues = vs
, umDistinct = pumDistinct pum
}
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,d,ix)) = case lookupNode ref d ix of
Just pum | fnLocal -> (gs,pum:fs)
| otherwise -> (pum:gs,fs)
Nothing -> (gs,fs)
lookupNode ref d ix = do
Typed { typedValue = ValMd (ValMdNode vs) } <- Map.lookup ref es
return PartialUnnamedMd
{ pumIndex = ix
, pumValues = vs
, pumDistinct = d
}
type MetadataAttachments = Map.Map Int [(String,PValMd)]
type ParsedMetadata = ([NamedMd],([PartialUnnamedMd],[PartialUnnamedMd]),MetadataAttachments)
parsedMetadata :: PartialMetadata -> ParsedMetadata
parsedMetadata pm = (namedEntries pm, unnamedEntries pm, pmAttachments 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 `mplus` parseField r 0 string
return $! updateMetadataTable (addString str) pm
-- [type num, value num]
2 -> label "METADATA_VALUE" $ do
unless (length (recordFields r) == 2)
(fail "Invalid record")
let field = parseField r
ty <- getType =<< field 0 numeric
when (ty == PrimType Metadata || ty == PrimType Void)
(fail "invalid record")
cxt <- getContext
ix <- field 1 numeric
let tv = forwardRef cxt ix vt
return $! updateMetadataTable (addMdValue tv) pm
-- [n x md num]
3 -> label "METADATA_NODE" (parseMetadataNode False mt r pm)
-- [values]
4 -> label "METADATA_NAME" $ do
name <- parseFields r 0 char `mplus` parseField r 0 cstring
return $! setNextName name pm
-- [n x md num]
5 -> label "METADATA_DISTINCT_NODE" (parseMetadataNode True mt r pm)
-- [n x [id, name]]
6 -> label "METADATA_KIND" $ do
kind <- parseField r 0 numeric
name <- parseFields r 1 char
addKind kind name
return pm
-- [distinct, line, col, scope, inlined-at?]
7 -> label "METADATA_LOCATION" $ do
fail "not implemented"
-- [n x (type num, value num)]
8 -> label "METADATA_OLD_NODE" (parseMetadataOldNode False vt mt r pm)
-- [n x (type num, value num)]
9 -> label "METADATA_OLD_FN_NODE" (parseMetadataOldNode 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
let field = parseField r
inst <- field 0 numeric
md <- parseAttachment r
return $! addAttachment inst md pm
code -> fail ("unknown record code: " ++ show code)
parseMetadataEntry _ _ pm (abbrevDef -> Just _) =
return pm
parseMetadataEntry _ _ _ r =
fail ("unexpected: " ++ show r)
parseAttachment :: Record -> Parse [(String,PValMd)]
parseAttachment r = loop (length (recordFields r) - 1) []
where
loop 0 acc = return acc
loop n acc = do
kind <- getKind =<< parseField r (n - 1) numeric
md <- getMetadata =<< parseField r n numeric
loop (n - 2) ((kind,typedValue md) : acc)
-- | Parse a metadata node.
parseMetadataNode :: Bool -> MetadataTable -> Record -> PartialMetadata
-> Parse PartialMetadata
parseMetadataNode isDistinct mt r pm = do
ixs <- parseFields r 0 numeric
cxt <- getContext
let lkp ix | ix > 0 = Just (mdForwardRef cxt mt (ix - 1))
| otherwise = Nothing
return $! updateMetadataTable (addNode isDistinct (map lkp ixs)) pm
-- | Parse out a metadata node in the old format.
parseMetadataOldNode :: Bool -> ValueTable -> MetadataTable -> Record
-> PartialMetadata -> Parse PartialMetadata
parseMetadataOldNode fnLocal vt mt r pm = do
values <- loop =<< parseFields r 0 numeric
return $! updateMetadataTable (addOldNode 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 $ Typed (PrimType Metadata)
(ValMd (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"