1
|
(********************************************************************)
|
2
|
(* *)
|
3
|
(* The LustreC compiler toolset / The LustreC Development Team *)
|
4
|
(* Copyright 2012 - -- ONERA - CNRS - INPT - LIFL *)
|
5
|
(* *)
|
6
|
(* LustreC is free software, distributed WITHOUT ANY WARRANTY *)
|
7
|
(* under the terms of the GNU Lesser General Public License *)
|
8
|
(* version 2.1. *)
|
9
|
(* *)
|
10
|
(* This file was originally from the Prelude compiler *)
|
11
|
(* *)
|
12
|
(********************************************************************)
|
13
|
|
14
|
|
15
|
(** Main clock-calculus module. Based on type inference algorithms with
|
16
|
destructive unification. Uses a bit of subtyping for periodic clocks. *)
|
17
|
|
18
|
(* Though it shares similarities with the typing module, no code
|
19
|
is shared. Simple environments, very limited identifier scoping, no
|
20
|
identifier redefinition allowed. *)
|
21
|
open Utils
|
22
|
open LustreSpec
|
23
|
open Corelang
|
24
|
open Clocks
|
25
|
open Format
|
26
|
|
27
|
let loc_of_cond loc_containing id =
|
28
|
let pos_start =
|
29
|
{loc_containing.Location.loc_end with
|
30
|
Lexing.pos_cnum=loc_containing.Location.loc_end.Lexing.pos_cnum-(String.length id)}
|
31
|
in
|
32
|
{Location.loc_start = pos_start;
|
33
|
Location.loc_end = loc_containing.Location.loc_end}
|
34
|
|
35
|
(** [occurs cvar ck] returns true if the clock variable [cvar] occurs in
|
36
|
clock [ck]. False otherwise. *)
|
37
|
let rec occurs cvar ck =
|
38
|
let ck = repr ck in
|
39
|
match ck.cdesc with
|
40
|
| Carrow (ck1, ck2) ->
|
41
|
(occurs cvar ck1) || (occurs cvar ck2)
|
42
|
| Ctuple ckl ->
|
43
|
List.exists (occurs cvar) ckl
|
44
|
| Con (ck',_,_) -> occurs cvar ck'
|
45
|
| Pck_up (ck',_) -> occurs cvar ck'
|
46
|
| Pck_down (ck',_) -> occurs cvar ck'
|
47
|
| Pck_phase (ck',_) -> occurs cvar ck'
|
48
|
| Cvar _ -> ck=cvar
|
49
|
| Cunivar _ | Pck_const (_,_) -> false
|
50
|
| Clink ck' -> occurs cvar ck'
|
51
|
| Ccarrying (_,ck') -> occurs cvar ck'
|
52
|
|
53
|
(* Clocks generalization *)
|
54
|
let rec generalize_carrier cr =
|
55
|
match cr.carrier_desc with
|
56
|
| Carry_const _
|
57
|
| Carry_name ->
|
58
|
if cr.carrier_scoped then
|
59
|
raise (Scope_carrier cr);
|
60
|
cr.carrier_desc <- Carry_var
|
61
|
| Carry_var -> ()
|
62
|
| Carry_link cr' -> generalize_carrier cr'
|
63
|
|
64
|
(** Promote monomorphic clock variables to polymorphic clock variables. *)
|
65
|
(* Generalize by side-effects *)
|
66
|
let rec generalize ck =
|
67
|
match ck.cdesc with
|
68
|
| Carrow (ck1,ck2) ->
|
69
|
generalize ck1; generalize ck2
|
70
|
| Ctuple clist ->
|
71
|
List.iter generalize clist
|
72
|
| Con (ck',cr,_) -> generalize ck'; generalize_carrier cr
|
73
|
| Cvar cset ->
|
74
|
if ck.cscoped then
|
75
|
raise (Scope_clock ck);
|
76
|
ck.cdesc <- Cunivar cset
|
77
|
| Pck_up (ck',_) -> generalize ck'
|
78
|
| Pck_down (ck',_) -> generalize ck'
|
79
|
| Pck_phase (ck',_) -> generalize ck'
|
80
|
| Pck_const (_,_) | Cunivar _ -> ()
|
81
|
| Clink ck' ->
|
82
|
generalize ck'
|
83
|
| Ccarrying (cr,ck') ->
|
84
|
generalize_carrier cr; generalize ck'
|
85
|
|
86
|
let try_generalize ck_node loc =
|
87
|
try
|
88
|
generalize ck_node
|
89
|
with (Scope_carrier cr) ->
|
90
|
raise (Error (loc, Carrier_extrusion (ck_node, cr)))
|
91
|
| (Scope_clock ck) ->
|
92
|
raise (Error (loc, Clock_extrusion (ck_node, ck)))
|
93
|
|
94
|
(* Clocks instanciation *)
|
95
|
let instantiate_carrier cr inst_cr_vars =
|
96
|
let cr = carrier_repr cr in
|
97
|
match cr.carrier_desc with
|
98
|
| Carry_const _
|
99
|
| Carry_name -> cr
|
100
|
| Carry_link _ ->
|
101
|
failwith "Internal error"
|
102
|
| Carry_var ->
|
103
|
try
|
104
|
List.assoc cr.carrier_id !inst_cr_vars
|
105
|
with Not_found ->
|
106
|
let cr_var = new_carrier Carry_name true in
|
107
|
inst_cr_vars := (cr.carrier_id,cr_var)::!inst_cr_vars;
|
108
|
cr_var
|
109
|
|
110
|
(** Downgrade polymorphic clock variables to monomorphic clock variables *)
|
111
|
(* inst_ck_vars ensures that a polymorphic variable is instanciated with
|
112
|
the same monomorphic variable if it occurs several times in the same
|
113
|
type. inst_cr_vars is the same for carriers. *)
|
114
|
let rec instantiate inst_ck_vars inst_cr_vars ck =
|
115
|
match ck.cdesc with
|
116
|
| Carrow (ck1,ck2) ->
|
117
|
{ck with cdesc =
|
118
|
Carrow ((instantiate inst_ck_vars inst_cr_vars ck1),
|
119
|
(instantiate inst_ck_vars inst_cr_vars ck2))}
|
120
|
| Ctuple cklist ->
|
121
|
{ck with cdesc = Ctuple
|
122
|
(List.map (instantiate inst_ck_vars inst_cr_vars) cklist)}
|
123
|
| Con (ck',c,l) ->
|
124
|
let c' = instantiate_carrier c inst_cr_vars in
|
125
|
{ck with cdesc = Con ((instantiate inst_ck_vars inst_cr_vars ck'),c',l)}
|
126
|
| Cvar _ | Pck_const (_,_) -> ck
|
127
|
| Pck_up (ck',k) ->
|
128
|
{ck with cdesc = Pck_up ((instantiate inst_ck_vars inst_cr_vars ck'),k)}
|
129
|
| Pck_down (ck',k) ->
|
130
|
{ck with cdesc = Pck_down ((instantiate inst_ck_vars inst_cr_vars ck'),k)}
|
131
|
| Pck_phase (ck',q) ->
|
132
|
{ck with cdesc = Pck_phase ((instantiate inst_ck_vars inst_cr_vars ck'),q)}
|
133
|
| Clink ck' ->
|
134
|
{ck with cdesc = Clink (instantiate inst_ck_vars inst_cr_vars ck')}
|
135
|
| Ccarrying (cr,ck') ->
|
136
|
let cr' = instantiate_carrier cr inst_cr_vars in
|
137
|
{ck with cdesc =
|
138
|
Ccarrying (cr',instantiate inst_ck_vars inst_cr_vars ck')}
|
139
|
| Cunivar cset ->
|
140
|
try
|
141
|
List.assoc ck.cid !inst_ck_vars
|
142
|
with Not_found ->
|
143
|
let var = new_ck (Cvar cset) true in
|
144
|
inst_ck_vars := (ck.cid, var)::!inst_ck_vars;
|
145
|
var
|
146
|
|
147
|
(** [subsume pck1 cset1] subsumes clock [pck1] by clock subset
|
148
|
[cset1]. The clock constraint is actually recursively transfered to
|
149
|
the clock variable appearing in [pck1] *)
|
150
|
let subsume pck1 cset1 =
|
151
|
let rec aux pck cset =
|
152
|
match cset with
|
153
|
| CSet_all ->
|
154
|
()
|
155
|
| CSet_pck (k,(a,b)) ->
|
156
|
match pck.cdesc with
|
157
|
| Cvar cset' ->
|
158
|
pck.cdesc <- Cvar (intersect cset' cset)
|
159
|
| Pck_up (pck',k') ->
|
160
|
let rat = if a=0 then (0,1) else (a,b*k') in
|
161
|
aux pck' (CSet_pck ((k*k'),rat))
|
162
|
| Pck_down (pck',k') ->
|
163
|
let k''=k/(gcd k k') in
|
164
|
aux pck' (CSet_pck (k'',(a*k',b)))
|
165
|
| Pck_phase (pck',(a',b')) ->
|
166
|
let (a'',b'')= max_rat (sum_rat (a,b) (-a',b')) (0,1) in
|
167
|
aux pck' (CSet_pck (k, (a'',b'')))
|
168
|
| Pck_const (n,(a',b')) ->
|
169
|
if n mod k <> 0 || (max_rat (a,b) (a',b')) <> (a',b') then
|
170
|
raise (Subsume (pck1, cset1))
|
171
|
| Clink pck' ->
|
172
|
aux pck' cset
|
173
|
| Cunivar _ -> ()
|
174
|
| Ccarrying (_,ck') ->
|
175
|
aux ck' cset
|
176
|
| _ -> raise (Subsume (pck1, cset1))
|
177
|
in
|
178
|
aux pck1 cset1
|
179
|
|
180
|
let rec update_scope_carrier scoped cr =
|
181
|
if (not scoped) then
|
182
|
begin
|
183
|
cr.carrier_scoped <- scoped;
|
184
|
match cr.carrier_desc with
|
185
|
| Carry_const _ | Carry_name | Carry_var -> ()
|
186
|
| Carry_link cr' -> update_scope_carrier scoped cr'
|
187
|
end
|
188
|
|
189
|
let rec update_scope scoped ck =
|
190
|
if (not scoped) then
|
191
|
begin
|
192
|
ck.cscoped <- scoped;
|
193
|
match ck.cdesc with
|
194
|
| Carrow (ck1,ck2) ->
|
195
|
update_scope scoped ck1; update_scope scoped ck2
|
196
|
| Ctuple clist ->
|
197
|
List.iter (update_scope scoped) clist
|
198
|
| Con (ck',cr,_) -> update_scope scoped ck'(*; update_scope_carrier scoped cr*)
|
199
|
| Cvar cset ->
|
200
|
ck.cdesc <- Cvar cset
|
201
|
| Pck_up (ck',_) -> update_scope scoped ck'
|
202
|
| Pck_down (ck',_) -> update_scope scoped ck'
|
203
|
| Pck_phase (ck',_) -> update_scope scoped ck'
|
204
|
| Pck_const (_,_) | Cunivar _ -> ()
|
205
|
| Clink ck' ->
|
206
|
update_scope scoped ck'
|
207
|
| Ccarrying (cr,ck') ->
|
208
|
update_scope_carrier scoped cr; update_scope scoped ck'
|
209
|
end
|
210
|
|
211
|
(* Unifies two static pclocks. *)
|
212
|
let unify_static_pck ck1 ck2 =
|
213
|
if (period ck1 <> period ck2) || (phase ck1 <> phase ck2) then
|
214
|
raise (Unify (ck1,ck2))
|
215
|
|
216
|
(* Unifies two clock carriers *)
|
217
|
let unify_carrier cr1 cr2 =
|
218
|
let cr1 = carrier_repr cr1 in
|
219
|
let cr2 = carrier_repr cr2 in
|
220
|
if cr1==cr2 then ()
|
221
|
else
|
222
|
match cr1.carrier_desc, cr2.carrier_desc with
|
223
|
| Carry_const id1, Carry_const id2 ->
|
224
|
if id1 <> id2 then raise (Mismatch (cr1, cr2))
|
225
|
| Carry_const _, Carry_name ->
|
226
|
begin
|
227
|
cr2.carrier_desc <- Carry_link cr1;
|
228
|
update_scope_carrier cr2.carrier_scoped cr1
|
229
|
end
|
230
|
| Carry_name, Carry_const _ ->
|
231
|
begin
|
232
|
cr1.carrier_desc <- Carry_link cr2;
|
233
|
update_scope_carrier cr1.carrier_scoped cr2
|
234
|
end
|
235
|
| Carry_name, Carry_name ->
|
236
|
if cr1.carrier_id < cr2.carrier_id then
|
237
|
begin
|
238
|
cr2.carrier_desc <- Carry_link cr1;
|
239
|
update_scope_carrier cr2.carrier_scoped cr1
|
240
|
end
|
241
|
else
|
242
|
begin
|
243
|
cr1.carrier_desc <- Carry_link cr2;
|
244
|
update_scope_carrier cr1.carrier_scoped cr2
|
245
|
end
|
246
|
| _,_ -> assert false
|
247
|
|
248
|
(* Semi-unifies two clock carriers *)
|
249
|
let semi_unify_carrier cr1 cr2 =
|
250
|
let cr1 = carrier_repr cr1 in
|
251
|
let cr2 = carrier_repr cr2 in
|
252
|
if cr1==cr2 then ()
|
253
|
else
|
254
|
match cr1.carrier_desc, cr2.carrier_desc with
|
255
|
| Carry_const id1, Carry_const id2 ->
|
256
|
if id1 <> id2 then raise (Mismatch (cr1, cr2))
|
257
|
| Carry_const _, Carry_name ->
|
258
|
begin
|
259
|
cr2.carrier_desc <- Carry_link cr1;
|
260
|
update_scope_carrier cr2.carrier_scoped cr1
|
261
|
end
|
262
|
| Carry_name, Carry_const _ -> raise (Mismatch (cr1, cr2))
|
263
|
| Carry_name, Carry_name ->
|
264
|
if cr1.carrier_id < cr2.carrier_id then
|
265
|
begin
|
266
|
cr2.carrier_desc <- Carry_link cr1;
|
267
|
update_scope_carrier cr2.carrier_scoped cr1
|
268
|
end
|
269
|
else
|
270
|
begin
|
271
|
cr1.carrier_desc <- Carry_link cr2;
|
272
|
update_scope_carrier cr1.carrier_scoped cr2
|
273
|
end
|
274
|
| _,_ -> assert false
|
275
|
|
276
|
let try_unify_carrier ck1 ck2 loc =
|
277
|
try
|
278
|
unify_carrier ck1 ck2
|
279
|
with
|
280
|
| Unify (ck1',ck2') ->
|
281
|
raise (Error (loc, Clock_clash (ck1',ck2')))
|
282
|
| Subsume (ck,cset) ->
|
283
|
raise (Error (loc, Clock_set_mismatch (ck,cset)))
|
284
|
| Mismatch (cr1,cr2) ->
|
285
|
raise (Error (loc, Carrier_mismatch (cr1,cr2)))
|
286
|
|
287
|
(** [unify ck1 ck2] unifies clocks [ck1] and [ck2]. Raises [Unify
|
288
|
(ck1,ck2)] if the clocks are not unifiable.*)
|
289
|
let rec unify ck1 ck2 =
|
290
|
let ck1 = repr ck1 in
|
291
|
let ck2 = repr ck2 in
|
292
|
if ck1==ck2 then
|
293
|
()
|
294
|
else
|
295
|
let left_const = is_concrete_pck ck1 in
|
296
|
let right_const = is_concrete_pck ck2 in
|
297
|
if left_const && right_const then
|
298
|
unify_static_pck ck1 ck2
|
299
|
else
|
300
|
match ck1.cdesc,ck2.cdesc with
|
301
|
| Cvar cset1,Cvar cset2->
|
302
|
if ck1.cid < ck2.cid then
|
303
|
begin
|
304
|
ck2.cdesc <- Clink (simplify ck1);
|
305
|
update_scope ck2.cscoped ck1;
|
306
|
subsume ck1 cset2
|
307
|
end
|
308
|
else
|
309
|
begin
|
310
|
ck1.cdesc <- Clink (simplify ck2);
|
311
|
update_scope ck1.cscoped ck2;
|
312
|
subsume ck2 cset1
|
313
|
end
|
314
|
| Cvar cset, Pck_up (_,_) | Cvar cset, Pck_down (_,_)
|
315
|
| Cvar cset, Pck_phase (_,_) | Cvar cset, Pck_const (_,_) ->
|
316
|
if (occurs ck1 ck2) then
|
317
|
begin
|
318
|
if (simplify ck2 = ck1) then
|
319
|
ck2.cdesc <- Clink (simplify ck1)
|
320
|
else
|
321
|
raise (Unify (ck1,ck2));
|
322
|
end
|
323
|
else
|
324
|
begin
|
325
|
ck1.cdesc <- Clink (simplify ck2);
|
326
|
subsume ck2 cset
|
327
|
end
|
328
|
| Pck_up (_,_), Cvar cset | Pck_down (_,_), Cvar cset
|
329
|
| Pck_phase (_,_), Cvar cset | Pck_const (_,_), Cvar cset ->
|
330
|
if (occurs ck2 ck1) then
|
331
|
begin
|
332
|
if ((simplify ck1) = ck2) then
|
333
|
ck1.cdesc <- Clink (simplify ck2)
|
334
|
else
|
335
|
raise (Unify (ck1,ck2));
|
336
|
end
|
337
|
else
|
338
|
begin
|
339
|
ck2.cdesc <- Clink (simplify ck1);
|
340
|
subsume ck1 cset
|
341
|
end
|
342
|
| (Cvar cset,_) when (not (occurs ck1 ck2)) ->
|
343
|
subsume ck2 cset;
|
344
|
update_scope ck1.cscoped ck2;
|
345
|
ck1.cdesc <- Clink (simplify ck2)
|
346
|
| (_, (Cvar cset)) when (not (occurs ck2 ck1)) ->
|
347
|
subsume ck1 cset;
|
348
|
update_scope ck2.cscoped ck1;
|
349
|
ck2.cdesc <- Clink (simplify ck1)
|
350
|
| Ccarrying (cr1,ck1'),Ccarrying (cr2,ck2') ->
|
351
|
unify_carrier cr1 cr2;
|
352
|
unify ck1' ck2'
|
353
|
| Ccarrying (_,_),_ | _,Ccarrying (_,_) ->
|
354
|
raise (Unify (ck1,ck2))
|
355
|
| Carrow (c1,c2), Carrow (c1',c2') ->
|
356
|
unify c1 c1'; unify c2 c2'
|
357
|
| Ctuple ckl1, Ctuple ckl2 ->
|
358
|
if (List.length ckl1) <> (List.length ckl2) then
|
359
|
raise (Unify (ck1,ck2));
|
360
|
List.iter2 unify ckl1 ckl2
|
361
|
| Con (ck',c1,l1), Con (ck'',c2,l2) when l1=l2 ->
|
362
|
unify_carrier c1 c2;
|
363
|
unify ck' ck''
|
364
|
| Pck_const (i,r), Pck_const (i',r') ->
|
365
|
if i<>i' || r <> r' then
|
366
|
raise (Unify (ck1,ck2))
|
367
|
| (_, Pck_up (pck2',k)) when (not right_const) ->
|
368
|
let ck1' = simplify (new_ck (Pck_down (ck1,k)) true) in
|
369
|
unify ck1' pck2'
|
370
|
| (_,Pck_down (pck2',k)) when (not right_const) ->
|
371
|
subsume ck1 (CSet_pck (k,(0,1)));
|
372
|
let ck1' = simplify (new_ck (Pck_up (ck1,k)) true) in
|
373
|
unify ck1' pck2'
|
374
|
| (_,Pck_phase (pck2',(a,b))) when (not right_const) ->
|
375
|
subsume ck1 (CSet_pck (b,(a,b)));
|
376
|
let ck1' = simplify (new_ck (Pck_phase (ck1,(-a,b))) true) in
|
377
|
unify ck1' pck2'
|
378
|
| Pck_up (pck1',k),_ ->
|
379
|
let ck2' = simplify (new_ck (Pck_down (ck2,k)) true) in
|
380
|
unify pck1' ck2'
|
381
|
| Pck_down (pck1',k),_ ->
|
382
|
subsume ck2 (CSet_pck (k,(0,1)));
|
383
|
let ck2' = simplify (new_ck (Pck_up (ck2,k)) true) in
|
384
|
unify pck1' ck2'
|
385
|
| Pck_phase (pck1',(a,b)),_ ->
|
386
|
subsume ck2 (CSet_pck (b,(a,b)));
|
387
|
let ck2' = simplify (new_ck (Pck_phase (ck2,(-a,b))) true) in
|
388
|
unify pck1' ck2'
|
389
|
| Cunivar _, _ | _, Cunivar _ -> ()
|
390
|
| _,_ -> raise (Unify (ck1,ck2))
|
391
|
|
392
|
(** [unify ck1 ck2] semi-unifies clocks [ck1] and [ck2]. Raises [Unify
|
393
|
(ck1,ck2)] if the clocks are not semi-unifiable.*)
|
394
|
let rec semi_unify ck1 ck2 =
|
395
|
let ck1 = repr ck1 in
|
396
|
let ck2 = repr ck2 in
|
397
|
if ck1==ck2 then
|
398
|
()
|
399
|
else
|
400
|
match ck1.cdesc,ck2.cdesc with
|
401
|
| Cvar cset1,Cvar cset2->
|
402
|
if ck1.cid < ck2.cid then
|
403
|
begin
|
404
|
ck2.cdesc <- Clink (simplify ck1);
|
405
|
update_scope ck2.cscoped ck1
|
406
|
end
|
407
|
else
|
408
|
begin
|
409
|
ck1.cdesc <- Clink (simplify ck2);
|
410
|
update_scope ck1.cscoped ck2
|
411
|
end
|
412
|
| (Cvar cset,_) -> raise (Unify (ck1,ck2))
|
413
|
| (_, (Cvar cset)) when (not (occurs ck2 ck1)) ->
|
414
|
update_scope ck2.cscoped ck1;
|
415
|
ck2.cdesc <- Clink (simplify ck1)
|
416
|
| Ccarrying (cr1,ck1'),Ccarrying (cr2,ck2') ->
|
417
|
semi_unify_carrier cr1 cr2;
|
418
|
semi_unify ck1' ck2'
|
419
|
| Ccarrying (_,_),_ | _,Ccarrying (_,_) ->
|
420
|
raise (Unify (ck1,ck2))
|
421
|
| Carrow (c1,c2), Carrow (c1',c2') ->
|
422
|
begin
|
423
|
semi_unify c1 c1';
|
424
|
semi_unify c2 c2'
|
425
|
end
|
426
|
| Ctuple ckl1, Ctuple ckl2 ->
|
427
|
if (List.length ckl1) <> (List.length ckl2) then
|
428
|
raise (Unify (ck1,ck2));
|
429
|
List.iter2 semi_unify ckl1 ckl2
|
430
|
| Con (ck',c1,l1), Con (ck'',c2,l2) when l1=l2 ->
|
431
|
semi_unify_carrier c1 c2;
|
432
|
semi_unify ck' ck''
|
433
|
| Cunivar _, _ | _, Cunivar _ -> ()
|
434
|
| _,_ -> raise (Unify (ck1,ck2))
|
435
|
|
436
|
(* Returns the value corresponding to a pclock (integer) factor
|
437
|
expression. Expects a constant expression (checked by typing). *)
|
438
|
let int_factor_of_expr e =
|
439
|
match e.expr_desc with
|
440
|
| Expr_const
|
441
|
(Const_int i) -> i
|
442
|
| _ -> failwith "Internal error: int_factor_of_expr"
|
443
|
|
444
|
(** [clock_uncarry ck] drops the possible carrier(s) name(s) from clock [ck] *)
|
445
|
let rec clock_uncarry ck =
|
446
|
let ck = repr ck in
|
447
|
match ck.cdesc with
|
448
|
| Ccarrying (_, ck') -> ck'
|
449
|
| Con(ck', cr, l) -> clock_on (clock_uncarry ck') cr l
|
450
|
| Ctuple ckl -> clock_of_clock_list (List.map clock_uncarry ckl)
|
451
|
| _ -> ck
|
452
|
|
453
|
let try_unify ck1 ck2 loc =
|
454
|
try
|
455
|
unify ck1 ck2
|
456
|
with
|
457
|
| Unify (ck1',ck2') ->
|
458
|
raise (Error (loc, Clock_clash (ck1',ck2')))
|
459
|
| Subsume (ck,cset) ->
|
460
|
raise (Error (loc, Clock_set_mismatch (ck,cset)))
|
461
|
| Mismatch (cr1,cr2) ->
|
462
|
raise (Error (loc, Carrier_mismatch (cr1,cr2)))
|
463
|
|
464
|
let try_semi_unify ck1 ck2 loc =
|
465
|
try
|
466
|
semi_unify ck1 ck2
|
467
|
with
|
468
|
| Unify (ck1',ck2') ->
|
469
|
raise (Error (loc, Clock_clash (ck1',ck2')))
|
470
|
| Subsume (ck,cset) ->
|
471
|
raise (Error (loc, Clock_set_mismatch (ck,cset)))
|
472
|
| Mismatch (cr1,cr2) ->
|
473
|
raise (Error (loc, Carrier_mismatch (cr1,cr2)))
|
474
|
|
475
|
(* ck2 is a subtype of ck1 *)
|
476
|
let rec sub_unify sub ck1 ck2 =
|
477
|
match (repr ck1).cdesc, (repr ck2).cdesc with
|
478
|
| Ctuple cl1 , Ctuple cl2 ->
|
479
|
if List.length cl1 <> List.length cl2
|
480
|
then raise (Unify (ck1, ck2))
|
481
|
else List.iter2 (sub_unify sub) cl1 cl2
|
482
|
| Ctuple [c1] , _ -> sub_unify sub c1 ck2
|
483
|
| _ , Ctuple [c2] -> sub_unify sub ck1 c2
|
484
|
| Con (c1, cr1, t1) , Con (c2, cr2, t2) when t1=t2 ->
|
485
|
begin
|
486
|
unify_carrier cr1 cr2;
|
487
|
sub_unify sub c1 c2
|
488
|
end
|
489
|
| Ccarrying (cr1, c1), Ccarrying (cr2, c2)->
|
490
|
begin
|
491
|
unify_carrier cr1 cr2;
|
492
|
sub_unify sub c1 c2
|
493
|
end
|
494
|
| _, Ccarrying (_, c2) when sub -> sub_unify sub ck1 c2
|
495
|
| _ -> unify ck1 ck2
|
496
|
|
497
|
let try_sub_unify sub ck1 ck2 loc =
|
498
|
try
|
499
|
sub_unify sub ck1 ck2
|
500
|
with
|
501
|
| Unify (ck1',ck2') ->
|
502
|
raise (Error (loc, Clock_clash (ck1',ck2')))
|
503
|
| Subsume (ck,cset) ->
|
504
|
raise (Error (loc, Clock_set_mismatch (ck,cset)))
|
505
|
| Mismatch (cr1,cr2) ->
|
506
|
raise (Error (loc, Carrier_mismatch (cr1,cr2)))
|
507
|
|
508
|
(* Unifies all the clock variables in the clock type of a tuple
|
509
|
expression, so that the clock type only uses at most one clock variable *)
|
510
|
let unify_tuple_clock ref_ck_opt ck loc =
|
511
|
(*(match ref_ck_opt with
|
512
|
| None -> Format.eprintf "unify_tuple_clock None %a@." Clocks.print_ck ck
|
513
|
| Some ck' -> Format.eprintf "unify_tuple_clock (Some %a) %a@." Clocks.print_ck ck' Clocks.print_ck ck);*)
|
514
|
let ck_var = ref ref_ck_opt in
|
515
|
let rec aux ck =
|
516
|
match (repr ck).cdesc with
|
517
|
| Con _
|
518
|
| Cvar _ ->
|
519
|
begin
|
520
|
match !ck_var with
|
521
|
| None ->
|
522
|
ck_var:=Some ck
|
523
|
| Some v ->
|
524
|
(* may fail *)
|
525
|
try_unify ck v loc
|
526
|
end
|
527
|
| Ctuple cl ->
|
528
|
List.iter aux cl
|
529
|
| Carrow _ -> assert false (* should not occur *)
|
530
|
| Ccarrying (_, ck1) ->
|
531
|
aux ck1
|
532
|
| _ -> ()
|
533
|
in aux ck
|
534
|
|
535
|
(* Unifies all the clock variables in the clock type of an imported
|
536
|
node, so that the clock type only uses at most one base clock variable,
|
537
|
that is, the activation clock of the node *)
|
538
|
let unify_imported_clock ref_ck_opt ck loc =
|
539
|
let ck_var = ref ref_ck_opt in
|
540
|
let rec aux ck =
|
541
|
match (repr ck).cdesc with
|
542
|
| Cvar _ ->
|
543
|
begin
|
544
|
match !ck_var with
|
545
|
| None ->
|
546
|
ck_var:=Some ck
|
547
|
| Some v ->
|
548
|
(* cannot fail *)
|
549
|
try_unify ck v loc
|
550
|
end
|
551
|
| Ctuple cl ->
|
552
|
List.iter aux cl
|
553
|
| Carrow (ck1,ck2) ->
|
554
|
aux ck1; aux ck2
|
555
|
| Ccarrying (_, ck1) ->
|
556
|
aux ck1
|
557
|
| Con (ck1, _, _) -> aux ck1
|
558
|
| _ -> ()
|
559
|
in
|
560
|
aux ck
|
561
|
|
562
|
(* Computes the root clock of a tuple or a node clock,
|
563
|
which is not the same as the base clock.
|
564
|
Root clock will be used as the call clock
|
565
|
of a given node instance *)
|
566
|
let compute_root_clock ck =
|
567
|
let root = Clocks.root ck in
|
568
|
let branch = ref None in
|
569
|
let rec aux ck =
|
570
|
match (repr ck).cdesc with
|
571
|
| Ctuple cl ->
|
572
|
List.iter aux cl
|
573
|
| Carrow (ck1,ck2) ->
|
574
|
aux ck1; aux ck2
|
575
|
| _ ->
|
576
|
begin
|
577
|
match !branch with
|
578
|
| None ->
|
579
|
branch := Some (Clocks.branch ck)
|
580
|
| Some br ->
|
581
|
(* cannot fail *)
|
582
|
branch := Some (Clocks.common_prefix br (Clocks.branch ck))
|
583
|
end
|
584
|
in
|
585
|
begin
|
586
|
aux ck;
|
587
|
Clocks.clock_of_root_branch root (Utils.desome !branch)
|
588
|
end
|
589
|
|
590
|
(* Clocks a list of arguments of Lustre builtin operators:
|
591
|
- type each expression, remove carriers of clocks as
|
592
|
carriers may only denote variables, not arbitrary expr.
|
593
|
- try to unify these clocks altogether
|
594
|
*)
|
595
|
let rec clock_standard_args env expr_list =
|
596
|
let ck_list = List.map (fun e -> clock_uncarry (clock_expr env e)) expr_list in
|
597
|
let ck_res = new_var true in
|
598
|
List.iter2 (fun e ck -> try_unify ck ck_res e.expr_loc) expr_list ck_list;
|
599
|
ck_res
|
600
|
|
601
|
(* emulates a subtyping relation between clocks c and (cr : c),
|
602
|
used during node application only *)
|
603
|
and clock_subtyping_arg env ?(sub=true) real_arg formal_clock =
|
604
|
let loc = real_arg.expr_loc in
|
605
|
let real_clock = clock_expr env real_arg in
|
606
|
try_sub_unify sub formal_clock real_clock loc
|
607
|
|
608
|
(* computes clocks for node application *)
|
609
|
and clock_appl env f args clock_reset loc =
|
610
|
let args = expr_list_of_expr args in
|
611
|
if Basic_library.is_internal_fun f && List.exists is_tuple_expr args
|
612
|
then
|
613
|
let args = Utils.transpose_list (List.map expr_list_of_expr args) in
|
614
|
Clocks.clock_of_clock_list (List.map (fun args -> clock_call env f args clock_reset loc) args)
|
615
|
else
|
616
|
clock_call env f args clock_reset loc
|
617
|
|
618
|
and clock_call env f args clock_reset loc =
|
619
|
let cfun = clock_ident false env f loc in
|
620
|
let cins, couts = split_arrow cfun in
|
621
|
let cins = clock_list_of_clock cins in
|
622
|
List.iter2 (clock_subtyping_arg env) args cins;
|
623
|
unify_imported_clock (Some clock_reset) cfun loc;
|
624
|
couts
|
625
|
|
626
|
and clock_ident nocarrier env id loc =
|
627
|
clock_expr ~nocarrier:nocarrier env (expr_of_ident id loc)
|
628
|
|
629
|
and clock_carrier env c loc ce =
|
630
|
let expr_c = expr_of_ident c loc in
|
631
|
let ck = clock_expr ~nocarrier:false env expr_c in
|
632
|
let cr = new_carrier Carry_name (*Carry_const c*) ck.cscoped in
|
633
|
let ckb = new_var true in
|
634
|
let ckcarry = new_ck (Ccarrying (cr, ckb)) ck.cscoped in
|
635
|
try_unify ck ckcarry expr_c.expr_loc;
|
636
|
unify_tuple_clock (Some ckb) ce expr_c.expr_loc;
|
637
|
cr
|
638
|
|
639
|
(** [clock_expr env expr] performs the clock calculus for expression [expr] in
|
640
|
environment [env] *)
|
641
|
and clock_expr ?(nocarrier=true) env expr =
|
642
|
let resulting_ck =
|
643
|
match expr.expr_desc with
|
644
|
| Expr_const cst ->
|
645
|
let ck = new_var true in
|
646
|
expr.expr_clock <- ck;
|
647
|
ck
|
648
|
| Expr_ident v ->
|
649
|
let ckv =
|
650
|
try
|
651
|
Env.lookup_value env v
|
652
|
with Not_found ->
|
653
|
failwith ("Internal error, variable \""^v^"\" not found")
|
654
|
in
|
655
|
let ck = instantiate (ref []) (ref []) ckv in
|
656
|
expr.expr_clock <- ck;
|
657
|
ck
|
658
|
| Expr_array elist ->
|
659
|
let ck = clock_standard_args env elist in
|
660
|
expr.expr_clock <- ck;
|
661
|
ck
|
662
|
| Expr_access (e1, d) ->
|
663
|
(* dimension, being a static value, doesn't need to be clocked *)
|
664
|
let ck = clock_standard_args env [e1] in
|
665
|
expr.expr_clock <- ck;
|
666
|
ck
|
667
|
| Expr_power (e1, d) ->
|
668
|
(* dimension, being a static value, doesn't need to be clocked *)
|
669
|
let ck = clock_standard_args env [e1] in
|
670
|
expr.expr_clock <- ck;
|
671
|
ck
|
672
|
| Expr_tuple elist ->
|
673
|
let ck = new_ck (Ctuple (List.map (clock_expr env) elist)) true in
|
674
|
expr.expr_clock <- ck;
|
675
|
ck
|
676
|
| Expr_ite (c, t, e) ->
|
677
|
let ck_c = clock_standard_args env [c] in
|
678
|
let ck = clock_standard_args env [t; e] in
|
679
|
(* Here, the branches may exhibit a tuple clock, not the condition *)
|
680
|
unify_tuple_clock (Some ck_c) ck expr.expr_loc;
|
681
|
expr.expr_clock <- ck;
|
682
|
ck
|
683
|
| Expr_appl (id, args, r) ->
|
684
|
(try
|
685
|
(* for a modular compilation scheme, all inputs/outputs must share the same clock !
|
686
|
this is also the reset clock !
|
687
|
*)
|
688
|
let cr =
|
689
|
match r with
|
690
|
| None -> new_var true
|
691
|
| Some c -> clock_standard_args env [c] in
|
692
|
let couts = clock_appl env id args (clock_uncarry cr) expr.expr_loc in
|
693
|
expr.expr_clock <- couts;
|
694
|
couts
|
695
|
with exn -> (
|
696
|
Format.eprintf "Current expr: %a@." Printers.pp_expr expr;
|
697
|
raise exn
|
698
|
))
|
699
|
| Expr_fby (e1,e2)
|
700
|
| Expr_arrow (e1,e2) ->
|
701
|
let ck = clock_standard_args env [e1; e2] in
|
702
|
unify_tuple_clock None ck expr.expr_loc;
|
703
|
expr.expr_clock <- ck;
|
704
|
ck
|
705
|
| Expr_pre e -> (* todo : deal with phases as in tail ? *)
|
706
|
let ck = clock_standard_args env [e] in
|
707
|
expr.expr_clock <- ck;
|
708
|
ck
|
709
|
| Expr_when (e,c,l) ->
|
710
|
let ce = clock_standard_args env [e] in
|
711
|
let c_loc = loc_of_cond expr.expr_loc c in
|
712
|
let cr = clock_carrier env c c_loc ce in
|
713
|
let ck = clock_on ce cr l in
|
714
|
let cr' = new_carrier (Carry_const c) ck.cscoped in
|
715
|
let ck' = clock_on ce cr' l in
|
716
|
expr.expr_clock <- ck';
|
717
|
ck
|
718
|
| Expr_merge (c,hl) ->
|
719
|
let cvar = new_var true in
|
720
|
let crvar = new_carrier Carry_name true in
|
721
|
List.iter (fun (t, h) -> let ckh = clock_uncarry (clock_expr env h) in unify_tuple_clock (Some (new_ck (Con (cvar,crvar,t)) true)) ckh h.expr_loc) hl;
|
722
|
let cr = clock_carrier env c expr.expr_loc cvar in
|
723
|
try_unify_carrier cr crvar expr.expr_loc;
|
724
|
let cres = clock_current ((snd (List.hd hl)).expr_clock) in
|
725
|
expr.expr_clock <- cres;
|
726
|
cres
|
727
|
in
|
728
|
Log.report ~level:4 (fun fmt -> Format.fprintf fmt "Clock of expr %a: %a@." Printers.pp_expr expr Clocks.print_ck resulting_ck);
|
729
|
resulting_ck
|
730
|
|
731
|
let clock_of_vlist vars =
|
732
|
let ckl = List.map (fun v -> v.var_clock) vars in
|
733
|
clock_of_clock_list ckl
|
734
|
|
735
|
(** [clock_eq env eq] performs the clock-calculus for equation [eq] in
|
736
|
environment [env] *)
|
737
|
let clock_eq env eq =
|
738
|
let expr_lhs = expr_of_expr_list eq.eq_loc (List.map (fun v -> expr_of_ident v eq.eq_loc) eq.eq_lhs) in
|
739
|
let ck_rhs = clock_expr env eq.eq_rhs in
|
740
|
clock_subtyping_arg env expr_lhs ck_rhs
|
741
|
|
742
|
(* [clock_coreclock cck] returns the clock_expr corresponding to clock
|
743
|
declaration [cck] *)
|
744
|
let clock_coreclock env cck id loc scoped =
|
745
|
match cck.ck_dec_desc with
|
746
|
| Ckdec_any -> new_var scoped
|
747
|
| Ckdec_pclock (n,(a,b)) ->
|
748
|
let ck = new_ck (Pck_const (n,(a,b))) scoped in
|
749
|
if n mod b <> 0 then raise (Error (loc,Invalid_const ck));
|
750
|
ck
|
751
|
| Ckdec_bool cl ->
|
752
|
let temp_env = Env.add_value env id (new_var true) in
|
753
|
(* We just want the id to be present in the environment *)
|
754
|
let dummy_id_expr = expr_of_ident id loc in
|
755
|
let when_expr =
|
756
|
List.fold_left
|
757
|
(fun expr (x,l) ->
|
758
|
{expr_tag = new_tag ();
|
759
|
expr_desc = Expr_when (expr,x,l);
|
760
|
expr_type = Types.new_var ();
|
761
|
expr_clock = new_var scoped;
|
762
|
expr_delay = Delay.new_var ();
|
763
|
expr_loc = loc;
|
764
|
expr_annot = None})
|
765
|
dummy_id_expr cl
|
766
|
in
|
767
|
clock_expr temp_env when_expr
|
768
|
|
769
|
(* Clocks a variable declaration *)
|
770
|
let clock_var_decl scoped env vdecl =
|
771
|
let ck = clock_coreclock env vdecl.var_dec_clock vdecl.var_id vdecl.var_loc scoped in
|
772
|
let ck =
|
773
|
(* WTF ????
|
774
|
if vdecl.var_dec_const
|
775
|
then
|
776
|
(try_generalize ck vdecl.var_loc; ck)
|
777
|
else
|
778
|
*)
|
779
|
if Types.get_clock_base_type vdecl.var_type <> None
|
780
|
then new_ck (Ccarrying ((new_carrier Carry_name scoped),ck)) scoped
|
781
|
else ck in
|
782
|
(if vdecl.var_dec_const
|
783
|
then match vdecl.var_dec_value with
|
784
|
| None -> ()
|
785
|
| Some v ->
|
786
|
begin
|
787
|
let ck_static = clock_expr env v in
|
788
|
try_unify ck ck_static v.expr_loc
|
789
|
end);
|
790
|
try_unify ck vdecl.var_clock vdecl.var_loc;
|
791
|
Env.add_value env vdecl.var_id ck
|
792
|
|
793
|
(* Clocks a variable declaration list *)
|
794
|
let clock_var_decl_list env scoped l =
|
795
|
List.fold_left (clock_var_decl scoped) env l
|
796
|
|
797
|
(** [clock_node env nd] performs the clock-calculus for node [nd] in
|
798
|
environment [env].
|
799
|
Generalization of clocks wrt scopes follows this rule:
|
800
|
- generalize inputs (unscoped).
|
801
|
- generalize outputs. As they are scoped, only clocks coming from inputs
|
802
|
are allowed to be generalized.
|
803
|
- generalize locals. As outputs don't depend on them (checked the step before),
|
804
|
they can be generalized.
|
805
|
*)
|
806
|
let clock_node env loc nd =
|
807
|
(* let is_main = nd.node_id = !Options.main_node in *)
|
808
|
let new_env = clock_var_decl_list env false nd.node_inputs in
|
809
|
let new_env = clock_var_decl_list new_env true nd.node_outputs in
|
810
|
let new_env = clock_var_decl_list new_env true nd.node_locals in
|
811
|
List.iter (clock_eq new_env) (get_node_eqs nd);
|
812
|
let ck_ins = clock_of_vlist nd.node_inputs in
|
813
|
let ck_outs = clock_of_vlist nd.node_outputs in
|
814
|
let ck_node = new_ck (Carrow (ck_ins,ck_outs)) false in
|
815
|
unify_imported_clock None ck_node loc;
|
816
|
Log.report ~level:3 (fun fmt -> print_ck fmt ck_node);
|
817
|
(* Local variables may contain first-order carrier variables that should be generalized.
|
818
|
That's not the case for types. *)
|
819
|
try_generalize ck_node loc;
|
820
|
(*
|
821
|
List.iter (fun vdecl -> try_generalize vdecl.var_clock vdecl.var_loc) nd.node_inputs;
|
822
|
List.iter (fun vdecl -> try_generalize vdecl.var_clock vdecl.var_loc) nd.node_outputs;*)
|
823
|
(*List.iter (fun vdecl -> try_generalize vdecl.var_clock vdecl.var_loc) nd.node_locals;*)
|
824
|
(* TODO : Xavier pourquoi ai je cette erreur ? *)
|
825
|
(* if (is_main && is_polymorphic ck_node) then
|
826
|
raise (Error (loc,(Cannot_be_polymorphic ck_node)));
|
827
|
*)
|
828
|
Log.report ~level:3 (fun fmt -> print_ck fmt ck_node);
|
829
|
nd.node_clock <- ck_node;
|
830
|
Env.add_value env nd.node_id ck_node
|
831
|
|
832
|
|
833
|
let check_imported_pclocks loc ck_node =
|
834
|
let pck = ref None in
|
835
|
let rec aux ck =
|
836
|
match ck.cdesc with
|
837
|
| Carrow (ck1,ck2) -> aux ck1; aux ck2
|
838
|
| Ctuple cl -> List.iter aux cl
|
839
|
| Con (ck',_,_) -> aux ck'
|
840
|
| Pck_up (_,_) | Pck_down (_,_) | Pck_phase (_,_) ->
|
841
|
raise (Error (loc, (Invalid_imported_clock ck_node)))
|
842
|
| Pck_const (n,p) ->
|
843
|
begin
|
844
|
match !pck with
|
845
|
| None -> pck := Some (n,p)
|
846
|
| Some (n',p') ->
|
847
|
if (n,p) <> (n',p') then
|
848
|
raise (Error (loc, (Invalid_imported_clock ck_node)))
|
849
|
end
|
850
|
| Clink ck' -> aux ck'
|
851
|
| Ccarrying (_,ck') -> aux ck'
|
852
|
| Cvar _ | Cunivar _ ->
|
853
|
match !pck with
|
854
|
| None -> ()
|
855
|
| Some (_,_) ->
|
856
|
raise (Error (loc, (Invalid_imported_clock ck_node)))
|
857
|
in
|
858
|
aux ck_node
|
859
|
|
860
|
let clock_imported_node env loc nd =
|
861
|
let new_env = clock_var_decl_list env false nd.nodei_inputs in
|
862
|
ignore(clock_var_decl_list new_env false nd.nodei_outputs);
|
863
|
let ck_ins = clock_of_vlist nd.nodei_inputs in
|
864
|
let ck_outs = clock_of_vlist nd.nodei_outputs in
|
865
|
let ck_node = new_ck (Carrow (ck_ins,ck_outs)) false in
|
866
|
unify_imported_clock None ck_node loc;
|
867
|
check_imported_pclocks loc ck_node;
|
868
|
try_generalize ck_node loc;
|
869
|
nd.nodei_clock <- ck_node;
|
870
|
Env.add_value env nd.nodei_id ck_node
|
871
|
|
872
|
let clock_top_const env cdecl=
|
873
|
let ck = new_var false in
|
874
|
try_generalize ck cdecl.const_loc;
|
875
|
Env.add_value env cdecl.const_id ck
|
876
|
|
877
|
let clock_top_consts env clist =
|
878
|
List.fold_left clock_top_const env clist
|
879
|
|
880
|
let rec clock_top_decl env decl =
|
881
|
match decl.top_decl_desc with
|
882
|
| Node nd ->
|
883
|
clock_node env decl.top_decl_loc nd
|
884
|
| ImportedNode nd ->
|
885
|
clock_imported_node env decl.top_decl_loc nd
|
886
|
| Const c ->
|
887
|
clock_top_const env c
|
888
|
| TypeDef _ -> List.fold_left clock_top_decl env (consts_of_enum_type decl)
|
889
|
| Open _ -> env
|
890
|
|
891
|
let clock_prog env decls =
|
892
|
List.fold_left clock_top_decl env decls
|
893
|
|
894
|
(* Once the Lustre program is fully clocked,
|
895
|
we must get back to the original description of clocks,
|
896
|
with constant parameters, instead of unifiable internal variables. *)
|
897
|
|
898
|
(* The following functions aims at 'unevaluating' carriers occuring in clock expressions,
|
899
|
i.e. replacing unifiable second_order variables with the original carrier names.
|
900
|
Once restored in this formulation, clocks may be meaningfully printed.
|
901
|
*)
|
902
|
let uneval_vdecl_generics vdecl =
|
903
|
(*Format.eprintf "Clock_calculus.uneval_vdecl_generics %a@." Printers.pp_node_var vdecl;*)
|
904
|
if Types.get_clock_base_type vdecl.var_type <> None
|
905
|
then
|
906
|
match get_carrier_name vdecl.var_clock with
|
907
|
| None -> (Format.eprintf "internal error: %a@." print_ck vdecl.var_clock; assert false)
|
908
|
| Some cr -> Clocks.uneval vdecl.var_id cr
|
909
|
|
910
|
let uneval_node_generics vdecls =
|
911
|
List.iter uneval_vdecl_generics vdecls
|
912
|
|
913
|
let uneval_top_generics decl =
|
914
|
match decl.top_decl_desc with
|
915
|
| Node nd ->
|
916
|
(* A node could contain first-order carrier variable in local vars. This is not the case for types. *)
|
917
|
uneval_node_generics (nd.node_inputs @ nd.node_locals @ nd.node_outputs)
|
918
|
| ImportedNode nd ->
|
919
|
uneval_node_generics (nd.nodei_inputs @ nd.nodei_outputs)
|
920
|
| Const _
|
921
|
| Open _
|
922
|
| TypeDef _ -> ()
|
923
|
|
924
|
let uneval_prog_generics prog =
|
925
|
List.iter uneval_top_generics prog
|
926
|
|
927
|
let check_env_compat header declared computed =
|
928
|
uneval_prog_generics header;
|
929
|
Env.iter declared (fun k decl_clock_k ->
|
930
|
let computed_c = instantiate (ref []) (ref []) (Env.lookup_value computed k) in
|
931
|
try_semi_unify decl_clock_k computed_c Location.dummy_loc
|
932
|
)
|
933
|
(* Local Variables: *)
|
934
|
(* compile-command:"make -C .." *)
|
935
|
(* End: *)
|