nitc :: AbstractCompiler :: defaultinit
# Singleton that store the knowledge about the compilation process
abstract class AbstractCompiler
type VISITOR: AbstractCompilerVisitor
# Table corresponding c_names to nit names (methods)
var names = new HashMap[String, String]
# The main module of the program currently compiled
# Is assigned during the separate compilation
var mainmodule: MModule is writable
# The real main module of the program
var realmainmodule: MModule is noinit
# The modelbuilder used to know the model and the AST
var modelbuilder: ModelBuilder is protected writable
# The associated toolchain
#
# Set by `modelbuilder.write_and_make` and permit sub-routines to access the current toolchain if required.
var toolchain: Toolchain is noinit
# Is hardening asked? (see --hardening)
fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value
# The targeted specific platform
var target_platform: Platform is noinit
# All methods who already has a callref_thunk generated for
var compiled_callref_thunk = new HashSet[MMethodDef]
var all_routine_types_name: Set[String] do
var res = new HashSet[String]
for name in ["Fun", "Proc", "FunRef", "ProcRef"] do
# Currently there's 20 arity per func type
for i in [0..20[ do
res.add("{name}{i}")
end
end
return res
end
init
do
self.realmainmodule = mainmodule
target_platform = mainmodule.target_platform or else new Platform
end
# Do the full code generation of the program `mainmodule`
# It is the main method usually called after the instantiation
fun do_compilation is abstract
# Force the creation of a new file
# The point is to avoid contamination between must-be-compiled-separately files
fun new_file(name: String): CodeFile
do
if modelbuilder.toolcontext.opt_group_c_files.value then
if self.files.is_empty then
var f = new CodeFile(mainmodule.c_name)
self.files.add(f)
end
return self.files.first
end
var f = new CodeFile(name)
self.files.add(f)
return f
end
# The list of all associated files
# Used to generate .c files
var files = new Array[CodeFile]
# Initialize a visitor specific for a compiler engine
fun new_visitor: VISITOR is abstract
# Where global declaration are stored (the main .h)
var header: CodeWriter is writable, noinit
# Additionnal linker script for `ld`.
# Mainly used to do specific link-time symbol resolution
var linker_script = new Array[String]
# 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]
private var requirers_of_declarations = new HashMap[String, ANode]
# Builds the .c and .h files to be used when generating a Stack Trace
# Binds the generated C function names to Nit function names
fun build_c_to_nit_bindings
do
var compile_dir = toolchain.compile_dir
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")
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.escape_to_c)
stream.write("\",\"")
stream.write(names[i].escape_to_c)
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 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
extern_bodies.add(new ExternCFile("c_functions_hash.c", ""))
end
# Compile C headers
# This method call compile_header_strucs method that has to be refined
fun compile_header do
self.header.add_decl("#include <stdlib.h>")
self.header.add_decl("#include <stdio.h>")
self.header.add_decl("#include <string.h>")
# longjmp !
self.header.add_decl("#include <setjmp.h>\n")
self.header.add_decl("#include <sys/types.h>\n")
self.header.add_decl("#include <unistd.h>\n")
self.header.add_decl("#include <stdint.h>\n")
self.header.add_decl("#ifdef __linux__")
self.header.add_decl(" #include <endian.h>")
self.header.add_decl("#endif")
self.header.add_decl("#include <inttypes.h>\n")
self.header.add_decl("#include \"gc_chooser.h\"")
if modelbuilder.toolcontext.opt_trace.value then self.header.add_decl("#include \"traces.h\"")
self.header.add_decl("#ifdef __APPLE__")
self.header.add_decl(" #include <TargetConditionals.h>")
self.header.add_decl(" #include <syslog.h>")
self.header.add_decl(" #include <libkern/OSByteOrder.h>")
self.header.add_decl(" #define be32toh(x) OSSwapBigToHostInt32(x)")
self.header.add_decl("#endif")
self.header.add_decl("#ifdef _WIN32")
self.header.add_decl(" #define be32toh(val) _byteswap_ulong(val)")
self.header.add_decl("#endif")
self.header.add_decl("#ifdef ANDROID")
self.header.add_decl(" #ifndef be32toh")
self.header.add_decl(" #define be32toh(val) betoh32(val)")
self.header.add_decl(" #endif")
self.header.add_decl(" #include <android/log.h>")
self.header.add_decl(" #define PRINT_ERROR(...) (void)__android_log_print(ANDROID_LOG_WARN, \"Nit\", __VA_ARGS__)")
self.header.add_decl("#elif TARGET_OS_IPHONE")
self.header.add_decl(" #define PRINT_ERROR(...) syslog(LOG_ERR, __VA_ARGS__)")
self.header.add_decl("#else")
self.header.add_decl(" #define PRINT_ERROR(...) fprintf(stderr, __VA_ARGS__)")
self.header.add_decl("#endif")
compile_header_structs
compile_nitni_structs
compile_catch_stack
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 fatal_exit(int);")
else
self.header.add_decl("void fatal_exit(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;")
self.header.add_decl("extern char **glob_argv;")
self.header.add_decl("extern val *glob_sys;")
end
# Stack stocking environment for longjumps
protected fun compile_catch_stack do
self.header.add_decl """
struct catch_stack_t {
int cursor;
int currentSize;
jmp_buf *envs;
};
extern struct catch_stack_t *getCatchStack();
"""
end
# Declaration of structures for live Nit types
protected fun compile_header_structs is abstract
# Declaration of structures for nitni undelying the FFI
protected fun compile_nitni_structs
do
self.header.add_decl """
/* Native reference to Nit objects */
/* This structure is used to represent every Nit type in extern methods and custom C code. */
struct nitni_ref {
struct nitni_ref *next,
*prev; /* adjacent global references in global list */
int count; /* number of time this global reference has been marked */
};
/* List of global references from C code to Nit objects */
/* Instanciated empty at init of Nit system and filled explicitly by user in C code */
struct nitni_global_ref_list_t {
struct nitni_ref *head, *tail;
};
extern struct nitni_global_ref_list_t *nitni_global_ref_list;
/* Initializer of global reference list */
extern void nitni_global_ref_list_init();
/* Intern function to add a global reference to the list */
extern void nitni_global_ref_add( struct nitni_ref *ref );
/* Intern function to remove a global reference from the list */
extern void nitni_global_ref_remove( struct nitni_ref *ref );
/* Increase count on an existing global reference */
extern void nitni_global_ref_incr( struct nitni_ref *ref );
/* Decrease count on an existing global reference */
extern void nitni_global_ref_decr( struct nitni_ref *ref );
"""
end
fun compile_finalizer_function
do
var finalizable_type = mainmodule.finalizable_type
if finalizable_type == null then return
var finalize_meth = mainmodule.try_get_primitive_method("finalize", finalizable_type.mclass)
if finalize_meth == null then
modelbuilder.toolcontext.error(null, "Error: the `Finalizable` class does not declare the `finalize` method.")
return
end
var v = self.new_visitor
v.add_decl "void gc_finalize (void *obj, void *client_data) \{"
var recv = v.new_expr("obj", finalizable_type)
v.send(finalize_meth, [recv])
v.add "\}"
end
# Hook to add specif piece of code before the the main C function.
#
# Is called by `compile_main_function`
fun compile_before_main(v: VISITOR)
do
end
# Hook to add specif piece of code at the begin on the main C function.
#
# Is called by `compile_main_function`
fun compile_begin_main(v: VISITOR)
do
end
# Generate the main C function.
#
# This function:
#
# * allocate the Sys object if it exists
# * call init if is exists
# * call main if it exists
fun compile_main_function
do
var v = self.new_visitor
v.add_decl("#include <signal.h>")
var platform = target_platform
var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value
if platform.supports_libunwind then
v.add_decl("#ifndef NO_STACKTRACE")
v.add_decl("#define UNW_LOCAL_ONLY")
v.add_decl("#include <libunwind.h>")
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;")
v.add_decl("val *glob_sys;")
# Store catch stack in thread local storage
v.add_decl """
#if defined(TARGET_OS_IPHONE)
// Use pthread_key_create and others for iOS
#include <pthread.h>
static pthread_key_t catch_stack_key;
static pthread_once_t catch_stack_key_created = PTHREAD_ONCE_INIT;
static void create_catch_stack()
{
pthread_key_create(&catch_stack_key, NULL);
}
struct catch_stack_t *getCatchStack()
{
pthread_once(&catch_stack_key_created, &create_catch_stack);
struct catch_stack_t *data = pthread_getspecific(catch_stack_key);
if (data == NULL) {
data = malloc(sizeof(struct catch_stack_t));
data->cursor = -1;
data->currentSize = 0;
data->envs = NULL;
pthread_setspecific(catch_stack_key, data);
}
return data;
}
#else
// Use __thread when available
__thread struct catch_stack_t catchStack = {-1, 0, NULL};
struct catch_stack_t *getCatchStack()
{
return &catchStack;
}
#endif
"""
if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
for tag in count_type_test_tags do
v.add_decl("long count_type_test_resolved_{tag};")
v.add_decl("long count_type_test_unresolved_{tag};")
v.add_decl("long count_type_test_skipped_{tag};")
v.compiler.header.add_decl("extern long count_type_test_resolved_{tag};")
v.compiler.header.add_decl("extern long count_type_test_unresolved_{tag};")
v.compiler.header.add_decl("extern long count_type_test_skipped_{tag};")
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
if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
v.add_decl("long count_attr_reads = 0;")
v.add_decl("long count_isset_checks = 0;")
v.compiler.header.add_decl("extern long count_attr_reads;")
v.compiler.header.add_decl("extern long count_isset_checks;")
end
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)\{")
v.add_decl("unw_context_t uc;")
v.add_decl("unw_word_t ip;")
v.add_decl("char* procname = malloc(sizeof(char) * 100);")
v.add_decl("unw_getcontext(&uc);")
v.add_decl("unw_init_local(&cursor, &uc);")
v.add_decl("PRINT_ERROR(\"-------------------------------------------------\\n\");")
v.add_decl("PRINT_ERROR(\"-- Stack Trace ------------------------------\\n\");")
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);")
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(" \}")
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("\}")
v.add_decl("void sig_handler(int signo)\{")
v.add_decl "#ifdef _WIN32"
v.add_decl "PRINT_ERROR(\"Caught signal : %s\\n\", signo);"
v.add_decl "#else"
v.add_decl("PRINT_ERROR(\"Caught signal : %s\\n\", strsignal(signo));")
v.add_decl "#endif"
v.add_decl("show_backtrace();")
# rethrows
v.add_decl("signal(signo, SIG_DFL);")
v.add_decl "#ifndef _WIN32"
v.add_decl("kill(getpid(), signo);")
v.add_decl "#endif"
v.add_decl("\}")
v.add_decl("void fatal_exit(int status) \{")
v.add_decl("show_backtrace();")
v.add_decl("exit(status);")
v.add_decl("\}")
compile_before_main(v)
if no_main then
v.add_decl("int nit_main(int argc, char** argv) \{")
else
v.add_decl("int main(int argc, char** argv) \{")
end
compile_begin_main(v)
v.add "#if !defined(__ANDROID__) && !defined(TARGET_OS_IPHONE)"
v.add("signal(SIGABRT, sig_handler);")
v.add("signal(SIGFPE, sig_handler);")
v.add("signal(SIGILL, sig_handler);")
v.add("signal(SIGINT, sig_handler);")
v.add("signal(SIGTERM, sig_handler);")
v.add("signal(SIGSEGV, sig_handler);")
v.add "#endif"
v.add "#ifndef _WIN32"
v.add("signal(SIGPIPE, SIG_IGN);")
v.add "#endif"
v.add("glob_argc = argc; glob_argv = argv;")
v.add("initialize_gc_option();")
v.add "initialize_nitni_global_refs();"
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.mclass)
if main_init != null then
v.send(main_init, [glob_sys])
end
var main_method = mainmodule.try_get_primitive_method("run", main_type.mclass) or else
mainmodule.try_get_primitive_method("main", main_type.mclass)
if main_method != null then
v.send(main_method, [glob_sys])
end
end
if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
v.add_decl("long count_type_test_resolved_total = 0;")
v.add_decl("long count_type_test_unresolved_total = 0;")
v.add_decl("long count_type_test_skipped_total = 0;")
v.add_decl("long count_type_test_total_total = 0;")
for tag in count_type_test_tags do
v.add_decl("long count_type_test_total_{tag};")
v.add("count_type_test_total_{tag} = count_type_test_resolved_{tag} + count_type_test_unresolved_{tag} + count_type_test_skipped_{tag};")
v.add("count_type_test_resolved_total += count_type_test_resolved_{tag};")
v.add("count_type_test_unresolved_total += count_type_test_unresolved_{tag};")
v.add("count_type_test_skipped_total += count_type_test_skipped_{tag};")
v.add("count_type_test_total_total += count_type_test_total_{tag};")
end
v.add("printf(\"# dynamic count_type_test: total %l\\n\");")
v.add("printf(\"\\tresolved\\tunresolved\\tskipped\\ttotal\\n\");")
var tags = count_type_test_tags.to_a
tags.add("total")
for tag in tags do
v.add("printf(\"{tag}\");")
v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_resolved_{tag}, 100.0*count_type_test_resolved_{tag}/count_type_test_total_total);")
v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_unresolved_{tag}, 100.0*count_type_test_unresolved_{tag}/count_type_test_total_total);")
v.add("printf(\"\\t%ld (%.2f%%)\", count_type_test_skipped_{tag}, 100.0*count_type_test_skipped_{tag}/count_type_test_total_total);")
v.add("printf(\"\\t%ld (%.2f%%)\\n\", count_type_test_total_{tag}, 100.0*count_type_test_total_{tag}/count_type_test_total_total);")
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
if self.modelbuilder.toolcontext.opt_isset_checks_metrics.value then
v.add("printf(\"# dynamic attribute reads: %ld\\n\", count_attr_reads);")
v.add("printf(\"# dynamic isset checks: %ld\\n\", count_isset_checks);")
end
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
fun compile_nitni_global_ref_functions
do
var v = self.new_visitor
v.add """
struct nitni_global_ref_list_t *nitni_global_ref_list;
void initialize_nitni_global_refs() {
nitni_global_ref_list = (struct nitni_global_ref_list_t*)nit_alloc(sizeof(struct nitni_global_ref_list_t));
nitni_global_ref_list->head = NULL;
nitni_global_ref_list->tail = NULL;
}
void nitni_global_ref_add( struct nitni_ref *ref ) {
if ( nitni_global_ref_list->head == NULL ) {
nitni_global_ref_list->head = ref;
ref->prev = NULL;
} else {
nitni_global_ref_list->tail->next = ref;
ref->prev = nitni_global_ref_list->tail;
}
nitni_global_ref_list->tail = ref;
ref->next = NULL;
}
void nitni_global_ref_remove( struct nitni_ref *ref ) {
if ( ref->prev == NULL ) {
nitni_global_ref_list->head = ref->next;
} else {
ref->prev->next = ref->next;
}
if ( ref->next == NULL ) {
nitni_global_ref_list->tail = ref->prev;
} else {
ref->next->prev = ref->prev;
}
}
extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
if ( ref->count == 0 ) /* not registered */
{
/* add to list */
nitni_global_ref_add( ref );
}
ref->count ++;
}
extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
if ( ref->count == 1 ) /* was last reference */
{
/* remove from list */
nitni_global_ref_remove( ref );
}
ref->count --;
}
"""
end
# List of additional files required to compile (FFI)
var extern_bodies = new Array[ExternFile]
# List of source files to copy over to the compile dir
var files_to_copy = new Array[String]
# This is used to avoid adding an extern file more than once
private var seen_extern = new ArraySet[String]
# Generate code that initialize the attributes on a new instance
fun generate_init_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
do
var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
self.mainmodule.linearize_mclassdefs(cds)
for cd in cds do
for npropdef in modelbuilder.collect_attr_propdef(cd) do
npropdef.init_expr(v, recv)
end
end
end
# Generate code that check if an attribute is correctly initialized
fun generate_check_attr(v: VISITOR, recv: RuntimeVariable, mtype: MClassType)
do
var cds = mtype.collect_mclassdefs(self.mainmodule).to_a
self.mainmodule.linearize_mclassdefs(cds)
for cd in cds do
for npropdef in modelbuilder.collect_attr_propdef(cd) do
npropdef.check_expr(v, recv)
end
end
end
# stats
var count_type_test_tags: Array[String] = ["isa", "as", "auto", "covariance", "erasure"]
var count_type_test_resolved: HashMap[String, Int] = init_count_type_test_tags
var count_type_test_unresolved: HashMap[String, Int] = init_count_type_test_tags
var count_type_test_skipped: HashMap[String, Int] = init_count_type_test_tags
protected fun init_count_type_test_tags: HashMap[String, Int]
do
var res = new HashMap[String, Int]
for tag in count_type_test_tags do
res[tag] = 0
end
return res
end
# 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 skipped
# * type tests total
fun display_stats
do
if self.modelbuilder.toolcontext.opt_typing_test_metrics.value then
print "# static count_type_test"
print "\tresolved:\tunresolved\tskipped\ttotal"
var count_type_test_total = init_count_type_test_tags
count_type_test_resolved["total"] = 0
count_type_test_unresolved["total"] = 0
count_type_test_skipped["total"] = 0
count_type_test_total["total"] = 0
for tag in count_type_test_tags do
count_type_test_total[tag] = count_type_test_resolved[tag] + count_type_test_unresolved[tag] + count_type_test_skipped[tag]
count_type_test_resolved["total"] += count_type_test_resolved[tag]
count_type_test_unresolved["total"] += count_type_test_unresolved[tag]
count_type_test_skipped["total"] += count_type_test_skipped[tag]
count_type_test_total["total"] += count_type_test_total[tag]
end
var count_type_test = count_type_test_total["total"]
var tags = count_type_test_tags.to_a
tags.add("total")
for tag in tags do
printn tag
printn "\t{count_type_test_resolved[tag]} ({div(count_type_test_resolved[tag],count_type_test)}%)"
printn "\t{count_type_test_unresolved[tag]} ({div(count_type_test_unresolved[tag],count_type_test)}%)"
printn "\t{count_type_test_skipped[tag]} ({div(count_type_test_skipped[tag],count_type_test)}%)"
printn "\t{count_type_test_total[tag]} ({div(count_type_test_total[tag],count_type_test)}%)"
print ""
end
end
end
fun finalize_ffi_for_module(mmodule: MModule) do mmodule.finalize_ffi(self)
end
src/compiler/abstract_compiler.nit:601,1--1275,3