layout_builders: simplified ResolutionLayoutBuilder (for future refactoring)
[nit.git] / src / layout_builders.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 # Table layout builders
16 module layout_builders
17
18 import abstract_compiler
19
20 # Layouts
21
22 class Layout[E: Object]
23 # Ids or each element
24 var ids: Map[E, Int] = new HashMap[E, Int]
25 # Fixed positions of each element in all tables
26 var pos: Map[E, Int] = new HashMap[E, Int]
27 end
28
29 class PHLayout[HOLDER: Object, E: Object]
30 super Layout[E]
31 # Masks used by hash function
32 var masks: Map[HOLDER, Int] = new HashMap[HOLDER, Int]
33 # Positions of each element for each tables
34 var hashes: Map[HOLDER, Map[E, Int]] = new HashMap[HOLDER, Map[E, Int]]
35 end
36
37 class PropertyLayout[E: Object]
38 # Fixed positions of each element in all tables
39 var pos: Map[E, Int] = new HashMap[E, Int]
40 end
41
42 # Builders
43
44 interface TypingLayoutBuilder[E: Object]
45 # Build typing table layout
46 fun build_layout(elements: Set[E]): Layout[E] is abstract
47 end
48
49 abstract class PropertyLayoutBuilder[E: MProperty]
50
51 type LAYOUT: PropertyLayout[E]
52
53 # Compute properties ids and position
54 fun build_layout(mclasses: Set[MClass]): LAYOUT is abstract
55 end
56
57 # Layout builder for MProperty using Coloring (CL)
58 class CLPropertyLayoutBuilder[E: MProperty]
59 super PropertyLayoutBuilder[E]
60
61 private var colorer: MPropertyColorer[E]
62
63 init(colorer: MPropertyColorer[E]) do
64 self.colorer = colorer
65 end
66
67 # Compute mclasses ids and position using BM
68 redef fun build_layout(mclasses) do
69 var result = new PropertyLayout[E]
70 result.pos = self.colorer.colorize(mclasses)
71 return result
72 end
73 end
74
75 # Layout builder for MProperty using Perfect Hashing (PH)
76 # TODO implement this class without sublcassing CL builder
77 class PHPropertyLayoutBuilder[E: MProperty]
78 super CLPropertyLayoutBuilder[E]
79 end
80
81 abstract class ResolutionLayoutBuilder
82 type LAYOUT: Layout[MType]
83 init do end
84 fun build_layout(elements: Map[MClassType, Set[MType]]): LAYOUT is abstract
85 end
86
87 # Layout builder for MClass using Binary Matrix (BM)
88 class BMResolutionLayoutBuilder
89 super ResolutionLayoutBuilder
90
91 init do super
92
93 # Compute resolved types position using BM
94 redef fun build_layout(elements) do
95 var result = new Layout[MType]
96 result.ids = self.compute_ids(elements)
97 result.pos = result.ids
98 return result
99 end
100
101 fun compute_ids(elements: Map[MClassType, Set[MType]]): Map[MType, Int] do
102 var ids = new HashMap[MType, Int]
103 var color = 0
104 for mclasstype, mclasstypes in elements do
105 for element in mclasstypes do
106 if ids.has_key(element) then continue
107 ids[element] = color
108 color += 1
109 end
110 end
111 return ids
112 end
113 end
114
115 # Layout builder for resolution tables using Coloring (CL)
116 class CLResolutionLayoutBuilder
117 super ResolutionLayoutBuilder
118
119 private var colorer: ResolutionColorer = new ResolutionColorer
120
121 init do super
122
123 # Compute resolved types colors
124 redef fun build_layout(elements) do
125 var result = new Layout[MType]
126 result.ids = self.compute_ids(elements)
127 result.pos = self.colorer.colorize(elements)
128 return result
129 end
130
131 fun compute_ids(elements: Map[MClassType, Set[MType]]): Map[MType, Int] do
132 var ids = new HashMap[MType, Int]
133 var color = 0
134 for mclasstype, mclasstypes in elements do
135 for element in mclasstypes do
136 if ids.has_key(element) then continue
137 ids[element] = color
138 color += 1
139 end
140 end
141 return ids
142 end
143 end
144
145 # Layout builder for resolution table using Perfect Hashing (PH)
146 class PHResolutionLayoutBuilder
147 super ResolutionLayoutBuilder
148
149 redef type LAYOUT: PHLayout[MClassType, MType]
150
151 private var hasher: PerfectHasher[MClassType, MType]
152
153 init(operator: PHOperator) do self.hasher = new PerfectHasher[MClassType, MType](operator)
154
155 # Compute resolved types masks and hashes
156 redef fun build_layout(elements) do
157 var result = new PHLayout[MClassType, MType]
158 result.ids = self.compute_ids(elements)
159 result.pos = result.ids
160 result.masks = self.hasher.compute_masks(elements, result.ids)
161 result.hashes = self.hasher.compute_hashes(elements, result.ids, result.masks)
162 return result
163 end
164
165 fun compute_ids(elements: Map[MClassType, Set[MType]]): Map[MType, Int] do
166 var ids = new HashMap[MType, Int]
167 var color = 1
168 for mclasstype, mclasstypes in elements do
169 for element in mclasstypes do
170 if ids.has_key(element) then continue
171 ids[element] = color
172 color += 1
173 end
174 end
175 return ids
176 end
177 end
178
179 # Matrice computers
180
181 abstract class TypingBMizer[E: Object]
182 super TypingLayoutBuilder[E]
183
184 var mmodule: MModule
185
186 init(mmodule: MModule) do
187 self.mmodule = mmodule
188 end
189
190 # Compute mtypes ids and position using BM
191 redef fun build_layout(elements: Set[E]): Layout[E] do
192 var result = new Layout[E]
193 var ids = new HashMap[E, Int]
194 var lin = self.reverse_linearize(elements)
195 for element in lin do
196 ids[element] = ids.length
197 end
198 result.ids = ids
199 result.pos = ids
200 return result
201 end
202
203 private fun reverse_linearize(elements: Set[E]): Array[E] is abstract
204 end
205
206 class MTypeBMizer
207 super TypingBMizer[MType]
208
209 init(mmodule: MModule) do super(mmodule)
210
211 redef fun reverse_linearize(elements) do
212 return self.mmodule.reverse_linearize_mtypes(elements)
213 end
214 end
215
216 class MClassBMizer
217 super TypingBMizer[MClass]
218
219 init(mmodule: MModule) do super(mmodule)
220
221 redef fun reverse_linearize(elements) do
222 return self.mmodule.reverse_linearize_mclasses(elements)
223 end
224 end
225
226 # Colorers
227
228 abstract class TypingColorer[E: Object]
229 super TypingLayoutBuilder[E]
230
231 private var core: Set[E] = new HashSet[E]
232 private var crown: Set[E] = new HashSet[E]
233 private var border: Set[E] = new HashSet[E]
234
235 private var coloration_result: Map[E, Int] = new HashMap[E, Int]
236
237 init do end
238
239 # Compute the layout with coloring
240 redef fun build_layout(elements: Set[E]): Layout[E] do
241 var result = new Layout[E]
242 result.ids = compute_ids(elements)
243 result.pos = colorize(elements)
244 return result
245 end
246
247 private fun compute_ids(elements: Set[E]): Map[E, Int] do
248 var ids = new HashMap[E, Int]
249 var lin = reverse_linearize(elements)
250 for element in lin do
251 ids[element] = ids.length
252 end
253 return ids
254 end
255
256 private fun colorize(elements: Set[E]): Map[E, Int] do
257 tag_elements(elements)
258 build_conflicts_graph(elements)
259 colorize_elements(core)
260 colorize_elements(border)
261 colorize_elements(crown)
262 return coloration_result
263 end
264
265 # Colorize a collection of elements
266 private fun colorize_elements(elements: Set[E]) do
267 var min_color = 0
268
269 var lin = reverse_linearize(elements)
270 for element in lin do
271 var color = min_color
272 while not self.is_color_free(element, elements, color) do
273 color += 1
274 end
275 coloration_result[element] = color
276 color = min_color
277 end
278 end
279
280 # Check if a related element to the element already use the color
281 private fun is_color_free(element: E, elements: Set[E], color: Int): Bool do
282 if conflicts_graph.has_key(element) then
283 for st in conflicts_graph[element] do
284 if coloration_result.has_key(st) and coloration_result[st] == color then return false
285 end
286 end
287 for st in self.super_elements(element, elements) do
288 if coloration_result.has_key(st) and coloration_result[st] == color then return false
289 end
290 return true
291 end
292
293 # Tag elements as core, crown or border
294 private fun tag_elements(elements: Set[E]) do
295 for element in elements do
296 # Check if sub elements are all in single inheritance
297 var all_subelements_si = true
298 for subelem in self.sub_elements(element, elements) do
299 if self.is_element_mi(subelem, elements) then
300 all_subelements_si = false
301 break
302 end
303 end
304
305 # Tag as core, crown or border
306 if self.is_element_mi(element, elements) then
307 core.add_all(self.super_elements(element, elements))
308 core.add(element)
309 if all_subelements_si then
310 border.add(element)
311 end
312 else if not all_subelements_si then
313 core.add_all(self.super_elements(element, elements))
314 core.add(element)
315 else
316 crown.add(element)
317 end
318 end
319 end
320
321 # Conflicts graph of elements hierarchy (two types are in conflict if they have common subelements)
322 private fun build_conflicts_graph(elements: Set[E]) do
323 self.conflicts_graph = new HashMap[E, HashSet[E]]
324 var core = reverse_linearize(self.core)
325 for t in core do
326 for i in self.linear_extension(t, elements) do
327 if t == i then continue
328
329 var lin_i = self.linear_extension(i, elements)
330
331 for j in self.linear_extension(t, elements) do
332 if i == j or j == t then continue
333 var lin_j = self.linear_extension(j, elements)
334
335 var d_ij = lin_i - lin_j
336 var d_ji = lin_j - lin_i
337
338 for ed1 in d_ij do
339 if not conflicts_graph.has_key(ed1) then conflicts_graph[ed1] = new HashSet[E]
340 # add ed1 x ed2 to conflicts graph
341 for ed2 in d_ji do conflicts_graph[ed1].add(ed2)
342 end
343 for ed1 in d_ij do
344 if not conflicts_graph.has_key(ed1) then conflicts_graph[ed1] = new HashSet[E]
345 # add ed1 x ed2 to conflicts graph
346 for ed2 in d_ji do conflicts_graph[ed1].add(ed2)
347 end
348 end
349 end
350 end
351 end
352
353 private var conflicts_graph: nullable HashMap[E, Set[E]]
354
355 # cache for linear_extensions
356 private var linear_extensions_cache: Map[E, Array[E]] = new HashMap[E, Array[E]]
357
358 # Return a linear_extension of super_elements of the element
359 private fun linear_extension(element: E, elements: Set[E]): Array[E] do
360 if not self.linear_extensions_cache.has_key(element) then
361 var supers = new HashSet[E]
362 supers.add(element)
363 supers.add_all(self.super_elements(element, elements))
364 self.linear_extensions_cache[element] = self.linearize(supers)
365 end
366 return self.linear_extensions_cache[element]
367 end
368
369 private fun super_elements(element: E, elements: Set[E]): Set[E] is abstract
370 private fun sub_elements(element: E, elements: Set[E]): Set[E] is abstract
371 private fun is_element_mi(element: E, elements: Set[E]): Bool is abstract
372 private fun linearize(elements: Set[E]): Array[E] is abstract
373 private fun reverse_linearize(elements: Set[E]): Array[E] is abstract
374 end
375
376 # MType coloring
377 class MTypeColorer
378 super TypingColorer[MType]
379
380 var mmodule: MModule
381
382 init(mmodule: MModule) do self.mmodule = mmodule
383
384 redef fun super_elements(element, elements) do return self.mmodule.super_mtypes(element, elements)
385 redef fun is_element_mi(element, elements) do return self.super_elements(element, elements).length > 1
386 redef fun sub_elements(element, elements) do do return self.mmodule.sub_mtypes(element, elements)
387 redef fun linearize(elements) do return self.mmodule.linearize_mtypes(elements)
388 redef fun reverse_linearize(elements) do return self.mmodule.reverse_linearize_mtypes(elements)
389 end
390
391 # MClass coloring
392 class MClassColorer
393 super TypingColorer[MClass]
394
395 private var mmodule: MModule
396
397 init(mmodule: MModule) do self.mmodule = mmodule
398
399 redef fun super_elements(element, elements) do return self.mmodule.super_mclasses(element)
400 fun parent_elements(element: MClass): Set[MClass] do return self.mmodule.parent_mclasses(element)
401 redef fun is_element_mi(element, elements) do return self.parent_elements(element).length > 1
402 redef fun sub_elements(element, elements) do do return self.mmodule.sub_mclasses(element)
403 redef fun linearize(elements) do return self.mmodule.linearize_mclasses(elements)
404 redef fun reverse_linearize(elements) do return self.mmodule.reverse_linearize_mclasses(elements)
405 end
406
407 # MProperty coloring
408 abstract class MPropertyColorer[E: MProperty]
409
410 private var mmodule: MModule
411 private var class_colorer: MClassColorer
412 private var coloration_result: Map[E, Int] = new HashMap[E, Int]
413
414 init(mmodule: MModule) do
415 self.mmodule = mmodule
416 self.class_colorer = new MClassColorer(mmodule)
417 end
418
419 fun colorize(mclasses: Set[MClass]): Map[E, Int] do
420 self.class_colorer.tag_elements(mclasses)
421 self.class_colorer.build_conflicts_graph(mclasses)
422 self.colorize_core(self.class_colorer.core)
423 self.colorize_crown(self.class_colorer.crown)
424 return self.coloration_result
425 end
426
427 # Colorize properties of the core hierarchy
428 private fun colorize_core(mclasses: Set[MClass]) do
429 var min_color = 0
430 for mclass in mclasses do
431 var color = min_color
432
433 # if the class is root, get the minimal color
434 if self.mmodule.parent_mclasses(mclass).length == 0 then
435 colorize_elements(self.properties(mclass), color)
436 else
437 # check last color used by parents
438 color = max_color(color, self.mmodule.parent_mclasses(mclass))
439 # check max color used in conflicts
440 if self.class_colorer.conflicts_graph.has_key(mclass) then
441 color = max_color(color, self.class_colorer.conflicts_graph[mclass])
442 end
443 colorize_elements(self.properties(mclass), color)
444 end
445 end
446 end
447
448 # Colorize properties of the crown hierarchy
449 private fun colorize_crown(mclasses: Set[MClass]) do
450 for mclass in mclasses do
451 colorize_elements(self.properties(mclass), max_color(0, self.mmodule.parent_mclasses(mclass)))
452 end
453 end
454
455 # Colorize a collection of mproperties given a starting color
456 private fun colorize_elements(elements: Collection[E], start_color: Int) do
457 for element in elements do
458 if self.coloration_result.has_key(element) then continue
459 self.coloration_result[element] = start_color
460 start_color += 1
461 end
462 end
463
464 private fun max_color(min_color: Int, mclasses: Collection[MClass]): Int do
465 var max_color = min_color
466
467 for mclass in mclasses do
468 for mproperty in self.properties(mclass) do
469 var color = min_color
470 if self.coloration_result.has_key(mproperty) then
471 color = self.coloration_result[mproperty]
472 if color >= max_color then max_color = color + 1
473 end
474 end
475 end
476 return max_color
477 end
478
479 # Filter properties
480 private fun properties(mclass: MClass): Set[E] is abstract
481 end
482
483 # Coloring for MMethods
484 class MMethodColorer
485 super MPropertyColorer[MMethod]
486
487 init(mmodule: MModule) do super
488
489 redef fun properties(mclass) do
490 var properties = new HashSet[MMethod]
491 for mprop in self.mmodule.properties(mclass) do
492 if mprop isa MMethod then properties.add(mprop)
493 end
494 return properties
495 end
496 end
497
498 # Coloring for MMAttributes
499 class MAttributeColorer
500 super MPropertyColorer[MAttribute]
501
502 init(mmodule: MModule) do super
503
504 redef fun properties(mclass) do
505 var properties = new HashSet[MAttribute]
506 for mprop in self.mmodule.properties(mclass) do
507 if mprop isa MAttribute then properties.add(mprop)
508 end
509 return properties
510 end
511 end
512
513 # Coloring for MVirtualTypeProps
514 class MVirtualTypePropColorer
515 super MPropertyColorer[MVirtualTypeProp]
516
517 init(mmodule: MModule) do super
518
519 redef fun properties(mclass) do
520 var properties = new HashSet[MVirtualTypeProp]
521 for mprop in self.mmodule.properties(mclass) do
522 if mprop isa MVirtualTypeProp then properties.add(mprop)
523 end
524 return properties
525 end
526 end
527
528 # Colorer for type resolution table
529 class ResolutionColorer
530
531 private var coloration_result: Map[MType, Int] = new HashMap[MType, Int]
532
533 init do end
534
535 fun colorize(elements: Map[MClassType, Set[MType]]): Map[MType, Int] do
536 self.build_conflicts_graph(elements)
537 self.colorize_elements(elements)
538 return coloration_result
539 end
540
541 # Colorize a collection of elements
542 fun colorize_elements(elements: Map[MClassType, Set[MType]]) do
543 var min_color = 0
544 for mclasstype, mclasstypes in elements do
545 for element in mclasstypes do
546 if self.coloration_result.has_key(element) then continue
547 var color = min_color
548 while not self.is_color_free(element, color) do
549 color += 1
550 end
551 coloration_result[element] = color
552 color = min_color
553 end
554 end
555 end
556
557 # Check if a related element to the element already use the color
558 private fun is_color_free(element: MType, color: Int): Bool do
559 if conflicts_graph.has_key(element) then
560 for st in conflicts_graph[element] do
561 if coloration_result.has_key(st) and coloration_result[st] == color then return false
562 end
563 end
564 return true
565 end
566
567 # look for unanchored types generated by the same type
568 private fun build_conflicts_graph(elements: Map[MClassType, Set[MType]]) do
569 for mclasstype, mtypes in elements do
570 for mtype in mtypes do
571 for otype in mtypes do
572 if otype == mtype then continue
573 self.add_conflict(mtype, otype)
574 end
575 end
576 end
577 end
578
579 private var conflicts_graph: Map[MType, Set[MType]] = new HashMap[MType, Set[MType]]
580
581 private fun add_conflict(mtype: MType, otype: MType) do
582 if mtype == otype then return
583 if not self.conflicts_graph.has_key(mtype) then self.conflicts_graph[mtype] = new HashSet[MType]
584 self.conflicts_graph[mtype].add(otype)
585 if not self.conflicts_graph.has_key(otype) then self.conflicts_graph[otype] = new HashSet[MType]
586 self.conflicts_graph[otype].add(mtype)
587 end
588 end
589
590 # Perfect Hashing (PH)
591 # T = type of holder
592 # U = type of elements to hash
593 private class PerfectHasher[T: Object, U: Object]
594
595 var operator: PHOperator
596
597 init(operator: PHOperator) do self.operator = operator
598
599 fun compute_masks(conflicts: Map[T, Set[U]], ids: Map[U, Int]): Map[T, Int] do
600 var masks = new HashMap[T, Int]
601 for mclasstype, mtypes in conflicts do
602 masks[mclasstype] = compute_mask(mtypes, ids)
603 end
604 return masks
605 end
606
607 private fun compute_mask(mtypes: Set[U], ids: Map[U, Int]): Int do
608 var mask = 0
609 loop
610 var used = new List[Int]
611 for mtype in mtypes do
612 var res = operator.op(mask, ids[mtype])
613 if used.has(res) then
614 break
615 else
616 used.add(res)
617 end
618 end
619 if used.length == mtypes.length then break
620 mask += 1
621 end
622 return mask
623 end
624
625 fun compute_hashes(elements: Map[T, Set[U]], ids: Map[U, Int], masks: Map[T, Int]): Map[T, Map[U, Int]] do
626 var hashes = new HashMap[T, Map[U, Int]]
627 for mclasstype, mtypes in elements do
628 var mask = masks[mclasstype]
629 var inhashes = new HashMap[U, Int]
630 for mtype in mtypes do
631 inhashes[mtype] = operator.op(mask, ids[mtype])
632 end
633 hashes[mclasstype] = inhashes
634 end
635 return hashes
636 end
637 end
638
639 # Abstract operator used for perfect hashing
640 abstract class PHOperator
641 fun op(mask: Int, id:Int): Int is abstract
642 end
643
644 # Hashing using modulo (MOD) operator
645 # slower but compact
646 class PHModOperator
647 super PHOperator
648 init do end
649 redef fun op(mask, id) do return mask % id
650 end
651
652 # Hashing using binary and (AND) operator
653 # faster but sparse
654 class PHAndOperator
655 super PHOperator
656 init do end
657 redef fun op(mask, id) do return mask.bin_and(id)
658 end
659
660 class TypingHasher[E: Object]
661 super PerfectHasher[E, E]
662 super TypingLayoutBuilder[E]
663
664 var mmodule: MModule
665
666 init(operator: PHOperator, mmodule: MModule) do
667 super(operator)
668 self.mmodule = mmodule
669 end
670
671 redef fun build_layout(elements: Set[E]): PHLayout[E, E] do
672 var result = new PHLayout[E, E]
673 var conflicts = self.build_conflicts(elements)
674 result.ids = self.compute_ids(elements)
675 result.masks = self.compute_masks(conflicts, result.ids)
676 result.hashes = self.compute_hashes(conflicts, result.ids, result.masks)
677 return result
678 end
679
680 # Ids start from 1 instead of 0
681 private fun compute_ids(elements: Set[E]): Map[E, Int] do
682 var ids = new HashMap[E, Int]
683 var lin = self.reverse_linearize(elements)
684 for e in lin do
685 ids[e] = ids.length + 1
686 end
687 return ids
688 end
689
690 private fun build_conflicts(elements: Set[E]): Map[E, Set[E]] do
691 var conflicts = new HashMap[E, Set[E]]
692 for e in elements do
693 var supers = self.super_elements(e, elements)
694 supers.add(e)
695 conflicts[e] = supers
696 end
697 return conflicts
698 end
699
700 private fun super_elements(element: E, elements: Set[E]): Set[E] is abstract
701 private fun reverse_linearize(elements: Set[E]): Array[E] is abstract
702 end
703
704 class MTypeHasher
705 super TypingHasher[MType]
706
707 init(operator: PHOperator, mmodule: MModule) do super(operator, mmodule)
708
709 redef fun super_elements(element, elements) do
710 return self.mmodule.super_mtypes(element, elements)
711 end
712
713 redef fun reverse_linearize(elements) do
714 return self.mmodule.reverse_linearize_mtypes(elements)
715 end
716 end
717
718 class MClassHasher
719 super TypingHasher[MClass]
720
721 init(operator: PHOperator, mmodule: MModule) do super(operator, mmodule)
722
723 redef fun super_elements(element, elements) do
724 return self.mmodule.super_mclasses(element)
725 end
726
727 redef fun reverse_linearize(elements) do
728 return self.mmodule.reverse_linearize_mclasses(elements)
729 end
730 end