quebex

A software analysis framework built around the QBE intermediate language

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

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