X-Git-Url: http://nitlanguage.org diff --git a/src/abstract_compiler.nit b/src/abstract_compiler.nit index a5d43c3..c2f4e1e 100644 --- a/src/abstract_compiler.nit +++ b/src/abstract_compiler.nit @@ -27,6 +27,8 @@ redef class ToolContext var opt_output: OptionString = new OptionString("Output file", "-o", "--output") # --no-cc var opt_no_cc: OptionBool = new OptionBool("Do not invoke C compiler", "--no-cc") + # --cc-paths + var opt_cc_path: OptionArray = new OptionArray("Set include path for C header files (may be used more than once)", "--cc-path") # --make-flags var opt_make_flags: OptionString = new OptionString("Additional options to make", "--make-flags") # --hardening @@ -56,6 +58,42 @@ redef class ToolContext end redef class ModelBuilder + # The list of directories to search for included C headers (-I for C compilers) + # The list is initially set with : + # * the toolcontext --cc-path option + # * the NIT_CC_PATH environment variable + # * some heuristics including the NIT_DIR environment variable and the progname of the process + # Path can be added (or removed) by the client + var cc_paths = new Array[String] + + redef init(model, toolcontext) + do + super + + # Look for the the Nit clib path + var path_env = "NIT_DIR".environ + if not path_env.is_empty then + var libname = "{path_env}/clib" + if libname.file_exists then cc_paths.add(libname) + end + + var libname = "{sys.program_name.dirname}/../clib" + if libname.file_exists then cc_paths.add(libname.simplify_path) + + if cc_paths.is_empty then + toolcontext.error(null, "Cannot determine the nit clib path. define envvar NIT_DIR.") + end + + # Add user defined cc_paths + cc_paths.append(toolcontext.opt_cc_path.value) + + path_env = "NIT_CC_PATH".environ + if not path_env.is_empty then + cc_paths.append(path_env.split_with(':')) + end + + end + protected fun write_and_make(compiler: AbstractCompiler) do var mainmodule = compiler.mainmodule @@ -64,50 +102,73 @@ redef class ModelBuilder # A single C file regroups many compiled rumtime functions # Note that we do not try to be clever an a small change in a Nit source file may change the content of all the generated .c files var time0 = get_time + self.toolcontext.info("*** WRITING C ***", 1) + + ".nit_compile".mkdir var outname = self.toolcontext.opt_output.value if outname == null then - outname = "{mainmodule.name}.bin" + outname = "{mainmodule.name}" end - var hfilename = ".nit_compile/{mainmodule.name}.1.h" - var h = new OFStream.open(hfilename) + var hfilename = compiler.header.file.name + ".h" + var hfilepath = ".nit_compile/{hfilename}" + var h = new OFStream.open(hfilepath) for l in compiler.header.decl_lines do h.write l h.write "\n" end + for l in compiler.header.lines do + h.write l + h.write "\n" + end h.close var cfiles = new Array[String] - var file: nullable OFStream = null - var count = 0 - - ".nit_compile".mkdir - var i = 0 - for vis in compiler.visitors do - count += vis.lines.length - if file == null or count > 10000 or vis.file_break then - i += 1 - if file != null then file.close - var cfilename = ".nit_compile/{mainmodule.name}.{i}.c" - cfiles.add(cfilename) - file = new OFStream.open(cfilename) - file.write "#include \"{mainmodule.name}.1.h\"\n" - count = vis.lines.length + for f in compiler.files do + var i = 0 + var hfile: nullable OFStream = null + var count = 0 + var cfilename = ".nit_compile/{f.name}.0.h" + hfile = new OFStream.open(cfilename) + hfile.write "#include \"{hfilename}\"\n" + for key in f.required_declarations do + if not compiler.provided_declarations.has_key(key) then + print "No provided declaration for {key}" + abort + end + hfile.write compiler.provided_declarations[key] + hfile.write "\n" end - if vis != compiler.header then + hfile.close + var file: nullable OFStream = null + for vis in f.writers do + if vis == compiler.header then continue + var total_lines = vis.lines.length + vis.decl_lines.length + if total_lines == 0 then continue + count += total_lines + if file == null or count > 10000 then + i += 1 + if file != null then file.close + cfilename = ".nit_compile/{f.name}.{i}.c" + self.toolcontext.info("new C source files to compile: {cfilename}", 3) + cfiles.add(cfilename) + file = new OFStream.open(cfilename) + file.write "#include \"{f.name}.0.h\"\n" + count = total_lines + end for l in vis.decl_lines do file.write l file.write "\n" end + for l in vis.lines do + file.write l + file.write "\n" + end end - for l in vis.lines do - file.write l - file.write "\n" - end + if file != null then file.close end - if file != null then file.close self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2) @@ -116,23 +177,34 @@ redef class ModelBuilder var makename = ".nit_compile/{mainmodule.name}.mk" var makefile = new OFStream.open(makename) - makefile.write("CC = ccache cc\nCFLAGS = -g -O2\nLDFLAGS ?= \nLDLIBS ?= -lm -lgc\n\n") + var cc_includes = "" + for p in cc_paths do + #p = "..".join_path(p) + cc_includes += " -I \"" + p + "\"" + end + makefile.write("CC = ccache cc\nCFLAGS = -g -O2\nCINCL = {cc_includes}\nLDFLAGS ?= \nLDLIBS ?= -lm -lgc\n\n") makefile.write("all: {outname}\n\n") var ofiles = new Array[String] # Compile each generated file for f in cfiles do var o = f.strip_extension(".c") + ".o" - makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -D NONITCNI -c -o {o} {f}\n\n") + makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) $(CINCL) -D NONITCNI -c -o {o} {f}\n\n") ofiles.add(o) end + + # Add gc_choser.h to aditionnal bodies + var gc_chooser = new ExternCFile("{cc_paths.first}/gc_chooser.c", "-DWITH_LIBGC") + compiler.extern_bodies.add(gc_chooser) + # Compile each required extern body into a specific .o for f in compiler.extern_bodies do - i += 1 - var o = ".nit_compile/{mainmodule.name}.{i}.o" - makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -D NONITCNI -c -o {o} {f}\n\n") + var basename = f.filename.basename(".c") + var o = ".nit_compile/{basename}.extern.o" + makefile.write("{o}: {f.filename}\n\t$(CC) $(CFLAGS) -D NONITCNI {f.cflags} -c -o {o} {f.filename}\n\n") ofiles.add(o) end + # Link edition makefile.write("{outname}: {ofiles.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outname} {ofiles.join(" ")} $(LDLIBS)\n\n") # Clean @@ -141,7 +213,7 @@ redef class ModelBuilder self.toolcontext.info("Generated makefile: {makename}", 2) var time1 = get_time - self.toolcontext.info("*** END COMPILING TO C: {time1-time0} ***", 2) + self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2) # Execute the Makefile @@ -172,8 +244,12 @@ end abstract class AbstractCompiler type VISITOR: AbstractCompilerVisitor - # The main module of the program - var mainmodule: MModule protected writable + # The main module of the program currently compiled + # Is assigned during the separate compilation + var mainmodule: MModule 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 @@ -184,30 +260,39 @@ abstract class AbstractCompiler init(mainmodule: MModule, modelbuilder: ModelBuilder) do self.mainmodule = mainmodule + self.realmainmodule = mainmodule self.modelbuilder = modelbuilder end # Force the creation of a new file # The point is to avoid contamination between must-be-compiled-separately files - fun new_file + fun new_file(name: String): CodeFile do - var v = self.new_visitor - v.file_break = true + var f = new CodeFile(name) + self.files.add(f) + return f end - # The list of all associated visitors + # The list of all associated files # Used to generate .c files - # FIXME: should not be vistors but just somewhere to store lines - var visitors: List[VISITOR] = new List[VISITOR] + var files: List[CodeFile] = new List[CodeFile] # Initialize a visitor specific for a compiler engine fun new_visitor: VISITOR is abstract # Where global declaration are stored (the main .h) - # - # FIXME: should not be a visitor but just somewhere to store lines - # FIXME: should not have a global .h since it does not help recompilations - var header: VISITOR writable + var header: CodeWriter writable + + # Provide a declaration that can be requested (before or latter) by a visitor + fun provide_declaration(key: String, s: String) + do + if self.provided_declarations.has_key(key) then + assert self.provided_declarations[key] == s + end + self.provided_declarations[key] = s + end + + private var provided_declarations = new HashMap[String, String] # Compile C headers # This method call compile_header_strucs method that has to be refined @@ -216,16 +301,7 @@ abstract class AbstractCompiler self.header.add_decl("#include ") self.header.add_decl("#include ") self.header.add_decl("#include ") - self.header.add_decl("#ifndef NOBOEHM") - self.header.add_decl("#include ") - self.header.add_decl("#ifdef NOBOEHM_ATOMIC") - self.header.add_decl("#undef GC_MALLOC_ATOMIC") - self.header.add_decl("#define GC_MALLOC_ATOMIC(x) GC_MALLOC(x)") - self.header.add_decl("#endif /*NOBOEHM_ATOMIC*/") - self.header.add_decl("#else /*NOBOEHM*/") - self.header.add_decl("#define GC_MALLOC(x) calloc(1, (x))") - self.header.add_decl("#define GC_MALLOC_ATOMIC(x) calloc(1, (x))") - self.header.add_decl("#endif /*NOBOEHM*/") + self.header.add_decl("#include ") compile_header_structs @@ -262,16 +338,17 @@ abstract class AbstractCompiler end v.add_decl("int main(int argc, char** argv) \{") v.add("glob_argc = argc; glob_argv = argv;") + v.add("initialize_gc_option();") var main_type = mainmodule.sys_type if main_type != null then var mainmodule = v.compiler.mainmodule var glob_sys = v.init_instance(main_type) v.add("glob_sys = {glob_sys};") - var main_init = mainmodule.try_get_primitive_method("init", main_type) + var main_init = mainmodule.try_get_primitive_method("init", main_type.mclass) if main_init != null then v.send(main_init, [glob_sys]) end - var main_method = mainmodule.try_get_primitive_method("main", main_type) + var main_method = mainmodule.try_get_primitive_method("main", main_type.mclass) if main_method != null then v.send(main_method, [glob_sys]) end @@ -306,31 +383,11 @@ abstract class AbstractCompiler v.add("\}") end - # look for a needed .h and .c file for a given .nit source-file - # FIXME: bad API, parameter should be a MModule, not its source-file - fun add_extern(file: String) - do - file = file.strip_extension(".nit") - var tryfile = file + ".nit.h" - if tryfile.file_exists then - self.header.add_decl("#include \"{"..".join_path(tryfile)}\"") - end - tryfile = file + "_nit.h" - if tryfile.file_exists then - self.header.add_decl("#include \"{"..".join_path(tryfile)}\"") - end - tryfile = file + ".nit.c" - if tryfile.file_exists then - self.extern_bodies.add(tryfile) - end - tryfile = file + "_nit.c" - if tryfile.file_exists then - self.extern_bodies.add(tryfile) - end - end - # List of additional .c files required to compile (native interface) - var extern_bodies = new ArraySet[String] + var extern_bodies = new Array[ExternCFile] + + # This is used to avoid adding an extern file more than once + private var seen_extern = new ArraySet[String] # Generate code that check if an instance is correctly initialized fun generate_check_init_instance(mtype: MClassType) is abstract @@ -428,6 +485,34 @@ abstract class AbstractCompiler end end +# A file unit (may be more than one file if +# A file unit aim to be autonomous and is made or one or more `CodeWriter`s +class CodeFile + var name: String + var writers = new Array[CodeWriter] + var required_declarations = new HashSet[String] +end + +# Where to store generated lines +class CodeWriter + var file: CodeFile + var lines: List[String] = new List[String] + var decl_lines: List[String] = new List[String] + + # Add a line in the main part of the generated C + fun add(s: String) do self.lines.add(s) + + # Add a line in the + # (used for local or global declaration) + fun add_decl(s: String) do self.decl_lines.add(s) + + init(file: CodeFile) + do + self.file = file + file.writers.add(self) + end +end + # A visitor on the AST of property definition that generate the C code. abstract class AbstractCompilerVisitor @@ -448,12 +533,12 @@ abstract class AbstractCompilerVisitor # Alias for self.compiler.mainmodule.bool_type fun bool_type: MClassType do return self.compiler.mainmodule.bool_type - var file_break: Bool = false + var writer: CodeWriter init(compiler: COMPILER) do self.compiler = compiler - compiler.visitors.add(self) + self.writer = new CodeWriter(compiler.files.last) end # Force to get the primitive class named `name' or abort @@ -462,7 +547,8 @@ abstract class AbstractCompilerVisitor # Force to get the primitive property named `name' in the instance `recv' or abort fun get_property(name: String, recv: MType): MMethod do - return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv, self.compiler.mainmodule) + assert recv isa MClassType + return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv.mclass, self.compiler.mainmodule) end fun compile_callsite(callsite: CallSite, args: Array[RuntimeVariable]): nullable RuntimeVariable @@ -740,15 +826,51 @@ abstract class AbstractCompilerVisitor # Code generation - private var lines: List[String] = new List[String] - private var decl_lines: List[String] = new List[String] - # Add a line in the main part of the generated C - fun add(s: String) do self.lines.add(s) + fun add(s: String) do self.writer.lines.add(s) # Add a line in the # (used for local or global declaration) - fun add_decl(s: String) do self.decl_lines.add(s) + fun add_decl(s: String) do self.writer.decl_lines.add(s) + + # Request the presence of a global declaration + fun require_declaration(key: String) + do + self.writer.file.required_declarations.add(key) + end + + # Add a declaration in the local-header + # The declaration is ensured to be present once + fun declare_once(s: String) + do + self.compiler.provide_declaration(s, s) + self.require_declaration(s) + end + + # look for a needed .h and .c file for a given .nit source-file + # FIXME: bad API, parameter should be a MModule, not its source-file + fun add_extern(file: String) + do + file = file.strip_extension(".nit") + var tryfile = file + ".nit.h" + if tryfile.file_exists then + self.declare_once("#include \"{"..".join_path(tryfile)}\"") + end + tryfile = file + "_nit.h" + if tryfile.file_exists then + self.declare_once("#include \"{"..".join_path(tryfile)}\"") + end + + if self.compiler.seen_extern.has(file) then return + self.compiler.seen_extern.add(file) + tryfile = file + ".nit.c" + if not tryfile.file_exists then + tryfile = file + "_nit.c" + if not tryfile.file_exists then return + end + var f = new ExternCFile(tryfile, "") + self.compiler.extern_bodies.add(f) + end # Return a new local runtime_variable initialized with the C expression `cexpr'. fun new_expr(cexpr: String, mtype: MType): RuntimeVariable @@ -934,6 +1056,14 @@ class Frame var returnlabel: nullable String writable = null end +# An extern C file to compile +class ExternCFile + # The filename of the file + var filename: String + # Additionnal specific CC compiler -c flags + var cflags: String +end + redef class String # Mangle a string to be a unique valid C identifier fun to_cmangle: String @@ -1483,7 +1613,7 @@ redef class AInternMethPropdef v.ret(v.new_expr("glob_sys", ret.as(not null))) return else if pname == "calloc_string" then - v.ret(v.new_expr("(char*)GC_MALLOC_ATOMIC({arguments[1]})", ret.as(not null))) + v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null))) return else if pname == "calloc_array" then v.calloc_array(ret.as(not null), arguments) @@ -1503,7 +1633,13 @@ redef class AInternMethPropdef v.ret(v.new_expr("(char*){nat}", ret.as(not null))) return else if pname == "force_garbage_collection" then - v.add("GC_gcollect();") + v.add("nit_gcollect();") + return + else if pname == "native_argc" then + v.ret(v.new_expr("glob_argc", ret.as(not null))) + return + else if pname == "native_argv" then + v.ret(v.new_expr("glob_argv[{arguments[1]}]", ret.as(not null))) return end v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");") @@ -1524,7 +1660,7 @@ redef class AExternMethPropdef externname = nextern.text.substring(1, nextern.text.length-2) if location.file != null then var file = location.file.filename - v.compiler.add_extern(file) + v.add_extern(file) end var res: nullable RuntimeVariable = null var ret = mpropdef.msignature.return_mtype @@ -1556,7 +1692,7 @@ redef class AExternInitPropdef externname = nextern.text.substring(1, nextern.text.length-2) if location.file != null then var file = location.file.filename - v.compiler.add_extern(file) + v.add_extern(file) end v.adapt_signature(mpropdef, arguments) var ret = arguments.first.mtype @@ -2239,19 +2375,7 @@ end # Utils -redef class HashSet[E] - init from(elements: Collection[E]) do - init - self.add_all(elements) - end -end - redef class Array[E] - init from(elements: Collection[E]) do - init - self.add_all(elements) - end - # Return a new Array with the elements only contened in 'self' and not in 'o' fun -(o: Array[E]): Array[E] do var res = new Array[E] @@ -2261,208 +2385,25 @@ redef class Array[E] end redef class MModule - - # Return a linearization of a set of mtypes - fun linearize_mtypes(mtypes: Set[MType]): Array[MType] do - var lin = new Array[MType].from(mtypes) - var sorter = new TypeSorter(self) - sorter.sort(lin) - return lin - end - - # Return a reverse linearization of a set of mtypes - fun reverse_linearize_mtypes(mtypes: Set[MType]): Array[MType] do - var lin = new Array[MType].from(mtypes) - var sorter = new ReverseTypeSorter(self) - sorter.sort(lin) - return lin - end - - # Return super types of a `mtype` in `self` - fun super_mtypes(mtype: MType, mtypes: Set[MType]): Set[MType] do - if not self.super_mtypes_cache.has_key(mtype) then - var supers = new HashSet[MType] - for otype in mtypes do - if otype == mtype then continue - if mtype.is_subtype(self, null, otype) then - supers.add(otype) - end - end - self.super_mtypes_cache[mtype] = supers - end - return self.super_mtypes_cache[mtype] - end - - private var super_mtypes_cache: Map[MType, Set[MType]] = new HashMap[MType, Set[MType]] - - # Return all sub mtypes (directs and indirects) of a `mtype` in `self` - fun sub_mtypes(mtype: MType, mtypes: Set[MType]): Set[MType] do - if not self.sub_mtypes_cache.has_key(mtype) then - var subs = new HashSet[MType] - for otype in mtypes do - if otype == mtype then continue - if otype.is_subtype(self, null, mtype) then - subs.add(otype) - end - end - self.sub_mtypes_cache[mtype] = subs - end - return self.sub_mtypes_cache[mtype] - end - - private var sub_mtypes_cache: Map[MType, Set[MType]] = new HashMap[MType, Set[MType]] - - # Return a linearization of a set of mclasses - fun linearize_mclasses_2(mclasses: Set[MClass]): Array[MClass] do - var lin = new Array[MClass].from(mclasses) - var sorter = new ClassSorter(self) - sorter.sort(lin) - return lin - end - - # Return a reverse linearization of a set of mtypes - fun reverse_linearize_mclasses(mclasses: Set[MClass]): Array[MClass] do - var lin = new Array[MClass].from(mclasses) - var sorter = new ReverseClassSorter(self) - sorter.sort(lin) - return lin - end - - # Return all super mclasses (directs and indirects) of a `mclass` in `self` - fun super_mclasses(mclass: MClass): Set[MClass] do - if not self.super_mclasses_cache.has_key(mclass) then - var supers = new HashSet[MClass] - if self.flatten_mclass_hierarchy.has(mclass) then - for sup in self.flatten_mclass_hierarchy[mclass].greaters do - if sup == mclass then continue - supers.add(sup) - end - end - self.super_mclasses_cache[mclass] = supers - end - return self.super_mclasses_cache[mclass] - end - - private var super_mclasses_cache: Map[MClass, Set[MClass]] = new HashMap[MClass, Set[MClass]] - - # Return all parents of a `mclass` in `self` - fun parent_mclasses(mclass: MClass): Set[MClass] do - if not self.parent_mclasses_cache.has_key(mclass) then - var parents = new HashSet[MClass] - if self.flatten_mclass_hierarchy.has(mclass) then - for sup in self.flatten_mclass_hierarchy[mclass].direct_greaters do - if sup == mclass then continue - parents.add(sup) - end - end - self.parent_mclasses_cache[mclass] = parents - end - return self.parent_mclasses_cache[mclass] - end - - private var parent_mclasses_cache: Map[MClass, Set[MClass]] = new HashMap[MClass, Set[MClass]] - - # Return all sub mclasses (directs and indirects) of a `mclass` in `self` - fun sub_mclasses(mclass: MClass): Set[MClass] do - if not self.sub_mclasses_cache.has_key(mclass) then - var subs = new HashSet[MClass] - if self.flatten_mclass_hierarchy.has(mclass) then - for sub in self.flatten_mclass_hierarchy[mclass].smallers do - if sub == mclass then continue - subs.add(sub) - end - end - self.sub_mclasses_cache[mclass] = subs - end - return self.sub_mclasses_cache[mclass] - end - - private var sub_mclasses_cache: Map[MClass, Set[MClass]] = new HashMap[MClass, Set[MClass]] - # All 'mproperties' associated to all 'mclassdefs' of `mclass` fun properties(mclass: MClass): Set[MProperty] do if not self.properties_cache.has_key(mclass) then var properties = new HashSet[MProperty] - var parents = self.super_mclasses(mclass) + var parents = new Array[MClass] + if self.flatten_mclass_hierarchy.has(mclass) then + parents.add_all(mclass.in_hierarchy(self).direct_greaters) + end for parent in parents do properties.add_all(self.properties(parent)) end - for mclassdef in mclass.mclassdefs do - for mpropdef in mclassdef.mpropdefs do - properties.add(mpropdef.mproperty) + for mprop in mclassdef.intro_mproperties do + properties.add(mprop) end end self.properties_cache[mclass] = properties end return properties_cache[mclass] end - private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]] end - -# A sorter for linearize list of types -private class TypeSorter - super AbstractSorter[MType] - - private var mmodule: MModule - - init(mmodule: MModule) do self.mmodule = mmodule - - redef fun compare(a, b) do - if a == b then - return 0 - else if a.is_subtype(self.mmodule, null, b) then - return -1 - end - return 1 - end -end - -# A sorter for reverse linearization -private class ReverseTypeSorter - super TypeSorter - - init(mmodule: MModule) do end - - redef fun compare(a, b) do - if a == b then - return 0 - else if a.is_subtype(self.mmodule, null, b) then - return 1 - end - return -1 - end -end - -# A sorter for linearize list of classes -private class ClassSorter - super AbstractSorter[MClass] - - var mmodule: MModule - - redef fun compare(a, b) do - if a == b then - return 0 - else if self.mmodule.flatten_mclass_hierarchy.has(a) and self.mmodule.flatten_mclass_hierarchy[a].greaters.has(b) then - return -1 - end - return 1 - end -end - -# A sorter for reverse linearization -private class ReverseClassSorter - super AbstractSorter[MClass] - - var mmodule: MModule - - redef fun compare(a, b) do - if a == b then - return 0 - else if self.mmodule.flatten_mclass_hierarchy.has(a) and self.mmodule.flatten_mclass_hierarchy[a].greaters.has(b) then - return 1 - end - return -1 - end -end