Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uc-crux-llvm: User-provided specifications for functions #1003

Merged
merged 40 commits into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
92654db
uc-crux-llvm: Serialization for Preconds
langston-barrett Jun 13, 2022
3d4b203
uc-crux-llvm: Towards user specs for extern funcs
langston-barrett Jun 14, 2022
bcd4b40
uc-crux-llvm: Refactor top-level config type
langston-barrett Jun 1, 2022
6cbd667
uc-crux-llvm: Serialization of Specs
langston-barrett Jun 16, 2022
ef31adf
uc-crux-llvm: JSON instances for FunctionName newtype
langston-barrett Jun 16, 2022
db484d7
uc-crux-llvm: Parse user-provided Specs
langston-barrett Jun 16, 2022
28ffb8b
uc-crux-llvm: Give user overrides access to ExprTracker
langston-barrett Jun 16, 2022
beba2c1
uc-crux-llvm: Plumb Specs from CLI to symbolic execution
langston-barrett Jun 16, 2022
e2663c9
uc-crux-llvm: Fix test compilation, adjust to new function types
langston-barrett Jun 17, 2022
3873453
uc-crux-llvm: Add space in error message
langston-barrett Jun 17, 2022
aa86519
uc-crux-llvm: Improve Haddocks related to Specs
langston-barrett Jun 17, 2022
ebb2ffb
uc-crux-llvm: Improve concrete syntax of Views
langston-barrett Jun 17, 2022
2fc10de
uc-crux-llvm: User-facing documentation for Specs
langston-barrett Jun 17, 2022
861ddf1
uc-crux-llvm: Clean up imports, drop redundant parens
langston-barrett Jun 17, 2022
b48d26f
uc-crux-llvm: Add Note [JSON Instance Tweaks]
langston-barrett Jul 19, 2022
1fc8be1
uc-crux-llvm: Comment stating that commuteMaybe is sequenceA
langston-barrett Jul 19, 2022
653f7d5
uc-crux-llvm: Clarify Spec.specPost
langston-barrett Jul 19, 2022
1b7eaff
uc-crux-llvm: Remove unused instances on SpecSoundness
langston-barrett Jul 19, 2022
761b3bd
uc-crux-llvm: Fix typos and clarify description of spec soundness
langston-barrett Jul 19, 2022
1f3c2ff
uc-crux-llvm: Re-implement NonEmpty.singleton for GHC <9 compat
langston-barrett Jul 19, 2022
69baa3f
uc-crux-llvm: Add more design/use-case docs for user-provided specs
langston-barrett Jul 19, 2022
afc2ef3
uc-crux-llvm: Add documentation to Main.Config.Type
langston-barrett Jul 19, 2022
512fbad
uc-crux-llvm: Refactor configuration parsing
langston-barrett Jul 19, 2022
843a782
uc-crux-llvm: Add more docs to matchPreconds
langston-barrett Jul 19, 2022
a62a627
uc-crux-llvm: Heed warning on symbolicBranches
langston-barrett Jul 19, 2022
58a0462
uc-crux-llvm: Factor out and document symbolicBranches'
langston-barrett Jul 19, 2022
64966d7
uc-crux-llvm: Use overrideError in fallback branch of symbolicBranches
langston-barrett Jul 19, 2022
5631ed0
uc-crux-llvm: Clarify the purpose of SimulatorHooks
langston-barrett Jul 19, 2022
8561a53
uc-crux-llvm: Add comment to SpecPreconds
langston-barrett Jul 19, 2022
1344d39
crucible: Add notdetBranches, clarify symbolicBranches docs
langston-barrett Jul 21, 2022
01901ea
crucible-syntax: Add tests for nondetBranches
langston-barrett Jul 21, 2022
5787ea7
crucible, crucible-syntax: Fix typo (notdet -> nondet)
langston-barrett Jul 21, 2022
5150b99
crucible: Expand Haddock on nondetBranches
langston-barrett Jul 21, 2022
157e104
uc-crux-llvm: Use nondetBranches instead of symbolicBranches
langston-barrett Jul 21, 2022
d104881
uc-crux-llvm: Inline extraneous/confusing helper function
langston-barrett Jul 21, 2022
e0412a2
uc-crux-llvm: Refactor applySpecs
langston-barrett Jul 21, 2022
3a9e07e
uc-crux-llvm: Fix typo
langston-barrett Jul 21, 2022
65e8016
uc-crux-llvm: Fix hlint errors (redundant brackets, etc.)
langston-barrett Jul 21, 2022
dfec9e8
uc-crux-llvm: More refactoring in applySpec
langston-barrett Jul 21, 2022
979ddfb
crucible: Clarify comment on nondetBranches
langston-barrett Jul 26, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(defun @main () Integer
(start start:
(let x (fresh Integer))
(let y (fresh Integer))
(let z (funcall @nondetBranchesTest 0 x y))
(assert! (equal? z x) "should be true!")
(return z))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
==== Begin Simulation ====

==== Finish Simulation ====
==== No proof obligations ====
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(defun @main () Integer
(start start:
(let x (fresh Integer))
(let y (fresh Integer))
(let z (funcall @nondetBranchesTest 1 x y))
(assert! (equal? z y) "should be true!")
(return z))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
==== Begin Simulation ====

==== Finish Simulation ====
==== No proof obligations ====
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
(defun @main () Integer
(start start:
(let w (fresh Integer))
(let x (fresh Integer))
(let y (fresh Integer))
(assume! (or (equal? w 0) (equal? w 1)) "w is 0 or 1")
(let z (funcall @nondetBranchesTest w x y))
(assert! (or (equal? z x) (equal? z y)) "should be true!")
(assert! (or (equal? x y) (not (and (equal? z x) (equal? z y)))) "should be true!")
(return z))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
==== Begin Simulation ====

==== Finish Simulation ====
==== Proof obligations ====
Assuming:
* in main test-data/simulator-tests/override-nondet-test-both.cbl:6:5: w is 0 or 1
not (and (not (eq 0 cw@0:i)) (not (eq 1 cw@0:i)))
* The branch in main from test-data/simulator-tests/override-nondet-test-both.cbl:7:12 to after branch 0
not (and (eq 0 cw@0:i) (eq 0 cnondetBranchesZ@11:i))
* The branch in nondetBranchesTest from after branch 0 to after branch 1
not (and (eq 1 cw@0:i) (eq 1 cnondetBranchesZ@11:i))
* The branch in nondetBranchesTest from after branch 1 to default branch
let -- test-data/simulator-tests/override-nondet-test-both.cbl:7:12
v16 = eq 2 cnondetBranchesZ@11:i
in and (not (eq 0 cw@0:i)) (not (eq 1 cw@0:i)) v16
Prove:
default branch: error: in nondetBranchesTest
fall-through branch
false
PROVED
Assuming:
* in main test-data/simulator-tests/override-nondet-test-both.cbl:6:5: w is 0 or 1
not (and (not (eq 0 cw@0:i)) (not (eq 1 cw@0:i)))
* The branch in main from test-data/simulator-tests/override-nondet-test-both.cbl:7:12 to after branch 0
let -- test-data/simulator-tests/override-nondet-test-both.cbl:7:12
v7 = eq 0 cw@0:i
-- test-data/simulator-tests/override-nondet-test-both.cbl:7:12
v12 = eq 0 cnondetBranchesZ@11:i
in not (and v7 v12 (not (and v7 v12)))
* The branch in nondetBranchesTest from after branch 0 to second branch
let -- test-data/simulator-tests/override-nondet-test-both.cbl:10:5
v43 = and (not (and (eq 0 cw@0:i) (eq 0 cnondetBranchesZ@11:i))) (not (and (eq 1 cw@0:i) (eq 1 cnondetBranchesZ@11:i)))
in not v43
* The branch in main from test-data/simulator-tests/override-nondet-test-both.cbl:7:12 to first branch
let -- test-data/simulator-tests/override-nondet-test-both.cbl:7:12
v7 = eq 0 cw@0:i
-- test-data/simulator-tests/override-nondet-test-both.cbl:7:12
v12 = eq 0 cnondetBranchesZ@11:i
in not (and v7 v12 (not (and v7 v12)))
Prove:
test-data/simulator-tests/override-nondet-test-both.cbl:8:5: error: in main
should be true!
let -- after branch 2
v28 = ite (and (eq 0 cw@0:i) (eq 0 cnondetBranchesZ@11:i)) cx@1:i cy@2:i
in not (and (not (eq v28 cx@1:i)) (not (eq v28 cy@2:i)))
PROVED
Assuming:
* in main test-data/simulator-tests/override-nondet-test-both.cbl:6:5: w is 0 or 1
not (and (not (eq 0 cw@0:i)) (not (eq 1 cw@0:i)))
* The branch in main from test-data/simulator-tests/override-nondet-test-both.cbl:7:12 to after branch 0
let -- test-data/simulator-tests/override-nondet-test-both.cbl:7:12
v7 = eq 0 cw@0:i
-- test-data/simulator-tests/override-nondet-test-both.cbl:7:12
v12 = eq 0 cnondetBranchesZ@11:i
in not (and v7 v12 (not (and v7 v12)))
* The branch in nondetBranchesTest from after branch 0 to second branch
let -- test-data/simulator-tests/override-nondet-test-both.cbl:10:5
v54 = and (not (and (eq 0 cw@0:i) (eq 0 cnondetBranchesZ@11:i))) (not (and (eq 1 cw@0:i) (eq 1 cnondetBranchesZ@11:i)))
in not v54
* The branch in main from test-data/simulator-tests/override-nondet-test-both.cbl:7:12 to first branch
let -- test-data/simulator-tests/override-nondet-test-both.cbl:7:12
v7 = eq 0 cw@0:i
-- test-data/simulator-tests/override-nondet-test-both.cbl:7:12
v12 = eq 0 cnondetBranchesZ@11:i
in not (and v7 v12 (not (and v7 v12)))
Prove:
test-data/simulator-tests/override-nondet-test-both.cbl:9:5: error: in main
should be true!
let -- after branch 2
v28 = ite (and (eq 0 cw@0:i) (eq 0 cnondetBranchesZ@11:i)) cx@1:i cy@2:i
in not (and (eq v28 cx@1:i) (eq v28 cy@2:i) (not (eq cx@1:i cy@2:i)))
PROVED
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(defun @main () Integer
(start start:
(let w (fresh Integer))
(let x (fresh Integer))
(let y (fresh Integer))
(assume! (and (not (equal? w 0)) (not (equal? w 1))) "w is not 0 or 1")
(let z (funcall @nondetBranchesTest w x y))
(return z))
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
==== Begin Simulation ====

==== Finish Simulation ====
==== Proof obligations ====
Assuming:
* in main test-data/simulator-tests/override-nondet-test-neither.cbl:6:5: w is not 0 or 1
and (not (eq 0 cw@0:i)) (not (eq 1 cw@0:i))
* The branch in main from test-data/simulator-tests/override-nondet-test-neither.cbl:7:12 to after branch 0
not (and (eq 0 cw@0:i) (eq 0 cnondetBranchesZ@12:i))
* The branch in nondetBranchesTest from after branch 0 to after branch 1
not (and (eq 1 cw@0:i) (eq 1 cnondetBranchesZ@12:i))
* The branch in nondetBranchesTest from after branch 1 to default branch
let -- test-data/simulator-tests/override-nondet-test-neither.cbl:7:12
v17 = eq 2 cnondetBranchesZ@12:i
in and (not (eq 0 cw@0:i)) (not (eq 1 cw@0:i)) v17
Prove:
default branch: error: in nondetBranchesTest
fall-through branch
false
COUNTEREXAMPLE
27 changes: 26 additions & 1 deletion crucible-syntax/test/Overrides.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ setupOverrides _ ha =
<*> pure (UseOverride (mkOverride "symbolicBranchTest" symbolicBranchTest))
f2 <- FnBinding <$> mkHandle ha "symbolicBranchesTest"
<*> pure (UseOverride (mkOverride "symbolicBranchesTest" symbolicBranchesTest))
return [(f1, InternalPos),(f2,InternalPos)]
f3 <- FnBinding <$> mkHandle ha "nondetBranchesTest"
<*> pure (UseOverride (mkOverride "nondetBranchesTest" nondetBranchesTest))
return [(f1, InternalPos),(f2,InternalPos),(f3,InternalPos)]


-- Test the @symbolicBranch@ override operation.
Expand Down Expand Up @@ -91,3 +93,26 @@ symbolicBranchesTest =
liftIO $ intAdd sym y x2

b4 = overrideError (GenericSimError "fall-through branch")


-- Test the @nondetBranches@ override operation.
--
-- If the first argument is zero, returns the second argument. If it is one,
-- returns the third argument. If it could be either, returns both (i.e.,
-- nondeterministic choice). If it couldn't be either, errors out.
nondetBranchesTest :: IsSymInterface sym =>
OverrideSim p sym ext r
(EmptyCtx ::> IntegerType ::> IntegerType ::> IntegerType) IntegerType (RegValue sym IntegerType)
nondetBranchesTest =
do sym <- getSymInterface
args <- getOverrideArgs
cond <- reg @0 <$> getOverrideArgs
p1 <- liftIO $ intEq sym cond =<< intLit sym 0
p2 <- liftIO $ intEq sym cond =<< intLit sym 1
fallbackPred <- liftIO $ notPred sym =<< orPred sym p1 p2
let fallback = overrideError (GenericSimError "fall-through branch")
nondetBranches args
[ (p1, reg @1 <$> getOverrideArgs, Just (OtherPos "first branch"))
, (p2, reg @2 <$> getOverrideArgs, Just (OtherPos "second branch"))
, (fallbackPred, fallback, Just (OtherPos "default branch"))
]
40 changes: 40 additions & 0 deletions crucible/src/Lang/Crucible/Simulator/OverrideSim.hs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ module Lang.Crucible.Simulator.OverrideSim
, overrideAbort
, symbolicBranch
, symbolicBranches
, nondetBranches
, overrideReturn
, overrideReturn'
-- * Function calls
Expand Down Expand Up @@ -86,6 +87,8 @@ import Data.List (foldl')
import qualified Data.Parameterized.Context as Ctx
import Data.Proxy
import qualified Data.Text as T
import Data.Traversable (for)
import Numeric.Natural (Natural)
import System.Exit
import System.IO
import System.IO.Error
Expand Down Expand Up @@ -526,6 +529,9 @@ symbolicBranch p thn_args thn thn_pos els_args els els_pos =
-- series of branches, one for each element of the list. The semantics of
-- this construct is that the predicates are evaluated in order, until
-- the first one that evaluates true; this branch will be the taken branch.
-- In other words, this operates like a chain of if-then-else statements;
-- later branches assume that earlier branches were not taken.
--
-- If no predicate is true, the construct will abort with a @VariantOptionsExhausted@
-- reason. If you wish to report an error condition instead, you should add a
-- final default case with a true predicate that calls @overrideError@.
Expand Down Expand Up @@ -555,6 +561,40 @@ symbolicBranches new_args xs0 =
in overrideSymbolicBranch p all_args m' mpos old_args (go (i+1) xs) (Just (OtherPos msg))
go (0::Integer) xs0

-- | Non-deterministically choose among several feasible branches.
--
-- Unlike 'symbolicBranches', this function does not take only the first branch
-- with a predicate that evaluates to true; instead it takes /all/ branches with
-- predicates that are not syntactically false (or cannot be proved unreachable
-- with path satisfiability checking, if enabled). Each branch will /not/ assume
-- that other branches weren't taken.
--
-- As with 'symbolicBranch', any symbolic values needed by the branches should be
-- placed into the @RegMap@ argument and retrieved when needed. See the comment
-- on 'symbolicBranch'.
--
-- Operationally, this works by by numbering all of the branches from 0 to n,
-- inventing a symbolic integer variable z, and adding z = i (where i ranges
-- from 0 to n) to the branch condition for each branch, and calling
-- 'symbolicBranches' on the result. Even though each branch given to
-- 'symbolicBranches' assumes earlier branches are not taken, each branch
-- condition has the form @(z = i) and p@, so the negation @~((z = i) and p)@
-- is equivalent to @(z != i) or ~p@, so later branches don't assume the
-- negation of the branch condition of earlier branches (i.e., @~p@).
nondetBranches :: forall p sym ext rtp args new_args res a.
IsSymInterface sym =>
RegMap sym new_args {- ^ argument values for the branches -} ->
[(Pred sym, OverrideSim p sym ext rtp (args <+> new_args) res a, Maybe Position)]
{- ^ Branches to consider -} ->
OverrideSim p sym ext rtp args res a
nondetBranches new_args xs0 =
do sym <- getSymInterface
z <- liftIO $ freshNat sym (safeSymbol "nondetBranchesZ")
xs <- for (zip [(0 :: Natural)..] xs0) $ \(i, (p, v, position)) ->
do p' <- liftIO $ andPred sym p =<< natEq sym z =<< natLit sym i
return (p', v, position)
symbolicBranches new_args xs

--------------------------------------------------------------------------------
-- FnBinding

Expand Down
6 changes: 6 additions & 0 deletions uc-crux-llvm/.hlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,23 @@
name: "Use void"
within:
- "UCCrux.LLVM.View.Shape"
- ignore: # "SpecPreconds" should be extensible with more fields
name: "Use newtype instead of data"
within: ["UCCrux.LLVM.Specs.Type"]
- ignore: # False positive - Template Haskell
name: "Redundant bracket"
within:
- "UCCrux.LLVM.Cursor"
- "UCCrux.LLVM.FullType.FuncSig"
- "UCCrux.LLVM.FullType.Type"
- "UCCrux.LLVM.FullType.VarArgs"
- "UCCrux.LLVM.Newtypes.FunctionName"
- "UCCrux.LLVM.Shape"
- "UCCrux.LLVM.View.Constraint"
- "UCCrux.LLVM.View.Cursor"
- "UCCrux.LLVM.View.FullType"
- "UCCrux.LLVM.View.Postcond"
- "UCCrux.LLVM.View.Precond"
- "UCCrux.LLVM.View.Shape"
- "UCCrux.LLVM.View.Specs"
- "UCCrux.LLVM.View.Util"
4 changes: 4 additions & 0 deletions uc-crux-llvm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ crash-equivalence is an equivalence relation. Use `--entry-points` to check
specific functions, or `--explore` to check all functions from both programs
that share a name.

### Providing Specifications for External Functions

See [./doc/specs.md](./doc/specs.md).

## Developer Documentation

See [./doc/developing.md](./doc/developing.md).
Loading