Plan 9 from Bell Labs’s /usr/web/sources/contrib/fernan/nhc98/src/compiler98/EmitState.hs

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


{- ---------------------------------------------------------------------------
Mini-interpreter for pretty-printing bytecodes into C array declarations 
-}
module EmitState where

import Char (isLower)
import GcodeLow (foreignfun)
import List (nub,isPrefixOf)
import Tree234

--  , {-type-} EmitState
--  , emitState
--  , startEmitState

-- accumulators:
--   (1) current absolute word offset
--   (2) current relative byte offset
--   (3) current incomplete word
--   (4) label defns
--   (5) bytecode in C

-- This module, as originally written, had 2 enormous space leaks.
--  1. The EmitState was originally built in a single pass, but we used
--     one part of it early, and another part late.  The late part hung
--     around as big closures for a long time.  So, we separated out the
--     two phases into Labels (early) and Code (late).  The Code closures
--     are now very much smaller.
--  2. When the Code closure was finally evaluated, it spiked, because
--     the large number of small closures inside it were all being built
--     suddenly before any of them were actually evaluated.  These closures
--     were compositions of 'shows'.  We now keep a list of Strings instead,
--     (in reverse order) and 'concat' them afterwards.  It still spikes,
--     but the spike is about half the size.

infixl >|>


data EmitState  = ES !Int !Int Incomplete [Label] [String]
type Incomplete = (String,String,String,String)
data Label = Define GL String Int | Use String Int
data GL = Global | Local
data Pass = Labels | Code	-- to avoid space leaks, we build the
	deriving Eq		-- state value in two passes

instance Eq Label where
    -- only used to eliminate duplicate extern decls, so not real equality.
    (Use s _) == (Use t _)  = s==t
    _ == _                  = error "Misuse of equality in compiler/EmitState"

eszero = "0"

empty :: Incomplete
empty = (eszero,eszero,eszero,eszero)

first :: String -> Incomplete
first x = (x,eszero,eszero,eszero)

preSym :: String
preSym = "startLabel"

startEmitState :: Pass -> EmitState
startEmitState Labels = ES 0 0 empty [] []
startEmitState Code   = ES 0 0 empty [] [begin]
  where begin = "\nstatic Node " ++ preSym ++ "[] = {\n "
                

emitByte :: Pass -> String -> EmitState -> EmitState
emitByte Labels a (ES n 0  word     labs code) = ES n 1 word labs code
emitByte Labels a (ES n 1  word     labs code) = ES n 2 word labs code
emitByte Labels a (ES n 2  word     labs code) = ES n 3 word labs code
emitByte Labels a (ES n 3  word     labs code) = ES n 4 word labs code
emitByte Labels a (ES n 4  word     labs code) = ES (n+1) 1 word labs code
emitByte Code   a (ES n 0  word     labs code) = ES n 1 (first a) labs code
emitByte Code   a (ES n 1 (w,x,y,z) labs code) = ES n 2 (w,a,y,z) labs code
emitByte Code   a (ES n 2 (w,x,y,z) labs code) = ES n 3 (w,x,a,z) labs code
emitByte Code   a (ES n 3 (w,x,y,z) labs code) = ES n 4 (w,x,y,a) labs code
emitByte Code   a (ES n 4  word     labs code) = ES (n+1) 1 (first a) labs
                                                            (outBytes word code)

emitWord :: Pass -> String -> EmitState -> EmitState
emitWord Labels a (ES n 0 word labs code) = ES (n+1) 0 empty labs code
emitWord Code   a (ES n 0 word labs code) = ES (n+1) 0 empty labs
                                                               (outWord a code)
emitWord Labels a (ES n b word labs code) = ES (n+2) 0 empty labs code
emitWord Code   a (ES n b word labs code) = ES (n+2) 0 empty labs
                                               (outWord a (outBytes word code))

emitString :: Pass -> String -> EmitState -> EmitState
emitString pass = foldr (>|>) (emitByte pass ("0"))
                  . map (emitByte pass.show.fromEnum)

emitAlign :: Pass -> EmitState -> EmitState
emitAlign _   es@(ES n 0 word labs code) = es
emitAlign Labels (ES n b word labs code) = ES (n+1) 0 empty labs code
emitAlign Code   (ES n b word labs code) = ES (n+1) 0 empty labs
                                                           (outBytes word code)

