X-Git-Url: http://nitlanguage.org diff --git a/src/abstract_compiler.nit b/src/abstract_compiler.nit index 95db84d..df82067 100644 --- a/src/abstract_compiler.nit +++ b/src/abstract_compiler.nit @@ -20,6 +20,7 @@ module abstract_compiler import literal import typing import auto_super_init +import frontend # Add compiling options redef class ToolContext @@ -27,8 +28,12 @@ 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") + # --compile-dir + var opt_compile_dir: OptionString = new OptionString("Directory used to generate temporary files", "--compile-dir") # --hardening var opt_hardening: OptionBool = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening") # --no-shortcut-range @@ -49,13 +54,49 @@ redef class ToolContext redef init do super - self.option_context.add_option(self.opt_output, self.opt_no_cc, self.opt_make_flags, self.opt_hardening, self.opt_no_shortcut_range) + self.option_context.add_option(self.opt_output, self.opt_no_cc, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening, self.opt_no_shortcut_range) self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_initialization, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_other) self.option_context.add_option(self.opt_typing_test_metrics) end 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,85 +105,127 @@ 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) + + var compile_dir = toolcontext.opt_compile_dir.value + if compile_dir == null then compile_dir = ".nit_compile" - ".nit_compile".mkdir + compile_dir.mkdir + var orig_dir=".." # FIXME only works if `compile_dir` is a subdirectory of cwd var outname = self.toolcontext.opt_output.value if outname == null then - outname = "{mainmodule.name}.bin" + outname = "{mainmodule.name}" end + var outpath = orig_dir.join_path(outname).simplify_path - var hfilename = ".nit_compile/{mainmodule.name}.1.h" - var h = new OFStream.open(hfilename) + var hfilename = compiler.header.file.name + ".h" + var hfilepath = "{compile_dir}/{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 - - var i = 0 - for vis in compiler.writers 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 = "{f.name}.0.h" + var cfilepath = "{compile_dir}/{cfilename}" + hfile = new OFStream.open(cfilepath) + 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 = "{f.name}.{i}.c" + cfilepath = "{compile_dir}/{cfilename}" + self.toolcontext.info("new C source files to compile: {cfilepath}", 3) + cfiles.add(cfilename) + file = new OFStream.open(cfilepath) + 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) # Generate the Makefile - var makename = ".nit_compile/{mainmodule.name}.mk" - var makefile = new OFStream.open(makename) + var makename = "{mainmodule.name}.mk" + var makepath = "{compile_dir}/{makename}" + var makefile = new OFStream.open(makepath) - makefile.write("CC = ccache cc\nCFLAGS = -g -O2\nLDFLAGS ?= \nLDLIBS ?= -lm -lgc\n\n") - makefile.write("all: {outname}\n\n") + var cc_includes = "" + for p in cc_paths do + p = orig_dir.join_path(p).simplify_path + 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: {outpath}\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 = "{basename}.extern.o" + var ff = orig_dir.join_path(f.filename).simplify_path + makefile.write("{o}: {ff}\n\t$(CC) $(CFLAGS) -D NONITCNI {f.cflags} -c -o {o} {ff}\n\n") ofiles.add(o) end + # Link edition - makefile.write("{outname}: {ofiles.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outname} {ofiles.join(" ")} $(LDLIBS)\n\n") + makefile.write("{outpath}: {ofiles.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath} {ofiles.join(" ")} $(LDLIBS)\n\n") # Clean makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n\n") makefile.close - self.toolcontext.info("Generated makefile: {makename}", 2) + self.toolcontext.info("Generated makefile: {makepath}", 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 @@ -152,13 +235,13 @@ redef class ModelBuilder self.toolcontext.info("*** COMPILING C ***", 1) var makeflags = self.toolcontext.opt_make_flags.value if makeflags == null then makeflags = "" - self.toolcontext.info("make -B -f {makename} -j 4 {makeflags}", 2) + self.toolcontext.info("make -B -C {compile_dir} -f {makename} -j 4 {makeflags}", 2) var res if self.toolcontext.verbose_level >= 3 then - res = sys.system("make -B -f {makename} -j 4 {makeflags} 2>&1") + res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1") else - res = sys.system("make -B -f {makename} -j 4 {makeflags} 2>&1 >/dev/null") + res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1 >/dev/null") end if res != 0 then toolcontext.error(null, "make failed! Error code: {res}.") @@ -173,8 +256,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 @@ -185,32 +272,40 @@ abstract class AbstractCompiler init(mainmodule: MModule, modelbuilder: ModelBuilder) do self.mainmodule = mainmodule + self.realmainmodule = mainmodule self.modelbuilder = modelbuilder - self.header = new CodeWriter - self.writers.add(self.header) 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 = new CodeWriter - v.file_break = true - self.writers.add(v) + 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 - var writers: List[CodeWriter] = new List[CodeWriter] + 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 have a global .h since it does not help recompilations 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 fun compile_header do @@ -218,16 +313,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 @@ -264,16 +350,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 @@ -308,31 +395,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 @@ -385,8 +452,8 @@ abstract class AbstractCompiler # 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 against resolved types (`x isa Collection[Animal]`) + # * type tests against unresolved types (`x isa Collection[E]`) # * type tests skipped # * type tests total # * @@ -430,9 +497,17 @@ 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_break: Bool = false + var file: CodeFile var lines: List[String] = new List[String] var decl_lines: List[String] = new List[String] @@ -442,6 +517,12 @@ class CodeWriter # 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. @@ -455,7 +536,7 @@ abstract class AbstractCompilerVisitor # The current visited AST node var current_node: nullable ANode writable = null - # The current Frame + # The current `Frame` var frame: nullable Frame writable # Alias for self.compiler.mainmodule.object_type @@ -464,21 +545,22 @@ abstract class AbstractCompilerVisitor # Alias for self.compiler.mainmodule.bool_type fun bool_type: MClassType do return self.compiler.mainmodule.bool_type - var writer = new CodeWriter + var writer: CodeWriter init(compiler: COMPILER) do self.compiler = compiler - compiler.writers.add(self.writer) + self.writer = new CodeWriter(compiler.files.last) end - # Force to get the primitive class named `name' or abort + # Force to get the primitive class named `name` or abort fun get_class(name: String): MClass do return self.compiler.mainmodule.get_primitive_class(name) - # Force to get the primitive property named `name' in the instance `recv' or abort + # 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 @@ -490,7 +572,7 @@ abstract class AbstractCompilerVisitor fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract - # Transform varargs, in raw arguments, into a single argument of type Array + # Transform varargs, in raw arguments, into a single argument of type `Array` # Note: this method modify the given `args` # If there is no vararg, then `args` is not modified. fun varargize(mpropdef: MPropDef, msignature: MSignature, args: Array[RuntimeVariable]) @@ -542,8 +624,8 @@ abstract class AbstractCompilerVisitor # Unsafely cast a value to a new type # ie the result share the same C variable but my have a different mcasttype - # NOTE: if the adaptation is useless then `value' is returned as it. - # ENSURE: return.name == value.name + # NOTE: if the adaptation is useless then `value` is returned as it. + # ENSURE: `result.name == value.name` fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable do mtype = self.anchor(mtype) @@ -567,7 +649,7 @@ abstract class AbstractCompilerVisitor fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) is abstract # Box or unbox a value to another type iff a C type conversion is needed - # ENSURE: result.mtype.ctype == mtype.ctype + # ENSURE: `result.mtype.ctype == mtype.ctype` fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable is abstract # Generate a polymorphic subtype test @@ -584,10 +666,10 @@ abstract class AbstractCompilerVisitor # Generate a static call on a method definition fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract - # Generate a polymorphic send for the method `m' and the arguments `args' + # Generate a polymorphic send for the method `m` and the arguments `args` fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable is abstract - # Generate a monomorphic send for the method `m', the type `t' and the arguments `args' + # Generate a monomorphic send for the method `m`, the type `t` and the arguments `args` fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable do assert t isa MClassType @@ -629,7 +711,7 @@ abstract class AbstractCompilerVisitor private var names: HashSet[String] = new HashSet[String] private var last: Int = 0 - # Return a new name based on `s' and unique in the visitor + # Return a new name based on `s` and unique in the visitor fun get_name(s: String): String do if not self.names.has(s) then @@ -663,7 +745,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 @@ -736,13 +818,11 @@ abstract class AbstractCompilerVisitor self.add("if ({name}) \{") self.add("{res} = {name};") self.add("\} else \{") - var nat = self.new_var(self.get_class("NativeString").mclass_type) + var native_mtype = self.get_class("NativeString").mclass_type + var nat = self.new_var(native_mtype) self.add("{nat} = \"{string.escape_to_c}\";") - var res2 = self.init_instance(mtype) - self.add("{res} = {res2};") var length = self.int_instance(string.length) - self.send(self.get_property("with_native", mtype), [res, nat, length]) - self.check_init_instance(res, mtype) + self.add("{res} = {self.send(self.get_property("to_s_with_length", native_mtype), [nat, length]).as(not null)};") self.add("{name} = {res};") self.add("\}") return res @@ -763,7 +843,46 @@ abstract class AbstractCompilerVisitor # (used for local or global declaration) fun add_decl(s: String) do self.writer.decl_lines.add(s) - # Return a new local runtime_variable initialized with the C expression `cexpr'. + # 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 do var res = new_var(mtype) @@ -775,15 +894,32 @@ abstract class AbstractCompilerVisitor # used by aborts, asserts, casts, etc. fun add_abort(message: String) do + self.add("fprintf(stderr, \"Runtime error: %s\", \"{message.escape_to_c}\");") + add_raw_abort + end + + fun add_raw_abort + do if self.current_node != null and self.current_node.location.file != null then - self.add("fprintf(stderr, \"Runtime error: %s (%s:%d)\\n\", \"{message.escape_to_c}\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});") + self.add("fprintf(stderr, \" (%s:%d)\\n\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});") else - self.add("fprintf(stderr, \"Runtime error: %s\\n\", \"{message.escape_to_c}\");") + self.add("fprintf(stderr, \"\\n\");") end self.add("exit(1);") end - # Generate a return with the value `s' + # Add a dynamic cast + fun add_cast(value: RuntimeVariable, mtype: MType, tag: String) + do + var res = self.type_test(value, mtype, tag) + self.add("if (!{res}) \{") + var cn = self.class_name_string(value) + self.add("fprintf(stderr, \"Runtime error: Cast failed. Expected `%s`, got `%s`\", \"{mtype.to_s.escape_to_c}\", {cn});") + self.add_raw_abort + self.add("\}") + end + + # Generate a return with the value `s` fun ret(s: RuntimeVariable) do self.assign(self.frame.returnvar.as(not null), s) @@ -814,17 +950,14 @@ abstract class AbstractCompilerVisitor res = autoadapt(res, nexpr.mtype.as(not null)) var implicit_cast_to = nexpr.implicit_cast_to if implicit_cast_to != null and not self.compiler.modelbuilder.toolcontext.opt_no_check_autocast.value then - var castres = self.type_test(res, implicit_cast_to, "auto") - self.add("if (!{castres}) \{") - self.add_abort("Cast failed") - self.add("\}") + add_cast(res, implicit_cast_to, "auto") res = autoadapt(res, implicit_cast_to) end self.current_node = old return res end - # Alias for `self.expr(nexpr, self.bool_type)' + # Alias for `self.expr(nexpr, self.bool_type)` fun expr_bool(nexpr: AExpr): RuntimeVariable do return expr(nexpr, bool_type) # Safely show a debug message on the current node and repeat the message in the C code as a comment @@ -870,7 +1003,7 @@ abstract class AbstractRuntimeFunction # May inline the body or generate a C function call fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract - # Generate the code for the RuntimeFunction + # Generate the code for the `AbstractRuntimeFunction` # Warning: compile more than once compilation makes CC unhappy fun compile_to_c(compiler: COMPILER) is abstract end @@ -878,7 +1011,7 @@ end # A runtime variable hold a runtime value in C. # Runtime variables are associated to Nit local variables and intermediate results in Nit expressions. # -# The tricky point is that a single C variable can be associated to more than one RuntimeVariable because the static knowledge of the type of an expression can vary in the C code. +# The tricky point is that a single C variable can be associated to more than one `RuntimeVariable` because the static knowledge of the type of an expression can vary in the C code. class RuntimeVariable # The name of the variable in the C code var name: String @@ -922,7 +1055,7 @@ class RuntimeVariable end end -# A frame correspond to a visited property in a GlobalCompilerVisitor +# A frame correspond to a visited property in a `GlobalCompilerVisitor` class Frame type VISITOR: AbstractCompilerVisitor @@ -947,61 +1080,12 @@ class Frame var returnlabel: nullable String writable = null end -redef class String - # Mangle a string to be a unique valid C identifier - fun to_cmangle: String - do - var res = new Buffer - var underscore = false - for c in self do - if (c >= 'a' and c <= 'z') or (c >='A' and c <= 'Z') then - res.add(c) - underscore = false - continue - end - if underscore then - res.append('_'.ascii.to_s) - res.add('d') - end - if c >= '0' and c <= '9' then - res.add(c) - underscore = false - else if c == '_' then - res.add(c) - underscore = true - else - res.add('_') - res.append(c.ascii.to_s) - res.add('d') - underscore = false - end - end - return res.to_s - end - - # Escape " \ ' and non printable characters for literal C strings or characters - fun escape_to_c: String - do - var b = new Buffer - for c in self do - if c == '\n' then - b.append("\\n") - else if c == '\0' then - b.append("\\0") - else if c == '"' then - b.append("\\\"") - else if c == '\'' then - b.append("\\\'") - else if c == '\\' then - b.append("\\\\") - else if c.ascii < 32 then - b.append("\\{c.ascii.to_base(8, false)}") - else - b.add(c) - end - end - return b.to_s - 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 MType @@ -1214,10 +1298,7 @@ redef class MMethodDef # generate the cast # note that v decides if and how to implements the cast v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */") - var cond = v.type_test(arguments[i+1], mtype, "covariance") - v.add("if (!{cond}) \{") - v.add_abort("Cast failed") - v.add("\}") + v.add_cast(arguments[i+1], mtype, "covariance") end end end @@ -1347,7 +1428,7 @@ redef class AInternMethPropdef v.add("printf(\"%c\", {arguments.first});") return else if pname == "object_id" then - v.ret(arguments.first) + v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) return else if pname == "+" then v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) @@ -1392,7 +1473,7 @@ redef class AInternMethPropdef v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");") return else if pname == "object_id" then - v.ret(arguments.first) + v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) return else if pname == "==" then v.ret(v.equal_test(arguments[0], arguments[1])) @@ -1453,24 +1534,6 @@ redef class AInternMethPropdef v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) return end - else if cname == "Char" then - if pname == "output" then - v.add("printf(\"%c\", {arguments.first});") - return - else if pname == "object_id" then - v.ret(arguments.first) - return - else if pname == "==" then - v.ret(v.equal_test(arguments[0], arguments[1])) - return - else if pname == "!=" then - var res = v.equal_test(arguments[0], arguments[1]) - v.ret(v.new_expr("!{res}", ret.as(not null))) - return - else if pname == "ascii" then - v.ret(v.new_expr("{arguments[0]}", ret.as(not null))) - return - end else if cname == "NativeString" then if pname == "[]" then v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null))) @@ -1496,7 +1559,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) @@ -1516,7 +1579,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\");") @@ -1537,7 +1606,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 @@ -1569,7 +1638,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 @@ -1654,13 +1723,17 @@ redef class AClassdef end redef class ADeferredMethPropdef - redef fun compile_to_c(v, mpropdef, arguments) do v.add_abort("Deferred method called") + redef fun compile_to_c(v, mpropdef, arguments) do + var cn = v.class_name_string(arguments.first) + v.add("fprintf(stderr, \"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});") + v.add_raw_abort + end redef fun can_inline do return true end redef class AExpr # Try to compile self as an expression - # Do not call this method directly, use `v.expr' instead + # Do not call this method directly, use `v.expr` instead private fun expr(v: AbstractCompilerVisitor): nullable RuntimeVariable do v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");") @@ -1675,7 +1748,7 @@ redef class AExpr end # Try to compile self as a statement - # Do not call this method directly, use `v.stmt' instead + # Do not call this method directly, use `v.stmt` instead private fun stmt(v: AbstractCompilerVisitor) do var res = expr(v) @@ -1688,6 +1761,15 @@ redef class ABlockExpr do for e in self.n_expr do v.stmt(e) end + redef fun expr(v) + do + var last = self.n_expr.last + for e in self.n_expr do + if e == last then break + v.stmt(e) + end + return v.expr(last, null) + end end redef class AVardeclExpr @@ -1718,6 +1800,13 @@ redef class AVarAssignExpr var i = v.expr(self.n_value, variable.declared_type) v.assign(v.variable(variable), i) end + redef fun expr(v) + do + var variable = self.variable.as(not null) + var i = v.expr(self.n_value, variable.declared_type) + v.assign(v.variable(variable), i) + return i + end end redef class AVarReassignExpr @@ -1771,6 +1860,18 @@ redef class AIfExpr v.stmt(self.n_else) v.add("\}") end + + redef fun expr(v) + do + var res = v.new_var(self.mtype.as(not null)) + var cond = v.expr_bool(self.n_expr) + v.add("if ({cond})\{") + v.assign(res, v.expr(self.n_then.as(not null), null)) + v.add("\} else \{") + v.assign(res, v.expr(self.n_else.as(not null), null)) + v.add("\}") + return res + end end redef class AIfexprExpr @@ -1925,6 +2026,21 @@ redef class AOrExpr end end +redef class AImpliesExpr + redef fun expr(v) + do + var res = v.new_var(self.mtype.as(not null)) + var i1 = v.expr_bool(self.n_expr) + v.add("if (!{i1}) \{") + v.add("{res} = 1;") + v.add("\} else \{") + var i2 = v.expr_bool(self.n_expr2) + v.add("{res} = {i2};") + v.add("\}") + return res + end +end + redef class AAndExpr redef fun expr(v) do @@ -1973,15 +2089,15 @@ redef class AEeExpr end redef class AIntExpr - redef fun expr(v) do return v.new_expr("{self.n_number.text}", self.mtype.as(not null)) + redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null)) end redef class AFloatExpr - redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) + redef fun expr(v) do return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) # FIXME use value, not n_float end redef class ACharExpr - redef fun expr(v) do return v.new_expr("{self.n_char.text}", self.mtype.as(not null)) + redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null)) end redef class AArrayExpr @@ -2068,10 +2184,7 @@ redef class AAsCastExpr var i = v.expr(self.n_expr, null) if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i - var cond = v.type_test(i, self.mtype.as(not null), "as") - v.add("if (!{cond}) \{") - v.add_abort("Cast failed") - v.add("\}") + v.add_cast(i, self.mtype.as(not null), "as") return i end end @@ -2252,20 +2365,8 @@ 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' + # 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] for e in self do if not o.has(e) then res.add(e) @@ -2274,208 +2375,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` + # All `MProperty` associated to all `MClassDef` 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