1 % (c) 2013-2023 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_expression_sharing,
6 [ apply_expression_sharing_to_machine/2
7 , create_initial_expression_cache/1
8 , find_common_subexpressions/4
9 , cse_optimize_predicate/2
10 , cse_optimize_substitution/2
11 , expand_cse_lazy_lets/2 ,expand_cse_lazy_lets_if_cse_enabled/2
12 , is_lazy_let_identifier/1
13 ]).
14
15 :- use_module(probsrc(module_information),[module_info/2]).
16 :- module_info(group,typechecker).
17 :- module_info(description,'Common-Subexpression Elimination.').
18
19 :- use_module(library(lists)).
20 :- use_module(library(ordsets)).
21 :- use_module(library(avl)).
22
23 :- use_module(probsrc(tools)).
24 :- use_module(probsrc(self_check)).
25 :- use_module(probsrc(error_manager)).
26 :- use_module(probsrc(debug)).
27
28 :- use_module(probsrc(bsyntaxtree)).
29 :- use_module(probsrc(bmachine_structure)).
30
31 :- use_module(probsrc(preferences),[get_preference/2]).
32
33 expression_sharing_enabled :-
34 get_preference(use_common_subexpression_elimination,true).
35
36 create_subcache(Id,cache(Id,Avl)) :- empty_avl(Avl).
37
38 create_initial_expression_cache(A) :-
39 expression_sharing_enabled,!,
40 A=[Cache],create_subcache(top,Cache).
41 create_initial_expression_cache(disabled).
42
43
44 apply_expression_sharing_to_machine(InMachine,OutMachine) :-
45 change_sections(InMachine,InExprs,OutExprs,OutMachine),
46 create_stores(0,EmptyStores),
47 remove_implicit_negation_l(InExprs,NormedExprs),
48 find_common_subexpressions_l(NormedExprs,EmptyStores,true,machine,1,OutExprs,Stores,_Stripped,_UsedIds,_UsedCSEIds),
49 ground_reference_counters(Stores),!.
50
51 change_sections(InMachine,InExprs,OutExprs,OutMachine) :-
52 change_section2([constraints,properties,invariant,
53 assertions,operation_bodies],
54 InMachine,InExprs,OutExprs,OutMachine).
55 change_section2([],Machine,[],[],Machine).
56 change_section2([Sec|Rest],InMachine,InExprs,OutExprs,OutMachine) :-
57 select_section_texprs(Sec,LInExprs,LOutExprs,InMachine,InterMachine),
58 append(LInExprs,RInExprs,InExprs),
59 append(LOutExprs,ROutExprs,OutExprs),
60 change_section2(Rest,InterMachine,RInExprs,ROutExprs,OutMachine).
61
62
63
64 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
65
66 :- use_module(probsrc(bsyntaxtree)).
67
68 % TESTS: x+y >100 & x+y <99 now fails deterministically in REPL with CSE
69
70 % (x+y)+(x+y)=(x+y)
71
72 % Note: we should detect if we are at the invariant level before calling cse_optimize_predicate: do not mess up Rodin rodinpos proof information or syntactically_relevance info
73
74 % new predicate to optimize an operation body; not yet used
75 :- public cse_optimize_operation/1.
76 :- use_module(probsrc(bmachine),[b_top_level_operation/1,b_get_machine_operation/4]).
77 cse_optimize_operation(OpName) :-
78 b_top_level_operation(OpName),
79 b_get_machine_operation(OpName,_Results,_Parameters,Body),
80 print(optimizing(OpName)),nl,
81 cse_optimize_substitution(Body,_Res).
82
83 % no difference thus far with cse_optimize_predicate
84 cse_optimize_substitution(Subst,Res) :- cse_optimize_predicate(Subst,Res),
85 (debug_mode(on) -> nl,print('FINISHED CSE: '),nl, translate:print_subst(Res),nl ; true).
86
87 cse_optimize_predicate(Pred,Res) :- already_cse_optimized(Pred),
88 !,
89 Res=Pred.
90 cse_optimize_predicate(Pred,Res) :-
91 cse_print_check('Running Common Sub Expressions (CSE) Optimization on ', Pred),
92 expand_cse_lazy_lets(Pred,ExpandedPred), % TO DO: see if we can do this more efficiently; do we need it at all ??
93 cse_print_check('Expanded Expression: ', ExpandedPred),
94 % Note: we can still have unexpanded lazy-lets hanging around in ExpandedPred from outer scopes
95 find_maximum_lazy_let_id(ExpandedPred,MaxID), % find maximum ID
96 StartNr is MaxID+1, % we will start numbering from there to avoid clashes with existing lazy-lets
97 find_common_subexpressions(ExpandedPred,StartNr,true,R,_Refs),
98 cse_print_check('Found common sub expressions: ',R),
99
100 % print('Common: '),nl, translate:nested_print_bexpr_or_subst(R),nl,nl,nl,
101 bup_init_acc(InitialAcc),
102 reset_let_dependency_info,
103 transform_bexpr_with_bup_accs(b_expression_sharing:bup_insert_lazy_lookups,R,R2,InitialAcc,_BUPAcc),
104 %portray_avl(BUPAcc),nl,
105 %print_bexpr_or_subst(R2),nl,nl,
106 % tools_printing:trace_print(R2,6),nl,nl,
107 empty_avl(Empty),
108 avl_store('$maximum_unregistered_lazy_let_id',Empty,MaxID,TDAcc),
109 top_down_gen_lazy_lets(R2,TDAcc,OptimizedPred),
110
111 %cse_print_check('Optimized Expression Result: ',OptimizedPred), % info fields not yet set
112 transform_bexpr(b_expression_sharing:cse_cleanup_infos,OptimizedPred,COPred),
113 cse_print_check('Cleaned up Optimized Expression Result: ',COPred),
114
115 % nl,print('FINISHED CSE: '),nl, translate:nested_print_bexpr_or_subst(COPred),nl, %portray_avl(LastAcc),nl,nl,
116 !,
117 mark_as_optimized(COPred,Res).
118 cse_optimize_predicate(P,Res) :-
119 add_internal_error('CSE FAILED: ',cse_optimize_predicate(P,Res)),
120 Res=P.
121
122 already_cse_optimized(b(_E,_T,I)) :- member(cse_optimized,I).
123 mark_as_optimized(b(E,T,I),b(E,T,I2)) :-
124 append(I,[cse_optimized],I2).
125
126 :- use_module(probsrc(translate),[print_bexpr_or_subst/1]).
127 cse_print_check(Msg,Expr) :- debug:debug_mode(on), %%
128 !,
129 print(Msg), print_functor(Expr),
130 print_bexpr_or_subst(Expr), nl,
131 cse_check_ast(Msg,Expr).
132 cse_print_check(_,_).
133
134 print_functor(b(E,T,_)) :- functor(E,F,A), format('Type: ~w, Functor: ~w/~w~n',[T,F,A]).
135 print_functor(E) :- functor(E,F,A), format('Unwrapped, Functor: ~w/~w~n',[F,A]).
136
137 :- load_files(library(system), [when(compile_time), imports([environ/2])]).
138 :- if(environ(prob_safe_mode,true)).
139 % check the AST (should be removed in production version of ProB):
140 cse_check_ast(PP,Pred) :-
141 debug_print(4,'Checking AST : '), debug_println(4,PP), % translate:print_bexpr_or_subst(Pred),nl,
142 check_ast(true,Pred).
143
144 %cse_check_ast_complete(PP,Pred) :- cse_check_ast(PP,Pred),
145 % debug_println(20,checking_ast_complete(PP)),
146 % % currently will generate spurious warnings when the term to start with has @identifiers introduced by LETs higher up
147 % map_over_bexpr_top_down_acc(b_expression_sharing:check_lazy_let_vars,Pred,[]),fail.
148 %cse_check_ast_complete(_,_).
149
150 %check_lazy_let_vars(X,Acc,R) :- print(t(X,Acc,R)),nl,fail.
151 check_lazy_let_vars(lazy_let_expr(TID,_,_),Acc,[ID|Acc]) :- !, get_texpr_id(TID,ID),
152 (member(ID,Acc) -> print('multiply defined id: '), print(ID),nl ; true).
153 check_lazy_let_vars(lazy_let_pred(TID,_,_),Acc,[ID|Acc]) :- !, get_texpr_id(TID,ID),
154 (member(ID,Acc) -> print('multiply defined id: '), print(ID),nl ; true).
155 check_lazy_let_vars(lazy_lookup_pred(ID),Acc,Acc) :- !,
156 (member(ID,Acc) -> true ; add_internal_error('Lazy lookup pred id not defined: ',ID)).
157 check_lazy_let_vars(lazy_lookup_expr(ID),Acc,Acc) :- !,
158 (member(ID,Acc) -> true ; add_internal_error('Lazy lookup expr id not defined: ',ID)).
159 %check_occurs_in_expr(ID,E) :-
160 % (occurs_in_expr(ID,E) -> true
161 % ; add_internal_error('Id does not occur in expression: ',ID),
162 % print('Expression: '), translate:print_bexpr_or_subst(E),nl).
163 :- else.
164 cse_check_ast(_PP,_Pred).
165 %cse_check_ast_complete(_,_).
166 %check_occurs_in_expr(_,_).
167 :- endif.
168
169
170 % -----------------------------------------------
171
172 % utility to expand lazy let expressions for better CSE if repeatedly applied
173 % Note: lazy_lookups could remain if only a partial predicate is passed
174
175 expand_cse_lazy_lets_if_cse_enabled(E,EE) :-
176 (expression_sharing_enabled -> expand_cse_lazy_lets(E,EE) ; E=EE).
177
178 expand_cse_lazy_lets(Expr,ExpandedExpr) :-
179 transform_bexpr(b_expression_sharing:expand_lazy_let,Expr,ExpandedExpr).
180
181 :- public expand_lazy_let/2.
182 %expand_lazy_let(B,R) :- tools_printing:print_term_summary(B),nl,fail.
183 expand_lazy_let(b(E,Type,Info),Result) :- expand_lazy_let_aux(E,Type,Info,Result).
184 expand_lazy_let_aux(lazy_let_expr(TID,SharedExpr,MainExpr),_,_,Result) :-
185 get_texpr_id(TID,ID), debug_println(4,expanding(ID)),
186 expand_lazy_lookup(ID,SharedExpr,MainExpr,Result).
187 expand_lazy_let_aux(lazy_let_pred(TID,SharedExpr,MainExpr),_,_,Result) :-
188 get_texpr_id(TID,ID), debug_println(4,expanding(ID)),
189 expand_lazy_lookup(ID,SharedExpr,MainExpr,Result).
190 expand_lazy_let_aux(lazy_let_subst(TID,SharedExpr,MainExpr),_,_,Result) :-
191 get_texpr_id(TID,ID), debug_println(4,expanding(ID)),
192 expand_lazy_lookup(ID,SharedExpr,MainExpr,Result).
193 expand_lazy_let_aux(E,Type,Info,Result) :- E \= value(_), E \= integer(_),
194 compute_static_expression(E,Val),
195 %print(computed(E,Val)),nl,
196 Result=b(value(Val),Type,Info).
197
198 expand_lazy_lookup(ID,SharedExpr,MainExpr,Result) :- \+ atom(ID),!,
199 add_internal_error('Illegal call: ',expand_lazy_lookup(ID,SharedExpr,MainExpr,Result)),
200 Result=MainExpr.
201 expand_lazy_lookup(ID,SharedExpr,MainExpr,Result) :-
202 transform_bexpr(b_expression_sharing:replace_lazy_lookup(ID,SharedExpr),MainExpr,Result).
203
204 % TO DO: unify with b_compile and ast_cleanup !
205 %compute_static_texpression(b(E,_,_),Result) :- compute_static_expression(E,Result).
206 compute_static_int_texpression(b(E,_,_),Result) :- compute_static_expression(E,int(Result)).
207
208 :- use_module(probsrc(kernel_objects),[safe_pown/3]).
209 compute_static_expression(add(A,B),int(Result)) :- % plus
210 compute_static_int_texpression(A,IA), compute_static_int_texpression(B,IB),
211 Result is IA+IB.
212 compute_static_expression(minus(A,B),int(Result)) :- % plus
213 compute_static_int_texpression(A,IA), compute_static_int_texpression(B,IB),
214 Result is IA-IB.
215 compute_static_expression(unary_minus(A),int(Result)) :- % plus
216 compute_static_int_texpression(A,IA),
217 Result is -IA.
218 compute_static_expression(multiplication(A,B),int(Result)) :-
219 compute_static_int_texpression(A,IA), compute_static_int_texpression(B,IB),
220 Result is IA*IB.
221 compute_static_expression(div(A,B),int(Result)) :- % TO DO: also add floored_div
222 compute_static_int_texpression(B,IB), IB \= 0,
223 compute_static_int_texpression(A,IA),
224 Result is IA//IB.
225 compute_static_expression(power_of(A,B),int(Result)) :-
226 compute_static_int_texpression(A,IA), compute_static_int_texpression(B,IB), IB >= 0,
227 safe_pown(IA,IB,Result), number(Result).
228 % TO DO: modulo, division, set_extension
229 compute_static_expression(integer(I),int(I)).
230 compute_static_expression(value(I),I).
231 compute_static_expression(equal(A,B),I) :- compute_static_int_texpression(A,SA), compute_static_int_texpression(B,SB),
232 (SA==SB -> I = pred_true ; I = pred_false).
233 compute_static_expression(not_equal(A,B),I) :- compute_static_int_texpression(A,SA), compute_static_int_texpression(B,SB),
234 (SA==SB -> I = pred_false ; I = pred_true).
235
236
237 % -----------------------------------------------
238
239 % find maximum id used in lazy lookups; useful to generate new unused identifiers
240 find_maximum_lazy_let_id(BExpr,MaxID) :-
241 reduce_over_bexpr(b_expression_sharing:find_maximum_lazy_let_id_aux,BExpr,-1,MaxID).
242
243 :- public find_maximum_lazy_let_id_aux/3.
244 find_maximum_lazy_let_id_aux(lazy_lookup_expr(ID),MaxSoFar,NewMax) :- !,
245 convert_atom_to_nr(ID,IDNr),
246 (IDNr @> MaxSoFar -> NewMax=IDNr ; NewMax=MaxSoFar).
247 find_maximum_lazy_let_id_aux(lazy_lookup_pred(ID),MaxSoFar,NewMax) :-
248 convert_atom_to_nr(ID,IDNr),
249 (IDNr @> MaxSoFar -> NewMax=IDNr ; NewMax=MaxSoFar).
250 find_maximum_lazy_let_id_aux(_,V,V).
251
252 % -----------------------------------------------
253
254 :- use_module(library(ordsets)).
255 :- use_module(library(avl)).
256
257 % BUP traversal; replacing shared expressions by lazy_lookup and propagate usage information upwards
258 % no introduction of lazy_let yet
259 % Accumulator contains list of introduced lazy_lets
260
261 % example call ((x+y)+z)*(x+y)+((x+z)+y) + (x+z) = 0
262
263 % the starting accumulator for this traversal:
264 % '$used_lazy_lookups': special key to store which lazy_lookups occur below
265 bup_init_acc(Acc3) :- empty_avl(E),
266 avl_store('$used_lazy_lookups',E,[]-[],Acc1), % used directly and indirectly
267 avl_store('$identifier_uses',Acc1,[],Acc2),
268 avl_store('$lazy_lookups_directly_used',Acc2,[],Acc3).
269
270 :- public bup_insert_lazy_lookups/4.
271 bup_insert_lazy_lookups(BE,Res,SubAccs,NewAcc) :-
272 BE2=BE, %update_info_field(BE,BE2), % we have just treated sub-expressions and we may insert BE into avl tree of shared expressions; info field is now updated in top-down pass
273 % combine all infos about lazy_lets from subexpressions:
274 merge_bup_accs(SubAccs,Acc),
275 bup_update_identifier_uses(BE,Acc,Acc1),
276 bup_insert_lazy_lookup_if_necessary(BE2,Res,Acc1,NewAcc).
277
278 % also compute used identifiers bup (ignoring @LazyLookup replacements)
279 % a slightly rewritten version of find_typed_identifier_uses2 without recursive calls
280 bup_update_identifier_uses(TExpr,Acc,NewAcc) :-
281 avl_fetch('$identifier_uses',Acc,UsedIds),
282 get_texpr_expr(TExpr,Expr),safe_syntaxelement(Expr,_Subs,TNames,_,_),
283 ( bsyntaxtree:uses_an_identifier(Expr,Id)
284 -> ord_add_element(UsedIds,Id,New),avl_store('$identifier_uses',Acc,New,NewAcc)
285 ; TNames=[] -> NewAcc=Acc
286 ; get_texpr_ids(TNames,Names), % these names are introduced and no longer visible higher up
287 sort(Names,SNames),
288 ord_subtract(UsedIds,SNames,New),
289 avl_store('$identifier_uses',Acc,New,Acc1),
290 % now invalidate all lazy_lookups which use an introduced variable name in SNames
291 avl_fetch('$used_lazy_lookups',Acc1,All-Intro),
292 exclude(filter_lazy_lookup(SNames),All,FAll),
293 exclude(filter_lazy_lookup(SNames),Intro,FIntro),
294 avl_store('$used_lazy_lookups',Acc1,FAll-FIntro,Acc2),
295 avl_fetch('$lazy_lookups_directly_used',Acc2,Direct),
296 exclude(filter_lazy_lookup(SNames),Direct,FDirect),
297 avl_store('$lazy_lookups_directly_used',Acc2,FDirect,NewAcc)
298 %,print(filter(SNames,Intro,FIntro)),nl
299 ).
300
301 % detect which lazy lookups are no longer valid higher up the AST
302 filter_lazy_lookup(SortedNames,LazyLookupID) :-
303 lazy_let_to_be_introduced(LazyLookupID,_,_,UsedIds,SharedExpr),
304 (ord_intersect(SortedNames,UsedIds) % if any of the introduced names is used: filter out lazy lookup
305 -> true
306 ; get_texpr_info(SharedExpr,Infos),
307 memberchk(contains_wd_condition,Infos)
308 %,print(wd_filter(LazyLookupID)),nl, translate:print_bexpr(SharedExpr),nl
309 ).
310 % TO DO: also filter Expressions with WD-condition attached
311
312 % merge accumulators from subexpressions:
313 merge_bup_accs([],Res) :- bup_init_acc(Res).
314 merge_bup_accs([H],Res) :- !, % only one accumulator/argument: remove all ids marked for introduction
315 % they can be introduced in the level below
316 avl_delete('$used_lazy_lookups',H,All-_Intro,Acc1),
317 avl_store('$used_lazy_lookups',Acc1,All-[],Acc2), % set introduce field to []
318 Res=Acc2.
319 merge_bup_accs([H|T],Res) :- avl_fetch('$used_lazy_lookups',H,All1-_),
320 avl_store('$used_lazy_lookups',H,All1-[],InitAcc), % set introduction set to empty
321 merge_bup_accs_aux(T,InitAcc,Res).
322 merge_bup_accs_aux([],R,R).
323 merge_bup_accs_aux([H|T],A,R) :- combine_acc(H,A,A2), merge_bup_accs_aux(T,A2,R).
324
325
326 combine_acc(Acc1,Acc2,NewAcc) :-
327 avl_to_list(Acc1,Acc1List),
328 empty_avl(Empty),
329 combine_acc_aux(Acc1List,Acc2,Empty,NewAcc).
330
331 combine_acc_aux([],_Acc2,Res,Res).
332 combine_acc_aux([Field-Value1|T],Acc2,RAcc,Res) :-
333 avl_delete(Field,Acc2,Value2,Acc3),
334 (field_combinator(Field,Value1,Value2,NewValue)
335 -> avl_store(Field,RAcc,NewValue,RAcc2)
336 ; RAcc2=Acc2, Acc3=Acc2),
337 combine_acc_aux(T,Acc3,RAcc2,Res).
338
339 % rules on how to combine the different fields of the accumulators:
340 field_combinator('$identifier_uses',Used1,Used2,Used) :- ord_union(Used1,Used2,Used).
341 field_combinator('$lazy_lookups_directly_used',Used1,Used2,Used) :- ord_union(Used1,Used2,Used).
342 field_combinator('$used_lazy_lookups',All1-_,AccAll-AccIntro,All-Intro) :-
343 ord_union(All1,AccAll,All),
344 ord_intersection(All1,AccAll,I1), % an @ID that is in this branch and some other branch
345 ord_union([I1,AccIntro],Intro).
346
347
348
349
350 % check if we need to introduce a lazy_lookup at the current node
351 bup_insert_lazy_lookup_if_necessary(BExpr,LAZYLOOKUP,Acc,NewAcc) :-
352 BExpr = b(Expr,Type,Info),
353 select(sharing(ID,TotalCount,_,_),Info,Info2), % this expressions is marked as being shared
354 TotalCount > 1, % with at least two occurrences
355 !,
356 %cse_check_ast(avl_store(ID),BExpr), % infos only re-established in TD phase
357 bup_update_info(Info2,Acc,Info3),
358 (lazy_let_to_be_introduced(ID,UsedLL,IntroLL,_,_)
359 -> true /* let already introduced; ensure we use same Ids/version everywhere */
360 ; get_used_lazy_lookups(Acc,UsedLL,IntroLL),
361 avl_fetch('$identifier_uses',Acc,UsedIds),
362 % nl, print(lookup(ID,UsedLL,IntroLL)),nl, translate:print_bexpr_or_subst(BExpr), % portray_avl(Acc),nl,
363 register_let_id(ID,UsedLL,IntroLL,UsedIds,b(Expr,Type,Info3)) % store that we need to introduce a lazy_let
364 ),
365 (UsedLL=[] -> true
366 ; last(UsedLL,LastLL),
367 (ID > LastLL -> true
368 ; format('*** LET ~w depends on later id ~w in ~w~n',[ID,LastLL,UsedLL]))
369 ),
370 ord_union([ID],UsedLL,NewUsedLL),
371 ord_union([ID],IntroLL,NewIntroLL),
372 avl_store('$lazy_lookups_directly_used',Acc,[ID],Acc1),
373 avl_store('$used_lazy_lookups',Acc1,NewUsedLL-NewIntroLL,NewAcc), % to the outside (above) we only see usage of @ID
374 bup_update_info(Info2,NewAcc,LazyLookupInfo),
375 gen_lazy_lookup(Type,ID,Info2,LazyLookupInfo,LAZYLOOKUP).
376 bup_insert_lazy_lookup_if_necessary(b(Expr,Type,Info1),b(Expr,Type,Info2),Acc,Acc) :-
377 %nl, print('BUP: '),translate:print_bexpr_or_subst(b(Expr,Type,Info1)), portray_avl(Acc),nl,
378 bup_update_info(Info1,Acc,Info2).
379
380 bup_update_info(Info0,Acc,Info4) :-
381 delete(Info0,sharing(_,_,_,_),Info1),
382 delete(Info1,cse_used_ids(_),Info2),
383 get_used_lazy_lookups(Acc,_,InterList),
384 avl_fetch('$lazy_lookups_directly_used',Acc,DirectlyUsed),
385 Info3=Info2, %Info3 = [cse_used_ids(DirectlyUsed)|Info2],
386 ord_intersection(InterList,DirectlyUsed,IntroduceHere), % those not directly used will be introduced automatically inside the shared expressions
387 (IntroduceHere=[] -> Info4=Info3
388 %,(DirectlyUsed=[] -> true ; print('*** WARNING: directly used not empty: '),print(DirectlyUsed),nl)
389 ; Info4 = ['$introduce_lazy_lookups'/IntroduceHere|Info3]). % store info in node for later top-down traversal).
390
391 get_used_lazy_lookups(Acc,All,Introduce) :-
392 (avl_fetch('$used_lazy_lookups',Acc,A-I) -> All=A,Introduce=I
393 ; add_internal_error('Failed: ',get_used_lazy_lookups(Acc,All,Introduce)),
394 All=[], Introduce=[]).
395
396
397 % ----------------------------------
398
399 % PHASE 2: Top-Down Phase to introduce LETs at latest possible moment:
400
401 % top-down traversal to introduce lazy_lets for lazy_lookups we have introduced in bottom-up phase
402 top_down_gen_lazy_lets(InExpr,Acc,ResExpr) :-
403 top_down_gen_lazy_lets1(InExpr,Acc,IntExpr),
404 update_info_field(IntExpr,ResExpr). % maybe we should do this only if there was a change
405
406 top_down_gen_lazy_lets1(b(LazyLookup,Type,_Info),Acc,NewExpr1) :-
407 directly_uses_cse_id(LazyLookup,LetID), % check if we have lazy_let_expr/_pred
408 (lazy_let_to_be_introduced(LetID,UsedInside,IntroduceHere,_,SharedExpression) -> true
409 ; avl_fetch('$maximum_unregistered_lazy_let_id',Acc,MaxId), % all ids up until MaxId come from a previous run of CSE
410 LetID > MaxId,
411 add_internal_error('**** Unregistered let: ',LetID),fail),
412 \+ avl_fetch(lazy_let_introduced(LetID),Acc,true),
413 !,
414 debug_println(9,inlining_single_usage(LetID,UsedInside,IntroduceHere)),
415 avl_store(lazy_let_inlined(LetID),Acc,true,Acc0), % NOT USED !
416 top_down_gen_lazy_lets(SharedExpression,Acc0,NewSharedExpression),
417 top_down_gen_lazy_lets_for_list(IntroduceHere,Type,Acc0,_,NewExpr1,NewSharedExpression).
418 top_down_gen_lazy_lets1(b(Expr,Type,Info),Acc,Res) :-
419 %syntaxtraversion(BExpr,Expr,_,_,Subs,_TNames),
420 select('$introduce_lazy_lookups'/IntroduceHere,Info,Info2),
421 % functor(Expr,Fun,Arity),print(intro(IntroduceHere,Fun,Arity)),nl, translate:print_bexpr_or_subst(b(Expr,Type,Info)),nl,
422 !,
423 % the lazy lookups in Inter must now be introduced, if not already done so
424 top_down_gen_lazy_lets_for_list(IntroduceHere,Type,Acc,NewAcc,Res,InnerRes),
425 syntaxtransformation(Expr,Subs,_Names,NSubs,NewExpr1),
426 InnerRes=b(NewExpr1,Type,Info2),
427 td_treat_sub_args(Subs,NewAcc,NSubs).
428 top_down_gen_lazy_lets1(b(Expr,Type,Info),Acc,b(NewExpr1,Type,Info)) :-
429 syntaxtransformation(Expr,Subs,_Names,NSubs,NewExpr1),
430 td_treat_sub_args(Subs,Acc,NSubs).
431
432 % treat sub-arguments of an expression for which we have introduced lets
433 td_treat_sub_args([],_Acc,[]).
434 td_treat_sub_args([TE|T],Acc,[NTE|NT]) :-
435 (top_down_gen_lazy_lets(TE,Acc,NTE)
436 -> true
437 ; add_internal_error('Call failed: ',top_down_gen_lazy_lets(TE,Acc,NTE)),
438 NTE=TE ),
439 td_treat_sub_args(T,Acc,NT).
440
441
442 top_down_gen_lazy_lets_for_list(IntroHere,Type,Acc,NewAcc,Res,InnerRes) :-
443 % first collect all lazy_lets we need here:
444 %td_collect_lazy_lets_to_introduce_here(Inter,Acc,IntroHere,[]),
445 % now update which lazy_lets are now visible
446 topsort(IntroHere,SortedIntroHere),
447 %print(sorted_intro(IntroHere,SortedIntroHere)),nl,
448 td_introduce_lazy_lets(SortedIntroHere,Type,Acc,NewAcc,Res,InnerRes).
449
450 % topological sorting using a naive quadratic algorithm; TO DO: improve
451 topsort([],[]).
452 topsort([H|T],Res) :- select(X,T,TT), let_depends_upon(H,X), !,topsort([X,H|TT],Res).
453 topsort([H|T],[H|ST]) :- topsort(T,ST).
454
455 :- dynamic let_depends_upon/2, lazy_let_to_be_introduced/5.
456 % a fact to store which let depends directly upon which other let
457 % UsedLL: Used LazyLet @IDs, IntroduceLL: LazyLet @IDs to be introduced, UsedIds: regular identifiers referenced directly or via other lazy_lookups
458 register_let_id(ID,UsedLL,IntroduceLL,UsedIds,SharedExpr) :-
459 assertz(lazy_let_to_be_introduced(ID,UsedLL,IntroduceLL,UsedIds,SharedExpr)),
460 %format('LAZY LET ~w (depends on ~w) [uses ~w ids]~n',[ID,UsedLL,UsedIds]),
461 member(ID2,UsedLL),
462 assertz(let_depends_upon(ID,ID2)),
463 % now do transitive closure in one direction
464 %let_depends_upon(ID0,ID),assertz(let_depends_upon(ID0,ID2)),
465 fail.
466 register_let_id(ID,UsedLL,_,_,_) :- fail,
467 % now do transitive closue in other direction
468 member(ID2,UsedLL),
469 let_depends_upon(ID2,ID3),
470 assertz(let_depends_upon(ID,ID3)),fail.
471 register_let_id(_,_,_,_,_).
472
473 reset_let_dependency_info :- retractall(let_depends_upon(_,_)),
474 retractall(lazy_let_to_be_introduced(_,_,_,_,_)).
475
476
477 % introduce lets in order
478 td_introduce_lazy_lets([],_,Acc,Acc,R,R).
479 td_introduce_lazy_lets([],Ty,Acc,AccR,T,RT) :-
480 add_internal_error('Call failed: ',td_introduce_lazy_lets([],Ty,Acc,AccR,T,RT)),fail.
481 td_introduce_lazy_lets([LetID|T],Type,Acc,Acc1,OuterTerm,InnerTerm) :-
482 avl_fetch(lazy_let_introduced(LetID),Acc,true),!,
483 td_introduce_lazy_lets(T,Type,Acc,Acc1,OuterTerm,InnerTerm).
484 td_introduce_lazy_lets([LetID|T],Type,Acc,Acc1,OuterTerm,InnerTerm) :-
485 avl_store(lazy_let_introduced(LetID),Acc,true,Acc0),
486 td_introduce_lazy_lets(T,Type,Acc0,Acc1,MidTerm,InnerTerm),
487 % now introduce let for ID
488 lazy_let_to_be_introduced(LetID,_,_,UsedIds,SharedExpression),
489 top_down_gen_lazy_lets(SharedExpression,Acc0,NewSharedExpression), % the lazy_lets introduced inside are not visible
490 %tools_printing:print_term_summary(gen_lazy_let(Type,LetID,NewSharedExpression,MidTerm,OuterTerm)),nl,
491 gen_lazy_let(Type,LetID,UsedIds,NewSharedExpression,MidTerm,OuterTerm).
492
493
494
495 % -----------------------------------------------
496
497
498 % copy position information; important e.g. for discharged checks for theorems and invariants
499 /*
500 copy_position_info(From,b(To,Type,Info1),b(To,Type,Info3)) :-
501 get_texpr_pos(From,PositionInfo),!,
502 delete(Info1,nodeid(_),Info2),
503 Info3 = [nodeid(PositionInfo)|Info2].
504 copy_position_info(_,E,E).
505 */
506
507 :- use_module(probsrc(b_ast_cleanup),[recompute_used_ids_info/2]).
508 % update info field of a typed expression after a modification of the expression
509 update_info_field(TE,NewTE) :-
510 update_wd_condition(TE,TE2),
511 recompute_used_ids_info(TE2,NewTE). % inserting lazy_lookups changes used ids for exists/forall
512
513 % check if we need to add contains_wd_condition info field
514 update_wd_condition(b(E,Type,Info1),Res) :-
515 nonmember(contains_wd_condition,Info1), % inlined update_wd_condition_info to just copy E in clause below; not sure this helps memory performance
516 sub_expression_contains_wd_condition(E),
517 !,
518 Res = b(E,Type,[contains_wd_condition|Info1]).
519 update_wd_condition(E,E).
520
521 update_wd_condition_info(E,Info1,Info2) :-
522 nonmember(contains_wd_condition,Info1),
523 sub_expression_contains_wd_condition(E),
524 !,
525 Info2 = [contains_wd_condition|Info1].
526 update_wd_condition_info(_,I,I).
527
528
529
530 % Remove sharing/4 info from SharedExpression + avoid reusing it for CSE if algorithm applied second time
531 %delete_sharing_info(b(E,T,Info),b(E,T,NewInfo)) :- delete(Info,sharing(_,_,_,_),NewInfo).
532
533 :- public cse_cleanup_infos/2.
534 % Remove info that is no longer necessary once CSE has run to completion:
535 cse_cleanup_infos(b(E,T,Info),Res) :-
536 cse_cleanup_infos1(E,T,Info,Res).
537
538 cse_cleanup_infos1(lazy_let_pred(ID,SharedExpr,Body),T,_,Res) :-
539 collect_important_info(SharedExpr,Body,Info),
540 cse_cleanup_aux(lazy_let_pred(ID,SharedExpr,Body),T,Info,Res).
541 cse_cleanup_infos1(lazy_let_expr(ID,SharedExpr,Body),T,_,Res) :-
542 collect_important_info(SharedExpr,Body,Info),
543 cse_cleanup_aux(lazy_let_expr(ID,SharedExpr,Body),T,Info,Res).
544 cse_cleanup_infos1(lazy_let_subst(ID,SharedExpr,Body),T,_,Res) :-
545 collect_important_info(SharedExpr,Body,Info),
546 cse_cleanup_aux(lazy_let_subst(ID,SharedExpr,Body),T,Info,Res).
547 cse_cleanup_infos1(sequence(Seq),T,Info,Res) :- !, % re-flatten sequences that were binarized for CSE sharing analysis
548 flatten_sequence(Seq,FSeq,WD),
549 (WD==contains_wd_condition, nonmember(contains_wd_condition,Info) ->
550 Info1 = [contains_wd_condition|Info] ; Info1=Info),
551 cse_cleanup_aux(sequence(FSeq),T,Info1,Res).
552 cse_cleanup_infos1(E,T,Info,Res) :- cse_cleanup_aux(E,T,Info,Res).
553
554 flatten_sequence([H,b(sequence(T),subst,Info)],[H|FT],WD) :- member(cse_generated,Info),
555 !,
556 % check if this sequence contains a WD condition
557 (var(WD),member(contains_wd_condition,Info) -> WD=contains_wd_condition ; true),
558 flatten_sequence(T,FT,WD).
559 flatten_sequence(X,X,_WD).
560
561
562 cse_cleanup_aux(E,T,Info,b(NewE,T,NewInfo)) :-
563 delete(Info,cse_used_ids(_),Info2),
564 ( nonmember(contains_wd_condition,Info2), % TO DO: allow in DISPROVER_MODE
565 post_cse_simplifier(E,SE) -> NewE = SE, NewInfo=Info2 % Note: maybe some lets no longer necessary now
566 ; reorder_lazy_let_expr(E,T,Info2,NewE,NewInfo) -> true
567 ; NewE=E,NewInfo=Info2).
568
569 % reorder/move lazy lets outside if this leads to better optimizations being applicable
570 reorder_lazy_let_expr(Expr,Type,Info,NewExpr,NewInfo) :-
571 reorder_argument(Expr,FunctorID,Match,TArgToReorder,OtherArgs,_),
572 get_texpr_expr(TArgToReorder,ArgToReorder),
573 is_lazy_let(ArgToReorder,LLType,ID,ShExpr,BodyA),
574 get_texpr_expr(BodyA,Match),
575 debug_println(19,reorder_lazy_let_expr(ID,FunctorID)),
576 !,
577 reorder_argument(NewBinExpr,FunctorID,_,BodyA,OtherArgs,Recur),
578 !, % FunctorID ensures only one possible solution for reorder_argument
579 ( Recur=recur, % we may have to re-order again
580 reorder_lazy_let_expr(NewBinExpr,Type,Info,NewBinExpr2,Info2)
581 -> NewInnerExpr = b(NewBinExpr2,Type,Info2)
582 ; NewInnerExpr = b(NewBinExpr,Type,Info)),
583 is_lazy_let(NewExpr,LLType,ID,ShExpr,NewInnerExpr), % TYPE MAY HAVE TO CHANGE for CASE ?? TO DO: Check with appropriate B Machine
584 % copy important info from InfoA ??
585 update_wd_condition_info(NewExpr,Info,NewInfo).
586 % TO DO: add more cases + what if both arguments need reordering --> fixpoint computation ?
587
588 is_lazy_let(lazy_let_expr(ID,ShExpr,BodyA),lazy_let_expr,ID,ShExpr,BodyA).
589 is_lazy_let(lazy_let_subst(ID,ShExpr,BodyA),lazy_let_subst,ID,ShExpr,BodyA).
590 is_lazy_let(lazy_let_pred(ID,ShExpr,BodyA),lazy_let_pred,ID,ShExpr,BodyA).
591
592 % reorder argument 1 (i.e., move lazy_let out) in case we match a certain pattern
593 % reorder_argument(Expression, ID, MATCH, ARG_TO_REORDER, OtherArgs)
594 reorder_argument(function(A,B),function_1,set_extension(_),A,B,do_not_recur). % there is optimized code: b_apply_function_set_extension
595 reorder_argument(max(A),max,set_extension(_),A,nil,do_not_recur). % we have optimized code for min({a,b,...}) and max
596 reorder_argument(min(A),min,set_extension(_),A,nil,do_not_recur).
597 reorder_argument(card(A),card,set_extension(_),A,nil,do_not_recur).
598 reorder_argument(image(A,B),image_1,closure(_),A,B,recur).
599 reorder_argument(image(B,A),image_2,set_extension(_),A,B,recur).
600 % avoid that Lazy Lets stand at illegal places
601 % avoid IF THEN LET(... ELSIF P THEN ....) if_elsif term
602 reorder_argument(if(IfList),if1,_,A,Rest,recur) :- select(A,IfList,Rest).
603 % avoid lazy let in place of a CASE case_or term
604 reorder_argument(case(Expr,Cases,Else),case_element,_,A,[Expr,Else|Rest],recur) :- select(A,Cases,Rest).
605 % avoid lazy let for a select_when term:
606 reorder_argument(select(SelWhenList),select,_,A,Rest,recur) :- select(A,SelWhenList,Rest). % SelWhenList: a list containing select_when/2 terms
607 reorder_argument(select(SelWhenList,Else),select_else,_,A,(Rest,Else),recur) :- select(A,SelWhenList,Rest).
608 % CSE will sometimes insert a lazy_let_subst where an if_elsif should stand; this not supported by the interpreter
609 %reorder_argument(while(COND,TSubst,INV,VARIANT),while_subst,_,TSubst,[COND,INV,VARIANT]). % TO DO: check if lazy_let does not depend on variables modified in loop
610
611 % TO DO: bring this in accordance with do_not_share_this rules:
612
613 % detect certain obvious inconsistencies / tautologies introduced by CSE
614 post_cse_simplifier(equal(A,B),truth) :- same_lazy_lookup(A,B).
615 post_cse_simplifier(not_equal(A,B),falsity) :- same_lazy_lookup(A,B).
616 post_cse_simplifier(greater(A,B),falsity) :- same_lazy_lookup(A,B).
617 post_cse_simplifier(greater_equal(A,B),truth) :- same_lazy_lookup(A,B).
618 post_cse_simplifier(less(A,B),falsity) :- same_lazy_lookup(A,B).
619 post_cse_simplifier(less_real(A,B),falsity) :- same_lazy_lookup(A,B).
620 post_cse_simplifier(less_equal(A,B),truth) :- same_lazy_lookup(A,B).
621 post_cse_simplifier(less_equal_real(A,B),truth) :- same_lazy_lookup(A,B).
622 post_cse_simplifier(subset(A,B),truth) :- same_lazy_lookup(A,B).
623 post_cse_simplifier(not_subset(A,B),falsity) :- same_lazy_lookup(A,B).
624 post_cse_simplifier(subset_strict(A,B),falsity) :- same_lazy_lookup(A,B).
625 post_cse_simplifier(not_subset_strict(A,B),truth) :- same_lazy_lookup(A,B).
626
627 /*
628 TO DO:
629 split up lazy_let_pred; as previously done in gen_lazy_let
630 gen_lazy_let(pred,CSEID,_UsedIds,SharedExpr,Body,Result) :-
631 true, % TO DO <------------- RENABLE
632 % check if we can split off Prefix and/or Suffix of Body not using CSEID
633 cse_split_conjunct(Body, CSEID, Prefix, MainBody, Suffix),
634 !,
635 print(optimizing_let_with_conjunction_body(CSEID)),nl,
636 gen_id(CSEID,SharedExpr,ID),
637 cse_conjunct_predicates(MainBody,NewBody),
638 %get_texpr_info(NewBody,NewInfo),
639 collect_important_info(SharedExpr,NewBody,NewInfo), % we could remove ID from cse_used_ids
640 LET = b(lazy_let_pred(ID,SharedExpr,NewBody),pred,NewInfo),
641 append(Prefix,[LET|Suffix],ConjunctionList),
642 cse_conjunct_predicates(ConjunctionList,Result).
643 */
644
645 same_lazy_lookup(A,B) :- get_lazy_lookup_id(A,ID), get_lazy_lookup_id(B,ID).
646
647 get_lazy_lookup_id(b(E,Type,_),Type,ID) :- get_lazy_lookup_id_aux(E,ID).
648 get_lazy_lookup_id(b(E,_,_),ID) :- get_lazy_lookup_id_aux(E,ID).
649 get_lazy_lookup_id_aux(lazy_lookup_expr(ID),ID).
650 get_lazy_lookup_id_aux(lazy_lookup_pred(ID),ID).
651 % there is no lazy_lookup_subst(ID)
652
653 %gen_lazy_let_with_check(Type, Identifier, UsedIds,SharedExpression, SubExpressionInsideLet, Result) :-
654 % cse_check_ast(before_gen_lazy_let_shared,SharedExpression),
655 % cse_check_ast(before_gen_lazy_let_body,SubExpressionInsideLet),
656 % (gen_lazy_let(Type, Identifier, UsedIds,SharedExpression, SubExpressionInsideLet, Result)
657 % -> cse_check_ast(gen_lazy_let,Result)
658 % ; add_internal_error('Call failed: ',gen_lazy_let_with_check(Type, Identifier, UsedIds,SharedExpression, SubExpressionInsideLet, Result)),
659 % fail).
660
661 % TO DO: maybe check that we are not in a bad context to prevent optimized treatment (e.g. {1|->a,2|->b}(x) ), rather than delegating this to reorder lazy_let code above ?
662 % gen_lazy_let(Type, Identifier, UsedIds,SharedExpression, SubExpressionInsideLet, Result)
663 gen_lazy_let(pred,A,_UsedIds,SharedExpr,Body,b(R,pred,[])) :-
664 % Info field will be set later in cse_cleanup_infos
665 !,
666 gen_id(A,SharedExpr,ID), debug_println(4,generating_lazy_let_pred(A,ID)),
667 R = lazy_let_pred(ID,SharedExpr,Body).
668 gen_lazy_let(subst,A,_UsedIds,SharedExpr,Body,b(R,subst,[])) :-
669 !,
670 gen_id(A,SharedExpr,ID), debug_println(4,generating_lazy_let_subst(A,ID)),
671 R = lazy_let_subst(ID,SharedExpr,Body).
672 gen_lazy_let(Type,A,_UsedIds,SharedExpr,C,b(lazy_let_expr(ID,SharedExpr,C),Type,[])) :-
673 gen_id(A,SharedExpr,ID).
674
675 % ----------------------
676
677
678 collect_important_info(SubA,SubB,Info) :- (var(SubA) ; var(SubB)), !,
679 add_internal_error('Illegal call: ',collect_important_info(SubA,SubB,Info)),fail.
680 collect_important_info(SubA,SubB,Info) :-
681 extract_info(SubA,SubB,Info1), % WD info (bsyntaxtree utility)
682 % was collect_uses_cse_id([SubA,SubB],IDS), % CSE usage info
683 exclude(do_not_copy_info_from_sub_terms,Info1,Info2), % is this necessary ??
684 Info=Info2. %Info = [cse_used_ids(IDS)|Info2].
685
686 % ----------------------
687
688 gen_lazy_lookup(pred,A,Infos,LazyLookupInfo,R) :- !, convert_to_atom(A,ID),
689 (member(negated_cse,Infos)
690 -> R = b(negation(b(lazy_lookup_pred(ID),pred,LazyLookupInfo)),pred,OuterInfos),
691 delete(LazyLookupInfo,'$introduce_lazy_lookups'/_,OuterInfos) % we do not need to generate the lazy_lookups here; they can be created inside the negation
692 %,print(create_neg_lookup(ID,LazyLookupInfo,OuterInfos)),nl
693 ; R = b(lazy_lookup_pred(ID),pred,LazyLookupInfo)).
694 gen_lazy_lookup(Type,A,_,LazyLookupInfo,
695 b(lazy_lookup_expr(ID),Type,LazyLookupInfo)) :- convert_to_atom(A,ID).
696
697 % generate atoms: many identifier treating predicates expect atoms
698 gen_id(A,B,b(identifier(ID),Type,[])) :- get_texpr_type(B,Type),convert_to_atom(A,ID).
699
700 convert_to_atom(A,ID) :- var(A),!, add_internal_error('Illegal call: ', convert_to_atom(A,ID)),fail.
701 convert_to_atom(A,ID) :- atom(A),!, ID=A.
702 convert_to_atom(Nr,ID) :- number_codes(Nr,C), atom_codes(ID,[64|C]). % 64 = @
703
704 convert_atom_to_nr(Atom,Nr) :- var(Atom),!, add_internal_error('Illegal call: ',convert_atom_to_nr(Atom,Nr)),fail.
705 convert_atom_to_nr(Atom,Nr) :- atom_codes(Atom,[64|C]), number_codes(Nr,C).
706
707 % check if an identifier is (potentially) a lazy_let ID starting with @
708 is_lazy_let_identifier(Atom) :- atom(Atom), atom_codes(Atom,[64|_]).
709
710
711 /*
712 collect_uses_cse_id([],[]).
713 collect_uses_cse_id([H|T],Res) :-
714 get_cse_used_ids_info(H,CSE),!,
715 collect_uses_cse_id(T,CSET),
716 ord_union(CSE,CSET,Res).
717
718 uses_cse_id(ID,Expr) :- get_cse_used_ids_info(Expr,List),!,
719 member(ID,List).
720 uses_cse_id(_ID,Expr) :- print('Could not extract cse_used_ids info: '), print(Expr),nl,
721 true.
722
723 % CSE Identifier usage infor either form info field or from direct usage by lazy_lookups
724 get_cse_used_ids_info(TExpr,IDList) :- get_texpr_expr(TExpr,E),
725 directly_uses_cse_id(E,UsedID),!,
726 IDList = [UsedID].
727 get_cse_used_ids_info(TExpr,IDList) :- get_texpr_info(TExpr,Info),
728 member(cse_used_ids(IDList),Info),!.
729 get_cse_used_ids_info(TE,L) :- print('Could not get cse_used_ids info: '), print(TE),
730 L=[].
731 */
732
733 directly_uses_cse_id(E,ID) :- var(E),!, add_internal_error('Illegal call: ',directly_uses_cse_id(E,ID)),fail.
734 directly_uses_cse_id(lazy_lookup_pred(AtomID),ID) :- convert_atom_to_nr(AtomID,ID).
735 directly_uses_cse_id(lazy_lookup_expr(AtomID),ID) :- convert_atom_to_nr(AtomID,ID).
736 % there is no lazy_lookup_subst
737
738 :- public print_cse_id_used/1.
739 print_cse_id_used(TypedExpr) :- get_texpr_info(TypedExpr,Info),
740 member(cse_used_ids(L),Info),!,
741 print(L).
742 print_cse_id_used(_) :- print(' no cse used id info ').
743
744 :- public replace_lazy_lookup/4.
745 :- use_module(probsrc(btypechecker), [unify_types_strict/2]).
746 % TO DO: maybe remove cse_used_ids ??
747 %replace_lazy_lookup(_,_,T,_) :- print(T),nl,fail.
748 replace_lazy_lookup(ID,SharedExpr,BExpr,SharedExpr) :-
749 get_lazy_lookup_id(BExpr,Type,TID),
750 !,
751 TID=ID,
752 get_texpr_type(SharedExpr,SharedType),
753 (my_unify_types(SharedType,Type) -> true
754 ; add_internal_error('Incompatible type: ',replace_lazy_lookup(ID,'\\='(SharedType,Type),BExpr,SharedExpr))).
755 replace_lazy_lookup(_,_,E,Res) :- update_info_field(E,Res). % probably not necessary ?!
756
757 :- if(environ(prob_safe_mode,true)).
758 my_unify_types(T1,T2) :- (\+ ground(T1) ; \+ ground(T2)),
759 format('*** Non-ground type unification: ~w = ~w~n~n',[T1,T2]),
760 fail.
761 :- endif.
762 my_unify_types(T1,T2) :- unify_types_strict(T1,T2).
763
764 % -----------------
765
766
767 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
768 % common subexpression detection
769
770 :- assert_must_succeed(
771 ( find_common_subexpressions(b(identifier(i),integer,[]),0,false,R),
772 R == b(identifier(i),integer,[sharing(0,1,top,[]),cse_used_ids([])])
773 )).
774 :- assert_must_succeed(
775 ( find_common_subexpressions(b(identifier(i),integer,[]),0,true,R),
776 R == b(identifier(i),integer,[cse_used_ids([])])
777 )).
778 :- assert_must_succeed(
779 ( I = b(identifier(i),integer,[]),
780 O = b(identifier(i),integer,[sharing(0,2,top,[]),cse_used_ids([])]),
781 find_common_subexpressions(b(add(I,I),integer,[]),0,false,R),
782 R == b(add(O,O),integer,[sharing(1,1,top,[]),cse_used_ids([0])])
783 )).
784 :- assert_must_succeed(
785 ( I = b(identifier(i),integer,[]),
786 O = b(identifier(i),integer,[cse_used_ids([])]),
787 find_common_subexpressions(b(add(I,I),integer,[]),0,true,R),
788 R == b(add(O,O),integer,[sharing(0,1,top,[]),cse_used_ids([])])
789 )).
790 % two quantifiers with the same variable name must not share
791 % expressions that contain the variable, other expressions should be
792 % shared
793 :- assert_must_succeed(
794 ( C = b(identifier(c),integer,[]),
795 X = b(identifier(x),integer,[]),
796 I = b(identifier(i),set(integer),[]),
797 O = b(integer(1),integer,[]),
798 % !x.(x:I => x<c+1)
799 F = b(forall([X],
800 b(member(X,I),pred,[]),
801 b(less(X,b(add(C,O),integer,[])),pred,[])),pred,[]),
802 % !x.(x:I => x>c+1)
803 Fx = b(forall([X],
804 b(member(X,I),pred,[]),
805 b(greater(X,b(add(C,O),integer,[])),pred,[])),pred,[]),
806 C1 = b(identifier(c),integer,[sharing(3,2,top,[]),cse_used_ids([])]),
807 X1 = b(identifier(x),integer,[sharing(0,3,7,[]),cse_used_ids([])]),
808 I1 = b(identifier(i),set(integer),[sharing(1,2,top,[]),cse_used_ids([])]),
809 O1 = b(integer(1),integer,[sharing(4,2,top,[]),cse_used_ids([])]),
810 F1 = b(forall([X1],
811 b(member(X1,I1),pred,[sharing(2,1,7,[]),cse_used_ids([0,1])]),
812 b(less(X1,b(add(C1,O1),integer,[sharing(5,2,top,[]),cse_used_ids([3,4])])),
813 pred,[sharing(6,1,7,[]),cse_used_ids([0,3,4,5])])),
814 pred,[sharing(7,1,top,[0]),cse_used_ids([0,1,2,3,4,5,6])]),
815 % e.g. c+1 should be shared (ref. 5)
816 % x and x:I must not be shared, they get a new reference (8 and 9)
817 X2 = b(identifier(x),integer,[sharing(8,3,11,[]),cse_used_ids([])]),
818 F2 = b(forall([X2],
819 b(member(X2,I1),pred,[sharing(9,1,11,[]),cse_used_ids([1,8])]),
820 b(greater(X2,b(add(C1,O1),integer,[sharing(5,2,top,[]),cse_used_ids([3,4])])),
821 pred,[sharing(10,1,11,[]),cse_used_ids([3,4,5,8])])),
822 pred,[sharing(11,1,top,[8]),cse_used_ids([1,3,4,5,8,9,10])]),
823 find_common_subexpressions(b(conjunct(F,Fx),pred,[]),0,false,R),
824 R == b(conjunct(F1,F2),pred,[sharing(12,1,top,[]),cse_used_ids([0,1,2,3,4,5,6,7,8,9,10,11])])
825 )).
826
827 % find_common_subexpressions/4 searches for common subexpressions and adds an
828 % information field sharing/4 to each AST node.
829 % find_common_subexpressions(+TExpr,+Start,+SkipSimple,-NTExpr):
830 % TExpr: the original AST
831 % Start: at which number the IDs should start (usually 0)
832 % NTExpr: the AST, each node annotated with an sharing(ID,Counter,SubId,References) information field
833 % two syntax nodes share the same ID when they should yield always the same
834 % value. Counter counts the number of occurrences of the same expression in
835 % the given AST. SubId is the unique reference of the quantified expression
836 % where a used ID is introduced, top if there is no such quantifier.
837 % References is a list of ids that are are sub-expressions of the node which
838 % occur more than once and which depend on the newly introduced identifiers
839 % (Please note: The declaration of a variable in a quantified expression
840 % is an occurrence of its own.)
841 % SkipSimple: set to true if expression sharing should not be applied to simple
842 % expressions like identifier(_), integer(_), etc.
843 find_common_subexpressions(TExpr,Start,SkipSimple,NTExpr) :-
844 find_common_subexpressions(TExpr,Start,SkipSimple,NTExpr,_).
845 find_common_subexpressions(TExpr,Start,SkipSimple,NTExpr,References) :-
846 create_stores(Start,EmptyStores),
847 find_common_subexpressions2(TExpr,EmptyStores,SkipSimple,root/1,NTExpr,Stores,_Stripped,_UsedIds,_UsedCSEIds),
848 ground_reference_counters(Stores),
849 get_current_references(Stores,References). % needed ??
850
851 :- if(environ(prob_safe_mode,true)).
852 add_cse_used_ids_info(Ids,OldInfos,NewInfos) :- (var(Ids) ; \+ ground(OldInfos)),!,
853 add_internal_error('Illegal Call: ',add_cse_used_ids_info(Ids,OldInfos,NewInfos)),
854 NewInfos = OldInfos.
855 :- endif.
856 add_cse_used_ids_info(Ids,OldInfos,NewInfos) :-
857 exclude(do_not_copy_info_from_sub_terms,OldInfos,OldInfos1),
858 NewInfos=[cse_used_ids(Ids)|OldInfos1].
859
860 do_not_copy_info_from_sub_terms(cse_used_ids(_)). % is_cse_used_ids
861 do_not_copy_info_from_sub_terms(negated_cse).
862
863 % find_common_subexpressions2(+TExpr,+InStores,+SkipSimple,+OuterFunctor,-NTExpr,-OutStores,-Stripped,-UsedIds,-UsedCSEIds):
864 find_common_subexpressions2(TExpr,InStores,SkipSimple,OuterFunctor,NTExpr,OutStores,Stripped,UsedIds,UsedCSEIds) :-
865 % decompose the typed expression into its components (Subs), look up the newly introduced
866 % identifiers (NewIds) and create a new typed expression (NTExpr) with the modified components
867 % (NSubs) and the new Info field (Info)
868 fc_parts(TExpr,Expr,Type,Subs,NewQuantifiedIds,InvalidatedIds,NSubs,OldInfos,NewInfos,NTExpr),
869 % introduce a map from expression to reference that is only visible to this node
870 % and it subnodes if new identifiers (NewIds) are introduced
871 % OutStores1 and OutStores contain a pattern such that if OutStores1 is set,
872 % OutStores is exactly like OutStores1 but without visibility of the newly introduced
873 % identifiers.
874 append(InvalidatedIds,NewQuantifiedIds,NewIds),
875 create_substore(NewIds,ID,InStores,SubStores),
876 % apply subexpression detection on the subexpressions
877 functor(Expr,OuterExprFunctor,_), % memorise functor for sub-expressions (allows deciding if sharing makes sense)
878 find_common_subexpressions_l(Subs,SubStores,SkipSimple,OuterExprFunctor,1,
879 NSubs,SubStores1,StrippedSubs,UsedSubIds,SubUsedCSEIds),
880 % create a stripped (i.e. no info field, no types) version of the syntax node for look ups
881 (Type==subst -> Stripped=not_relevant % substitions are never shared (and everything containing a subst is a subst itself)
882 ; create_stripped(Expr,StrippedSubs,Stripped)),
883 LookupKey = cse_lookup_key(Stripped,Type), % we also store the type along with the stripped AST (avoid sharing, e.g., two prj1/2 functions with different types, see test 255 !)
884 % merge the used ids of the subnodes with the used ids in this node,
885 % subtract newly introduced quantified identifiers because they are not visible from outside
886 update_used_ids(Expr,NewQuantifiedIds,UsedSubIds,UsedIds),
887 % if a substore was introduced due to newly introduced identifiers, remove it
888 %%(OuterExprFunctor=while -> portray_stores(SubStores1) ; true),
889 remove_last_substore(NewIds,SubStores1,Stores1,References),
890 % try to find the expression in the stores
891 (do_not_share_this(SkipSimple,Stripped,UsedIds,OuterFunctor,Type,OldInfos) ->
892 OutStores = Stores1,
893 add_cse_used_ids_info(SubUsedCSEIds,OldInfos,NewInfos),
894 UsedCSEIds = SubUsedCSEIds,
895 store_expression(LookupKey,UsedIds,Stores1,_,_,ID) /* just ground ID; used for Sub expressions */
896 %, print(not_sharing(ID))
897 ; lookup_cse(Type,LookupKey,UsedIds,Stores1,OutStores,SharingInfo,Negated,ID) ->
898 /* it is shared */
899 % print(looked_up(ID,Negated,used(UsedIds,sub(UsedSubIds)),Stores1)),nl,
900 (Negated=negated_cse
901 -> NewInfos=[Negated,SharingInfo|OldInfos1]
902 ; NewInfos=[SharingInfo|OldInfos1]),
903 add_cse_used_ids_info(SubUsedCSEIds,OldInfos,OldInfos1),
904 ord_union([ID],SubUsedCSEIds,UsedCSEIds),
905 set_references(References,SharingInfo)
906 % In this case we do not need to look for sharing in the sub-expressions !!
907 %,check_sharing_info(lookup,SharingInfo)
908 ;
909 % if not found, store the expression: it will be shared if we use it again later
910 store_expression(LookupKey,UsedIds,Stores1,OutStores,SharingInfo,ID),
911 ord_union([ID],SubUsedCSEIds,UsedCSEIds),
912 % print(new_id(ID,UsedIds,SubUsedCSEIds,OutStores)),nl,
913 % TO DO: what if later we find out that it is shared after all -> we should remove sub-expression counts
914 add_cse_used_ids_info(SubUsedCSEIds,OldInfos,OldInfos1),
915 NewInfos=[SharingInfo|OldInfos1],
916 set_references(References,SharingInfo)
917 %,check_sharing_info(new,SharingInfo)
918 ). % print(done_find(NTExpr,OutStores,UsedIds,UsedCSEIds)),nl.
919
920 lookup_cse(Type,LookupKey,UsedIds,Stores1,OutStores,SharingInfo,Negated,ID) :-
921 (lookup_expression(LookupKey,UsedIds,Stores1,OutStores,SharingInfo,ID)
922 -> Negated = normal_cse
923 ; Type=pred, negate_lookup_key(LookupKey,NegKey), % try looking up negation
924 lookup_expression(NegKey,UsedIds,Stores1,OutStores,SharingInfo,ID),
925 Negated = negated_cse).
926
927 negate_lookup_key(cse_lookup_key(P,pred),cse_lookup_key(NP,pred)) :-
928 negate_norm_strip(P,NP).
929
930 %check_sharing_info(_,sharing(Id,_RefCounter,Level,Refs)) :- number(Id), ground(Level), ground(Refs),!.
931 %check_sharing_info(PP,SI) :- print(illegal_sharing_info(PP,SI)),nl.
932
933 % NewIds = introduced IDs or modified Ids
934 fc_parts(TExpr,Expr,Type,Subs,SQuantifiedNewIds,InvalidatedIds,NSubs,OldInfos2,NewInfos,NTExpr) :-
935 my_decompose_texpr(TExpr,Expr,Type,OldInfos),
936 cse_syntaxtransformation(Expr,Subs,Names,NSubs,NExpr),
937 get_texpr_ids(Names,QuantifiedNewIds), list_to_ord_set(QuantifiedNewIds,SQuantifiedNewIds),
938 (select(invalidate_ids_for_cse(ModifiedIDs),OldInfos,OldInfos2)
939 % we have a stored invalidation info for CSE
940 -> InvalidatedIds=ModifiedIDs
941 ; InvalidatedIds= [],OldInfos=OldInfos2),
942 %functor(Expr,F,A), format('-> ~w/~w : ~w + ~w~n',[F,A,SQuantifiedNewIds,InvalidatedIds]),
943 create_texpr(NExpr,Type,NewInfos,NTExpr).
944 % TO DO: re-flatten the sequential compositions later
945
946 % CSE traversal which ignores certain sub-arguments (mainly LHS of assignments)
947 cse_syntaxtransformation(assign(LHS,RHS),RHS,[],NRHS,NExpr) :- !, % print(assign(LHS)),nl,
948 same_length(RHS,NRHS),
949 NExpr=assign(LHS,NRHS).
950 cse_syntaxtransformation(becomes_element_of(LHS,RHS),[RHS],[],[NRHS],NExpr) :- !,
951 NExpr=becomes_element_of(LHS,NRHS).
952 cse_syntaxtransformation(becomes_such(LHS,RHS),[RHS],[],[NRHS],NExpr) :- !,
953 NExpr=becomes_such(LHS,NRHS).
954 cse_syntaxtransformation(evb2_becomes_such(LHS,RHS),[RHS],[],[NRHS],NExpr) :- !,
955 NExpr=evb2_becomes_such(LHS,NRHS).
956 cse_syntaxtransformation(assign_single_id(Id,RHS),[RHS],[],[NRHS],NExpr) :- !,
957 NExpr=assign_single_id(Id,NRHS).
958 cse_syntaxtransformation(Expr,Subs,Names,NSubs,NExpr) :- syntaxtransformation(Expr,Subs,Names,NSubs,NExpr).
959 % TO DO: rlevent(Id,Section,Status,Parameters,Guard,Theorems,Actions,VWitnesses,PWitnesses,_Unmod,Refines)
960
961 :- use_module(probsrc(b_read_write_info), [get_accessed_vars/4]).
962 my_decompose_texpr(b(Expr,Type,Infos),Expr2,Type,Infos2) :- my_decompose2(Expr,Expr2,ModifiedIDs),
963 add_invalidated_ids_to_infos(Infos,ModifiedIDs,Infos2).
964 % decompose sequence into left and right part and store invalidated parts on the right
965 my_decompose2(sequence([TSubst|T]),Res,[]) :- !,
966 Res=sequence([TSubst|NT]),
967 get_accessed_vars(TSubst,[],ModifiedIDs,_),
968 debug_println(19,modified_sequence(ModifiedIDs)),
969 construct_seq_tail(T,ModifiedIDs,NT).
970 % annotate parts of while loop with variables which are invalidated by loop iteration
971 % note the While loop itself also invalidates the same variables, ensures that a substore
972 % is already set-up when entering the sub-nodes; TO DO: examine whether we can simplify this by improving the way shared expressions are stored above
973 my_decompose2(while(COND,TSubst,INV,VARIANT),while(COND2,TSubst2,INV2,VARIANT2),ModifiedIDs) :-
974 get_accessed_vars(TSubst,[],ModifiedIDs,_),
975 debug_println(19,modified_while(ModifiedIDs)),
976 add_invalidated_ids(TSubst,ModifiedIDs,TSubst2),
977 add_invalidated_ids(COND,ModifiedIDs,COND2),
978 add_invalidated_ids(INV,ModifiedIDs,INV2),
979 add_invalidated_ids(VARIANT,ModifiedIDs,VARIANT2).
980 my_decompose2(X,X,[]).
981
982 add_invalidated_ids_to_infos(L,[],R) :- !, R=L.
983 add_invalidated_ids_to_infos(L,Ids,[invalidate_ids_for_cse(Ids)|L]).
984
985 add_invalidated_ids(b(E,T,I),Ids,b(E,T,[invalidate_ids_for_cse(Ids)|I])).
986
987 construct_seq_tail([],_,[]) :- !.
988 %construct_seq_tail([X],[X]) :- !.
989 construct_seq_tail([H|T],ModifiedIDs,[b(sequence([H|T]),subst,[cse_generated,invalidate_ids_for_cse(ModifiedIDs)])]). % store that modified vars are invalid for CSE in tail
990
991
992
993 find_common_subexpressions_l([],Stores,_SkipSimple,_OuterFunctor,_,[],Stores,[],[],[]).
994 find_common_subexpressions_l([Expr|Erest],InStores,SkipSimple,OuterFunctor,ArgNr,
995 [NExpr|Nrest],OutStores,[Stripped|Srest],UsedIds,UsedCSEIds) :-
996 find_common_subexpressions2(Expr,InStores,SkipSimple,outer(OuterFunctor,ArgNr),NExpr,InterStores,Stripped,UsedIds1,UsedCSEIds1),
997 A1 is ArgNr+1,
998 find_common_subexpressions_l(Erest,InterStores,SkipSimple,OuterFunctor,A1,Nrest,OutStores,Srest,UsedIds2,UsedCSEIds2),
999 ord_union(UsedIds1,UsedIds2,UsedIds), % the B identifiers used
1000 ord_union(UsedCSEIds1,UsedCSEIds2,UsedCSEIds). % the identifiers to CSE used
1001
1002 boolean(boolean_true).
1003 boolean(boolean_false).
1004
1005
1006 do_not_share_this(_,_,_,_,Type,_) :- \+ ground(Type),!. % it could be that the Type is ground, but the sub-expressions contain non-ground types; see test 1090 size(arr)
1007 % nl,print(not_sharing_non_ground_type(Type)),nl,nl.
1008 % TO DO: can we maybe still share non-ground type terms if we are careful ? see also tests 402, 403 ?
1009 do_not_share_this(_,_,_,_,Type,_) :- do_not_share_type(Type),!.
1010 do_not_share_this(_,_Stripped,_,_Functor,_Type,Infos) :-
1011 memberchk(contains_wd_condition,Infos),
1012 get_preference(use_common_subexpression_wd_only,true),
1013 !. % also examine DISPROVER MODE ?
1014 do_not_share_this(_,_Stripped,_,_Functor,Type,_Infos) :- Type==pred,
1015 get_preference(use_common_subexpression_also_for_predicates,false),
1016 !.
1017 do_not_share_this(SkipSimple,Stripped,UsedIds,OuterFunctor,_Type,_Infos) :-
1018 SkipSimple==true,
1019 do_not_share_this_3(Stripped,UsedIds,OuterFunctor).
1020
1021 do_not_share_type(subst).
1022 do_not_share_type(status). % Event-B Event Status
1023
1024 :- use_module(probsrc(external_functions),[not_declarative/1]).
1025
1026 %do_not_share_this_3(card(identifier(DeferredSet)),_,outer(Comparison,_)) :-
1027 % b_global_sets:b_global_deferred_set(DeferredSet),
1028 % %we would like to avoid interfering with deferred set card detection, see test 893
1029 % member(Comparison,[equal,greater_equal,less_equal,greater,less]),!.
1030 do_not_share_this_3(case_or(_,_),_,_) :- !. % why does this have a pred type ?
1031 do_not_share_this_3(if_elsif(_,_),_,_) :- !.
1032 do_not_share_this_3(select_when(_,_),_,_) :- !. % has subst type; so will not be shared
1033 do_not_share_this_3(set_extension(X),_,outer(OuterFunctor,ArgNr)) :- !,
1034 %nl,print(sx(X,OuterFunctor/ArgNr)),nl,
1035 (X = [V], simple_explicit_value(V) ; % singleton set with simple value
1036 set_extension_optimized_treatment_available(OuterFunctor,ArgNr)).
1037 do_not_share_this_3(comprehension_set(_Id,_),_,M) :-
1038 % optimized treatment available; important for test 1079
1039 (nmem(M) -> true ; symbolic_set_op(M)). % -> true ; print(share(_Id,M)),nl,nl,fail).
1040 do_not_share_this_3(union(_,_),_,M) :-
1041 % union often used for composing functions
1042 (nmem(M) -> true ; symbolic_set_op(M)). % -> true ; print(share_union(M)),nl,nl,fail).
1043 % lambda is already translated into comprehension_set here
1044 %do_not_share_this_3(closure(_L),_,outer(OuterFunctor,ArgNr)) :- !,
1045 % closure1_optimized_treatment_available(OuterFunctor,ArgNr).
1046 %do_not_share_this_3(sequence_extension(_L),_,outer(OuterFunctor,ArgNr)) :- !, % we need to add b_compute_card2 for seq ?
1047 /*
1048 do_not_share_this_3(X,_,M) :- nmem(M), functor(X,F),
1049 (kernel_mappings:symbolic_closure_unary_operator(F) ;
1050 kernel_mappings:symbolic_closure_binary_operator(F)).
1051
1052 % case for unary_in_boolean_type solutions:
1053 */
1054 do_not_share_this_3(pow_subset(X),_,M) :- nmem(M) ; infinite_set(X).
1055 do_not_share_this_3(fin_subset(X),_,M) :- nmem(M) ; infinite_set(X).
1056 do_not_share_this_3(pow1_subset(X),_,M) :- nmem(M) ; infinite_set(X).
1057 do_not_share_this_3(fin1_subset(X),_,M) :- nmem(M) ; infinite_set(X).
1058 do_not_share_this_3(struct(_),_,M) :- nmem(M).
1059 do_not_share_this_3(seq(X),_,M) :- nmem(M) ; infinite_set(X).
1060 do_not_share_this_3(iseq(X),_,M) :- nmem(M) ; infinite_set(X).
1061 do_not_share_this_3(perm(_),_,M) :- nmem(M).
1062 do_not_share_this_3(domain(_),_,M) :- nmem(M).
1063 do_not_share_this_3(seq1(X),_,M) :- nmem(M) ; infinite_set(X).
1064 do_not_share_this_3(iseq1(X),_,M) :- nmem(M) ; infinite_set(X).
1065 do_not_share_this_3(identity(X),_,M) :- nmem(M) ; infinite_set(X).
1066 do_not_share_this_3(closure(_),_,M) :- nmem(M).
1067 do_not_share_this_3(external_pred_call(FunName,_Args),_,_) :- not_declarative(FunName). % in principle any element containing this should not be shared (but anyway there are little gurantees about preservation and order of side-effects)
1068 do_not_share_this_3(external_function_call(FunName,_Args),_,_) :- not_declarative(FunName).
1069 % case for binary_not_in_boolean_type solutions:
1070 /*
1071 do_not_share_this_3(partial_bijection(_,_),_,M) :- nmem(M).
1072 do_not_share_this_3(total_relation(_,_),_,M) :- nmem(M).
1073 do_not_share_this_3(surjection_relation(_,_),_,M) :- nmem(M).
1074 do_not_share_this_3(total_surjection_relation(_,_),_,M) :- nmem(M).
1075 do_not_share_this_3(parallel_product(_,_),_,M) :- nmem(M).
1076 % set_subtraction, intersection not covered
1077 */
1078 do_not_share_this_3(interval(A,B),_,M) :- nmem(M) ; small_interval(A,B).
1079 do_not_share_this_3(total_function(A,B),_,M) :- nmem(M) ; infinite_set(A) ; infinite_set(B).
1080 do_not_share_this_3(total_injection(A,B),_,M) :- nmem(M) ; infinite_set(A) ; infinite_set(B).
1081 do_not_share_this_3(total_surjection(A,B),_,M) :- nmem(M) ; infinite_set(A) ; infinite_set(B).
1082 do_not_share_this_3(total_bijection(A,B),_,M) :- nmem(M) ; infinite_set(A) ; infinite_set(B).
1083 do_not_share_this_3(partial_function(A,B),_,M) :- nmem(M) ; infinite_set(A) ; infinite_set(B).
1084 do_not_share_this_3(partial_injection(A,B),_,M) :- nmem(M) ; infinite_set(A) ; infinite_set(B). % >+>
1085 do_not_share_this_3(partial_surjection(A,B),_,M) :- nmem(M) ; infinite_set(A) ; infinite_set(B).
1086 do_not_share_this_3(relations(A,B),_,M) :- nmem(M) ; infinite_set(A) ; infinite_set(B).
1087 do_not_share_this_3(cartesian_product(A,B),_,M) :- nmem(M) ; infinite_set(A) ; infinite_set(B).
1088 %do_not_share_this_3(div(A,B),_,_) :- A=identifier(xx), B=integer(1000000000). % xx / 1000000000 ; purpose: test vesg performance issue
1089 %do_not_share_this_3(range(identifier('Block_Id')),_,_). % for vesg performance test in INVARIANT
1090
1091 do_not_share_this_3(Stripped,UsedIds,_OuterFunctor) :- too_simple_for_sharing2(Stripped,UsedIds).
1092
1093 % check if expression used as second arg of member check:
1094 nmem(outer(M,2)) :- mem_op(M).
1095 nmem(outer(M,_)) :- (M=partial_function ; M=total_function ; M=relations).
1096 mem_op(member).
1097 mem_op(not_member).
1098 symbolic_set_op(outer(M,P)) :- (M=union; M=function,P=1).
1099
1100 small_interval(integer(A),integer(B)) :- number(A),number(B), B < A+10000000.
1101 infinite_set(integer_set(_)). % :- (X='INTEGER' ; X = 'NATURAL' ; X = 'NATURAL1').
1102 infinite_set(real_set).
1103 infinite_set(string_set).
1104 % TO DO : couple
1105 simple_explicit_value(integer(_)).
1106 simple_explicit_value(string(_)).
1107 simple_explicit_value(boolean_true).
1108 simple_explicit_value(boolean_false).
1109 simple_explicit_value(identifier(_Id)). % :- b_global_sets:is_b_global_constant(_,_,Id). % no guarantee that this is really an enumerated set element because a local variable could overide this; but
1110 % TO DO: should any identifier not be ok here: basically, we just gain the lookup when sharing ??
1111 %simple_explicit_value(V) :- print(sev(V)),nl,fail.
1112
1113 set_extension_optimized_treatment_available(card,1).
1114 set_extension_optimized_treatment_available(min,1). % we have optimized code for min({a,b,...}) and max
1115 set_extension_optimized_treatment_available(max,1).
1116 set_extension_optimized_treatment_available(function,1).
1117 set_extension_optimized_treatment_available(subset,2). % there is a rule in cleanup_post which will rewrite this to member checks {x1,x2,...} <: B <=> x1:B & x2:B & ...; we could also try and ensure that this runs first !
1118 set_extension_optimized_treatment_available(image,2). % ??
1119
1120 %closure1_optimized_treatment_available(image,1). % there is some custom code for: image_for_closure1_wf; but it is also called if we do CSE ??!!
1121
1122 :- use_module(probsrc(external_functions),[performs_io/1]).
1123
1124 too_simple_for_sharing2(equal(A,B),_) :-
1125 (A = identifier(_Id), boolean(B) ;
1126 B = identifier(_Id), boolean(A) ).
1127 too_simple_for_sharing2(identifier(Id),[Id]).
1128 too_simple_for_sharing2(unary_minus(Int),[]) :- Int = integer(_).
1129 too_simple_for_sharing2(integer(_),[]).
1130 too_simple_for_sharing2(value(_),[]).
1131 too_simple_for_sharing2(boolean_true,[]).
1132 too_simple_for_sharing2(boolean_false,[]).
1133 too_simple_for_sharing2(integer_set(_),[]).
1134 too_simple_for_sharing2(bool_set,[]).
1135 too_simple_for_sharing2(float_set,[]).
1136 too_simple_for_sharing2(real_set,[]).
1137 too_simple_for_sharing2(typeset,[]).
1138 too_simple_for_sharing2(string(_),[]).
1139 too_simple_for_sharing2(string_set,[]).
1140 too_simple_for_sharing2(empty_set,[]).
1141 too_simple_for_sharing2(empty_sequence,[]).
1142 too_simple_for_sharing2(lazy_lookup_pred(_),_).
1143 too_simple_for_sharing2(lazy_lookup_expr(_),_).
1144 too_simple_for_sharing2(lazy_let_expr(_,_,_),_).
1145 too_simple_for_sharing2(lazy_let_pred(_,_,_),_).
1146 too_simple_for_sharing2(lazy_let_subst(_,_,_),_).
1147 too_simple_for_sharing2(couple(_,_),_). % maybe could be useful nonetheless ??
1148 %too_simple_for_sharing2(interval(integer(_),integer(_)),_).
1149 % TO DO: do not share arithmetic expressions which can be constant folded ??
1150 % or maybe perform constant folding at the same time ??
1151 too_simple_for_sharing2(truth,[]).
1152 too_simple_for_sharing2(falsity,[]).
1153 too_simple_for_sharing2(max_int,[]).
1154 too_simple_for_sharing2(min_int,[]).
1155 % TO DO: completely avoid sharing those expressions ? but usually they should not occur inside predicates/expressions anyway
1156 too_simple_for_sharing2(external_pred_call(FunName,_Args),_) :-
1157 performs_io(FunName).
1158 too_simple_for_sharing2(external_function_call(FunName,_Args),_) :-
1159 performs_io(FunName).
1160
1161
1162 set_references(References,sharing(_Id,_RefCounter,_Level,References)).
1163
1164 % the stores data type has the following form:
1165 % stores(Counter,References,Top,Substores)
1166 % - Counter is the next ID for new expressions, the same expressions
1167 % share the same id
1168 % - References is a mapping from an ID to a term rc(C,R) where C
1169 % contains the number of occurrences of the expressions so far and R the
1170 % final number of occurrences of this expression. Until the end, R is a variable
1171 % that will be unified with the last value of C
1172 % - Top is a mapping from a stripped expression to an ID. This mapping is for all
1173 % expressions that do not contain references to quantified variables
1174 % - Substores is a list of terms substore(Ids,SubStore) where Ids is a list of
1175 % introduced identifiers in a qualified expression and Substore is a mapping from
1176 % a stripped expression to its ID. The expression contains references to identifiers
1177 % in Ids. In case of nested quantified expressions, the inner expression has
1178 % a substore entry before the outer expression in the list.
1179 create_stores(Start,stores(Start,Empty,Empty,[])) :-
1180 empty_avl(Empty).
1181 create_substore([],_SubId,Stores,Stores) :- !.
1182 create_substore(NewIds,ID,InStores,SubStores) :-
1183 force_create_substore(NewIds,ID,InStores,SubStores).
1184 force_create_substore(Ids,SubId,
1185 stores(Counter,IR,Top, InSubs),
1186 stores(Counter,IR,Top, [substore(OrdIds,Empty,SubId,[])|InSubs])) :-
1187 list_to_ord_set(Ids,OrdIds),
1188 empty_avl(Empty).
1189
1190 % remove_last_substore(+Names,+InSubStores,-OutSubStores,-References)
1191 remove_last_substore([],Stores,Stores,[]).
1192 remove_last_substore([_|_], % new ids have been introduced; local scope / substore was added
1193 stores(Counter,IR,Top,[Substore|Subs]),
1194 stores(Counter,IR,Top,Subs),
1195 References) :-
1196 Substore = substore(_Names,_Store,_SubId,References).
1197
1198 % try and get references to common sub expression ids + the corresponding expressions
1199 get_current_references(stores(_Counter,IR,TopStore,_Subs),References) :-
1200 avl_to_list(IR,Refs),
1201 convlist(reused_expression(TopStore),Refs,References).
1202 reused_expression(TopStore,ID-rc(Count,Count),cse(ID,Expr,Count)) :- Count>1,
1203 ? avl_member(Expr,TopStore,ID). % reverse lookup; not efficient !
1204
1205 lookup_expression(Stripped,UsedIds,InStores,OutStores,sharing(Id,RefCounter,Level,_References),Id) :-
1206 InStores = stores(Counter,InRefCounter, TopStore,OldSubStores),
1207 OutStores = stores(Counter,OutRefCounter,TopStore,NewSubStores),
1208 lookup_expression2(OldSubStores,TopStore,Stripped,UsedIds,Id,Level,NewSubStores),!,
1209 avl_fetch(Id,InRefCounter,rc(OC,RefCounter)),
1210 NC is OC+1,
1211 avl_store(Id,InRefCounter,rc(NC,RefCounter),OutRefCounter).
1212 lookup_expression2([],TopStore,Stripped,_UsedIds,Id,top,[]) :-
1213 avl_fetch(Stripped,TopStore,Id).
1214 lookup_expression2([substore(Names,Store,SubId,OldRefs)|Rest],_TopStore,Stripped,_UsedIds,Id,SubId,
1215 [substore(Names,Store,SubId,NewRefs)|Rest]) :-
1216 avl_fetch(Stripped,Store,Id),!,
1217 ord_add_element(OldRefs,Id,NewRefs).
1218 lookup_expression2([substore(Names,Store,SubId,Refs)|Rest],TopStore,Stripped,UsedIds,Id,Level,
1219 [substore(Names,Store,SubId,Refs)|NRest]) :-
1220 ord_disjoint(UsedIds,Names),
1221 !,
1222 lookup_expression2(Rest,TopStore,Stripped,UsedIds,Id,Level,NRest).
1223
1224 %lookup_next_id(stores(Id,_,_,_),Id).
1225
1226 store_expression(Stripped,UsedIds,InStores,OutStores,sharing(Id,RefCounter,Level,_References),Id) :-
1227 InStores = stores(Id,InRefCounter,InTopStore,InSubStores),
1228 Counter is Id+1, % Generate a new CSE Identifier
1229 OutStores = stores(Counter,OutRefCounter,OutTopStore,OutSubStores),
1230 store_expression2(InSubStores,InTopStore,Stripped,UsedIds,Id,OutSubStores,OutTopStore,Level),
1231 avl_store(Id,InRefCounter,rc(1,RefCounter),OutRefCounter).
1232 store_expression2([],InTopStore,Stripped,_UsedIds,Id,[],OutTopStore,top) :-
1233 avl_store(Stripped,InTopStore,Id,OutTopStore).
1234 store_expression2([substore(Names,InStore,SubId,Refs) |Srest],TopStore,Stripped,UsedIds,Id,
1235 [substore(Names,OutStore,SubId,Refs)|Srest],TopStore,SubId) :-
1236 ord_intersect(Names,UsedIds),!, % the Shared Expression makes use of identifiers/names introduced at that level; it cannot be re-used higher up
1237 avl_store(Stripped,InStore,Id,OutStore).
1238 store_expression2([Substore|ISrest],InTopStore,Stripped,UsedIds,Id,
1239 [Substore|OSrest],OutTopStore,Level) :-
1240 store_expression2(ISrest,InTopStore,Stripped,UsedIds,Id,OSrest,OutTopStore,Level).
1241
1242 :- public portray_stores/1. % for debugging
1243 portray_stores(stores(Counter,RefCounter,TopStore,SubStores)) :-
1244 format('Store Counter:~w ~n Reference Counts: ',[Counter]),
1245 portray_avl(RefCounter),nl,
1246 print('Top = '), portray_avl(TopStore),nl,
1247 (maplist(portray_substore,SubStores) -> true ; print('Illegal Substores: '),nl).
1248 portray_substore(substore(Names,OutStore,_SubId,Refs)) :-
1249 format('--> Substore [Names=~w, Refs=~w]~n Store = ',[Names,Refs]),
1250 portray_avl(OutStore),nl.
1251
1252 update_used_ids(Expr,NewlyIntroducedIds,UsedSubIds,UsedIds) :-
1253 update_used_ids2(Expr,UsedSubIds,UsedIds1),
1254 ord_subtract(UsedIds1,NewlyIntroducedIds,UsedIds). % remove NewlyIntroducedIds; they are not visible outside
1255 update_used_ids2(identifier(Id),UsedSubIds,UsedIds) :- !, % we directly use an identifier
1256 ord_add_element(UsedSubIds,Id,UsedIds).
1257 update_used_ids2(_Expr,UsedSubIds,UsedIds) :- !, UsedSubIds=UsedIds.
1258
1259 % We do not have to use strip_and_norm_ast/2 from bsyntaxtree though
1260 % the result should be the same. We already have the stripped versions
1261 % of the subexpressions, so we can re-use that information
1262 create_stripped(Expr,StrippedSubs,NormStripped) :-
1263 (cse_syntaxtransformation(Expr,_Subs,_Names,StrippedSubs,Stripped)
1264 -> norm_strip(Stripped,NormStripped)
1265 ; add_internal_error('Illegal Expression: ',create_stripped(Expr,StrippedSubs,NormStripped)),
1266 NormStripped = Expr).
1267
1268 ground_reference_counters(stores(_Counter,RefCounter,_TopStore,_SubStores)) :-
1269 avl_to_list(RefCounter,List),
1270 maplist(ground_reference_counter,List).
1271 ground_reference_counter(_Id-rc(C,C)).
1272
1273 :- use_module(library(samsort)).
1274 norm_strip(empty_sequence,R) :- !, R = empty_set.
1275 norm_strip(greater_equal(A,B),R) :- !, R = less_equal(B,A).
1276 norm_strip(greater(A,B),R) :- !, R = less(B,A).
1277 norm_strip(set_extension(SX),R) :- !,
1278 sort(SX,SSX),
1279 R = set_extension(SSX).
1280 norm_strip(negation(A),R) :- negate_norm_strip(A,R).
1281 norm_strip(Old,New) :-
1282 functor(Old,Functor,2),
1283 is_commutative(Functor),!,
1284 norm_commutative(Functor,Old,New).
1285 norm_strip(Old,Old).
1286
1287 negate_norm_strip(equal(A,B),R) :- !, R=not_equal(A,B).
1288 negate_norm_strip(not_equal(A,B),R) :- !, R=equal(A,B).
1289 negate_norm_strip(less_equal(A,B),R) :- !, R=less(B,A).
1290 negate_norm_strip(less_equal_real(A,B),R) :- !, R=less_real(B,A).
1291 negate_norm_strip(less(A,B),R) :- !, R=less_equal(B,A).
1292 negate_norm_strip(less_real(A,B),R) :- !, R=less_equal_real(B,A).
1293 negate_norm_strip(member(A,B),R) :- !, R=not_member(A,B).
1294 negate_norm_strip(not_member(A,B),R) :- !, R=member(A,B).
1295 negate_norm_strip(subset(A,B),R) :- !, R=not_subset(A,B).
1296 negate_norm_strip(not_subset(A,B),R) :- !, R=subset(A,B).
1297 negate_norm_strip(subset_strict(A,B),R) :- !, R=not_subset_strict(A,B).
1298 negate_norm_strip(not_subset_strict(A,B),R) :- !, R=subset_strict(A,B).
1299 negate_norm_strip(conjunct(C),R) :- !,
1300 maplist(negate_norm_strip,C,NC),
1301 R=disjunct(NC).
1302 negate_norm_strip(disjunct(C),R) :- !,
1303 maplist(negate_norm_strip,C,NC),
1304 R=conjunct(NC).
1305 negate_norm_strip(negation(A),R) :- !, R=A.
1306 % what about implication, equivalence, exists, forall
1307 negate_norm_strip(X,negation(X)).
1308
1309 norm_commutative(Functor,Old,New) :- is_associative(Functor), !,
1310 functor(Old,Functor,2), arg(1,Old,OA), arg(2,Old,OB),
1311 % now collect all args with same functor in left and right:
1312 get_top_level_functor_arguments(OA,Functor,LeftArgs),
1313 get_top_level_functor_arguments(OB,Functor,RightArgs),
1314 append(LeftArgs,RightArgs,Args),
1315 samsort(Args,SortedArgs),
1316 New =.. [Functor,SortedArgs].
1317 norm_commutative(Functor,Old,New) :- % the functor is not associative (or cannot appear at top-level in OA,OB)
1318 functor(Old,Functor,2), arg(1,Old,OA), arg(2,Old,OB),
1319 New =.. [Functor,NA,NB],
1320 (OA @>= OB -> OA=NB,OB=NA ; OA=NA,OB=NB).
1321
1322 % we could use this instead of samsort
1323 % but we get error message e.g. for % (x\/y\/v\/x) /= (v\/x\/y) & x<:BOOL as an expression appears within itself ?!
1324 %sort_args(Functor,Args,SortedArgs) :-
1325 % (idempotent(Functor) -> sort(Args,SortedArgs) % we can remove duplicates
1326 % ; samsort(Args,SortedArgs)).
1327
1328
1329 get_top_level_functor_arguments(Top,Functor,Args) :- % must also be associative
1330 functor(Top,Functor,N), !,
1331 (N=2 -> Top =.. [Functor|Args] % no normalisation applied
1332 ; arg(1,Top,Args)). % Note: we do not recurse: we assume normalisation has already been applied fully below
1333 get_top_level_functor_arguments(Top,_,[Top]).
1334
1335 %is_commutative(_) :- !,fail.
1336 is_commutative(conjunct). % commutative only if there are no WD issues !
1337 is_commutative(disjunct). % commutative only if there are no WD issues !
1338 is_commutative(equivalence).
1339 is_commutative(equal).
1340 is_commutative(not_equal).
1341 is_commutative(add).
1342 is_commutative(multiplication).
1343 is_commutative(union).
1344 is_commutative(intersection).
1345
1346
1347 %is_associative(_) :- !,fail.
1348 is_associative(conjunct). % even if there are WD issues !
1349 is_associative(disjunct). % even if there are WD issues !
1350 is_associative(add).
1351 is_associative(multiplication).
1352 is_associative(union).
1353 is_associative(intersection).
1354 is_associative(concat).
1355 is_associative(composition).
1356
1357 % not used at the moment:
1358 :- public idempotent/1.
1359 idempotent(conjunct).
1360 idempotent(disjunct).
1361 idempotent(union).
1362
1363 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1364 % removing implicit negation:
1365 % predicates of the form a/=b (resp. a/:b, ...) are
1366 % rewritten to not(a=b), (resp. not(a:b), ...) to enable sharing of the
1367 % predicate a=b.
1368
1369 remove_implicit_negation(TExpr,NTExpr) :-
1370 remove_bt(TExpr,Expr,NewExpr,TExpr2),
1371 syntaxtransformation(Expr,Subs,_Names,NSubs,NewExpr),
1372 remove_implicit_negation_l(Subs,NSubs),
1373 remove_implicit_negation2(TExpr2,NTExpr).
1374
1375 remove_implicit_negation_l([],[]).
1376 remove_implicit_negation_l([OExpr|ORest],[NExpr|NRest]) :-
1377 remove_implicit_negation(OExpr,NExpr),
1378 remove_implicit_negation_l(ORest,NRest).
1379
1380 remove_implicit_negation2(TExpr,NTExpr) :-
1381 get_texpr_expr(TExpr,Expr),
1382 remove_implicit_negation3(Expr,NTExpr),!.
1383 remove_implicit_negation2(TExpr,TExpr).
1384
1385 remove_implicit_negation3(negation(P),R) :-
1386 get_texpr_expr(P,negation(Q)),!,R=Q. % eliminate double negation
1387 remove_implicit_negation3(not_equal(A,B),R) :- negate_bexpr(equal(A,B),A,B,R).
1388 remove_implicit_negation3(not_member(A,B),R) :- negate_bexpr(member(A,B),A,B,R).
1389 remove_implicit_negation3(not_subset(A,B),R) :- negate_bexpr(subset(A,B),A,B,R).
1390 remove_implicit_negation3(not_subset_strict(A,B),R) :- negate_bexpr(subset_strict(A,B),A,B,R).
1391
1392 negate_bexpr(Expr,SubA,SubB,R) :- extract_info(SubA,SubB,Info),
1393 create_texpr(Expr,pred,Info,P),
1394 create_negation(P,R).
1395
1396
1397 % TO DO: optimize away unnecessary nested LETs as below:
1398 % Optimized pred:
1399 % !(sddb1,sddb2).((sddb1 : INTEGER & sddb2 : INTEGER) & (sddb1 |-> sddb2 : dom(SDDBs_Signal) & (Signals_Cap__Name~)(Routes_Cap__Opposite_Signal_ID(route)) : ran(SDDBs_Signal(sddb1 |-> sddb2))) => (LET (@9)==ran(tses) IN (LET (@10)==dom(@9) IN sddb1 /: @10 & sddb2 /: @10)))
1400
1401
1402 /*
1403
1404 Code currently not used; but possibly to be re-included :
1405
1406
1407 % a version of conjunction which recomputes the cse_used_ids INFO
1408 cse_conjunct_predicates(L,Res) :- cse_conjunct_predicates2(L,b(E,pred,I)),
1409 (collect_uses_cse_id(L,CSEINFO)
1410 -> Res = b(E,pred,[cse_used_ids(CSEINFO)|I1]),
1411 exclude(do_not_copy_info_from_sub_terms,I,I1) %delete(I,cse_used_ids(_),I1)
1412 ; print('*** Could not collect uses_cse_id INFO :'), print(L),nl,
1413 Res = b(E,pred,I)).
1414
1415 % sometimes we get identical conjuncts; TO DO: generalize to longer lists,... (test 351 still leaves redundant conjuncts inside let body)
1416 cse_conjunct_predicates2([A,B],R) :- A==B, !, R=A.
1417 cse_conjunct_predicates2(L,R) :- conjunct_predicates(L,R).
1418
1419
1420 % find prefix list of conjuncts which do not use CSEID:
1421 find_prefix_not_using_cse_id([],_CSEID,[],[]).
1422 find_prefix_not_using_cse_id([H|T],CSEID,Prefix,Rest) :-
1423 (uses_cse_id(CSEID,H)
1424 -> Prefix=[], Rest= [H|T]
1425 ; % print('NOT USING '), print(CSEID),nl, translate:print_bexpr_or_subst(H),nl,
1426 Prefix = [H|TP],
1427 find_prefix_not_using_cse_id(T,CSEID,TP,Rest)).
1428
1429 % split a conjunct into PREFIX & MAIN & SUFFIX such that PREFIX/SUFFIX do not use CSEID identifier
1430 cse_split_conjunct(Conjunct,CSEID, PREFIX,MAIN,SUFFIX) :-
1431 is_a_conjunct(Conjunct,_,_),
1432 % try removing conjuncts which do not use CSEID by flattening first:
1433 conjunction_to_list(Conjunct,List),
1434 find_prefix_not_using_cse_id(List,CSEID,PREFIX,Rest1),
1435 reverse(Rest1,RevRest1),
1436 find_prefix_not_using_cse_id(RevRest1,CSEID,RevSuffix,RevMain),
1437 (PREFIX \= [] ; RevSuffix \= []), % only succeed if there is a non-trivial split
1438 reverse(RevMain,MAIN),
1439 reverse(RevSuffix,SUFFIX).
1440
1441
1442 % binders which pose problem for lazy_let introduction in case SharedExpression depends on bound variables
1443 non_unary_binder(b(P,_Type,_I),Ids) :-
1444 non_unary_binder_aux(P,Ids,_Left,_Right).
1445 non_unary_binder_aux(forall(Ids,A,B),Ids,A,B).
1446 non_unary_binder_aux(let_predicate(Ids,A,B),Ids,A,B).
1447 % expressions
1448 non_unary_binder_aux(general_sum(Ids,A,B),Ids,A,B).
1449 non_unary_binder_aux(general_product(Ids,A,B),Ids,A,B).
1450 non_unary_binder_aux(quantified_union(Ids,A,B),Ids,A,B).
1451 non_unary_binder_aux(quantified_intersection(Ids,A,B),Ids,A,B).
1452 non_unary_binder_aux(general_product(Ids,A,B),Ids,A,B).
1453 non_unary_binder_aux(event_b_comprehension_set(Ids,A,B),Ids,A,B).
1454 non_unary_binder_aux(lambda(Ids,A,B),Ids,A,B).
1455 non_unary_binder_aux(let_expression(Ids,A,B),Ids,A,B).
1456 %subst
1457 non_unary_binder_aux(let(Ids,A,B),Ids,A,B).
1458 non_unary_binder_aux(any(Ids,A,B),Ids,A,B).
1459 % TO DO: add more subst
1460
1461 % binders which only have a single sub-expression; here we just have to decide whether to introduce the let inside or outside
1462 unary_binder(b(P,_Type,_I),Ids) :-
1463 unary_binder_aux(P,Ids,_Left).
1464 unary_binder_aux(exists(Ids,A),Ids,A).
1465 unary_binder_aux(comprehension_set(Ids,A),Ids,A).
1466
1467
1468 */