icode: add IAllocateInstance, ICheckInstance and IInitAttributes
[nit.git] / src / icode / icode_tools.nit
index 22be379..100c4b3 100644 (file)
@@ -15,7 +15,7 @@
 # limitations under the License.
 
 # Tools to manipulate intermediace nit code representation
-import icode_base
+import icode_builder
 
 # A simple visitor to visit icode structures
 class ICodeVisitor
@@ -91,44 +91,83 @@ 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
+       # IRoutine currently inlining
+       # Used to avoid recursive inlining
+       var _current_inlining: Array[IRoutine] = new Array[IRoutine]
+
+       # Return false if routine can be saflely inlined
+       fun is_currently_inlining_routine(routine: IRoutine): Bool
+       do
+               return routine == iroutine or _current_inlining.has(routine)
+       end
+
+       # Inline an iroutine in the current icode sequence
+       # Require not is_currently_inlining
+       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 not is_currently_inlining_routine(routine)
+               _current_inlining.add(routine)
+               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 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)
+               _current_inlining.pop
+               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
+       # Return a correct bunch of registers
        fun dup_iregs(regs: Sequence[IRegister]): Sequence[IRegister]
        do
                var a = new Array[IRegister].with_capacity(regs.length)
@@ -145,20 +184,35 @@ private class ICodeDupContext
        # The assocation between old_ireg and new_ireg
        # Used by dup_ireg
        var _registers: Map[IRegister, IRegister] = new HashMap[IRegister, IRegister]
+
+       # 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
+                       c.closure_defs = closure_defs
+               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,10 +228,13 @@ 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
+               var old_seq = d._icb.seq
+               d._icb.seq = dest
                d._seqs[self] = dest
                for c in icodes do
-                       dest.icodes.add(c.dup_with(d))
+                       c.dup_with(d)
                end
+               d._icb.seq = old_seq
        end
 end
 
@@ -241,17 +298,49 @@ redef class INew
        end
 end
 
-redef class IClosCall
+redef class IAllocateInstance
        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 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
+               return new IInitAttributes(stype, d.dup_ireg(expr))
+       end
+end
+
+redef class IClosCall
+       redef fun dup_with(d)
+       do
+               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
+               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
-               return c2
        end
 end
 
@@ -325,6 +414,13 @@ end
 redef class IHasClos
        redef fun inner_dup_with(d)
        do
-               return new IHasClos(closure_decl)
+               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)
        end
 end