compiler: replace the option `--stacktrace` with a simpler `--no-stacktrace`
[nit.git] / src / compiler / abstract_compiler.nit
index 07a8ede..7e17187 100644 (file)
@@ -23,6 +23,7 @@ import platform
 import c_tools
 private import annotation
 import mixin
+import counter
 
 # Add compiling options
 redef class ToolContext
@@ -62,8 +63,8 @@ redef class ToolContext
        var opt_invocation_metrics = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
        # --isset-checks-metrics
        var opt_isset_checks_metrics = new OptionBool("Enable static and dynamic count of isset checks before attributes access", "--isset-checks-metrics")
-       # --stacktrace
-       var opt_stacktrace = new OptionString("Control the generation of stack traces", "--stacktrace")
+       # --no-stacktrace
+       var opt_no_stacktrace = new OptionBool("Disable the generation of stack traces", "--no-stacktrace")
        # --no-gcc-directives
        var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive")
        # --release
@@ -75,7 +76,7 @@ redef class ToolContext
                self.option_context.add_option(self.opt_output, self.opt_dir, self.opt_no_cc, self.opt_no_main, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening)
                self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_attr_isset, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_null, self.opt_no_check_all)
                self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_metrics, self.opt_isset_checks_metrics)
-               self.option_context.add_option(self.opt_stacktrace)
+               self.option_context.add_option(self.opt_no_stacktrace)
                self.option_context.add_option(self.opt_no_gcc_directive)
                self.option_context.add_option(self.opt_release)
                self.option_context.add_option(self.opt_max_c_lines, self.opt_group_c_files)
@@ -87,19 +88,8 @@ redef class ToolContext
        do
                super
 
-               var st = opt_stacktrace.value
-               if st == "none" or st == "libunwind" or st == "nitstack" then
-                       # Fine, do nothing
-               else if st == "auto" or st == null then
-                       # Default is nitstack
-                       opt_stacktrace.value = "nitstack"
-               else
-                       print "Error: unknown value `{st}` for --stacktrace. Use `none`, `libunwind`, `nitstack` or `auto`."
-                       exit(1)
-               end
-
                if opt_output.value != null and opt_dir.value != null then
-                       print "Error: cannot use both --dir and --output"
+                       print "Option Error: cannot use both --dir and --output"
                        exit(1)
                end
 
@@ -121,20 +111,30 @@ redef class ModelBuilder
        protected fun write_and_make(compiler: AbstractCompiler)
        do
                var platform = compiler.target_platform
-               var toolchain = platform.toolchain(toolcontext)
+               var toolchain = platform.toolchain(toolcontext, compiler)
                compile_dir = toolchain.compile_dir
-               toolchain.write_and_make compiler
+               toolchain.write_and_make
        end
 end
 
 redef class Platform
        # The specific tool-chain associated to the platform
-       fun toolchain(toolcontext: ToolContext): Toolchain do return new MakefileToolchain(toolcontext)
+       fun toolchain(toolcontext: ToolContext, compiler: AbstractCompiler): Toolchain
+       do
+               return new MakefileToolchain(toolcontext, compiler)
+       end
 end
 
+# Build toolchain for a specific target program, varies per `Platform`
 class Toolchain
+
+       # Toolcontext
        var toolcontext: ToolContext
 
+       # Compiler of the target program
+       var compiler: AbstractCompiler
+
+       # Directory where to generate all C files
        fun compile_dir: String
        do
                var compile_dir = toolcontext.opt_compile_dir.value
@@ -142,13 +142,15 @@ class Toolchain
                return compile_dir
        end
 
-       fun write_and_make(compiler: AbstractCompiler) is abstract
+       # Write all C files and compile them
+       fun write_and_make is abstract
 end
 
+# Default toolchain using a Makefile
 class MakefileToolchain
        super Toolchain
 
-       redef fun write_and_make(compiler)
+       redef fun write_and_make
        do
                var compile_dir = compile_dir
 
@@ -161,11 +163,11 @@ class MakefileToolchain
                compile_dir.mkdir
 
                var cfiles = new Array[String]
-               write_files(compiler, compile_dir, cfiles)
+               write_files(compile_dir, cfiles)
 
                # Generate the Makefile
 
-               write_makefile(compiler, compile_dir, cfiles)
+               write_makefile(compile_dir, cfiles)
 
                var time1 = get_time
                self.toolcontext.info("*** END WRITING C: {time1-time0} ***", 2)
@@ -177,16 +179,17 @@ class MakefileToolchain
                time0 = time1
                self.toolcontext.info("*** COMPILING C ***", 1)
 
-               compile_c_code(compiler, compile_dir)
+               compile_c_code(compile_dir)
 
                time1 = get_time
                self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2)
        end
 
