summaryrefslogtreecommitdiff
path: root/Lower.hs
blob: 34b5285771f4ef6d46b5f36ea9d1aaff9bc8e314 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
{-# LANGUAGE TupleSections, MultiWayIf, GeneralizedNewtypeDeriving #-}
module Lower(lowerIR) where

import AST (Name)
import Control.Monad.State.Strict
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import Intermediate


-- Not yet regalloc'd
data AsmProgram' = AsmProgram' [(Label, [AsmInstr'])]
  deriving (Show)

data AsmInstr'
    = Li ARef Int
    | Mv ARef ARef
    | Arith Arith ARef ARef ARef
    | Not ARef ARef
    | Call Label
    | Jcc CCond ARef Label
    | JccR CCond ARef ARef
    -- Load/Store: 'Int' is number of BYTES.
    | Load Int ARef ARef
    | Store Int ARef ARef
    -- Allocate n r: Allocate n bytes of memory, pointer in r
    | Alloc Int ARef
    -- Memcpy d s n: Copy n bytes from s to d
    | Memcpy ARef ARef ARef
    | SysExit
  deriving (Show)

data ARef
    = ASysReg Int  -- System register
    | AReg Int  -- Virtual register
    | ALabel Name  -- Function pointer for the label
    | ASClo Name  -- Static closure object of function
    | ANone
  deriving (Show)

newtype Label = Label String
  deriving (Show)

data Arith = Add | Sub | Mul | Div | Lt | Lte | And | Or | Xor | Sll | Slr | Sar
  deriving (Show)

data CCond = CCZ | CCNZ
  deriving (Show)

type GFDMap = Map.Map Name GlobFuncDef

type BId = Int


data LowerState = LowerState
    { lsNextId :: Int }
  deriving (Show)

newtype LM a = LM {unLM :: State LowerState a}
  deriving (Functor, Applicative, Monad, MonadState LowerState)

initState :: IRProgram -> LowerState
initState (IRProgram bbs _ _) = LowerState (maxTemp + 1)
  where
    maxTemp = maximum (map maxTempB bbs)
    maxTempB (BB _ inss term) = max (maximum (map maxTempI inss)) (maxTempT term)
    maxTempI (r, ins) = max (maxTempR r) (maxTempI' ins)
    maxTempI' ins = case ins of
        IAssign r      -> maxTempR r
        IParam _       -> 0
        IClosure _     -> 0
        IData _        -> 0
        ICallC r rs    -> max (maxTempR r) (maximum (map maxTempR rs))
        IAllocClo _ rs -> maximum (map maxTempR rs)
        IDiscard r     -> maxTempR r
    maxTempT term = case term of
        IBr r _ _ -> maxTempR r
        IJmp _    -> 0
        IRet r    -> maxTempR r
        IExit     -> 0
        IUnknown  -> 0
    maxTempR r = case r of
        RConst _ -> 0
        RTemp n  -> n
        RSClo _  -> 0
        RNone    -> 0

runLM :: IRProgram -> LM a -> a
runLM initIR act = evalState (unLM act) (initState initIR)

genId :: LM Int
genId = state $ \s -> (lsNextId s, s {lsNextId = lsNextId s + 1})

genTemp :: LM ARef
genTemp = liftM AReg genId



-- Calling convention:
-- Upon function entry, the stack should look as follows:
-- - Closure item N-1
-- ...
-- - Closure item 1
-- - Closure item 0
-- - Argument M-1
-- ...
-- - Argument 1
-- - Argument 0
-- - Link register [pushed by callee]
-- Thus, the stack pointer is at the link entry.
--
-- A closure object is laid out as follows:
-- - Function pointer
-- - Number of closure items (N)
-- - Closure item 0
-- - Closure item 1
-- ...
-- - Closure item N-1


lowerIR :: IRProgram -> AsmProgram'
lowerIR origProgram =
    -- TODO: For each function, push and pop the link register
    let res1 = closuresAreParams origProgram
        IRProgram bbs gfds _ = res1

        blocks = sequence [(lab,) <$> lowerBB bb gfds
                          | bb@(BB bid _ _) <- bbs
                          , let lab = Label ("BB" ++ show bid)]
    in AsmProgram' (runLM origProgram blocks)

lowerBB :: BB -> GFDMap -> LM [AsmInstr']
lowerBB (BB _bid inss term) gfds = do
    res1 <- concat <$> mapM (\ins -> lowerIns ins gfds) inss
    res2 <- lowerTerm term
    return (res1 ++ res2)

lowerIns :: Instruction -> GFDMap -> LM [AsmInstr']
lowerIns (dest, instruction) gfds = case instruction of
    IAssign src -> do
        (inss, srcr) <- toARef src
        return $ inss ++ [Mv (toARefStore dest) srcr]

    IParam idx -> do
        offsetr <- genTemp
        return [Li offsetr (8 * (idx + 1))
               ,Arith Add offsetr offsetr (ASysReg 15)
               ,Load 8 (toARefStore dest) offsetr]

    IClosure _ -> error "Unexpected IClosure, why didn't closuresAreParams run?"

    IData _ -> error "Unsupported IData in Lower"

    ICallC closure args | Just act <- lowerBuiltin closure args -> act
                        | otherwise -> do
        (inss1, closurer) <- toARef closure
        (inss2, argrs) <- toARefs args
        eightr <- genTemp
        numitemsr <- genTemp
        numbytesr <- genTemp
        stackshiftr <- genTemp
        walkr <- genTemp
        funptrr <- genTemp
        return $
            inss1 ++ inss2 ++
            [Li eightr 8
            -- Copy the closure parameters
            ,Arith Add walkr closurer eightr
            ,Load 8 numitemsr walkr
            ,Arith Add walkr walkr eightr
            ,Arith Mul numbytesr numitemsr eightr
            ,Arith Sub (ASysReg 15) (ASysReg 15) numbytesr
            ,Memcpy (ASysReg 15) walkr numbytesr] ++
            -- Copy the function parameters
            concat [[Arith Sub (ASysReg 15) (ASysReg 15) eightr
                    ,Store 8 (ASysReg 15) ref]
                   | ref <- reverse argrs] ++
            [Load 8 funptrr closurer
            -- Do the call; don't separate these two instructions!
            ,Arith Add (ASysReg 14) (ASysReg 0) eightr
            ,JccR CCNZ (ASysReg 0) funptrr
            -- Clean up the stack
            ,Li stackshiftr (8 * length argrs)
            ,Arith Add stackshiftr stackshiftr numbytesr
            ,Arith Add (ASysReg 15) (ASysReg 15) stackshiftr
            ,Mv (toARefStore dest) (ASysReg 13)]

    IAllocClo name refs ->
        let GlobFuncDef _startBId _nargs closureSlots = gfds Map.! name
        in if | length refs /= length closureSlots ->
                    error $ "INTERNAL: Call to function '" ++ name ++ "' with " ++
                            show (length refs) ++ " closure params, while it expects " ++
                            show (length closureSlots)
              | otherwise -> do
                    let destr = toARefStore dest
                        GlobFuncDef initBId _ _ = gfds Map.! name
                    itemr <- genTemp
                    numitemsr <- genTemp
                    eightr <- genTemp
                    (inss, refs') <- toARefs refs
                    return $
                        inss ++
                        [Alloc (16 + 8 * length refs) destr
                        ,Store 8 destr (ALabel ("BB" ++ show initBId))
                        ,Li eightr 8
                        ,Arith Add itemr destr eightr
                        ,Li numitemsr (length refs')
                        ,Store 8 itemr numitemsr] ++
                        concat [[Arith Add itemr itemr eightr, Store 8 itemr ref]
                               | ref <- refs']

    IDiscard _  -> return []

lowerTerm :: Terminator -> LM [AsmInstr']
lowerTerm terminator = case terminator of
    IBr cond tg1 tg2 -> do
        (inss, condr) <- toARef cond
        return $
            inss ++
            [Jcc CCNZ condr (Label ("BB" ++ show tg1))
            ,Jcc CCNZ (ASysReg 0) (Label ("BB" ++ show tg2))]

    IJmp bid ->
        return [Jcc CCNZ (ASysReg 0) (Label ("BB" ++ show bid))]

    IRet val -> do
        (inss, valr) <- toARef val
        ptrr <- genTemp
        eightr <- genTemp
        return $
            inss ++
            [Mv (ASysReg 13) valr
            ,Load 8 ptrr (ASysReg 15)
            ,Li eightr 8
            ,Arith Add (ASysReg 15) (ASysReg 15) eightr
            ,JccR CCNZ (ASysReg 0) ptrr]

    IExit ->
        return [SysExit]

    IUnknown ->
        error "Unexpected IUnknown"

-- Currently useless because Compiler puts the RSClo in a temporary before
-- calling it.
lowerBuiltin :: Ref -> [Ref] -> Maybe (LM [AsmInstr'])
lowerBuiltin (RSClo "+") [arg1, arg2] = Just $ do
    (inss1, r1) <- toARef arg1
    (inss2, r2) <- toARef arg2
    return $ inss1 ++ inss2 ++ [Arith Add (ASysReg 13) r1 r2]
lowerBuiltin _ _ = Nothing

toARef :: Ref -> LM ([AsmInstr'], ARef)
toARef (RConst val) = genTemp >>= \r -> return ([Li r val], r)
toARef ref = return ([], toARefStore ref)

toARefs :: [Ref] -> LM ([AsmInstr'], [ARef])
toARefs refs =
    foldr (\(i, r) (inss, rs) -> (i ++ inss, r : rs)) ([], [])
        <$> mapM toARef refs

toARefStore :: Ref -> ARef
toARefStore (RConst _) = error "Cannot store to a constant immediate value"
toARefStore (RTemp r) = AReg r
toARefStore (RSClo name) = ASClo name
toARefStore RNone = ANone

closuresAreParams :: IRProgram -> IRProgram
closuresAreParams (IRProgram bbs gfds datatbl) =
    IRProgram (map convertBB bbs) gfds datatbl
  where
    argccount = floodArgCountAll gfds bbs

    convertBB (BB bid inss term) = BB bid (map (convert bid) inss) term

    convert bid (dest, IClosure n) = (dest, IParam (n + argccount Map.! bid))
    convert _ i = i

floodArgCountAll :: GFDMap -> [BB] -> Map.Map BId Int
floodArgCountAll gfds bbs = Map.unions [floodArgCount gfd bbs | gfd <- Map.elems gfds]

floodArgCount :: GlobFuncDef -> [BB] -> Map.Map BId Int
floodArgCount (GlobFuncDef initBId nargs _) bbs =
    Map.fromList [(bid, nargs) | bid <- Set.toList (reachable initBId)]
  where
    bbMap :: Map.Map BId BB
    bbMap = Map.fromList [(bid, bb) | bb@(BB bid _ _) <- bbs]

    reachable :: BId -> Set.Set BId
    reachable bid = go Set.empty [bid]
      where
        go :: Set.Set BId -> [BId] -> Set.Set BId
        go accum [] = accum
        go accum front =
            let front' = Set.fromList (concatMap nexts front) Set.\\ accum
            in go (Set.union accum (Set.fromList front)) (Set.toList front')

    nexts :: BId -> [BId]
    nexts bid = case let BB _ _ term = bbMap Map.! bid in term of
        IBr _ a b -> [a, b]
        IJmp a    -> [a]
        IRet _    -> []
        IExit     -> []
        IUnknown  -> []