niti: write fatal error on stderr
[nit.git] / src / coloring.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Graph coloring tools
16 module coloring
17
18 import rapid_type_analysis # for type coloration
19
20 abstract class AbstractColoring[E: Object]
21
22 private var sorter: AbstractSorter[E]
23 private var reverse_sorter: AbstractSorter[E]
24
25 private var core: OrderedSet[E] = new OrderedSet[E]
26 private var crown: OrderedSet[E] = new OrderedSet[E]
27 private var border: OrderedSet[E] = new OrderedSet[E]
28
29 private var coloration_result: Map[E, Int] = new HashMap[E, Int]
30 private var conflicts_graph_cache: nullable HashMap[E, Set[E]]
31
32 init(sorter: AbstractSorter[E], reverse_sorter: AbstractSorter[E]) do
33 self.sorter = sorter
34 self.reverse_sorter = reverse_sorter
35 end
36
37 fun colorize(elements: Collection[E]): Map[E, Int] do
38 # tag each element as part of group core, crown or border
39 for e in elements do
40 tag_element(e)
41 end
42
43 #print "core: {core.join(", ")}"
44 #print "border: {border.join(", ")}"
45 #print "crown: {crown.join(", ")}"
46
47 # sort by reverse linearization order
48 reverse_sorter.sort(core)
49 reverse_sorter.sort(border)
50 reverse_sorter.sort(crown)
51
52 #print "conflicts"
53 #for k, v in conflicts_graph do
54 # if k isa MType then
55 # print "{k}: {v.join(", ")}"
56 # end
57 #end
58
59 # colorize graph
60 colorize_elements(core)
61 colorize_elements(border)
62 colorize_elements(crown)
63
64 return coloration_result
65 end
66
67 # Colorize a collection of elements
68 private fun colorize_elements(elements: Collection[E]) do
69 var min_color = 0
70
71 for element in elements do
72 var color = min_color
73 while not self.is_color_free(element, color) do
74 color += 1
75 end
76 coloration_result[element] = color
77 color = min_color
78 end
79 end
80
81 # Check if a related element to the element already use the color
82 private fun is_color_free(element: E, color: Int): Bool do
83 if conflicts_graph.has_key(element) then
84 for st in conflicts_graph[element] do
85 if coloration_result.has_key(st) and coloration_result[st] == color then return false
86 end
87 end
88 for st in self.super_elements(element) do
89 if coloration_result.has_key(st) and coloration_result[st] == color then return false
90 end
91 return true
92 end
93
94 # Tag element as core, crown or border
95 private fun tag_element(element: E) do
96 # Check if sub elements are all in single inheritance
97 var all_subelements_si = true
98 for subelem in self.sub_elements(element) do
99 if self.is_element_mi(subelem) then
100 all_subelements_si = false
101 break
102 end
103 end
104
105 # Tag as core, crown or border
106 if self.is_element_mi(element) then
107 core.add_all(self.super_elements(element))
108 core.add(element)
109 if all_subelements_si then
110 border.add(element)
111 end
112 else if not all_subelements_si then
113 core.add_all(self.super_elements(element))
114 core.add(element)
115 else
116 crown.add(element)
117 end
118 end
119
120 # Conflicts graph of elements hierarchy (two types are in conflict if they have common subelements)
121 private fun conflicts_graph: Map[E, Set[E]] do
122 if self.conflicts_graph_cache == null then
123 self.conflicts_graph_cache = new HashMap[E, HashSet[E]]
124 for t in self.core do
125 for i in self.linear_extension(t) do
126 if t == i then continue
127
128 var lin_i = self.linear_extension(i)
129
130 for j in self.linear_extension(t) do
131 if i == j or j == t then continue
132 var lin_j = self.linear_extension(j)
133
134 var d_ij = lin_i - lin_j
135 var d_ji = lin_j - lin_i
136
137 for ed1 in d_ij do
138 if not conflicts_graph_cache.has_key(ed1) then conflicts_graph_cache[ed1] = new HashSet[E]
139 # add ed1 x ed2 to conflicts graph
140 for ed2 in d_ji do conflicts_graph_cache[ed1].add(ed2)
141 end
142 for ed1 in d_ij do
143 if not conflicts_graph_cache.has_key(ed1) then conflicts_graph_cache[ed1] = new HashSet[E]
144 # add ed1 x ed2 to conflicts graph
145 for ed2 in d_ji do conflicts_graph_cache[ed1].add(ed2)
146 end
147 end
148 end
149 end
150 end
151 return conflicts_graph_cache.as(not null)
152 end
153
154 # cache for linear_extensions
155 private var linear_extensions_cache: Map[E, OrderedSet[E]] = new HashMap[E, OrderedSet[E]]
156
157 # Return a linear_extension of super_elements of the element
158 private fun linear_extension(element: E): OrderedSet[E] do
159 if not self.linear_extensions_cache.has_key(element) then
160 var lin = new OrderedSet[E]
161 lin.add(element)
162 lin.add_all(self.super_elements(element))
163 self.sorter.sort(lin)
164 self.linear_extensions_cache[element] = lin
165 end
166 return self.linear_extensions_cache[element]
167 end
168
169 # Return all super elements (directs and indirects) of an element
170 private fun super_elements(element: E): Collection[E] is abstract
171
172 # Return all sub elements (directs and indirects) of an element
173 private fun sub_elements(element: E): Collection[E] is abstract
174
175 # Is the element in multiple inheritance ?
176 private fun is_element_mi(element: E): Bool is abstract
177 end
178
179 # MClassType coloring
180 class TypeColoring
181 super AbstractColoring[MClassType]
182
183 type T: MClassType
184
185 private var mmodule: MModule
186 private var mtypes: Set[MClassType] = new HashSet[MClassType]
187
188 # caches
189 private var super_elements_cache: Map[T, Set[T]] = new HashMap[T, Set[T]]
190 private var sub_elements_cache: Map[T, Set[T]] = new HashMap[T, Set[T]]
191
192 init(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis) do
193 super(new TypeSorter(mainmodule), new ReverseTypeSorter(mainmodule))
194 self.mmodule = mainmodule
195 self.mtypes.add_all(runtime_type_analysis.live_types)
196 self.mtypes.add_all(runtime_type_analysis.live_cast_types)
197 end
198
199 # Build type tables
200 private fun build_type_tables(mtypes: Set[T], colors: Map[T, Int]): Map[T, Array[nullable T]] do
201 var tables = new HashMap[T, Array[nullable T]]
202
203 for mtype in mtypes do
204 var table = new Array[nullable T]
205 var supers = new HashSet[T]
206 supers.add_all(self.super_elements(mtype))
207 supers.add(mtype)
208 for sup in supers do
209 var color = colors[sup]
210 if table.length <= color then
211 for i in [table.length .. color[ do
212 table[i] = null
213 end
214 end
215 table[color] = sup
216 end
217 tables[mtype] = table
218 end
219 return tables
220 end
221
222 redef fun super_elements(element) do
223 if not self.super_elements_cache.has_key(element) then
224 var supers = new HashSet[T]
225 for mtype in self.mtypes do
226 if element == mtype then continue
227 if element.is_subtype(self.mmodule, null, mtype) then
228 supers.add(mtype)
229 end
230 end
231 self.super_elements_cache[element] = supers
232 end
233 return self.super_elements_cache[element]
234 end
235
236 # Return all direct super elements of an element
237 redef fun is_element_mi(element) do
238 return self.mmodule.flatten_mclass_hierarchy[element.mclass].direct_greaters.length > 1
239 end
240
241 # Return all sub elements (directs and indirects) of an element
242 redef fun sub_elements(element) do
243 if not self.sub_elements_cache.has_key(element) then
244 var subs = new HashSet[T]
245 for mtype in self.mtypes do
246 if element == mtype then continue
247 if mtype.is_subtype(self.mmodule, null, element) then
248 subs.add(mtype)
249 end
250 end
251 self.sub_elements_cache[element] = subs
252 end
253 return self.sub_elements_cache[element]
254 end
255 end
256
257 # A sorter for linearize list of types
258 private class TypeSorter
259 super AbstractSorter[MClassType]
260
261 private var mmodule: MModule
262
263 init(mmodule: MModule) do self.mmodule = mmodule
264
265 redef fun compare(a, b) do
266 if a == b then
267 return 0
268 else if a.is_subtype(self.mmodule, null, b) then
269 return -1
270 end
271 return 1
272 end
273 end
274
275 # A sorter for reverse linearization
276 private class ReverseTypeSorter
277 super TypeSorter
278
279 redef fun compare(a, b) do
280 if a == b then
281 return 0
282 else if a.is_subtype(self.mmodule, null, b) then
283 return 1
284 end
285 return -1
286 end
287 end
288
289 # MClass coloring
290 class ClassColoring
291 super AbstractColoring[MClass]
292
293 type T: MClass
294
295 private var mmodule: MModule
296
297 # caches
298 private var super_elements_cache: Map[T, Set[T]] = new HashMap[T, Set[T]]
299 private var parent_elements_cache: Map[T, Set[T]] = new HashMap[T, Set[T]]
300 private var sub_elements_cache: Map[T, Set[T]] = new HashMap[T, Set[T]]
301
302 init(mainmodule: MModule) do
303 super(new ClassSorter(mainmodule), new ReverseClassSorter(mainmodule))
304 self.mmodule = mainmodule
305 end
306
307 # Build type tables
308 private fun build_type_tables(mclasses: Array[T], colors: Map[T, Int]): Map[T, Array[nullable T]] do
309 var tables = new HashMap[T, Array[nullable T]]
310
311 for mclasse in mclasses do
312 var table = new Array[nullable T]
313 var supers = new HashSet[T]
314 supers.add_all(self.super_elements(mclasse))
315 supers.add(mclasse)
316 for sup in supers do
317 var color = colors[sup]
318 if table.length <= color then
319 for i in [table.length .. color[ do
320 table[i] = null
321 end
322 end
323 table[color] = sup
324 end
325 tables[mclasse] = table
326 end
327 return tables
328 end
329
330 redef fun super_elements(element) do
331 if not self.super_elements_cache.has_key(element) then
332 var supers = new HashSet[T]
333 if self.mmodule.flatten_mclass_hierarchy.has(element) then
334 for sup in self.mmodule.flatten_mclass_hierarchy[element].greaters do
335 if element == sup then continue
336 supers.add(sup)
337 end
338 end
339 self.super_elements_cache[element] = supers
340 end
341 return self.super_elements_cache[element]
342 end
343
344 private fun parent_elements(element: T): Set[T] do
345 if not self.parent_elements_cache.has_key(element) then
346 var parents = new HashSet[T]
347 if self.mmodule.flatten_mclass_hierarchy.has(element) then
348 for parent in self.mmodule.flatten_mclass_hierarchy[element].direct_greaters do
349 if element == parent then continue
350 parents.add(parent)
351 end
352 end
353 self.parent_elements_cache[element] = parents
354 end
355 return self.parent_elements_cache[element]
356 end
357
358 # Return all sub elements (directs and indirects) of an element
359 redef fun sub_elements(element) do
360 if not self.sub_elements_cache.has_key(element) then
361 var subs = new HashSet[T]
362 if self.mmodule.flatten_mclass_hierarchy.has(element) then
363 for sub in self.mmodule.flatten_mclass_hierarchy[element].smallers do
364 subs.add(sub)
365 end
366 end
367 self.sub_elements_cache[element] = subs
368 end
369 return self.sub_elements_cache[element]
370 end
371
372 # Return all direct super elements of an element
373 redef fun is_element_mi(element) do
374 if not self.mmodule.flatten_mclass_hierarchy.has(element) then return false
375 return self.mmodule.flatten_mclass_hierarchy[element].direct_greaters.length > 1
376 end
377 end
378
379 # A sorter for linearize list of classes
380 private class ClassSorter
381 super AbstractSorter[MClass]
382
383 var mmodule: MModule
384
385 redef fun compare(a, b) do
386 if a == b then
387 return 0
388 else if self.mmodule.flatten_mclass_hierarchy.has(a) and self.mmodule.flatten_mclass_hierarchy[a].greaters.has(b) then
389 return -1
390 end
391 return 1
392 end
393 end
394
395 # A sorter for reverse linearization
396 private class ReverseClassSorter
397 super AbstractSorter[MClass]
398
399 var mmodule: MModule
400
401 redef fun compare(a, b) do
402 if a == b then
403 return 0
404 else if self.mmodule.flatten_mclass_hierarchy.has(a) and self.mmodule.flatten_mclass_hierarchy[a].greaters.has(b) then
405 return 1
406 end
407 return -1
408 end
409 end
410
411 # MProperty coloring
412 class PropertyColoring
413
414 type MPROP: MProperty
415 type MPROPDEF: MPropDef
416
417 private var class_coloring: ClassColoring
418 private var coloration_result: Map[MPROP, Int] = new HashMap[MPROP, Int]
419
420 init(class_coloring: ClassColoring) do
421 self.class_coloring = class_coloring
422 end
423
424 private fun colorize: Map[MPROP, Int] do
425 colorize_core_properties
426 colorize_crown_properties
427 return self.coloration_result
428 end
429
430 private fun build_property_tables: Map[MClass, Array[nullable MPROPDEF]] do
431 var tables = new HashMap[MClass, Array[nullable MPROPDEF]]
432
433 for mclass in self.class_coloring.coloration_result.keys do
434 var table = new Array[nullable MPROPDEF]
435 # first, fill table from parents by reverse linearization order
436 var parents = new OrderedSet[MClass]
437 parents.add_all(self.class_coloring.super_elements(mclass))
438 self.class_coloring.reverse_sorter.sort(parents)
439 for parent in parents do
440 for mproperty in self.properties(parent) do
441 var color = self.coloration_result[mproperty]
442 if table.length <= color then
443 for i in [table.length .. color[ do
444 table[i] = null
445 end
446 end
447 for mpropdef in mproperty.mpropdefs do
448 if mpropdef.mclassdef.mclass == parent then
449 table[color] = mpropdef
450 end
451 end
452 end
453 end
454
455 # then override with local properties
456 for mproperty in self.properties(mclass) do
457 var color = self.coloration_result[mproperty]
458 if table.length <= color then
459 for i in [table.length .. color[ do
460 table[i] = null
461 end
462 end
463 for mpropdef in mproperty.mpropdefs do
464 if mpropdef.mclassdef.mclass == mclass then
465 table[color] = mpropdef
466 end
467 end
468 end
469 tables[mclass] = table
470 end
471 return tables
472 end
473
474 # Colorize properties of the core hierarchy
475 private fun colorize_core_properties do
476 var mclasses = self.class_coloring.core
477 var min_color = 0
478
479 for mclass in mclasses do
480 var color = min_color
481
482 # if the class is root, get the minimal color
483 if self.class_coloring.parent_elements(mclass).length == 0 then
484 colorize_elements(self.properties(mclass), color)
485 else
486 # check last color used by parents
487 color = max_color(color, self.class_coloring.parent_elements(mclass))
488 # check max color used in conflicts
489 if self.class_coloring.conflicts_graph.has_key(mclass) then
490 color = max_color(color, self.class_coloring.conflicts_graph[mclass])
491 end
492
493 # colorize
494 colorize_elements(self.properties(mclass), color)
495 end
496 end
497 end
498
499 # Colorize properties of the crown hierarchy
500 private fun colorize_crown_properties do
501 for mclass in self.class_coloring.crown do
502 colorize_elements(self.properties(mclass), max_color(0, self.class_coloring.parent_elements(mclass)))
503 end
504 end
505
506 # Colorize a collection of properties given a starting color
507 private fun colorize_elements(elements: Collection[MPROP], start_color: Int) do
508 for element in elements do
509 if self.coloration_result.has_key(element) then continue
510 self.coloration_result[element] = start_color
511 start_color += 1
512 end
513 end
514
515 private fun max_color(min_color: Int, mclasses: Collection[MClass]): Int do
516 var max_color = min_color
517
518 for mclass in mclasses do
519 for mproperty in self.properties(mclass) do
520 var color = min_color
521 if self.coloration_result.has_key(mproperty) then
522 color = self.coloration_result[mproperty]
523 if color >= max_color then max_color = color + 1
524 end
525 end
526 end
527 return max_color
528 end
529
530 # properties cache
531 private var properties_cache: Map[MClass, Set[MPROP]] = new HashMap[MClass, Set[MPROP]]
532
533 # All 'mproperties' associated to all 'mclassdefs' of the class
534 private fun properties(mclass: MClass): Set[MPROP] do
535 if not self.properties_cache.has_key(mclass) then
536 var properties = new HashSet[MPROP]
537 var parents = self.class_coloring.super_elements(mclass)
538 for parent in parents do
539 properties.add_all(self.properties(parent))
540 end
541
542 for mclassdef in mclass.mclassdefs do
543 for mpropdef in mclassdef.mpropdefs do
544 var mproperty = mpropdef.mproperty
545 if mproperty isa MPROP then
546 properties.add(mproperty)
547 end
548 end
549 end
550 self.properties_cache[mclass] = properties
551 end
552 return properties_cache[mclass]
553 end
554 end
555
556 # MMethod coloring
557 class MethodColoring
558 super PropertyColoring
559
560 redef type MPROP: MMethod
561 redef type MPROPDEF: MMethodDef
562 end
563
564 # MAttribute coloring
565 class AttributeColoring
566 super PropertyColoring
567
568 redef type MPROP: MAttribute
569 redef type MPROPDEF: MAttributeDef
570 end
571
572 # MParameterType coloring
573 class FTColoring
574 private var class_coloring: ClassColoring
575 private var coloration_result: Map[MParameterType, Int] = new HashMap[MParameterType, Int]
576
577 init(class_coloring: ClassColoring) do
578 self.class_coloring = class_coloring
579 end
580
581 private fun colorize: Map[MParameterType, Int] do
582 colorize_core_properties
583 colorize_crown_properties
584 return self.coloration_result
585 end
586
587 # Colorize properties of the core hierarchy
588 private fun colorize_core_properties do
589 var mclasses = self.class_coloring.core
590 var min_color = 0
591
592 for mclass in mclasses do
593 var color = min_color
594
595 # if the class is root, get the minimal color
596 if self.class_coloring.parent_elements(mclass).length == 0 then
597 colorize_elements(self.fts(mclass), color)
598 else
599 # check last color used by parents
600 color = max_color(color, self.class_coloring.parent_elements(mclass))
601 # check max color used in conflicts
602 if self.class_coloring.conflicts_graph.has_key(mclass) then
603 color = max_color(color, self.class_coloring.conflicts_graph[mclass])
604 end
605 # colorize
606 colorize_elements(self.fts(mclass), color)
607 end
608 end
609 end
610
611 # Colorize properties of the crown hierarchy
612 private fun colorize_crown_properties do
613 for mclass in self.class_coloring.crown do
614 colorize_elements(self.fts(mclass), max_color(0, self.class_coloring.parent_elements(mclass)))
615 end
616 end
617
618 # Colorize a collection of properties given a starting color
619 private fun colorize_elements(elements: Collection[MParameterType], start_color: Int) do
620 for element in elements do
621 if self.coloration_result.has_key(element) then continue
622 self.coloration_result[element] = start_color
623 start_color += 1
624 end
625 end
626
627 private fun max_color(min_color: Int, mclasses: Collection[MClass]): Int do
628 var max_color = min_color
629
630 for mclass in mclasses do
631 for ft in self.fts(mclass) do
632 var color = min_color
633 if self.coloration_result.has_key(ft) then
634 color = self.coloration_result[ft]
635 if color >= max_color then max_color = color + 1
636 end
637 end
638 end
639 return max_color
640 end
641
642 # fts cache
643 private var fts_cache: Map[MClass, Set[MParameterType]] = new HashMap[MClass, Set[MParameterType]]
644
645 private fun fts(mclass: MClass): Set[MParameterType] do
646 if not self.fts_cache.has_key(mclass) then
647 var fts = new HashSet[MParameterType]
648 var mclass_type = mclass.mclass_type
649 if mclass_type isa MGenericType then
650 for ft in mclass_type.arguments do
651 fts.add(ft.as(MParameterType))
652 end
653 end
654 self.fts_cache[mclass] = fts
655 end
656 return fts_cache[mclass]
657 end
658
659 private fun build_ft_tables: Map[MClass, Array[nullable MParameterType]] do
660 var tables = new HashMap[MClass, Array[nullable MParameterType]]
661
662 for mclass in self.class_coloring.coloration_result.keys do
663 var table = new Array[nullable MParameterType]
664
665 # first, fill table from parents
666 for parent in self.class_coloring.super_elements(mclass) do
667 for ft in self.fts(parent) do
668 var color = self.coloration_result[ft]
669 if table.length <= color then
670 for i in [table.length .. color[ do
671 table[i] = null
672 end
673 end
674 table[color] = ft
675 end
676 end
677
678 # then override with local properties
679 for ft in self.fts(mclass) do
680 var color = self.coloration_result[ft]
681 if table.length <= color then
682 for i in [table.length .. color[ do
683 table[i] = null
684 end
685 end
686 table[color] = ft
687 end
688 tables[mclass] = table
689 end
690 return tables
691 end
692 end
693
694 # Utils
695
696 # An ordered set
697 private class OrderedSet[E]
698 super Array[E]
699
700 redef fun add(e) do
701 if not self.has(e) then
702 super(e)
703 end
704 end
705
706 # Return a new OrderedSet with the elements only contened in 'self' and not in 'o'
707 fun -(o: OrderedSet[E]): OrderedSet[E] do
708 var res = new OrderedSet[E]
709 for e in self do if not o.has(e) then res.add(e)
710 return res
711 end
712 end