quebex

A software analysis framework built around the QBE intermediate language

git clone https://git.8pit.net/quebex.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 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  = AllocWord
117  | AllocLong
118  | AllocLongLong
119  deriving (Show, Eq)
120
121getSize :: AllocSize -> Int
122getSize AllocWord = 4
123getSize AllocLong = 8
124getSize AllocLongLong = 16
125
126data TypeDef
127  = TypeDef
128  { aggName :: UserIdent,
129    aggAlign :: Maybe Word64,
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 Word64,
153    objs :: [DataObj]
154  }
155  deriving (Show, Eq)
156
157dataSize :: DataDef -> Int
158dataSize dataDef =
159  sum $ map objSize (objs dataDef)
160
161data DataObj
162  = OItem ExtType [DataItem]
163  | OZeroFill Word64
164  deriving (Show, Eq)
165
166objAlign :: DataObj -> Word64
167objAlign (OZeroFill _) = 1 :: Word64
168objAlign (OItem ty _) = fromIntegral $ extTypeByteSize ty
169
170objSize :: DataObj -> Int
171objSize (OZeroFill n) = fromIntegral n
172objSize (OItem ty items) = extTypeByteSize ty * cnt items
173  where
174    cnt :: [DataItem] -> Int
175    cnt [] = 0
176    cnt ((DString s) : xs) = length s + cnt xs
177    cnt (_ : xs) = 1 + cnt xs
178
179data DataItem
180  = DSymOff GlobalIdent Word64
181  | DString String
182  | DConst Const
183  deriving (Show, Eq)
184
185data FuncDef
186  = FuncDef
187  { fLinkage :: [Linkage],
188    fName :: GlobalIdent,
189    fAbity :: Maybe Abity,
190    fParams :: [FuncParam],
191    fBlock :: [Block] -- TODO: Use a Map here
192  }
193  deriving (Show, Eq)
194
195data FuncParam
196  = Regular Abity LocalIdent
197  | Env LocalIdent
198  | Variadic
199  deriving (Show, Eq)
200
201data FuncArg
202  = ArgReg Abity Value
203  | ArgEnv Value
204  | ArgVar
205  deriving (Show, Eq)
206
207data JumpInstr
208  = Jump BlockIdent
209  | Jnz Value BlockIdent BlockIdent
210  | Return (Maybe Value)
211  | Halt
212  deriving (Show, Eq)
213
214data LoadType
215  = LSubWord SubWordType
216  | LBase BaseType
217  deriving (Show, Eq)
218
219-- TODO: Could/Should define this on ExtType instead.
220loadByteSize :: LoadType -> Word64
221loadByteSize (LSubWord UnsignedByte) = 1
222loadByteSize (LSubWord SignedByte) = 1
223loadByteSize (LSubWord SignedHalf) = 2
224loadByteSize (LSubWord UnsignedHalf) = 2
225loadByteSize (LBase Word) = 4
226loadByteSize (LBase Long) = 8
227loadByteSize (LBase Single) = 4
228loadByteSize (LBase Double) = 8
229
230data ExtArg
231  = ExtSingle
232  | ExtSubWord SubWordType
233  | ExtSignedWord
234  | ExtUnsignedWord
235  deriving (Show, Eq)
236
237toExtType :: ExtArg -> (Bool, ExtType)
238toExtType (ExtSubWord SignedByte) = (True, Byte)
239toExtType (ExtSubWord UnsignedByte) = (False, Byte)
240toExtType (ExtSubWord SignedHalf) = (True, HalfWord)
241toExtType (ExtSubWord UnsignedHalf) = (False, HalfWord)
242toExtType ExtSignedWord = (True, Base Word)
243toExtType ExtUnsignedWord = (False, Base Word)
244toExtType ExtSingle = (True, Base Single)
245
246data FloatArg = FDouble | FSingle
247  deriving (Show, Eq)
248
249f2BaseType :: FloatArg -> BaseType
250f2BaseType FSingle = Single
251f2BaseType FDouble = Double
252
253data IntArg = IWord | ILong
254  deriving (Show, Eq)
255
256i2BaseType :: IntArg -> BaseType
257i2BaseType IWord = Word
258i2BaseType ILong = Long
259
260-- TODO: Distinict types for floating point comparison?
261data IntCmpOp
262  = IEq
263  | INe
264  | ISle
265  | ISlt
266  | ISge
267  | ISgt
268  | IUle
269  | IUlt
270  | IUge
271  | IUgt
272  deriving (Show, Eq)
273
274data FloatCmpOp
275  = FEq
276  | FNe
277  | FLe
278  | FLt
279  | FGe
280  | FGt
281  | FOrd
282  | FUnord
283  deriving (Show, Eq)
284
285data Instr
286  = Add Value Value
287  | Sub Value Value
288  | Div Value Value
289  | Mul Value Value
290  | Neg Value
291  | URem Value Value
292  | Rem Value Value
293  | UDiv Value Value
294  | Or Value Value
295  | Xor Value Value
296  | And Value Value
297  | Sar Value Value
298  | Shr Value Value
299  | Shl Value Value
300  | Alloc AllocSize Value
301  | Load LoadType Value
302  | CompareInt IntArg IntCmpOp Value Value
303  | CompareFloat FloatArg FloatCmpOp Value Value
304  | Ext ExtArg Value
305  | FloatToInt FloatArg Bool Value
306  | IntToFloat IntArg Bool Value
307  | TruncDouble Value
308  | Cast Value
309  | Copy Value
310  | VAArg Value
311  deriving (Show, Eq)
312
313data VolatileInstr
314  = Store ExtType Value Value
315  | VAStart Value
316  | Blit Value Value Word64
317  | DBGLoc Word64 Word64 (Maybe Word64)
318  deriving (Show, Eq)
319
320data Statement
321  = Assign LocalIdent BaseType Instr
322  | Call (Maybe (LocalIdent, Abity)) Value [FuncArg]
323  | Volatile VolatileInstr
324  deriving (Show, Eq)
325
326data Phi
327  = Phi
328  { pName :: LocalIdent,
329    pType :: BaseType,
330    pLabels :: Map BlockIdent Value
331  }
332  deriving (Show, Eq)
333
334data Block'
335  = Block'
336  { label' :: BlockIdent,
337    phi' :: [Phi],
338    stmt' :: [Statement],
339    term' :: Maybe JumpInstr
340  }
341  deriving (Show, Eq)
342
343insertJumps :: [Block'] -> Maybe [Block]
344insertJumps xs = foldM go [] $ zipWithNext xs
345  where
346    zipWithNext :: [a] -> [(a, Maybe a)]
347    zipWithNext [] = []
348    zipWithNext lst@(_ : t) = zip lst $ map Just t ++ [Nothing]
349
350    fromBlock' :: Block' -> JumpInstr -> Block
351    fromBlock' (Block' l p s _) = Block l p s
352
353    go :: [Block] -> (Block', Maybe Block') -> Maybe [Block]
354    go acc (x@Block' {term' = Just ji}, _) =
355      Just (acc ++ [fromBlock' x ji])
356    go acc (x@Block' {term' = Nothing}, Just nxt) =
357      Just (acc ++ [fromBlock' x (Jump $ label' nxt)])
358    go _ (Block' {term' = Nothing}, Nothing) =
359      Nothing
360
361data Block
362  = Block
363  { label :: BlockIdent,
364    phi :: [Phi],
365    stmt :: [Statement],
366    term :: JumpInstr
367  }
368  deriving (Show, Eq)