-       fun write_files(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
+       # Write all source files to the `compile_dir`
+       fun write_files(compile_dir: String, cfiles: Array[String])
        do
                var platform = compiler.target_platform
-               if self.toolcontext.opt_stacktrace.value == "nitstack" and platform.supports_libunwind then compiler.build_c_to_nit_bindings
+               if platform.supports_libunwind then compiler.build_c_to_nit_bindings
                var cc_opt_with_libgc = "-DWITH_LIBGC"
                if not platform.supports_libgc then cc_opt_with_libgc = ""
 
@@ -212,7 +215,7 @@ class MakefileToolchain
 
                var hfilename = compiler.header.file.name + ".h"
                var hfilepath = "{compile_dir}/{hfilename}"
-               var h = new OFStream.open(hfilepath)
+               var h = new FileWriter.open(hfilepath)
                for l in compiler.header.decl_lines do
                        h.write l
                        h.write "\n"
@@ -227,7 +230,7 @@ class MakefileToolchain
                for f in compiler.files do
                        var i = 0
                        var count = 0
-                       var file: nullable OFStream = null
+                       var file: nullable FileWriter = null
                        for vis in f.writers do
                                if vis == compiler.header then continue
                                var total_lines = vis.lines.length + vis.decl_lines.length
@@ -240,7 +243,7 @@ class MakefileToolchain
                                        var cfilepath = "{compile_dir}/{cfilename}"
                                        self.toolcontext.info("new C source files to compile: {cfilepath}", 3)
                                        cfiles.add(cfilename)
-                                       file = new OFStream.open(cfilepath)
+                                       file = new FileWriter.open(cfilepath)
                                        file.write "#include \"{f.name}.0.h\"\n"
                                        count = total_lines
                                end
@@ -258,8 +261,8 @@ class MakefileToolchain
 
                        var cfilename = "{f.name}.0.h"
                        var cfilepath = "{compile_dir}/{cfilename}"
-                       var hfile: nullable OFStream = null
-                       hfile = new OFStream.open(cfilepath)
+                       var hfile: nullable FileWriter = null
+                       hfile = new FileWriter.open(cfilepath)
                        hfile.write "#include \"{hfilename}\"\n"
                        for key in f.required_declarations do
                                if not compiler.provided_declarations.has_key(key) then
@@ -280,17 +283,14 @@ class MakefileToolchain
                self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2)
        end
 
-       fun makefile_name(mainmodule: MModule): String do return "{mainmodule.c_name}.mk"
+       # Get the name of the Makefile to use
+       fun makefile_name: String do return "{compiler.mainmodule.c_name}.mk"
 
-       fun default_outname(mainmodule: MModule): String
+       # Get the default name of the executable to produce
+       fun default_outname: String
        do
-               # Search a non fictive module
-               var res = mainmodule.name
-               while mainmodule.is_fictive do
-                       mainmodule = mainmodule.in_importation.direct_greaters.first
-                       res = mainmodule.name
-               end
-               return res
+               var mainmodule = compiler.mainmodule.first_real_mmodule
+               return mainmodule.name
        end
 
        # Combine options and platform informations to get the final path of the outfile
@@ -298,13 +298,14 @@ class MakefileToolchain
        do
                var res = self.toolcontext.opt_output.value
                if res != null then return res
-               res = default_outname(mainmodule)
+               res = default_outname
                var dir = self.toolcontext.opt_dir.value
                if dir != null then return dir.join_path(res)
                return res
        end
 
-       fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
+       # Write the Makefile
+       fun write_makefile(compile_dir: String, cfiles: Array[String])
        do
                var mainmodule = compiler.mainmodule
                var platform = compiler.target_platform
@@ -319,9 +320,9 @@ class MakefileToolchain
                        # 2. copy the binary at the right place in the `all` goal.
                        outpath = mainmodule.c_name
                end
-               var makename = makefile_name(mainmodule)
+               var makename = makefile_name
                var makepath = "{compile_dir}/{makename}"
-               var makefile = new OFStream.open(makepath)
+               var makefile = new FileWriter.open(makepath)
 
                var linker_options = new HashSet[String]
                for m in mainmodule.in_importation.greaters do
@@ -331,25 +332,50 @@ class MakefileToolchain
 
                makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g -O2 -Wno-unused-value -Wno-switch -Wno-attributes\nCINCL =\nLDFLAGS ?= \nLDLIBS  ?= -lm {linker_options.join(" ")}\n\n")
 
-               var ost = toolcontext.opt_stacktrace.value
-               if (ost == "libunwind" or ost == "nitstack") and platform.supports_libunwind then makefile.write("NEED_LIBUNWIND := YesPlease\n")
+               makefile.write "\n# SPECIAL CONFIGURATION FLAGS\n"
+               if platform.supports_libunwind then
+                       if toolcontext.opt_no_stacktrace.value then
+                               makefile.write "NO_STACKTRACE=True"
+                       else
+                               makefile.write "NO_STACKTRACE= # Set to `True` to enable"
+                       end
+               end
 
                # Dynamic adaptations
                # While `platform` enable complex toolchains, they are statically applied
                # For a dynamic adaptsation of the compilation, the generated Makefile should check and adapt things itself
+               makefile.write "\n\n"
 
                # Check and adapt the targeted system
                makefile.write("uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')\n")
