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