Merge pull request #154 from GaloisInc/feature/normflat2

Update macaw-aarch32 for changes to the ASL translator
This commit is contained in:
Daniel Matichuk 2020-08-03 15:04:03 -07:00 committed by GitHub
commit f74cd557d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 567 additions and 347 deletions

View File

@ -19,6 +19,7 @@ install:
# our submodules without worrying about ssh keys. # our submodules without worrying about ssh keys.
- sed -i 's/git@github.com:/https:\/\/github.com\//' .gitmodules - sed -i 's/git@github.com:/https:\/\/github.com\//' .gitmodules
- git submodule update --init - git submodule update --init
- cabal v2-configure --project-file=cabal.project.dist --flags="+asl-lite"
- cabal v2-update --project-file=cabal.project.dist - cabal v2-update --project-file=cabal.project.dist
script: script:
@ -26,3 +27,4 @@ script:
# any command which exits with a non-zero exit code causes the build to fail. # any command which exits with a non-zero exit code causes the build to fail.
# Build packages # Build packages
- cabal v2-test --project-file=cabal.project.dist x86 x86_symbolic - cabal v2-test --project-file=cabal.project.dist x86 x86_symbolic
- cabal v2-test --project-file=cabal.project.dist macaw-asl-tests

2
deps/asl-translator vendored

@ -1 +1 @@
Subproject commit 808f2d2ea51ec0c8fd60a32a5d9b944274e166b9 Subproject commit d0bac677e038a54f47af0467e68c4aab95a32d64

2
deps/crucible vendored

@ -1 +1 @@
Subproject commit f4d6f6bb5b30050c0089bed17e0f98132db433eb Subproject commit 5cb47b4f77299b54b0ead3f93f25dc24447c80f3

2
deps/dismantle vendored

@ -1 +1 @@
Subproject commit 21c5d44d2fdfe5bfbed6278668ddd433668218a9 Subproject commit 1f61d7259228bbfb51053e7c990fac6d9228e154

2
deps/flexdis86 vendored

@ -1 +1 @@
Subproject commit 5981054db6354e0deb54323d93274d66e6a119f9 Subproject commit 51317819dbb7a39891f36010d3c4bf196789d032

2
deps/semmc vendored

@ -1 +1 @@
Subproject commit 120eef4f1900f70adb5a306014c2cc2f3c17b4c5 Subproject commit 990ce7ab63dd67cf0f23876d5d4d93da507ec11e

2
deps/what4 vendored

@ -1 +1 @@
Subproject commit a1290af1d571b2bcbc42ebe0ae455f2a3b184874 Subproject commit f9a8f950e7c66f0f04312ce3983a42f3facd576e

@ -1 +1 @@
Subproject commit 140ad099d7856c35d03ee8f18a94af00867b8eff Subproject commit e0a013a24a459a71f96a2238a238ff4f3bc9f111

View File

@ -32,6 +32,7 @@ import Data.Parameterized.Some ( Some(..) )
import qualified Data.Parameterized.SymbolRepr as PSR import qualified Data.Parameterized.SymbolRepr as PSR
import qualified Data.Parameterized.TraversableFC as FC import qualified Data.Parameterized.TraversableFC as FC
import qualified Data.Parameterized.TH.GADT as PTH import qualified Data.Parameterized.TH.GADT as PTH
import qualified Data.Parameterized.List as P
import qualified Data.Set as Set import qualified Data.Set as Set
import qualified Data.Text as T import qualified Data.Text as T
import GHC.TypeLits import GHC.TypeLits
@ -73,7 +74,7 @@ data ARMReg tp where
, tp' ~ ASL.GlobalsType s , tp' ~ ASL.GlobalsType s
, tp ~ BaseToMacawType tp') , tp ~ BaseToMacawType tp')
=> ASL.GlobalRef s -> ARMReg tp => ASL.GlobalRef s -> ARMReg tp
ARMWriteMode :: tp ~ MT.BVType 2 => ARMReg (MT.BVType 2) ARMDummyReg :: ARMReg (MT.TupleType '[])
-- | GPR14 is the link register for ARM -- | GPR14 is the link register for ARM
arm_LR :: (w ~ MC.RegAddrWidth ARMReg, 1 <= w) => ARMReg (MT.BVType w) arm_LR :: (w ~ MC.RegAddrWidth ARMReg, 1 <= w) => ARMReg (MT.BVType w)
@ -86,7 +87,7 @@ instance Show (ARMReg tp) where
show r = case r of show r = case r of
ARMGlobalBV globalRef -> show (ASL.globalRefSymbol globalRef) ARMGlobalBV globalRef -> show (ASL.globalRefSymbol globalRef)
ARMGlobalBool globalRef -> show (ASL.globalRefSymbol globalRef) ARMGlobalBool globalRef -> show (ASL.globalRefSymbol globalRef)
ARMWriteMode -> "ARMWriteMode" ARMDummyReg -> "()"
instance ShowF ARMReg where instance ShowF ARMReg where
showF = show showF = show
@ -125,7 +126,7 @@ instance MT.HasRepr ARMReg MT.TypeRepr where
case r of case r of
ARMGlobalBV globalRef -> baseToMacawTypeRepr (ASL.globalRefRepr globalRef) ARMGlobalBV globalRef -> baseToMacawTypeRepr (ASL.globalRefRepr globalRef)
ARMGlobalBool globalRef -> baseToMacawTypeRepr (ASL.globalRefRepr globalRef) ARMGlobalBool globalRef -> baseToMacawTypeRepr (ASL.globalRefRepr globalRef)
ARMWriteMode -> MT.BVTypeRepr (NR.knownNat @2) ARMDummyReg -> MT.TupleTypeRepr P.Nil
type instance MC.ArchReg SA.AArch32 = ARMReg type instance MC.ArchReg SA.AArch32 = ARMReg
type instance MC.RegAddrWidth ARMReg = 32 type instance MC.RegAddrWidth ARMReg = 32
@ -151,6 +152,7 @@ armRegs = FC.toListFC asARMReg ( FC.fmapFC ASL.SimpleGlobalRef ASL.simpleGlobalR
asARMReg gr = case ASL.globalRefRepr gr of asARMReg gr = case ASL.globalRefRepr gr of
WT.BaseBoolRepr -> Some (ARMGlobalBool gr) WT.BaseBoolRepr -> Some (ARMGlobalBool gr)
WT.BaseBVRepr _ -> Some (ARMGlobalBV gr) WT.BaseBVRepr _ -> Some (ARMGlobalBV gr)
WT.BaseStructRepr Ctx.Empty -> Some ARMDummyReg
tp -> error $ "unsupported global type " <> show tp tp -> error $ "unsupported global type " <> show tp
-- | The set of registers preserved across Linux system calls is defined by the ABI. -- | The set of registers preserved across Linux system calls is defined by the ABI.
@ -185,6 +187,8 @@ locToRegTH (SA.Location globalRef) = do
[| ARMGlobalBool (ASL.knownGlobalRef :: ASL.GlobalRef $(return (TH.LitT (TH.StrTyLit refName)))) |] [| ARMGlobalBool (ASL.knownGlobalRef :: ASL.GlobalRef $(return (TH.LitT (TH.StrTyLit refName)))) |]
WT.BaseBVRepr _ -> WT.BaseBVRepr _ ->
[| ARMGlobalBV (ASL.knownGlobalRef :: ASL.GlobalRef $(return (TH.LitT (TH.StrTyLit refName)))) |] [| ARMGlobalBV (ASL.knownGlobalRef :: ASL.GlobalRef $(return (TH.LitT (TH.StrTyLit refName)))) |]
WT.BaseStructRepr Ctx.Empty ->
[| ARMDummyReg |]
_tp -> [| error $ "locToRegTH undefined for unrecognized location: " <> $(return $ TH.LitE (TH.StringL refName)) |] _tp -> [| error $ "locToRegTH undefined for unrecognized location: " <> $(return $ TH.LitE (TH.StringL refName)) |]
branchTakenReg :: ARMReg MT.BoolType branchTakenReg :: ARMReg MT.BoolType

View File

@ -161,7 +161,7 @@ data ARMPrimFn (f :: MT.Type -> Type) tp where
FPRecipStep :: (1 <= w) FPRecipStep :: (1 <= w)
=> NR.NatRepr w => NR.NatRepr w
-> f (MT.BVType w) -> f (MT.BVType w)
-> f (MT.BVType 32) -> f (MT.BVType w)
-> ARMPrimFn f (MT.BVType w) -> ARMPrimFn f (MT.BVType w)
FPSqrtEstimate :: (1 <= w) FPSqrtEstimate :: (1 <= w)
=> NR.NatRepr w => NR.NatRepr w
@ -171,7 +171,7 @@ data ARMPrimFn (f :: MT.Type -> Type) tp where
FPRSqrtStep :: (1 <= w) FPRSqrtStep :: (1 <= w)
=> NR.NatRepr w => NR.NatRepr w
-> f (MT.BVType w) -> f (MT.BVType w)
-> f (MT.BVType 32) -> f (MT.BVType w)
-> ARMPrimFn f (MT.BVType w) -> ARMPrimFn f (MT.BVType w)
FPSqrt :: (1 <= w) FPSqrt :: (1 <= w)
=> NR.NatRepr w => NR.NatRepr w

View File

@ -64,7 +64,6 @@ initialBlockRegs addr _preconds = MSG.initRegState addr &
-- once we get Thumb support, we will want to refer to the semantics -- once we get Thumb support, we will want to refer to the semantics
-- for this. -- for this.
MC.boundValue (AR.ARMGlobalBV (ASL.knownGlobalRef @"PSTATE_T")) .~ MC.BVValue knownNat 0 & MC.boundValue (AR.ARMGlobalBV (ASL.knownGlobalRef @"PSTATE_T")) .~ MC.BVValue knownNat 0 &
MC.boundValue AR.ARMWriteMode .~ MC.BVValue knownNat 0 &
MC.boundValue (AR.ARMGlobalBV (ASL.knownGlobalRef @"PSTATE_IT")) .~ MC.BVValue knownNat 0 & MC.boundValue (AR.ARMGlobalBV (ASL.knownGlobalRef @"PSTATE_IT")) .~ MC.BVValue knownNat 0 &
MC.boundValue (AR.ARMGlobalBV (ASL.knownGlobalRef @"PSTATE_T")) .~ MC.BVValue knownNat 0 & MC.boundValue (AR.ARMGlobalBV (ASL.knownGlobalRef @"PSTATE_T")) .~ MC.BVValue knownNat 0 &
MC.boundValue (AR.ARMGlobalBV (ASL.knownGlobalRef @"PSTATE_nRW")) .~ MC.BVValue knownNat 1 & MC.boundValue (AR.ARMGlobalBV (ASL.knownGlobalRef @"PSTATE_nRW")) .~ MC.BVValue knownNat 1 &

View File

@ -16,7 +16,7 @@ import qualified Data.ByteString as BS
import qualified Data.List as L import qualified Data.List as L
import Data.Macaw.ARM.ARMReg ( locToRegTH ) import Data.Macaw.ARM.ARMReg ( locToRegTH )
import Data.Macaw.ARM.Arch ( a32InstructionMatcher ) import Data.Macaw.ARM.Arch ( a32InstructionMatcher )
import Data.Macaw.ARM.Semantics.TH ( armAppEvaluator, armNonceAppEval, loadSemantics ) import Data.Macaw.ARM.Semantics.TH ( armAppEvaluator, armNonceAppEval, loadSemantics, armTranslateType )
import qualified Data.Macaw.CFG as MC import qualified Data.Macaw.CFG as MC
import Data.Macaw.SemMC.Generator ( Generator ) import Data.Macaw.SemMC.Generator ( Generator )
import Data.Macaw.SemMC.TH ( MacawTHConfig(..), genExecInstruction ) import Data.Macaw.SemMC.TH ( MacawTHConfig(..), genExecInstruction )
@ -33,6 +33,7 @@ import qualified SemMC.Architecture.AArch32 as ARMSem
import SemMC.Architecture.ARM.Opcodes ( ASLSemantics(..), allA32OpcodeInfo ) import SemMC.Architecture.ARM.Opcodes ( ASLSemantics(..), allA32OpcodeInfo )
import qualified SemMC.Formula as SF import qualified SemMC.Formula as SF
import qualified What4.Expr.Builder as WEB import qualified What4.Expr.Builder as WEB
import qualified What4.Interface as S
execInstruction :: MC.Value ARMSem.AArch32 ids (MT.BVType 32) execInstruction :: MC.Value ARMSem.AArch32 ids (MT.BVType 32)
-> Instruction -> Instruction
@ -62,6 +63,7 @@ execInstruction =
, archTypeQ = [t| ARMSem.AArch32 |] , archTypeQ = [t| ARMSem.AArch32 |]
, genLibraryFunction = notVecLib , genLibraryFunction = notVecLib
, genOpcodeCase = notVecOpc , genOpcodeCase = notVecOpc
, archTranslateType = armTranslateType
} }
genExecInstruction (Proxy @ARMSem.AArch32) genExecInstruction (Proxy @ARMSem.AArch32)

View File

