nitc: renamed --no-check-initialization in --no-check-attr-isset to follow the new...
[nit.git] / src / separate_compiler.nit
index cd5692f..4bbdd2d 100644 (file)
@@ -18,7 +18,6 @@ module separate_compiler
 import abstract_compiler
 import layout_builders
 import rapid_type_analysis
-import compiler_ffi
 
 # Add separate compiler specific options
 redef class ToolContext
@@ -807,7 +806,8 @@ class SeparateCompiler
                        self.header.add_decl("struct instance_{c_instance_name} \{")
                        self.header.add_decl("const struct type *type;")
                        self.header.add_decl("const struct class *class;")
-                       # NativeArrays are just a instance header followed by an array of values
+                       # NativeArrays are just a instance header followed by a length and an array of values
+                       self.header.add_decl("int length;")
                        self.header.add_decl("val* values[0];")
                        self.header.add_decl("\};")
 
@@ -815,15 +815,16 @@ class SeparateCompiler
                        self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(int length, const struct type* type);")
                        v.add_decl("/* allocate {mtype} */")
                        v.add_decl("{mtype.ctype} NEW_{c_name}(int length, const struct type* type) \{")
-                       var res = v.new_named_var(mtype, "self")
-                       res.is_exact = true
+                       var res = v.get_name("self")
+                       v.add_decl("struct instance_{c_instance_name} *{res};")
                        var mtype_elt = mtype.arguments.first
                        v.add("{res} = nit_alloc(sizeof(struct instance_{c_instance_name}) + length*sizeof({mtype_elt.ctype}));")
                        v.add("{res}->type = type;")
                        hardening_live_type(v, "type")
                        v.require_declaration("class_{c_name}")
                        v.add("{res}->class = &class_{c_name};")
-                       v.add("return {res};")
+                       v.add("{res}->length = length;")
+                       v.add("return (val*){res};")
                        v.add("\}")
                        return
                end
@@ -934,10 +935,10 @@ class SeparateCompiler
                self.header.add_decl("struct nitni_instance \{struct instance *value;\};")
        end
        
-       redef fun finalize_ffi_for_module(nmodule)
+       redef fun finalize_ffi_for_module(mmodule)
        do
                var old_module = self.mainmodule
-               self.mainmodule = nmodule.mmodule.as(not null)
+               self.mainmodule = mmodule
                super
                self.mainmodule = old_module
        end
@@ -984,7 +985,7 @@ class SeparateCompilerVisitor
                        self.require_declaration("BOX_{valtype.c_name}")
                        self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
                        return res
-               else if value.mtype.cname_blind == "void*" and mtype.cname_blind == "void*" then
+               else if value.mtype.ctype == "void*" and mtype.ctype == "void*" then
                        return value
                else
                        # Bad things will appen!
@@ -1012,13 +1013,21 @@ class SeparateCompilerVisitor
        do
                var rta = compiler.runtime_type_analysis
                var recv = args.first.mtype
-               if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and recv isa MClassType then
+               if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null then
                        var tgs = rta.live_targets(callsite)
                        if tgs.length == 1 then
                                # DIRECT CALL
                                var mmethod = callsite.mproperty
                                self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), args)
-                               return call(tgs.first, recv, args)
+                               var res0 = before_send(mmethod, args)
+                               var res = call(tgs.first, tgs.first.mclassdef.bound_mtype, args)
+                               if res0 != null then
+                                       assert res != null
+                                       self.assign(res0, res)
+                                       res = res0
+                               end
+                               add("\}") # close the before_send
+                               return res
                        end
                end
                return super
@@ -1040,47 +1049,27 @@ class SeparateCompilerVisitor
                return table_send(mmethod, arguments, mmethod.const_color)
        end
 
-       private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], const_color: String): nullable RuntimeVariable
+       # Handel common special cases before doing the effective method invocation
+       # This methods handle the `==` and `!=` methods and the case of the null receiver.
+       # Note: a { is open in the generated C, that enclose and protect the effective method invocation.
+       # Client must not forget to close the } after them.
+       #
+       # The value returned is the result of the common special cases.
+       # If not null, client must compine it with the result of their own effective method invocation.
+       #
+       # If `before_send` can shortcut the whole message sending, a dummy `if(0){`
+       # is generated to cancel the effective method invocation that will follow
+       # TODO: find a better approach
+       private fun before_send(mmethod: MMethod, arguments: Array[RuntimeVariable]): nullable RuntimeVariable
        do
