qute

A software analysis framework built around the QBE intermediate language

git clone https://git.8pit.net/qute.git

  1-- SPDX-FileCopyrightText: 2025-2026 Sören Tempel <soeren+git@soeren-tempel.net>
  2--
  3-- SPDX-License-Identifier: GPL-3.0-only
  4
  5module Language.QBE.Types
  6  ( -- * Identifiers
  7    UserIdent (..),
  8    LocalIdent (..),
  9    BlockIdent (..),
 10    GlobalIdent (..),
 11
 12    -- * Types
 13    BaseType (..),
 14    baseTypeByteSize,
 15    baseTypeBitSize,
 16    ExtType (..),
 17    extTypeBitSize,
 18    extTypeByteSize,
 19    SubWordType (..),
 20    SubType (..),
 21    LoadType (..),
 22    loadByteSize,
 23
 24    -- * Values
 25    Const (..),
 26    DynConst (..),
 27    Value (..),
 28
 29    -- * Definitions
 30    TypeDef (..),
 31    DataDef (..),
 32    Linkage (..),
 33    Field,
 34    AggType (..),
 35    dataSize,
 36    DataObj (..),
 37    objAlign,
 38    objSize,
 39    DataItem (..),
 40    JumpInstr (..),
 41
 42    -- * Functions
 43    FuncDef (..),
 44    FuncParam (..),
 45    FuncArg (..),
 46    Abity (..),
 47    abityToBase,
 48    Block (..),
 49    fEntry,
 50
 51    -- * Instructions
 52    Statement (..),
 53    Instr (..),
 54    VolatileInstr (..),
 55    ExtArg (..),
 56    toExtType,
 57    FloatArg (..),
 58    f2BaseType,
 59    IntArg (..),
 60    i2BaseType,
 61    IntCmpOp (..),
 62    FloatCmpOp (..),
 63    Phi (..),
 64    AllocSize (..),
 65    getSize,
 66  )
 67where
 68
 69import Data.Map (Map)
 70import Data.Map qualified as Map
 71import Data.Maybe (fromJust)
 72import Data.Word (Word64)
 73
 74-- TODO: Prefix all constructors
 75
 76newtype UserIdent = UserIdent {userIdent :: String}
 77  deriving (Eq, Ord)
 78
 79instance Show UserIdent where
 80  show (UserIdent s) = ':' : s
 81
 82newtype LocalIdent = LocalIdent {localIdent :: String}
 83  deriving (Eq, Ord)
 84
 85instance Show LocalIdent where
 86  show (LocalIdent s) = '%' : s
 87
 88newtype BlockIdent = BlockIdent {blockIdent :: String}
 89  deriving (Eq, Ord)
 90
 91instance Show BlockIdent where
 92  show (BlockIdent s) = '@' : s
 93
 94newtype GlobalIdent = GlobalIdent {globalIdent :: String}
 95  deriving (Eq, Ord)
 96
 97instance Show GlobalIdent where
 98  show (GlobalIdent s) = '$' : s
 99