@ -1,13 +1,16 @@
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-} {-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeOperators #-}
{-# LANGUAGE LambdaCase #-} {-# LANGUAGE LambdaCase #-}
{-# LANGUAGE DeriveLift #-} {-# LANGUAGE DeriveLift #-}
{-# LANGUAGE GADTs #-}
module Data.Macaw.ARM.Semantics.TH module Data.Macaw.ARM.Semantics.TH
( armAppEvaluator ( armAppEvaluator
, armNonceAppEval , armNonceAppEval
@ -16,6 +19,7 @@ module Data.Macaw.ARM.Semantics.TH
, getSIMD , getSIMD
, setSIMD , setSIMD
, loadSemantics , loadSemantics
, armTranslateType
) )
where where
@ -27,12 +31,12 @@ 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 Data.Macaw.SemMC.TH ( addEltTH, natReprTH, symFnName ) import Data.Macaw.SemMC.TH ( addEltTH, natReprTH, symFnName, translateBaseTypeRepr )
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 Data.Parameterized.Classes import Data.Parameterized.Classes
import qualified Data.Parameterized.Classes as PC
import qualified Data.Parameterized.Context as Ctx import qualified Data.Parameterized.Context as Ctx
import qualified Data.Parameterized.TraversableFC as FC
import Data.Parameterized.NatRepr import Data.Parameterized.NatRepr
import GHC.TypeLits as TL import GHC.TypeLits as TL
import qualified Lang.Crucible.Backend.Simple as CBS import qualified Lang.Crucible.Backend.Simple as CBS
@ -72,13 +76,36 @@ armNonceAppEval bvi nonceApp =
let fnName = symFnName symFn let fnName = symFnName symFn
tp = WB.symFnReturnType symFn tp = WB.symFnReturnType symFn
in case fnName of in case fnName of
-- NOTE: The state fields corresponding to the assertion
-- failure, undefined behavior and unpredictable behavior flags
-- are not used, and have been wrapped in the following 3 functions.
-- To save time, for now we can simply avoid translating them.
"uf_update_assert" -> case args of
Ctx.Empty Ctx.:> _assertVar -> Just $ do
--assertVarE <- addEltTH M.LittleEndian bvi assertVar
--liftQ [| $(refBinding assertVarE) |]
liftQ [| return $ M.BoolValue False |]
_ -> fail "Invalid uf_update_assert"
"uf_update_undefB" -> case args of
Ctx.Empty Ctx.:> _undefVar -> Just $ do
--undefVarE <- addEltTH M.LittleEndian bvi undefVar
--liftQ [| $(refBinding undefVarE) |]
liftQ [| return $ M.BoolValue False |]
_ -> fail "Invalid uf_update_undefB"
"uf_update_unpredB" -> case args of
Ctx.Empty Ctx.:> _unpredVar -> Just $ do
--unpredVarE <- addEltTH M.LittleEndian bvi unpredVar
--liftQ [| $(refBinding unpredVarE) |]
liftQ [| return $ M.BoolValue False |]
_ -> fail "Invalid uf_update_unpredB"
"uf_simd_set" -> "uf_simd_set" ->
case args of case args of
Ctx.Empty Ctx.:> rgf Ctx.:> rid Ctx.:> val -> Just $ do Ctx.Empty Ctx.:> rgf Ctx.:> rid Ctx.:> val -> Just $ do
rgfE <- addEltTH M.LittleEndian bvi rgf rgfE <- addEltTH M.LittleEndian bvi rgf
ridE <- addEltTH M.LittleEndian bvi rid ridE <- addEltTH M.LittleEndian bvi rid
valE <- addEltTH M.LittleEndian bvi val valE <- addEltTH M.LittleEndian bvi val
liftQ [| join (setSIMD <$> $(refBinding rgfE) <*> $(refBinding ridE) <*> $(refBinding valE)) |] liftQ $ joinOp3 [| setSIMD |] rgfE ridE valE
_ -> fail "Invalid uf_simd_get" _ -> fail "Invalid uf_simd_get"
"uf_gpr_set" -> "uf_gpr_set" ->
case args of case args of
@ -86,7 +113,7 @@ armNonceAppEval bvi nonceApp =
rgfE <- addEltTH M.LittleEndian bvi rgf rgfE <- addEltTH M.LittleEndian bvi rgf
ridE <- addEltTH M.LittleEndian bvi rid ridE <- addEltTH M.LittleEndian bvi rid
valE <- addEltTH M.LittleEndian bvi val valE <- addEltTH M.LittleEndian bvi val
liftQ [| join (setGPR <$> $(refBinding rgfE) <*> $(refBinding ridE) <*> $(refBinding valE)) |] liftQ $ joinOp3 [| setGPR |] rgfE ridE valE
_ -> fail "Invalid uf_gpr_get" _ -> fail "Invalid uf_gpr_get"
"uf_simd_get" -> "uf_simd_get" ->
case args of case args of
@ -94,7 +121,7 @@ armNonceAppEval bvi nonceApp =
Just $ do Just $ do
_rgf <- addEltTH M.LittleEndian bvi array _rgf <- addEltTH M.LittleEndian bvi array
rid <- addEltTH M.LittleEndian bvi ix rid <- addEltTH M.LittleEndian bvi ix
liftQ [| getSIMD =<< $(refBinding rid) |] liftQ $ joinOp1 [| getSIMD |] rid
_ -> fail "Invalid uf_simd_get" _ -> fail "Invalid uf_simd_get"
"uf_gpr_get" -> "uf_gpr_get" ->
case args of case args of
@ -102,7 +129,7 @@ armNonceAppEval bvi nonceApp =
Just $ do Just $ do
_rgf <- addEltTH M.LittleEndian bvi array _rgf <- addEltTH M.LittleEndian bvi array
rid <- addEltTH M.LittleEndian bvi ix rid <- addEltTH M.LittleEndian bvi ix
liftQ [| getGPR =<< $(refBinding rid) |] liftQ $ joinOp1 [| getGPR |] rid
_ -> fail "Invalid uf_gpr_get" _ -> fail "Invalid uf_gpr_get"
_ | "uf_write_mem_" `isPrefixOf` fnName -> _ | "uf_write_mem_" `isPrefixOf` fnName ->
case args of case args of
@ -113,16 +140,15 @@ armNonceAppEval bvi nonceApp =
addrE <- addEltTH M.LittleEndian bvi addr addrE <- addEltTH M.LittleEndian bvi addr
valE <- addEltTH M.LittleEndian bvi val valE <- addEltTH M.LittleEndian bvi val
let memWidth = fromIntegral (intValue memWidthRepr) `div` 8 let memWidth = fromIntegral (intValue memWidthRepr) `div` 8
liftQ [| join (writeMem <$> $(refBinding memE) <*> $(refBinding addrE) <*> pure $(natReprFromIntTH memWidth) <*> $(refBinding valE)) |] liftQ $ joinOp3 [| writeMem $(natReprFromIntTH memWidth) |] memE addrE valE
_ -> fail "invalid write_mem" _ -> fail "invalid write_mem"
_ | "uf_unsignedRSqrtEstimate" `isPrefixOf` fnName -> _ | "uf_unsignedRSqrtEstimate" `isPrefixOf` fnName ->
case args of case args of
Ctx.Empty Ctx.:> op -> Just $ do Ctx.Empty Ctx.:> op -> Just $ do
ope <- addEltTH M.LittleEndian bvi op ope <- addEltTH M.LittleEndian bvi op
liftQ [| G.addExpr =<< (UnsignedRSqrtEstimate knownNat <$> $(refBinding ope)) |] liftQ [| G.addExpr =<< $(joinOp1 [| \e1E -> addArchAssignment (UnsignedRSqrtEstimate knownNat e1E) |] ope) |]
_ -> fail "Invalid unsignedRSqrtEstimate arguments" _ -> fail "Invalid unsignedRSqrtEstimate arguments"
-- NOTE: This must come before fpMul, since fpMul is a prefix of this -- NOTE: This must come before fpMul, since fpMul is a prefix of this
@ -133,7 +159,8 @@ armNonceAppEval bvi nonceApp =
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
op3e <- addEltTH M.LittleEndian bvi op3 op3e <- addEltTH M.LittleEndian bvi op3
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPMulAdd knownNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding op3e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< join ((\o1 o2 o3 o4 -> addArchAssignment (FPMulAdd knownNat o1 o2 o3 o4)) <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding op3e) <*> $(refBinding fpcre)) |]
_ -> fail "Invalid fpMulAdd arguments" _ -> fail "Invalid fpMulAdd arguments"
@ -143,7 +170,7 @@ armNonceAppEval bvi nonceApp =
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPSub knownNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp3 [| \e1E e2E e3E -> addArchAssignment (FPSub knownNat e1E e2E e3E) |] op1e op2e fpcre) |]
_ -> fail "Invalid fpSub arguments" _ -> fail "Invalid fpSub arguments"
_ | "uf_fpAdd" `isPrefixOf` fnName -> _ | "uf_fpAdd" `isPrefixOf` fnName ->
case args of case args of
@ -151,7 +178,7 @@ armNonceAppEval bvi nonceApp =
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPAdd knownNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp3 [| \e1E e2E e3E -> addArchAssignment (FPAdd knownNat e1E e2E e3E) |] op1e op2e fpcre) |]
_ -> fail "Invalid fpAdd arguments" _ -> fail "Invalid fpAdd arguments"
_ | "uf_fpMul" `isPrefixOf` fnName -> _ | "uf_fpMul" `isPrefixOf` fnName ->
case args of case args of
@ -159,7 +186,7 @@ armNonceAppEval bvi nonceApp =
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPMul knownNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp3 [| \e1E e2E e3E -> addArchAssignment (FPMul knownNat e1E e2E e3E) |] op1e op2e fpcre) |]
_ -> fail "Invalid fpMul arguments" _ -> fail "Invalid fpMul arguments"
_ | "uf_fpDiv" `isPrefixOf` fnName -> _ | "uf_fpDiv" `isPrefixOf` fnName ->
case args of case args of
@ -167,7 +194,7 @@ armNonceAppEval bvi nonceApp =
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPMul knownNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp3 [| \e1E e2E e3E -> addArchAssignment (FPDiv knownNat e1E e2E e3E) |] op1e op2e fpcre) |]
_ -> fail "Invalid fpDiv arguments" _ -> fail "Invalid fpDiv arguments"
_ | "uf_fpMaxNum" `isPrefixOf` fnName -> _ | "uf_fpMaxNum" `isPrefixOf` fnName ->
@ -176,7 +203,7 @@ armNonceAppEval bvi nonceApp =
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPMaxNum knownNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp3 [| \e1E e2E e3E -> addArchAssignment (FPMaxNum knownNat e1E e2E e3E) |] op1e op2e fpcre) |]
_ -> fail "Invalid fpMaxNum arguments" _ -> fail "Invalid fpMaxNum arguments"
_ | "uf_fpMinNum" `isPrefixOf` fnName -> _ | "uf_fpMinNum" `isPrefixOf` fnName ->
case args of case args of
@ -184,7 +211,7 @@ armNonceAppEval bvi nonceApp =
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPMinNum knownNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp3 [| \e1E e2E e3E -> addArchAssignment (FPMinNum knownNat e1E e2E e3E) |] op1e op2e fpcre) |]
_ -> fail "Invalid fpMinNum arguments" _ -> fail "Invalid fpMinNum arguments"
_ | "uf_fpMax" `isPrefixOf` fnName -> _ | "uf_fpMax" `isPrefixOf` fnName ->
case args of case args of
@ -192,7 +219,7 @@ armNonceAppEval bvi nonceApp =
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPMax knownNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp3 [| \e1E e2E e3E -> addArchAssignment (FPMax knownNat e1E e2E e3E) |] op1e op2e fpcre) |]
_ -> fail "Invalid fpMax arguments" _ -> fail "Invalid fpMax arguments"
_ | "uf_fpMin" `isPrefixOf` fnName -> _ | "uf_fpMin" `isPrefixOf` fnName ->
case args of case args of
@ -200,7 +227,7 @@ armNonceAppEval bvi nonceApp =
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPMin knownNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp3 [| \e1E e2E e3E -> addArchAssignment (FPMin knownNat e1E e2E e3E) |] op1e op2e fpcre) |]
_ -> fail "Invalid fpMin arguments" _ -> fail "Invalid fpMin arguments"
_ | "uf_fpRecipEstimate" `isPrefixOf` fnName -> _ | "uf_fpRecipEstimate" `isPrefixOf` fnName ->
@ -208,35 +235,35 @@ armNonceAppEval bvi nonceApp =
Ctx.Empty Ctx.:> op1 Ctx.:> fpcr -> Just $ do Ctx.Empty Ctx.:> op1 Ctx.:> fpcr -> Just $ do
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPRecipEstimate knownNat <$> $(refBinding op1e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp2 [| \e1E e2E -> addArchAssignment (FPRecipEstimate knownNat e1E e2E) |] op1e fpcre) |]
_ -> fail "Invalid fpRecipEstimate arguments" _ -> fail "Invalid fpRecipEstimate arguments"
_ | "uf_fpRecipStep" `isPrefixOf` fnName -> _ | "uf_fpRecipStep" `isPrefixOf` fnName ->
case args of case args of
Ctx.Empty Ctx.:> op1 Ctx.:> fpcr -> Just $ do Ctx.Empty Ctx.:> op1 Ctx.:> op2 -> Just $ do
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
fpcre <- addEltTH M.LittleEndian bvi fpcr op2e <- addEltTH M.LittleEndian bvi op2
liftQ [| G.addExpr =<< (FPRecipStep knownNat <$> $(refBinding op1e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp2 [| \e1E e2E -> addArchAssignment (FPRecipStep knownNat e1E e2E) |] op1e op2e) |]
_ -> fail "Invalid fpRecipStep arguments" _ -> fail "Invalid fpRecipStep arguments"
_ | "uf_fpSqrtEstimate" `isPrefixOf` fnName -> _ | "uf_fpSqrtEstimate" `isPrefixOf` fnName ->
case args of case args of
Ctx.Empty Ctx.:> op1 Ctx.:> fpcr -> Just $ do Ctx.Empty Ctx.:> op1 Ctx.:> fpcr -> Just $ do
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPSqrtEstimate knownNat <$> $(refBinding op1e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp2 [| \e1E e2E -> addArchAssignment (FPSqrtEstimate knownNat e1E e2E) |] op1e fpcre) |]
_ -> fail "Invalid fpSqrtEstimate arguments" _ -> fail "Invalid fpSqrtEstimate arguments"
_ | "uf_fprSqrtStep" `isPrefixOf` fnName -> _ | "uf_fprSqrtStep" `isPrefixOf` fnName ->
case args of case args of
Ctx.Empty Ctx.:> op1 Ctx.:> fpcr -> Just $ do Ctx.Empty Ctx.:> op1 Ctx.:> fpcr -> Just $ do
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPRSqrtStep knownNat <$> $(refBinding op1e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp2 [| \e1E e2E -> addArchAssignment (FPRSqrtStep knownNat e1E e2E) |] op1e fpcre) |]
_ -> fail "Invalid fprSqrtStep arguments" _ -> fail "Invalid fprSqrtStep arguments"
_ | "uf_fpSqrt" `isPrefixOf` fnName -> _ | "uf_fpSqrt" `isPrefixOf` fnName ->
case args of case args of
Ctx.Empty Ctx.:> op1 Ctx.:> fpcr -> Just $ do Ctx.Empty Ctx.:> op1 Ctx.:> fpcr -> Just $ do
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPSqrt knownNat <$> $(refBinding op1e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp2 [| \e1E e2E -> addArchAssignment (FPSqrt knownNat e1E e2E) |] op1e fpcre) |]
_ -> fail "Invalid fpSqrt arguments" _ -> fail "Invalid fpSqrt arguments"
_ | "uf_fpCompareGE" `isPrefixOf` fnName -> _ | "uf_fpCompareGE" `isPrefixOf` fnName ->
@ -245,7 +272,7 @@ armNonceAppEval bvi nonceApp =
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPCompareGE knownNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp3 [| \e1E e2E e3E -> addArchAssignment (FPCompareGE knownNat e1E e2E e3E) |] op1e op2e fpcre) |]
_ -> fail "Invalid fpCompareGE arguments" _ -> fail "Invalid fpCompareGE arguments"
_ | "uf_fpCompareGT" `isPrefixOf` fnName -> _ | "uf_fpCompareGT" `isPrefixOf` fnName ->
case args of case args of
@ -253,7 +280,7 @@ armNonceAppEval bvi nonceApp =
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPCompareGT knownNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp3 [| \e1E e2E e3E -> addArchAssignment (FPCompareGT knownNat e1E e2E e3E) |] op1e op2e fpcre) |]
_ -> fail "Invalid fpCompareGT arguments" _ -> fail "Invalid fpCompareGT arguments"
_ | "uf_fpCompareEQ" `isPrefixOf` fnName -> _ | "uf_fpCompareEQ" `isPrefixOf` fnName ->
case args of case args of
@ -261,7 +288,7 @@ armNonceAppEval bvi nonceApp =
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPCompareEQ knownNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp3 [| \e1E e2E e3E -> addArchAssignment (FPCompareEQ knownNat e1E e2E e3E) |] op1e op2e fpcre) |]
_ -> fail "Invalid fpCompareEQ arguments" _ -> fail "Invalid fpCompareEQ arguments"
_ | "uf_fpCompareNE" `isPrefixOf` fnName -> _ | "uf_fpCompareNE" `isPrefixOf` fnName ->
case args of case args of
@ -269,7 +296,7 @@ armNonceAppEval bvi nonceApp =
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPCompareNE knownNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp3 [| \e1E e2E e3E -> addArchAssignment (FPCompareNE knownNat e1E e2E e3E) |] op1e op2e fpcre) |]
_ -> fail "Invalid fpCompareNE arguments" _ -> fail "Invalid fpCompareNE arguments"
_ | "uf_fpCompareUN" `isPrefixOf` fnName -> _ | "uf_fpCompareUN" `isPrefixOf` fnName ->
case args of case args of
@ -277,7 +304,7 @@ armNonceAppEval bvi nonceApp =
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
fpcre <- addEltTH M.LittleEndian bvi fpcr fpcre <- addEltTH M.LittleEndian bvi fpcr
liftQ [| G.addExpr =<< (FPCompareUN knownNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding fpcre)) |] liftQ [| G.addExpr =<< $(joinOp3 [| \e1E e2E e3E -> addArchAssignment (FPCompareUN knownNat e1E e2E e3E) |] op1e op2e fpcre) |]
_ -> fail "Invalid fpCompareUN arguments" _ -> fail "Invalid fpCompareUN arguments"
"uf_fpToFixedJS" -> "uf_fpToFixedJS" ->
@ -286,7 +313,7 @@ armNonceAppEval bvi nonceApp =
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
op3e <- addEltTH M.LittleEndian bvi op3 op3e <- addEltTH M.LittleEndian bvi op3
liftQ [| G.addExpr =<< (FPToFixedJS <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding op3e)) |] liftQ [| G.addExpr =<< $(joinOp3 [| \e1E e2E e3E -> addArchAssignment (FPToFixedJS e1E e2E e3E) |] op1e op2e op3e) |]
_ -> fail "Invalid fpToFixedJS arguments" _ -> fail "Invalid fpToFixedJS arguments"
_ | "uf_fpToFixed" `isPrefixOf` fnName -> _ | "uf_fpToFixed" `isPrefixOf` fnName ->
case args of case args of
@ -296,7 +323,7 @@ armNonceAppEval bvi nonceApp =
op3e <- addEltTH M.LittleEndian bvi op3 op3e <- addEltTH M.LittleEndian bvi op3
op4e <- addEltTH M.LittleEndian bvi op4 op4e <- addEltTH M.LittleEndian bvi op4
op5e <- addEltTH M.LittleEndian bvi op5 op5e <- addEltTH M.LittleEndian bvi op5
liftQ [| G.addExpr =<< (FPToFixed knonwNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding op3e) <*> $(refBinding op4e) <*> $(refBinding op5e)) |] liftQ [| G.addExpr =<< join ((\o1 o2 o3 o4 o5 -> addArchAssignment (FPToFixed knownNat o1 o2 o3 o4 o5)) <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding op3e) <*> $(refBinding op4e) <*> $(refBinding op5e)) |]
_ -> fail "Invalid fpToFixed arguments" _ -> fail "Invalid fpToFixed arguments"
_ | "uf_fixedToFP" `isPrefixOf` fnName -> _ | "uf_fixedToFP" `isPrefixOf` fnName ->
case args of case args of
@ -306,7 +333,7 @@ armNonceAppEval bvi nonceApp =
op3e <- addEltTH M.LittleEndian bvi op3 op3e <- addEltTH M.LittleEndian bvi op3
op4e <- addEltTH M.LittleEndian bvi op4 op4e <- addEltTH M.LittleEndian bvi op4
op5e <- addEltTH M.LittleEndian bvi op5 op5e <- addEltTH M.LittleEndian bvi op5
liftQ [| G.addExpr =<< (FixedToFP knonwNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding op3e) <*> $(refBinding op4e) <*> $(refBinding op5e)) |] liftQ [| G.addExpr =<< join ((\o1 o2 o3 o4 o5 -> addArchAssignment (FixedToFP knownNat o1 o2 o3 o4 o5)) <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding op3e) <*> $(refBinding op4e) <*> $(refBinding op5e)) |]
_ -> fail "Invalid fixedToFP arguments" _ -> fail "Invalid fixedToFP arguments"
_ | "uf_fpConvert" `isPrefixOf` fnName -> _ | "uf_fpConvert" `isPrefixOf` fnName ->
case args of case args of
@ -314,7 +341,7 @@ armNonceAppEval bvi nonceApp =
op1e <- addEltTH M.LittleEndian bvi op1 op1e <- addEltTH M.LittleEndian bvi op1
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
op3e <- addEltTH M.LittleEndian bvi op3 op3e <- addEltTH M.LittleEndian bvi op3
liftQ [| G.addExpr =<< (FPConvert knownNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding op3e)) |] liftQ [| G.addExpr =<< $(joinOp3 [| \e1E e2E e3E -> addArchAssignment (FPConvert knownNat e1E e2E e3E) |] op1e op2e op3e) |]
_ -> fail "Invalid fpConvert arguments" _ -> fail "Invalid fpConvert arguments"
_ | "uf_fpRoundInt" `isPrefixOf` fnName -> _ | "uf_fpRoundInt" `isPrefixOf` fnName ->
case args of case args of
@ -323,79 +350,42 @@ armNonceAppEval bvi nonceApp =
op2e <- addEltTH M.LittleEndian bvi op2 op2e <- addEltTH M.LittleEndian bvi op2
op3e <- addEltTH M.LittleEndian bvi op3 op3e <- addEltTH M.LittleEndian bvi op3
op4e <- addEltTH M.LittleEndian bvi op4 op4e <- addEltTH M.LittleEndian bvi op4
liftQ [| G.addExpr =<< (FPRoundInt knownNat <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding op3e) <*> $(refBinding op4e)) |] liftQ [| G.addExpr =<< join ((\o1 o2 o3 o4 -> addArchAssignment (FPRoundInt knownNat o1 o2 o3 o4)) <$> $(refBinding op1e) <*> $(refBinding op2e) <*> $(refBinding op3e) <*> $(refBinding op4e)) |]
_ -> fail "Invalid fpRoundInt arguments" _ -> fail "Invalid fpRoundInt arguments"
-- NOTE: These three cases occasionally end up unused. Because we let
-- bind almost everything, that can lead to the 'arch' type parameter "uf_init_gprs" -> Just $ liftQ [| return $ ARMWriteAction (return ARMWriteGPRs) |]
-- being ambiguous, which is an error for various reasons. "uf_init_memory" -> Just $ liftQ [| return $ ARMWriteAction (return ARMWriteMemory)|]
-- "uf_init_simds" -> Just $ liftQ [| return $ ARMWriteAction (return ARMWriteSIMDs) |]
-- To fix that, we add an explicit type application here.
"uf_init_gprs" -> Just $ liftQ [| M.AssignedValue <$> G.addAssignment @ARM.AArch32 (M.SetUndefined $(what4TypeTH tp)) |]
"uf_init_memory" -> Just $ liftQ [| M.AssignedValue <$> G.addAssignment @ARM.AArch32 (M.SetUndefined $(what4TypeTH tp)) |]
"uf_init_simds" -> Just $ liftQ [| M.AssignedValue <$> G.addAssignment @ARM.AArch32 (M.SetUndefined $(what4TypeTH tp)) |]
-- NOTE: These cases are tricky because they generate groups of -- These functions indicate that the wrapped monadic actions in the corresponding
-- statements that need to behave a bit differently in (eager) -- 'ARMWriteAction' should be executed, committing their stateful actions.
-- top-level code generation and (lazy) conditional code generation.
--
-- In either case, the calls to 'setWriteMode' /must/ bracket the
-- memory/register update function.
--
-- In the eager translation case, that means that we have to lexically
-- emit the update between the write mode guards (so that the effect
-- actually happens).
--
-- In contrast, the lazy translation case has to emit the write
-- operation as a lazy let binding to preserve sharing, but group all
-- three statements in the actual 'Generator' monad.
"uf_update_gprs" "uf_update_gprs"
| Ctx.Empty Ctx.:> gprs <- args -> Just $ do | Ctx.Empty Ctx.:> gprs <- args -> Just $ do
istl <- isTopLevel gprs' <- addEltTH M.LittleEndian bvi gprs
case istl of appendStmt $ [| join (execWriteAction <$> $(refBinding gprs')) |]
False -> do liftQ [| return $ unitValue |]
gprs' <- addEltTH M.LittleEndian bvi gprs
liftQ [| do setWriteMode WriteGPRs
$(refBinding gprs')
setWriteMode WriteNone
|]
True -> do
appendStmt [| setWriteMode WriteGPRs |]
gprs' <- addEltTH M.LittleEndian bvi gprs
appendStmt [| setWriteMode WriteNone |]
extractBound gprs'
"uf_update_simds" "uf_update_simds"
| Ctx.Empty Ctx.:> simds <- args -> Just $ do | Ctx.Empty Ctx.:> simds <- args -> Just $ do
istl <- isTopLevel simds' <- addEltTH M.LittleEndian bvi simds
case istl of appendStmt $ [| join (execWriteAction <$> $(refBinding simds')) |]
False -> do liftQ [| return $ unitValue |]
simds' <- addEltTH M.LittleEndian bvi simds
liftQ [| do setWriteMode WriteSIMDs
$(refBinding simds')
setWriteMode WriteNone
|]
True -> do
appendStmt [| setWriteMode WriteSIMDs |]
simds' <- addEltTH M.LittleEndian bvi simds
appendStmt [| setWriteMode WriteNone |]
extractBound simds'
"uf_update_memory" "uf_update_memory"
| Ctx.Empty Ctx.:> mem <- args -> Just $ do | Ctx.Empty Ctx.:> mem <- args -> Just $ do
istl <- isTopLevel mem' <- addEltTH M.LittleEndian bvi mem
case istl of appendStmt $ [| join (execWriteAction <$> $(refBinding mem')) |]
False -> do liftQ [| return $ unitValue |]
mem' <- addEltTH M.LittleEndian bvi mem
liftQ [| do setWriteMode WriteMemory
$(refBinding mem')
setWriteMode WriteNone
|]
True -> do
appendStmt [| setWriteMode WriteMemory |]
mem' <- addEltTH M.LittleEndian bvi mem
appendStmt [| setWriteMode WriteNone |]
extractBound mem'
"uf_noop" | Ctx.Empty <- args -> Just $ liftQ [| return $ M.BoolValue True |]
"uf_join_units"
| Ctx.Empty Ctx.:> u1 Ctx.:> u2 <- args -> Just $ do
_ <- addEltTH M.LittleEndian bvi u1
_ <- addEltTH M.LittleEndian bvi u2
liftQ [| return $ unitValue |]
_ | "uf_assertBV_" `isPrefixOf` fnName -> _ | "uf_assertBV_" `isPrefixOf` fnName ->
case args of case args of
Ctx.Empty Ctx.:> assert Ctx.:> bv -> Just $ do Ctx.Empty Ctx.:> assert Ctx.:> bv -> Just $ do
@ -411,71 +401,56 @@ armNonceAppEval bvi nonceApp =
_ -> fail "Invalid call to assertBV" _ -> fail "Invalid call to assertBV"
_ | "uf_UNDEFINED_" `isPrefixOf` fnName -> _ | "uf_UNDEFINED_" `isPrefixOf` fnName ->
Just $ liftQ [| M.AssignedValue <$> G.addAssignment (M.SetUndefined $(what4TypeTH tp)) |] Just $ liftQ [| M.AssignedValue <$> G.addAssignment (M.SetUndefined $(translateBaseTypeRepr tp)) |]
_ | "uf_INIT_GLOBAL_" `isPrefixOf` fnName -> _ | "uf_INIT_GLOBAL_" `isPrefixOf` fnName ->
Just $ liftQ [| M.AssignedValue <$> G.addAssignment (M.SetUndefined $(what4TypeTH tp)) |] Just $ liftQ [| M.AssignedValue <$> G.addAssignment (M.SetUndefined $(translateBaseTypeRepr tp)) |]
_ -> Nothing _ -> Nothing
_ -> Nothing -- fallback to default handling _ -> Nothing -- fallback to default handling
unitValue :: M.Value ARM.AArch32 ids (M.TupleType '[])
unitValue = M.Initial ARMDummyReg
natReprFromIntTH :: Int -> Q Exp natReprFromIntTH :: Int -> Q Exp
natReprFromIntTH i = [| knownNat :: M.NatRepr $(litT (numTyLit (fromIntegral i))) |] natReprFromIntTH i = [| knownNat :: M.NatRepr $(litT (numTyLit (fromIntegral i))) |]
data WriteMode =
WriteNone
| WriteGPRs
| WriteSIMDs
| WriteMemory
deriving (Show, Eq, Lift)
getWriteMode :: G.Generator ARM.AArch32 ids s WriteMode data ARMWriteGPRs = ARMWriteGPRs
getWriteMode = do data ARMWriteMemory = ARMWriteMemory
G.getRegVal ARMWriteMode >>= \case data ARMWriteSIMDs = ARMWriteSIMDs
M.BVValue _ i -> return $ case i of
0 -> WriteNone
1 -> WriteGPRs
2 -> WriteSIMDs
3 -> WriteMemory
_ -> error "impossible"
_ -> error "impossible"
setWriteMode :: WriteMode -> G.Generator ARM.AArch32 ids s () newtype ARMWriteAction ids s tp where
setWriteMode wm = ARMWriteAction :: G.Generator ARM.AArch32 ids s tp -> ARMWriteAction ids s tp
let deriving (Functor, Applicative, Monad)
i = case wm of
WriteNone -> 0 execWriteAction :: ARMWriteAction ids s tp -> G.Generator ARM.AArch32 ids s tp
WriteGPRs -> 1 execWriteAction (ARMWriteAction f) = f
WriteSIMDs -> 2
WriteMemory -> 3
in G.setRegVal ARMWriteMode (M.BVValue knownNat i)
writeMem :: 1 <= w writeMem :: 1 <= w
=> M.Value ARM.AArch32 ids tp => M.NatRepr w
-> ARMWriteAction ids s ARMWriteMemory
-> M.Value ARM.AArch32 ids (M.BVType 32) -> M.Value ARM.AArch32 ids (M.BVType 32)
-> M.NatRepr w
-> M.Value ARM.AArch32 ids (M.BVType (8 TL.* w)) -> M.Value ARM.AArch32 ids (M.BVType (8 TL.* w))
-> G.Generator ARM.AArch32 ids s (M.Value ARM.AArch32 ids tp) -> G.Generator ARM.AArch32 ids s (ARMWriteAction ids s ARMWriteMemory)
writeMem mem addr sz val = do writeMem sz mem addr val = return $ do
wm <- getWriteMode _ <- mem
case wm of ARMWriteAction $ G.addStmt (M.WriteMem addr (M.BVMemRepr sz M.LittleEndian) val)
WriteMemory -> do return $ ARMWriteMemory
G.addStmt (M.WriteMem addr (M.BVMemRepr sz M.LittleEndian) val)
return mem
_ -> return mem
setGPR :: M.Value ARM.AArch32 ids tp setGPR :: ARMWriteAction ids s ARMWriteGPRs
-> M.Value ARM.AArch32 ids (M.BVType 4) -> M.Value ARM.AArch32 ids (M.BVType 4)
-> M.Value ARM.AArch32 ids (M.BVType 32) -> M.Value ARM.AArch32 ids (M.BVType 32)
-> G.Generator ARM.AArch32 ids s (M.Value ARM.AArch32 ids tp) -> G.Generator ARM.AArch32 ids s (ARMWriteAction ids s ARMWriteGPRs)
setGPR handle regid v = do setGPR handle regid v = do
reg <- case regid of reg <- case regid of
M.BVValue w i M.BVValue w i
| intValue w == 4 | intValue w == 4
, Just reg <- integerToReg i -> return reg , Just reg <- integerToReg i -> return reg
_ -> E.throwError (G.GeneratorMessage $ "Bad GPR identifier (uf_gpr_set): " <> show (M.ppValueAssignments v)) _ -> E.throwError (G.GeneratorMessage $ "Bad GPR identifier (uf_gpr_set): " <> show (M.ppValueAssignments v))
getWriteMode >>= \case return $ do
WriteGPRs -> G.setRegVal reg v _ <- handle
_ -> return () ARMWriteAction $ G.setRegVal reg v
return handle return $ ARMWriteGPRs
getGPR :: M.Value ARM.AArch32 ids tp getGPR :: M.Value ARM.AArch32 ids tp
-> G.Generator ARM.AArch32 ids s (M.Value ARM.AArch32 ids (M.BVType 32)) -> G.Generator ARM.AArch32 ids s (M.Value ARM.AArch32 ids (M.BVType 32))
@ -487,20 +462,21 @@ getGPR v = do
_ -> E.throwError (G.GeneratorMessage $ "Bad GPR identifier (uf_gpr_get): " <> show (M.ppValueAssignments v)) _ -> E.throwError (G.GeneratorMessage $ "Bad GPR identifier (uf_gpr_get): " <> show (M.ppValueAssignments v))
G.getRegSnapshotVal reg G.getRegSnapshotVal reg
setSIMD :: M.Value ARM.AArch32 ids tp setSIMD :: ARMWriteAction ids s ARMWriteSIMDs
-> M.Value ARM.AArch32 ids (M.BVType 8) -> M.Value ARM.AArch32 ids (M.BVType 8)
-> M.Value ARM.AArch32 ids (M.BVType 128) -> M.Value ARM.AArch32 ids (M.BVType 128)
-> G.Generator ARM.AArch32 ids s (M.Value ARM.AArch32 ids tp) -> G.Generator ARM.AArch32 ids s (ARMWriteAction ids s ARMWriteSIMDs)
setSIMD handle regid v = do setSIMD handle regid v = do
reg <- case regid of reg <- case regid of
M.BVValue w i M.BVValue w i
| intValue w == 8 | intValue w == 8
, Just reg <- integerToSIMDReg i -> return reg , Just reg <- integerToSIMDReg i -> return reg
_ -> E.throwError (G.GeneratorMessage $ "Bad SIMD identifier (uf_simd_set): " <> show (M.ppValueAssignments v)) _ -> E.throwError (G.GeneratorMessage $ "Bad SIMD identifier (uf_simd_set): " <> show (M.ppValueAssignments v))
getWriteMode >>= \case return $ do
WriteSIMDs -> G.setRegVal reg v _ <- handle
_ -> return () ARMWriteAction $ G.setRegVal reg v
return handle return $ ARMWriteSIMDs
getSIMD :: M.Value ARM.AArch32 ids tp getSIMD :: M.Value ARM.AArch32 ids tp
-> G.Generator ARM.AArch32 ids s (M.Value ARM.AArch32 ids (M.BVType 128)) -> G.Generator ARM.AArch32 ids s (M.Value ARM.AArch32 ids (M.BVType 128))
@ -510,13 +486,7 @@ getSIMD v = do
| intValue w == 8 | intValue w == 8
, Just reg <- integerToSIMDReg i -> return reg , Just reg <- integerToSIMDReg i -> return reg
_ -> E.throwError (G.GeneratorMessage $ "Bad SIMD identifier (uf_simd_get): " <> show (M.ppValueAssignments v)) _ -> E.throwError (G.GeneratorMessage $ "Bad SIMD identifier (uf_simd_get): " <> show (M.ppValueAssignments v))
G.getRegVal reg G.getRegSnapshotVal reg
what4TypeTH :: WT.BaseTypeRepr tp -> Q Exp
what4TypeTH (WT.BaseBVRepr natRepr) = [| M.BVTypeRepr $(natReprTH natRepr) |]
what4TypeTH WT.BaseBoolRepr = [| M.BoolTypeRepr |]
what4TypeTH tp = error $ "Unsupported base type: " <> show tp
-- ---------------------------------------------------------------------- -- ----------------------------------------------------------------------
@ -536,33 +506,17 @@ isPlaceholderType tp = case tp of
_ | Just Refl <- testEquality tp (knownRepr :: WT.BaseTypeRepr ASL.AllSIMDBaseType) -> True _ | Just Refl <- testEquality tp (knownRepr :: WT.BaseTypeRepr ASL.AllSIMDBaseType) -> True
_ -> False _ -> False
-- | This combinator provides conditional evaluation of its branches -- | For placeholder types, we can't translate them into a Mux, and so we
-- -- need to rely on the conditional being resolved to a concrete value so
-- Many conditionals in the semantics are translated as muxes (effectively -- we can translate it into a haskell if-then-else.
-- if-then-else expressions). This is great most of the time, but problematic
-- if the branches include side effects (e.g., memory writes). We only want concreteIte :: M.Value ARM.AArch32 ids (M.BoolType)
-- side effects to happen if the condition really is true. -> a
-- -> a
-- This combinator checks to see if the condition is concretely true or false -> a
-- (as expected) and then evaluates the corresponding 'G.Generator' action. concreteIte v t f = case v of
--
-- It is meant to be used in a context like:
--
-- > val <- concreteIte condition trueThings falseThings
--
-- where @condition@ has type Value and the branches have type 'G.Generator'
-- 'M.Value' (i.e., the branches get to return a value).
--
-- NOTE: This function panics (and throws an error) if the argument is not
-- concrete.
concreteIte :: M.TypeRepr tp
-> M.Value ARM.AArch32 ids (M.BoolType)
-> G.Generator ARM.AArch32 ids s (M.Value ARM.AArch32 ids tp)
-> G.Generator ARM.AArch32 ids s (M.Value ARM.AArch32 ids tp)
-> G.Generator ARM.AArch32 ids s (M.Value ARM.AArch32 ids tp)
concreteIte rep 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
_ -> G.addExpr =<< G.AppExpr <$> (M.Mux rep v <$> t <*> f) _ -> error "concreteIte: value must be concrete"
-- | A smart constructor for division -- | A smart constructor for division
-- --
@ -583,6 +537,57 @@ sdiv repr dividend divisor =
in G.ValueExpr <$> G.addExpr (G.AppExpr app) in G.ValueExpr <$> G.addExpr (G.AppExpr app)
_ -> addArchAssignment (SDiv repr dividend divisor) _ -> addArchAssignment (SDiv repr dividend divisor)
mkTupT :: [TypeQ] -> Q Type
mkTupT [t] = t
mkTupT ts = foldl appT (tupleT (length ts)) ts
armTranslateType :: Q Type
-> Q Type
-> WT.BaseTypeRepr tp
-> Maybe (Q Type)
armTranslateType idsTy sTy tp = case tp of
WT.BaseStructRepr reprs -> Just $ mkTupT $ FC.toListFC translateBaseType reprs
_ | isPlaceholderType tp -> Just $ translateBaseType tp
_ -> Nothing
where
translateBaseType :: forall tp'. WT.BaseTypeRepr tp' -> Q Type
translateBaseType tp' = case tp' of
_ | Just Refl <- testEquality tp' (knownRepr :: WT.BaseTypeRepr ASL.MemoryBaseType) ->
[t| ARMWriteAction $(idsTy) $(sTy) ARMWriteMemory |]
_ | Just Refl <- testEquality tp' (knownRepr :: WT.BaseTypeRepr ASL.AllSIMDBaseType) ->
[t| ARMWriteAction $(idsTy) $(sTy) ARMWriteSIMDs |]
_ | Just Refl <- testEquality tp' (knownRepr :: WT.BaseTypeRepr ASL.AllGPRBaseType) ->
[t| ARMWriteAction $(idsTy) $(sTy) ARMWriteGPRs |]
WT.BaseBoolRepr -> [t| M.Value ARM.AArch32 $(idsTy) M.BoolType |]
WT.BaseBVRepr n -> [t| M.Value ARM.AArch32 $(idsTy) (M.BVType $(litT (numTyLit (intValue n)))) |]
_ -> fail $ "unsupported base type: " ++ show tp
extractTuple :: Int -> Int -> Q Exp
extractTuple len i = do
nm <- newName "x"
let pat = tupP $ [ if i' == i then varP nm else wildP | i' <- [0..len-1] ]
lamE [pat] (varE nm)
joinTuple :: [ExpQ] -> Q Exp
joinTuple es = go [] es
where
go :: [Name] -> [ExpQ] -> Q Exp
go ns (e : es') = do
n <- newName "bval"
[| $(e) >>= $(lamE [varP n] (go (n : ns) es')) |]
go ns [] = [| return $(tupE $ map varE (reverse ns)) |]
refField :: Ctx.Size ctx -> Ctx.Index ctx tp -> BoundExp -> MacawQ arch t fs BoundExp
refField sz idx e = case Ctx.viewSize sz of
Ctx.IncSize sz' | Ctx.ZeroSize <- Ctx.viewSize sz' -> return e
_ -> case e of
EagerBoundExp (TupE es) | Ctx.indexVal idx < length es -> return $ EagerBoundExp $ es !! (Ctx.indexVal idx)
EagerBoundExp _ -> bindTH [| $(extractTuple (Ctx.sizeInt sz) (Ctx.indexVal idx)) $(refEager e) |]
LazyBoundExp _ -> letTH [| $(extractTuple (Ctx.sizeInt sz) (Ctx.indexVal idx)) <$> $(refBinding e) |]
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
@ -590,41 +595,60 @@ armAppEvaluator :: M.Endianness
armAppEvaluator endianness interps elt = armAppEvaluator endianness interps elt =
case elt of case elt of
WB.BaseIte bt _ test t f | isPlaceholderType bt -> return $ do WB.BaseIte bt _ test t f | isPlaceholderType bt -> return $ do
-- NOTE: This case is very special. The placeholder types denote -- In this case, the placeholder type indicates that
-- conditionals that are guarding the state update functions with -- expression is to be translated as a (wrapped) stateful action
-- mutation. -- rather than an actual macaw term. This is therefore translated
-- -- as a Haskell if-then-else statement, rather than
-- We need to ensure that state updates are only done lazily. This -- a Mux.
-- works because the arguments to the branches are expressions in the
-- Generator monad. We can do this translation while preserving sharing
-- by turning every recursively-traversed term into a let binding at the
-- top-level. After that, we can build bodies for the "arms" of the
-- concreteIte that instantiate those terms in the appropriate monadic
-- context. It is slightly problematic that the core TH translation
-- doesn't really support that because it wants to (more efficiently)
-- evaluate all of the monadic stuff. However, we don't need quite as
-- much generality for this code, so maybe a smaller core that just does
-- all of the necessary applicative binding of 'Generator' terms will be
-- sufficient.
testE <- addEltTH endianness interps test testE <- addEltTH endianness interps test
inConditionalContext $ do tE <- addEltTH endianness interps t
tE <- addEltTH endianness interps t fE <- addEltTH endianness interps f
fE <- addEltTH endianness interps f case all isEager [testE, tE, fE] of
liftQ [| join (concreteIte PC.knownRepr <$> $(refBinding testE) <*> (return $(refBinding tE)) <*> (return $(refBinding fE))) |] True -> liftQ [| return $ concreteIte $(refEager testE) $(refEager tE) $(refEager fE) |]
False -> liftQ [| concreteIte <$> $(refBinding testE) <*> $(refBinding tE) <*> $(refBinding fE) |]
WB.StructField struct _ _ |
(WT.BaseStructRepr (Ctx.Empty Ctx.:> _)) <- WB.exprType struct -> Just $ do
structE <- addEltTH endianness interps struct
extractBound structE
WB.StructField struct idx _ -> Just $ do
WT.BaseStructRepr reprs <- return $ WB.exprType struct
bnd <- lookupElt struct >>= \case
Just bnd -> return bnd
Nothing -> do
bnd <- addEltTH endianness interps struct
case isEager bnd of
True -> do
nms <- sequence $ FC.toListFC (\_ -> liftQ (newName "lval")) reprs
letBindPat struct (tupP $ map varP nms, tupE $ map varE nms) (refEager bnd)
res <- liftQ $ tupE $ map varE nms
return $ EagerBoundExp res
False -> return bnd
fldBnd <- refField (Ctx.size reprs) idx bnd
extractBound fldBnd
WB.StructCtor _ (Ctx.Empty Ctx.:> e) -> Just $ do
bnd <- addEltTH endianness interps e
extractBound bnd
WB.StructCtor _ flds -> Just $ do
fldEs <- sequence $ FC.toListFC (addEltTH endianness interps) flds
case all isEager fldEs of
True -> liftQ $ [| return $(tupE (map refEager fldEs)) |]
False -> liftQ $ joinTuple (map refBinding fldEs)
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 [| G.addExpr =<< join (sdiv $(natReprTH w) <$> $(refBinding e1) <*> $(refBinding e2)) |] liftQ [| G.addExpr =<< $(joinOp2 [| sdiv $(natReprTH w) |] e1 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
liftQ [| G.addExpr =<< join (addArchAssignment <$> (URem $(natReprTH w) <$> $(refBinding e1) <*> $(refBinding e2))) liftQ [| G.addExpr =<< $(joinOp2 [| \e1E e2E -> addArchAssignment (URem $(natReprTH w) e1E e2E) |] e1 e2) |]
|]
WB.BVSrem w bv1 bv2 -> return $ do WB.BVSrem 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 [| G.addExpr =<< join (addArchAssignment <$> (SRem $(natReprTH w) <$> $(refBinding e1) <*> $(refBinding e2))) liftQ [| G.addExpr =<< $(joinOp2 [| \e1E e2E -> addArchAssignment (SRem $(natReprTH w) e1E e2E) |] e1 e2) |]
|]
WB.IntegerToBV _ _ -> return $ liftQ [| error "IntegerToBV" |] WB.IntegerToBV _ _ -> return $ liftQ [| error "IntegerToBV" |]
WB.SBVToInteger _ -> return $ liftQ [| error "SBVToInteger" |] WB.SBVToInteger _ -> return $ liftQ [| error "SBVToInteger" |]
WB.BaseIte bt _ test t f -> WB.BaseIte bt _ test t f ->

View File

@ -24,7 +24,9 @@ instance MSS.SimplifierExtension ARM.AArch32 where
coalesceAdditionByConstant a <|> coalesceAdditionByConstant a <|>
simplifyNestedMux a <|> simplifyNestedMux a <|>
distributeAddOverConstantMux a <|> distributeAddOverConstantMux a <|>
doubleNegation a doubleNegation a <|>
negatedTrivialMux a <|>
negatedMux a
simplifyArchFn = armSimplifyArchFn simplifyArchFn = armSimplifyArchFn
armSimplifyArchFn :: MC.ArchFn ARM.AArch32 (MC.Value ARM.AArch32 ids) tp armSimplifyArchFn :: MC.ArchFn ARM.AArch32 (MC.Value ARM.AArch32 ids) tp
@ -154,4 +156,21 @@ doubleNegation app = do
MC.NotApp r2 <- MC.valueAsApp r1 MC.NotApp r2 <- MC.valueAsApp r1
MC.valueAsApp r2 MC.valueAsApp r2
negatedTrivialMux :: MC.App (MC.Value ARM.AArch32 ids) tp
-> Maybe (MC.App (MC.Value ARM.AArch32 ids) tp)
negatedTrivialMux app = case app of
MC.Mux _ cond (MC.BoolValue False) (MC.BoolValue True) ->
case MSS.simplifyArchApp (MC.NotApp cond) of
Just app' -> return app'
_ -> return $ MC.NotApp cond
_ -> Nothing
negatedMux :: MC.App (MC.Value ARM.AArch32 ids) tp
-> Maybe (MC.App (MC.Value ARM.AArch32 ids) tp)
negatedMux app = do
MC.Mux rep c l r <- return app
MC.NotApp c' <- MC.valueAsApp c
return $ MC.Mux rep c' r l
-- Potentially Normalize negations? -- Potentially Normalize negations?

View File

@ -54,6 +54,7 @@ execInstruction =
, archTypeQ = [t| (SP.AnyPPC SP.V32) |] , archTypeQ = [t| (SP.AnyPPC SP.V32) |]
, genLibraryFunction = \_ -> True , genLibraryFunction = \_ -> True
, genOpcodeCase = genOpc , genOpcodeCase = genOpc
, archTranslateType = \_ _ _ -> Nothing
} }
genExecInstruction proxy genExecInstruction proxy

View File

@ -55,6 +55,7 @@ execInstruction =
, archTypeQ = [t| (SP.AnyPPC SP.V64) |] , archTypeQ = [t| (SP.AnyPPC SP.V64) |]
, genLibraryFunction = \_ -> True , genLibraryFunction = \_ -> True
, genOpcodeCase = genOpc , genOpcodeCase = genOpc
, archTranslateType = \_ _ _ -> Nothing
} }
genExecInstruction proxy genExecInstruction proxy

View File

@ -96,6 +96,8 @@ simplifyApp a =
BVUnsignedLt v1 v2 -> unsignedRelOp (<) v1 v2 BVUnsignedLt v1 v2 -> unsignedRelOp (<) v1 v2
Mux _ _ t f Mux _ _ t f
| Just Refl <- testEquality t f -> Just t | Just Refl <- testEquality t f -> Just t
Mux _ c (BoolValue True) (BoolValue False) -> Just c
Mux _ c (BoolValue False) (BoolValue True) -> simplifyApp (NotApp c)
_ -> Nothing _ -> Nothing
where where
unop :: forall n . (tp ~ BVType n) unop :: forall n . (tp ~ BVType n)

View File

@ -36,18 +36,21 @@ module Data.Macaw.SemMC.TH (
floatInfoTH, floatInfoTH,
floatInfoFromPrecisionTH, floatInfoFromPrecisionTH,
symFnName, symFnName,
asName asName,
translateBaseType,
translateBaseTypeRepr
) where ) where
import GHC.TypeLits ( Symbol ) import GHC.TypeLits ( Symbol )
import Control.Lens ( (^.) ) import Control.Lens ( (^.) )
import Control.Monad ( ap, join, void ) import Control.Monad ( ap, join, void, liftM, foldM, forM )
import qualified Control.Concurrent.Async as Async import qualified Control.Concurrent.Async as Async
import qualified Data.Functor.Const as C import qualified Data.Functor.Const as C
import Data.Functor.Product import Data.Functor.Product
import qualified Data.Foldable as F import qualified Data.Foldable as F
import qualified Data.List as L import qualified Data.List as L
import qualified Data.List.NonEmpty as NE
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Maybe ( fromMaybe ) import Data.Maybe ( fromMaybe )
import Data.Proxy ( Proxy(..) ) import Data.Proxy ( Proxy(..) )
@ -568,11 +571,12 @@ translateFunction thConf fnName df ff = do
idsTy <- varT <$> newName "ids" idsTy <- varT <$> newName "ids"
sTy <- varT <$> newName "s" sTy <- varT <$> newName "s"
let translate :: forall tp. CT.BaseTypeRepr tp -> Q Type let translate :: forall tp. CT.BaseTypeRepr tp -> Q Type
translate tp = translate tp = case archTranslateType thConf idsTy sTy tp of
[t| M.Value $(archTypeQ thConf) $(idsTy) $(translateBaseType tp) |] Just t -> t
Nothing -> [t| M.Value $(archTypeQ thConf) $(idsTy) $(translateBaseType tp) |]
argHsTys = FC.toListFC translate (ffArgTypes ff) argHsTys = FC.toListFC translate (ffArgTypes ff)
retHsTy = [t| G.Generator $(archTypeQ thConf) $(idsTy) $(sTy) retHsTy = [t| G.Generator $(archTypeQ thConf) $(idsTy) $(sTy) $(translate (ffRetType ff)) |]
$(translate (ffRetType ff)) |]
ty = foldr (\a r -> [t| $(a) -> $(r) |]) retHsTy argHsTys ty = foldr (\a r -> [t| $(a) -> $(r) |]) retHsTy argHsTys
body = doE (map return stmts) body = doE (map return stmts)
sig <- sigD var ty sig <- sigD var ty
@ -580,10 +584,16 @@ translateFunction thConf fnName df ff = do
return (var, sig, def) return (var, sig, def)
translateBaseType :: CT.BaseTypeRepr tp -> Q Type translateBaseType :: CT.BaseTypeRepr tp -> Q Type
translateBaseType tp = translateBaseType tp = case tp of
CT.BaseBoolRepr -> [t| M.BoolType |]
CT.BaseBVRepr n -> appT [t| M.BVType |] (litT (numTyLit (intValue n)))
_ -> fail $ "unsupported base type: " ++ show tp
translateBaseTypeRepr :: CT.BaseTypeRepr tp -> Q Exp
translateBaseTypeRepr tp =
case tp of case tp of
CT.BaseBoolRepr -> [t| M.BoolType |] CT.BaseBoolRepr -> [| M.BoolTypeRepr |]
CT.BaseBVRepr n -> appT [t| M.BVType |] (litT (numTyLit (intValue n))) CT.BaseBVRepr n -> [| M.BVTypeRepr $(natReprTH n) |]
_ -> fail $ "unsupported base type: " ++ show tp _ -> fail $ "unsupported base type: " ++ show tp
-- | wrapper around bitvector constants that forces some type -- | wrapper around bitvector constants that forces some type
@ -616,7 +626,10 @@ addEltTH endianness interps elt = do
-- for now. Once that works, we can be smarter and translate what we -- for now. Once that works, we can be smarter and translate what we
-- can eagerly. -- can eagerly.
genExpr <- appToExprTH endianness (S.appExprApp appElt) interps genExpr <- appToExprTH endianness (S.appExprApp appElt) interps
letBindExpr elt genExpr istl <- isTopLevel
if istl
then bindExpr elt (return genExpr)
else letBindExpr elt genExpr
S.BoundVarExpr bVar -> do S.BoundVarExpr bVar -> do
x <- evalBoundVar interps bVar x <- evalBoundVar interps bVar
letBindPureExpr elt [| $(return x) |] letBindPureExpr elt [| $(return x) |]
@ -655,7 +668,7 @@ evalBoundVar interps bVar =
return (VarE name) return (VarE name)
| otherwise -> fail $ "bound var not found: " ++ show bVar | otherwise -> fail $ "bound var not found: " ++ show bVar
symFnName :: S.ExprSymFn t args ret -> String symFnName :: S.ExprSymFn t (S.Expr t) args ret -> String
symFnName = T.unpack . Sy.solverSymbolAsText . S.symFnName symFnName = T.unpack . Sy.solverSymbolAsText . S.symFnName
bvarName :: S.ExprBoundVar t tp -> String bvarName :: S.ExprBoundVar t tp -> String
@ -666,7 +679,7 @@ bvarName = T.unpack . Sy.solverSymbolAsText . S.bvarName
writeMemTH :: forall arch t fs args ret writeMemTH :: forall arch t fs args ret
. (A.Architecture arch) . (A.Architecture arch)
=> BoundVarInterpretations arch t fs => BoundVarInterpretations arch t fs
-> S.ExprSymFn t args ret -> S.ExprSymFn t (S.Expr t) args ret
-> Ctx.Assignment (S.Expr t) args -> Ctx.Assignment (S.Expr t) args
-> M.Endianness -> M.Endianness
-> MacawQ arch t fs (Some (S.Expr t)) -> MacawQ arch t fs (Some (S.Expr t))
@ -726,10 +739,11 @@ defaultNonceAppEvaluator endianness bvi nonceApp =
case funMaybe of case funMaybe of
Just fun -> do Just fun -> do
argExprs <- sequence $ FC.toListFC (addEltTH endianness bvi) args argExprs <- sequence $ FC.toListFC (addEltTH endianness bvi) args
let applyQ e be = [| $(e) `ap` $(refBinding be) |] case all isEager argExprs of
-- FIXME: Check if all argExprs are 'EagerBoundExp's; if so, generate True -> liftQ $ foldl appE (return fun) (map refEager argExprs)
-- a pure let bound version instead False -> do
liftQ [| join $(foldl applyQ [| return $(return fun) |] argExprs) |] let applyQ e be = [| $(e) `ap` $(refBinding be) |]
liftQ [| join $(foldl applyQ [| return $(return fun) |] argExprs) |]
_ -> do _ -> do
let fnArgTypes = S.symFnArgTypes symFn let fnArgTypes = S.symFnArgTypes symFn
fnRetType = S.symFnReturnType symFn fnRetType = S.symFnReturnType symFn
@ -834,27 +848,27 @@ defaultAppEvaluator :: (A.Architecture arch)
defaultAppEvaluator endianness elt interps = case elt of defaultAppEvaluator endianness elt interps = case elt of
S.NotPred bool -> do S.NotPred bool -> do
e <- addEltTH endianness interps bool e <- addEltTH endianness interps bool
liftQ [| addApp =<< (M.NotApp <$> $(refBinding e)) |] liftQ $ joinPure1 [| addApp |] [| M.NotApp |] e
S.ConjPred boolmap -> evalBoolMap endianness interps AndOp True boolmap >>= extractBound S.ConjPred boolmap -> do
l <- boolMapList (addEltTH endianness interps) boolmap
case all (\(bnd,_) -> isEager bnd) l of
True -> do
tms <- liftQ $ listE $ map (\(bnd, b) -> (tupE [refEager bnd, lift b])) l
liftQ [| allPreds $(return tms) |]
False -> do
tms <- liftQ $ listE $ map (\(bnd, b) -> (tupE [refBinding bnd, lift b])) l
liftQ [| joinPreds $(return tms) |]
S.BaseIte bt _ test t f -> do S.BaseIte bt _ test t f -> do
-- FIXME: Generate code that dynamically checks for a concrete condition and -- FIXME: Generate code that dynamically checks for a concrete condition and
-- make an ite instead of a mux if possible -- make an ite instead of a mux if possible
testE <- addEltTH endianness interps test testE <- addEltTH endianness interps test
tE <- addEltTH endianness interps t tE <- addEltTH endianness interps t
fE <- addEltTH endianness interps f fE <- addEltTH endianness interps f
case bt of case bt of
CT.BaseBoolRepr -> liftQ [| addApp =<< CT.BaseBoolRepr -> liftQ $ joinPure3 [| addApp |] [| M.Mux M.BoolTypeRepr |] testE tE fE
(M.Mux M.BoolTypeRepr <$> CT.BaseBVRepr w -> liftQ $ joinPure3 [| addApp |] [| M.Mux (M.BVTypeRepr $(natReprTH w)) |] testE tE fE
$(refBinding testE) <*> $(refBinding tE) <*> $(refBinding fE)) CT.BaseFloatRepr fpp -> liftQ $ joinPure3 [| addApp |] [| M.Mux (M.FloatTypeRepr $(floatInfoFromPrecisionTH fpp)) |] testE tE fE
|]
CT.BaseBVRepr w -> liftQ [| addApp =<<
(M.Mux (M.BVTypeRepr $(natReprTH w)) <$>
$(refBinding testE) <*> $(refBinding tE) <*> $(refBinding fE))
|]
CT.BaseFloatRepr fpp -> liftQ [| addApp =<<
(M.Mux (M.FloatTypeRepr $(floatInfoFromPrecisionTH fpp)) <$>
$(refBinding testE) <*> $(refBinding tE) <*> $(refBinding fE))
|]
CT.BaseNatRepr -> liftQ [| error "Macaw semantics for nat ITE unsupported" |] CT.BaseNatRepr -> liftQ [| error "Macaw semantics for nat ITE unsupported" |]
CT.BaseIntegerRepr -> liftQ [| error "Macaw semantics for integer ITE unsupported" |] CT.BaseIntegerRepr -> liftQ [| error "Macaw semantics for integer ITE unsupported" |]
CT.BaseRealRepr -> liftQ [| error "Macaw semantics for real ITE unsupported" |] CT.BaseRealRepr -> liftQ [| error "Macaw semantics for real ITE unsupported" |]
@ -866,27 +880,27 @@ defaultAppEvaluator endianness elt interps = case elt of
S.BaseEq _bt bv1 bv2 -> do S.BaseEq _bt bv1 bv2 -> do
e1 <- addEltTH endianness interps bv1 e1 <- addEltTH endianness interps bv1
e2 <- addEltTH endianness interps bv2 e2 <- addEltTH endianness interps bv2
liftQ [| addApp =<< (M.Eq <$> $(refBinding e1) <*> $(refBinding e2)) |] liftQ $ joinPure2 [| addApp |] [| M.Eq |] e1 e2
S.BVSlt bv1 bv2 -> do S.BVSlt bv1 bv2 -> do
e1 <- addEltTH endianness interps bv1 e1 <- addEltTH endianness interps bv1
e2 <- addEltTH endianness interps bv2 e2 <- addEltTH endianness interps bv2
liftQ [| addApp =<< (M.BVSignedLt <$> $(refBinding e1) <*> $(refBinding e2)) |] liftQ $ joinPure2 [| addApp |] [| M.BVSignedLt |] e1 e2
S.BVUlt bv1 bv2 -> do S.BVUlt bv1 bv2 -> do
e1 <- addEltTH endianness interps bv1 e1 <- addEltTH endianness interps bv1
e2 <- addEltTH endianness interps bv2 e2 <- addEltTH endianness interps bv2
liftQ [| addApp =<< (M.BVUnsignedLt <$> $(refBinding e1) <*> $(refBinding e2)) |] liftQ $ joinPure2 [| addApp |] [| M.BVUnsignedLt |] e1 e2
S.BVConcat w bv1 bv2 -> do S.BVConcat w bv1 bv2 -> do
let u = S.bvWidth bv1 let u = S.bvWidth bv1
v = S.bvWidth bv2 v = S.bvWidth bv2
e1 <- addEltTH endianness interps bv1 e1 <- addEltTH endianness interps bv1
e2 <- addEltTH endianness interps bv2 e2 <- addEltTH endianness interps bv2
liftQ [| G.addExpr =<< join ((TR.bvconcat <$> $(refBinding e1) <*> $(refBinding e2) <*> pure $(natReprTH v) <*> pure $(natReprTH u) <*> pure $(natReprTH w))) |] liftQ $ [| G.addExpr =<< $(joinOp2 [| \v1 v2 -> TR.bvconcat v1 v2 $(natReprTH v) $(natReprTH u) $(natReprTH w) |] e1 e2) |]
S.BVSelect idx n bv -> do S.BVSelect idx n bv -> do
let w = S.bvWidth bv let w = S.bvWidth bv
case natValue n + 1 <= natValue w of case natValue n + 1 <= natValue w of
True -> do True -> do
e <- addEltTH endianness interps bv e <- addEltTH endianness interps bv
liftQ [| G.addExpr =<< join ((TR.bvselect <$> $(refBinding e) <*> pure $(natReprTH n) <*> pure $(natReprTH idx) <*> pure $(natReprTH w))) |] liftQ [| G.addExpr =<< $(joinOp1 [| \v1 -> TR.bvselect v1 $(natReprTH n) $(natReprTH idx) $(natReprTH w) |] e) |]
False -> do False -> do
e <- addEltTH endianness interps bv e <- addEltTH endianness interps bv
liftQ [| case testEquality $(natReprTH n) $(natReprTH w) of liftQ [| case testEquality $(natReprTH n) $(natReprTH w) of
@ -895,42 +909,43 @@ defaultAppEvaluator endianness elt interps = case elt of
|] |]
S.BVTestBit idx bv -> do S.BVTestBit idx bv -> do
bvValExp <- addEltTH endianness interps bv bvValExp <- addEltTH endianness interps bv
liftQ [| addApp =<< (M.BVTestBit (M.BVValue $(natReprTH (S.bvWidth bv)) $(lift (toInteger idx))) <$> $(refBinding bvValExp)) liftQ $ joinPure1 [| addApp |] [| M.BVTestBit (M.BVValue $(natReprTH (S.bvWidth bv)) $(lift (toInteger idx))) |] bvValExp
|]
S.SemiRingSum sm -> S.SemiRingSum sm ->
case WSum.sumRepr sm of case WSum.sumRepr sm of
SR.SemiRingBVRepr SR.BVArithRepr w -> SR.SemiRingBVRepr fl w ->
let smul mul e = do y <- addEltTH endianness interps e let smul mul e = do
letTH [| addApp =<< (M.BVMul $(natReprTH w) (M.BVValue $(natReprTH w) $(lift (BVS.asUnsigned mul))) <$> $(refBinding y)) y <- addEltTH endianness interps e
|] return $ [(y, BVS.asUnsigned mul)]
one = case fl of
SR.BVArithRepr -> 1
SR.BVBitsRepr -> SI.maxUnsigned w
sval v = do sval v = do
EagerBoundExp <$> liftQ [| M.BVValue $(natReprTH w) $(lift (BVS.asUnsigned v)) |] bnd <- EagerBoundExp <$> liftQ [| M.BVValue $(natReprTH w) $(lift (BVS.asUnsigned v)) |]
add x y = do return $ [(bnd, one)]
letTH [| addApp =<< (M.BVAdd $(natReprTH w) <$> $(refBinding x) <*> $(refBinding y)) |]
in WSum.evalM add smul sval sm >>= extractBound in do
SR.SemiRingBVRepr SR.BVBitsRepr w -> bnds <- WSum.evalM (\a b -> return $ a ++ b) smul sval sm
let smul mul e = do y <- addEltTH endianness interps e case all (\(bnd,_) -> isEager bnd) bnds of
letTH [| addApp =<< (M.BVAnd $(natReprTH w) (M.BVValue $(natReprTH w) $(lift (BVS.asUnsigned mul))) <$> $(refBinding y)) True -> do
|] tms <- liftQ $ listE $ map (\(bnd, i) -> (tupE [refEager bnd, lift i])) bnds
sval v = do liftQ [| sumBVs $(liftFlavor fl) $(natReprTH w) $(return tms) |]
EagerBoundExp <$> liftQ [| M.BVValue $(natReprTH w) $(lift (BVS.asUnsigned v)) |] False -> do
add x y = do tms <- liftQ $ listE $ map (\(bnd, i) -> (tupE [refBinding bnd, lift i])) bnds
letTH [| addApp =<< (M.BVXor $(natReprTH w) <$> $(refBinding x) <*> $(refBinding y)) |] liftQ [| joinSumBVs $(liftFlavor fl) $(natReprTH w) $(return tms) |]
in WSum.evalM add smul sval sm >>= extractBound
_ -> liftQ [| error "unsupported SemiRingSum repr for macaw semmc TH" |] _ -> liftQ [| error "unsupported SemiRingSum repr for macaw semmc TH" |]
S.SemiRingProd pd -> S.SemiRingProd pd ->
case WSum.prodRepr pd of case WSum.prodRepr pd of
SR.SemiRingBVRepr SR.BVArithRepr w -> SR.SemiRingBVRepr SR.BVArithRepr w ->
let pmul x y = do let pmul x y = do
letTH [| addApp =<< (M.BVMul $(natReprTH w) <$> $(refBinding x) <*> $(refBinding y)) |] bindPure2 [| addApp |] [| M.BVMul $(natReprTH w) |] x y
unit = liftQ [| pure (M.BVValue $(natReprTH w) 1) |] unit = liftQ [| pure (M.BVValue $(natReprTH w) 1) |]
convert = addEltTH endianness interps convert = addEltTH endianness interps
in WSum.prodEvalM pmul convert pd >>= maybe unit extractBound in WSum.prodEvalM pmul convert pd >>= maybe unit extractBound
SR.SemiRingBVRepr SR.BVBitsRepr w -> SR.SemiRingBVRepr SR.BVBitsRepr w ->
let pmul x y = do let pmul x y = do
letTH [| addApp =<< (M.BVAnd $(natReprTH w) <$> $(refBinding x) <*> $(refBinding y)) |] bindPure2 [| addApp |] [| M.BVAnd $(natReprTH w) |] x y
unit = liftQ [| pure (M.BVValue $(natReprTH w) $(lift $ SI.maxUnsigned w)) |] unit = liftQ [| pure (M.BVValue $(natReprTH w) $(lift $ SI.maxUnsigned w)) |]
convert = addEltTH endianness interps convert = addEltTH endianness interps
in WSum.prodEvalM pmul convert pd >>= maybe unit extractBound in WSum.prodEvalM pmul convert pd >>= maybe unit extractBound
@ -942,70 +957,124 @@ defaultAppEvaluator endianness elt interps = case elt of
-- These are all TH Exprs that are of the (Macaw) Value at run-time -- These are all TH Exprs that are of the (Macaw) Value at run-time
bs' <- mapM (addEltTH endianness interps) (S.bvOrToList bs) bs' <- mapM (addEltTH endianness interps) (S.bvOrToList bs)
let por x y = do let por x y = do
letTH [| addApp =<< (M.BVOr $(natReprTH w) <$> $(refBinding x) <*> $(refBinding y)) |] bindPure2 [| addApp |] [| M.BVOr $(natReprTH w) |] x y
F.foldrM por (EagerBoundExp zero) bs' >>= extractBound F.foldrM por (EagerBoundExp zero) bs' >>= extractBound
S.BVShl w bv1 bv2 -> do S.BVShl w bv1 bv2 -> do
e1 <- addEltTH endianness interps bv1 e1 <- addEltTH endianness interps bv1
e2 <- addEltTH endianness interps bv2 e2 <- addEltTH endianness interps bv2
liftQ [| addApp =<< (M.BVShl $(natReprTH w) <$> $(refBinding e1) <*> $(refBinding e2)) |] liftQ $ joinPure2 [| addApp |] [| M.BVShl $(natReprTH w) |] e1 e2
S.BVLshr w bv1 bv2 -> do S.BVLshr w bv1 bv2 -> do
e1 <- addEltTH endianness interps bv1 e1 <- addEltTH endianness interps bv1
e2 <- addEltTH endianness interps bv2 e2 <- addEltTH endianness interps bv2
liftQ [| addApp =<< (M.BVShr $(natReprTH w) <$> $(refBinding e1) <*> $(refBinding e2)) |] liftQ $ joinPure2 [| addApp |] [| M.BVShr $(natReprTH w) |] e1 e2
S.BVAshr w bv1 bv2 -> do S.BVAshr w bv1 bv2 -> do
e1 <- addEltTH endianness interps bv1 e1 <- addEltTH endianness interps bv1
e2 <- addEltTH endianness interps bv2 e2 <- addEltTH endianness interps bv2
liftQ [| addApp =<< (M.BVSar $(natReprTH w) <$> $(refBinding e1) <*> $(refBinding e2)) |] liftQ $ joinPure2 [| addApp |] [| M.BVSar $(natReprTH w) |] e1 e2
S.BVZext w bv -> do S.BVZext w bv -> do
e <- addEltTH endianness interps bv e <- addEltTH endianness interps bv
liftQ [| addApp =<< (M.UExt <$> $(refBinding e) <*> pure $(natReprTH w)) |] liftQ $ joinPure1 [| addApp |] [| (\x -> M.UExt x $(natReprTH w)) |] e
S.BVSext w bv -> do S.BVSext w bv -> do
e <- addEltTH endianness interps bv e <- addEltTH endianness interps bv
liftQ [| addApp =<< (M.SExt <$> $(refBinding e) <*> pure $(natReprTH w)) |] liftQ $ joinPure1 [| addApp |] [| (\x -> M.SExt x $(natReprTH w)) |] e
_ -> error $ "unsupported Crucible elt: " <> show elt _ -> error $ "unsupported Crucible elt: " <> show elt
---------------------------------------------------------------------- ----------------------------------------------------------------------
data BoolMapOp = AndOp | OrOp getBVOps :: 1 SI.<= n
=> CT.NatRepr n
-> SR.BVFlavorRepr t
-> (M.Value arch ids (M.BVType n)
-> M.Value arch ids (M.BVType n)
-> M.App (M.Value arch ids) (M.BVType n)
, M.Value arch ids (M.BVType n)
-> M.Value arch ids (M.BVType n)
-> M.App (M.Value arch ids) (M.BVType n))
getBVOps repr fl = case fl of
SR.BVArithRepr -> (M.BVMul repr, M.BVAdd repr)
SR.BVBitsRepr -> (M.BVAnd repr, M.BVXor repr)
liftFlavor :: SR.BVFlavorRepr t -> Q Exp
liftFlavor fl = case fl of
SR.BVArithRepr -> [| SR.BVArithRepr |]
SR.BVBitsRepr -> [| SR.BVBitsRepr |]
sumBVs :: 1 SI.<= n
=> ( MSS.SimplifierExtension arch
, OrdF (M.ArchReg arch)
, M.MemWidth (M.RegAddrWidth (M.ArchReg arch))
, ShowF (M.ArchReg arch)
)
=> SR.BVFlavorRepr t
-> CT.NatRepr n
-> [(M.Value arch ids (M.BVType n), Integer)]
-> G.Generator arch ids s (M.Value arch ids (M.BVType n))
sumBVs fl repr vs = do
let (mulOp, addOp) = getBVOps repr fl
vals <- mapM (\(x,y) -> G.addExpr $ G.AppExpr $ mulOp x (M.BVValue repr y)) vs
foldM (\a b -> G.addExpr $ G.AppExpr $ addOp a b) (M.BVValue repr 0) vals
joinSumBVs :: 1 SI.<= n
=> ( MSS.SimplifierExtension arch
, OrdF (M.ArchReg arch)
, M.MemWidth (M.RegAddrWidth (M.ArchReg arch))
, ShowF (M.ArchReg arch)
)
=> SR.BVFlavorRepr t
-> CT.NatRepr n
-> [(G.Generator arch ids s (M.Value arch ids (M.BVType n)), Integer)]
-> G.Generator arch ids s (M.Value arch ids (M.BVType n))
joinSumBVs fl repr vs = do
let (mulOp, addOp) = getBVOps repr fl
vals <- mapM (\(xF,y) -> xF >>= \x -> G.addExpr $ G.AppExpr $ mulOp x (M.BVValue repr y)) vs
foldM (\a b -> G.addExpr $ G.AppExpr $ addOp a b) (M.BVValue repr 0) vals
evalBoolMap :: A.Architecture arch allPreds :: ( MSS.SimplifierExtension arch
=> M.Endianness , OrdF (M.ArchReg arch)
-> BoundVarInterpretations arch t fs , M.MemWidth (M.RegAddrWidth (M.ArchReg arch))
-> BoolMapOp , ShowF (M.ArchReg arch)
-> Bool )
=> [(M.Value arch ids M.BoolType, Bool)]
-> G.Generator arch ids s (M.Value arch ids M.BoolType)
allPreds vs = do
let
mkApp b (a, True) = G.addExpr $ G.AppExpr $ M.AndApp a b
mkApp b (a, False) = do
notA <- G.addExpr $ G.AppExpr $ M.NotApp a
G.addExpr $ G.AppExpr $ M.AndApp notA b
foldM mkApp (M.BoolValue True) vs
joinPreds :: ( MSS.SimplifierExtension arch
, OrdF (M.ArchReg arch)
, M.MemWidth (M.RegAddrWidth (M.ArchReg arch))
, ShowF (M.ArchReg arch)
)
=> [(G.Generator arch ids s (M.Value arch ids M.BoolType), Bool)]
-> G.Generator arch ids s (M.Value arch ids M.BoolType)
joinPreds vs = do
let
mkApp b (aF, True) = do
a <- aF
G.addExpr $ G.AppExpr $ M.AndApp a b
mkApp b (aF, False) = do
a <- aF
notA <- G.addExpr $ G.AppExpr $ M.NotApp a
G.addExpr $ G.AppExpr $ M.AndApp notA b
foldM mkApp (M.BoolValue True) vs
boolMapList :: (forall tp. S.Expr t tp -> MacawQ arch t fs BoundExp)
-> BooM.BoolMap (S.Expr t) -> BooM.BoolMap (S.Expr t)
-> MacawQ arch t fs BoundExp -> MacawQ arch t fs [(BoundExp, Bool)]
evalBoolMap endianness interps op defVal bmap = boolMapList f bm = case BooM.viewBoolMap bm of
case BooM.viewBoolMap bmap of BooM.BoolMapUnit -> return []
BooM.BoolMapUnit -> letTH [| G.addExpr (boolBase $(lift defVal)) |] BooM.BoolMapDualUnit -> do
BooM.BoolMapDualUnit -> letTH [| G.addExpr (bNotBase $(lift defVal)) |] bnd <- EagerBoundExp <$> liftQ [| M.BoolValue False |]
BooM.BoolMapTerms ts -> return $ [(bnd, True)]
do d <- letTH [| G.addExpr (boolBase $(lift defVal)) |] BooM.BoolMapTerms ts -> liftM NE.toList $ forM ts $ \(e, p) -> do
F.foldl (joinBool endianness interps op) (return d) ts eE <- f e
return $ (eE, p == BooM.Positive)
boolBase, bNotBase :: A.Architecture arch => Bool -> G.Expr arch t 'M.BoolType
boolBase = G.ValueExpr . M.BoolValue
bNotBase = boolBase . not
joinBool :: A.Architecture arch
=> M.Endianness
-> BoundVarInterpretations arch t fs
-> BoolMapOp
-> MacawQ arch t fs BoundExp
-> (S.Expr t SI.BaseBoolType, S.Polarity)
-> MacawQ arch t fs BoundExp
joinBool endianness interps op e r =
do n <- case r of
(t, BooM.Positive) -> do addEltTH endianness interps t
(t, BooM.Negative) -> do p <- addEltTH endianness interps t
letTH [| addApp =<< (M.NotApp <$> $(refBinding p)) |]
j <- e
case op of
AndOp ->
letTH [| addApp =<< (M.AndApp <$> $(refBinding j) <*> $(refBinding n)) |]
OrOp ->
letTH [| addApp =<< (M.OrApp <$> $(refBinding j) <*> $(refBinding n)) |]

View File

@ -19,14 +19,27 @@ module Data.Macaw.SemMC.TH.Monad (
letBindExpr, letBindExpr,
letBindPureExpr, letBindPureExpr,
bindTH, bindTH,
letBindPat,
letTH, letTH,
extractBound, extractBound,
refBinding, refBinding,
inConditionalContext, inConditionalContext,
isTopLevel, isTopLevel,
definedFunction definedFunction,
isEager,
refEager,
joinOp1,
joinOp2,
joinOp3,
joinPure1,
joinPure2,
joinPure3,
bindPure1,
bindPure2,
bindPure3
) where ) where
import Control.Monad ( join )
import qualified Control.Monad.Fail as MF import qualified Control.Monad.Fail as MF
import qualified Control.Monad.State.Strict as St import qualified Control.Monad.State.Strict as St
import Control.Monad.Trans ( lift ) import Control.Monad.Trans ( lift )
@ -37,6 +50,8 @@ import qualified Data.Sequence as Seq
import Language.Haskell.TH import Language.Haskell.TH
import qualified Data.Macaw.CFG as M import qualified Data.Macaw.CFG as M
import qualified Data.Parameterized.Context as Ctx
import qualified Data.Parameterized.TraversableFC as FC
import qualified Data.Parameterized.Map as Map import qualified Data.Parameterized.Map as Map
import Data.Parameterized.Some ( Some(..) ) import Data.Parameterized.Some ( Some(..) )
import qualified Lang.Crucible.Backend.Simple as S import qualified Lang.Crucible.Backend.Simple as S
@ -85,6 +100,9 @@ data MacawTHConfig arch opc t fs =
-- ^ A TH action to generate the type tag for the architecture -- ^ A TH action to generate the type tag for the architecture
, genLibraryFunction :: forall sym . Some (SF.FunctionFormula sym) -> Bool , genLibraryFunction :: forall sym . Some (SF.FunctionFormula sym) -> Bool
, genOpcodeCase :: forall tps . opc tps -> Bool , genOpcodeCase :: forall tps . opc tps -> Bool
, archTranslateType :: forall tp. Q Type -> Q Type -> SI.BaseTypeRepr tp -> Maybe (Q Type)
-- ^ An optional override when translating What4 types, where the first two
-- arguments correspond to the 'ids' and 's' type variables.
} }
data QState arch t fs = QState { accumulatedStatements :: !(Seq.Seq Stmt) data QState arch t fs = QState { accumulatedStatements :: !(Seq.Seq Stmt)
@ -221,40 +239,54 @@ data BoundExp where
-- and the new name is returned. -- and the new name is returned.
bindExpr :: S.Expr t tp -> ExpQ -> MacawQ arch t fs BoundExp bindExpr :: S.Expr t tp -> ExpQ -> MacawQ arch t fs BoundExp
bindExpr elt eq = do bindExpr elt eq = do
pureFn <- liftQ $ [| pure |]
e <- liftQ eq e <- liftQ eq
n <- liftQ (newName "val") case e of
let res = VarE n AppE f (VarE n) | pureFn == f -> return $ EagerBoundExp $ VarE n
St.modify' $ \s -> s { accumulatedStatements = accumulatedStatements s Seq.|> BindS (VarP n) e _ -> do
, expressionCache = M.insert (Some elt) res (expressionCache s) n <- liftQ (newName "val")
} let res = VarE n
return (EagerBoundExp res) St.modify' $ \s -> s { accumulatedStatements = accumulatedStatements s Seq.|> BindS (VarP n) e
, expressionCache = M.insert (Some elt) res (expressionCache s)
}
return (EagerBoundExp res)
letBindPureExpr :: S.Expr t tp -> ExpQ -> MacawQ arch t fs BoundExp letBindPureExpr :: S.Expr t tp -> ExpQ -> MacawQ arch t fs BoundExp
letBindPureExpr elt eq = do letBindPureExpr elt eq = do
e <- liftQ eq e <- liftQ eq
n <- liftQ (newName "lval") case e of
let res = VarE n VarE n -> return $ EagerBoundExp $ VarE n
St.modify' $ \s -> s { accumulatedStatements = accumulatedStatements s Seq.|> LetS [ValD (VarP n) (NormalB e) []] _ -> do
, expressionCache = M.insert (Some elt) res (expressionCache s) n <- liftQ (newName "lval")
} let res = VarE n
return (EagerBoundExp res) St.modify' $ \s -> s { accumulatedStatements = accumulatedStatements s Seq.|> LetS [ValD (VarP n) (NormalB e) []]
, expressionCache = M.insert (Some elt) res (expressionCache s)
}
return (EagerBoundExp res)
letBindExpr :: S.Expr t tp -> Exp -> MacawQ arch t fs BoundExp letBindExpr :: S.Expr t tp -> Exp -> MacawQ arch t fs BoundExp
letBindExpr elt e = do letBindExpr elt e = do
n <- liftQ (newName "lval") pureFn <- liftQ $ [| pure |]
let res = VarE n case e of
St.modify' $ \s -> s { accumulatedStatements = accumulatedStatements s Seq.|> LetS [ValD (VarP n) (NormalB e) []] AppE f (VarE n) | pureFn == f -> return $ EagerBoundExp $ VarE n
, lazyExpressionCache = M.insert (Some elt) res (lazyExpressionCache s) _ -> do
} n <- liftQ (newName "lval")
return (LazyBoundExp res) let res = VarE n
St.modify' $ \s -> s { accumulatedStatements = accumulatedStatements s Seq.|> LetS [ValD (VarP n) (NormalB e) []]
, lazyExpressionCache = M.insert (Some elt) res (lazyExpressionCache s)
}
return (LazyBoundExp res)
letTH :: ExpQ -> MacawQ arch t fs BoundExp letTH :: ExpQ -> MacawQ arch t fs BoundExp
letTH eq = do letTH eq = do
e <- liftQ eq e <- liftQ eq
n <- liftQ (newName "lval") case e of
St.modify' $ \s -> s { accumulatedStatements = accumulatedStatements s Seq.|> LetS [ValD (VarP n) (NormalB e) []] VarE n -> return $ LazyBoundExp $ VarE n
} _ -> do
return (LazyBoundExp (VarE n)) n <- liftQ (newName "lval")
St.modify' $ \s -> s { accumulatedStatements = accumulatedStatements s Seq.|> LetS [ValD (VarP n) (NormalB e) []]
}
return (LazyBoundExp (VarE n))
bindTH :: ExpQ -> MacawQ arch t fs BoundExp bindTH :: ExpQ -> MacawQ arch t fs BoundExp
bindTH eq = do bindTH eq = do
@ -264,6 +296,14 @@ bindTH eq = do
} }
return (EagerBoundExp (VarE n)) return (EagerBoundExp (VarE n))
letBindPat :: S.Expr t tp -> (PatQ, ExpQ) -> ExpQ -> MacawQ arch t fs ()
letBindPat elt (patq,resq) eq = do
pat <- liftQ patq
res <- liftQ resq
e <- liftQ eq
St.modify' $ \s -> s { accumulatedStatements = accumulatedStatements s Seq.|> LetS [ValD pat (NormalB e) []]
, expressionCache = M.insert (Some elt) res (expressionCache s) }
definedFunction :: String -> MacawQ arch t fs (Maybe Exp) definedFunction :: String -> MacawQ arch t fs (Maybe Exp)
definedFunction name = do definedFunction name = do
df <- St.gets definedFunctionEvaluator df <- St.gets definedFunctionEvaluator
@ -286,3 +326,60 @@ refBinding be =
EagerBoundExp e -> [| pure $(return e) |] EagerBoundExp e -> [| pure $(return e) |]
-- If it is lazy, we need it "bare" in the applicative wrappers -- If it is lazy, we need it "bare" in the applicative wrappers
LazyBoundExp e -> return e LazyBoundExp e -> return e
isEager :: BoundExp -> Bool
isEager be = case be of
EagerBoundExp _ -> True
LazyBoundExp _ -> False
refEager :: BoundExp -> Q Exp
refEager be = case be of
EagerBoundExp e -> return e
LazyBoundExp _ -> fail "refEager: cannot eagerly reference a lazy value"
joinOp1 :: ExpQ -> BoundExp -> Q Exp
joinOp1 fun arg1 = case isEager arg1 of
True -> [| $(fun) $(refEager arg1) |]
False -> [| $(fun) =<< $(refBinding arg1) |]
joinOp2 :: ExpQ -> BoundExp -> BoundExp -> Q Exp
joinOp2 fun arg1 arg2 = case all isEager [arg1, arg2] of
True -> [| $(fun) $(refEager arg1) $(refEager arg2) |]
False -> [| join ($(fun) <$> $(refBinding arg1) <*> $(refBinding arg2)) |]
joinOp3 :: ExpQ -> BoundExp -> BoundExp -> BoundExp -> Q Exp
joinOp3 fun arg1 arg2 arg3 = case all isEager [arg1, arg2, arg3] of
True -> [| $(fun) $(refEager arg1) $(refEager arg2) $(refEager arg3)|]
False -> [| join ($(fun) <$> $(refBinding arg1) <*> $(refBinding arg2) <*> $(refBinding arg3)) |]
joinPure1 :: ExpQ -> ExpQ -> BoundExp -> Q Exp
joinPure1 mfun fun arg1 = case isEager arg1 of
True -> [| $(mfun) ($(fun) $(refEager arg1)) |]
False -> [| $(mfun) =<< ($(fun) <$> $(refBinding arg1)) |]
bindPure1 :: ExpQ -> ExpQ -> BoundExp -> MacawQ arch t fs BoundExp
bindPure1 mfun fun arg1 = case isEager arg1 of
True -> bindTH $ joinPure1 mfun fun arg1
False -> letTH $ joinPure1 mfun fun arg1
joinPure2 :: ExpQ -> ExpQ -> BoundExp -> BoundExp -> Q Exp
joinPure2 mfun fun arg1 arg2 = case all isEager [arg1, arg2] of
True -> [| $(mfun) ($(fun) $(refEager arg1) $(refEager arg2)) |]
False -> [| $(mfun) =<< ($(fun) <$> $(refBinding arg1)) <*> $(refBinding arg2) |]
bindPure2 :: ExpQ -> ExpQ -> BoundExp -> BoundExp -> MacawQ arch t fs BoundExp
bindPure2 mfun fun arg1 arg2 = case all isEager [arg1, arg2] of
True -> bindTH $ joinPure2 mfun fun arg1 arg2
False -> letTH $ joinPure2 mfun fun arg1 arg2
joinPure3 :: ExpQ -> ExpQ -> BoundExp -> BoundExp -> BoundExp -> Q Exp
joinPure3 mfun fun arg1 arg2 arg3 = case all isEager [arg1, arg2, arg3] of
True -> [| $(mfun) ($(fun) $(refEager arg1) $(refEager arg2) $(refEager arg3)) |]
False -> [| $(mfun) =<< ($(fun) <$> $(refBinding arg1)) <*> $(refBinding arg2) <*> $(refBinding arg3) |]
bindPure3 :: ExpQ -> ExpQ -> BoundExp -> BoundExp -> BoundExp -> MacawQ arch t fs BoundExp
bindPure3 mfun fun arg1 arg2 arg3 = case all isEager [arg1, arg2, arg3] of
True -> bindTH $ joinPure3 mfun fun arg1 arg2 arg3
False -> letTH $ joinPure3 mfun fun arg1 arg2 arg3

View File

@ -28,7 +28,7 @@ data Solver = CVC4 | Yices | Z3
withNewBackend :: (MonadIO m) withNewBackend :: (MonadIO m)
=> Solver => Solver
-> (forall proxy t solver fs sym . (sym ~ CBS.SimpleBackend t fs, CB.IsSymInterface sym, WPO.OnlineSolver t solver) => proxy solver -> WPF.ProblemFeatures -> sym -> m a) -> (forall proxy t solver fs sym . (sym ~ CBS.SimpleBackend t fs, CB.IsSymInterface sym, WPO.OnlineSolver solver) => proxy solver -> WPF.ProblemFeatures -> sym -> m a)
-> m a -> m a
withNewBackend s k = do withNewBackend s k = do
sym :: CBS.SimpleBackend PN.GlobalNonceGenerator (WE.Flags WE.FloatUninterpreted) sym :: CBS.SimpleBackend PN.GlobalNonceGenerator (WE.Flags WE.FloatUninterpreted)
@ -40,7 +40,7 @@ withNewBackend s k = do
let features = WPF.useBitvectors .|. WPF.useSymbolicArrays .|. WPF.useStructs .|. WPF.useNonlinearArithmetic let features = WPF.useBitvectors .|. WPF.useSymbolicArrays .|. WPF.useStructs .|. WPF.useNonlinearArithmetic
k proxy features sym k proxy features sym
Yices -> do Yices -> do
let proxy = Proxy @(WSY.Connection PN.GlobalNonceGenerator) let proxy = Proxy @WSY.Connection
liftIO $ WC.extendConfig WSY.yicesOptions (WI.getConfiguration sym) liftIO $ WC.extendConfig WSY.yicesOptions (WI.getConfiguration sym)
-- For some reason, non-linear arithmetic is required for cvc4 and z3 but doesn't work at all with yices -- For some reason, non-linear arithmetic is required for cvc4 and z3 but doesn't work at all with yices
let features = WPF.useBitvectors .|. WPF.useSymbolicArrays .|. WPF.useStructs .|. WPF.useLinearArithmetic let features = WPF.useBitvectors .|. WPF.useSymbolicArrays .|. WPF.useStructs .|. WPF.useLinearArithmetic

View File

@ -387,7 +387,7 @@ genIPConstraint ctx sym ipVal = liftIO $ do
-- | Probe the SMT solver for additional models of the given expression up to a maximum @count@ -- | Probe the SMT solver for additional models of the given expression up to a maximum @count@
genModels genModels
:: forall t solver fs m arch sym w proxy :: forall t solver fs m arch sym w proxy
. ( W.OnlineSolver t solver . ( W.OnlineSolver solver
, KnownNat w , KnownNat w
, 1 <= w , 1 <= w
, MonadIO m , MonadIO m
@ -417,7 +417,7 @@ genModels proxy sym solver_proc assumptions expr count
extractIPModels :: forall arch solver m sym t fp extractIPModels :: forall arch solver m sym t fp
. ( MS.SymArchConstraints arch . ( MS.SymArchConstraints arch
, W.OnlineSolver t solver , W.OnlineSolver solver
, MU.MonadUnliftIO m , MU.MonadUnliftIO m
, CB.IsSymInterface sym , CB.IsSymInterface sym
, sym ~ CBS.SimpleBackend t fp , sym ~ CBS.SimpleBackend t fp