-               makefile.write("ifeq ($(uname_S),Darwin)\n")
-               # remove -lunwind since it is already included on macosx
-               makefile.write("\tNEED_LIBUNWIND :=\n")
-               makefile.write("endif\n\n")
 
                # Check and adapt for the compiler used
                # clang need an additionnal `-Qunused-arguments`
                makefile.write("clang_check := $(shell sh -c '$(CC) -v 2>&1 | grep -q clang; echo $$?')\nifeq ($(clang_check), 0)\n\tCFLAGS += -Qunused-arguments\nendif\n")
 
-               makefile.write("ifdef NEED_LIBUNWIND\n\tLDLIBS += -lunwind\nendif\n")
+               if platform.supports_libunwind then
+                       makefile.write """
+ifneq ($(NO_STACKTRACE), True)
+  # Check and include lib-unwind in a portable way
+  ifneq ($(uname_S),Darwin)
+    # already included on macosx, but need to get the correct flags in other supported platforms.
+    ifeq ($(shell pkg-config --exists 'libunwind'; echo $$?), 0)
+      LDLIBS += `pkg-config --libs libunwind`
+      CFLAGS += `pkg-config --cflags libunwind`
+    else
+      $(warning "[_] stack-traces disabled. Please install libunwind-dev.")
+      CFLAGS += -D NO_STACKTRACE
+    endif
+  endif
+else
+  # Stacktraces disabled
+  CFLAGS += -D NO_STACKTRACE
+endif
+
+"""
+               else
+                       makefile.write("CFLAGS += -D NO_STACKTRACE\n\n")
+               end
 
                makefile.write("all: {outpath}\n")
                if outpath != real_outpath then
@@ -371,7 +397,7 @@ class MakefileToolchain
                if not compiler.linker_script.is_empty then
                        var linker_script_path = "{compile_dir}/linker_script"
                        ofiles.add "linker_script"
-                       var f = new OFStream.open(linker_script_path)
+                       var f = new FileWriter.open(linker_script_path)
                        for l in compiler.linker_script do
                                f.write l
                                f.write "\n"
@@ -444,22 +470,25 @@ endif
                makepath.file_copy_to "{compile_dir}/Makefile"
        end
 
-       fun compile_c_code(compiler: AbstractCompiler, compile_dir: String)
+       # The C code is generated, compile it to an executable
+       fun compile_c_code(compile_dir: String)
        do
-               var makename = makefile_name(compiler.mainmodule)
+               var makename = makefile_name
 
                var makeflags = self.toolcontext.opt_make_flags.value
                if makeflags == null then makeflags = ""
-               self.toolcontext.info("make -B -C {compile_dir} -f {makename} -j 4 {makeflags}", 2)
+
+               var command = "make -B -C {compile_dir} -f {makename} -j 4 {makeflags}"
+               self.toolcontext.info(command, 2)
 
                var res
                if self.toolcontext.verbose_level >= 3 then
-                       res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1")
+                       res = sys.system("{command} 2>&1")
                else
-                       res = sys.system("make -B -C {compile_dir} -f {makename} -j 4 {makeflags} 2>&1 >/dev/null")
+                       res = sys.system("{command} 2>&1 >/dev/null")
                end
                if res != 0 then
-                       toolcontext.error(null, "make failed! Error code: {res}.")
+                       toolcontext.error(null, "Compilation Error: `make` failed with error code: {res}. The command was `{command}`.")
                end
        end
 end
@@ -546,7 +575,7 @@ abstract class AbstractCompiler
        do
                var compile_dir = modelbuilder.compile_dir
 
-               var stream = new OFStream.open("{compile_dir}/c_functions_hash.c")
+               var stream = new FileWriter.open("{compile_dir}/c_functions_hash.c")
                stream.write("#include <string.h>\n")
                stream.write("#include <stdlib.h>\n")
                stream.write("#include \"c_functions_hash.h\"\n")
@@ -576,7 +605,7 @@ abstract class AbstractCompiler
                stream.write("\}\n")
                stream.close
 
-               stream = new OFStream.open("{compile_dir}/c_functions_hash.h")
+               stream = new FileWriter.open("{compile_dir}/c_functions_hash.h")
                stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n")
                stream.close
 
@@ -589,6 +618,8 @@ abstract class AbstractCompiler
                self.header.add_decl("#include <stdlib.h>")
                self.header.add_decl("#include <stdio.h>")
                self.header.add_decl("#include <string.h>")
+               self.header.add_decl("#include <sys/types.h>\n")
+               self.header.add_decl("#include <unistd.h>\n")
                self.header.add_decl("#include \"gc_chooser.h\"")
                self.header.add_decl("#ifdef ANDROID")
                self.header.add_decl("  #include <android/log.h>")
@@ -603,9 +634,9 @@ abstract class AbstractCompiler
                var gccd_disable = modelbuilder.toolcontext.opt_no_gcc_directive.value
                if gccd_disable.has("noreturn") or gccd_disable.has("all") then
                        # Signal handler function prototype
-                       self.header.add_decl("void show_backtrace(int);")
+                       self.header.add_decl("void fatal_exit(int);")
                else
-                       self.header.add_decl("void show_backtrace(int) __attribute__ ((noreturn));")
+                       self.header.add_decl("void fatal_exit(int) __attribute__ ((noreturn));")
                end
 
                if gccd_disable.has("likely") or gccd_disable.has("all") then
