nitg: add GlobalCompiler::new_file
[nit.git] / src / separate_compiler.nit
index a155d94..612c22d 100644 (file)
@@ -26,6 +26,12 @@ redef class ToolContext
        # --no-inline-intern
        var opt_no_inline_intern: OptionBool = new OptionBool("Do not inline call to intern methods", "--no-inline-intern")
 
+       # --no-union-attribute
+       var opt_no_union_attribute: OptionBool = new OptionBool("Put primitive attibutes in a box instead of an union", "--no-union-attribute")
+
+       # --no-shortcut-equate
+       var opt_no_shortcut_equate: OptionBool = new OptionBool("Always call == in a polymorphic way", "--no-shortcut-equal")
+
        # --inline-coloring-numbers
        var opt_inline_coloring_numbers: OptionBool = new OptionBool("Inline colors and ids", "--inline-coloring-numbers")
 
@@ -46,6 +52,8 @@ redef class ToolContext
                super
                self.option_context.add_option(self.opt_separate)
                self.option_context.add_option(self.opt_no_inline_intern)
+               self.option_context.add_option(self.opt_no_union_attribute)
+               self.option_context.add_option(self.opt_no_shortcut_equate)
                self.option_context.add_option(self.opt_inline_coloring_numbers)
                self.option_context.add_option(self.opt_bm_typing)
                self.option_context.add_option(self.opt_phmod_typing)
@@ -61,6 +69,7 @@ redef class ModelBuilder
                self.toolcontext.info("*** COMPILING TO C ***", 1)
 
                var compiler = new SeparateCompiler(mainmodule, runtime_type_analysis, self)
+               compiler.compile_header
 
                # compile class structures
                for m in mainmodule.in_importation.greaters do
@@ -70,14 +79,17 @@ redef class ModelBuilder
                end
 
                # The main function of the C
+               compiler.new_file
                compiler.compile_main_function
 
                # compile methods
                for m in mainmodule.in_importation.greaters do
+                       compiler.new_file
                        compiler.compile_module_to_c(m)
                end
 
                # compile live & cast type structures
+               compiler.new_file
                var mtypes = compiler.do_type_coloring
                for t in mtypes do
                        compiler.compile_type_to_c(t)
@@ -90,6 +102,8 @@ redef class ModelBuilder
                        end
                end
 
+               compiler.display_stats
+
                write_and_make(compiler)
        end
 end
@@ -137,7 +151,7 @@ class SeparateCompiler
 
        redef fun compile_header_structs do
                self.header.add_decl("typedef void(*nitmethod_t)(void); /* general C type representing a Nit method. */")
-               self.header.add_decl("typedef void* nitattribute_t; /* general C type representing a Nit attribute. */")
+               self.compile_header_attribute_structs
                self.header.add_decl("struct class \{ int box_kind; nitmethod_t vft[1]; \}; /* general C type representing a Nit class. */")
 
                if modelbuilder.toolcontext.opt_generic_tree.value then
@@ -158,6 +172,21 @@ class SeparateCompiler
                self.header.add_decl("typedef struct \{ struct type *type; struct class *class; nitattribute_t attrs[1]; \} val; /* general C type representing a Nit instance. */")
        end
 
+       fun compile_header_attribute_structs
+       do
+               if modelbuilder.toolcontext.opt_no_union_attribute.value then
+                       self.header.add_decl("typedef void* nitattribute_t; /* general C type representing a Nit attribute. */")
+               else
+                       self.header.add_decl("typedef union \{")
+                       self.header.add_decl("void* val;")
+                       for c, v in self.box_kinds do
+                               var t = c.mclass_type
+                               self.header.add_decl("{t.ctype} {t.ctypename};")
+                       end
+                       self.header.add_decl("\} nitattribute_t; /* general C type representing a Nit attribute. */")
+               end
+       end
+
        redef fun compile_class_names do
                abort # There is no class name compilation since the name is stored in the type structure
        end
@@ -830,10 +859,12 @@ class SeparateCompiler
                        v.add("if(type == NULL) \{")
                        v.add_abort("type null")
                        v.add("\}")
-                       v.add("if(type->unanchored_table == NULL) \{")
-                       v.add("fprintf(stderr, \"Insantiation of a dead type: %s\\n\", type->name);")
-                       v.add_abort("type dead")
-                       v.add("\}")
+                       if not v.compiler.modelbuilder.toolcontext.opt_generic_tree.value then
+                               v.add("if(type->unanchored_table == NULL) \{")
+                               v.add("fprintf(stderr, \"Insantiation of a dead type: %s\\n\", type->name);")
+                               v.add_abort("type dead")
+                               v.add("\}")
+                       end
                end
                v.add("{res}->class = (struct class*) &class_{c_name};")
 
