Skip to content

Commit 429df8e

Browse files
authored
Merge pull request #425 from GaloisInc/vr/fix-assertPred
fix `assertPred` and make its code more uniform
2 parents 186c35d + 9e61b39 commit 429df8e

File tree

1 file changed

+131
-60
lines changed

1 file changed

+131
-60
lines changed

base/src/Data/Macaw/AbsDomain/JumpBounds.hs

+131-60
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ known equivalent values.
1111
{-# LANGUAGE FlexibleContexts #-}
1212
{-# LANGUAGE KindSignatures #-}
1313
{-# LANGUAGE MultiParamTypeClasses #-}
14+
{-# LANGUAGE MultiWayIf #-}
1415
{-# LANGUAGE OverloadedStrings #-}
1516
{-# LANGUAGE PolyKinds #-}
1617
{-# LANGUAGE RankNTypes #-}
@@ -400,7 +401,7 @@ exprRangePred info e = do
400401
lowerBound = truncUnsigned $ rangeLowerBound r
401402
upperBound = truncUnsigned $ rangeUpperBound r
402403
in
403-
SomeRangePred (mkRangeBound w lowerBound upperBound)
404+
SomeRangePred (mkRangeBound w lowerBound upperBound)
404405
_ -> NoRangePred
405406

406407
-- | This returns a natural number with a computed upper bound for the
@@ -674,89 +675,159 @@ addRangePred cns v p =
674675
_ ->
675676
Right $ branchAssignRangePred n p
676677

678+
-- | @addLowerBoundPred cns w l x@ adds a @l <= x@ constraint over the `w`-bit-truncated values.
679+
-- Fails when the bound excludes all possible values (lower bound greater than the maximum `w`-bit
680+
-- value).
681+
-- Note: The arguments are ordered so that it reads more naturally, as the difference of type
682+
-- between `l` and `x` prevents mistakes.
683+
addLowerBoundPred ::
684+
RegisterInfo (ArchReg arch) =>
685+
BlockIntraStackConstraints arch ids -> NatRepr n -> Integer -> Value arch ids (BVType n) ->
686+
Either String (BranchConstraints arch ids)
687+
addLowerBoundPred cns w l x
688+
| l > maxUnsigned w = Left "Lower bound excludes all possible values"
689+
| otherwise = addRangePred cns (intraStackValueExpr cns x) $! mkLowerBound w (fromInteger l)
690+
691+
-- | @addUpperBoundPred cns w x u@ adds a @x <= u@ constraint over the `w`-bit-truncated values.
692+
-- Fails when the bound excludes all possible values (`u` < 0).
693+
addUpperBoundPred ::
694+
RegisterInfo (ArchReg arch) =>
695+
BlockIntraStackConstraints arch ids -> NatRepr n -> Value arch ids (BVType n) -> Integer ->
696+
Either String (BranchConstraints arch ids)
697+
addUpperBoundPred cns w x u
698+
| u < 0 = Left "Upper bound excludes all possible values"
699+
| otherwise = addRangePred cns (intraStackValueExpr cns x) $! mkUpperBound w (fromInteger u)
700+
701+
clamp :: NatRepr w -> Integer -> Integer
702+
clamp w x
703+
| x < 0 = 0
704+
| x > maxUnsigned w = maxUnsigned w
705+
| otherwise = x
706+
707+
-- | @addWithinRangePred cns w l x u@ adds a @l <= x <= u@ constraint over the `w`-bit-truncated values.
708+
-- Fails when the bound excludes all possible values.
709+
-- Note: The arguments are ordered so that it reads more naturally.
710+
addWithinRangePred ::
711+
RegisterInfo (ArchReg arch) =>
712+
BlockIntraStackConstraints arch ids -> NatRepr n -> Integer -> Value arch ids (BVType n) -> Integer ->
713+
Either String (BranchConstraints arch ids)
714+
addWithinRangePred cns w l x u
715+
-- Note: we could avoid this duplication by instead producing a conjunction of `addLowerBoundPred`
716+
-- and `addUpperBoundPred`, but this produces a single range instead.
717+
| l > maxUnsigned w = Left "Lower bound excludes all possible values"
718+
| u < 0 = Left "Upper bound excludes all possible values"
719+
| clamp w u < clamp w l = Left "Empty range excludes all possible values"
720+
| otherwise =
721+
addRangePred cns (intraStackValueExpr cns x) $! mkRangeBound w (fromInteger l) (fromInteger u)
722+
723+
-- | @addExcludeRangePred cns w x l u@ adds a @(x < l) or (u < x)@ constraint over the
724+
-- `w`-bit-truncated values. Fails when the bound excludes all possible values.
725+
addExcludeRangePred ::
726+
RegisterInfo (ArchReg arch) =>
727+
BlockIntraStackConstraints arch ids -> NatRepr n -> Value arch ids (BVType n) -> Integer -> Integer ->
728+
Either String (BranchConstraints arch ids)
729+
addExcludeRangePred cns w x l u = do
730+
let lowerBoundTooLow = l <= 0 -- (l - 1) would be negative
731+
let upperBoundTooHigh = u >= maxUnsigned w -- (u + 1) would overflow
732+
if
733+
| lowerBoundTooLow && upperBoundTooHigh -> Left "Excluded range excludes all possible values"
734+
| lowerBoundTooLow -> addLowerBoundPred cns w (u + 1) x
735+
| upperBoundTooHigh -> addUpperBoundPred cns w x (l - 1)
736+
| otherwise ->
737+
-- compiles it to @(x <= l - 1) or (u + 1 <= x)@
738+
disjoinBranchConstraints
739+
<$> addUpperBoundPred cns w x (l - 1)
740+
<*> addLowerBoundPred cns w (u + 1) x
741+
742+
-- | @assertEqPred cns x w c isTrue@ asserts equality @x = BVValue c w@ is either true or false,
743+
-- based on `isTrue`.
744+
assertEqPred ::
745+
RegisterInfo (ArchReg arch) =>
746+
-- | Constraints that let us evaluate `x`
747+
BlockIntraStackConstraints arch ids ->
748+
-- | Bitwidth the comparison should apply to
749+
NatRepr w ->
750+
-- | Value to be constrained
751+
Value arch ids (BVType w) ->
752+
-- | Numeric constant x should be equal/different to
753+
Integer ->
754+
-- | `True` if we should assert equality, `False` if we should assert non-equality
755+
Bool ->
756+
Either String (BranchConstraints arch ids)
757+
assertEqPred cns w x c isTrue
758+
| isTrue =
759+
-- x == c becomes c <= x <= c
760+
addWithinRangePred cns w c x c
761+
| otherwise =
762+
-- !(x == c) is handled via !(c <= x <= c) and becomes (x <= c - 1) or (c + 1 <= x)
763+
addExcludeRangePred cns w x c c
764+
677765
-- | Assert a predicate is true/false and update bounds.
678766
--
679767
-- If it returns a new upper bounds, then that may be used.
680768
-- Otherwise, it detects an inconsistency and returns a message
681769
-- explaing why.
682-
assertPred :: ( OrdF (ArchReg arch)
683-
, HasRepr (ArchReg arch) TypeRepr
684-
, MemWidth (ArchAddrWidth arch)
685-
, ShowF (ArchReg arch)
686-
)
770+
assertPred :: RegisterInfo (ArchReg arch)
687771
=> IntraJumpBounds arch ids
688772
-> Value arch ids BoolType -- ^ Value representing predicate
689773
-> Bool -- ^ Controls whether predicate is true or false
690774
-> Either String (BranchConstraints arch ids)
691775
assertPred bnds (AssignedValue a) isTrue = do
692776
let cns = intraStackConstraints bnds
693777
case assignRhs a of
694-
EvalApp (Eq x (BVValue w c)) -> do
695-
addRangePred cns (intraStackValueExpr cns x)
696-
(mkRangeBound w (fromInteger c) (fromInteger c))
697-
EvalApp (Eq (BVValue w c) x) -> do
698-
addRangePred cns (intraStackValueExpr cns x)
699-
(mkRangeBound w (fromInteger c) (fromInteger c))
700-
-- Given x < c), assert x <= c-1
701-
EvalApp (BVUnsignedLt x (BVValue _ c)) -> do
702-
if isTrue then do
703-
when (c == 0) $ Left "x < 0 must be false."
704-
addRangePred cns (intraStackValueExpr cns x) $!
705-
mkUpperBound (typeWidth x) (fromInteger (c-1))
706-
else do
707-
addRangePred cns (intraStackValueExpr cns x) $!
708-
mkLowerBound (typeWidth x) (fromInteger c)
709-
-- Given not (c < y), assert y <= c
710-
EvalApp (BVUnsignedLt (BVValue w c) y) -> do
711-
p <-
712-
if isTrue then do
713-
when (c >= maxUnsigned w) $ Left "x <= max_unsigned must be true"
714-
pure $! mkLowerBound w (fromInteger (c+1))
715-
else do
716-
pure $! mkUpperBound w (fromInteger c)
717-
addRangePred cns (intraStackValueExpr cns y) p
718-
-- Given x <= c, assert x <= c
719-
EvalApp (BVUnsignedLe x (BVValue w c)) -> do
720-
p <-
721-
if isTrue then
722-
pure $! mkUpperBound w (fromInteger c)
723-
else do
724-
when (c >= maxUnsigned w) $ Left "x <= max_unsigned must be true"
725-
pure $! mkLowerBound w (fromInteger (c+1))
726-
addRangePred cns (intraStackValueExpr cns x) p
727-
-- Given not (c <= y), assert y <= (c-1)
728-
EvalApp (BVUnsignedLe (BVValue _ c) y)
729-
| isTrue -> do
730-
addRangePred cns (intraStackValueExpr cns y)
731-
(mkLowerBound (typeWidth y) (fromInteger c))
732-
| otherwise -> do
733-
when (c == 0) $ Left "0 <= x cannot be false"
734-
addRangePred cns
735-
(intraStackValueExpr cns y)
736-
(mkUpperBound (typeWidth y) (fromInteger (c-1)))
737-
EvalApp (AndApp l r) ->
738-
if isTrue then
778+
EvalApp (Eq x (BVValue w c)) -> assertEqPred cns w x c isTrue
779+
EvalApp (Eq (BVValue w c) x) -> assertEqPred cns w x c isTrue
780+
EvalApp (BVUnsignedLt x (BVValue w c))
781+
| isTrue ->
782+
-- x < c becomes x <= c - 1
783+
addUpperBoundPred cns w x (c - 1)
784+
| otherwise ->
785+
-- !(x < c) becomes c <= x
786+
addLowerBoundPred cns w c x
787+
EvalApp (BVUnsignedLt (BVValue w c) y)
788+
| isTrue ->
789+
-- c < y becomes c + 1 <= y
790+
addLowerBoundPred cns w (c + 1) y
791+
| otherwise ->
792+
-- !(c < y) becomes y <= c
793+
addUpperBoundPred cns w y c
794+
EvalApp (BVUnsignedLe x (BVValue w c))
795+
| isTrue ->
796+
-- x <= c
797+
addUpperBoundPred cns w x c
798+
| otherwise ->
799+
-- !(x <= c) becomes c + 1 <= x
800+
addLowerBoundPred cns w (c + 1) x
801+
EvalApp (BVUnsignedLe (BVValue w c) y)
802+
| isTrue ->
803+
-- c <= y
804+
addLowerBoundPred cns w c y
805+
| otherwise ->
806+
-- !(c <= y) becomes y <= c - 1
807+
addUpperBoundPred cns w y (c - 1)
808+
EvalApp (AndApp l r)
809+
| isTrue ->
810+
-- l && r
739811
conjoinBranchConstraints
740812
<$> assertPred bnds l True
741813
<*> assertPred bnds r True
742-
else
814+
| otherwise ->
815+
-- !(l && r) becomes !l || !r
743816
disjoinBranchConstraints
744817
<$> assertPred bnds l False
745818
<*> assertPred bnds r False
746-
-- Given not (x || y), assert not x, then assert not y
747-
EvalApp (OrApp l r) ->
748-
if isTrue then
749-
-- Assert l | r
819+
EvalApp (OrApp l r)
820+
| isTrue ->
821+
-- l || r
750822
disjoinBranchConstraints
751823
<$> assertPred bnds l True
752824
<*> assertPred bnds r True
753-
else
754-
-- Assert not l && not r
825+
| otherwise ->
826+
-- !(l || r) becomes !l && !r
755827
conjoinBranchConstraints
756828
<$> assertPred bnds l False
757829
<*> assertPred bnds r False
758-
EvalApp (NotApp p) ->
759-
assertPred bnds p (not isTrue)
830+
EvalApp (NotApp p) -> assertPred bnds p (not isTrue)
760831
_ -> Right emptyBranchConstraints
761832
assertPred _ _ _ =
762833
Right emptyBranchConstraints

0 commit comments

Comments
 (0)