import literal
import typing
import auto_super_init
-import frontend
-import common_ffi
import platform
import c_tools
var opt_no_check_other: OptionBool = new OptionBool("Disable implicit tests: unset attribute, null receiver (dangerous)", "--no-check-other")
# --typing-test-metrics
var opt_typing_test_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics")
- # --no-stacktrace
- var opt_no_stacktrace: OptionBool = new OptionBool("Disables libunwind and generation of C stack traces (can be problematic when compiling to targets such as Android or NaCl)", "--no-stacktrace")
- # --stack-trace-C-to-Nit-name-binding
- var opt_stacktrace: OptionBool = new OptionBool("Enables the use of gperf to bind C to Nit function names when encountering a Stack trace at runtime", "--nit-stacktrace")
+ # --invocation-metrics
+ var opt_invocation_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all method invocations", "--invocation-metrics")
+ # --stacktrace
+ var opt_stacktrace: OptionString = new OptionString("Control the generation of stack traces", "--stacktrace")
+ # --no-gcc-directives
+ var opt_no_gcc_directive = new OptionArray("Disable a advanced gcc directives for optimization", "--no-gcc-directive")
redef init
do
super
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)
+ self.option_context.add_option(self.opt_typing_test_metrics, self.opt_invocation_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)
end
-end
-redef class ModelBuilder
- redef init(model, toolcontext)
+ redef fun process_options(args)
do
- if toolcontext.opt_no_stacktrace.value and toolcontext.opt_stacktrace.value then
- print "Cannot use --nit-stacktrace when --no-stacktrace is activated"
+ super
+
+ var st = opt_stacktrace.value
+ if st == null or st == "none" or st == "libunwind" or st == "nitstack" then
+ # Fine, do nothing
+ else if st == "auto" then
+ # Default just unset
+ opt_stacktrace.value = null
+ else
+ print "Error: unknown value `{st}` for --stacktrace. Use `none`, `libunwind`, `nitstack` or `auto`."
exit(1)
end
-
- super
end
+end
+redef class ModelBuilder
# The compilation directory
var compile_dir: String
# 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
+ # * `toolcontext.nit_dir`
# Path can be added (or removed) by the client
var cc_paths = new Array[String]
protected fun gather_cc_paths
do
# Look for the the Nit clib path
- var path_env = "NIT_DIR".environ
- if not path_env.is_empty then
+ var path_env = toolcontext.nit_dir
+ if path_env != null 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
fun write_files(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String])
do
- if self.toolcontext.opt_stacktrace.value then compiler.build_c_to_nit_bindings
+ if self.toolcontext.opt_stacktrace.value == "nitstack" then compiler.build_c_to_nit_bindings
# Add gc_choser.h to aditionnal bodies
var gc_chooser = new ExternCFile("gc_chooser.c", "-DWITH_LIBGC")
# FFI
var m2m = toolcontext.modelbuilder.mmodule2nmodule
- for m in compiler.mainmodule.in_importation.greaters do if m2m.keys.has(m) then
- var amodule = m2m[m]
- if m.uses_ffi or amodule.uses_legacy_ni then
- compiler.finalize_ffi_for_module(amodule)
- end
+ for m in compiler.mainmodule.in_importation.greaters do
+ compiler.finalize_ffi_for_module(m)
end
# Copy original .[ch] files to compile_dir
var linker_options = new HashSet[String]
var m2m = toolcontext.modelbuilder.mmodule2nmodule
- for m in mainmodule.in_importation.greaters do if m2m.keys.has(m) then
- var amod = m2m[m]
- linker_options.add(amod.c_linker_options)
+ for m in mainmodule.in_importation.greaters do
+ var libs = m.collect_linker_libs
+ if libs != null then linker_options.add_all(libs)
end
- if not toolcontext.opt_no_stacktrace.value then linker_options.add("-lunwind")
+ var ost = toolcontext.opt_stacktrace.value
+ if ost == "libunwind" or ost == "nitstack" then linker_options.add("-lunwind")
makefile.write("CC = ccache cc\nCFLAGS = -g -O2\nCINCL = {cc_includes}\nLDFLAGS ?= \nLDLIBS ?= -lm -lgc {linker_options.join(" ")}\n\n")
makefile.write("all: {outpath}\n\n")
# Compile each generated file
for f in cfiles do
var o = f.strip_extension(".c") + ".o"
- makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) $(CINCL) -D NONITCNI -c -o {o} {f}\n\n")
+ makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) $(CINCL) -c -o {o} {f}\n\n")
ofiles.add(o)
dep_rules.add(o)
end
+ var java_files = new Array[ExternFile]
+
# Compile each required extern body into a specific .o
for f in compiler.extern_bodies do
- if f isa ExternCFile then
- var basename = f.filename.basename(".c")
- var o = "{basename}.extern.o"
- var ff = f.filename.basename("")
- makefile.write("{o}: {ff}\n\t$(CC) $(CFLAGS) -D NONITCNI {f.cflags} -c -o {o} {ff}\n\n")
- ofiles.add(o)
- dep_rules.add(o)
- else
- var o = f.makefile_rule_name
- var ff = f.filename.basename("")
- makefile.write("{o}: {ff}\n")
- makefile.write("\t{f.makefile_rule_content}\n\n")
- dep_rules.add(f.makefile_rule_name)
+ var o = f.makefile_rule_name
+ var ff = f.filename.basename("")
+ makefile.write("{o}: {ff}\n")
+ makefile.write("\t{f.makefile_rule_content}\n\n")
+ dep_rules.add(f.makefile_rule_name)
- if f isa ExternCppFile then ofiles.add(o)
- end
+ if f.compiles_to_o_file then ofiles.add(o)
+ if f.add_to_jar then java_files.add(f)
+ end
+
+ if not java_files.is_empty then
+ var jar_file = "{outpath}.jar"
+
+ var class_files_array = new Array[String]
+ for f in java_files do class_files_array.add(f.makefile_rule_name)
+ var class_files = class_files_array.join(" ")
+
+ makefile.write("{jar_file}: {class_files}\n")
+ makefile.write("\tjar cf {jar_file} {class_files}\n\n")
+ dep_rules.add jar_file
end
# Link edition
do
var compile_dir = modelbuilder.compile_dir
- var stream = new OFStream.open("{compile_dir}/C_fun_names")
- stream.write("%\{\n#include \"c_functions_hash.h\"\n%\}\n")
- stream.write("%define lookup-function-name get_nit_name\n")
- stream.write("struct C_Nit_Names;\n")
- stream.write("%%\n")
- stream.write("####\n")
+ var stream = new OFStream.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")
+ stream.write("typedef struct C_Nit_Names\{char* name; char* nit_name;\}C_Nit_Names;\n")
+ stream.write("const char* get_nit_name(register const char* procproc, register unsigned int len)\{\n")
+ stream.write("char* procname = malloc(len+1);")
+ stream.write("memcpy(procname, procproc, len);")
+ stream.write("procname[len] = '\\0';")
+ stream.write("static const C_Nit_Names map[{names.length}] = \{\n")
for i in names.keys do
+ stream.write("\{\"")
stream.write(i)
- stream.write(",\t\"")
+ stream.write("\",\"")
stream.write(names[i])
- stream.write("\"\n")
- end
- stream.write("####\n")
- stream.write("%%\n")
+ stream.write("\"\},\n")
+ end
+ stream.write("\};\n")
+ stream.write("int i;")
+ stream.write("for(i = 0; i < {names.length}; i++)\{")
+ stream.write("if(strcmp(procname,map[i].name) == 0)\{")
+ stream.write("free(procname);")
+ stream.write("return map[i].nit_name;")
+ stream.write("\}")
+ stream.write("\}")
+ stream.write("free(procname);")
+ stream.write("return NULL;")
+ stream.write("\}\n")
stream.close
stream = new OFStream.open("{compile_dir}/c_functions_hash.h")
- stream.write("typedef struct C_Nit_Names\{char* name; char* nit_name;\}C_Nit_Names;\n")
- stream.write("const struct C_Nit_Names* get_nit_name(register const char *str, register unsigned int len);\n")
+ stream.write("const char* get_nit_name(register const char* procname, register unsigned int len);\n")
stream.close
- var x = new Process("gperf","{compile_dir}/C_fun_names","-t","-7","--output-file={compile_dir}/c_functions_hash.c","-C")
- x.wait
-
extern_bodies.add(new ExternCFile("{compile_dir}/c_functions_hash.c", ""))
end
compile_header_structs
compile_nitni_structs
- # Signal handler function prototype
- self.header.add_decl("void show_backtrace(int);")
+ 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);")
+ else
+ self.header.add_decl("void show_backtrace(int) __attribute__ ((noreturn));")
+ end
+
+ if gccd_disable.has("likely") or gccd_disable.has("all") then
+ self.header.add_decl("#define likely(x) (x)")
+ self.header.add_decl("#define unlikely(x) (x)")
+ else if gccd_disable.has("correct-likely") then
+ # invert the `likely` definition
+ # Used by masochists to bench the worst case
+ self.header.add_decl("#define likely(x) __builtin_expect((x),0)")
+ self.header.add_decl("#define unlikely(x) __builtin_expect((x),1)")
+ else
+ self.header.add_decl("#define likely(x) __builtin_expect((x),1)")
+ self.header.add_decl("#define unlikely(x) __builtin_expect((x),0)")
+ end
# Global variable used by intern methods
self.header.add_decl("extern int glob_argc;")
fun compile_main_function
do
var v = self.new_visitor
- if modelbuilder.toolcontext.opt_stacktrace.value then
- v.add_decl("#include \"c_functions_hash.h\"")
- end
v.add_decl("#include <signal.h>")
- if not modelbuilder.toolcontext.opt_no_stacktrace.value then
+ var ost = modelbuilder.toolcontext.opt_stacktrace.value
+
+ if ost == null then
+ ost = "nitstack"
+ modelbuilder.toolcontext.opt_stacktrace.value = ost
+ end
+
+ if ost == "nitstack" or ost == "libunwind" then
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
end
v.add_decl("int glob_argc;")
v.add_decl("char **glob_argv;")
end
end
+ if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
+ v.add_decl("long count_invoke_by_tables;")
+ v.add_decl("long count_invoke_by_direct;")
+ v.add_decl("long count_invoke_by_inline;")
+ v.compiler.header.add_decl("extern long count_invoke_by_tables;")
+ v.compiler.header.add_decl("extern long count_invoke_by_direct;")
+ v.compiler.header.add_decl("extern long count_invoke_by_inline;")
+ end
+
v.add_decl("void sig_handler(int signo)\{")
v.add_decl("printf(\"Caught signal : %s\\n\", strsignal(signo));")
v.add_decl("show_backtrace(signo);")
v.add_decl("\}")
v.add_decl("void show_backtrace (int signo) \{")
- if not modelbuilder.toolcontext.opt_no_stacktrace.value then
+ 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("if(opt==NULL)\{")
v.add_decl("printf(\"-------------------------------------------------\\n\");")
v.add_decl("while (unw_step(&cursor) > 0) \{")
v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);")
- if modelbuilder.toolcontext.opt_stacktrace.value then
- v.add_decl(" const C_Nit_Names* recv = get_nit_name(procname, strlen(procname));")
- v.add_decl(" if (recv != 0)\{")
- v.add_decl(" printf(\"` %s\\n\", recv->nit_name);")
+ 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(" printf(\"` %s\\n\", recv);")
v.add_decl(" \}else\{")
v.add_decl(" printf(\"` %s\\n\", procname);")
v.add_decl(" \}")
end
end
+ if self.modelbuilder.toolcontext.opt_invocation_metrics.value then
+ v.add_decl("long count_invoke_total;")
+ v.add("count_invoke_total = count_invoke_by_tables + count_invoke_by_direct + count_invoke_by_inline;")
+ v.add("printf(\"# dynamic count_invocation: total %ld\\n\", count_invoke_total);")
+ v.add("printf(\"by table: %ld (%.2f%%)\\n\", count_invoke_by_tables, 100.0*count_invoke_by_tables/count_invoke_total);")
+ v.add("printf(\"direct: %ld (%.2f%%)\\n\", count_invoke_by_direct, 100.0*count_invoke_by_direct/count_invoke_total);")
+ v.add("printf(\"inlined: %ld (%.2f%%)\\n\", count_invoke_by_inline, 100.0*count_invoke_by_inline/count_invoke_total);")
+ end
v.add("return 0;")
v.add("\}")
end
end
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
if b == 0 then return "n/a"
return ((a*10000/b).to_f / 100.0).to_precision(2)
end
-
- fun finalize_ffi_for_module(nmodule: AModule)
- do
- var visitor = new_visitor
- nmodule.finalize_ffi(visitor, modelbuilder)
- nmodule.finalize_nitni(visitor)
- end
end
# A file unit (may be more than one file if
var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
if maybenull then
- self.add("if ({recv} == NULL) \{")
+ self.add("if (unlikely({recv} == NULL)) \{")
self.add_abort("Receiver is null")
self.add("\}")
end
fun add_cast(value: RuntimeVariable, mtype: MType, tag: String)
do
var res = self.type_test(value, mtype, tag)
- self.add("if (!{res}) \{")
+ self.add("if (unlikely(!{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
else if pname == "unary -" then
v.ret(v.new_expr("-{arguments[0]}", ret.as(not null)))
return
- else if pname == "succ" then
- v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
- return
- else if pname == "prec" then
- v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
- return
else if pname == "*" then
v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null)))
return
else if pname == "object_id" then
v.ret(v.new_expr("(long){arguments.first}", ret.as(not null)))
return
- else if pname == "+" then
+ else if pname == "successor" then
v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null)))
return
- else if pname == "-" then
+ else if pname == "predecessor" then
v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null)))
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 == "succ" then
- v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null)))
- return
- else if pname == "prec" then
- v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null)))
- return
else if pname == "<" then
v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null)))
return
if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return
var cond = v.expr_bool(self.n_expr)
- v.add("if (!{cond}) \{")
+ v.add("if (unlikely(!{cond})) \{")
v.stmt(self.n_else)
var nid = self.n_id
if nid != null then
var i = v.expr(self.n_expr, null)
if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
- v.add("if ({i} == NULL) \{")
+ v.add("if (unlikely({i} == NULL)) \{")
v.add_abort("Cast failed")
v.add("\}")
return i
return properties_cache[mclass]
end
private var properties_cache: Map[MClass, Set[MProperty]] = new HashMap[MClass, Set[MProperty]]
-end
-
-redef class AModule
- # Does this module use the legacy native interface?
- fun uses_legacy_ni: Bool is abstract
- # Write FFI results to file
- fun finalize_ffi(v: AbstractCompilerVisitor, modelbuilder: ModelBuilder) is abstract
+ # Write FFI and nitni results to file
+ fun finalize_ffi(c: AbstractCompiler) do end
- # Write nitni results to file
- fun finalize_nitni(v: AbstractCompilerVisitor) is abstract
+ # Give requided addinional system libraries (as given to LD_LIBS)
+ # Note: can return null instead of an empty set
+ fun collect_linker_libs: nullable Set[String] do return null
end