Merge branch 'tools' into wip
[nit.git] / src / icode / icode_tools.nit
index 2d059e2..ff2b2e8 100644 (file)
@@ -15,7 +15,8 @@
 # limitations under the License.
 
 # Tools to manipulate intermediace nit code representation
-import icode_base
+module icode_tools
+import icode_builder
 
 # A simple visitor to visit icode structures
 class ICodeVisitor
@@ -91,45 +92,79 @@ class ICodeVisitor
        end
 end
 
-redef class IRoutine
-       # Inline an iroutine in an icode sequence
-       fun inline_in_seq(seq: ISeq, args: Sequence[IRegister]): nullable IRegister
+redef class ICodeBuilder
+       # Inline an iroutine in the current icode sequence
+       # Require that routine != self.iroutine
+       fun inline_routine(routine: IRoutine, args: Sequence[IRegister], closdefs: nullable Sequence[nullable IClosureDef]): nullable IRegister
        do
-               var d = new ICodeDupContext
-               if args.length != params.length then print "{args.length} != {params.length}"
-               assert args.length == params.length
+               assert routine != self.iroutine
+               var d = new ICodeDupContext(self)
+               assert args.length == routine.params.length
+               var closdecls = routine.closure_decls
+               var cdefsa = if closdefs != null then closdefs.length else 0
+               var cdeclsa = if closdecls != null then closdecls.length else 0
+               assert cdefsa <= cdeclsa
+
+               # Fill register duplicate association
+               var dico = d._registers
+               var res = routine.result
+               if res != null then
+                       var res2 = new_register(res.stype)
+                       dico[res] = res2
+                       res = res2
+               end
+               for reg in routine.registers do
+                       assert not dico.has_key(reg)
+                       dico[reg] = new_register(reg.stype)
+               end
                for i in [0..args.length[ do
                        # FIXME The following assumes that params are readonly.
                        # The alternative is safe but add one move :/
-                       d._registers[params[i]] = args[i]
-                       #seq.icodes.add(new IMove(d.dup_ireg(params[i]), args[i]))
+                       dico[routine.params[i]] = args[i]
+                       #seq.icodes.add(new IMove(dico[routine.params[i]]), args[i]))
                end
-               seq.icodes.add(body.dup_with(d))
-               var r = result
-               if r != null then r = d.dup_ireg(r)
-               return r
+
+               # Fill escape mark association
+               for m in routine.escape_marks do
+                       var m2 = new IEscapeMark
+                       iroutine.escape_marks.add(m2)
+                       d._marks[m] = m2
+               end
+
+               # Fill closure association
+               if closdecls != null then
+                       var cdico = d._closures
+                       for i in [0..cdefsa[ do
+                               cdico[closdecls[i]] = closdefs[i]
+                       end
+                       for i in [cdefsa..cdeclsa[ do
+                               cdico[closdecls[i]] = null
+                       end
+               end
+
+               # Process inlining
+               routine.body.dup_with(d)
+               return res
        end
 end
 
 # This class stores reference to allow correct duplication of icodes
 private class ICodeDupContext
-       # Duplicate one register
-       # Subsequent invocation will return the same register
+       # Return the correct register
+       # * a duplicate of the local register 'r' of the inlined iroutine
+       # * 'r' else (it is a register of the caller iroutine)
        fun dup_ireg(r: IRegister): IRegister
        do
                var rs = _registers
                if rs.has_key(r) then
                        return rs[r]
                else
-                       var r2 = new IRegister(r.stype)
-                       rs[r] = r2
-                       return r2
+                       return r
                end
        end
 
-       # Duplicate a bunch of registers
-       # Subsequent invocation will return the same registers
-       fun dup_iregs(regs: Sequence[IRegister]): Sequence[IRegister]
+       # Return a correct bunch of registers
+       fun dup_iregs(regs: Sequence[IRegister]): Array[IRegister]
        do
                var a = new Array[IRegister].with_capacity(regs.length)
                for r in regs do
@@ -138,27 +173,71 @@ private class ICodeDupContext
                return a
        end
 
-       # The associoation between old_seq and new_seq
-       # Directly used by the IEscape
-       var _seqs: Map[ISeq, ISeq] = new HashMap[ISeq, ISeq]
-
        # The assocation between old_ireg and new_ireg
        # Used by dup_ireg
        var _registers: Map[IRegister, IRegister] = new HashMap[IRegister, IRegister]
+
+       # Return the correct escape mark
+       # * a duplicate of the local escape mark 'm' of the inlined iroutine
+       # * 'r' else (it is a escape mark of the caller iroutine)
+       fun dup_mark(m: IEscapeMark): IEscapeMark
+       do
+               var ms = _marks
+               if ms.has_key(m) then
+                       return ms[m]
+               else
+                       return m
+               end
+       end
+
+       # The associoation between old_seq and new_seq
+       # Used by dup_mark
+       var _marks: Map[IEscapeMark, IEscapeMark] = new HashMap[IEscapeMark, IEscapeMark]
+
+       # The association between a closure_decl and its closure_def (if any)
+       var _closures: Map[IClosureDecl, nullable IClosureDef] = new ArrayMap[IClosureDecl, nullable IClosureDef]
+
+       # The current code builder
+       var _icb: ICodeBuilder
+
+       init(icb: ICodeBuilder)
+       do
+               _icb = icb
+       end
 end
 
 redef class ICode
-       # Duplicate the current icode (generic part)
-       private fun dup_with(d: ICodeDupContext): ICode
+       # Duplicate the current icode in the icode builder of the ICodeDupContext
+       private fun dup_with(d: ICodeDupContext)
        do
                var c = inner_dup_with(d)
+               if self isa ICodeN then
+                       assert c isa ICodeN
+                       var cdecl = closure_defs
+                       if cdecl != null then
+                               # Duplicate the colsure definitions
+                               var cdecl2 = new Array[nullable IClosureDef].with_capacity(cdecl.length)
+                               for cd in cdecl do
+                                       if cd == null then
+                                               cdecl2.add(null)
+                                       else
+                                               var r = cd.result
+                                               if r != null then r = d.dup_ireg(r)
+                                               var cd2 = new IClosureDef(d.dup_iregs(cd.params), r)
+                                               cdecl2.add(cd2)
+                                               cd.body.dup_seq_to(d, cd2.body)
+                                       end
+                               end
+                               c.closure_defs = cdecl2
+                       end
+               end
                var r = result
                if r != null then c.result = d.dup_ireg(r)
                c.location = location
-               return c
+               d._icb.seq.icodes.add(c)
        end
 
-       # Duplicate the current icode (specific part)
+       # Simle partial duplication of the current icode
        private fun inner_dup_with(d: ICodeDupContext): ICode is abstract
 end
 
@@ -174,9 +253,16 @@ redef class ISeq
        # Note: dest must be empty and not modified afted duplication or IEscapes may be wrongly duplicated
        private fun dup_seq_to(d: ICodeDupContext, dest: ISeq)
        do
-               d._seqs[self] = dest
+               var old_seq = d._icb.seq
+               d._icb.seq = dest
                for c in icodes do
-                       dest.icodes.add(c.dup_with(d))
+                       c.dup_with(d)
+               end
+               d._icb.seq = old_seq
+               assert dest.iescape_mark == null
+               var mark = iescape_mark
+               if mark != null then
+                       dest.iescape_mark = d.dup_mark(mark)
                end
        end
 end
@@ -203,20 +289,17 @@ end
 redef class IEscape
        redef fun inner_dup_with(d)
        do
-               if d._seqs.has_key(seq) then
-                       # Jump to a duplicated sequence
-                       return new IEscape(d._seqs[seq])
-               else
-                       # Jump to an englobing unduplicated sequence
-                       return new IEscape(seq)
-               end
+               # Get the associated mark (duplicated of not)
+               var mark = d.dup_mark(iescape_mark)
+               # Jump to the mark
+               return new IEscape(mark)
        end
 end
 
 redef class IAbort
        redef fun inner_dup_with(d)
        do
-               return new IAbort(texts, property_location, module_location)
+               return new IAbort(texts, module_location)
        end
 end
 
@@ -241,29 +324,108 @@ redef class INew
        end
 end
 
-redef class IClosCall
+redef class IAllocateInstance
+       redef fun inner_dup_with(d)
+       do
+               return new IAllocateInstance(stype)
+       end
+end
+
+redef class IStaticCall
+       redef fun inner_dup_with(d)
+       do
+               return new IStaticCall(property, d.dup_iregs(exprs))
+       end
+end
+
+redef class ICheckInstance
+       redef fun inner_dup_with(d)
+       do
+               return new ICheckInstance(stype, d.dup_ireg(expr))
+       end
+end
+
+redef class IInitAttributes
        redef fun inner_dup_with(d)
        do
-               var c2 = new IClosCall(closure_decl, d.dup_iregs(exprs))
-               var bs = break_seq
-               if bs != null then
-                       var bs2 = new ISeq
-                       c2.break_seq = bs2
-                       bs.dup_seq_to(d, bs2)
+               return new IInitAttributes(stype, d.dup_ireg(expr))
+       end
+end
+
+redef class IClosCall
+       redef fun dup_with(d)
+       do
+               if d._closures.has_key(closure_decl) then
+                       # The icloscall need to be replaced with an inlined closdef
+                       var closdef = d._closures[closure_decl]
+                       if closdef == null then
+                               # Default is already guarded and inlined
+                               return
+                       end
+                       # Break sequence cannot be inlined :/
+                       assert break_seq == null
+                       # Inline the closdef
+                       var res = d._icb.inline_routine(closdef, d.dup_iregs(exprs), null)
+                       if result != null then
+                               assert res != null
+                               d._icb.stmt(new IMove(d.dup_ireg(result.as(not null)), res))
+                       end
+               else
+                       # Standard icloscall duplication
+                       super
                end
-               return c2
+       end
+
+       redef fun inner_dup_with(d)
+       do
+               return new IClosCall(closure_decl, exprs)
        end
 end
 
 redef class INative
        redef fun inner_dup_with(d)
        do
-               var c2 = new INative(code, d.dup_iregs(exprs))
+               var c2 = new INative(method, d.dup_iregs(exprs))
                c2.is_pure = is_pure
                return c2
        end
 end
 
+redef class IIntValue
+       redef fun inner_dup_with(d)
+       do
+               return new IIntValue(value)
+       end
+end
+
+redef class IBoolValue
+       redef fun inner_dup_with(d)
+       do
+               return new IBoolValue(value)
+       end
+end
+
+redef class IStringValue
+       redef fun inner_dup_with(d)
+       do
+               return new IStringValue(value)
+       end
+end
+
+redef class IFloatValue
+       redef fun inner_dup_with(d)
+       do
+               return new IFloatValue(value)
+       end
+end
+
+redef class ICharValue
+       redef fun inner_dup_with(d)
+       do
+               return new ICharValue(value)
+       end
+end
+
 redef class IMove
        redef fun inner_dup_with(d)
        do
@@ -295,7 +457,7 @@ end
 redef class ITypeCheck
        redef fun inner_dup_with(d)
        do
-               return new ITypeCheck(d.dup_ireg(expr), stype)
+               return new ITypeCheck(d.dup_ireg(expr1), d.dup_ireg(expr2), stype)
        end
 end
 
@@ -325,6 +487,18 @@ end
 redef class IHasClos
        redef fun inner_dup_with(d)
        do
-               return new IHasClos(closure_decl)
+               if d._closures.has_key(closure_decl) then
+                       # closdef will be inlined
+                       var closdef = d._closures[closure_decl]
+                       var res: IRegister
+                       if closdef != null then
+                               res = d._icb.lit_true_reg
+                       else
+                               res = d._icb.lit_false_reg
+                       end
+                       return new IMove(d.dup_ireg(result.as(not null)), res)
+               else
+                       return new IHasClos(closure_decl)
+               end
        end
 end