mirror of
https://github.com/GaloisInc/macaw.git
synced 2024-11-23 16:35:02 +03:00
Merge remote-tracking branch 'origin/feature/asl' into feature/asl
This commit is contained in:
commit
a2ee426714
@ -30,6 +30,7 @@ library
|
|||||||
, ansi-wl-pprint
|
, ansi-wl-pprint
|
||||||
, asl-translator
|
, asl-translator
|
||||||
, bytestring
|
, bytestring
|
||||||
|
, bv-sized
|
||||||
, cereal
|
, cereal
|
||||||
, containers
|
, containers
|
||||||
, crucible
|
, crucible
|
||||||
|
@ -76,7 +76,6 @@ import qualified Data.ByteString as BS
|
|||||||
import qualified Data.ByteString.Lazy as LBS
|
import qualified Data.ByteString.Lazy as LBS
|
||||||
import Data.Macaw.ARM.ARMReg
|
import Data.Macaw.ARM.ARMReg
|
||||||
import Data.Macaw.ARM.Arch()
|
import Data.Macaw.ARM.Arch()
|
||||||
import Data.Macaw.AbsDomain.AbsState as MA
|
|
||||||
import Data.Macaw.CFG
|
import Data.Macaw.CFG
|
||||||
import Data.Macaw.CFG.Block
|
import Data.Macaw.CFG.Block
|
||||||
import qualified Data.Macaw.CFG.Core as MC
|
import qualified Data.Macaw.CFG.Core as MC
|
||||||
@ -93,7 +92,6 @@ import qualified Dismantle.ARM.T32 as ThumbD
|
|||||||
import Text.Printf ( printf )
|
import Text.Printf ( printf )
|
||||||
|
|
||||||
import qualified SemMC.Architecture.AArch32 as ARM
|
import qualified SemMC.Architecture.AArch32 as ARM
|
||||||
import Debug.Trace
|
|
||||||
|
|
||||||
data InstructionSet = A32I ARMD.Instruction | T32I ThumbD.Instruction
|
data InstructionSet = A32I ARMD.Instruction | T32I ThumbD.Instruction
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
@ -185,8 +183,8 @@ disassembleBlock lookupSemantics gs curPCAddr blockOff maxOffset = do
|
|||||||
Right (i, bytesRead) -> do
|
Right (i, bytesRead) -> do
|
||||||
-- FIXME: Set PSTATE.T based on whether instruction is A32I or T32I
|
-- FIXME: Set PSTATE.T based on whether instruction is A32I or T32I
|
||||||
-- traceM ("II: " ++ show i)
|
-- traceM ("II: " ++ show i)
|
||||||
let curPC = MM.segmentOffAddr seg off
|
-- let curPC = MM.segmentOffAddr seg off
|
||||||
nextPCOffset = off + bytesRead
|
let nextPCOffset = off + bytesRead
|
||||||
nextPC = MM.segmentOffAddr seg nextPCOffset
|
nextPC = MM.segmentOffAddr seg nextPCOffset
|
||||||
nextPCVal :: Value ARM.AArch32 ids (BVType 32) = MC.RelocatableValue (MM.addrWidthRepr curPCAddr) nextPC
|
nextPCVal :: Value ARM.AArch32 ids (BVType 32) = MC.RelocatableValue (MM.addrWidthRepr curPCAddr) nextPC
|
||||||
-- curPCVal :: Value ARM.AArch32 ids (BVType 32) = MC.RelocatableValue (MM.addrWidthRepr curPCAddr) curPC
|
-- curPCVal :: Value ARM.AArch32 ids (BVType 32) = MC.RelocatableValue (MM.addrWidthRepr curPCAddr) curPC
|
||||||
@ -241,18 +239,17 @@ readInstruction :: (MM.MemWidth w)
|
|||||||
-> Either (ARMMemoryError w) (InstructionSet, MM.MemWord w)
|
-> Either (ARMMemoryError w) (InstructionSet, MM.MemWord w)
|
||||||
readInstruction addr = do
|
readInstruction addr = do
|
||||||
let seg = MM.segoffSegment addr
|
let seg = MM.segoffSegment addr
|
||||||
segRelAddrRaw = MM.segoffAddr addr
|
let segRelAddr = MM.segoffAddr addr
|
||||||
-- Addresses specified in ARM instructions have the low bit
|
-- Addresses specified in ARM instructions have the low bit
|
||||||
-- clear, but Thumb (T32) target addresses have the low bit sit.
|
-- clear, but Thumb (T32) target addresses have the low bit sit.
|
||||||
-- This is only manifested in the instruction addresses: the
|
-- This is only manifested in the instruction addresses: the
|
||||||
-- actual PC for fetching instructions clears the low bit to
|
-- actual PC for fetching instructions clears the low bit to
|
||||||
-- generate aligned memory accesses.
|
-- generate aligned memory accesses.
|
||||||
loBit = MM.addrOffset segRelAddrRaw .&. 1
|
let alignedMsegOff = MM.clearSegmentOffLeastBit addr
|
||||||
segRelAddr = segRelAddrRaw { addrOffset = MM.addrOffset segRelAddrRaw `xor` loBit }
|
|
||||||
if MM.segmentFlags seg `MMP.hasPerm` MMP.execute
|
if MM.segmentFlags seg `MMP.hasPerm` MMP.execute
|
||||||
then do
|
then do
|
||||||
let ao = addrOffset segRelAddr
|
-- traceM ("Orig addr = " ++ show addr ++ " modified to " ++ show ao)
|
||||||
alignedMsegOff <- liftMaybe (ARMInvalidInstructionAddress seg ao) (MM.resolveSegmentOff seg ao)
|
-- alignedMsegOff <- liftMaybe (ARMInvalidInstructionAddress seg ao) (MM.resolveSegmentOff seg ao)
|
||||||
contents <- liftMemError $ MM.segoffContentsAfter alignedMsegOff
|
contents <- liftMemError $ MM.segoffContentsAfter alignedMsegOff
|
||||||
case contents of
|
case contents of
|
||||||
[] -> ET.throwError $ ARMMemoryError (MM.AccessViolation segRelAddr)
|
[] -> ET.throwError $ ARMMemoryError (MM.AccessViolation segRelAddr)
|
||||||
@ -276,12 +273,6 @@ readInstruction addr = do
|
|||||||
Nothing -> ET.throwError $ ARMInvalidInstruction segRelAddr contents
|
Nothing -> ET.throwError $ ARMInvalidInstruction segRelAddr contents
|
||||||
else ET.throwError $ ARMMemoryError (MM.PermissionsError segRelAddr)
|
else ET.throwError $ ARMMemoryError (MM.PermissionsError segRelAddr)
|
||||||
|
|
||||||
liftMaybe :: ARMMemoryError w -> Maybe a -> Either (ARMMemoryError w) a
|
|
||||||
liftMaybe err ma =
|
|
||||||
case ma of
|
|
||||||
Just a -> Right a
|
|
||||||
Nothing -> Left err
|
|
||||||
|
|
||||||
liftMemError :: Either (MM.MemoryError w) a -> Either (ARMMemoryError w) a
|
liftMemError :: Either (MM.MemoryError w) a -> Either (ARMMemoryError w) a
|
||||||
liftMemError e =
|
liftMemError e =
|
||||||
case e of
|
case e of
|
||||||
|
@ -10,7 +10,7 @@ module Data.Macaw.ARM.Identify
|
|||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Lens ( (^.) )
|
import Control.Lens ( (^.) )
|
||||||
import Data.Macaw.ARM.Arch
|
import qualified Data.Macaw.ARM.ARMReg as AR
|
||||||
import Data.Macaw.AbsDomain.AbsState ( AbsProcessorState
|
import Data.Macaw.AbsDomain.AbsState ( AbsProcessorState
|
||||||
, AbsValue(..)
|
, AbsValue(..)
|
||||||
, transferValue
|
, transferValue
|
||||||
@ -18,6 +18,7 @@ import Data.Macaw.AbsDomain.AbsState ( AbsProcessorState
|
|||||||
)
|
)
|
||||||
import qualified Data.Macaw.CFG as MC
|
import qualified Data.Macaw.CFG as MC
|
||||||
import qualified Data.Macaw.Memory as MM
|
import qualified Data.Macaw.Memory as MM
|
||||||
|
import qualified Data.Macaw.SemMC.Simplify as MSS
|
||||||
import qualified Data.Macaw.Types as MT
|
import qualified Data.Macaw.Types as MT
|
||||||
import Data.Semigroup
|
import Data.Semigroup
|
||||||
import qualified Data.Sequence as Seq
|
import qualified Data.Sequence as Seq
|
||||||
@ -42,8 +43,14 @@ identifyCall :: MM.Memory (MC.ArchAddrWidth ARM.AArch32)
|
|||||||
-> Seq.Seq (MC.Stmt ARM.AArch32 ids)
|
-> Seq.Seq (MC.Stmt ARM.AArch32 ids)
|
||||||
-> MC.RegState (MC.ArchReg ARM.AArch32) (MC.Value ARM.AArch32 ids)
|
-> MC.RegState (MC.ArchReg ARM.AArch32) (MC.Value ARM.AArch32 ids)
|
||||||
-> Maybe (Seq.Seq (MC.Stmt ARM.AArch32 ids), MC.ArchSegmentOff ARM.AArch32)
|
-> Maybe (Seq.Seq (MC.Stmt ARM.AArch32 ids), MC.ArchSegmentOff ARM.AArch32)
|
||||||
identifyCall _mem _stmts0 _rs = Nothing -- KWQ: for now, nothing is identified as a call
|
identifyCall mem stmts0 rs
|
||||||
|
| not (null stmts0)
|
||||||
|
, MC.RelocatableValue {} <- rs ^. MC.boundValue AR.arm_LR
|
||||||
|
, Just retVal <- MSS.simplifyValue (rs ^. MC.boundValue AR.arm_LR)
|
||||||
|
, Just retAddrVal <- MC.valueAsMemAddr retVal
|
||||||
|
, Just retAddr <- MM.asSegmentOff mem retAddrVal =
|
||||||
|
Just (stmts0, retAddr)
|
||||||
|
| otherwise = Nothing
|
||||||
|
|
||||||
-- | Intended to identify a return statement.
|
-- | Intended to identify a return statement.
|
||||||
--
|
--
|
||||||
|
@ -25,14 +25,15 @@ import qualified Control.Monad.Except as E
|
|||||||
import qualified Control.Monad.State.Strict as St
|
import qualified Control.Monad.State.Strict as St
|
||||||
import qualified Data.Map as Map
|
import qualified Data.Map as Map
|
||||||
import qualified Data.Functor.Const as C
|
import qualified Data.Functor.Const as C
|
||||||
|
import qualified Data.Bits as B
|
||||||
|
import qualified Data.BitVector.Sized as BVS
|
||||||
import Data.List (isPrefixOf)
|
import Data.List (isPrefixOf)
|
||||||
import Data.Macaw.SemMC.TH.Monad
|
import Data.Macaw.SemMC.TH.Monad
|
||||||
import Data.Macaw.ARM.ARMReg
|
import Data.Macaw.ARM.ARMReg
|
||||||
import Data.Macaw.ARM.Arch
|
import Data.Macaw.ARM.Arch
|
||||||
import qualified Data.Macaw.CFG as M
|
import qualified Data.Macaw.CFG as M
|
||||||
import qualified Data.Macaw.SemMC.Generator as G
|
import qualified Data.Macaw.SemMC.Generator as G
|
||||||
import qualified Data.Macaw.SemMC.Operands as O
|
import Data.Macaw.SemMC.TH ( addEltTH, appToExprTH, evalNonceAppTH, evalBoundVar, natReprTH, symFnName )
|
||||||
import Data.Macaw.SemMC.TH ( addEltTH, appToExprTH, evalNonceAppTH, evalBoundVar, natReprTH, symFnName, asName )
|
|
||||||
import Data.Macaw.SemMC.TH.Monad
|
import Data.Macaw.SemMC.TH.Monad
|
||||||
import qualified Data.Macaw.Types as M
|
import qualified Data.Macaw.Types as M
|
||||||
import qualified Data.Parameterized.Context as Ctx
|
import qualified Data.Parameterized.Context as Ctx
|
||||||
@ -44,13 +45,11 @@ import Data.Parameterized.Some ( Some(..) )
|
|||||||
import qualified Data.Parameterized.TraversableFC as FC
|
import qualified Data.Parameterized.TraversableFC as FC
|
||||||
import Data.Proxy ( Proxy(..) )
|
import Data.Proxy ( Proxy(..) )
|
||||||
import GHC.TypeLits as TL
|
import GHC.TypeLits as TL
|
||||||
|
import Data.Parameterized.NatRepr
|
||||||
import Language.Haskell.TH
|
import Language.Haskell.TH
|
||||||
import Language.Haskell.TH.Syntax
|
import Language.Haskell.TH.Syntax
|
||||||
import qualified SemMC.Architecture.AArch32 as ARM
|
import qualified SemMC.Architecture.AArch32 as ARM
|
||||||
import qualified SemMC.Architecture.ARM.Opcodes as ARM
|
import qualified SemMC.Architecture.ARM.Opcodes as ARM
|
||||||
import qualified SemMC.Architecture as A
|
|
||||||
import qualified SemMC.Architecture.ARM.Location as Loc
|
|
||||||
import qualified SemMC.Architecture.Location as L
|
|
||||||
import qualified What4.BaseTypes as WT
|
import qualified What4.BaseTypes as WT
|
||||||
import qualified What4.Expr.Builder as WB
|
import qualified What4.Expr.Builder as WB
|
||||||
|
|
||||||
@ -58,7 +57,6 @@ import qualified Data.Kind as Kind
|
|||||||
import Numeric.Natural
|
import Numeric.Natural
|
||||||
|
|
||||||
import qualified Language.ASL.Globals as ASL
|
import qualified Language.ASL.Globals as ASL
|
||||||
import qualified Language.ASL.Globals.Definitions as ASL
|
|
||||||
|
|
||||||
loadSemantics :: IO (ARM.ASLSemantics)
|
loadSemantics :: IO (ARM.ASLSemantics)
|
||||||
loadSemantics = ARM.loadSemantics (ARM.ASLSemanticsOpts { ARM.aslOptTrimRegs = True})
|
loadSemantics = ARM.loadSemantics (ARM.ASLSemanticsOpts { ARM.aslOptTrimRegs = True})
|
||||||
@ -155,12 +153,12 @@ armNonceAppEval bvi nonceApp =
|
|||||||
case args of
|
case args of
|
||||||
Ctx.Empty Ctx.:> assert Ctx.:> bv -> Just $ do
|
Ctx.Empty Ctx.:> assert Ctx.:> bv -> Just $ do
|
||||||
assertTH <- addEltTH M.LittleEndian bvi assert
|
assertTH <- addEltTH M.LittleEndian bvi assert
|
||||||
bv <- addEltTH M.LittleEndian bvi bv
|
bvElt <- addEltTH M.LittleEndian bvi bv
|
||||||
liftQ [| case $(return assertTH) of
|
liftQ [| case $(return assertTH) of
|
||||||
M.BoolValue True -> return $(return bv)
|
M.BoolValue True -> return $(return bvElt)
|
||||||
M.BoolValue False -> E.throwError (G.GeneratorMessage $ "Bitvector length assertion failed!")
|
M.BoolValue False -> E.throwError (G.GeneratorMessage $ "Bitvector length assertion failed!")
|
||||||
-- FIXME: THIS SHOULD THROW AN ERROR
|
-- FIXME: THIS SHOULD THROW AN ERROR
|
||||||
_ -> return $(return bv)
|
_ -> return $(return bvElt)
|
||||||
-- nm -> E.throwError (G.GeneratorMessage $ "Bitvector length assertion failed: <FIXME: PRINT NAME>")
|
-- nm -> E.throwError (G.GeneratorMessage $ "Bitvector length assertion failed: <FIXME: PRINT NAME>")
|
||||||
|]
|
|]
|
||||||
_ -> fail "Invalid call to assertBV"
|
_ -> fail "Invalid call to assertBV"
|
||||||
@ -314,6 +312,25 @@ concreteIte v t f = case v of
|
|||||||
M.CValue (M.BoolCValue b) -> if b then t else f
|
M.CValue (M.BoolCValue b) -> if b then t else f
|
||||||
_ -> E.throwError (G.GeneratorMessage $ "concreteIte: requires concrete value" <> show (M.ppValueAssignments v))
|
_ -> E.throwError (G.GeneratorMessage $ "concreteIte: requires concrete value" <> show (M.ppValueAssignments v))
|
||||||
|
|
||||||
|
-- | A smart constructor for division
|
||||||
|
--
|
||||||
|
-- The smart constructor recognizes divisions that can be converted into shifts.
|
||||||
|
-- We convert the operation to a shift if the divisior is a power of two.
|
||||||
|
sdiv :: (1 <= n)
|
||||||
|
=> NatRepr n
|
||||||
|
-> M.Value ARM.AArch32 ids (M.BVType n)
|
||||||
|
-> M.Value ARM.AArch32 ids (M.BVType n)
|
||||||
|
-> G.Generator ARM.AArch32 ids s (G.Expr ARM.AArch32 ids (M.BVType n))
|
||||||
|
sdiv repr dividend divisor =
|
||||||
|
case divisor of
|
||||||
|
M.BVValue nr val
|
||||||
|
| bv <- BVS.bitVector' repr val
|
||||||
|
, BVS.bvPopCount bv == 1 ->
|
||||||
|
withKnownNat repr $
|
||||||
|
let app = M.BVSar nr dividend (M.BVValue nr (fromIntegral (B.countTrailingZeros bv)))
|
||||||
|
in (G.ValueExpr . M.AssignedValue) <$> G.addAssignment (M.EvalApp app)
|
||||||
|
_ -> addArchAssignment (SDiv repr dividend divisor)
|
||||||
|
|
||||||
armAppEvaluator :: M.Endianness
|
armAppEvaluator :: M.Endianness
|
||||||
-> BoundVarInterpretations ARM.AArch32 t fs
|
-> BoundVarInterpretations ARM.AArch32 t fs
|
||||||
-> WB.App (WB.Expr t) ctp
|
-> WB.App (WB.Expr t) ctp
|
||||||
@ -328,8 +345,7 @@ armAppEvaluator endianness interps elt =
|
|||||||
WB.BVSdiv w bv1 bv2 -> return $ do
|
WB.BVSdiv w bv1 bv2 -> return $ do
|
||||||
e1 <- addEltTH endianness interps bv1
|
e1 <- addEltTH endianness interps bv1
|
||||||
e2 <- addEltTH endianness interps bv2
|
e2 <- addEltTH endianness interps bv2
|
||||||
liftQ [| addArchAssignment (SDiv $(natReprTH w) $(return e1) $(return e2))
|
liftQ [| sdiv $(natReprTH w) $(return e1) $(return e2) |]
|
||||||
|]
|
|
||||||
WB.BVUrem w bv1 bv2 -> return $ do
|
WB.BVUrem w bv1 bv2 -> return $ do
|
||||||
e1 <- addEltTH endianness interps bv1
|
e1 <- addEltTH endianness interps bv1
|
||||||
e2 <- addEltTH endianness interps bv2
|
e2 <- addEltTH endianness interps bv2
|
||||||
|
BIN
macaw-aarch32/tests/arm/test-direct-call-a32.exe
Executable file
BIN
macaw-aarch32/tests/arm/test-direct-call-a32.exe
Executable file
Binary file not shown.
10
macaw-aarch32/tests/arm/test-direct-call-a32.mcw.expected
Normal file
10
macaw-aarch32/tests/arm/test-direct-call-a32.mcw.expected
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
R { funcs = [ (0x100d8, [(0x100d8, 48)])
|
||||||
|
, (0x1010c, [ (0x1010c, 32)
|
||||||
|
, (0x1012c, 12)
|
||||||
|
])
|
||||||
|
, (0x10138, [ (0x10138, 28)
|
||||||
|
, (0x10154, 4)
|
||||||
|
])
|
||||||
|
]
|
||||||
|
, ignoreBlocks = [0x10158]
|
||||||
|
}
|
@ -1,4 +0,0 @@
|
|||||||
R { funcs = [ (0x8001, [(0x8001, 6), (0x8007,10)])
|
|
||||||
]
|
|
||||||
, ignoreBlocks = []
|
|
||||||
}
|
|
@ -52,6 +52,7 @@ simplifyApp a =
|
|||||||
BVOr sz l r -> binopbv (.|.) sz l r
|
BVOr sz l r -> binopbv (.|.) sz l r
|
||||||
BVShl sz l r -> binopbv (\l' r' -> shiftL l' (fromIntegral r')) sz l r
|
BVShl sz l r -> binopbv (\l' r' -> shiftL l' (fromIntegral r')) sz l r
|
||||||
BVShr sz l r -> binopbv (\l' r' -> shiftR l' (fromIntegral r')) sz l r
|
BVShr sz l r -> binopbv (\l' r' -> shiftR l' (fromIntegral r')) sz l r
|
||||||
|
BVSar sz l r -> binopbv (\l' r' -> shiftR (toSigned sz l') (fromIntegral (toSigned sz r'))) sz l r
|
||||||
BVAdd _ l (BVValue _ 0) -> Just l
|
BVAdd _ l (BVValue _ 0) -> Just l
|
||||||
BVAdd _ (BVValue _ 0) r -> Just r
|
BVAdd _ (BVValue _ 0) r -> Just r
|
||||||
BVAdd rep l@(BVValue {}) r@(RelocatableValue {}) ->
|
BVAdd rep l@(BVValue {}) r@(RelocatableValue {}) ->
|
||||||
@ -66,10 +67,17 @@ simplifyApp a =
|
|||||||
BVMul rep l r -> binopbv (*) rep l r
|
BVMul rep l r -> binopbv (*) rep l r
|
||||||
SExt (BVValue u n) sz -> Just (BVValue sz (toUnsigned sz (toSigned u n)))
|
SExt (BVValue u n) sz -> Just (BVValue sz (toUnsigned sz (toSigned u n)))
|
||||||
UExt (BVValue _ n) sz -> Just (mkLit sz n)
|
UExt (BVValue _ n) sz -> Just (mkLit sz n)
|
||||||
|
UExt (RelocatableValue _arep addr) sz -> do
|
||||||
|
memword <- MM.asAbsoluteAddr addr
|
||||||
|
return $ mkLit sz (fromIntegral memword)
|
||||||
Trunc (BVValue _ x) sz -> Just (mkLit sz x)
|
Trunc (BVValue _ x) sz -> Just (mkLit sz x)
|
||||||
|
|
||||||
Eq l r -> boolop (==) l r
|
Eq l r -> boolop (==) l r
|
||||||
BVComplement sz x -> unop complement sz x
|
BVComplement sz x -> unop complement sz x
|
||||||
|
BVSignedLe v1 v2 -> signedRelOp (<=) v1 v2
|
||||||
|
BVSignedLt v1 v2 -> signedRelOp (<) v1 v2
|
||||||
|
BVUnsignedLe v1 v2 -> unsignedRelOp (<=) v1 v2
|
||||||
|
BVUnsignedLt v1 v2 -> unsignedRelOp (<) v1 v2
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
where
|
where
|
||||||
unop :: forall n . (tp ~ BVType n)
|
unop :: forall n . (tp ~ BVType n)
|
||||||
@ -97,3 +105,19 @@ simplifyApp a =
|
|||||||
binopbv f sz (BVValue _ l) (BVValue _ r) =
|
binopbv f sz (BVValue _ l) (BVValue _ r) =
|
||||||
Just (mkLit sz (f l r))
|
Just (mkLit sz (f l r))
|
||||||
binopbv _ _ _ _ = Nothing
|
binopbv _ _ _ _ = Nothing
|
||||||
|
signedRelOp :: forall n
|
||||||
|
. (Integer -> Integer -> Bool)
|
||||||
|
-> Value arch ids (BVType n)
|
||||||
|
-> Value arch ids (BVType n)
|
||||||
|
-> Maybe (Value arch ids BoolType)
|
||||||
|
signedRelOp op (BVValue r1 v1) (BVValue _ v2) =
|
||||||
|
Just (BoolValue (op (toSigned r1 v1) (toSigned r1 v2)))
|
||||||
|
signedRelOp _ _ _ = Nothing
|
||||||
|
unsignedRelOp :: forall n
|
||||||
|
. (Integer -> Integer -> Bool)
|
||||||
|
-> Value arch ids (BVType n)
|
||||||
|
-> Value arch ids (BVType n)
|
||||||
|
-> Maybe (Value arch ids BoolType)
|
||||||
|
unsignedRelOp op (BVValue r1 v1) (BVValue _ v2) =
|
||||||
|
Just (BoolValue (op (toUnsigned r1 v1) (toUnsigned r1 v2)))
|
||||||
|
unsignedRelOp _ _ _ = Nothing
|
||||||
|
Loading…
Reference in New Issue
Block a user