-               compiler.modelbuilder.nb_invok_by_tables += 1
-
-               assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
-
-               var res: nullable RuntimeVariable
-               var msignature = mmethod.intro.msignature.resolve_for(mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.mmodule, true)
-               var ret = msignature.return_mtype
-               if mmethod.is_new then
-                       ret = arguments.first.mtype
-                       res = self.new_var(ret)
-               else if ret == null then
-                       res = null
-               else
-                       res = self.new_var(ret)
-               end
-
-               var s = new Buffer
-               var ss = new Buffer
-
+               var res: nullable RuntimeVariable = null
                var recv = arguments.first
-               s.append("val*")
-               ss.append("{recv}")
-               for i in [0..msignature.arity[ do
-                       var a = arguments[i+1]
-                       var t = msignature.mparameters[i].mtype
-                       if i == msignature.vararg_rank then
-                               t = arguments[i+1].mcasttype
-                       end
-                       s.append(", {t.ctype}")
-                       a = self.autobox(a, t)
-                       ss.append(", {a}")
-               end
-
                var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_other.value or mmethod.name == "==" or mmethod.name == "!="
                var maybenull = recv.mcasttype isa MNullableType and consider_null
                if maybenull then
                        self.add("if ({recv} == NULL) \{")
                        if mmethod.name == "==" then
-                               assert res != null
+                               res = self.new_var(bool_type)
                                var arg = arguments[1]
                                if arg.mcasttype isa MNullableType then
                                        self.add("{res} = ({arg} == NULL);")
@@ -1090,7 +1079,7 @@ class SeparateCompilerVisitor
                                        self.add("{res} = 0; /* {arg.inspect} cannot be null */")
                                end
                        else if mmethod.name == "!=" then
-                               assert res != null
+                               res = self.new_var(bool_type)
                                var arg = arguments[1]
                                if arg.mcasttype isa MNullableType then
                                        self.add("{res} = ({arg} != NULL);")
@@ -1103,9 +1092,11 @@ class SeparateCompilerVisitor
                                self.add_abort("Receiver is null")
                        end
                        self.add("\} else \{")
+               else
+                       self.add("\{")
                end
                if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=") then
-                       assert res != null
+                       if res == null then res = self.new_var(bool_type)
                        # Recv is not null, thus is arg is, it is easy to conclude (and respect the invariants)
                        var arg = arguments[1]
                        if arg.mcasttype isa MNullType then
@@ -1114,12 +1105,51 @@ class SeparateCompilerVisitor
                                else
                                        self.add("{res} = 1; /* arg is null and recv is not */")
                                end
-                               if maybenull then
-                                       self.add("\}")
-                               end
-                               return res
+                               self.add("\}") # closes the null case
+                               self.add("if (0) \{") # what follow is useless, CC will drop it
                        end
                end
+               return res
+       end
+
+       private fun table_send(mmethod: MMethod, arguments: Array[RuntimeVariable], const_color: String): nullable RuntimeVariable
+       do
+               compiler.modelbuilder.nb_invok_by_tables += 1
+               if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_tables++;")
+
+               assert arguments.length == mmethod.intro.msignature.arity + 1 else debug("Invalid arity for {mmethod}. {arguments.length} arguments given.")
+               var recv = arguments.first
+
+               var res0 = before_send(mmethod, arguments)
+
+               var res: nullable RuntimeVariable
+               var msignature = mmethod.intro.msignature.resolve_for(mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.mmodule, true)
+               var ret = msignature.return_mtype
+               if mmethod.is_new then
+                       ret = arguments.first.mtype
+                       res = self.new_var(ret)
+               else if ret == null then
+                       res = null
+               else
+                       res = self.new_var(ret)
+               end
+
+               var s = new FlatBuffer
+               var ss = new FlatBuffer
+
+               s.append("val*")
+               ss.append("{recv}")
+               for i in [0..msignature.arity[ do
+                       var a = arguments[i+1]
+                       var t = msignature.mparameters[i].mtype
+                       if i == msignature.vararg_rank then
+                               t = arguments[i+1].mcasttype
+                       end
+                       s.append(", {t.ctype}")
+                       a = self.autobox(a, t)
+                       ss.append(", {a}")
+               end
+
 
                var r
                if ret == null then r = "void" else r = ret.ctype
@@ -1132,10 +1162,14 @@ class SeparateCompilerVisitor
                        self.add("{call};")
                end
 
-               if maybenull then
-                       self.add("\}")
+               if res0 != null then
+                       assert res != null
+                       assign(res0,res)
+                       res = res0
                end
 
+               self.add("\}") # closes the null case
+
                return res
        end
 
@@ -1158,6 +1192,7 @@ class SeparateCompilerVisitor
                if (mmethoddef.is_intern and not compiler.modelbuilder.toolcontext.opt_no_inline_intern.value) or
                        (compiler.modelbuilder.toolcontext.opt_inline_some_methods.value and mmethoddef.can_inline(self)) then
                        compiler.modelbuilder.nb_invok_by_inline += 1
+                       if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_inline++;")
                        var frame = new Frame(self, mmethoddef, recvtype, arguments)
                        frame.returnlabel = self.get_name("RET_LABEL")
                        frame.returnvar = res
@@ -1171,6 +1206,7 @@ class SeparateCompilerVisitor
                        return res
                end
                compiler.modelbuilder.nb_invok_by_direct += 1
+               if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_direct++;")
 
                # Autobox arguments
                self.adapt_signature(mmethoddef, arguments)
@@ -1267,7 +1303,7 @@ class SeparateCompilerVisitor
                        self.add("{res} = {recv}->attrs[{a.const_color}]; /* {a} on {recv.inspect} */")
 
                        # Check for Uninitialized attribute
-                       if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then
+                       if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
                                self.add("if (unlikely({res} == NULL)) \{")
                                self.add_abort("Uninitialized attribute {a.name}")
                                self.add("\}")
@@ -1281,7 +1317,7 @@ class SeparateCompilerVisitor
                        self.add("{res} = {recv}->attrs[{a.const_color}].{ret.ctypename}; /* {a} on {recv.inspect} */")
 
                        # Check for Uninitialized attribute
-                       if ret.ctype == "val*" and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then
+                       if ret.ctype == "val*" and not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then
                                self.add("if (unlikely({res} == NULL)) \{")
                                self.add_abort("Uninitialized attribute {a.name}")
                                self.add("\}")
@@ -1662,6 +1698,9 @@ class SeparateCompilerVisitor
                else if pname == "[]=" then
                        self.add("{recv}[{arguments[1]}]={arguments[2]};")
                        return
+               else if pname == "length" then
+                       self.ret(self.new_expr("((struct instance_{nclass.c_instance_name}*){arguments[0]})->length", ret_type.as(not null)))
+                       return
                else if pname == "copy_to" then
                        var recv1 = "((struct instance_{nclass.c_instance_name}*){arguments[1]})->values"
                        self.add("memcpy({recv1}, {recv}, {arguments[2]}*sizeof({elttype.ctype}));")
@@ -1732,8 +1771,8 @@ class SeparateRuntimeFunction
 
                var msignature = mmethoddef.msignature.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true)
 
-               var sig = new Buffer
-               var comment = new Buffer
+               var sig = new FlatBuffer
+               var comment = new FlatBuffer
                var ret = msignature.return_mtype
                if ret != null then
                        sig.append("{ret.ctype} ")
@@ -1805,8 +1844,8 @@ class VirtualRuntimeFunction
                var frame = new Frame(v, mmethoddef, recv, arguments)
                v.frame = frame
 
-               var sig = new Buffer
-               var comment = new Buffer
+               var sig = new FlatBuffer
+               var comment = new FlatBuffer
 
                # Because the function is virtual, the signature must match the one of the original class
                var intromclassdef = self.mmethoddef.mproperty.intro.mclassdef