summaryrefslogtreecommitdiff
path: root/compiler/GHC/CmmToAsm/RISCV64/Instr.hs
blob: 5ade3b5c61ed0a5d93711a958d493b94055ce771 (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
{-# LANGUAGE EmptyCase #-}
module GHC.CmmToAsm.RISCV64.Instr where

import GHC.Cmm
import GHC.Cmm.BlockId
import GHC.Cmm.Dataflow.Label
import GHC.CmmToAsm.Config
import GHC.CmmToAsm.Instr hiding (patchRegsOfInstr, takeDeltaInstr, regUsageOfInstr, isMetaInstr, jumpDestsOfInstr)
import GHC.CmmToAsm.Types
import GHC.Platform
import GHC.Platform.Reg
import GHC.Types.Unique.Supply
import GHC.Utils.Outputable
import Prelude
import GHC.Platform.Regs (freeReg)
import GHC.Cmm.CLabel

data Instr
  = -- comment pseudo-op
    COMMENT SDoc
  | MULTILINE_COMMENT SDoc
  | -- Annotated instruction. Should print <instr> # <doc>
    ANN SDoc Instr
    -- specify current stack offset for
    -- benefit of subsequent passes
  | DELTA   Int

  | -- some static data spat out during code
    -- generation.  Will be extracted before
    -- pretty-printing.
    LDATA Section RawCmmStatics
  | -- start a new basic block.  Useful during
    -- codegen, removed later.  Preceding
    -- instruction should be a jump, as per the
    -- invariants for a BasicBlock (see Cmm).
    NEWBLOCK BlockId
  | -- load immediate pseudo-instruction
    LI Reg Integer
  | -- jump pseudo-instruction
    J Target

data Target
    = TBlock BlockId
    | TLabel CLabel

allocMoreStack ::
  Int ->
  NatCmmDecl statics GHC.CmmToAsm.RISCV64.Instr.Instr ->
  UniqSM (NatCmmDecl statics GHC.CmmToAsm.RISCV64.Instr.Instr, [(BlockId, BlockId)])
allocMoreStack = error "TODO: allocMoreStack"

-- saved return address + previous fp
-- (https://pdos.csail.mit.edu/6.S081/2020/lec/l-riscv.txt)
stackFrameHeaderSize :: Int
stackFrameHeaderSize = 2 * spillSlotSize

-- | All registers are 8 byte wide.
spillSlotSize :: Int
spillSlotSize = 8

-- | The number of spill slots available without allocating more.
maxSpillSlots :: NCGConfig -> Int
maxSpillSlots config
--  = 0 -- set to zero, to see when allocMoreStack has to fire.
    = ((ncgSpillPreallocSize config - stackFrameHeaderSize)
         `div` spillSlotSize) - 1

makeFarBranches ::
  LabelMap RawCmmStatics ->
  [NatBasicBlock Instr] ->
  [NatBasicBlock Instr]
makeFarBranches _ _ = error "TODO: makeFarBranches"

-- | Get the registers that are being used by this instruction.
--      regUsage doesn't need to do any trickery for jumps and such.
--      Just state precisely the regs read and written by that insn.
--      The consequences of control flow transfers, as far as register
--      allocation goes, are taken care of by the register allocator.
regUsageOfInstr ::
  Platform ->
  Instr ->
  RegUsage
regUsageOfInstr platform instr = case instr of
    ANN _ i                  -> regUsageOfInstr platform i
    COMMENT{}                -> usage ([], [])
    MULTILINE_COMMENT{}      -> usage ([], [])
    LDATA{}                  -> usage ([], [])
    DELTA{}                  -> usage ([], [])
    NEWBLOCK{}               -> usage ([], [])
    LI reg _                 -> usage ([], [reg])
    -- Looks like J doesn't change registers (beside PC)
    -- This might be wrong.
    J{}                      -> usage ([], [])
  where
        -- filtering the usage is necessary, otherwise the register
        -- allocator will try to allocate pre-defined fixed stg
        -- registers as well, as they show up.
        usage (src, dst) = RU (filter (interesting platform) src)
                              (filter (interesting platform) dst)

        interesting :: Platform -> Reg -> Bool
        interesting _        (RegVirtual _)              = True
        interesting platform (RegReal (RealRegSingle i)) = freeReg platform i


-- | Apply a given mapping to all the register references in this
--      instruction.
patchRegsOfInstr ::
  Instr ->
  (Reg -> Reg) ->
  Instr
patchRegsOfInstr instr env = case instr of
    ANN _ i                  -> patchRegsOfInstr i env
    COMMENT{}                -> instr
    MULTILINE_COMMENT{}      -> instr
    LDATA{}                  -> instr
    DELTA{}                  -> instr
    NEWBLOCK{}               -> instr
    LI reg i                 -> LI (env reg) i
    -- Looks like J doesn't change registers (beside PC)
    -- This might be wrong.
    J{}                      -> instr


-- | Checks whether this instruction is a jump/branch instruction.
--      One that can change the flow of control in a way that the
--      register allocator needs to worry about.
isJumpishInstr ::  Instr -> Bool
isJumpishInstr COMMENT {} = False
isJumpishInstr MULTILINE_COMMENT {} = False
isJumpishInstr ANN {} = False
isJumpishInstr DELTA {} = False
isJumpishInstr LDATA {} = False
isJumpishInstr NEWBLOCK {} = False
isJumpishInstr LI {} = False
isJumpishInstr J {} = True


-- | Checks whether this instruction is a jump/branch instruction.
-- One that can change the flow of control in a way that the
-- register allocator needs to worry about.
jumpDestsOfInstr :: Instr -> [BlockId]
jumpDestsOfInstr (ANN _ i) = jumpDestsOfInstr i
jumpDestsOfInstr (J (TBlock t)) = [t]
jumpDestsOfInstr _ = []

-- | Change the destination of this jump instruction.
--      Used in the linear allocator when adding fixup blocks for join
--      points.
patchJumpInstr ::
  instr ->
  (BlockId -> BlockId) ->
  instr
patchJumpInstr _ _ = error "TODO: patchJumpInstr"

-- | An instruction to spill a register into a spill slot.
mkSpillInstr ::
  NCGConfig ->
  -- | the reg to spill
  Reg ->
  -- | the current stack delta
  Int ->
  -- | spill slot to use
  Int ->
  -- | instructions
  [instr]
mkSpillInstr _ _ _ _ = error "TODO: mkSpillInstr"

-- | An instruction to reload a register from a spill slot.
mkLoadInstr ::
  NCGConfig ->
  -- | the reg to reload.
  Reg ->
  -- | the current stack delta
  Int ->
  -- | the spill slot to use
  Int ->
  -- | instructions
  [instr]
mkLoadInstr _ _ _ _ = error "TODO: mkLoadInstr"

-- | See if this instruction is telling us the current C stack delta
takeDeltaInstr :: Instr -> Maybe Int
takeDeltaInstr (ANN _ i) = takeDeltaInstr i
takeDeltaInstr (DELTA i) = Just i
takeDeltaInstr _         = Nothing


-- | Check whether this instruction is some meta thing inserted into
--      the instruction stream for other purposes.
--
--      Not something that has to be treated as a real machine instruction
--      and have its registers allocated.
--
--      eg, comments, delta, ldata, etc.
isMetaInstr :: Instr -> Bool
isMetaInstr instr
 = case instr of
    ANN _ i     -> isMetaInstr i
    COMMENT{}   -> True
    MULTILINE_COMMENT{} -> True
    LDATA{}     -> True
    NEWBLOCK{}  -> True
    LI{}        -> False
    J{}        -> False


-- | Copy the value in a register to another one.
--      Must work for all register classes.
mkRegRegMoveInstr ::
  Platform ->
  -- | source register
  Reg ->
  -- | destination register
  Reg ->
  instr
mkRegRegMoveInstr _ _ _ = error "TODO: mkRegRegMoveInstr"

-- | Take the source and destination from this reg -> reg move instruction
--      or Nothing if it's not one
takeRegRegMoveInstr :: Instr -> Maybe (Reg, Reg)
takeRegRegMoveInstr COMMENT {} = Nothing
takeRegRegMoveInstr MULTILINE_COMMENT {} = Nothing
takeRegRegMoveInstr ANN {} = Nothing
takeRegRegMoveInstr DELTA {} = Nothing
takeRegRegMoveInstr LDATA {} = Nothing
takeRegRegMoveInstr NEWBLOCK {} = Nothing
takeRegRegMoveInstr LI {} = Nothing
takeRegRegMoveInstr J {} = Nothing

-- | Make an unconditional jump instruction.
--      For architectures with branch delay slots, its ok to put
--      a NOP after the jump. Don't fill the delay slot with an
--      instruction that references regs or you'll confuse the
--      linear allocator.
mkJumpInstr ::
  BlockId ->
  [Instr]
mkJumpInstr id = [J (TBlock id)]

-- Subtract an amount from the C stack pointer
mkStackAllocInstr ::
  Platform ->
  Int ->
  [instr]
mkStackAllocInstr _ _ = error "TODO: mkStackAllocInstr"

-- Add an amount to the C stack pointer
mkStackDeallocInstr ::
  Platform ->
  Int ->
  [instr]
mkStackDeallocInstr _ _ = error "TODO: mkStackDeallocInstr"