nit: introduce interpretor type test with dynamic loading
authorAlexandre Terrasa <alexandre@moz-concept.com>
Tue, 2 Oct 2012 16:03:44 +0000 (12:03 -0400)
committerJean Privat <jean@pryen.org>
Tue, 2 Oct 2012 18:53:04 +0000 (14:53 -0400)
Signed-off-by: Alexandre Terrasa <alexandre@moz-concept.com>

src/interpretor_type_test.nit [new file with mode: 0644]

diff --git a/src/interpretor_type_test.nit b/src/interpretor_type_test.nit
new file mode 100644 (file)
index 0000000..762e7f9
--- /dev/null
@@ -0,0 +1,1110 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2012 Alexandre Terrasa <alexandre@moz-concept.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Introduce incremental typing in naive interpreter
+# Typing modes are :
+# * Legacy: default mode, using request to precomputed metamodel
+# * BM: binary matrix
+# * CL: coloration
+# * PHAND: perfect hashing using binary AND
+# * PHMOD: perfect hashing using modulo
+module interpretor_type_test
+
+intrude import naive_interpreter
+
+# Add options for incremental typing
+redef class ToolContext
+       # --ic-typing-mode BM | CL | PHMOD | PHAND
+       var opt_ic_typing_mode = new OptionString("Incremental typing method. Possible values are:\n\t\t\t\t - BM: binary matrix\n\t\t\t\t - CL: coloration\n\t\t\t\t - PHAND: perfect hashing whith AND\n\t\t\t\t - PHMOD: perfect hashing whith MOD", "--ic-typing-mode")
+       # --ic-load-mode
+       var opt_ic_load_mode = new OptionString("Dynamic loading mode. Possible values are:\n\t\t\t\t - type (default): load only the new type\n\t\t\t\t - module: load the module of new type", "--ic-load-mode")
+       # --ic-bootstrap
+       var opt_ic_bootstrap = new OptionBool("Bootstrap typing with standard library (default is no bootstrap)", "--ic-bootstrap")
+       # --ic-recompute-mode always | never | threshold | increment
+       var opt_ic_recompute_mode = new OptionString("When to recompute typing. Possible values are:\n\t\t\t\t - always: recompute for each new type\n\t\t\t\t - never (default):  never recompute, use fallback instead\n\t\t\t\t - threshold:  recompute only when the threshold is reached, use fallback before\n\t\t\t\t - increment: try to update structures instead of recompute", "--ic-recompute-mode")
+       # --ic-threshold-mode increment | fallback
+       var opt_ic_threshold_mode = new OptionString("Threshold mode. Possible values are:\n\t\t\t\t - increment: recompute when the number of new type reach the threshold count\n\t\t\t\t -fallback: recompute when the use number of fallback use reach the threshold count", "--ic-threshold-mode")
+       # --ic-threshold-count <int>
+       var opt_ic_threshold_count = new OptionInt("Threshold count. Take an integer", 0, "--ic-threshold-count")
+       # --ic-cache-size <int>
+       var opt_ic_cache_size = new OptionInt("Cache size. Take an integer", 0, "--ic-cache-size")
+       # --ic-show-stats
+       var opt_ic_show_stats = new OptionBool("Show statistics about typing at the end of the interpretation", "--ic-show-stats")
+
+       redef init
+       do
+               super
+               self.option_context.add_option(self.opt_ic_typing_mode)
+               self.option_context.add_option(self.opt_ic_load_mode)
+               self.option_context.add_option(self.opt_ic_bootstrap)
+               self.option_context.add_option(self.opt_ic_recompute_mode)
+               self.option_context.add_option(self.opt_ic_threshold_mode)
+               self.option_context.add_option(self.opt_ic_threshold_count)
+               self.option_context.add_option(self.opt_ic_cache_size)
+               self.option_context.add_option(self.opt_ic_show_stats)
+       end
+end
+
+redef class ModelBuilder
+       # Add statistics display at the end of the interpretation
+       redef fun run_naive_interpreter(mainmodule, arguments) do
+               var time0 = get_time
+               self.toolcontext.info("*** START INTERPRETING ***", 1)
+
+               var interpreter = new NaiveInterpreter(self, mainmodule, arguments)
+               init_naive_interpreter(interpreter, mainmodule)
+
+               # stats
+               if interpreter.show_stats then
+                       print "# Interpretation statistics"
+
+                       print "## Dynamic loading"
+                       print " - nb dynamically lodaed types: {interpreter.typing.nb_loads} ({interpreter.typing.nb_gen_loads} were generic)"
+                       print " - nb dynamically lodaed modules: {interpreter.typing.nb_mod_loads}"
+
+                       print "## Typing structures"
+                       print " - nb increments: {interpreter.typing.nb_increment_loads} ({interpreter.typing.nb_gen_increment_loads} were generic)"
+                       print " - nb destructive increments: {interpreter.typing.nb_destructive_increments} ({interpreter.typing.nb_gen_destructive_increments} were generic)"
+                       print " - nb full structure recomputation: {interpreter.typing.nb_recomputes}"
+                       print " - overall max size table: {interpreter.typing.max_size_tables}"
+                       print " - total: size needed for tables: {interpreter.typing.sum_size_tables}"
+
+                       print "## Type test"
+                       print " - nb isa: {interpreter.typing.nb_isas} ({interpreter.typing.nb_gen_isas} involving generic types)"
+                       print " - nb isa using fallback: {interpreter.typing.nb_fallbacks} ({interpreter.typing.nb_gen_fallbacks} involving generic types)"
+                       print " - nb isa using fallback responded from cache: {interpreter.typing.nb_cache_res}"
+               end
+
+               var time1 = get_time
+               self.toolcontext.info("*** END INTERPRETING: {time1-time0} ***", 2)
+       end
+end
+
+# Add gestion handling for incremental typing mecanisms
+redef class NaiveInterpreter
+
+       var typing: Typing
+       var module_increment: Bool
+       var recompute_mode: String
+       var threshold_mode: String = "null"
+       var threshold_count: Int = 0
+       var cache_size: Int = 0
+       var show_stats: Bool = false
+
+       # Add option handling
+       redef init(modelbuilder, mainmodule, arguments) do
+               super(modelbuilder, mainmodule, arguments)
+
+               # choose typing method
+               var mode = modelbuilder.toolcontext.opt_ic_typing_mode.value
+               if mode == "BM" then
+                       self.modelbuilder.toolcontext.info("IC-TYPING: Using BM typing", 1)
+                       typing = new BMTyping(self)
+               else if mode == "CL" then
+                       self.modelbuilder.toolcontext.info("IC-TYPING: Using CL typing", 1)
+                       typing = new CLTyping(self)
+               else if mode == "PHMOD" then
+                       self.modelbuilder.toolcontext.info("IC-TYPING: Using PH-MOD typing", 1)
+                       typing = new PHModTyping(self)
+               else if mode == "PHAND" then
+                       self.modelbuilder.toolcontext.info("IC-TYPING: Using PH-AND typing", 1)
+                       typing = new PHAndTyping(self)
+               else
+                       self.modelbuilder.toolcontext.info("IC-TYPING: Using legacy typing", 1)
+                       typing = new LegacyTyping(self)
+               end
+
+               # load full module or juste the type ?
+               mode = modelbuilder.toolcontext.opt_ic_load_mode.value
+               if mode == "module" then
+                       self.modelbuilder.toolcontext.info("IC-TYPING: Using load mode: module", 1)
+                       module_increment = true
+               else
+                       self.modelbuilder.toolcontext.info("IC-TYPING: Using load mode: type", 1)
+                       module_increment = false
+               end
+
+               # bootstrap type structures with stdlib types
+               if modelbuilder.toolcontext.opt_ic_bootstrap.value then
+                       self.modelbuilder.toolcontext.info("IC-TYPING: Using bootstrap", 1)
+                       typing.bootstrap
+               end
+
+               # typing recomputation mode
+               var recompute_mode = modelbuilder.toolcontext.opt_ic_recompute_mode.value
+               if recompute_mode != null then
+                       self.recompute_mode = recompute_mode
+
+                       if recompute_mode == "threshold" then
+                               var threshold_mode = modelbuilder.toolcontext.opt_ic_threshold_mode.value
+                               if threshold_mode == null then
+                                       print "IC-ERROR : Recompute mode 'threshold' need a threshold mode"
+                                       abort
+                               else
+                                       self.modelbuilder.toolcontext.info("IC-TYPING: Using threshold mode: {threshold_mode}", 1)
+                                       self.threshold_mode = threshold_mode
+                                       var threshold_count = modelbuilder.toolcontext.opt_ic_threshold_count.value
+                                       if threshold_count == 0 then
+                                               print "IC-ERROR : Recompute mode 'threshold' need a threshold count"
+                                               abort
+                                       else
+                                               self.threshold_count = threshold_count
+                                               self.modelbuilder.toolcontext.info("IC-TYPING: Using threshold value: {threshold_count}", 1)
+                                       end
+                               end
+                       end
+               else
+                       self.recompute_mode = "never"
+               end
+               self.modelbuilder.toolcontext.info("IC-TYPING: Using recompute mode: {self.recompute_mode}", 1)
+
+               # cache size
+               cache_size = modelbuilder.toolcontext.opt_ic_cache_size.value
+               self.modelbuilder.toolcontext.info("IC-TYPING: Using cache size: {cache_size}", 1)
+
+               # stats
+               if modelbuilder.toolcontext.opt_ic_show_stats.value then show_stats = true
+       end
+
+       # Handle subtypes tests and redispatch to selected mode
+       redef fun is_subtype(sub, sup) do
+               self.modelbuilder.toolcontext.info("IC-TYPING[DEBUG]: {sub} ISA {sup}", 2)
+
+               # stats
+               typing.nb_isas += 1
+               if sup isa MGenericType or sub isa MGenericType then typing.nb_gen_isas += 1
+
+               # special case of nullables types
+               if sub isa MNullType and sup isa MNullableType then
+                       return true
+               else if sub isa MNullType and not sup isa MNullableType then
+                       return false
+               end
+               if sub isa MNullableType then sub = sub.mtype
+               if sup isa MNullableType then sup = sup.mtype
+
+               # std case
+               var t1 = typing.load_increment(sub.as(MClassType))
+               var t2 = typing.load_increment(sup.as(MClassType))
+
+               var res: Bool
+               if typing.is_valid then
+                       res = t1.is_subtype(t2)
+               else
+                       # recompute mode = thresold
+                       if recompute_mode == "threshold" then
+                               if threshold_mode == "fallback" then
+                                       typing.increment_count += 1
+                                       # recompute instead fallback
+                                       if typing.increment_count > threshold_count then
+                                               typing.compute_typing
+                                               typing.increment_count = 0
+                                               return t1.is_subtype(t2)
+                                       end
+                               end
+                       end
+                       res = t1.is_subtype_fallback(t2)
+               end
+               return res
+       end
+end
+
+# Incremental typing
+
+# The base of incremental typing
+private abstract class Typing
+       type T: Type
+
+       # List of already loaded types
+       var loaded_types: HashMap[MType, T] = new HashMap[MType, T]
+       # List of already loaded modules
+       var loaded_modules: HashSet[MModule] = new HashSet[MModule]
+       # Last type ID used
+       var lastID: Int = -1
+       # Is the typing still valid (no destructive increment)
+       var is_valid: Bool = false
+       # Increment count since the last full recomputation
+       var increment_count = 0
+
+       private var interpreter: NaiveInterpreter
+
+       # stats
+       var nb_loads = 0
+       var nb_gen_loads = 0
+       var nb_mod_loads = 0
+       var nb_increment_loads = 0
+       var nb_gen_increment_loads = 0
+       var nb_destructive_increments = 0
+       var nb_gen_destructive_increments = 0
+       var nb_isas = 0
+       var nb_gen_isas = 0
+       var nb_recomputes = 0
+       var nb_fallbacks = 0
+       var nb_gen_fallbacks = 0
+       var nb_cache_res = 0
+       var max_size_tables = 0
+       var sum_size_tables = 0
+
+       private init(interpreter: NaiveInterpreter) do
+               self.interpreter = interpreter
+       end
+
+       # Load the std lib types and compute typing
+       fun bootstrap do
+               interpreter.modelbuilder.toolcontext.info("IC-TYPING[DEBUG]: bootstrap start", 2)
+               var mods = interpreter.mainmodule.model.get_mmodules_by_name("standard")
+               if mods != null then
+                       var std = mods.first
+                       for m in std.in_nesting.direct_greaters do
+                               load_module(m)
+                       end
+               end
+               compute_typing
+               interpreter.modelbuilder.toolcontext.info("IC-TYPING[DEBUG]: bootstrap end", 2)
+       end
+
+       # Load all types contained in a module
+       fun load_module(mmodule: MModule) do
+               interpreter.modelbuilder.toolcontext.info("IC-TYPING[DEBUG]: load module {mmodule}", 2)
+               # import nested module first
+               for m in mmodule.in_nesting.greaters do
+                       if mmodule == m then continue
+                       load_module(m)
+               end
+
+               # load classes in module
+               if loaded_modules.has(mmodule) then return
+
+               # stats
+               nb_mod_loads += 1
+
+               for c in mmodule.intro_mclasses do
+                       # skip unanchored types (they will be handled by load_supertypes of anchored types
+                       if c.mclass_type.need_anchor then continue
+                       load_type(c.mclass_type)
+               end
+               loaded_modules.add(mmodule)
+       end
+
+       # Load a MType and return its Type representation
+       fun load_type(mtype: MClassType): T do
+               if is_loaded(mtype) then
+                       return loaded_types[mtype]
+               else
+                       # stats
+                       nb_loads += 1
+                       if mtype isa MGenericType then nb_gen_loads += 1
+
+                       var supertypes = load_supertypes(mtype)
+                       var t = init_type(lastID + 1, mtype, supertypes, self)
+                       lastID = t.id
+                       loaded_types[mtype] = t
+
+                       # load formal parameter types
+                       if mtype isa MGenericType then
+                               for arg in mtype.arguments do
+                                       if arg isa MClassType then load_type(arg)
+                               end
+                       end
+
+                       return t
+               end
+       end
+
+       # Load compile time known super-types of a type
+       # supertypes are all known super classes at compile time and unresolved generic classes
+       fun load_supertypes(child: MClassType): Array[T] do
+               var parents = interpreter.mainmodule.flatten_mclass_hierarchy[child.mclass].greaters
+               var plist = new Array[T]
+
+               # load each parent
+               for p in parents do
+                       # not a parent but the type itself
+                       if p.mclass_type.mclass == child.mclass then continue
+
+                       if p.mclass_type.need_anchor then
+                               # the parent need to be anchored to the child (contains a formal type)
+                               var anchor = p.mclass_type.anchor_to(interpreter.mainmodule, child)
+                               plist.add(load_type(anchor))
+                       else
+                               # already revolved type
+                               plist.add(load_type(p.mclass_type))
+                       end
+               end
+               return plist
+       end
+
+       # Increment types structures with the new type
+       fun load_increment(mtype: MClassType): Type do
+               # old type, no need of recomputation
+               if is_loaded(mtype) then
+                       return load_type(mtype)
+               else
+                       # current structures are expired, a new type appeared
+                       is_valid = false
+               end
+
+               interpreter.modelbuilder.toolcontext.info("IC-TYPING[DEBUG]: load increment {mtype}", 2)
+
+               # load the module
+               if interpreter.module_increment then
+                       load_module(mtype.mclass.intro_mmodule)
+               end
+
+               var t = load_type(mtype)
+
+               # stats
+               nb_increment_loads += 1
+               if mtype isa MGenericType then nb_gen_increment_loads += 1
+
+               # recompute mode = always
+               if interpreter.recompute_mode == "always" then
+                       compute_typing
+               end
+
+               # recompute mode = thresold
+               if interpreter.recompute_mode == "threshold" then
+                       if interpreter.threshold_mode == "increment" then
+                               increment_count += 1
+
+                               if increment_count > interpreter.threshold_count then
+                                       compute_typing
+                                       increment_count = 0
+                               end
+                       end
+               end
+
+               # recompute mode = increment
+               if interpreter.recompute_mode == "increment" then
+                       compute_increment(t)
+                       if not is_valid then compute_typing
+               end
+
+               return t
+       end
+
+       # Factory for specializations of Type
+       fun init_type(id: Int, mtype: MClassType, supertypes: Array[T], typing: Typing): T is abstract
+
+       # Is the type already loaded ?
+       fun is_loaded(mtype: MType): Bool do return loaded_types.has_key(mtype)
+
+       # Run the typing full computation
+       # Do nothing, need to be redefined
+       fun compute_typing do
+               nb_recomputes += 1
+               interpreter.modelbuilder.toolcontext.info("IC-TYPING[DEBUG]: compute typing", 1)
+               is_valid = true
+       end
+
+       # Compute typing with the increment
+       # if the increment is destructive, need to recompute the whole typing structure
+       # else just compute typing structure for the new type
+       fun compute_increment(t: T) is abstract
+
+       # Is the increment destructive ?
+       # destructive increment depends on typing method used
+       fun is_destructive_increment(t: T): Bool is abstract
+end
+
+# An incremental typing type representation
+private abstract class Type
+       type T: Type
+
+       # The unique type id
+       var id: Int
+       # all resolved super-types
+       var supertypes: Array[T]
+
+       var mtype: MClassType
+       var typing: Typing
+       var cache: Cache
+
+       init(id: Int, mtype: MClassType, supertypes: Array[T], typing: Typing) do
+               self.id = id
+               self.mtype = mtype
+               self.supertypes = supertypes
+               self.typing = typing
+               self.cache = new Cache(typing.interpreter.cache_size)
+               self.supertypes.add(self)
+       end
+
+       # Subtyping using computed representation
+       fun is_subtype(sup: T): Bool is abstract
+
+       # Subtyping for uncomputed types
+       # unefficient algorithme using linear probing in known super-types
+       fun is_subtype_fallback(sup: T): Bool do
+               # stats
+               typing.nb_fallbacks += 1
+               if self.mtype isa MGenericType or sup.mtype isa MGenericType then typing.nb_gen_fallbacks += 1
+
+               # try in cache cache
+               if cache.contains(sup) then
+                       typing.nb_cache_res += 1
+                       return cache.response(sup)
+               end
+
+               # test implies generic typing
+               if sup.mtype isa MGenericType then
+                       for p in supertypes do
+                               if p == sup then
+                                       # found the same type (ie. p = B[Int] = sup)
+                                       if not cache.contains(sup) then cache.add(sup, true)
+                                       return true
+                               else if p.mtype.mclass == sup.mtype.mclass then
+                                       # found the same class (ie. p.mclass = B[B#0] = sup.mclass)
+                                       # compare formal types arguments
+                                       for i in [0..p.mtype.as(MGenericType).arguments.length[ do
+                                               # erase nullable annotation of p arg
+                                               var sarg = p.mtype.as(MGenericType).arguments[i]
+                                               if sarg isa MNullableType then sarg = sarg.mtype
+                                               var sft = typing.load_type(sarg.as(MClassType))
+                                               # erase nullable annotation of super arg
+                                               var suparg = sup.mtype.as(MGenericType).arguments[i]
+                                               if suparg isa MNullableType then suparg = suparg.mtype
+                                               var pft = typing.load_type(suparg.as(MClassType))
+                                               if not sft.is_subtype_fallback(pft) then
+                                                       if not cache.contains(sup) then cache.add(sup, false)
+                                                       return false
+                                               end
+                                       end
+                                       if not cache.contains(sup) then cache.add(sup, true)
+                                       return true
+                               end
+                       end
+                       if not cache.contains(sup) then cache.add(sup, false)
+                       return false
+               end
+
+               # for non generic type: look up in supertypes
+               # 0(n) where n = nb supertypes of the current type
+               for p in supertypes do
+                       if p == sup then
+                               if not cache.contains(sup) then cache.add(sup, true)
+                               return true
+                       end
+               end
+               if not cache.contains(sup) then cache.add(sup, false)
+               return false
+       end
+
+       redef fun to_s do return "{mtype.to_s} (id: {id})"
+end
+
+# Binary Matrix (BM) typing
+private class BMTyping
+       super Typing
+
+       redef type T: BMType
+
+       init(interpreter: NaiveInterpreter) do super(interpreter)
+
+       redef fun init_type(id, mtype, supertypes, typing) do return new BMType(id, mtype, supertypes, typing)
+
+       # Compute rows in the binary matrix for each loaded type
+       redef fun compute_typing do
+               super
+               for t in loaded_types.values do
+                       t.compute_row(loaded_types, interpreter.mainmodule)
+               end
+       end
+
+       # If the increment is destructive, the typing representation is invalidated
+       # else add a row to the matrix and compute it
+       redef fun compute_increment(t) do
+               #print "DEBUG: compute increment {t}: is destructive = {is_destructive_increment(t)}"
+
+               # compute increments of supertypes first
+               for st in t.supertypes do
+                       if t == st then continue
+                       if st.row == null then compute_increment(st)
+               end
+
+               if is_destructive_increment(t) then
+                       # increment is destructive, need to recompute the whole typing structure
+                       is_valid = false
+               else
+                       # increment is additive, compute only the type (and newly added parents)
+                       is_valid = true
+                       t.compute_row(loaded_types, interpreter.mainmodule)
+               end
+       end
+
+       # For BM typing, destructive increments are only supertypes of already computed types
+       # This appends only when a new generic type on an already computed generic class appears
+       # ie. increment is GenericType[Something] and loaded-types already contains GenericType[SomethingElse]
+       redef fun is_destructive_increment(t) do
+               if t.mtype isa MGenericType then
+                       for ot in loaded_types.values do
+                               if ot.mtype.mclass == t.mtype.mclass then
+                                       nb_destructive_increments += 1
+                                       nb_gen_destructive_increments += 1
+                                       return true
+                               end
+                       end
+               end
+               return false
+       end
+end
+
+private class BMType
+       super Type
+
+       redef type T: BMType
+
+       # A row is an array of bits
+       var row: nullable Array[Bool]
+
+       # For each other types, fill the cell with 1 if subtype else 0
+       fun compute_row(types: HashMap[MType, T], mainmodule: MModule) do
+               row = new Array[Bool].filled_with(false, types.length)
+               for t in types.values do
+                       if self.is_subtype_fallback(t) then
+                               row[t.id] = true
+                       end
+               end
+
+               # stats
+               if row.length > typing.max_size_tables then typing.max_size_tables = row.length
+               typing.sum_size_tables += row.length
+       end
+
+       # O(1)
+       # return self.row[t.id]
+       redef fun is_subtype(t) do
+               if t.id >= self.row.length then return false
+               return self.row[t.id]
+       end
+end
+
+# Perfect Hashing (PH) typing
+private abstract class PHTyping
+       super Typing
+
+       redef type T: PHType
+
+       init(interpreter: NaiveInterpreter) do
+               super(interpreter)
+               lastID = 0
+       end
+
+       redef fun compute_typing do
+               super
+               for t in loaded_types.values do
+                       t.compute_row(loaded_types, interpreter.mainmodule)
+               end
+       end
+
+       # If the increment is destructive, the typing representation is invalidated
+       # else compute the hashmap of the type
+       redef fun compute_increment(t) do
+               # compute increments of supertypes first
+               for st in t.supertypes do
+                       if t == st then continue
+                       if st.row == null then compute_increment(st)
+               end
+
+               if is_destructive_increment(t) then
+                       # increment is destructive, need to recompute the whole typing structure
+                       is_valid = false
+               else
+                       # increment is additive, compute only the type (and newly added parents)
+                       is_valid = true
+                       t.compute_row(loaded_types, interpreter.mainmodule)
+               end
+       end
+
+       # For HP typing, destructive increments are only supertypes of already computed types
+       # This appends only when a new generic type on an already computed generic class appears
+       # ie. increment is GenericType[Something] and loaded-types already contains GenericType[SomethingElse]
+       redef fun is_destructive_increment(t) do
+               if t.mtype isa MGenericType then
+                       for ot in loaded_types.values do
+                               if ot.mtype.mclass == t.mtype.mclass then
+                                       # stats
+                                       nb_destructive_increments += 1
+                                       nb_gen_destructive_increments += 1
+                                       return true
+                               end
+                       end
+               end
+               return false
+       end
+end
+
+private abstract class PHType
+       super Type
+
+       redef type T: PHType
+
+       # Mask is used to hash an id
+       var mask: nullable Int
+       # A row is an array of IDs (Int)
+       var row: nullable Array[Int]
+
+       # First compute the mask needed to perfectly hash the parents list
+       # Then fill the hashmap
+       fun compute_row(types: HashMap[MType, T], mainmodule: MModule) do
+               # Create super type list
+               var stypes = new List[Type]
+               for st in types.values do
+                       if self.is_subtype_fallback(st) then stypes.add(st)
+               end
+
+               # Compute the hashing 'mask'
+               self.mask = compute_mask(stypes)
+
+               # Fill the row
+               row = new Array[Int]
+               for st in stypes do
+                       var idx = phash(st.id)
+                       if idx > row.length then for i in [row.length .. idx[ do row[i] = 0
+                       row[idx] = st.id
+               end
+
+               # stats
+               if row.length > typing.max_size_tables then typing.max_size_tables = row.length
+               typing.sum_size_tables += row.length
+       end
+
+       # Hash IDs
+       fun phash(id: Int): Int is abstract
+
+       # O(1)
+       # return self.row[phash(t.id)] == t.id
+       redef fun is_subtype(t) do
+               if self.row.length <= phash(t.id) then return false
+               return self.row[phash(t.id)] == t.id
+       end
+
+       fun compute_mask(stypes: List[Type]): Int is abstract
+end
+
+private class PHModTyping
+       super PHTyping
+       redef type T: PHModType
+       redef fun init_type(id, mtype, supertypes, typing) do return new PHModType(id, mtype, supertypes, typing)
+end
+
+private class PHModType
+       super PHType
+
+       redef type T: PHModType
+
+       # Hash using modulo
+       # return mask % id
+       redef fun phash(id: Int): Int do return mask.as(not null) % id
+
+       # Look for the first mask allowing perfect hashing for stypes ids
+       # TODO: Look for a more efficien way to do this
+       redef fun compute_mask(stypes: List[Type]): Int do
+               var mask = 0
+               loop
+                       var used = new List[Int]
+                       for st in stypes do
+                               var res = (mask % st.id)
+                               if used.has(res) then
+                                       break
+                               else
+                                       used.add(res)
+                               end
+                       end
+                       if used.length == stypes.length then break
+                       mask += 1
+               end
+               return mask
+       end
+end
+
+private class PHAndTyping
+       super PHTyping
+       redef type T: PHAndType
+       redef fun init_type(id, mtype, supertypes, typing) do return new PHAndType(id, mtype, supertypes, typing)
+end
+
+private class PHAndType
+       super PHType
+
+       redef type T: PHAndType
+
+       # Hash using binary and
+       # return mask & id
+       redef fun phash(id: Int): Int do return mask.as(not null).bin_and(id)
+
+       # Look for the first mask allowing perfect hashing for stypes ids
+       # TODO: Look for a more efficien way to do this
+       redef fun compute_mask(stypes: List[Type]): Int do
+               var mask = 0
+               loop
+                       var used = new List[Int]
+                       for st in stypes do
+                               var res = (mask.bin_and(st.id))
+                               if used.has(res) then
+                                       break
+                               else
+                                       used.add(res)
+                               end
+                       end
+                       if used.length == stypes.length then break
+                       mask += 1
+               end
+               return mask
+       end
+end
+
+# Coloration (CL) typing
+private class CLTyping
+       super Typing
+
+       redef type T: CLType
+
+       # Core of type hierarchy (types with at least two supertypes and their indirect supertypes)
+       var core: Array[T] = new Array[T]
+       # All types in single inheritance that are not part of core
+       var crown: Array[T] = new Array[T]
+       # All minimal classes of core
+       var border: Array[T] = new Array[T]
+
+       # Conflicts graph of hierarchy
+       var conflict_graph: HashMap[T, HashSet[T]] = new HashMap[T, HashSet[T]]
+
+       # Max color used
+       # Increments are colored with injective colors
+       var max_color = 0
+
+       var sorter: TypeSorter = new TypeSorter
+
+       init(interpreter: NaiveInterpreter) do
+               super(interpreter)
+               lastID = 0
+       end
+
+       redef fun init_type(id, mtype, supertypes, typing) do return new CLType(id, mtype, supertypes, typing)
+
+       # Colorize all known types and fill the type tables
+       redef fun compute_typing do
+               super
+               compute_init
+               compute_conflicts
+               colorize(core)
+               colorize(border)
+               colorize(crown)
+               fill_tables
+       end
+
+       # Compute a linear extension of parents of each types and tag each type as core, border or crown
+       fun compute_init do
+               core.clear
+               crown.clear
+               border.clear
+
+               # clear already build tables
+               for t in loaded_types.values do
+                       #t.parents = new HashSet[T]
+                       t.nb_parents = 0
+                       t.sub_types = new HashSet[T]
+               end
+
+               # compute linear ext of each type type
+               for t in loaded_types.values do compute_linear_ext(t)
+
+               # tag each type as core, crown or border
+               for t in loaded_types.values do tag_type(t)
+
+               # sort by reverse linearization order
+               sorter.sort(core)
+               sorter.sort(crown)
+               sorter.sort(border)
+       end
+
+       # Compute linear extension of the type
+       fun compute_linear_ext(t: T) do
+               var lin = new Array[T]
+               for st in loaded_types.values do
+                       if t.is_subtype_fallback(st) then
+                               lin.add(st)
+                               t.nb_parents = interpreter.mainmodule.flatten_mclass_hierarchy[t.mtype.mclass].direct_greaters.length
+                               if t != st then st.sub_types.add(t)
+                       end
+               end
+               sorter.sort(lin)
+               t.lin = lin
+       end
+
+       # Tag type as core, crown or border
+       fun tag_type(t: T) do
+               if t.nb_parents > 1 then
+                       if t.sub_types.length <= 1 then
+                               # border
+                               border.add(t)
+                       else
+                               # core
+                               core.add(t)
+                       end
+                       for st in t.lin do if t != st and not core.has(st) then core.add(st)
+               else
+                       # crown
+                       var subtypes_are_si = true
+                       for sub in t.sub_types do if sub.nb_parents > 1 then subtypes_are_si = false
+                       if subtypes_are_si then crown.add(t)
+               end
+       end
+
+       # Compute related classes to detect coloring conflicts
+       fun compute_conflicts do
+               conflict_graph.clear
+               var mi_types = new Array[T]
+               mi_types.add_all(core)
+               mi_types.add_all(border)
+               sorter.sort(mi_types)
+
+               for t in mi_types do
+                       for i in t.lin do
+                               if i == t then continue
+                               var lin_i = i.lin
+
+                               for j in t.lin do
+                                       if i == j or j == t then continue
+                                       var lin_j = j.lin
+
+                                       var d_ij = lin_i - lin_j
+                                       var d_ji = lin_j - lin_i
+
+                                       for ed1 in d_ij do
+                                               if not conflict_graph.has_key(ed1) then conflict_graph[ed1] = new HashSet[T]
+                                               # add ed1 x ed2 to conflicts graph
+                                               for ed2 in d_ji do conflict_graph[ed1].add(ed2)
+                                       end
+                                       for ed1 in d_ij do
+                                               if not conflict_graph.has_key(ed1) then conflict_graph[ed1] = new HashSet[T]
+                                               # add ed1 x ed2 to conflicts graph
+                                               for ed2 in d_ji do conflict_graph[ed1].add(ed2)
+                                       end
+                               end
+                       end
+               end
+               self.conflict_graph = conflict_graph
+       end
+
+       # Colorize an array of types
+       fun colorize(elts: Array[T]) do
+               var min_color = 0
+
+               for t in elts do
+                       var color = min_color
+                       while not color_free(t, color) do
+                               color += 1
+                       end
+                       t.color = color
+                       if color > max_color then max_color = color
+                       color = min_color
+               end
+       end
+
+       # Check if a related type to t already use the color
+       fun color_free(t: T, color: Int): Bool do
+               if conflict_graph.has_key(t) then
+                       for st in conflict_graph[t] do if st.color == color then return false
+               end
+               for st in t.lin do
+                       if st == t then continue
+                       if st.color == color then return false
+               end
+               return true
+       end
+
+       # Fill de colored tables
+       fun fill_tables do for t in loaded_types.values do fill_table(t)
+
+       fun fill_table(t: T) do
+               t.row = new Array[Int]
+               for st in t.lin do
+                       if t.row.length <= st.color.as(not null) then
+                               for i in [t.row.length..st.color.as(not null)[ do t.row.add(0)
+                       end
+                       t.row[st.color.as(not null)] = st.id
+               end
+
+               # stats
+               if t.row.length > max_size_tables then max_size_tables = t.row.length
+               sum_size_tables += t.row.length
+       end
+
+       # Compute table for a type increment: compute linearization of parents and fill the table
+       fun compute_table_increment(t: T) do
+               compute_linear_ext(t)
+               fill_table(t)
+       end
+
+       redef fun compute_increment(t) do
+               #print "DEBUG: compute increment {t}: is destructive = {is_destructive_increment(t)}"
+
+               # injective color
+               max_color += 1
+               t.color = max_color
+
+               # parents first
+               for st in t.supertypes do
+                       if t == st then continue
+                       if st.row == null then compute_increment(st)
+               end
+
+               if is_destructive_increment(t) then
+                       is_valid = false
+               else
+                       # increment is additive, compute only the type (and newly added parents)
+                       is_valid = true
+                       compute_table_increment(t)
+               end
+       end
+
+       # For CL typing, destructive increments are supertypes of already computed types and common types of two already computed types
+       redef fun is_destructive_increment(t) do
+               if t.mtype isa MGenericType then
+                       for ot in loaded_types.values do
+                               if ot.mtype.mclass == t.mtype.mclass then
+                                       # stats
+                                       nb_destructive_increments += 1
+                                       nb_gen_destructive_increments += 1
+                                       return true
+                               end
+                       end
+               end
+
+               compute_linear_ext(t)
+               if t.nb_parents > 1 then
+                       # stats
+                       nb_destructive_increments += 1
+                       if t.mtype isa MGenericType then nb_gen_destructive_increments = 0
+                       return true
+               end
+
+               return false
+       end
+end
+
+private class CLType
+       super Type
+
+       redef type T: CLType
+
+       # Linearization of super-types including the type itself
+       var lin: Array[T] = new Array[T]
+       # The color assignated to the type
+       var color: nullable Int
+       # Number of direct super-types
+       var nb_parents: Int = 0
+       # All sub types
+       var sub_types: HashSet[T] = new HashSet[T]
+
+       # A row is the an array of IDs
+       var row: nullable Array[Int]
+
+       # O(1)
+       # return row[t.color] == t.id
+       redef fun is_subtype(t) do
+               if t.color >= row.length then return false
+               return row[t.color.as(not null)] == t.id
+       end
+end
+
+# Wrapper for interpretor legacy typing based on precomputed metalmodel
+private class LegacyTyping
+       super Typing
+       redef type T: LegacyType
+       init(interpreter: NaiveInterpreter) do super(interpreter)
+       redef fun init_type(id, mtype, supertypes, typing) do return new LegacyType(id, mtype, supertypes, typing)
+end
+
+# Wrapper for interpretor legacy typing based on precomputed metalmodel
+private class LegacyType
+       super Type
+       redef type T: LegacyType
+       redef fun is_subtype(t) do return self.mtype.is_subtype(typing.interpreter.mainmodule, typing.interpreter.frame.arguments.first.mtype.as(MClassType), t.mtype)
+end
+
+# Tools
+
+private class Cache
+
+       var elements: Array[CachePair]
+       var size: Int
+       var cache: nullable CachePair
+
+       init(size: Int) do
+               self.size = size
+               self.elements = new Array[CachePair].with_capacity(size)
+       end
+
+       # Add a response to the cache
+       fun add(t: Type, res: Bool) do
+               var pair = new CachePair(t, res)
+               elements.insert(pair, 0)
+               if elements.length > size then elements.pop
+       end
+
+       # Check if the cache contains the subtype test response
+       fun contains(t: Type): Bool do
+               for e in elements do
+                       if e.t == t then
+                               cache = e
+                               return true
+                       end
+               end
+               return false
+       end
+
+       # Get a subtype test response from the cache
+       # ensure contains before
+       fun response(t: Type): Bool do
+               if cache != null and cache.t == t then return cache.res
+
+               for e in elements do if e.t == t then return e.res
+
+               print "IC-ERROR: Cache fault"
+               abort
+       end
+end
+
+private class CachePair
+       var t: Type
+       var res: Bool
+end
+
+# Add operator - to Array
+redef class Array[T]
+       # Return a new array with the elements only contened in 'self' and not in 'o'
+       fun -(o: Array[T]): Array[T] do
+               var res = new Array[T]
+               for e in self do if not o.has(e) then res.add(e)
+               return res
+       end
+end
+
+# A sorter for reversed linearization of types
+private class TypeSorter
+       super AbstractSorter[Type]
+
+       redef fun compare(a, b) do
+               if a == b then
+                       return 0
+               else if a.is_subtype_fallback(b) then
+                       return 1
+               end
+               return -1
+       end
+end