@@ -1106,7 +1137,7 @@ class SeparateCompilerVisitor
                var recv = arguments.first
                s.append("val*")
                ss.append("{recv}")
-               self.varargize(msignature, arguments)
+               self.varargize(mmethod.intro, mmethod.intro.msignature.as(not null), arguments)
                for i in [0..msignature.arity[ do
                        var a = arguments[i+1]
                        var t = msignature.mparameters[i].mtype
@@ -1147,6 +1178,22 @@ class SeparateCompilerVisitor
                        end
                        self.add("\} else \{")
                end
+               if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=") then
+                       assert res != null
+                       # 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
+                               if mmethod.name == "==" then
+                                       self.add("{res} = 0; /* arg is null but recv is not */")
+                               else
+                                       self.add("{res} = 1; /* arg is null and recv is not */")
+                               end
+                               if maybenull then
+                                       self.add("\}")
+                               end
+                               return res
+                       end
+               end
 
                var r
                if ret == null then r = "void" else r = ret.ctype
@@ -1208,11 +1255,50 @@ class SeparateCompilerVisitor
                return res
        end
 
+       redef fun vararg_instance(mpropdef, recv, varargs, elttype)
+       do
+               # A vararg must be stored into an new array
+               # The trick is that the dymaic type of the array may depends on the receiver
+               # of the method (ie recv) if the static type is unresolved
+               # This is more complex than usual because the unanchored type must not be resolved
+               # with the current receiver (ie self).
+               # Therefore to isolate the resolution from self, a local Frame is created.
+               # One can see this implementation as an inlined method of the receiver whose only
+               # job is to allocate the array
+               var old_frame = self.frame
+               var frame = new Frame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv])
+               self.frame = frame
+               #print "required Array[{elttype}] for recv {recv.inspect}. bound=Array[{self.resolve_for(elttype, recv)}]. selfvar={frame.arguments.first.inspect}"
+               var res = self.array_instance(varargs, elttype)
+               self.frame = old_frame
+               return res
+       end
+
        redef fun isset_attribute(a, recv)
        do
                self.check_recv_notnull(recv)
                var res = self.new_var(bool_type)
-               self.add("{res} = {recv}->attrs[{a.const_color}] != NULL; /* {a} on {recv.inspect}*/")
+
+               # What is the declared type of the attribute?
+               var mtype = a.intro.static_mtype.as(not null)
+               var intromclassdef = a.intro.mclassdef
+               mtype = mtype.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
+
+               if mtype isa MNullableType then
+                       self.add("{res} = 1; /* easy isset: {a} on {recv.inspect} */")
+                       return res
+               end
+
+               if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
+                       self.add("{res} = {recv}->attrs[{a.const_color}] != NULL; /* {a} on {recv.inspect}*/")
+               else
+
+                       if mtype.ctype == "val*" then
+                               self.add("{res} = {recv}->attrs[{a.const_color}].val != NULL; /* {a} on {recv.inspect} */")
+                       else
+                               self.add("{res} = 1; /* NOT YET IMPLEMENTED: isset of primitives: {a} on {recv.inspect} */")
+                       end
+               end
                return res
        end
 
@@ -1225,22 +1311,37 @@ class SeparateCompilerVisitor
                var intromclassdef = a.intro.mclassdef
                ret = ret.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true)
 
-               # Get the attribute or a box (ie. always a val*)
-               var cret = self.object_type.as_nullable
-               var res = self.new_var(cret)
-               res.mcasttype = ret
-               self.add("{res} = {recv}->attrs[{a.const_color}]; /* {a} on {recv.inspect} */")
+               if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
+                       # Get the attribute or a box (ie. always a val*)
+                       var cret = self.object_type.as_nullable
+                       var res = self.new_var(cret)
+                       res.mcasttype = ret
 
-               # Check for Uninitialized attribute
-               if not ret isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then
-                       self.add("if ({res} == NULL) \{")
-                       self.add_abort("Uninitialized attribute {a.name}")
-                       self.add("\}")
-               end
+                       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
+                               self.add("if ({res} == NULL) \{")
+                               self.add_abort("Uninitialized attribute {a.name}")
+                               self.add("\}")
+                       end
+
+                       # Return the attribute or its unboxed version
+                       # Note: it is mandatory since we reuse the box on write, we do not whant that the box escapes
+                       return self.autobox(res, ret)
+               else
+                       var res = self.new_var(ret)
+                       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
+                               self.add("if ({res} == NULL) \{")
+                               self.add_abort("Uninitialized attribute {a.name}")
+                               self.add("\}")
+                       end
 
