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  | Common GlobalIdent
166  | Extern GlobalIdent
167  | ExternThread GlobalIdent
168  deriving (Show, Eq)
169
170data Value
171  = VConst DynConst
172  | VLocal LocalIdent
173  deriving (Show, Eq)
174
175data Linkage
176  = LExport
177  | LThread
178  | LSection String (Maybe String)
179  deriving (Show, Eq)
180
181data AllocSize
182  = AllocWord
183  | AllocLong
184  | AllocLongLong
185  deriving (Show, Eq)
186
187getSize :: AllocSize -> Int
188getSize AllocWord = 4
189getSize AllocLong = 8
190getSize AllocLongLong = 16
191
192data TypeDef
193  = TypeDef
194  { aggName :: UserIdent,
195    aggAlign :: Maybe Word64,
196    aggType :: AggType
197  }
198  deriving (Show, Eq)
199
200data SubType
201  = SExtType ExtType
202  | SUserDef UserIdent
203  deriving (Show, Eq)
204
205type Field = (SubType, Maybe Word64)
206
207-- TODO: Type for tuple
208data AggType
209  = ARegular [Field]
210  | AUnion [[Field]]
211  | AOpaque Word64
212  deriving (Show, Eq)
213
214data DataDef
215  = DataDef
216  { linkage :: [Linkage],
217    name :: GlobalIdent,
218    align :: Maybe Word64,
219    objs :: [DataObj]
220  }
221  deriving (Show, Eq)
222
223dataSize :: DataDef -> Int
224dataSize dataDef =
225  sum $ map objSize (objs dataDef)
226
227data DataObj
228  = OItem ExtType [DataItem]
229  | OZeroFill Word64
230  deriving (Show, Eq)
231
232objAlign :: DataObj -> Word64
233objAlign (OZeroFill _) = 1 :: Word64
234objAlign (OItem ty _) = fromIntegral $ extTypeByteSize ty
235
236objSize :: DataObj -> Int
237objSize (OZeroFill n) = fromIntegral n
238objSize (OItem ty items) = extTypeByteSize ty * cnt items
239  where
240    cnt :: [DataItem] -> Int
241    cnt [] = 0
242    cnt ((DString s) : xs) = length s + cnt xs
243    cnt (_ : xs) = 1 + cnt xs
244
245data DataItem
246  = DSymOff GlobalIdent Word64
247  | DString String
248  | DConst Const
249  deriving (Show, Eq)
250
251data FuncDef
252  = FuncDef
253  { fLinkage :: [Linkage],
254    fName :: GlobalIdent,
255    fStart :: BlockIdent,
256    fAbity :: Maybe Abity,
257    fParams :: [FuncParam],
258    fBlock :: Map BlockIdent Block
259  }
260  deriving (Show, Eq)
261
262fEntry :: FuncDef -> Block
263fEntry func = fromJust $ Map.lookup (fStart func) (fBlock func)
264
265data FuncParam
266  = Regular Abity LocalIdent
267  | Env LocalIdent
268  | Variadic
269  deriving (Show, Eq)
270
271data FuncArg
272  = ArgReg Abity Value
273  | ArgEnv Value
274  | ArgVar
275  deriving (Show, Eq)
276
277data JumpInstr
278  = Jump BlockIdent
279  | Jnz Value BlockIdent BlockIdent
280  | Return (Maybe Value)
281  | Halt
282  deriving (Show, Eq)
283
284data LoadType
285  = LSubWord SubWordType
286  | LBase BaseType
287  deriving (Show, Eq)
288
289-- TODO: Could/Should define this on ExtType instead.
290loadByteSize :: LoadType -> Word64
291loadByteSize (LSubWord UnsignedByte) = 1
292loadByteSize (LSubWord SignedByte) = 1
293loadByteSize (LSubWord SignedHalf) = 2
294loadByteSize (LSubWord UnsignedHalf) = 2
295loadByteSize (LBase Word) = 4
296loadByteSize (LBase Long) = 8
297loadByteSize (LBase Single) = 4
298loadByteSize (LBase Double) = 8
299
300data ExtArg
301  = ExtSingle
302  | ExtSubWord SubWordType
303  | ExtSignedWord
304  | ExtUnsignedWord
305  deriving (Show, Eq)
306
307toExtType :: ExtArg -> (Bool, ExtType)
308toExtType (ExtSubWord SignedByte) = (True, Byte)
309toExtType (ExtSubWord UnsignedByte) = (False, Byte)
310toExtType (ExtSubWord SignedHalf) = (True, HalfWord)
311toExtType (ExtSubWord UnsignedHalf) = (False, HalfWord)
312toExtType ExtSignedWord = (True, Base Word)
313toExtType ExtUnsignedWord = (False, Base Word)
314toExtType ExtSingle = (True, Base Single)
315
316data FloatArg = FDouble | FSingle
317  deriving (Show, Eq)
318
319f2BaseType :: FloatArg -> BaseType
320f2BaseType FSingle = Single
321f2BaseType FDouble = Double
322
323data IntArg = IWord | ILong
324  deriving (Show, Eq)
325
326i2BaseType :: IntArg -> BaseType
327i2BaseType IWord = Word
328i2BaseType ILong = Long
329
330-- TODO: Distinict types for floating point comparison?
331data IntCmpOp
332  = IEq
333  | INe
334  | ISle
335  | ISlt
336  | ISge
337  | ISgt
338  | IUle
339  | IUlt
340  | IUge
341  | IUgt
342  deriving (Show, Eq)
343
344data FloatCmpOp
345  = FEq
346  | FNe
347  | FLe
348  | FLt
349  | FGe
350  | FGt
351  | FOrd
352  | FUnord
353  deriving (Show, Eq)
354
355data Instr
356  = Add Value Value
357  | Sub Value Value
358  | Div Value Value
359  | Mul Value Value
360  | Neg Value
361  | URem Value Value
362  | Rem Value Value
363  | UDiv Value Value
364  | Or Value Value
365  | Xor Value Value
366  | And Value Value
367  | Sar Value Value
368  | Shr Value Value
369  | Shl Value Value
370  | Alloc AllocSize Value
371  | Load LoadType Value
372  | CompareInt IntArg IntCmpOp Value Value
373  | CompareFloat FloatArg FloatCmpOp Value Value
374  | Ext ExtArg Value
375  | FloatToInt FloatArg Bool Value
376  | IntToFloat IntArg Bool Value
377  | TruncDouble Value
378  | Cast Value
379  | Copy Value
380  | VAArg Value
381  deriving (Show, Eq)
382
383data VolatileInstr
384  = Store ExtType Value Value
385  | VAStart Value
386  | Blit Value Value Word64
387  | DBGLoc Word64 Word64 (Maybe Word64)
388  deriving (Show, Eq)
389
390data Statement
391  = Assign LocalIdent BaseType Instr
392  | Call (Maybe (LocalIdent, Abity)) Value [FuncArg]
393  | Volatile VolatileInstr
394  deriving (Show, Eq)
395
396data Phi
397  = Phi
398  { pName :: LocalIdent,
399    pType :: BaseType,
400    pLabels :: Map BlockIdent Value
401  }
402  deriving (Show, Eq)
403
404data Block
405  = Block
406  { label :: BlockIdent, -- TODO: Consider removing this (part of the Map)
407    phi :: [Phi],
408    stmt :: [Statement],
409    term :: JumpInstr
410  }
411  deriving (Show, Eq)