@@ -674,7 +705,7 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
 
                if finalize_meth == null then
-                       modelbuilder.toolcontext.error(null, "The `Finalizable` class doesn't declare the `finalize` method.")
+                       modelbuilder.toolcontext.error(null, "Error: the `Finalizable` class does not declare the `finalize` method.")
                        return
                end
 
@@ -696,19 +727,16 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
        do
                var v = self.new_visitor
                v.add_decl("#include <signal.h>")
-               var ost = modelbuilder.toolcontext.opt_stacktrace.value
                var platform = target_platform
 
-               if not platform.supports_libunwind then ost = "none"
-
                var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value
 
-               if ost == "nitstack" or ost == "libunwind" then
+               if platform.supports_libunwind then
+                       v.add_decl("#ifndef NO_STACKTRACE")
                        v.add_decl("#define UNW_LOCAL_ONLY")
                        v.add_decl("#include <libunwind.h>")
-                       if ost == "nitstack" then
-                               v.add_decl("#include \"c_functions_hash.h\"")
-                       end
+                       v.add_decl("#include \"c_functions_hash.h\"")
+                       v.add_decl("#endif")
                end
                v.add_decl("int glob_argc;")
                v.add_decl("char **glob_argv;")
@@ -741,13 +769,9 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                        v.compiler.header.add_decl("extern long count_isset_checks;")
                end
 
-               v.add_decl("void sig_handler(int signo)\{")
-               v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
-               v.add_decl("show_backtrace(signo);")
-               v.add_decl("\}")
-
-               v.add_decl("void show_backtrace (int signo) \{")
-               if ost == "nitstack" or ost == "libunwind" then
+               v.add_decl("static void show_backtrace(void) \{")
+               if platform.supports_libunwind then
+                       v.add_decl("#ifndef NO_STACKTRACE")
                        v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
                        v.add_decl("unw_cursor_t cursor;")
                        v.add_decl("if(opt==NULL)\{")
@@ -761,22 +785,31 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
                        v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
                        v.add_decl("while (unw_step(&cursor) > 0) \{")
                        v.add_decl("    unw_get_proc_name(&cursor, procname, 100, &ip);")
-                       if ost == "nitstack" then
                        v.add_decl("    const char* recv = get_nit_name(procname, strlen(procname));")
                        v.add_decl("    if (recv != NULL)\{")
                        v.add_decl("            PRINT_ERROR(\"` %s\\n\", recv);")
                        v.add_decl("    \}else\{")
                        v.add_decl("            PRINT_ERROR(\"` %s\\n\", procname);")
                        v.add_decl("    \}")
-                       else
-                       v.add_decl("    PRINT_ERROR(\"` %s \\n\",procname);")
-                       end
                        v.add_decl("\}")
                        v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
                        v.add_decl("free(procname);")
                        v.add_decl("\}")
+                       v.add_decl("#endif /* NO_STACKTRACE */")
                end
-               v.add_decl("exit(signo);")
+               v.add_decl("\}")
+
+               v.add_decl("void sig_handler(int signo)\{")
+               v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
+               v.add_decl("show_backtrace();")
+               # rethrows
+               v.add_decl("signal(signo, SIG_DFL);")
+               v.add_decl("kill(getpid(), signo);")
+               v.add_decl("\}")
+
+               v.add_decl("void fatal_exit(int status) \{")
+               v.add_decl("show_backtrace();")
+               v.add_decl("exit(status);")
                v.add_decl("\}")
 
                if no_main then
@@ -856,6 +889,12 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref );
 
                v.add("return 0;")
                v.add("\}")
+
+               for m in mainmodule.in_importation.greaters do
+                       var f = "FILE_"+m.c_name
+                       v.add "const char {f}[] = \"{m.location.file.filename.escape_to_c}\";"
+                       provide_declaration(f, "extern const char {f}[];")
+               end
        end
 
        # Copile all C functions related to the [incr|decr]_ref features of the FFI
@@ -1008,14 +1047,6 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
        end
 
        fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
-
-       # Division facility
-       # Avoid division by zero by returning the string "n/a"
-       fun div(a,b:Int):String
-       do
-               if b == 0 then return "n/a"
-               return ((a*10000/b).to_f / 100.0).to_precision(2)
-       end
 end
 
 # A file unit (may be more than one file if
@@ -1072,9 +1103,6 @@ abstract class AbstractCompilerVisitor
                self.writer = new CodeWriter(compiler.files.last)
        end
 
-       # 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
        fun get_property(name: String, recv: MType): MMethod
        do
@@ -1116,43 +1144,56 @@ abstract class AbstractCompilerVisitor
 
        fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) is abstract
 
+       # Return an element of a native array.
+       # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
+       fun native_array_get(native_array: RuntimeVariable, index: Int): RuntimeVariable is abstract
+
+       # Store an element in a native array.
+       # The method is unsafe and is just a direct wrapper for the specific implementation of native arrays
+       fun native_array_set(native_array: RuntimeVariable, index: Int, value: RuntimeVariable) is abstract
+
        # Evaluate `args` as expressions in the call of `mpropdef` on `recv`.
        # This method is used to manage varargs in signatures and returns the real array
        # of runtime variables to use in the call.
