1 | % (c) 2009-2019 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, | |
2 | % Heinrich Heine Universitaet Duesseldorf | |
3 | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) | |
4 | ||
5 | :- module(b_read_write_info,[get_accessed_vars/4, get_accessed_vars/5, | |
6 | recompute_while_read_write_info/2, | |
7 | get_nondet_modified_vars/3, | |
8 | tcltk_read_write_matrix/1, | |
9 | tcltk_variable_read_write_matrix/2, | |
10 | tcltk_dot_variable_modification_analysis/1, | |
11 | tcltk_get_guards_for_ops/1, | |
12 | tcltk_get_parameter_free_guards_for_ops/1, | |
13 | b_get_read_write/3, | |
14 | b_get_operation_read_guard_vars/3, | |
15 | b_get_read_write_vars/5, | |
16 | b_get_written_variables/1, check_all_variables_written/0, | |
17 | %,b_get_read_may_must_write/7 % currently not used outside | |
18 | ||
19 | get_lhs_assigned_identifier/2 | |
20 | ]). | |
21 | ||
22 | ||
23 | :- use_module(module_information,[module_info/2]). | |
24 | :- module_info(group,typechecker). | |
25 | :- module_info(description,'ProBs type checker and type inference module.'). | |
26 | ||
27 | :- use_module(library(lists)). | |
28 | :- use_module(library(ordsets)). | |
29 | :- use_module(self_check). | |
30 | :- use_module(error_manager). | |
31 | :- use_module(bsyntaxtree). | |
32 | ||
33 | ||
34 | recompute_while_read_write_info(TExpr,NewTExpr) :- | |
35 | (transform_bexpr(b_read_write_info:update_while_info,TExpr,NewTExpr) | |
36 | -> true | |
37 | ; add_internal_error('Call failed: ',transform_bexpr(b_read_write_info:update_while_info,TExpr,NewTExpr)), | |
38 | NewTExpr=TExpr). | |
39 | ||
40 | :- use_module(translate,[print_bexpr/1]). | |
41 | :- public update_while_info/2. | |
42 | update_while_info(b(WHILE,subst,OldInfo),b(WHILE,subst,NewInfo)) :- | |
43 | WHILE = while(_TPred,_TBody,_TInv,TVar), | |
44 | get_accessed_vars(b(WHILE,subst,OldInfo),[],Modifies,Reads), | |
45 | (debug:debug_mode(off) -> true | |
46 | ; nl,print('UPDATING WHILE with VARIANT: '),translate:print_bexpr(TVar),nl, | |
47 | format(' WHILE Reads : ~w~n Modifies : ~w~n',[Reads,Modifies]) | |
48 | ), | |
49 | (delete(OldInfo,reads(OldReads),OI1) -> true ; OldReads=[], OI1=OldInfo), | |
50 | (delete(OI1,modifies(OldModifies),OI2) -> true ; OldModifies=[], OI2=OI1), | |
51 | ((Reads,Modifies)=(OldReads,OldModifies) | |
52 | -> NewInfo=OldInfo | |
53 | ; format('***** different from Reads : ~w~n Modifies : ~w~n',[OldReads,OldModifies]), | |
54 | ord_subtract(Reads,OldReads,NewR), | |
55 | ord_subtract(OldReads,Reads,DelR), | |
56 | format(' added: ~w~n deleted: ~w~n****~n',[NewR,DelR]), | |
57 | NewInfo = [modifies(Modifies),reads(Reads)|OI2] | |
58 | ). | |
59 | ||
60 | % TO DO: also update info for operation call ! | |
61 | ||
62 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
63 | ||
64 | :- use_module(bsyntaxtree,[get_global_identifiers/1]). | |
65 | ||
66 | /* get_accessed_vars/4 computes the variables that are read or written to | |
67 | by a substitution. | |
68 | get_accessed_vars(+Subst,+LocalVars,-Modified,-Reads): | |
69 | Subst is the substitution (as a typed syntax tree) | |
70 | LocalVars is a list of (untyped) ids that are local variables. | |
71 | Local variables are ignored and not included in the list of read or written | |
72 | variables. | |
73 | Modified is the list of (untyped) identifiers that are written by the substitution and | |
74 | Reads is the list of (untyped) identifiers that are read. */ | |
75 | get_accessed_vars(TSubst,LocalVars,Modified,Reads) :- | |
76 | get_accessed_vars(TSubst,LocalVars,Modified,_,Reads). | |
77 | get_accessed_vars(TSubst,LocalVars,Modified,NonDetModified,Reads) :- | |
78 | get_global_identifiers(IS), % TO DO: currently this information is set up only *after* type checking !! | |
79 | % this means the accessed vars will also contain SETS and enumerated set elements ! | |
80 | % either we need to filter them out later, or change the startup / precompilation process | |
81 | list_to_ord_set(LocalVars,SLV), | |
82 | ord_union(IS,SLV,LocalVarsToIgnore), | |
83 | get_accessed_vars1(TSubst,LocalVarsToIgnore,In,Out), | |
84 | extract_reads_and_modifies(In,Out,Modified,NonDetModified,Reads). | |
85 | extract_reads_and_modifies(mrinfo(UnsortedMods,NonDetMods,UnsortedReads),mrinfo([],[],[]), | |
86 | Modified,NonDetModified,Reads) :- | |
87 | sort(UnsortedMods,Modified), | |
88 | sort(NonDetMods,NonDetModified), | |
89 | sort(UnsortedReads,Reads). | |
90 | ||
91 | % get variables which are modified non-deterministically; does not traverse operation calls at the moment | |
92 | get_nondet_modified_vars(TSubst,LocalVars,NonDetModified) :- | |
93 | Out=mrinfo([],[],[]), | |
94 | list_to_ord_set(LocalVars,SLocalVars), | |
95 | get_accessed_vars1(TSubst,SLocalVars,In,Out), | |
96 | In=mrinfo(_,UnsortedMods,_), | |
97 | sort(UnsortedMods,NonDetModified). | |
98 | ||
99 | ||
100 | is_operation_call(operation_call(Operation,Results,Arguments),Operation,Results,Arguments). | |
101 | is_operation_call(operation_call_in_expr(Operation,Arguments),Operation,[],Arguments). | |
102 | ||
103 | get_accessed_vars1(TSubst,LocalVars,In,Out) :- | |
104 | get_texpr_expr(TSubst,Subst), | |
105 | ( is_operation_call(Subst,Operation,Results,Arguments) -> | |
106 | get_texpr_info(TSubst,Info), | |
107 | def_get_texpr_id(Operation,op(OperationName)), %print(info(OperationName,Info)),nl, | |
108 | memberchk(modifies(MVars),Info), | |
109 | (memberchk(non_det_modifies(NonDetMVars),Info) -> true ; NonDetMVars=[]), | |
110 | memberchk(reads(RVars),Info), | |
111 | % info fields added in btype2(operation(...) in btypechecker.pl | |
112 | %op_modifies_reads_cache(OperationName,MVarsC,RVarsC) %this is no longer necessary | |
113 | (ground(MVars),ground(RVars) | |
114 | -> true | |
115 | ; add_error(b_read_write_info, 'Cannot obtain modifies/reads info for called operation (probably illegal local call): ',OperationName), | |
116 | % TEST 655 used to reach this branch | |
117 | MVars=[], RVars=[], NonDetMVars=[] | |
118 | % this should only happen when local operation called !?! | |
119 | % or ensure that operations are processed before we reach operation calls | |
120 | % this may be problematic for local operations | |
121 | ), | |
122 | % print(compute_acc_vars_for_op_call(modifies(MVars,NonDetMVars),Info,Subst)),nl, %trace, | |
123 | accessed_vars_for_operation_call(MVars,NonDetMVars,RVars,LocalVars,Results,Arguments,In,Out) | |
124 | ; get_accessed_vars2(Subst,LocalVars,In,Out) -> | |
125 | true | |
126 | ; Subst = identifier(ID) -> add_error(b_read_write_info,'Identifier not a valid B Operation: ',ID,TSubst),In=Out | |
127 | ; otherwise -> | |
128 | add_internal_error('Could not extract information about accessed variables for substitution', | |
129 | get_accessed_vars2(Subst,LocalVars,In,Out)), | |
130 | %trace, get_accessed_vars2(Subst,LocalVars,In,Out), | |
131 | fail). | |
132 | accessed_vars_for_operation_call(MVars,NonDetMVars,RVars,LocalVars,Results,Arguments) --> | |
133 | add_non_local_modified_vars(MVars,LocalVars), | |
134 | add_non_local_read_vars(RVars,LocalVars), | |
135 | add_modified_vars(Results,LocalVars), | |
136 | add_non_local_non_det_modified_vars(NonDetMVars,LocalVars), | |
137 | add_read_vars_l(Arguments,LocalVars). | |
138 | ||
139 | get_accessed_vars2(skip,_) --> !. | |
140 | get_accessed_vars2(block(Subst),LocalVars) --> get_accessed_vars1(Subst,LocalVars). | |
141 | get_accessed_vars2(init_statement(Subst),LocalVars) --> get_accessed_vars1(Subst,LocalVars). | |
142 | get_accessed_vars2(assertion(A,Subst),R) --> add_read_vars(A,R),get_accessed_vars1(Subst,R). | |
143 | get_accessed_vars2(assign(LHS,E),R) --> %Note: LHS can be nested function application f(a)(b) := | |
144 | {maplist(get_lhs_assigned_identifier,LHS,Ids)}, add_modified_vars(Ids,R), | |
145 | {convlist(get_lhs_read_expression,LHS,LHS_R), append(LHS_R,E,RE)}, add_read_vars_l(RE,R). | |
146 | get_accessed_vars2(assign_single_id(Id,E),R) --> | |
147 | add_read_vars_l([E],R), % Note: LHS is a single Identifier: nothing is ever read there | |
148 | add_modified_vars([Id],R). | |
149 | get_accessed_vars2(precondition(P,Subst),R) -->add_read_vars(P,R),get_accessed_vars1(Subst,R). | |
150 | get_accessed_vars2(assertion(Subst),R) --> get_accessed_vars1(Subst,R). | |
151 | get_accessed_vars2(choice(Substs),R) --> get_accessed_vars_for_choice(Substs,R). | |
152 | get_accessed_vars2(if(Substs),R) --> get_accessed_vars_for_if(Substs,R). | |
153 | get_accessed_vars2(select(Whens,Else),R) --> get_accessed_vars_for_select(Whens,Else,R). | |
154 | get_accessed_vars2(select(Whens),R) --> get_accessed_vars_for_select(Whens,none,R). | |
155 | get_accessed_vars2(case(Expr,Substs,Else),R) --> get_accessed_vars_for_case(Expr,Substs,Else,R). | |
156 | get_accessed_vars2(case_or(Exprs,Subst),R) --> add_read_vars_l(Exprs,R),get_accessed_vars1(Subst,R). | |
157 | get_accessed_vars2(any(Ids,G,Subst),R) --> get_mrinfo(ModIn,_,_), | |
158 | add_tvars_to_ord_list(R,Ids,L),add_read_vars(G,L), | |
159 | get_accessed_vars1(Subst,L), | |
160 | add_delta_modified_to_non_det_modified(ModIn). | |
161 | get_accessed_vars2(let(Ids,G,Subst),R) --> | |
162 | add_tvars_to_ord_list(R,Ids,L),add_read_vars(G,L),get_accessed_vars1(Subst,L). | |
163 | get_accessed_vars2(lazy_let_subst(ID,SharedExpr,Subst),R) --> add_tvars_to_ord_list(R,[ID],L), | |
164 | add_read_vars(SharedExpr,L),get_accessed_vars1(Subst,L). | |
165 | get_accessed_vars2(becomes_element_of(Ids,E),R) --> add_non_det_modified_vars(Ids,R),add_read_vars(E,R). | |
166 | get_accessed_vars2(becomes_such(Ids,E),LocalVars0) --> add_non_det_modified_vars(Ids,LocalVars0), | |
167 | {append_primed_vars(Ids,LocalVars0,LocalVars1)}, % all Ids are already marked as accessed, any reference to Id$0 is a read of the old value; do not add Id$0 to the accessed variables | |
168 | add_read_vars(E,LocalVars1). | |
169 | %get_accessed_vars2(evb2_becomes_such(Ids,E),R) --> % now translated to becomes_such | |
170 | % add_non_det_modified_vars(Ids,R), add_read_vars(E,R). | |
171 | get_accessed_vars2(sequence(Substs),R) --> get_accessed_vars_l(Substs,R). | |
172 | get_accessed_vars2(parallel(Substs),R) --> get_accessed_vars_l(Substs,R). | |
173 | get_accessed_vars2(while(Cond,Subst,Inv,Variant),R) --> | |
174 | get_accessed_vars_for_while(Cond,Subst,Inv,Variant,R). | |
175 | get_accessed_vars2(var(Ids,Subst),R) --> add_tvars_to_ord_list(R,Ids,L),get_accessed_vars1(Subst,L). | |
176 | % TODO: we need information about which variables are modified by an external function | |
177 | get_accessed_vars2(external_subst_call(_Name,Exprs),R) --> add_read_vars_l(Exprs,R). | |
178 | get_accessed_vars2(rlevent(_Name,_Section,_Status,Params,Guard,Theorems,Actions,VWitnesses,PWitnesses,_Unmod,AbstractEvents),R) --> | |
179 | add_tvars_to_ord_list(R,Params,L), | |
180 | add_read_vars(Guard,L), | |
181 | add_read_vars_l(Theorems,L), | |
182 | add_witness_read_vars_l(VWitnesses,L), | |
183 | add_witness_read_vars_l(PWitnesses,L), | |
184 | get_accessed_vars_l(Actions,L), | |
185 | get_accessed_vars_l(AbstractEvents,L). | |
186 | ||
187 | get_accessed_vars_for_while(Cond,Subst,Inv,Variant,Locals) --> | |
188 | get_accessed_vars_for_while2(Inv,Locals), | |
189 | add_read_vars(Cond,Locals), | |
190 | add_read_vars(Variant,Locals), | |
191 | get_accessed_vars1(Subst,Locals). | |
192 | get_accessed_vars_for_while2(Inv,Locals,In,Out) :- | |
193 | add_read_vars(Inv,Locals,LI,LO), | |
194 | extract_reads_and_modifies(LI,LO,Modified,_,Reads0), | |
195 | maplist(remove_optional_prime,Reads0,Reads), | |
196 | list_to_ord_set(Reads,SReads), % removing prime can create duplicates | |
197 | add_non_local_read_vars(SReads,Locals,In,Inter), | |
198 | add_non_local_modified_vars(Modified,Locals,Inter,Out). | |
199 | remove_optional_prime(Id,Orig) :- atom_concat(Orig,'$0',Id),!. | |
200 | remove_optional_prime(Id,Id). | |
201 | ||
202 | get_accessed_vars_for_if(ElseIfs,Locals) --> | |
203 | { maplist(split_elseif,ElseIfs,Conds,Subst), | |
204 | ( if_has_else_block(Conds) -> HasElse = has_else | |
205 | ; otherwise -> HasElse = if_with_no_else) }, | |
206 | % mark all used variables in the conditions as read | |
207 | add_read_vars_l(Conds,Locals), | |
208 | get_accessed_vars_for_if_when(HasElse,Subst,Locals). | |
209 | split_elseif(Expr,Cond,Subst) :- get_texpr_expr(Expr,if_elsif(Cond,Subst)). | |
210 | ||
211 | get_accessed_vars_for_select(Whens,Else,Locals) --> | |
212 | { maplist(split_when,Whens,Conds,Subst1), | |
213 | ( Else = none -> | |
214 | HasElse=select_no_else, Subst = Subst1 | |
215 | ; otherwise -> | |
216 | HasElse=has_else, Subst = [Else|Subst1]) }, | |
217 | % mark all used variables in the conditions as read | |
218 | add_read_vars_l(Conds,Locals), | |
219 | get_accessed_vars_for_if_when(HasElse,Subst,Locals). | |
220 | split_when(Expr,Cond,Subst) :- get_texpr_expr(Expr,select_when(Cond,Subst)). | |
221 | ||
222 | get_accessed_vars_for_if_when(HasElse,Subst,Locals) --> | |
223 | { get_accessed_vars_for_if_when2(HasElse,Subst,Locals,Modified,NonDetModifies,Reads) }, | |
224 | add_read_vars2(Reads), | |
225 | %add_modified_vars2(Modified) | |
226 | ? | ({(HasElse==has_else ; HasElse==if_with_no_else ; Subst = [_,_|_])} |
227 | -> {ord_union(NonDetModifies,Modified,M2)}, | |
228 | add_non_det_modified_vars2(M2) | |
229 | ; add_non_det_modified_vars2(NonDetModifies), | |
230 | add_modified_vars2(Modified) % there is only one branch: Modified variables not also marked as non-det modified | |
231 | ). | |
232 | ||
233 | :- meta_predicate maplist5(4,-,-,-,-). | |
234 | maplist5(_P,[],[],[],[]). | |
235 | maplist5(P,[H1|T1],[H2|T2],[H3|T3],[H4|T4]) :- | |
236 | if(call(P,H1,H2,H3,H4), | |
237 | maplist5(P,T1,T2,T3,T4), | |
238 | (add_internal_error('Call fails: ',maplist5(P,H1,H2,H3,H4)), | |
239 | fail)). | |
240 | ||
241 | get_accessed_vars_for_if_when2(has_else,Subst,Locals,ModifiedBySome,NonDetModifiedBySome,ReadBySome) :- | |
242 | maplist5(get_accessed_vars_x(Locals),Subst,Modifies,NonDetModifies,Reads), | |
243 | % We want to calculate those elements that are modified by some (in the union) | |
244 | % but not by all (in the intersection) | |
245 | ord_union(NonDetModifies,NonDetModifiedBySome), | |
246 | ord_union(Modifies,ModifiedBySome), | |
247 | ord_intersection(Modifies,ModifiedByAll), | |
248 | ord_subtract(ModifiedBySome,ModifiedByAll,ModifiedByNotAll), | |
249 | % we add those values that are not modified by all to the set of read variables | |
250 | % because e.g. "IF P THEN x:=1 END" is equivalent to "IF P THEN x:=1 ELSE x:=x END" | |
251 | ord_union([ModifiedByNotAll|Reads],ReadBySome). | |
252 | get_accessed_vars_for_if_when2(select_no_else,Subst,Locals,ModifiedBySome,NonDetModifiedBySome,ReadBySome) :- | |
253 | % Note "SELECT P THEN x:=1 END" is NOT equivalent to "SELECT P THEN x:=1 ELSE x:=x END" ! | |
254 | % But treatment for computing ModiiedBySome,ReadBySome is identical | |
255 | get_accessed_vars_for_if_when2(if_with_no_else,Subst,Locals,ModifiedBySome,NonDetModifiedBySome,ReadBySome). | |
256 | get_accessed_vars_for_if_when2(if_with_no_else,Subst,Locals,ModifiedBySome,NonDetModifiedBySome,ReadBySome) :- | |
257 | % no else block present, we must assume that each written variable | |
258 | % can also be treated like a read variable (see example in the other condition) | |
259 | maplist5(get_accessed_vars_x(Locals),Subst,Modifies,NonDetModifies,Reads), | |
260 | ord_union(NonDetModifies,NonDetModifiedBySome), | |
261 | ord_union(Modifies,ModifiedBySome), | |
262 | ord_union([ModifiedBySome|Reads],ReadBySome). | |
263 | ||
264 | get_accessed_vars_for_choice(Substs,Locals,In,mrinfo(ModOut,NDOut2,Read)) :- | |
265 | get_accessed_vars_for_if_when(has_else,Substs,Locals,In,Out), | |
266 | In = mrinfo(ModIn,_,_), | |
267 | Out = mrinfo(ModOut,NDOut,Read), % these three fields will be all variables | |
268 | % mark all assignments as non-deterministically chosen: | |
269 | add_modified_to_non_det_modified(ModIn,ModOut,NDOut,NDOut2). | |
270 | ||
271 | add_modified_to_non_det_modified(ModIn,ModOut,NDIn,NDOut) :- ModIn==ModOut,!,NDIn=NDOut. | |
272 | add_modified_to_non_det_modified([H|ModIn],ModOut,[H|NDIn],NDOut) :- | |
273 | add_modified_to_non_det_modified(ModIn,ModOut,NDIn,NDOut). % duplicates in NDOut not an issue !?! | |
274 | ||
275 | get_mrinfo(Modified,NonDetModified,InRead,I,I) :- | |
276 | I = mrinfo(Modified,NonDetModified,InRead). | |
277 | ||
278 | add_delta_modified_to_non_det_modified(ModIn,mrinfo(ModOut,NonDet,Read),mrinfo(ModOut,NonDet2,Read)) :- | |
279 | add_modified_to_non_det_modified(ModIn,ModOut,NonDet,NonDet2). | |
280 | ||
281 | get_accessed_vars_for_case(Expr,Eithers,Else,Locals) --> | |
282 | % we can treat a case statement analogously to an IF-THEN-ELSE statement | |
283 | % where an ELSE is present | |
284 | % additionally, we have to add all expressions to the read set | |
285 | add_read_vars(Expr,Locals), | |
286 | { maplist(split_case_or,Eithers,ExprsList,Substs), append(ExprsList,Exprs) }, | |
287 | add_read_vars_l(Exprs,Locals), | |
288 | get_accessed_vars_for_if_when(has_else,[Else|Substs],Locals). | |
289 | split_case_or(CaseOr,Exprs,Subst) :- get_texpr_expr(CaseOr,case_or(Exprs,Subst)). | |
290 | ||
291 | % just the same as get_accessed_vars, but with different order of parameters | |
292 | % to allow call with maplist | |
293 | get_accessed_vars_x(Locals,Subst,Modifies,NonDetModifies,Reads) :- | |
294 | get_accessed_vars(Subst,Locals,Modifies,NonDetModifies,Reads). | |
295 | ||
296 | if_has_else_block(Conds) :- | |
297 | last(Conds,Cond),is_truth(Cond). | |
298 | ||
299 | % add ids of unsorted list of typed identifiers to sorted list of ids: | |
300 | add_tvars_to_ord_list(SortedVars,TIds,NewVars,X,X) :- | |
301 | get_texpr_ids(TIds,Ids), | |
302 | list_to_ord_set(Ids,SIds), | |
303 | ord_union(SortedVars,SIds,NewVars). | |
304 | %append(Vars,Ids,NewVars). | |
305 | ||
306 | add_read_vars_l([],_LocalVars) --> !. | |
307 | add_read_vars_l([Expr|Rest],LocalVars) --> !, | |
308 | add_read_vars(Expr,LocalVars), | |
309 | add_read_vars_l(Rest,LocalVars). | |
310 | add_read_vars_l(E,LV) --> !, {add_internal_error('Not a list: ',add_read_vars_l(E,LV,_,_))}. | |
311 | ||
312 | add_witness_read_vars_l([],_LocalVars) --> !. | |
313 | add_witness_read_vars_l([Expr|Rest],LocalVars) --> | |
314 | add_witness_read_vars(Expr,LocalVars), !, | |
315 | add_witness_read_vars_l(Rest,LocalVars). | |
316 | add_witness_read_vars_l(E,LV) --> !, {add_internal_error('Not a witness list: ',add_witness_read_vars_l(E,LV,_,_))}. | |
317 | ||
318 | % extract modified identifier from LHS of assign: | |
319 | get_lhs_assigned_identifier(b(function(LHS,_),_,_),Res) :- !, get_lhs_assigned_identifier(LHS,Res). | |
320 | get_lhs_assigned_identifier(ID,ID). | |
321 | ||
322 | % extract part of LHS of assign which reads information: | |
323 | get_lhs_read_expression(b(function(LHS,Read1),T,I),Res) :- !, | |
324 | Res = b(function(LHS,Read1),T,I). | |
325 | % old version which for f(a) := b only marked a as read in LHS f(a); now we also mark f | |
326 | % indeed f(a) := b is equivalent to f := f <+ {a|->b} ; so the old value of f is implicitly read (important for ltsmin symbolic model checking) | |
327 | %get_lhs_read_expression(b(function(LHS,Read1),_,_),Res) :- !, | |
328 | % (get_lhs_read_expression(LHS,Read2) -> create_couple(Read1,Read2,Res) | |
329 | % ; Res = Read1). | |
330 | ||
331 | add_witness_read_vars(b(witness(ID,Predicate),witness,_),LocalVars) --> !, | |
332 | % a parameter witness talks about a parameter no longer there: it is not really read here, but the predicate is used to set the any local parameter in another context | |
333 | % a variable witness alks about a variable non-deterministically assigned in the abstraction; we also pretend it is not read here (but assigned somewhere else) | |
334 | add_tvars_to_ord_list(LocalVars,[ID],LocalVars2), % we add ID to local variables not read "really" read here | |
335 | %{print(witness_add_read_vars(ID)),nl}, | |
336 | add_read_vars(Predicate,LocalVars2). | |
337 | ||
338 | add_read_vars(Expression,LocalVars) --> | |
339 | {find_identifier_uses(Expression,LocalVars,ReadVars)}, | |
340 | add_read_vars2(ReadVars). | |
341 | add_read_vars2(Vars,mrinfo(Modified,NonDetModified,InRead), | |
342 | mrinfo(Modified,NonDetModified,OutRead)) :- append(Vars,OutRead,InRead). | |
343 | ||
344 | add_non_local_read_vars(ReadVars,LocalVars) --> | |
345 | {ord_subtract(ReadVars,LocalVars,AddVars)}, | |
346 | add_read_vars2(AddVars). | |
347 | add_non_local_modified_vars(WriteVars,LocalVars) --> | |
348 | {ord_subtract(WriteVars,LocalVars,AddVars)}, | |
349 | add_modified_vars2(AddVars). | |
350 | ||
351 | add_modified_vars(Ids,LocalVars) --> | |
352 | {filter_result_vars(Ids,LocalVars,ModifiedVars)}, | |
353 | add_modified_vars2(ModifiedVars). | |
354 | add_modified_vars2(Vars,In,Res) :- | |
355 | (var(Vars) | |
356 | -> add_internal_error('Modified Vars variable: ',add_modified_vars2(Vars,In,Res)) | |
357 | ; true), | |
358 | In = mrinfo(InModified,NonDetModified,Read), | |
359 | Res = mrinfo(OutModified,NonDetModified,Read), | |
360 | append(Vars,OutModified,InModified). | |
361 | ||
362 | add_non_local_non_det_modified_vars(WriteVars,LocalVars) --> | |
363 | {ord_subtract(WriteVars,LocalVars,AddVars)}, | |
364 | add_non_det_modified_vars2(AddVars). | |
365 | ||
366 | add_non_det_modified_vars(Ids,LocalVars) --> | |
367 | {filter_result_vars(Ids,LocalVars,ModifiedVars)}, | |
368 | add_non_det_modified_vars2(ModifiedVars). | |
369 | add_non_det_modified_vars2(Vars,In,Res) :- | |
370 | (var(Vars) | |
371 | -> add_internal_error('Modified Vars variable: ',add_non_det_modified_vars2(Vars,In,Res)) | |
372 | ; true), | |
373 | In = mrinfo(InModified,InNonDetModified,Read), | |
374 | Res = mrinfo(OutModified,OutNonDetModified,Read), | |
375 | append(Vars,OutModified,InModified), | |
376 | append(Vars,OutNonDetModified,InNonDetModified). | |
377 | ||
378 | ||
379 | :- use_module(tools,[remove_all/3]). | |
380 | filter_result_vars(Ids,LocalVars,ModifiedVars) :- | |
381 | % extract_changed_id might fail if the user entered | |
382 | % a wrong LHS of an assignment. This should lead to an | |
383 | % type error, handled by the assign type checking rule. | |
384 | convlist(extract_changed_id,Ids,Names), | |
385 | remove_all(Names,LocalVars,ModifiedVars). | |
386 | extract_changed_id(Id,Name) :- | |
387 | get_texpr_id(Id,Name),!. | |
388 | extract_changed_id(Func,Name) :- | |
389 | get_texpr_expr(Func,function(TId,_)), !, | |
390 | extract_changed_id(TId,Name). % we can have nested function calls f(x)(y) := z | |
391 | extract_changed_id(X,Name) :- | |
392 | add_internal_error('Could not extract changed id: ',extract_changed_id(X,Name)),fail. | |
393 | ||
394 | get_accessed_vars_l([],_) --> []. | |
395 | get_accessed_vars_l([S|Rest],R) --> get_accessed_vars1(S,R),get_accessed_vars_l(Rest,R). | |
396 | ||
397 | ||
398 | ||
399 | :- use_module(tools,[number_suffix/3]). | |
400 | :- assert_must_succeed(( b_read_write_info:append_primed_vars([b(identifier(x),integer,[])],[],R), R = ['x$0'])). | |
401 | append_primed_vars(TIds,R,L) :- | |
402 | maplist(add_dollar_zero,TIds,Primed), | |
403 | append(Primed,R,L). | |
404 | :- assert_must_succeed(( b_read_write_info:add_dollar_zero(b(identifier(x),integer,[]),R), R = 'x$0')). | |
405 | add_dollar_zero(TId,P) :- | |
406 | get_texpr_id(TId,Id), | |
407 | number_suffix(Id,0,P). | |
408 | ||
409 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
410 | ||
411 | % READ-WRITE-INFO for OPERATIONS | |
412 | ||
413 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | |
414 | ||
415 | ||
416 | :- use_module(bmachine,[b_get_operation_normalized_read_write_info/3, b_get_machine_operation/4, | |
417 | b_get_machine_variables/1, b_get_machine_constants/1, b_is_variable/1]). | |
418 | ||
419 | b_get_read_write(O,R,W) :- | |
420 | if(b_get_operation_normalized_read_write_info(O,R,W),true, | |
421 | (R,W)=('$all','$all')). % it used to be that in Event-B no read information was stored; all calls to b_get_operation_normalized_read_write_info failed; has been fixed | |
422 | ||
423 | b_get_machine_operation_with_local_paras(OpName,AllLocalParas,Body) :- | |
424 | b_get_machine_operation(OpName,Results,Parameters,Body), | |
425 | append(Parameters,Results,TP), | |
426 | get_texpr_ids(TP,TPIds), | |
427 | list_to_ord_set(TPIds,AllLocalParas). | |
428 | ||
429 | /* We distinguish between read variables in the guard and read variables in the event statement */ | |
430 | b_get_read_write_guard_action(Operation,ReadGuard,ReadAction,Read,Write) :- | |
431 | b_get_read_write(Operation,Read,Write), | |
432 | b_get_operation_read_guard_vars(Operation,false,ReadGuard), | |
433 | b_get_machine_operation_with_local_paras(Operation,LocalParas,Body), | |
434 | get_texpr_expr(Body,RLEVENT), | |
435 | (RLEVENT = rlevent(_Name,_Section,_Stat,_Parameters,_Grd,_Thm,_Act,_VWit,_PWit,_Unmod,_AbsEvents) -> | |
436 | % special code to deal with *all* guards/actions (also in abstractions) + not extract guards from actions | |
437 | % TO DO: maybe move this to get_guard/get_action (see below) ; or maybe provide parameter to choose | |
438 | % in some circumstances (Deadlock checking) it is sufficient to look at lowest refinement level | |
439 | % for LTSMin,... : we need info from all refinement levels and from theorems | |
440 | findall(A1,(get_full_eventb_action(RLEVENT,AA),member(A1,AA)),A) | |
441 | ; get_action(Operation,A) -> true | |
442 | ; otherwise -> add_error(b_get_read_write_guard_action,'Could not get action for operation: ',Operation), fail | |
443 | ), | |
444 | (is_list(A) % in case of Event-B events the action block is a list of assignments | |
445 | -> find_identifier_uses_l(A,LocalParas,RActionVars) % TO DO: use get_accessed_vars ? | |
446 | ; get_accessed_vars(A,LocalParas,_,RActionVars) | |
447 | ), | |
448 | list_to_ord_set(RActionVars,RActionVars1), | |
449 | rw_ord_intersection(RActionVars1,Read,ReadAction). | |
450 | %,debug_println(9,b_get_read_write(O,RGuard,R,W)). | |
451 | ||
452 | % this gets the variables/identifiers read in the guard of an operation | |
453 | % Note: this part will include parts of the guard that depend on the parameters | |
454 | b_get_operation_read_guard_vars(Operation,JustVariables,Result) :- | |
455 | b_get_machine_operation_with_local_paras(Operation,Paras,_), | |
456 | get_operation_guard(Operation,G), | |
457 | % RGuardVars can comprise also local variables by operations with parameters | |
458 | find_identifier_uses(G,Paras,RGuardVars), | |
459 | list_to_ord_set(RGuardVars,ReadGuard), | |
460 | (JustVariables=false | |
461 | -> Result = ReadGuard | |
462 | ; b_get_machine_variables(TVars),get_texpr_ids(TVars,Vars), | |
463 | list_to_ord_set(Vars,Variables), | |
464 | ord_intersection(ReadGuard,Variables,Result) | |
465 | ). | |
466 | ||
467 | % show full guards | |
468 | tcltk_get_guards_for_ops(list(Res)) :- | |
469 | findall([Name,' GUARD ',GS], | |
470 | (get_operation_guard(Name,G), | |
471 | translate:translate_bexpression(G,GS)), Res). | |
472 | ||
473 | % show guards which do not depend on parameters and the rest (the Action) | |
474 | tcltk_get_parameter_free_guards_for_ops(list(Res)) :- | |
475 | findall([Name,' LTSMIN-GUARD ',GS, 'ACTION ',AS], | |
476 | (get_operation_propositional_guards(Name,Guards,A),conjunct_predicates(Guards,G), | |
477 | translate:translate_bexpression(G,GS), | |
478 | translate:translate_substitution(A,AS)), Res). | |
479 | ||
480 | get_operation_guard(Operation,G) :- | |
481 | b_get_machine_operation(Operation,_RR,_P,TBody), | |
482 | get_texpr_expr(TBody,Body), | |
483 | get_operation_guard_aux(Body,Operation,G). | |
484 | ||
485 | :- use_module(probsrc(b_state_model_check), [get_guard/2]). | |
486 | get_operation_guard_aux(RLEVENT,_,G) :- | |
487 | RLEVENT = rlevent(_Name,_Section,_Stat,_Parameters,_Grd,_Thm,_Act,_VWit,_PWit,_Unmod,_AbsEvents), | |
488 | !, | |
489 | findall(G1,get_full_eventb_guard(RLEVENT,G1),Gs), | |
490 | conjunct_predicates(Gs,G). | |
491 | get_operation_guard_aux(_,Operation,G) :- get_guard(Operation,G). | |
492 | ||
493 | ||
494 | % TO DO: we should move this code to b_state_model_check/predicate_debugger:get_operation_enabling_condition2 ? | |
495 | get_full_eventb_guard(rlevent(_Name,_Section,_Stat,_Parameters,Grd,Thms,_Act,_VWit,_PWit,_Unmod,AbsEvents),G) :- | |
496 | (G=Grd | |
497 | ; member(G,Thms) % this is relevant for LTS Min; but not for normal enabling analysis | |
498 | ; member(b(E,_,_),AbsEvents),get_full_eventb_guard(E,G)). | |
499 | get_full_eventb_action(rlevent(_Name,_Section,_Stat,_Parameters,_Grd,_Thm,Act,_VWit,_PWit,_Unmod,AbsEvents),A) :- | |
500 | (A=Act ; member(b(E,_,_),AbsEvents),get_full_eventb_action(E,A)). | |
501 | ||
502 | ||
503 | % Note: this is the LTSMin meaning of action (for LTSMin a guard depending on the parameter is part of the action) | |
504 | % Action will also contain parts of the guard that depend on parameters ! | |
505 | % This is important for independence | |
506 | % Example: CopyYY2XX(p) = PRE v>1 & p = yy THEN xx := p END --> Guard is #p.(p=yy) --> v>1 ; LTS Min Action is PRE p=yy THEN x := yy END; | |
507 | get_action(OpName,Action) :- get_operation_propositional_guards(OpName,_,Action). | |
508 | ||
509 | ||
510 | % we need just the variables that are read by the operation (constants will not be considered) | |
511 | b_get_read_write_vars(Operation,ReadGuardV,ReadActionV,ReadV,Write) :- | |
512 | b_get_read_write_vars_and_consts(false,Operation,ReadGuardV,ReadActionV,ReadV,Write). | |
513 | b_get_read_write_vars_and_consts(IncludeConstants,Operation,ReadGuardV,ReadActionV,ReadV,Write) :- | |
514 | b_get_read_write_guard_action(Operation,ReadGuard,ReadAction,Read,Write), | |
515 | b_get_machine_variables(TVars), | |
516 | (IncludeConstants==true | |
517 | -> b_get_machine_constants(TConsts), append(TConsts,TVars,TCV) % should we include get_global_identifiers ? | |
518 | ; TCV = TVars), | |
519 | get_texpr_ids(TCV,Vars), | |
520 | list_to_ord_set(Vars,Variables), | |
521 | ord_intersection(ReadGuard,Variables,ReadGuardV), | |
522 | ord_intersection(ReadAction,Variables,ReadActionV), | |
523 | ord_intersection(Read,Variables,ReadV). | |
524 | ||
525 | ||
526 | % ----------------------------------------------- | |
527 | % compute MAY/MUST write information (for LTSMin) | |
528 | % ----------------------------------------------- | |
529 | ||
530 | :- use_module(bsyntaxtree, [get_texpr_id/2, def_get_texpr_ids/2, is_truth/1, def_get_texpr_id/2]). | |
531 | ||
532 | % get read/write with may/must write info | |
533 | b_get_read_may_must_write(Operation,MustWriteV,MayWriteV) :- | |
534 | b_get_machine_operation(Operation,ReturnVars,_P,Body), | |
535 | b_get_read_write(Operation,_,WriteV), | |
536 | (is_eventb_event(Body) -> MayWriteV = [], | |
537 | WriteV = MustWriteV % in Event-B there are no conditions on the actions | |
538 | ; extract_must_write(Body,MustWriteV1) -> | |
539 | % remove return values: they are not global variables written to | |
540 | def_get_texpr_ids(ReturnVars,RV),list_to_ord_set(RV,SRV), %print(removing(SRV,Operation)),nl, | |
541 | ord_subtract(MustWriteV1,SRV,MustWriteV), | |
542 | ord_subtract(WriteV,MustWriteV,MayWriteV) | |
543 | ; add_internal_error('Extraction of Must-Write failed: ',extract_must_write(Body,_)), | |
544 | MustWriteV=[], MayWriteV=WriteV). | |
545 | /* | |
546 | b_get_read_may_must_write(Operation,ReadGuardV,ReadActionV,ReadV,MustWriteV,MayWriteV,WriteV) :- | |
547 | b_get_read_write_vars_and_consts(true,Operation,ReadGuardV,ReadActionV,ReadV,WriteV), | |
548 | b_get_machine_operation(Operation,ReturnVars,_P,Body), | |
549 | (is_eventb_event(Body) -> MayWriteV = [], MustWriteV=WriteV % in Event-B there are no conditions on the actions | |
550 | ; extract_must_write(Body,MustWriteV1) -> | |
551 | % remove return values: they are not global variables written to | |
552 | def_get_texpr_ids(ReturnVars,RV),list_to_ord_set(RV,SRV), %print(removing(SRV,Operation)),nl, | |
553 | ord_subtract(MustWriteV1,SRV,MustWriteV), | |
554 | ord_subtract(WriteV,MustWriteV,MayWriteV) | |
555 | ; add_internal_error('Extraction of Must-Write failed: ',extract_must_write(Body,_)), | |
556 | MustWriteV=[], MayWriteV=WriteV). | |
557 | */ | |
558 | is_eventb_event(b(E,_,_)) :- | |
559 | E = rlevent(_Name,_Section,_Status,_Params,_Guard,_Theorems,_Actions,_VWitnesses,_PWitnesses,_Unmod,_AbstractEvents). | |
560 | ||
561 | % compute the variables that must be written by an operation if it is enabled | |
562 | extract_must_write(b(Sub,_,_),MV) :- extract_must_write_aux(Sub,MV),!. | |
563 | extract_must_write(O,MV) :- add_internal_error('Call failed: ',extract_must_write(O,MV)),MV=[]. | |
564 | ||
565 | ||
566 | %extract_must_write_aux(P,V) :- functor(P,F,N), print(ex_aux(F,N)),nl,fail. | |
567 | extract_must_write_aux(assign(LHS,_),V) :- maplist(get_id,LHS,Vars), list_to_ord_set(Vars,V). | |
568 | extract_must_write_aux(assign_single_id(TID,_),[ID]) :- get_id(TID,ID). | |
569 | extract_must_write_aux(becomes_such(LHS,_),V) :- maplist(get_id,LHS,Vars), list_to_ord_set(Vars,V). | |
570 | extract_must_write_aux(becomes_element_of(LHS,_),V) :- maplist(get_id,LHS,Vars), list_to_ord_set(Vars,V). | |
571 | extract_must_write_aux(precondition(_,TBody),MV) :- extract_must_write(TBody,MV). | |
572 | extract_must_write_aux(assertion(_,TBody),MV) :- extract_must_write(TBody,MV). | |
573 | extract_must_write_aux(any(_,_,TBody),MV) :- extract_must_write(TBody,MV). | |
574 | extract_must_write_aux(let(_,_,TBody),MV) :- extract_must_write(TBody,MV). | |
575 | extract_must_write_aux(lazy_let_subst(_,_,TBody),MV) :- extract_must_write(TBody,MV). | |
576 | extract_must_write_aux(block(TBody),MV) :- extract_must_write(TBody,MV). | |
577 | extract_must_write_aux(choice([A]),MV) :- !, extract_must_write(A,MV). | |
578 | extract_must_write_aux(choice([A|T]),MV) :- | |
579 | extract_must_write(A,MV1), extract_must_write_aux(choice(T),MV2), ord_intersection(MV1,MV2,MV). | |
580 | extract_must_write_aux(if([I]),MV) :- !, get_texpr_expr(I,if_elsif(Cond,TBody)), | |
581 | (is_truth(Cond) -> extract_must_write(TBody,MV) ; MV=[]). % we have ELSE skip and then write nothing | |
582 | extract_must_write_aux(if([I|T]),MV) :- get_texpr_expr(I,if_elsif(_,TBody)), | |
583 | extract_must_write(TBody,MV1), extract_must_write_aux(if(T),MV2), ord_intersection(MV1,MV2,MV). | |
584 | extract_must_write_aux(select([SW]),MV) :- !, get_texpr_expr(SW,select_when(_,TBody)),extract_must_write(TBody,MV). | |
585 | extract_must_write_aux(select([SW|T]),MV) :- | |
586 | get_texpr_expr(SW,select_when(_,TBody)), extract_must_write(TBody,MV1), | |
587 | extract_must_write_aux(select(T),MV2),ord_intersection(MV1,MV2,MV). | |
588 | extract_must_write_aux(select(T,Else),MV) :- | |
589 | extract_must_write(Else,MV1), | |
590 | extract_must_write_aux(select(T),MV2),ord_intersection(MV1,MV2,MV). | |
591 | extract_must_write_aux(case(_,[],Else),MV) :- extract_must_write(Else,MV). | |
592 | extract_must_write_aux(case(E,[C|T],Else),MV) :- | |
593 | get_texpr_expr(C,case_or(_,TBody)), | |
594 | extract_must_write(TBody,MV1), | |
595 | extract_must_write_aux(case(E,T,Else),MV2),ord_intersection(MV1,MV2,MV). | |
596 | extract_must_write_aux(parallel([A]),MV) :- !, extract_must_write(A,MV). | |
597 | extract_must_write_aux(parallel([A|T]),MV) :- | |
598 | extract_must_write(A,MV1), extract_must_write_aux(parallel(T),MV2), ord_union(MV1,MV2,MV). | |
599 | extract_must_write_aux(sequence(S),MV) :- !, extract_must_write_aux(parallel(S),MV). | |
600 | extract_must_write_aux(skip,[]). | |
601 | extract_must_write_aux(external_subst_call(_,_),[]). % assume there is no guarantee that something is written | |
602 | extract_must_write_aux(while(_,_,_,_),[]). | |
603 | extract_must_write_aux(var(Parameters,TBody),MV) :- | |
604 | def_get_texpr_ids(Parameters,IDs), | |
605 | list_to_ord_set(IDs,MV2), | |
606 | extract_must_write(TBody,MV1), | |
607 | ord_subtract(MV1,MV2,MV). | |
608 | extract_must_write_aux(operation_call(TOperation,_,_),MustWriteV) :- | |
609 | def_get_texpr_id(TOperation,op(Operation)), | |
610 | b_get_read_may_must_write(Operation,MustWriteV,_). %and ideally cache values | |
611 | extract_must_write_aux(Other,[]) :- print(uncovered_subst_for_must_write(Other)),nl. | |
612 | ||
613 | get_id(b(function(A,_),_,_),ID) :- !, get_id(A,ID). | |
614 | get_id(TID,ID) :- get_texpr_id(TID,ID). | |
615 | ||
616 | % mainly used for LTS Min (uses LTSMin's definition of Action and (parameter-free) Guard): | |
617 | tcltk_read_write_matrix(list([list(['Operation','ReadsGuard','ReadsAction','MustWrite','MayWrite'])|Result])) :- | |
618 | findall(list([Operation,RG,RA,MustW,MayW]),b_get_read_may_must_write_for_ltsmin(Operation,RG,RA,MustW,MayW),Result). | |
619 | ||
620 | :- use_module(bsyntaxtree,[get_global_identifiers/1]). | |
621 | % obtains the read and written variables (constants, SETS and enumerated elements not included) | |
622 | % seperates the operations into parameter-independent guards and the rest, the Action | |
623 | :- use_module(predicate_debugger,[get_operation_propositional_guards/3]). | |
624 | :- use_module(probsrc(b_interpreter),[b_get_machine_operation_for_animation/6]). | |
625 | :- use_module(bmachine,[b_top_level_operation/1]). | |
626 | ||
627 | ||
628 | get_operation(Operation,Results,Paras) :- | |
629 | b_top_level_operation(Operation), | |
630 | b_get_machine_operation_for_animation(Operation,Results,Paras,_TB,_OT,true). | |
631 | ||
632 | b_get_read_may_must_write_for_ltsmin(Operation,ReadsGuard,ReadsAction,MustW,MayW) :- | |
633 | get_operation(Operation,Results,Paras), | |
634 | % LTSmin needs the constants in the read matrices; | |
635 | % if they are not included, it will transfer the uninitialised constants from the root state | |
636 | %b_get_machine_constants(TConsts), append(Paras,TConsts,TP), | |
637 | append(Results, Paras, ResultsAndParas), | |
638 | get_texpr_ids(ResultsAndParas,TPIds), | |
639 | get_global_identifiers(Global), | |
640 | append(TPIds,Global,PIDs),list_to_ord_set(PIDs,VarsToIgnore), % the VarsToIgnore should not be included in the matrix | |
641 | b_get_read_may_must_write(Operation,MustW,MayW), % we ignore the info about guards and actions: LTS Min has another definition of what a guard is than b_get_read_may_must_write | |
642 | get_operation_propositional_guards(Operation,Guards,RestBody), % obtain LTSMin guards, which are independent of the parameters | |
643 | get_accessed_vars(RestBody,VarsToIgnore,_Modified,ReadsAction), % this computes the ReadsAction the LTSMin way | |
644 | %format('READS ACTION for ~w ~n ~w~n',[Operation,ReadsAction]), | |
645 | conjunct_predicates(Guards,G), | |
646 | find_identifier_uses(G,VarsToIgnore,ReadsGuard). | |
647 | % (op_has_parameters(Operation) | |
648 | % -> ReadsAction = FullReads % guards on parameters may be projected away; leading to implicit reads in actions | |
649 | % % TO DO: convert operations into normal form; until this is done: simply return full reads | |
650 | % ; ReadsAction = RA). | |
651 | ||
652 | %op_has_parameters(OpName) :- | |
653 | % b_get_machine_operation_for_animation(OpName,_Results,Paras,_TB,_OT,true), | |
654 | % Paras = [_|_]. | |
655 | ||
656 | ||
657 | rw_ord_intersection('$all',B,R) :- !,R=B. | |
658 | rw_ord_intersection(A,'$all',R) :- !,R=A. | |
659 | rw_ord_intersection(A,B,R) :- ord_intersection(A,B,R). | |
660 | ||
661 | ||
662 | % compute the variables that are written by at least one operation: | |
663 | b_get_written_variables(WrittenVariables) :- | |
664 | findall(Var, | |
665 | (b_top_level_operation(Operation), | |
666 | %get_operation(Operation,_Results,_Paras), | |
667 | b_get_read_write(Operation,_,WriteV), | |
668 | member(Var,WriteV) | |
669 | ),Vars), | |
670 | sort(Vars,WrittenVariables). | |
671 | ||
672 | % compute the variables that are read by at least one operation: | |
673 | b_get_read_variables(ReadVariables) :- | |
674 | findall(Var, | |
675 | (b_top_level_operation(Operation), | |
676 | b_get_read_write(Operation,ReadV,_), | |
677 | member(Var,ReadV) | |
678 | ),Vars), | |
679 | sort(Vars,ReadVariables). | |
680 | % compute the variables that are read by at least one operation: | |
681 | b_get_read_guard_variables(ReadVariables) :- | |
682 | findall(Var, | |
683 | (b_top_level_operation(Operation), | |
684 | b_get_operation_read_guard_vars(Operation,true,ReadV), | |
685 | member(Var,ReadV) | |
686 | ),Vars), | |
687 | sort(Vars,ReadVariables). | |
688 | ||
689 | % cannot be run in bmachine_static_checks as operations are not yet precompiled | |
690 | check_all_variables_written :- | |
691 | b_get_written_variables(WrittenVariables), | |
692 | b_get_machine_variables(TVars), | |
693 | member(TVar,TVars), | |
694 | get_texpr_id(TVar,Var), | |
695 | \+ ord_member(Var,WrittenVariables), | |
696 | add_warning(check_all_variables_written,'Variable is written by no operation (consider defining is as a constant): ',Var,TVar), | |
697 | fail. | |
698 | check_all_variables_written. | |
699 | ||
700 | % compute a matrix which shows for variables who reads and modifies them: | |
701 | tcltk_variable_read_write_matrix(Warn,list([list(['Variable','Read by','Written by'])|Result])) :- | |
702 | findall(list([Variable,Reads,Writes]), | |
703 | get_read_write_ops_for_variable(Variable,Reads,Writes),Result), | |
704 | (Warn=no_check -> true ; check_all_variables_written). | |
705 | ||
706 | get_read_write_ops_for_variable(Var,ReadOps,WriteOps) :- | |
707 | b_get_machine_variables(TVars), member(TVar,TVars), get_texpr_id(TVar,Var), | |
708 | findall(Op,(b_top_level_operation(Op),b_get_read_write(Op,Reads,_),ord_member(Var,Reads)),ReadOps), | |
709 | findall(Op,(b_top_level_operation(Op),b_get_read_write(Op,_,Writes),ord_member(Var,Writes)),WriteOps). | |
710 | ||
711 | ||
712 | % -------------------- READ/WRITE VARIABLE ANALYSIS GRAPH ------------------------- | |
713 | ||
714 | :-use_module(dot_graph_generator,[gen_dot_graph/6]). | |
715 | tcltk_dot_variable_modification_analysis(File) :- | |
716 | gen_dot_graph(File,b_read_write_info,var_op_node_predicate,var_mod_pred,none,none). | |
717 | ||
718 | ||
719 | :- use_module(tools_strings,[ajoin/2]). | |
720 | :- use_module(preferences,[get_preference/2]). | |
721 | var_op_node_predicate(NodeID,SubGraph,NodeDesc,Shape,Style,Color) :- | |
722 | b_get_written_variables(WrittenVariables), | |
723 | b_get_read_variables(ReadVariables), | |
724 | b_get_read_guard_variables(ReadGrdVariables), | |
725 | SubGraph=none, | |
726 | Color='RoyalBlue4', | |
727 | b_is_variable(NodeID), | |
728 | (ord_member(NodeID,ReadGrdVariables) | |
729 | -> Style=none | |
730 | ; get_preference(dot_var_mod_hide_only_written,false), | |
731 | Style=dotted | |
732 | ), | |
733 | (\+ ord_member(NodeID,WrittenVariables) | |
734 | -> Shape=invtrapezium, ajoin([NodeID,'\\n (NOT WRITTEN)'],NodeDesc) | |
735 | ; \+ ord_member(NodeID,ReadVariables) | |
736 | -> Shape=trapezium, ajoin([NodeID,'\\n (NOT READ)'],NodeDesc) | |
737 | ; Shape=box, NodeDesc=NodeID | |
738 | ). | |
739 | var_op_node_predicate(NodeID,SubGraph,NodeDesc,Shape,Style,Color) :- | |
740 | b_top_level_operation(NodeID), | |
741 | %b_get_read_write(NodeID,_,Writes), dif(Writes,[]), % do not show query operations | |
742 | SubGraph=none, Shape=rect, Style='filled,rounded', | |
743 | NodeDesc=NodeID, Color=gray90. | |
744 | ||
745 | ||
746 | :- use_module(translate,[translate_bexpression_with_limit/3]). | |
747 | :- public var_mod_pred/5. | |
748 | var_mod_pred(OpNodeID,Label,SuccID,Color,Style) :- Color=red4, Style=solid, | |
749 | b_get_read_guard_variables(ReadVariables), | |
750 | b_get_read_may_must_write(OpNodeID,MustWrite,_), | |
751 | member(SuccID,MustWrite), | |
752 | (get_preference(dot_var_mod_hide_only_written,true) | |
753 | -> ord_member(SuccID,ReadVariables) ; true), | |
754 | (get_assignment_value(OpNodeID,SuccID,Val) | |
755 | -> translate_bexpression_with_limit(Val,30,VS), | |
756 | ajoin([':= \\n',VS],Label) | |
757 | ; Label = writes). | |
758 | var_mod_pred(OpNodeID,Label,SuccID,Color,Style) :- Color=goldenrod, Label='may write',Style=dotted, | |
759 | b_get_read_guard_variables(ReadVariables), | |
760 | b_get_read_may_must_write(OpNodeID,_,MayWrite), | |
761 | member(SuccID,MayWrite), | |
762 | (get_preference(dot_var_mod_hide_only_written,true) | |
763 | -> ord_member(SuccID,ReadVariables) ; true). | |
764 | var_mod_pred(NodeID,Label,SuccOpID,Color,Style) :- Color=azure4, Style=solid, | |
765 | %b_get_read_write(SuccOpID,Reads,_), | |
766 | %b_get_read_may_must_write_for_ltsmin(SuccOpID,ReadsGuard,_,_,_), | |
767 | b_get_operation_read_guard_vars(SuccOpID,true,ReadsGuard), | |
768 | member(NodeID,ReadsGuard), | |
769 | (get_decisive_predicate_from_guard(SuccOpID,NodeID,_Pred,Op,Val) | |
770 | -> translate_bexpression_with_limit(Val,30,VS), | |
771 | ajoin([Op,'\\n',VS],Label) | |
772 | ; Label = reads). | |
773 | ||
774 | % try to find relevant, decisive checks from guard | |
775 | get_decisive_predicate_from_guard(Operation,Variable,TPred,Op,TVal) :- | |
776 | get_operation_guard(Operation,G), | |
777 | member_in_conjunction(TPred,G), | |
778 | get_texpr_expr(TPred,Pred), | |
779 | is_decisive(Pred,Variable,Op,TVal). | |
780 | ||
781 | is_decisive(equal(TID,VAL),Variable,'=',VAL) :- get_texpr_id(TID,Variable), simple_val(VAL). | |
782 | is_decisive(equal(VAL,TID),Variable,'=',VAL) :- get_texpr_id(TID,Variable), simple_val(VAL). | |
783 | % TO DO: other checks ? not_equal, greater, ... | |
784 | simple_val(b(V,_,_)) :- simple_val2(V). | |
785 | simple_val2(empty_set). | |
786 | simple_val2(empty_sequence). | |
787 | simple_val2(boolean_true). | |
788 | simple_val2(boolean_false). | |
789 | simple_val2(integer(_)). | |
790 | simple_val2(string(_)). | |
791 | simple_val2(max_int). | |
792 | simple_val2(min_int). | |
793 | simple_val2(value(_)). | |
794 | ||
795 | get_assignment_value(Operation,Variable,Val) :- | |
796 | get_action(Operation,TAction), | |
797 | get_texpr_expr(TAction,Action), | |
798 | get_assign_val(Action,Variable,Val). | |
799 | ||
800 | get_assign_val(assign_single_id(TID,Val),ID,Val) :- | |
801 | get_texpr_id(TID,ID),!, | |
802 | (simple_val(Val) -> true ; simple_modification(Val,ID)). | |
803 | get_assign_val(any(_,_,TBody),Var,Val) :- get_texpr_expr(TBody,Body), get_assign_val(Body,Var,Val). | |
804 | get_assign_val(select([b(select_when(_,TBody),_,_)]),Var,Val) :- | |
805 | get_texpr_expr(TBody,Body), get_assign_val(Body,Var,Val). | |
806 | get_assign_val(parallel(Bodies),Var,Val) :- member(TBody,Bodies), | |
807 | get_texpr_expr(TBody,Body), get_assign_val(Body,Var,Val). | |
808 | % TO DO: sequence, ... | |
809 | ||
810 | simple_modification(TExpr,Var) :- get_texpr_expr(TExpr,Expr), | |
811 | simple_modification_aux(Expr,Var). | |
812 | simple_modification_aux(add(A,B),Var) :- simple_binop(A,B,Var). | |
813 | simple_modification_aux(minus(A,B),Var) :- simple_binop(A,B,Var). | |
814 | simple_modification_aux(multiplication(A,B),Var) :- simple_binop(A,B,Var). | |
815 | ||
816 | simple_binop(TID,Val,Var) :- get_texpr_id(TID,Var), simple_val(Val). | |
817 | simple_binop(Val,TID,Var) :- get_texpr_id(TID,Var), simple_val(Val). | |
818 |