-- | Function to turns bytecode graph structure back into a linear list of instructions module ByteCode.Flatten(bcFlatten) where import ByteCode.Type import ByteCode.Graph import Control.Monad.State import qualified Data.Set as Set import List(sortBy) import Debug.Trace(trace) -------------------------------------------------------------------------------------------------------- type Flattener a = GraphMonad (Set.Set GLabel) a flMark :: GLabel -> Flattener Bool flMark m = gWriteX $ \ s -> (Set.insert m s, m `Set.member` s) flIsMarked :: GLabel -> Flattener Bool flIsMarked m = gReadX $ \ s -> m `Set.member` s -------------------------------------------------------------------------------------------------------- -- | Turn bytecode represented as a graph into bytecode represented as a linear sequence -- of instructions bcFlatten :: BCModule -> BCModule bcFlatten m = m { bcmDecls = map flDecl $ bcmDecls m } -- flatten a single declaration flDecl :: BCDecl -> BCDecl flDecl (Fun n p z as cs cn pr st nd fl) = Fun n p z as (flCode cs) cn pr st nd fl flDecl x = x -- flatten a code block flCode :: Code -> Code flCode (CGraph start graph jumps) = CLinear is' where st = GState start graph jumps Set.empty is = evalState (flGraph start False) st is' = map (\i -> (i,emptyUS)) $ is ++ [END_CODE] -- flatten a program graph into a linear list of instructions flGraph :: GLabel -> Bool -> Flattener [Ins] flGraph label needsJump = do -- check the predecessors of this node incoming <- gGetJumpers label markeds <- mapM flIsMarked $ Set.toList incoming if not $ and markeds then -- not all our predecessors have been generated yet so we should wait -- and just insert a jump here (if needed) if needsJump then return [JUMP (toLabel label)] else return [] else do -- all predecessors marked so generate here flMark label node <- gGetNode label rest <- case node of GLinear ins eval next -> flLinear label ins eval next GIf true false -> flIf label true false GCase int alts mdef -> flCase label int alts mdef GReturn -> return [RETURN] GDead -> error $ "flGraph: somehow reached dead code! "++show label return $ LABEL (toLabel label) : rest -- | flatten a linear block of code flLinear :: GLabel -> [UseIns] -> Bool -> GLabel -> Flattener [Ins] flLinear label isus eval next = do ret <- gAlwaysReturns next let is = map fst isus if ret then let retins = if eval then [RETURN_EVAL] else [RETURN] in return $ is ++ retins else do rest <- flGraph next True return $ is ++ rest -- | flatten an if statement flIf :: GLabel -> GLabel -> GLabel -> Flattener [Ins] flIf label true false = do ts <- flGraph true True fs <- flGraph false False return $ JUMP_FALSE (toLabel false) : ts ++ fs -- | flatten a case statement flCase :: GLabel -> Bool -> [(Tag,GLabel)] -> Maybe GLabel -> Flattener [Ins] flCase label int alts mdef = do ais <- mapM (\(_,j) -> flGraph j False) alts dis <- case mdef of Just j -> flGraph j False Nothing -> return [] let alts' = map (\(t,j) -> (t, toLabel j)) alts mdef' = maybe Nothing (Just . toLabel) mdef sw = switch int alts' mdef' return $ sw : concat ais ++ dis -- converts a graph label to a normal label toLabel :: GLabel -> Label toLabel (GLabel label) = label -- choose the right switch instruction switch :: Bool -> [(Tag,Label)] -> Maybe Label -> Ins switch True alts (Just def) = INT_SWITCH alts def switch False alts (Just def) = LOOKUP_SWITCH alts def switch False alts Nothing = TABLE_SWITCH alts' where alts' = map snd $ sortBy (\(t,x) (u,y) -> compare t u) alts