-       fun varargize(mpropdef: MMethodDef, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
+       fun varargize(mpropdef: MMethodDef, map: nullable SignatureMap, recv: RuntimeVariable, args: SequenceRead[AExpr]): Array[RuntimeVariable]
        do
                var msignature = mpropdef.new_msignature or else mpropdef.msignature.as(not null)
                var res = new Array[RuntimeVariable]
                res.add(recv)
 
-               if args.is_empty then return res
+               if msignature.arity == 0 then return res
+
+               if map == null then
+                       assert args.length == msignature.arity
+                       for ne in args do
+                               res.add self.expr(ne, null)
+                       end
+                       return res
+               end
 
-               var vararg_rank = msignature.vararg_rank
-               var vararg_len = args.length - msignature.arity
-               if vararg_len < 0 then vararg_len = 0
+               # Eval in order of arguments, not parameters
+               var exprs = new Array[RuntimeVariable].with_capacity(args.length)
+               for ne in args do
+                       exprs.add self.expr(ne, null)
+               end
 
+               # Fill `res` with the result of the evaluation according to the mapping
                for i in [0..msignature.arity[ do
-                       if i == vararg_rank then
-                               var ne = args[i]
-                               if ne isa AVarargExpr then
-                                       var e = self.expr(ne.n_expr, null)
-                                       res.add(e)
-                                       continue
-                               end
-                               var vararg = new Array[RuntimeVariable]
-                               for j in [vararg_rank..vararg_rank+vararg_len] do
-                                       var e = self.expr(args[j], null)
-                                       vararg.add(e)
-                               end
-                               var elttype = msignature.mparameters[vararg_rank].mtype
+                       var param = msignature.mparameters[i]
+                       var j = map.map.get_or_null(i)
+                       if j == null then
+                               # default value
+                               res.add(null_instance)
+                               continue
+                       end
+                       if param.is_vararg and map.vararg_decl > 0 then
+                               var vararg = exprs.sub(j, map.vararg_decl)
+                               var elttype = param.mtype
                                var arg = self.vararg_instance(mpropdef, recv, vararg, elttype)
                                res.add(arg)
-                       else
-                               var j = i
-                               if i > vararg_rank then j += vararg_len
-                               var e = self.expr(args[j], null)
-                               res.add(e)
+                               continue
                        end
+                       res.add exprs[j]
                end
                return res
        end
@@ -1389,7 +1430,7 @@ abstract class AbstractCompilerVisitor
                var recv
                var ctype = mtype.ctype
                assert mtype.mclass.name != "NativeArray"
-               if ctype == "val*" then
+               if not mtype.is_c_primitive then
                        recv = init_instance(mtype)
                else if ctype == "char*" then
                        recv = new_expr("NULL/*special!*/", mtype)
@@ -1410,37 +1451,64 @@ abstract class AbstractCompilerVisitor
                end
        end
 
+       # The currently processed module
+       #
+       # alias for `compiler.mainmodule`
+       fun mmodule: MModule do return compiler.mainmodule
+
        # Generate an integer value
        fun int_instance(value: Int): RuntimeVariable
        do
-               var res = self.new_var(self.get_class("Int").mclass_type)
-               self.add("{res} = {value};")
+               var t = mmodule.int_type
+               var res = new RuntimeVariable("{value.to_s}l", t, t)
+               return res
+       end
+
+       # Generate a char value
+       fun char_instance(value: Char): RuntimeVariable
+       do
+               var t = mmodule.char_type
+               var res = new RuntimeVariable("'{value.to_s.escape_to_c}'", t, t)
+               return res
+       end
+
+       # Generate a float value
+       #
+       # FIXME pass a Float, not a string
+       fun float_instance(value: String): RuntimeVariable
+       do
+               var t = mmodule.float_type
+               var res = new RuntimeVariable("{value}", t, t)
                return res
        end
 
        # Generate an integer value
        fun bool_instance(value: Bool): RuntimeVariable
        do
-               var res = self.new_var(self.get_class("Bool").mclass_type)
-               if value then
-                       self.add("{res} = 1;")
-               else
-                       self.add("{res} = 0;")
-               end
+               var s = if value then "1" else "0"
+               var res = new RuntimeVariable(s, bool_type, bool_type)
+               return res
+       end
+
+       # Generate the `null` value
+       fun null_instance: RuntimeVariable
+       do
+               var t = compiler.mainmodule.model.null_type
+               var res = new RuntimeVariable("((val*)NULL)", t, t)
                return res
        end
 
        # Generate a string value
        fun string_instance(string: String): RuntimeVariable
        do
-               var mtype = self.get_class("String").mclass_type
+               var mtype = mmodule.string_type
                var name = self.get_name("varonce")
                self.add_decl("static {mtype.ctype} {name};")
                var res = self.new_var(mtype)
-               self.add("if ({name}) \{")
+               self.add("if (likely({name}!=NULL)) \{")
                self.add("{res} = {name};")
                self.add("\} else \{")
-               var native_mtype = self.get_class("NativeString").mclass_type
+               var native_mtype = mmodule.native_string_type
                var nat = self.new_var(native_mtype)
                self.add("{nat} = \"{string.escape_to_c}\";")
                var length = self.int_instance(string.length)
@@ -1543,12 +1611,15 @@ abstract class AbstractCompilerVisitor
 
        fun add_raw_abort
        do
-               if self.current_node != null and self.current_node.location.file != null then
-                       self.add("PRINT_ERROR(\" (%s:%d)\\n\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});")
+               if self.current_node != null and self.current_node.location.file != null and
+                               self.current_node.location.file.mmodule != null then
+                       var f = "FILE_{self.current_node.location.file.mmodule.c_name}"
+                       self.require_declaration(f)
+                       self.add("PRINT_ERROR(\" (%s:%d)\\n\", {f}, {current_node.location.line_start});")
                else
                        self.add("PRINT_ERROR(\"\\n\");")
                end
-               self.add("show_backtrace(1);")
+               self.add("fatal_exit(1);")
        end
 
        # Add a dynamic cast
@@ -1573,6 +1644,12 @@ abstract class AbstractCompilerVisitor
        fun stmt(nexpr: nullable AExpr)
        do
                if nexpr == null then return
+               if nexpr.mtype == null and not nexpr.is_typed then
+                       # Untyped expression.
+                       # Might mean dead code
+                       # So just return
+                       return
+               end
 
                var narray = nexpr.comprehension
                if narray != null then
@@ -1592,6 +1669,13 @@ abstract class AbstractCompilerVisitor
        # `mtype` is the expected return type, pass null if no specific type is expected.
        fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable
        do
+               if nexpr.mtype == null then
+                       # Untyped expression.
+                       # Might mean dead code
+                       # so return a placebo result
+                       if mtype == null then mtype = compiler.mainmodule.object_type
+                       return new_var(mtype)
+               end
                var old = self.current_node
                self.current_node = nexpr
                var res = nexpr.expr(self).as(not null)
@@ -1745,12 +1829,16 @@ redef class MType
 
        # Short name of the `ctype` to use in unions
        fun ctypename: String do return "val"
+
+       # Is the associated C type a primitive one?
+       #
+       # ENSURE `result == (ctype != "val*")`
+       fun is_c_primitive: Bool do return false
 end
 
 redef class MClassType
 
-       redef fun ctype: String
-       do
+       redef var ctype is lazy do
                if mclass.name == "Int" then
                        return "long"
                else if mclass.name == "Bool" then
@@ -1768,6 +1856,8 @@ redef class MClassType
                end
        end
 
+       redef var is_c_primitive is lazy do return ctype != "val*"
+
        redef fun ctype_extern: String
        do
                if mclass.kind == extern_kind then
@@ -1825,6 +1915,15 @@ redef class MMethodDef
                var modelbuilder = v.compiler.modelbuilder
                var val = constant_value
                var node = modelbuilder.mpropdef2node(self)
+
+               if is_abstract then
+                       var cn = v.class_name_string(arguments.first)
+                       v.current_node = node
+                       v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mproperty.name.escape_to_c}\", {cn});")
+                       v.add_raw_abort
+                       return null
+               end
+
                if node isa APropdef then
                        var oldnode = v.current_node
                        v.current_node = node
@@ -1884,13 +1983,6 @@ end
 redef class AMethPropdef
        redef fun compile_to_c(v, mpropdef, arguments)
        do
-               if mpropdef.is_abstract then
-                       var cn = v.class_name_string(arguments.first)
-                       v.add("PRINT_ERROR(\"Runtime error: Abstract method `%s` called on `%s`\", \"{mpropdef.mproperty.name.escape_to_c}\", {cn});")
-                       v.add_raw_abort
-                       return
-               end
-
                # Call the implicit super-init
                var auto_super_inits = self.auto_super_inits
                if auto_super_inits != null then
@@ -1975,6 +2067,9 @@ redef class AMethPropdef
                        else if pname == "unary -" then
                                v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
                                return true
+                       else if pname == "unary +" then
+                               v.ret(arguments[0])
+                               return true
                        else if pname == "*" then
                                v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
                                return true
@@ -2086,6 +2181,9 @@ redef class AMethPropdef
                        else if pname == "unary -" then
                                v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
                                return true
+                       else if pname == "unary +" then
+                               v.ret(arguments[0])
+                               return true
                        else if pname == "succ" then
                                v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
                                return true
@@ -2134,6 +2232,9 @@ redef class AMethPropdef
                        else if pname == "atoi" then
                                v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null)))
                                return true
