1-- SPDX-FileCopyrightText: 2025 Sören Tempel <soeren+git@soeren-tempel.net>2--3-- SPDX-License-Identifier: GPL-3.0-only4{-# LANGUAGE TemplateHaskell #-}5-- The code generated by template-haskell does not have type signatures.6{-# OPTIONS_GHC -fno-warn-missing-signatures #-}78-- | This module provides an implementation of the expression abstract from9-- 'Language.QBE.Simulator.Expression' which uses concrete fixed-width integer10-- values from "Data.Word" internally.11module Language.QBE.Simulator.Default.Expression12 ( RegVal (..),13 bitSize,14 fromBits,15 )16where1718import Control.Exception (assert)19import Data.Bits20 ( FiniteBits,21 finiteBitSize,22 shift,23 shiftR,24 unsafeShiftL,25 unsafeShiftR,26 xor,27 (.&.),28 (.|.),29 )30import Data.Int (Int16, Int32, Int64, Int8)31import Data.Word (Word16, Word32, Word64, Word8)32import GHC.Float33 ( castDoubleToWord64,34 castFloatToWord32,35 castWord32ToFloat,36 castWord64ToDouble,37 double2Float,38 float2Double,39 )40import Language.QBE.Simulator.Default.Generator (generateOperators)41import Language.QBE.Simulator.Expression qualified as E42import Language.QBE.Simulator.Memory qualified as MEM43import Language.QBE.Types qualified as QBE4445-- TODO: Can we just wrap base type here?46-- TODO: Do not export the constructors47data RegVal48 = VByte Word849 | VHalf Word1650 | VWord Word3251 | VLong Word6452 | VSingle Float53 | VDouble Double54 deriving (Show, Eq)5556-- | Size of the value in bits.57bitSize :: RegVal -> Int58bitSize (VByte _) = 859bitSize (VHalf _) = 1660bitSize (VWord _) = 3261bitSize (VLong _) = 6462bitSize (VSingle _) = 3263bitSize (VDouble _) = 646465-- | Create a a 'RegVal' from an 'Integer' inferring the type from a given66-- amount of bits instead of requiring the user to provide a 'QBE.ExtType',67-- as required by 'Language.QBE.Simulator.Expression.fromLit'.68fromBits :: Int -> Integer -> Maybe RegVal69fromBits 08 = Just . VHalf . fromIntegral70fromBits 16 = Just . VHalf . fromIntegral71fromBits 32 = Just . VWord . fromIntegral72fromBits 64 = Just . VLong . fromIntegral73fromBits _ = const Nothing7475fromBool :: Bool -> RegVal76fromBool True = VLong 177fromBool False = VLong 07879------------------------------------------------------------------------8081shiftInstr ::82 (RegVal -> Word32 -> Maybe RegVal) ->83 RegVal ->84 RegVal ->85 Maybe RegVal86shiftInstr shiftOp val (VWord amount) = val `shiftOp` amount87shiftInstr _ _ _ = Nothing8889toShiftAmount :: Word32 -> Word32 -> Int90toShiftAmount valBitSize amount =91 -- From the QBE specification: "The shifting amount92 -- is taken modulo the size of the result type."93 let s = fromIntegral $ amount `mod` valBitSize94 in assert (s > 0) s9596shiftSar :: RegVal -> Word32 -> Maybe RegVal97shiftSar (VWord val) amount =98 (Just . VWord . fromIntegral) $99 (fromIntegral val :: Int32) `unsafeShiftR` toShiftAmount 32 amount100shiftSar (VLong val) amount =101 (Just . VLong . fromIntegral) $102 (fromIntegral val :: Int64) `unsafeShiftR` toShiftAmount 64 amount103shiftSar _ _ = Nothing104105shiftShr :: RegVal -> Word32 -> Maybe RegVal106shiftShr (VWord val) amount =107 (Just . VWord) $ val `unsafeShiftR` toShiftAmount 32 amount108shiftShr (VLong val) amount =109 (Just . VLong) $ val `unsafeShiftR` toShiftAmount 64 amount110shiftShr _ _ = Nothing111112shiftShl :: RegVal -> Word32 -> Maybe RegVal113shiftShl (VWord val) amount =114 (Just . VWord) $ val `unsafeShiftL` toShiftAmount 32 amount115shiftShl (VLong val) amount =116 (Just . VLong) $ val `unsafeShiftL` toShiftAmount 64 amount117shiftShl _ _ = Nothing118119------------------------------------------------------------------------120121regToBytes :: RegVal -> [Word8]122regToBytes val =123 let f w =124 map125 (\off -> fromIntegral $ shiftR w off .&. 0xff)126 (take (bytesize w) $ iterate (+ 8) 0)127 in case val of128 (VByte v) -> [v]129 (VWord v) -> f v130 (VHalf v) -> f v131 (VLong v) -> f v132 (VSingle v) -> MEM.toBytes (VWord $ castFloatToWord32 v)133 (VDouble v) -> MEM.toBytes (VLong $ castDoubleToWord64 v)134 where135 bytesize :: (FiniteBits a) => a -> Int136 bytesize v = finiteBitSize v `div` 8137138regFromBytes :: QBE.LoadType -> [Word8] -> Maybe RegVal139regFromBytes ty lst =140 let f a =141 foldl142 (\acc (byte, idx) -> (fromIntegral byte `shift` (idx * 8)) .|. acc)143 0144 $ zip a [0 ..]145 in case (ty, lst) of146 (QBE.LSubWord QBE.UnsignedByte, [byte]) -> Just (VWord (fromIntegral byte))147 (QBE.LSubWord QBE.SignedByte, [byte]) -> Just (VWord $ fromIntegral (fromIntegral byte :: Int8))148 (QBE.LSubWord QBE.SignedHalf, bytes@[_, _]) -> Just (VWord $ fromIntegral (f bytes :: Int16))149 (QBE.LSubWord QBE.UnsignedHalf, bytes@[_, _]) -> Just (VWord $ fromIntegral (f bytes :: Word16))150 (QBE.LBase QBE.Word, bytes@[_, _, _, _]) -> Just (VWord $ f bytes)151 (QBE.LBase QBE.Long, bytes@[_, _, _, _, _, _, _, _]) -> Just (VLong $ f bytes)152 (QBE.LBase QBE.Single, bytes@[_, _, _, _]) ->153 Just (VSingle $ castWord32ToFloat (f bytes))154 (QBE.LBase QBE.Double, bytes@[_, _, _, _, _, _, _, _]) ->155 Just (VDouble $ castWord64ToDouble (f bytes))156 _ -> Nothing157158instance MEM.Storable RegVal Word8 where159 toBytes = regToBytes160 fromBytes = regFromBytes161162------------------------------------------------------------------------163164-- TODO: Insert the generated code directly into the instance declaration.165generateOperators166167maxValue :: RegVal -> Maybe RegVal168maxValue val =169 let bitSiz = bitSize val170 maxVal = (2 ^ bitSiz) - 1171 in fromBits bitSiz maxVal172173withZeroDiv ::174 Maybe RegVal ->175 (RegVal -> RegVal -> Maybe RegVal) ->176 RegVal ->177 RegVal ->178 Maybe RegVal179withZeroDiv defVal op lhs rhs180 | E.toWord64 rhs == 0 = defVal181 | otherwise = op lhs rhs182183-- Signed division overflow occurs when the most-negative integer is divided by -1.184withSDivOverflow ::185 Maybe RegVal ->186 (RegVal -> RegVal -> Maybe RegVal) ->187 RegVal ->188 RegVal ->189 Maybe RegVal190withSDivOverflow defVal op lhs rhs191 | E.toWord64 lhs == mostNeg && E.toWord64 rhs == minusOne = defVal192 | otherwise = op lhs rhs193 where194 numBits :: Int195 numBits =196 assert (bitSize lhs == bitSize rhs) $197 bitSize lhs198199 minusOne :: Word64200 minusOne = (2 ^ numBits) - 1201202 mostNeg :: Word64203 mostNeg = 2 ^ (numBits - 1)204205-- We could also add support for unary operators to the generator. However,206-- presently there is only one unary operator so it isn't worth it.207neg' :: RegVal -> Maybe RegVal208neg' (VWord v) = Just . VWord $ negate v209neg' (VLong v) = Just . VLong $ negate v210neg' (VSingle v) = Just . VSingle $ negate v211neg' (VDouble v) = Just . VDouble $ negate v212neg' _ = Nothing213214-- This can't be easily auto generated because the operation differs215-- based on the type.216div' :: RegVal -> RegVal -> Maybe RegVal217div' (VWord lhs) (VWord rhs) =218 (Just . VWord . fromIntegral) $219 (fromIntegral lhs :: Int32) `quot` (fromIntegral rhs :: Int32)220div' (VLong lhs) (VLong rhs) =221 (Just . VLong . fromIntegral) $222 (fromIntegral lhs :: Int64) `quot` (fromIntegral rhs :: Int64)223div' (VSingle lhs) (VSingle rhs) = (Just . VSingle) $ lhs / rhs224div' (VDouble lhs) (VDouble rhs) = (Just . VDouble) $ lhs / rhs225div' _ _ = Nothing226227instance E.ValueRepr RegVal where228 fromLit QBE.Byte n = VByte $ fromIntegral n229 fromLit QBE.HalfWord n = VHalf $ fromIntegral n230 fromLit (QBE.Base QBE.Long) n = VLong n231 fromLit (QBE.Base QBE.Word) n = VWord $ fromIntegral n232 fromLit (QBE.Base QBE.Single) n = VSingle $ castWord32ToFloat (fromIntegral n)233 fromLit (QBE.Base QBE.Double) n = VDouble $ castWord64ToDouble n234235 toWord64 (VByte v) = fromIntegral v236 toWord64 (VHalf v) = fromIntegral v237 toWord64 (VWord v) = fromIntegral v238 toWord64 (VLong v) = v239 toWord64 (VSingle v) = fromIntegral $ castFloatToWord32 v240 toWord64 (VDouble v) = castDoubleToWord64 v241242 fromFloat = VSingle243 fromDouble = VDouble244245 -- stosi246 floatToInt ty@(QBE.Base QBE.Word) True (VSingle v) =247 Just $ E.fromLit ty (fromIntegral (truncate v :: Int32))248 floatToInt ty@(QBE.Base QBE.Long) True (VSingle v) =249 Just $ E.fromLit ty (fromIntegral (truncate v :: Int64))250 -- stoui251 floatToInt ty@(QBE.Base QBE.Word) False (VSingle v) =252 Just $ E.fromLit ty (fromIntegral (truncate v :: Word32))253 floatToInt ty@(QBE.Base QBE.Long) False (VSingle v) =254 Just $ E.fromLit ty (truncate v :: Word64)255 -- dtosi256 floatToInt ty@(QBE.Base QBE.Word) True (VDouble v) =257 Just $ E.fromLit ty (fromIntegral (truncate v :: Int32))258 floatToInt ty@(QBE.Base QBE.Long) True (VDouble v) =259 Just $ E.fromLit ty (fromIntegral (truncate v :: Int64))260 -- dtoui261 floatToInt ty@(QBE.Base QBE.Word) False (VDouble v) =262 Just $ E.fromLit ty (fromIntegral (truncate v :: Word32))263 floatToInt ty@(QBE.Base QBE.Long) False (VDouble v) =264 Just $ E.fromLit ty (truncate v :: Word64)265 -- rest266 floatToInt _ _ _ = Nothing267268 -- swtof269 intToFloat ty@(QBE.Base QBE.Single) True (VWord v) =270 Just $ E.fromLit ty (fromIntegral (fromIntegral v :: Int32))271 intToFloat ty@(QBE.Base QBE.Double) True (VWord v) =272 Just $ E.fromLit ty (fromIntegral (fromIntegral v :: Int32))273 -- uwtof274 intToFloat ty@(QBE.Base QBE.Single) False (VWord v) =275 Just $ E.fromLit ty (fromIntegral (fromIntegral v :: Word32))276 intToFloat ty@(QBE.Base QBE.Double) False (VWord v) =277 Just $ E.fromLit ty (fromIntegral (fromIntegral v :: Word32))278 -- sltof279 intToFloat ty@(QBE.Base QBE.Single) True (VLong v) =280 Just $ E.fromLit ty (fromIntegral (fromIntegral v :: Int64))281 intToFloat ty@(QBE.Base QBE.Double) True (VLong v) =282 Just $ E.fromLit ty (fromIntegral (fromIntegral v :: Int64))283 -- ultof284 intToFloat ty@(QBE.Base QBE.Single) False (VLong v) =285 Just $ E.fromLit ty (fromIntegral (fromIntegral v :: Word64))286 intToFloat ty@(QBE.Base QBE.Double) False (VLong v) =287 Just $ E.fromLit ty (fromIntegral (fromIntegral v :: Word64))288 -- rest289 intToFloat _ _ _ = Nothing290291 extendFloat (VSingle v) = Just $ VDouble (float2Double v)292 extendFloat _ = Nothing293294 truncFloat (VDouble v) = Just $ VSingle (double2Float v)295 truncFloat _ = Nothing296297 getType (VByte _) = QBE.Byte298 getType (VHalf _) = QBE.HalfWord299 getType (VWord _) = QBE.Base QBE.Word300 getType (VLong _) = QBE.Base QBE.Long301 getType (VSingle _) = QBE.Base QBE.Single302 getType (VDouble _) = QBE.Base QBE.Double303304 -- TODO: Consider replacing Nothing cases with assert as this on the hot path.305 extend extTy isSigned val306 | QBE.extTypeBitSize extTy <= bitSize val = Nothing307 | otherwise =308 E.fromLit extTy309 <$> case (isSigned, val) of310 (True, VByte v) -> Just $ fromIntegral (fromIntegral v :: Int8)311 (True, VHalf v) -> Just $ fromIntegral (fromIntegral v :: Int16)312 (True, VWord v) -> Just $ fromIntegral (fromIntegral v :: Int32)313 (True, VLong v) -> Just $ fromIntegral (fromIntegral v :: Int64)314 (False, VByte v) -> Just $ fromIntegral (fromIntegral v :: Word8)315 (False, VHalf v) -> Just $ fromIntegral (fromIntegral v :: Word16)316 (False, VWord v) -> Just $ fromIntegral (fromIntegral v :: Word32)317 (False, VLong v) -> Just $ fromIntegral (fromIntegral v :: Word64)318 _ -> Nothing319320 -- TODO: Consider replacing Nothing cases with assert as this on the hot path.321 extract (QBE.Base QBE.Single) _ = Nothing322 extract (QBE.Base QBE.Double) _ = Nothing323 extract _ (VSingle _) = Nothing324 extract _ (VDouble _) = Nothing325 extract extTy v326 | QBE.extTypeBitSize extTy > bitSize v = Nothing327 | otherwise =328 let word = E.toWord64 v329 mask = (2 `unsafeShiftL` (QBE.extTypeBitSize extTy - 1)) - 1330 in Just $ E.fromLit extTy (word .&. mask)331332 -- This is needed to align the behavior of qute/ and qute-symex/ on333 -- division-by-zero. QBE does not explicitly mandate a specific behavior334 -- for this edge case. Therefore, in order to avoid extra branches in the335 -- symbolic executor, we use the behavior mandated by SMT-LIB here.336 --337 -- TODO: Move this into the Expression abstraction (just like overshift handling).338 div lhs = withZeroDiv (maxValue lhs) (withSDivOverflow (Just lhs) div') lhs339 udiv lhs = withZeroDiv (maxValue lhs) udiv' lhs340 urem lhs = withZeroDiv (Just lhs) urem' lhs341 srem lhs = withZeroDiv (Just lhs) (withSDivOverflow (fromBits (bitSize lhs) 0) srem') lhs342343 add = add'344 sub = sub'345 mul = mul'346 or = or'347 xor = xor'348 and = and'349350 neg = neg'351352 sar = shiftInstr shiftSar353 shr = shiftInstr shiftShr354 shl = shiftInstr shiftShl355356 -- TODO: Provide default implementations357 eq = eq'358 ne = ne'359 sle = sle'360 slt = slt'361 sge = sge'362 sgt = sgt'363 ule = ule'364 ult = ult'365 uge = uge'366 ugt = ugt'367368 ord (VSingle lhs) (VSingle rhs) =369 Just . fromBool $ not (isNaN lhs || isNaN rhs)370 ord (VDouble lhs) (VDouble rhs) =371 Just . fromBool $ not (isNaN lhs || isNaN rhs)372 ord _ _ = Nothing