1 {-# LANGUAGE OverloadedStrings #-}
2 module Mate.BasicBlocks(
10 testCFG -- added by hs to perform benches from outside
13 import Data.Binary hiding (get)
15 import qualified Data.Map as M
16 import qualified Data.Set as S
17 import qualified Data.ByteString.Lazy as B
19 import Control.Monad.State
20 import Control.Applicative
31 -- (offset in bytecode, offset to jump target, ins)
32 type OffIns = (Int, Maybe BBEnd, Instruction)
35 type BBState = S.Set Target
36 type AnalyseState = State BBState [OffIns]
39 emptyBasicBlock :: BasicBlock
40 emptyBasicBlock = BasicBlock
43 , successor = Return }
45 printMapBB :: MapBB -> IO ()
48 let keys = M.keys hmap
49 mapM_ (printfBb . flip (++) ", " . show) keys
50 printfBb "\n\nBasicBlocks:\n"
53 printMapBB' :: [BlockID] -> MapBB -> IO ()
54 printMapBB' [] _ = return ()
55 printMapBB' (i:is) hmap' = case M.lookup i hmap' of
57 printfBb $ "Block " ++ show i ++ ". len: " ++ (show $ bblength bb) ++ "\n"
58 mapM_ (printfBb . flip (++) "\n" . (++) "\t" . show) $ code bb
59 printfBb $ case successor bb of
61 FallThrough t1 -> "Sucessor: " ++ show t1 ++ "\n"
62 OneTarget t1 -> "Sucessor: " ++ show t1 ++ "\n"
63 TwoTarget t1 t2 -> "Sucessor: " ++ show t1 ++ ", " ++ show t2 ++ "\n"
65 Nothing -> error $ "BlockID " ++ show i ++ " not found."
68 testInstance :: String -> B.ByteString -> MethodSignature -> IO ()
69 testInstance cf method sig = do
70 cls <- parseClassFile cf
71 hmap <- parseMethod cls method sig
81 test_01, test_02, test_03, test_04 :: IO ()
82 test_01 = testInstance "./tests/Fib.class" "fib"
83 test_02 = testInstance "./tests/While.class" "f"
84 test_03 = testInstance "./tests/While.class" "g"
85 test_04 = testInstance "./tests/Fac.class" "fac"
89 parseMethod :: Class Direct -> B.ByteString -> MethodSignature -> IO RawMethod
90 parseMethod cls methodname sig = do
91 let method = fromMaybe
92 (error $ "method " ++ (show . toString) methodname ++ " not found")
93 (lookupMethodSig methodname sig cls)
94 let codeseg = fromMaybe
95 (error $ "codeseg " ++ (show . toString) methodname ++ " not found")
96 (attrByName method "Code")
97 let decoded = decodeMethod codeseg
98 let mapbb = testCFG decoded
99 let locals = fromIntegral (codeMaxLocals decoded)
100 let stacks = fromIntegral (codeStackSize decoded)
101 let codelen = fromIntegral (codeLength decoded)
102 let methoddirect = methodInfoToMethod (MethodInfo methodname "" sig) cls
103 let isStatic = methodIsStatic methoddirect
104 let nametype = methodNameType methoddirect
105 let argscount = methodGetArgsCount nametype + (if isStatic then 0 else 1)
107 let exceptionMap :: ExceptionMap
108 exceptionMap = foldl f M.empty $ codeExceptions decoded
112 then M.adjust (value:) key emap
113 else M.insert key [value] emap
115 key = (&&&) eStartPC eEndPC ce
116 value = (&&&) g eHandlerPC ce
118 g ce' = case eCatchType ce' of
120 x -> buildClassID cls x
122 let msig = methodSignature method
123 printfBb $ printf "BB: analysing \"%s\"\n" $ toString (methodname `B.append` ": " `B.append` encode msig)
125 return $ RawMethod mapbb exceptionMap locals stacks argscount codelen
128 testCFG :: Code -> MapBB
129 testCFG c = buildCFG (codeInstructions c) (codeExceptions c)
131 buildCFG :: [Instruction] -> [CodeException] -> MapBB
132 buildCFG xs excps = execState (mapM buildCFG' $ alltargets ++ handlerEntries) M.empty
134 (offins, targets) = runState (calculateInstructionOffset tryBlocks xs) S.empty
135 alltargets = S.toList $ S.insert 0 targets
136 tryBlocks = map (fromIntegral . eStartPC) excps
137 handlerEntries = map (fromIntegral . eHandlerPC) excps
139 buildCFG' :: Int -> State MapBB ()
141 let value = parseBasicBlock off offins
142 modify (M.insert off value)
144 parseBasicBlock :: Int -> [OffIns] -> BasicBlock
145 parseBasicBlock i insns = emptyBasicBlock
146 { code = zip offsets insonly
147 , bblength = lastoff - i + (insnLength lastins)
148 , successor = endblock }
150 (lastblock, is) = takeWhilePlusOne validins omitins insns
151 (offsets, _, insonly) = unzip3 is
152 (lastoff, Just endblock, lastins) = fromJust lastblock
154 -- also take last (non-matched) element and return it
155 takeWhilePlusOne :: (a -> Bool) -> (a -> Bool) -> [a] -> (Maybe a, [a])
156 takeWhilePlusOne _ _ [] = (Nothing, [])
157 takeWhilePlusOne p omit (x:xs)
159 | p x = second (x:) next
160 | otherwise = (Just x, [x])
162 next = takeWhilePlusOne p omit xs
164 validins :: OffIns -> Bool
165 validins (_, x, _) = isNothing x
167 omitins :: OffIns -> Bool
168 omitins (off, _, _) = off < i
171 calculateInstructionOffset :: [BlockID] -> [Instruction] -> AnalyseState
172 calculateInstructionOffset exstarts = cio' 0
174 addW16Signed i w16 = i + fromIntegral s16
175 where s16 = fromIntegral w16 :: Int16
177 cio' :: Int -> [Instruction] -> AnalyseState
178 cio' _ [] = return $ []
179 cio' off (x:xs) = case x of
180 IF _ w16 -> twotargets w16
181 IF_ICMP _ w16 -> twotargets w16
182 IF_ACMP _ w16 -> twotargets w16
183 IFNONNULL w16 -> twotargets w16
184 IFNULL w16 -> twotargets w16
185 GOTO w16 -> onetarget w16
190 _ -> if newoffset `elem` exstarts
192 modify (S.insert newoffset)
193 ((off, Just $ OneTarget newoffset, x):) <$> next
197 tailinsns <- next -- eval remaining instructions
198 isNextInsATarget <- (S.member newoffset) <$> get
199 let bbtyp = if isNextInsATarget
200 then Just $ FallThrough newoffset
202 return $ (off, bbtyp, x):tailinsns
203 notarget = ((off, Just Return, x):) <$> next
205 let jump = off `addW16Signed` w16
206 modify (S.insert jump)
207 ((off, Just $ OneTarget jump, x):) <$> next
210 modify (S.insert nojump)
211 let jump = off `addW16Signed` w16
212 modify (S.insert jump)
213 ((off, Just $ TwoTarget nojump jump, x):) <$> next
214 next = cio' newoffset xs
215 newoffset = off + insLen
216 insLen = insnLength x
218 -- TODO(bernhard): does GHC memomize results? i.e. does it calculate the size
219 -- of `NOP' only once?
220 insnLength :: Num a => Instruction -> a
221 insnLength = fromIntegral . B.length . encodeInstructions . (:[])