+                       else if pname == "fast_cstring" then
+                               v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
+                               return true
                        else if pname == "new" then
                                v.ret(v.new_expr("(char*)nit_alloc({arguments[1]})", ret.as(not null)))
                                return true
@@ -2255,7 +2356,7 @@ redef class AAttrPropdef
                        if is_lazy then
                                var set
                                var ret = self.mpropdef.static_mtype
-                               var useiset = ret.ctype == "val*" and not ret isa MNullableType
+                               var useiset = not ret.is_c_primitive and not ret isa MNullableType
                                var guard = self.mlazypropdef.mproperty
                                if useiset then
                                        set = v.isset_attribute(self.mpropdef.mproperty, recv)
@@ -2270,7 +2371,7 @@ redef class AAttrPropdef
 
                                v.assign(res, value)
                                if not useiset then
-                                       var true_v = v.new_expr("1", v.bool_type)
+                                       var true_v = v.bool_instance(true)
                                        v.write_attribute(guard, arguments.first, true_v)
                                end
                                v.add("\}")
@@ -2283,9 +2384,9 @@ redef class AAttrPropdef
                        v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1])
                        if is_lazy then
                                var ret = self.mpropdef.static_mtype
-                               var useiset = ret.ctype == "val*" and not ret isa MNullableType
+                               var useiset = not ret.is_c_primitive and not ret isa MNullableType
                                if not useiset then
