1
+ using System ;
2
+ using System . Collections . Generic ;
3
+ using System . Diagnostics . Contracts ;
4
+ using System . Linq ;
5
+ using VC ;
6
+
7
+ namespace Microsoft . Boogie ;
8
+
9
+ sealed class CachedVerificationResultInjector : StandardVisitor
10
+ {
11
+ private ExecutionEngineOptions options ;
12
+ readonly Program Program ;
13
+
14
+ // TODO(wuestholz): We should probably increase the threshold to something like 2 seconds.
15
+ static readonly double TimeThreshold = - 1.0d ;
16
+ Program programInCachedSnapshot ;
17
+ Implementation currentImplementation ;
18
+ int assumptionVariableCount ;
19
+ int temporaryVariableCount ;
20
+
21
+ public static readonly CachedVerificationResultInjectorStatistics Statistics =
22
+ new CachedVerificationResultInjectorStatistics ( ) ;
23
+
24
+ int FreshAssumptionVariableName
25
+ {
26
+ get { return assumptionVariableCount ++ ; }
27
+ }
28
+
29
+ int FreshTemporaryVariableName
30
+ {
31
+ get { return temporaryVariableCount ++ ; }
32
+ }
33
+
34
+ CachedVerificationResultInjector ( ExecutionEngineOptions options , Program program )
35
+ {
36
+ Program = program ;
37
+ this . options = options ;
38
+ }
39
+
40
+ public Implementation Inject ( Implementation implementation , Program programInCachedSnapshot )
41
+ {
42
+ Contract . Requires ( implementation != null && programInCachedSnapshot != null ) ;
43
+
44
+ this . programInCachedSnapshot = programInCachedSnapshot ;
45
+ assumptionVariableCount = 0 ;
46
+ temporaryVariableCount = 0 ;
47
+ currentImplementation = implementation ;
48
+
49
+ #region Introduce explict assumption about the precondition.
50
+
51
+ var oldProc = programInCachedSnapshot . FindProcedure ( currentImplementation . Proc . Name ) ;
52
+ if ( oldProc != null
53
+ && oldProc . DependencyChecksum != currentImplementation . Proc . DependencyChecksum
54
+ && currentImplementation . ExplicitAssumptionAboutCachedPrecondition == null )
55
+ {
56
+ var axioms = new List < Axiom > ( ) ;
57
+ var after = new List < Cmd > ( ) ;
58
+ Expr assumedExpr = new LiteralExpr ( Token . NoToken , false ) ;
59
+ var canUseSpecs = DependencyCollector . CanExpressOldSpecs ( oldProc , Program , true ) ;
60
+ if ( canUseSpecs && oldProc . SignatureEquals ( options , currentImplementation . Proc ) )
61
+ {
62
+ var always = Substituter . SubstitutionFromDictionary ( currentImplementation . GetImplFormalMap ( options ) , true ,
63
+ currentImplementation . Proc ) ;
64
+ var forOld = Substituter . SubstitutionFromDictionary ( new Dictionary < Variable , Expr > ( ) ) ;
65
+ var clauses = oldProc . Requires . Select ( r =>
66
+ Substituter . FunctionCallReresolvingApply ( always , forOld , r . Condition , Program ) ) ;
67
+ var conj = Expr . And ( clauses , true ) ;
68
+ assumedExpr = conj != null
69
+ ? FunctionExtractor . Extract ( conj , Program , axioms )
70
+ : new LiteralExpr ( Token . NoToken , true ) ;
71
+ }
72
+
73
+ if ( assumedExpr != null )
74
+ {
75
+ var lv = new LocalVariable ( Token . NoToken ,
76
+ new TypedIdent ( Token . NoToken , string . Format ( "a##cached##{0}" , FreshAssumptionVariableName ) , Type . Bool ) ,
77
+ new QKeyValue ( Token . NoToken , "assumption" , new List < object > ( ) , null ) ) ;
78
+ currentImplementation . InjectAssumptionVariable ( lv , ! canUseSpecs ) ;
79
+ var lhs = new SimpleAssignLhs ( Token . NoToken , new IdentifierExpr ( Token . NoToken , lv ) ) ;
80
+ var rhs = LiteralExpr . And ( new IdentifierExpr ( Token . NoToken , lv ) , assumedExpr ) ;
81
+ var assumed = new AssignCmd ( currentImplementation . tok , new List < AssignLhs > { lhs } , new List < Expr > { rhs } ) ;
82
+ assumed . IrrelevantForChecksumComputation = true ;
83
+ currentImplementation . ExplicitAssumptionAboutCachedPrecondition = assumed ;
84
+ after . Add ( assumed ) ;
85
+ }
86
+
87
+ if ( options . TraceCachingForTesting || options . TraceCachingForBenchmarking ) {
88
+ using var tokTxtWr = new TokenTextWriter ( "<console>" , options . OutputWriter , false , false , options ) ;
89
+ var loc = currentImplementation . tok != null && currentImplementation . tok != Token . NoToken
90
+ ? string . Format ( "{0}({1},{2})" , currentImplementation . tok . filename , currentImplementation . tok . line ,
91
+ currentImplementation . tok . col )
92
+ : "<unknown location>" ;
93
+ options . OutputWriter . WriteLine ( "Processing implementation {0} (at {1}):" , currentImplementation . VerboseName , loc ) ;
94
+ foreach ( var a in axioms )
95
+ {
96
+ options . OutputWriter . Write ( " >>> added axiom: " ) ;
97
+ a . Expr . Emit ( tokTxtWr ) ;
98
+ options . OutputWriter . WriteLine ( ) ;
99
+ }
100
+
101
+ foreach ( var b in after )
102
+ {
103
+ options . OutputWriter . Write ( " >>> added after assuming the current precondition: " ) ;
104
+ b . Emit ( tokTxtWr , 0 ) ;
105
+ }
106
+ }
107
+ }
108
+
109
+ #endregion
110
+
111
+ var result = VisitImplementation ( currentImplementation ) ;
112
+ currentImplementation = null ;
113
+ this . programInCachedSnapshot = null ;
114
+ return result ;
115
+ }
116
+
117
+ public static void Inject ( ExecutionEngine engine , Program program ,
118
+ IReadOnlyList < Implementation > implementations , string requestId ,
119
+ string programId , out long [ ] cachingActionCounts )
120
+ {
121
+ var eai = new CachedVerificationResultInjector ( engine . Options , program ) ;
122
+
123
+ cachingActionCounts = new long [ Enum . GetNames ( typeof ( VC . ConditionGeneration . CachingAction ) ) . Length ] ;
124
+ var run = new CachedVerificationResultInjectorRun
125
+ {
126
+ Start = DateTime . UtcNow , ImplementationCount = implementations . Count ( ) ,
127
+ CachingActionCounts = cachingActionCounts
128
+ } ;
129
+ foreach ( var impl in implementations )
130
+ {
131
+ var vr = engine . Cache . Lookup ( impl , engine . Options . RunDiagnosticsOnTimeout , out var priority ) ;
132
+ if ( vr != null && vr . ProgramId == programId )
133
+ {
134
+ if ( priority == Priority . LOW )
135
+ {
136
+ run . LowPriorityImplementationCount ++ ;
137
+ }
138
+ else if ( priority == Priority . MEDIUM )
139
+ {
140
+ run . MediumPriorityImplementationCount ++ ;
141
+ }
142
+ else if ( priority == Priority . HIGH )
143
+ {
144
+ run . HighPriorityImplementationCount ++ ;
145
+ }
146
+ else if ( priority == Priority . SKIP )
147
+ {
148
+ run . SkippedImplementationCount ++ ;
149
+ }
150
+
151
+ if ( priority == Priority . LOW || priority == Priority . MEDIUM || engine . Options . VerifySnapshots >= 3 )
152
+ {
153
+ if ( TimeThreshold < vr . End . Subtract ( vr . Start ) . TotalMilliseconds )
154
+ {
155
+ eai . SetErrorAndAssertionChecksumsInCachedSnapshot ( impl , vr ) ;
156
+ if ( vr . ProgramId != null )
157
+ {
158
+ var p = engine . Cache . CachedProgram ( vr . ProgramId ) ;
159
+ if ( p != null )
160
+ {
161
+ eai . Inject ( impl , p ) ;
162
+ run . TransformedImplementationCount ++ ;
163
+ }
164
+ }
165
+ }
166
+ }
167
+ }
168
+ }
169
+
170
+ run . End = DateTime . UtcNow ;
171
+ Statistics . AddRun ( requestId , run ) ;
172
+ }
173
+
174
+ private void SetErrorAndAssertionChecksumsInCachedSnapshot ( Implementation implementation ,
175
+ ImplementationRunResult result )
176
+ {
177
+ if ( result . VcOutcome == VcOutcome . Errors && result . Errors != null &&
178
+ result . Errors . Count < options . ErrorLimit )
179
+ {
180
+ implementation . SetErrorChecksumToCachedError ( result . Errors . Select ( cex =>
181
+ new Tuple < byte [ ] , byte [ ] , object > ( cex . Checksum , cex . SugaredCmdChecksum , cex ) ) ) ;
182
+ implementation . AssertionChecksumsInCachedSnapshot = result . AssertionChecksums ;
183
+ }
184
+ else if ( result . VcOutcome == VcOutcome . Correct )
185
+ {
186
+ implementation . SetErrorChecksumToCachedError ( new List < Tuple < byte [ ] , byte [ ] , object > > ( ) ) ;
187
+ implementation . AssertionChecksumsInCachedSnapshot = result . AssertionChecksums ;
188
+ }
189
+ }
190
+
191
+ public override Cmd VisitCallCmd ( CallCmd node )
192
+ {
193
+ var result = base . VisitCallCmd ( node ) ;
194
+
195
+ var oldProc = programInCachedSnapshot . FindProcedure ( node . Proc . Name ) ;
196
+ if ( oldProc != null
197
+ && oldProc . DependencyChecksum != node . Proc . DependencyChecksum
198
+ && node . AssignedAssumptionVariable == null )
199
+ {
200
+ var before = new List < Cmd > ( ) ;
201
+ var beforePreconditionCheck = new List < Cmd > ( ) ;
202
+ var after = new List < Cmd > ( ) ;
203
+ var axioms = new List < Axiom > ( ) ;
204
+ Expr assumedExpr = new LiteralExpr ( Token . NoToken , false ) ;
205
+ // TODO(wuestholz): Try out two alternatives: only do this for low priority implementations or not at all.
206
+ var canUseSpecs = DependencyCollector . CanExpressOldSpecs ( oldProc , Program ) ;
207
+ if ( canUseSpecs && oldProc . SignatureEquals ( options , node . Proc ) )
208
+ {
209
+ var desugaring = node . GetDesugaring ( options ) ;
210
+ Contract . Assert ( desugaring != null ) ;
211
+ var precond = node . CheckedPrecondition ( oldProc , Program , e => FunctionExtractor . Extract ( e , Program , axioms ) ) ;
212
+ if ( precond != null )
213
+ {
214
+ var assume = new AssumeCmd ( node . tok , precond ,
215
+ new QKeyValue ( Token . NoToken , "precondition_previous_snapshot" , new List < object > ( ) , null ) ) ;
216
+ assume . IrrelevantForChecksumComputation = true ;
217
+ beforePreconditionCheck . Add ( assume ) ;
218
+ }
219
+
220
+ var unmods = node . UnmodifiedBefore ( oldProc ) ;
221
+ var eqs = new List < Expr > ( ) ;
222
+ foreach ( var unmod in unmods )
223
+ {
224
+ var oldUnmod = new LocalVariable ( Token . NoToken ,
225
+ new TypedIdent ( Token . NoToken , string . Format ( "{0}##old##{1}" , unmod . Name , FreshTemporaryVariableName ) ,
226
+ unmod . Type ) ) ;
227
+ var lhs = new SimpleAssignLhs ( Token . NoToken , new IdentifierExpr ( Token . NoToken , oldUnmod ) ) ;
228
+ var rhs = new IdentifierExpr ( Token . NoToken , unmod . Decl ) ;
229
+ var cmd = new AssignCmd ( Token . NoToken , new List < AssignLhs > { lhs } , new List < Expr > { rhs } ) ;
230
+ cmd . IrrelevantForChecksumComputation = true ;
231
+ before . Add ( cmd ) ;
232
+ var eq = LiteralExpr . Eq ( new IdentifierExpr ( Token . NoToken , oldUnmod ) ,
233
+ new IdentifierExpr ( Token . NoToken , unmod . Decl ) ) ;
234
+ eq . Type = Type . Bool ;
235
+ eq . TypeParameters = SimpleTypeParamInstantiation . EMPTY ;
236
+ eqs . Add ( eq ) ;
237
+ }
238
+
239
+ var mods = node . ModifiedBefore ( oldProc ) ;
240
+ var oldSubst = new Dictionary < Variable , Expr > ( ) ;
241
+ foreach ( var mod in mods )
242
+ {
243
+ var oldMod = new LocalVariable ( Token . NoToken ,
244
+ new TypedIdent ( Token . NoToken , string . Format ( "{0}##old##{1}" , mod . Name , FreshTemporaryVariableName ) ,
245
+ mod . Type ) ) ;
246
+ oldSubst [ mod . Decl ] = new IdentifierExpr ( Token . NoToken , oldMod ) ;
247
+ var lhs = new SimpleAssignLhs ( Token . NoToken , new IdentifierExpr ( Token . NoToken , oldMod ) ) ;
248
+ var rhs = new IdentifierExpr ( Token . NoToken , mod . Decl ) ;
249
+ var cmd = new AssignCmd ( Token . NoToken , new List < AssignLhs > { lhs } , new List < Expr > { rhs } ) ;
250
+ cmd . IrrelevantForChecksumComputation = true ;
251
+ before . Add ( cmd ) ;
252
+ }
253
+
254
+ assumedExpr = node . Postcondition ( oldProc , eqs , oldSubst , Program ,
255
+ e => FunctionExtractor . Extract ( e , Program , axioms ) ) ;
256
+ if ( assumedExpr == null )
257
+ {
258
+ assumedExpr = new LiteralExpr ( Token . NoToken , true ) ;
259
+ }
260
+ }
261
+
262
+ if ( assumedExpr != null )
263
+ {
264
+ var lv = new LocalVariable ( Token . NoToken ,
265
+ new TypedIdent ( Token . NoToken , string . Format ( "a##cached##{0}" , FreshAssumptionVariableName ) , Type . Bool ) ,
266
+ new QKeyValue ( Token . NoToken , "assumption" , new List < object > ( ) , null ) ) ;
267
+ node . AssignedAssumptionVariable = lv ;
268
+ currentImplementation . InjectAssumptionVariable ( lv , ! canUseSpecs ) ;
269
+ var lhs = new SimpleAssignLhs ( Token . NoToken , new IdentifierExpr ( Token . NoToken , lv ) ) ;
270
+ var rhs = LiteralExpr . And ( new IdentifierExpr ( Token . NoToken , lv ) , assumedExpr ) ;
271
+ var assumed = new AssignCmd ( node . tok , new List < AssignLhs > { lhs } , new List < Expr > { rhs } ) ;
272
+ assumed . IrrelevantForChecksumComputation = true ;
273
+ after . Add ( assumed ) ;
274
+ }
275
+
276
+ node . ExtendDesugaring ( options , before , beforePreconditionCheck , after ) ;
277
+ if ( options . TraceCachingForTesting || options . TraceCachingForBenchmarking ) {
278
+ using var tokTxtWr = new TokenTextWriter ( "<console>" , options . OutputWriter , false , false , options ) ;
279
+ var loc = node . tok != null && node . tok != Token . NoToken
280
+ ? $ "{ node . tok . filename } ({ node . tok . line } ,{ node . tok . col } )"
281
+ : "<unknown location>" ;
282
+ options . OutputWriter . WriteLine ( "Processing call to procedure {0} in implementation {1} (at {2}):" , node . Proc . VerboseName ,
283
+ currentImplementation . VerboseName , loc ) ;
284
+ foreach ( var a in axioms )
285
+ {
286
+ options . OutputWriter . Write ( " >>> added axiom: " ) ;
287
+ a . Expr . Emit ( tokTxtWr ) ;
288
+ options . OutputWriter . WriteLine ( ) ;
289
+ }
290
+
291
+ foreach ( var b in before )
292
+ {
293
+ options . OutputWriter . Write ( " >>> added before: " ) ;
294
+ b . Emit ( tokTxtWr , 0 ) ;
295
+ }
296
+
297
+ foreach ( var b in beforePreconditionCheck )
298
+ {
299
+ options . OutputWriter . Write ( " >>> added before precondition check: " ) ;
300
+ b . Emit ( tokTxtWr , 0 ) ;
301
+ }
302
+
303
+ foreach ( var a in after )
304
+ {
305
+ options . OutputWriter . Write ( " >>> added after: " ) ;
306
+ a . Emit ( tokTxtWr , 0 ) ;
307
+ }
308
+ }
309
+ }
310
+
311
+ return result ;
312
+ }
313
+ }
0 commit comments