emitAlignDouble :: Pass -> EmitState -> EmitState
emitAlignDouble pass es@(ES n 0 word labs code)
    | n `div` 2 == 0               = es
    | otherwise && pass==Labels    = ES (n+1) 0 word labs code
    | otherwise && pass==Code      = ES (n+1) 0 word labs (outBytes empty code)
emitAlignDouble Labels (ES n b word labs code) =
    emitAlignDouble Labels (ES (n+1) 0 empty labs code)
emitAlignDouble Code   (ES n b word labs code) =
    emitAlignDouble Code   (ES (n+1) 0 empty labs (outBytes word code))

defineLabel :: Pass -> GL -> String -> EmitState -> EmitState
defineLabel Labels Local  sym (ES n b word labs code) =
    ES n b word (Define Local  (sym) (n*4+b): labs) code
defineLabel Code   Local  sym (ES n b word labs code) =
    ES n b word labs (comment:code)
  where comment = "\t/* " ++ sym ++ ": (byte " ++ show b ++ ") */\n "
defineLabel Labels Global sym (ES n 0 word labs code) =
    ES n 0 word (Define Global (sym) (n*4): labs) code
defineLabel Code   Global sym (ES n 0 word labs code) =
    ES n 0 word labs (newlab:code)
  where newlab = "};\nNode " ++ sym ++ "[] = {\n "
defineLabel pass Global ss es = defineLabel pass Global ss (emitAlign pass es)

useLabel :: Pass -> String -> EmitState -> EmitState
useLabel Labels sym (ES n b word labs code) =
    emitWord Labels (wrapUse sym)
                    (ES n b word (Use (sym) (n*4+b): labs) code)
useLabel Code   sym es@(ES n b word labs code) =
    emitWord Code   (wrapUse sym) es

mentionLabel :: Pass -> String -> EmitState -> EmitState
mentionLabel Labels sym (ES n b word labs code) =
    ES n b word (Use (sym) (n*4+b): labs) code
mentionLabel Code   sym es@(ES n b word labs code) = es

wrapUse :: String -> String
wrapUse sym = "useLabel(" ++ sym ++ ")"

outBytes :: Incomplete -> [String] -> [String]
outBytes (w,x,y,z) code = four:code
  where four = " bytes2word(" ++
               w ++ ',' :
               x ++ ',' :
               y ++ ',' :
               z ++ ")\n,"

outWord :: String -> [String] -> [String]
outWord x code = wx:code
  where wx = ' ': x ++ "\n,"

(>|>) :: (a->a) -> (a->a) -> (a->a)
left >|> right = right . left

emit :: Pass -> EmitState -> [String]
emit Labels (ES _ _ _ rlabs _) =
  let labs    = reverse rlabs
      locals  = filter isLocal labs
      uses    = filter isUse labs
      externs = nub $ filter (not . definedLabel) uses
      defines = treeFromList min cmp (map defAt (filter isDefine labs))
        where cmp (k, _) (k', _) = compare k k'
              defAt (Define Local  sym _)   = (sym, minBound) -- Before any use
              defAt (Define Global sym def) = (sym, def)
      definedLabel (Use s u) = treeSearch False (\(_, d) -> u >= d) cmp defines
        where cmp (k, _) = compare s k
      isLocal (Define Local _ _) = True
      isLocal  _                 = False
      isDefine (Define _ _ _)    = True
      isDefine _                 = False
      isUse (Use _ _)            = True
      isUse    _                 = False
      doLocal (Define Local sym def) = showString "#define " . showString sym .
                                       showString "\t((void*)" .
                                       showString preSym . showChar '+' .
                                       shows def .  showString ")\n"
      doExtern (Use sym _)
	-- This is a dreadful hack for distinguishing primitives from bytecode!
        | isLower (head sym) = showString "extern void *" . showString sym .
                               showString "();\n"
	-- It is somewhat easier to distinguish foreign imports.
        | foreignfun `isPrefixOf` sym = showString "void " . showString sym .
                               showString "(void);\n"
	-- If nothing else, it must be bytecode.
        | otherwise          = showString "extern Node " . showString sym .
                               showString "[];\n"
  in
  map ($"") (map doLocal locals ++ map doExtern externs)

emit Code es =
  let (ES _ _ _ _ code) = emitAlign Code es
  in
  reverse ("};\n":code)

{- End EmitState -------------------------------------------------------------}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.