1 | | % (c) 2013-2024 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_card_arithmetic),[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 | | */ |