@@ -19,9 +19,11 @@ import qualified Data.BitVector.Sized as BV
19
19
import Data.Foldable
20
20
import Data.Functor.Const
21
21
import Data.IORef
22
+ import Data.Map (Map )
22
23
import qualified Data.Map as Map
23
24
import Data.Parameterized.Context (pattern Empty , pattern (:>) , Assignment )
24
25
import Data.Parameterized.Nonce
26
+ import Data.Parameterized.Pair
25
27
import Data.Parameterized.Some
26
28
import Data.Parameterized.TraversableFC
27
29
import Data.Sequence (Seq )
@@ -41,13 +43,14 @@ import Lang.Crucible.Simulator
41
43
import Lang.Crucible.Types
42
44
43
45
import qualified Verifier.SAW.Prelude as SAW
46
+ import qualified Verifier.SAW.Recognizer as SAW (asExtCns )
44
47
import qualified Verifier.SAW.SharedTerm as SAW
45
48
import qualified Verifier.SAW.Simulator.What4.ReturnTrip as SAW
46
49
import qualified Verifier.SAW.TypedTerm as SAW
47
50
48
51
import qualified SAWScript.Crucible.Common.MethodSpec as MS
49
52
50
- import Crux.Types (Model )
53
+ import Crux.Types (HasModel )
51
54
52
55
import Mir.DefId
53
56
import Mir.Generator (CollectionState , collection )
@@ -72,6 +75,12 @@ data MethodSpecBuilder sym t = MethodSpecBuilder
72
75
, _msbNextAlloc :: MS. AllocIndex
73
76
, _msbSnapshotFrame :: FrameIdentifier
74
77
, _msbVisitCache :: W4. IdxCache t (Const () )
78
+ -- | Substitutions to apply to the entire `MethodSpec` after construction.
79
+ -- These are generated in place of equality postconditions to improve
80
+ -- performance. Variables that appear on the LHS of an entry here will be
81
+ -- removed from the `MethodSpec`'s fresh variable lists. Substitutions are
82
+ -- applied in the order listed.
83
+ , _msbSubsts :: [(SAW. ExtCns SAW. Term , SAW. Term )]
75
84
}
76
85
77
86
data StateExtra sym t = StateExtra
@@ -104,6 +113,7 @@ initMethodSpecBuilder cs sc eval spec snap cache = MethodSpecBuilder
104
113
, _msbNextAlloc = MS. AllocIndex 0
105
114
, _msbSnapshotFrame = snap
106
115
, _msbVisitCache = cache
116
+ , _msbSubsts = []
107
117
}
108
118
109
119
initStateExtra :: StateExtra sym t
@@ -172,14 +182,14 @@ instance (IsSymInterface sym, sym ~ W4.ExprBuilder t st fs) =>
172
182
-- MethodSpecBuilder implementation. This is the code that actually runs when
173
183
-- Rust invokes `msb.add_arg(...)` or similar.
174
184
175
- builderNew :: forall sym t st fs rtp .
176
- (IsSymInterface sym , sym ~ W4. ExprBuilder t st fs ) =>
185
+ builderNew :: forall sym p t st fs rtp .
186
+ (IsSymInterface sym , sym ~ W4. ExprBuilder t st fs , HasModel p ) =>
177
187
CollectionState ->
178
188
-- | `DefId` of the `builder_new` monomorphization. Its `Instance` should
179
189
-- have one type argument, which is the `TyFnDef` of the function that the
180
190
-- spec applies to.
181
191
DefId ->
182
- OverrideSim (Model sym ) sym MIR rtp
192
+ OverrideSim (p sym ) sym MIR rtp
183
193
EmptyCtx MethodSpecBuilderType (MethodSpecBuilder sym t )
184
194
builderNew cs defId = do
185
195
sym <- getSymInterface
@@ -214,10 +224,10 @@ builderNew cs defId = do
214
224
-- As a side effect, this clobbers any mutable memory reachable through the
215
225
-- argument. For example, if `argRef` points to an `&mut i32`, the `i32` will
216
226
-- be overwritten with a fresh symbolic variable.
217
- addArg :: forall sym t st fs rtp args ret tp .
218
- (IsSymInterface sym , sym ~ W4. ExprBuilder t st fs ) =>
227
+ addArg :: forall sym p t st fs rtp args ret tp .
228
+ (IsSymInterface sym , sym ~ W4. ExprBuilder t st fs , HasModel p ) =>
219
229
TypeRepr tp -> MirReferenceMux sym tp -> MethodSpecBuilder sym t ->
220
- OverrideSim (Model sym ) sym MIR rtp args ret (MethodSpecBuilder sym t )
230
+ OverrideSim (p sym ) sym MIR rtp args ret (MethodSpecBuilder sym t )
221
231
addArg tpr argRef msb = execBuilderT msb $ do
222
232
sym <- lift $ getSymInterface
223
233
loc <- liftIO $ W4. getCurrentProgramLoc sym
@@ -362,21 +372,78 @@ gatherAsserts msb = do
362
372
" ) references variable " ++ show v ++ " (" ++ show (W4. bvarName v) ++ " at " ++
363
373
show (W4. bvarLoc v) ++ " ), which does not appear in the function args"
364
374
Right x -> map fst x
375
+ newVars <- liftIO $ gatherVars sym [Some (MethodSpecValue BoolRepr pred ) | pred <- asserts']
376
+ let postVars' = Set. union (msb ^. msbPost . seVars) newVars
377
+ let postOnlyVars = postVars' `Set.difference` (msb ^. msbPre . seVars)
378
+
379
+ (asserts'', substs) <- liftIO $
380
+ gatherSubsts postOnlyVars vars [] [] asserts'
381
+ substTerms <- forM substs $ \ (Pair var expr) -> do
382
+ varTerm <- liftIO $ eval $ W4. BoundVarExpr var
383
+ varEc <- case SAW. asExtCns varTerm of
384
+ Just ec -> return ec
385
+ Nothing -> error $ " eval of BoundVarExpr produced non-ExtCns ?" ++ show varTerm
386
+ exprTerm <- liftIO $ eval expr
387
+ return (varEc, exprTerm)
365
388
366
389
let loc = msb ^. msbSpec . MS. csLoc
367
- assertConds <- liftIO $ forM asserts' $ \ pred -> do
390
+ assertConds <- liftIO $ forM asserts'' $ \ pred -> do
368
391
tt <- eval pred >>= SAW. mkTypedTerm sc
369
392
return $ MS. SetupCond_Pred loc tt
370
- newVars <- liftIO $ gatherVars sym [Some (MethodSpecValue BoolRepr pred ) | pred <- asserts']
371
393
372
394
return $ msb
373
395
& msbSpec . MS. csPostState . MS. csConditions %~ (++ assertConds)
374
- & msbPost . seVars %~ Set. union newVars
396
+ & msbPost . seVars .~ postVars'
397
+ & msbSubsts %~ (++ substTerms)
398
+
375
399
where
376
400
sc = msb ^. msbSharedContext
377
401
eval :: forall tp . W4. Expr t tp -> IO SAW. Term
378
402
eval = msb ^. msbEval
379
403
404
+ -- | Find assertions of the form `var == expr` that are suitable for
405
+ -- performing substitutions, and separate them from the list of assertions.
406
+ gatherSubsts ::
407
+ Set (Some (W4. ExprBoundVar t )) ->
408
+ Set (Some (W4. ExprBoundVar t )) ->
409
+ [W4. Expr t BaseBoolType ] ->
410
+ [Pair (W4. ExprBoundVar t ) (W4. Expr t )] ->
411
+ [W4. Expr t BaseBoolType ] ->
412
+ IO ([W4. Expr t BaseBoolType ], [Pair (W4. ExprBoundVar t ) (W4. Expr t )])
413
+ gatherSubsts _lhsOk _rhsOk accPreds accSubsts [] =
414
+ return (reverse accPreds, reverse accSubsts)
415
+ gatherSubsts lhsOk rhsOk accPreds accSubsts (pred : preds)
416
+ | Just (Pair var expr) <- asVarEqExpr pred = do
417
+ rhsSeenRef <- newIORef Set. empty
418
+ cache <- W4. newIdxCache
419
+ visitExprVars cache expr $ \ var -> modifyIORef rhsSeenRef $ Set. insert (Some var)
420
+ rhsSeen <- readIORef rhsSeenRef
421
+ let lhsOk' = Set. delete (Some var) lhsOk
422
+ let rhsOk' = Set. delete (Some var) rhsOk
423
+ -- We can't use `pred` as a substitution if the RHS contains variables
424
+ -- that were deleted by a previous substitution. Otherwise we'd end up
425
+ -- re-introducing a deleted variable. We also can't do substitutions
426
+ -- where the RHS expression contains the LHS variable, which is why we
427
+ -- check against `rhsOk'` here instead of `rhsOk`.
428
+ if rhsSeen `Set.isSubsetOf` rhsOk' then
429
+ gatherSubsts lhsOk' rhsOk' accPreds (Pair var expr : accSubsts) preds
430
+ else
431
+ gatherSubsts lhsOk rhsOk (pred : accPreds) accSubsts preds
432
+ | otherwise =
433
+ gatherSubsts lhsOk rhsOk (pred : accPreds) accSubsts preds
434
+ where
435
+ asVarEqExpr pred
436
+ | Just (W4. BaseEq _btpr x y) <- W4. asApp pred
437
+ , W4. BoundVarExpr v <- x
438
+ , Set. member (Some v) lhsOk
439
+ = Just (Pair v y)
440
+ | Just (W4. BaseEq _btpr x y) <- W4. asApp pred
441
+ , W4. BoundVarExpr v <- y
442
+ , Set. member (Some v) lhsOk
443
+ = Just (Pair v x)
444
+ | otherwise = Nothing
445
+
446
+
380
447
-- | Collect all the symbolic variables that appear in `vals`.
381
448
gatherVars ::
382
449
(IsSymInterface sym , sym ~ W4. ExprBuilder t st fs ) =>
@@ -461,10 +528,11 @@ finish msb = do
461
528
& MS. csPreState . MS. csAllocs .~ preAllocs
462
529
& MS. csPostState . MS. csFreshVars .~ postVars'
463
530
& MS. csPostState . MS. csAllocs .~ postAllocs
464
- nonce <- liftIO $ freshNonce ng
531
+ sm <- liftIO $ buildSubstMap (msb ^. msbSharedContext) (msb ^. msbSubsts)
532
+ ms' <- liftIO $ substMethodSpec (msb ^. msbSharedContext) sm ms
465
533
466
- let ms' = MethodSpec (msb ^. msbCollectionState) ms
467
- return $ M. MethodSpec ms' (indexValue nonce)
534
+ nonce <- liftIO $ freshNonce ng
535
+ return $ M. MethodSpec ( MethodSpec (msb ^. msbCollectionState) ms') (indexValue nonce)
468
536
469
537
where
470
538
sc = msb ^. msbSharedContext
@@ -480,6 +548,80 @@ finish msb = do
480
548
Nothing -> error $ " BoundVarExpr translated to non-ExtCns term? " ++ show tt
481
549
482
550
551
+ buildSubstMap ::
552
+ SAW. SharedContext ->
553
+ [(SAW. ExtCns SAW. Term , SAW. Term )] ->
554
+ IO (Map SAW. VarIndex SAW. Term )
555
+ buildSubstMap sc substs = go Map. empty substs
556
+ where
557
+ go sm [] = return sm
558
+ go sm ((ec, term) : substs) = do
559
+ -- Rewrite the RHSs of previous substitutions using the current one.
560
+ let sm1 = Map. singleton (SAW. ecVarIndex ec) term
561
+ sm' <- mapM (SAW. scInstantiateExt sc sm1) sm
562
+ -- Add the current subst and continue.
563
+ go (Map. insert (SAW. ecVarIndex ec) term sm') substs
564
+
565
+ substMethodSpec ::
566
+ SAW. SharedContext ->
567
+ Map SAW. VarIndex SAW. Term ->
568
+ MIRMethodSpec ->
569
+ IO MIRMethodSpec
570
+ substMethodSpec sc sm ms = do
571
+ preState' <- goState $ ms ^. MS. csPreState
572
+ postState' <- goState $ ms ^. MS. csPostState
573
+ argBindings' <- mapM goArg $ ms ^. MS. csArgBindings
574
+ retValue' <- mapM goSetupValue $ ms ^. MS. csRetValue
575
+ return $ ms
576
+ & MS. csPreState .~ preState'
577
+ & MS. csPostState .~ postState'
578
+ & MS. csArgBindings .~ argBindings'
579
+ & MS. csRetValue .~ retValue'
580
+
581
+ where
582
+ goState ss = do
583
+ pointsTos' <- mapM goPointsTo $ ss ^. MS. csPointsTos
584
+ conditions' <- mapM goSetupCondition $ ss ^. MS. csConditions
585
+ let freshVars' =
586
+ filter (\ tec -> not $ Map. member (SAW. ecVarIndex $ SAW. tecExt tec) sm) $
587
+ ss ^. MS. csFreshVars
588
+ return $ ss
589
+ & MS. csPointsTos .~ pointsTos'
590
+ & MS. csConditions .~ conditions'
591
+ & MS. csFreshVars .~ freshVars'
592
+
593
+ goArg (ty, sv) = do
594
+ sv' <- goSetupValue sv
595
+ return (ty, sv')
596
+
597
+ goPointsTo (MirPointsTo alloc svs) = MirPointsTo alloc <$> mapM goSetupValue svs
598
+
599
+ goSetupValue sv = case sv of
600
+ MS. SetupVar _ -> return sv
601
+ MS. SetupTerm tt -> MS. SetupTerm <$> goTypedTerm tt
602
+ MS. SetupNull _ -> return sv
603
+ MS. SetupStruct b packed svs -> MS. SetupStruct b packed <$> mapM goSetupValue svs
604
+ MS. SetupArray b svs -> MS. SetupArray b <$> mapM goSetupValue svs
605
+ MS. SetupElem b sv idx -> MS. SetupElem b <$> goSetupValue sv <*> pure idx
606
+ MS. SetupField b sv name -> MS. SetupField b <$> goSetupValue sv <*> pure name
607
+ MS. SetupGlobal _ _ -> return sv
608
+ MS. SetupGlobalInitializer _ _ -> return sv
609
+
610
+ goSetupCondition (MS. SetupCond_Equal loc sv1 sv2) =
611
+ MS. SetupCond_Equal loc <$> goSetupValue sv1 <*> goSetupValue sv2
612
+ goSetupCondition (MS. SetupCond_Pred loc tt) =
613
+ MS. SetupCond_Pred loc <$> goTypedTerm tt
614
+ goSetupCondition (MS. SetupCond_Ghost b loc gg tt) =
615
+ MS. SetupCond_Ghost b loc gg <$> goTypedTerm tt
616
+
617
+ goTypedTerm tt = do
618
+ term' <- goTerm $ SAW. ttTerm tt
619
+ return $ tt { SAW. ttTerm = term' }
620
+
621
+ goTerm term = SAW. scInstantiateExt sc sm term
622
+
623
+
624
+
483
625
-- RegValue -> SetupValue conversion
484
626
485
627
-- | Convert a RegValue into a SetupValue pattern. Symbolic variables in the
@@ -518,6 +660,7 @@ regToSetup sym p eval shp rv = go shp rv
518
660
| otherwise = error $ " regToSetup: type error: expected " ++ show shpTpr ++
519
661
" , but got Any wrapping " ++ show tpr
520
662
where shpTpr = StructRepr $ fmapFC fieldShapeType flds
663
+ go (TransparentShape _ shp) rv = go shp rv
521
664
go (RefShape refTy ty' tpr) ref = do
522
665
partIdxLen <- lift $ mirRef_indexAndLenSim ref
523
666
optIdxLen <- liftIO $ readPartExprMaybe sym partIdxLen
0 commit comments