-                                       v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.new_expr("1", v.bool_type))
+                                       v.write_attribute(self.mlazypropdef.mproperty, arguments.first, v.bool_instance(true))
                                end
                        end
                else
@@ -2295,7 +2396,7 @@ redef class AAttrPropdef
 
        fun init_expr(v: AbstractCompilerVisitor, recv: RuntimeVariable)
        do
-               if has_value and not is_lazy then evaluate_expr(v, recv)
+               if has_value and not is_lazy and not n_expr isa ANullExpr then evaluate_expr(v, recv)
        end
 
        # Evaluate, store and return the default value of the attribute
@@ -2304,7 +2405,7 @@ redef class AAttrPropdef
                var oldnode = v.current_node
                v.current_node = self
                var old_frame = v.frame
-               var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mcasttype.as_notnullable.as(MClassType), [recv])
+               var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mcasttype.undecorate.as(MClassType), [recv])
                v.frame = frame
 
                var value
@@ -2455,6 +2556,13 @@ redef class ASelfExpr
        redef fun expr(v) do return v.frame.arguments.first
 end
 
+redef class AImplicitSelfExpr
+       redef fun expr(v) do
+               if not is_sys then return super
+               return v.new_expr("glob_sys", mtype.as(not null))
+       end
+end
+
 redef class AEscapeExpr
        redef fun stmt(v) do v.add("goto BREAK_{v.escapemark_name(self.escapemark)};")
 end
@@ -2683,15 +2791,15 @@ redef class AOrElseExpr
 end
 
 redef class AIntExpr
-       redef fun expr(v) do return v.new_expr("{self.value.to_s}", self.mtype.as(not null))
+       redef fun expr(v) do return v.int_instance(self.value.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)) # FIXME use value, not n_float
+       redef fun expr(v) do return v.float_instance("{self.n_float.text}") # FIXME use value, not n_float
 end
 
 redef class ACharExpr
-       redef fun expr(v) do return v.new_expr("'{self.value.to_s.escape_to_c}'", self.mtype.as(not null))
+       redef fun expr(v) do return v.char_instance(self.value.as(not null))
 end
 
 redef class AArrayExpr
@@ -2719,14 +2827,64 @@ end
 redef class ASuperstringExpr
        redef fun expr(v)
        do
-               var array = new Array[RuntimeVariable]
+               var type_string = mtype.as(not null)
+
+               # Collect elements of the superstring
+               var array = new Array[AExpr]
                for ne in self.n_exprs do
+                       # Drop literal empty string.
+                       # They appears in things like "{a}" that is ["", a, ""]
                        if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings
