1-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>
2--
3-- SPDX-License-Identifier: GPL-3.0-only
4
5module Language.QBE.Types where
6
7import Control.Monad (foldM)
8import Data.Map (Map)
9import Data.Word (Word64)
10
11-- TODO: Prefix all constructors
12
13newtype UserIdent = UserIdent {userIdent :: String}
14 deriving (Eq, Ord)
15
16instance Show UserIdent where
17 show (UserIdent s) = ':' : s
18
19newtype LocalIdent = LocalIdent {localIdent :: String}
20 deriving (Eq, Ord)
21
22instance Show LocalIdent where
23 show (LocalIdent s) = '%' : s
24
25newtype BlockIdent = BlockIdent {blockIdent :: String}
26 deriving (Eq, Ord)
27
28instance Show BlockIdent where
29 show (BlockIdent s) = '@' : s
30
31newtype GlobalIdent = GlobalIdent {globalIdent :: String}
32 deriving (Eq, Ord)
33
34instance Show GlobalIdent where
35 show (GlobalIdent s) = '$' : s
36
37------------------------------------------------------------------------
38
39data BaseType
40 = Word
41 | Long
42 | Single
43 | Double
44 deriving (Show, Eq)
45
46baseTypeByteSize :: BaseType -> Int
47baseTypeByteSize Word = 4
48baseTypeByteSize Long = 8
49baseTypeByteSize Single = 4
50baseTypeByteSize Double = 8
51
52baseTypeBitSize :: BaseType -> Int
53baseTypeBitSize ty = baseTypeByteSize ty * 8
54
55data ExtType
56 = Base BaseType
57 | Byte
58 | HalfWord
59 deriving (Show, Eq)
60
61extTypeByteSize :: ExtType -> Int
62extTypeByteSize (Base b) = baseTypeByteSize b
63extTypeByteSize Byte = 1
64extTypeByteSize HalfWord = 2
65
66extTypeBitSize :: ExtType -> Int
67extTypeBitSize ty = extTypeByteSize ty * 8
68
69data SubWordType
70 = SignedByte
71 | UnsignedByte
72 | SignedHalf
73 | UnsignedHalf
74 deriving (Show, Eq)
75
76data Abity
77 = ABase BaseType
78 | ASubWordType SubWordType
79 | AUserDef UserIdent
80 deriving (Show, Eq)
81
82abityToBase :: Abity -> BaseType
83-- Calls with a sub-word return type define a temporary of base type
84-- w with its most significant bits unspecified.
85abityToBase (ASubWordType _) = Word
86-- When an aggregate type is used as argument type or return type, the
87-- value respectively passed or returned needs to be a pointer to a
88-- memory location holding the value.
89abityToBase (AUserDef _) = Long
90abityToBase (ABase ty) = ty
91
92data Const
93 = Number Word64
94 | SFP Float
95 | DFP Double
96 | Global GlobalIdent
97 deriving (Show, Eq)
98
99data DynConst
100 = Const Const
101 | Thread GlobalIdent
102 deriving (Show, Eq)
103
104data Value
105 = VConst DynConst
106 | VLocal LocalIdent
107 deriving (Show, Eq)
108
109data Linkage
110 = LExport
111 | LThread
112 | LSection String (Maybe String)
113 deriving (Show, Eq)
114
115data AllocSize
116 = AlignWord
117 | AlignLong
118 | AlignLongLong
119 deriving (Show, Eq)
120
121getSize :: AllocSize -> Int
122getSize AlignWord = 4
123getSize AlignLong = 8
124getSize AlignLongLong = 16
125
126data TypeDef
127 = TypeDef
128 { aggName :: UserIdent,
129 aggAlign :: Maybe AllocSize,
130 aggType :: AggType
131 }
132 deriving (Show, Eq)
133
134data SubType
135 = SExtType ExtType
136 | SUserDef UserIdent
137 deriving (Show, Eq)
138
139type Field = (SubType, Maybe Word64)
140
141-- TODO: Type for tuple
142data AggType
143 = ARegular [Field]
144 | AUnion [[Field]]
145 | AOpaque Word64
146 deriving (Show, Eq)
147
148data DataDef
149 = DataDef
150 { linkage :: [Linkage],
151 name :: GlobalIdent,
152 align :: Maybe AllocSize,
153 objs :: [DataObj]
154 }
155 deriving (Show, Eq)
156
157data DataObj
158 = OItem ExtType [DataItem]
159 | OZeroFill Word64
160 deriving (Show, Eq)
161
162data DataItem
163 = DSymbol GlobalIdent (Maybe Word64)
164 | DString String
165 | DConst Const
166 deriving (Show, Eq)
167
168data FuncDef
169 = FuncDef
170 { fLinkage :: [Linkage],
171 fName :: GlobalIdent,
172 fAbity :: Maybe Abity,
173 fParams :: [FuncParam],
174 fBlock :: [Block] -- TODO: Use a Map here
175 }
176 deriving (Show, Eq)
177
178data FuncParam
179 = Regular Abity LocalIdent
180 | Env LocalIdent
181 | Variadic
182 deriving (Show, Eq)
183
184data FuncArg
185 = ArgReg Abity Value
186 | ArgEnv Value
187 | ArgVar
188 deriving (Show, Eq)
189
190data JumpInstr
191 = Jump BlockIdent
192 | Jnz Value BlockIdent BlockIdent
193 | Return (Maybe Value)
194 | Halt
195 deriving (Show, Eq)
196
197data LoadType
198 = LSubWord SubWordType
199 | LBase BaseType
200 deriving (Show, Eq)
201
202-- TODO: Could/Should define this on ExtType instead.
203loadByteSize :: LoadType -> Word64
204loadByteSize (LSubWord UnsignedByte) = 1
205loadByteSize (LSubWord SignedByte) = 1
206loadByteSize (LSubWord SignedHalf) = 2
207loadByteSize (LSubWord UnsignedHalf) = 2
208loadByteSize (LBase Word) = 4
209loadByteSize (LBase Long) = 8
210loadByteSize (LBase Single) = 4
211loadByteSize (LBase Double) = 8
212
213data SubLongType
214 = SLSubWord SubWordType
215 | SLSignedWord
216 | SLUnsignedWord
217 deriving (Show, Eq)
218
219-- TODO: Distinict types for floating point comparison?
220data CmpOp
221 = CEq
222 | CNe
223 | CSle
224 | CSlt
225 | CSge
226 | CSgt
227 | CUle
228 | CUlt
229 | CUge
230 | CUgt
231 deriving (Show, Eq)
232
233data Instr
234 = Add Value Value
235 | Sub Value Value
236 | -- | Div Value Value
237 Mul Value Value
238 | URem Value Value
239 | Rem Value Value
240 | UDiv Value Value
241 | Or Value Value
242 | Xor Value Value
243 | And Value Value
244 | Alloc AllocSize Value
245 | Load LoadType Value
246 | Compare BaseType CmpOp Value Value
247 | Ext SubLongType Value
248 deriving (Show, Eq)
249
250data VolatileInstr
251 = Store ExtType Value Value
252 | Blit Value Value Word64
253 deriving (Show, Eq)
254
255data Statement
256 = Assign LocalIdent BaseType Instr
257 | Call (Maybe (LocalIdent, Abity)) Value [FuncArg]
258 | Volatile VolatileInstr
259 deriving (Show, Eq)
260
261data Phi
262 = Phi
263 { pName :: LocalIdent,
264 pType :: BaseType,
265 pLabels :: Map BlockIdent Value
266 }
267 deriving (Show, Eq)
268
269data Block'
270 = Block'
271 { label' :: BlockIdent,
272 phi' :: [Phi],
273 stmt' :: [Statement],
274 term' :: Maybe JumpInstr
275 }
276 deriving (Show, Eq)
277
278insertJumps :: [Block'] -> Maybe [Block]
279insertJumps xs = foldM go [] $ zipWithNext xs
280 where
281 zipWithNext :: [a] -> [(a, Maybe a)]
282 zipWithNext [] = []
283 zipWithNext lst@(_ : t) = zip lst $ map Just t ++ [Nothing]
284
285 fromBlock' :: Block' -> JumpInstr -> Block
286 fromBlock' (Block' l p s _) = Block l p s
287
288 go :: [Block] -> (Block', Maybe Block') -> Maybe [Block]
289 go acc (x@Block' {term' = Just ji}, _) =
290 Just (acc ++ [fromBlock' x ji])
291 go acc (x@Block' {term' = Nothing}, Just nxt) =
292 Just (acc ++ [fromBlock' x (Jump $ label' nxt)])
293 go _ (Block' {term' = Nothing}, Nothing) =
294 Nothing
295
296data Block
297 = Block
298 { label :: BlockIdent,
299 phi :: [Phi],
300 stmt :: [Statement],
301 term :: JumpInstr
302 }
303 deriving (Show, Eq)