100------------------------------------------------------------------------
101
102data BaseType
103  = Word
104  | Long
105  | Single
106  | Double
107  deriving (Show, Eq)
108
109baseTypeByteSize :: BaseType -> Int
110baseTypeByteSize Word = 4
111baseTypeByteSize Long = 8
112baseTypeByteSize Single = 4
113baseTypeByteSize Double = 8
114
115baseTypeBitSize :: BaseType -> Int
116baseTypeBitSize ty = baseTypeByteSize ty * 8
117
118data ExtType
119  = Base BaseType
120  | Byte
121  | HalfWord
122  deriving (Show, Eq)
123
124extTypeByteSize :: ExtType -> Int
125extTypeByteSize (Base b) = baseTypeByteSize b
126extTypeByteSize Byte = 1
127extTypeByteSize HalfWord = 2
128
129extTypeBitSize :: ExtType -> Int
130extTypeBitSize ty = extTypeByteSize ty * 8
131
132data SubWordType
133  = SignedByte
134  | UnsignedByte
135  | SignedHalf
136  | UnsignedHalf
137  deriving (Show, Eq)
138
139data Abity
140  = ABase BaseType
141  | ASubWordType SubWordType
142  | AUserDef UserIdent
143  deriving (Show, Eq)
144
145abityToBase :: Abity -> BaseType
146-- Calls with a sub-word return type define a temporary of base type
147-- w with its most significant bits unspecified.
148abityToBase (ASubWordType _) = Word
149-- When an aggregate type is used as argument type or return type, the
150-- value respectively passed or returned needs to be a pointer to a
151-- memory location holding the value.
152abityToBase (AUserDef _) = Long
153abityToBase (ABase ty) = ty
154
155data Const
156  = Number Word64
157  | SFP Float
158  | DFP Double
159  | Global GlobalIdent
160  deriving (Show, Eq)
161
162data DynConst
163  = Const Const
164  | Thread GlobalIdent
165  | Extern GlobalIdent
166  | ExternThread GlobalIdent
167  deriving (Show, Eq)
168
169data Value
170  = VConst DynConst
171  | VLocal LocalIdent
172  deriving (Show, Eq)
173
174data Linkage
175  = LExport
176  | LThread
177  | LSection String (Maybe String)
178  deriving (Show, Eq)
179
180data AllocSize
181  = AllocWord
182  | AllocLong
183  | AllocLongLong
184  deriving (Show, Eq)
185
186getSize :: AllocSize -> Int
187getSize AllocWord = 4
188getSize AllocLong = 8
189getSize AllocLongLong = 16
190
191data TypeDef
192  = TypeDef
193  { aggName :: UserIdent,
194    aggAlign :: Maybe Word64,
195    aggType :: AggType
196  }
197  deriving (Show, Eq)
198
199data SubType
200  = SExtType ExtType
201  | SUserDef UserIdent
202  deriving (Show, Eq)
203
204type Field = (SubType, Maybe Word64)
205
206-- TODO: Type for tuple
207data AggType
208  = ARegular [Field]
209  | AUnion [[Field]]
210  | AOpaque Word64
211  deriving (Show, Eq)
212
213data DataDef
214  = DataDef
215  { linkage :: [Linkage],
216    name :: GlobalIdent,
217    align :: Maybe Word64,
218    objs :: [DataObj]
219  }
220  deriving (Show, Eq)
221
222dataSize :: DataDef -> Int
223dataSize dataDef =
224  sum $ map objSize (objs dataDef)
225
226data DataObj
227  = OItem ExtType [DataItem]
228  | OZeroFill Word64
229  deriving (Show, Eq)
230
231objAlign :: DataObj -> Word64
232objAlign (OZeroFill _) = 1 :: Word64
233objAlign (OItem ty _) = fromIntegral $ extTypeByteSize ty
234
235objSize :: DataObj -> Int
236objSize (OZeroFill n) = fromIntegral n
237objSize (OItem ty items) = extTypeByteSize ty * cnt items
238  where
239    cnt :: [DataItem] -> Int
240    cnt [] = 0
241    cnt ((DString s) : xs) = length s + cnt xs
242    cnt (_ : xs) = 1 + cnt xs
243
244data DataItem
245  = DSymOff GlobalIdent Word64
246  | DString String
247  | DConst Const
248  deriving (Show, Eq)
249
250data FuncDef
251  = FuncDef
252  { fLinkage :: [Linkage],
253    fName :: GlobalIdent,
254    fStart :: BlockIdent,
255    fAbity :: Maybe Abity,
256    fParams :: [FuncParam],
257    fBlock :: Map BlockIdent Block
258  }
259  deriving (Show, Eq)
260
261fEntry :: FuncDef -> Block
262fEntry func = fromJust $ Map.lookup (fStart func) (fBlock func)
263
264data FuncParam
265  = Regular Abity LocalIdent
266  | Env LocalIdent
267  | Variadic
268  deriving (Show, Eq)
269
270data FuncArg
271  = ArgReg Abity Value
272  | ArgEnv Value
273  | ArgVar
274  deriving (Show, Eq)
275
276data JumpInstr
277  = Jump BlockIdent
278  | Jnz Value BlockIdent BlockIdent
279  | Return (Maybe Value)
280  | Halt
281  deriving (Show, Eq)
282
283data LoadType
284  = LSubWord SubWordType
285  | LBase BaseType
286  deriving (Show, Eq)
287
288-- TODO: Could/Should define this on ExtType instead.
289loadByteSize :: LoadType -> Word64
290loadByteSize (LSubWord UnsignedByte) = 1
291loadByteSize (LSubWord SignedByte) = 1
292loadByteSize (LSubWord SignedHalf) = 2
293loadByteSize (LSubWord UnsignedHalf) = 2
294loadByteSize (LBase Word) = 4
295loadByteSize (LBase Long) = 8
296loadByteSize (LBase Single) = 4
297loadByteSize (LBase Double) = 8
298
299data ExtArg
300  = ExtSingle
301  | ExtSubWord SubWordType
302  | ExtSignedWord
303  | ExtUnsignedWord
304  deriving (Show, Eq)
305
306toExtType :: ExtArg -> (Bool, ExtType)
307toExtType (ExtSubWord SignedByte) = (True, Byte)
308toExtType (ExtSubWord UnsignedByte) = (False, Byte)
309toExtType (ExtSubWord SignedHalf) = (True, HalfWord)
310toExtType (ExtSubWord UnsignedHalf) = (False, HalfWord)
311toExtType ExtSignedWord = (True, Base Word)
312toExtType ExtUnsignedWord = (False, Base Word)
313toExtType ExtSingle = (True, Base Single)
314
315data FloatArg = FDouble | FSingle
316  deriving (Show, Eq)
317
318f2BaseType :: FloatArg -> BaseType
319f2BaseType FSingle = Single
320f2BaseType FDouble = Double
321
322data IntArg = IWord | ILong
323  deriving (Show, Eq)
324
325i2BaseType :: IntArg -> BaseType
326i2BaseType IWord = Word
327i2BaseType ILong = Long
328
329-- TODO: Distinict types for floating point comparison?
330data IntCmpOp
331  = IEq
332  | INe
333  | ISle
334  | ISlt
335  | ISge
336  | ISgt
337  | IUle
338  | IUlt
339  | IUge
340  | IUgt
341  deriving (Show, Eq)
342
343data FloatCmpOp
344  = FEq
345  | FNe
346  | FLe
347  | FLt
348  | FGe
349  | FGt
350  | FOrd
351  | FUnord
352  deriving (Show, Eq)
353
354data Instr
355  = Add Value Value
356  | Sub Value Value
357  | Div Value Value
358  | Mul Value Value
359  | Neg Value
360  | URem Value Value
361  | Rem Value Value
362  | UDiv Value Value
363  | Or Value Value
364  | Xor Value Value
365  | And Value Value
366  | Sar Value Value
367  | Shr Value Value
368  | Shl Value Value
369  | Alloc AllocSize Value
370  | Load LoadType Value
371  | CompareInt IntArg IntCmpOp Value Value
372  | CompareFloat FloatArg FloatCmpOp Value Value
373  | Ext ExtArg Value
374  | FloatToInt FloatArg Bool Value
375  | IntToFloat IntArg Bool Value
376  | TruncDouble Value
377  | Cast Value
378  | Copy Value
379  | VAArg Value
380  deriving (Show, Eq)
381
382data VolatileInstr
383  = Store ExtType Value Value
384  | VAStart Value
385  | Blit Value Value Word64
386  | DBGLoc Word64 Word64 (Maybe Word64)
387  deriving (Show, Eq)
388
389data Statement
390  = Assign LocalIdent BaseType Instr
391  | Call (Maybe (LocalIdent, Abity)) Value [FuncArg]
392  | Volatile VolatileInstr
393  deriving (Show, Eq)
394
395data Phi
396  = Phi
397  { pName :: LocalIdent,
398    pType :: BaseType,
399    pLabels :: Map BlockIdent Value
400  }
401  deriving (Show, Eq)
402
403data Block
404  = Block
405  { label :: BlockIdent, -- TODO: Consider removing this (part of the Map)
406    phi :: [Phi],
407    stmt :: [Statement],
408    term :: JumpInstr
409  }
410  deriving (Show, Eq)