-                       var i = v.expr(ne, null)
-                       array.add(i)
+                       array.add(ne)
+               end
+
+               # Store the allocated native array in a static variable
+               # For reusing later
+               var varonce = v.get_name("varonce")
+               v.add("if (unlikely({varonce}==NULL)) \{")
+
+               # The native array that will contains the elements to_s-ized.
+               # For fast concatenation.
+               var a = v.native_array_instance(type_string, v.int_instance(array.length))
+
+               v.add_decl("static {a.mtype.ctype} {varonce};")
+
+               # Pre-fill the array with the literal string parts.
+               # So they do not need to be filled again when reused
+               for i in [0..array.length[ do
+                       var ne = array[i]
+                       if not ne isa AStringFormExpr then continue
+                       var e = v.expr(ne, null)
+                       v.native_array_set(a, i, e)
+               end
+
+               v.add("\} else \{")
+               # Take the native-array from the store.
+               # The point is to prevent that some recursive execution use (and corrupt) the same native array
+               # WARNING: not thread safe! (FIXME?)
+               v.add("{a} = {varonce};")
+               v.add("{varonce} = NULL;")
+               v.add("\}")
+
+               # Stringify the elements and put them in the native array
+               var to_s_method = v.get_property("to_s", v.object_type)
+               for i in [0..array.length[ do
+                       var ne = array[i]
+                       if ne isa AStringFormExpr then continue
+                       var e = v.expr(ne, null)
+                       # Skip the `to_s` if the element is already a String
+                       if not e.mcasttype.is_subtype(v.compiler.mainmodule, null, type_string) then
+                               e = v.send(to_s_method, [e]).as(not null)
+                       end
+                       v.native_array_set(a, i, e)
                end
-               var a = v.array_instance(array, v.object_type)
-               var res = v.send(v.get_property("to_s", a.mtype), [a])
+
+               # Fast join the native string to get the result
+               var res = v.send(v.get_property("native_to_s", a.mtype), [a])
+
+               # We finish to work with the native array,
+               # so store it so that it can be reused
+               v.add("{varonce} = {a};")
                return res
        end
 end
@@ -2756,15 +2914,15 @@ redef class AOrangeExpr
 end
 
 redef class ATrueExpr
-       redef fun expr(v) do return v.new_expr("1", self.mtype.as(not null))
+       redef fun expr(v) do return v.bool_instance(true)
 end
 
 redef class AFalseExpr
-       redef fun expr(v) do return v.new_expr("0", self.mtype.as(not null))
+       redef fun expr(v) do return v.bool_instance(false)
 end
 
 redef class ANullExpr
-       redef fun expr(v) do return v.new_expr("NULL", self.mtype.as(not null))
+       redef fun expr(v) do return v.null_instance
 end
 
 redef class AIsaExpr
@@ -2792,7 +2950,7 @@ redef class AAsNotnullExpr
                var i = v.expr(self.n_expr, null)
                if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
 
-               if i.mtype.ctype != "val*" then return i
+               if i.mtype.is_c_primitive then return i
 
                v.add("if (unlikely({i} == NULL)) \{")
                v.add_abort("Cast failed")
@@ -2814,7 +2972,7 @@ redef class AOnceExpr
                v.add_decl("static {mtype.ctype} {name};")
                v.add_decl("static int {guard};")
                var res = v.new_var(mtype)
-               v.add("if ({guard}) \{")
+               v.add("if (likely({guard})) \{")
                v.add("{res} = {name};")
                v.add("\} else \{")
                var i = v.expr(self.n_expr, mtype)
@@ -2831,7 +2989,7 @@ redef class ASendExpr
        do
                var recv = v.expr(self.n_expr, null)
                var callsite = self.callsite.as(not null)
-               var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+               var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
                return v.compile_callsite(callsite, args)
        end
 end
@@ -2841,7 +2999,7 @@ redef class ASendReassignFormExpr
        do
                var recv = v.expr(self.n_expr, null)
                var callsite = self.callsite.as(not null)
-               var args = v.varargize(callsite.mpropdef, recv, self.raw_arguments)
+               var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.raw_arguments)
 
                var value = v.expr(self.n_value, null)
 
@@ -2863,26 +3021,33 @@ redef class ASuperExpr
 
                var callsite = self.callsite
                if callsite != null then
-                       var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+                       var args
 
-                       # Add additional arguments for the super init call
-                       if args.length == 1 then
+                       if self.n_args.n_exprs.is_empty then
+                               # Add automatic arguments for the super init call
+                               args = [recv]
                                for i in [0..callsite.msignature.arity[ do
                                        args.add(v.frame.arguments[i+1])
                                end
+                       else
+                               args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
                        end
+
                        # Super init call
                        var res = v.compile_callsite(callsite, args)
                        return res
                end
 
                var mpropdef = self.mpropdef.as(not null)
-               var args = v.varargize(mpropdef, recv, self.n_args.n_exprs)
-               if args.length == 1 then
+
+               var args
+               if self.n_args.n_exprs.is_empty then
                        args = v.frame.arguments
+               else
+                       args = v.varargize(mpropdef, signaturemap, recv, self.n_args.n_exprs)
                end
 
-               # stantard call-next-method
+               # Standard call-next-method
                return v.supercall(mpropdef, recv.mtype.as(MClassType), args)
        end
 end
@@ -2903,8 +3068,10 @@ redef class ANewExpr
 
                var recv = v.init_instance_or_extern(mtype)
 
-               var callsite = self.callsite.as(not null)
-               var args = v.varargize(callsite.mpropdef, recv, self.n_args.n_exprs)
+               var callsite = self.callsite
+               if callsite == null then return recv
+
+               var args = v.varargize(callsite.mpropdef, callsite.signaturemap, recv, self.n_args.n_exprs)
                var res2 = v.compile_callsite(callsite, args)
                if res2 != null then
                        #self.debug("got {res2} from {mproperty}. drop {recv}")
@@ -2956,6 +3123,20 @@ redef class AIssetAttrExpr
        end
 end
 
+redef class AVarargExpr
+       redef fun expr(v)
+       do
+               return v.expr(self.n_expr, null)
+       end
+end
+
+redef class ANamedargExpr
+       redef fun expr(v)
+       do
+               return v.expr(self.n_expr, null)
+       end
+end
+
 redef class ADebugTypeExpr
        redef fun stmt(v)
        do
@@ -3021,7 +3202,7 @@ var modelbuilder = new ModelBuilder(model, toolcontext)
 
 var arguments = toolcontext.option_context.rest
 if arguments.length > 1 and toolcontext.opt_output.value != null then
-       print "Error: --output needs a single source file. Do you prefer --dir?"
+       print "Option Error: --output needs a single source file. Do you prefer --dir?"
        exit 1
 end