import c_tools
private import annotation
import mixin
+import counter
# Add compiling options
redef class ToolContext
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
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
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)
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
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
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
# 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 FileWriter.open(makepath)
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.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>")
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
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) \{")
+ v.add_decl("static void show_backtrace(void) \{")
if ost == "nitstack" or ost == "libunwind" then
v.add_decl("char* opt = getenv(\"NIT_NO_STACK\");")
v.add_decl("unw_cursor_t cursor;")
v.add_decl("free(procname);")
v.add_decl("\}")
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
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
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.
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)
else
self.add("PRINT_ERROR(\"\\n\");")
end
- self.add("show_backtrace(1);")
+ self.add("fatal_exit(1);")
end
# Add a dynamic cast
# 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
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
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
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)
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.bool_instance(true))
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
- var a = v.array_instance(array, v.object_type)
- var res = v.send(v.get_property("to_s", a.mtype), [a])
+
+ # 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
+
+ # 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
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")