-               # Return the attribute or its unboxed version
-               # Note: it is mandatory since we reuse the box on write, we do not whant that the box escapes
-               return self.autobox(res, ret)
+                       return res
+               end
        end
 
        redef fun write_attribute(a, recv, value)
@@ -1254,20 +1355,25 @@ class SeparateCompilerVisitor
 
                # Adapt the value to the declared type
                value = self.autobox(value, mtype)
-               var attr = "{recv}->attrs[{a.const_color}]"
-               if mtype.ctype != "val*" then
-                       assert mtype isa MClassType
-                       # The attribute is primitive, thus we store it in a box
-                       # The trick is to create the box the first time then resuse the box
-                       self.add("if ({attr} != NULL) \{")
-                       self.add("((struct instance_{mtype.c_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
-                       self.add("\} else \{")
-                       value = self.autobox(value, self.object_type.as_nullable)
-                       self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
-                       self.add("\}")
+
+               if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
+                       var attr = "{recv}->attrs[{a.const_color}]"
+                       if mtype.ctype != "val*" then
+                               assert mtype isa MClassType
+                               # The attribute is primitive, thus we store it in a box
+                               # The trick is to create the box the first time then resuse the box
+                               self.add("if ({attr} != NULL) \{")
+                               self.add("((struct instance_{mtype.c_name}*){attr})->value = {value}; /* {a} on {recv.inspect} */")
+                               self.add("\} else \{")
+                               value = self.autobox(value, self.object_type.as_nullable)
+                               self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
+                               self.add("\}")
+                       else
+                               # The attribute is not primitive, thus store it direclty
+                               self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
+                       end
                else
-                       # The attribute is not primitive, thus store it direclty
-                       self.add("{attr} = {value}; /* {a} on {recv.inspect} */")
+                       self.add("{recv}->attrs[{a.const_color}].{mtype.ctypename} = {value}; /* {a} on {recv.inspect} */")
                end
        end
 
@@ -1343,7 +1449,7 @@ class SeparateCompilerVisitor
        end
 
 
-       redef fun type_test(value, mtype)
+       redef fun type_test(value, mtype, tag)
        do
                self.add("/* {value.inspect} isa {mtype} */")
                var compiler = self.compiler.as(SeparateCompiler)
@@ -1365,6 +1471,15 @@ class SeparateCompilerVisitor
                        ntype = ntype.mtype
                end
 
+               if value.mcasttype.is_subtype(self.frame.mpropdef.mclassdef.mmodule, self.frame.mpropdef.mclassdef.bound_mtype, mtype) then
+                       self.add("{res} = 1; /* easy {value.inspect} isa {mtype}*/")
+                       if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
+                               self.compiler.count_type_test_skipped[tag] += 1
+                               self.add("count_type_test_skipped_{tag}++;")
+                       end
+                       return res
+               end
+
                if ntype.need_anchor then
                        var type_struct = self.get_name("type_struct")
                        self.add_decl("struct type* {type_struct};")
@@ -1398,6 +1513,10 @@ class SeparateCompilerVisitor
                                else
                                        self.add("{type_struct} = {recv_type_info}->unanchored_table->types[{ntype.const_color}];")
                                end
+                               if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
+                                       self.compiler.count_type_test_unresolved[tag] += 1
+                                       self.add("count_type_test_unresolved_{tag}++;")
+                               end
                        end
                        self.add("{cltype} = {type_struct}->color;")
                        self.add("{idtype} = {type_struct}->id;")
@@ -1407,6 +1526,10 @@ class SeparateCompilerVisitor
                        self.add("{cltype} = type_{mtype.c_name}.color;")
                        self.add("{idtype} = type_{mtype.c_name}.id;")
                        self.add("{is_nullable} = type_{mtype.c_name}.is_nullable;")
+                       if compiler.modelbuilder.toolcontext.opt_typing_test_metrics.value then
+                               self.compiler.count_type_test_resolved[tag] += 1
+                               self.add("count_type_test_resolved_{tag}++;")
+                       end
                else
                        self.add("printf(\"NOT YET IMPLEMENTED: type_test(%s, {mtype}).\\n\", \"{value.inspect}\"); exit(1);")
                end