X-Git-Url: http://nitlanguage.org diff --git a/src/abstract_compiler.nit b/src/compiler/abstract_compiler.nit similarity index 96% rename from src/abstract_compiler.nit rename to src/compiler/abstract_compiler.nit index 9e67eae..ef02aec 100644 --- a/src/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@ -18,10 +18,10 @@ module abstract_compiler import literal -import typing -import auto_super_init +import semantize import platform import c_tools +private import annotation # Add compiling options redef class ToolContext @@ -395,7 +395,7 @@ class MakefileToolchain if f.compiles_to_o_file then ofiles.add(o) if f.add_to_jar then java_files.add(f) end - + if not java_files.is_empty then var jar_file = "{outpath}.jar" @@ -445,13 +445,13 @@ abstract class AbstractCompiler # The main module of the program currently compiled # Is assigned during the separate compilation - var mainmodule: MModule writable + var mainmodule: MModule is writable # The real main module of the program var realmainmodule: MModule # The modeulbuilder used to know the model and the AST - var modelbuilder: ModelBuilder protected writable + var modelbuilder: ModelBuilder is protected writable # Is hardening asked? (see --hardening) fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value @@ -480,7 +480,7 @@ abstract class AbstractCompiler fun new_visitor: VISITOR is abstract # Where global declaration are stored (the main .h) - var header: CodeWriter writable + var header: CodeWriter is writable # Provide a declaration that can be requested (before or latter) by a visitor fun provide_declaration(key: String, s: String) @@ -643,10 +643,12 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ); end # Generate the main C function. + # # This function: - # * allocate the Sys object if it exists - # * call init if is exists - # * call main if it exists + # + # * allocate the Sys object if it exists + # * call init if is exists + # * call main if it exists fun compile_main_function do var v = self.new_visitor @@ -930,12 +932,13 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ) { end # Display stats about compilation process + # # Metrics used: - # * type tests against resolved types (`x isa Collection[Animal]`) - # * type tests against unresolved types (`x isa Collection[E]`) - # * type tests skipped - # * type tests total - # * + # + # * type tests against resolved types (`x isa Collection[Animal]`) + # * type tests against unresolved types (`x isa Collection[E]`) + # * type tests skipped + # * type tests total fun display_stats do if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then @@ -1015,10 +1018,10 @@ abstract class AbstractCompilerVisitor var compiler: COMPILER # The current visited AST node - var current_node: nullable ANode writable = null + var current_node: nullable ANode = null is writable # The current `Frame` - var frame: nullable Frame writable + var frame: nullable Frame is writable # Alias for self.compiler.mainmodule.object_type fun object_type: MClassType do return self.compiler.mainmodule.object_type @@ -1261,7 +1264,7 @@ abstract class AbstractCompilerVisitor private var escapemark_names = new HashMap[EscapeMark, String] # Return a "const char*" variable associated to the classname of the dynamic type of an object - # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program + # NOTE: we do not return a `RuntimeVariable` "NativeString" as the class may not exist in the module/program fun class_name_string(value: RuntimeVariable): String is abstract # Variables handling @@ -1332,7 +1335,7 @@ abstract class AbstractCompilerVisitor var mtype = recv.mtype var finalizable_type = compiler.mainmodule.finalizable_type if finalizable_type != null and not mtype.need_anchor and - mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then + mtype.is_subtype(compiler.mainmodule, null, finalizable_type) then add "gc_register_finalizer({recv});" end end @@ -1541,7 +1544,7 @@ abstract class AbstractRuntimeFunction # Non cached version of `c_name` protected fun build_c_name: String is abstract - protected var c_name_cache: nullable String writable = null + protected var c_name_cache: nullable String = null is writable # Implements a call of the runtime_function # May inline the body or generate a C function call @@ -1564,11 +1567,11 @@ class RuntimeVariable var mtype: MType # The current casted type of the variable (as known in Nit) - var mcasttype: MType writable + var mcasttype: MType is writable # If the variable exaclty a mcasttype? # false (usual value) means that the variable is a mcasttype or a subtype. - var is_exact: Bool writable = false + var is_exact: Bool = false is writable init(name: String, mtype: MType, mcasttype: MType) do @@ -1618,10 +1621,10 @@ class Frame var arguments: Array[RuntimeVariable] # The runtime_variable associated to the return (in a function) - var returnvar: nullable RuntimeVariable writable = null + var returnvar: nullable RuntimeVariable = null is writable # The label at the end of the property - var returnlabel: nullable String writable = null + var returnlabel: nullable String = null is writable end redef class MType @@ -1636,7 +1639,7 @@ redef class MType # Return the name of the C structure associated to a Nit live type fun c_name: String is abstract - protected var c_name_cache: nullable String protected writable + protected var c_name_cache: nullable String is protected writable end redef class MClassType @@ -1889,6 +1892,18 @@ redef class AMethPropdef v.supercall(mpropdef, arguments.first.mtype.as(MClassType), arguments) end + # Try special compilation + if mpropdef.is_intern then + if compile_intern_to_c(v, mpropdef, arguments) then return + else if mpropdef.is_extern then + if mpropdef.mproperty.is_init then + if compile_externinit_to_c(v, mpropdef, arguments) then return + else + if compile_externmeth_to_c(v, mpropdef, arguments) then return + end + end + + # Compile block if any var n_block = n_block if n_block != null then for i in [0..mpropdef.msignature.arity[ do @@ -1896,17 +1911,13 @@ redef class AMethPropdef v.assign(v.variable(variable), arguments[i+1]) end v.stmt(n_block) - else if mpropdef.is_intern then - compile_intern_to_c(v, mpropdef, arguments) - else if mpropdef.is_extern then - if mpropdef.mproperty.is_init then - compile_externinit_to_c(v, mpropdef, arguments) - else - compile_externmeth_to_c(v, mpropdef, arguments) - end - else - abort + return end + + # We have a problem + var cn = v.class_name_string(arguments.first) + v.add("PRINT_ERROR(\"Runtime error: uncompiled method `%s` called on `%s`. NOT YET IMPLEMENTED\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});") + v.add_raw_abort end redef fun can_inline @@ -1919,7 +1930,7 @@ redef class AMethPropdef return false end - fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) + fun compile_intern_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool do var pname = mpropdef.mproperty.name var cname = mpropdef.mclassdef.mclass.name @@ -1936,239 +1947,242 @@ redef class AMethPropdef if cname == "Int" then if pname == "output" then v.add("printf(\"%ld\\n\", {arguments.first});") - return + return true else if pname == "object_id" then v.ret(arguments.first) - return + return true else if pname == "+" then v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) - return + return true else if pname == "-" then v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) - return + return true else if pname == "unary -" then v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) - return + return true else if pname == "*" then v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) - return + return true else if pname == "/" then v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) - return + return true else if pname == "%" then v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null))) - return + return true else if pname == "lshift" then v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null))) - return + return true else if pname == "rshift" then v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null))) - return + return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + return true else if pname == "!=" then var res = v.equal_test(arguments[0], arguments[1]) v.ret(v.new_expr("!{res}", ret.as(not null))) - return + return true else if pname == "<" then v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">" then v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) - return + return true else if pname == "<=" then v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">=" then v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) - return + return true else if pname == "to_f" then v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null))) - return + return true else if pname == "ascii" then v.ret(v.new_expr("{arguments[0]}", ret.as(not null))) - return + return true end else if cname == "Char" then if pname == "output" then v.add("printf(\"%c\", {arguments.first});") - return + return true else if pname == "object_id" then v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) - return + return true else if pname == "successor" then v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) - return + return true else if pname == "predecessor" then v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) - return + return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + return true else if pname == "!=" then var res = v.equal_test(arguments[0], arguments[1]) v.ret(v.new_expr("!{res}", ret.as(not null))) - return + return true else if pname == "<" then v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">" then v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) - return + return true else if pname == "<=" then v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">=" then v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) - return + return true else if pname == "to_i" then v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null))) - return + return true else if pname == "ascii" then v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null))) - return + return true end else if cname == "Bool" then if pname == "output" then v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");") - return + return true else if pname == "object_id" then v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) - return + return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + return true else if pname == "!=" then var res = v.equal_test(arguments[0], arguments[1]) v.ret(v.new_expr("!{res}", ret.as(not null))) - return + return true end else if cname == "Float" then if pname == "output" then v.add("printf(\"%f\\n\", {arguments.first});") - return + return true else if pname == "object_id" then v.ret(v.new_expr("(double){arguments.first}", ret.as(not null))) - return + return true else if pname == "+" then v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) - return + return true else if pname == "-" then v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) - return + return true else if pname == "unary -" then v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) - return + return true else if pname == "succ" then v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null))) - return + return true else if pname == "prec" then v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null))) - return + return true else if pname == "*" then v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) - return + return true else if pname == "/" then v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) - return + return true else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + return true else if pname == "!=" then var res = v.equal_test(arguments[0], arguments[1]) v.ret(v.new_expr("!{res}", ret.as(not null))) - return + return true else if pname == "<" then v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">" then v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) - return + return true else if pname == "<=" then v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) - return + return true else if pname == ">=" then v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) - return + return true else if pname == "to_i" then v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) - return + return true end else if cname == "NativeString" then if pname == "[]" then v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null))) - return + return true else if pname == "[]=" then v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};") - return + return true else if pname == "copy_to" then v.add("memmove({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});") - return + return true else if pname == "atoi" then v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null))) - return + return true else if pname == "init" then v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null))) - return + return true end else if cname == "NativeArray" then v.native_array_def(pname, ret, arguments) - return + return true end if pname == "exit" then v.add("exit({arguments[1]});") - return + return true else if pname == "sys" then v.ret(v.new_expr("glob_sys", ret.as(not null))) - return + return true else if pname == "calloc_string" then v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null))) - return + return true else if pname == "calloc_array" then v.calloc_array(ret.as(not null), arguments) - return + return true else if pname == "object_id" then v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) - return + return true else if pname == "is_same_type" then v.ret(v.is_same_type_test(arguments[0], arguments[1])) - return + return true else if pname == "is_same_instance" then v.ret(v.equal_test(arguments[0], arguments[1])) - return + return true else if pname == "output_class_name" then var nat = v.class_name_string(arguments.first) v.add("printf(\"%s\\n\", {nat});") - return + return true else if pname == "native_class_name" then var nat = v.class_name_string(arguments.first) v.ret(v.new_expr("(char*){nat}", ret.as(not null))) - return + return true else if pname == "force_garbage_collection" then v.add("nit_gcollect();") - return + return true else if pname == "native_argc" then v.ret(v.new_expr("glob_argc", ret.as(not null))) - return + return true else if pname == "native_argv" then v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null))) - return + return true end - v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");") - debug("Not implemented {mpropdef}") + return false end - fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) + # Compile an extern method + # Return `true` if the compilation was successful, `false` if a fall-back is needed + fun compile_externmeth_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool do var externname - var nextern = self.n_extern - if nextern == null then - v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");") - v.add("show_backtrace(1);") - return + var at = self.get_single_annotation("extern", v.compiler.modelbuilder) + if at != null then + externname = at.arg_as_string(v.compiler.modelbuilder) + if externname == null then return false + else + var nextern = self.n_extern + if nextern == null then return false + externname = nextern.text.substring(1, nextern.text.length-2) end - externname = nextern.text.substring(1, nextern.text.length-2) if location.file != null then var file = location.file.filename v.add_extern(file) @@ -2189,18 +2203,23 @@ redef class AMethPropdef res = v.box_extern(res, ret.as(not null)) v.ret(res) end + return true end - fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) + # Compile an extern factory + # Return `true` if the compilation was successful, `false` if a fall-back is needed + fun compile_externinit_to_c(v: AbstractCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]): Bool do var externname - var nextern = self.n_extern - if nextern == null then - v.add("PRINT_ERROR(\"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");") - v.add("show_backtrace(1);") - return + var at = self.get_single_annotation("extern", v.compiler.modelbuilder) + if at != null then + externname = at.arg_as_string(v.compiler.modelbuilder) + if externname == null then return false + else + var nextern = self.n_extern + if nextern == null then return false + externname = nextern.text.substring(1, nextern.text.length-2) end - externname = nextern.text.substring(1, nextern.text.length-2) if location.file != null then var file = location.file.filename v.add_extern(file) @@ -2215,6 +2234,7 @@ redef class AMethPropdef v.add("{res} = {externname}({arguments.join(", ")});") res = v.box_extern(res, ret) v.ret(res) + return true end end @@ -3053,4 +3073,3 @@ for mmodule in mmodules do end toolcontext.run_global_phases(ms) end -