nitc :: GlobalCompiler :: defaultinit
# Compiler that use global compilation and perform hard optimisations like:
# * customization
# * switch dispatch
# * inlining
class GlobalCompiler
super AbstractCompiler
redef type VISITOR: GlobalCompilerVisitor
# The result of the RTA (used to know live types and methods)
var runtime_type_analysis: RapidTypeAnalysis
init
do
var file = new_file("{mainmodule.c_name}.nitgg")
self.header = new CodeWriter(file)
self.live_primitive_types = new Array[MClassType]
for t in runtime_type_analysis.live_types do
if t.is_c_primitive or t.mclass.name == "Pointer" then
self.live_primitive_types.add(t)
end
end
end
redef fun do_compilation
do
var compiler = self
compiler.compile_header
if mainmodule.model.get_mclasses_by_name("Pointer") != null then
runtime_type_analysis.live_types.add(mainmodule.pointer_type)
end
for t in runtime_type_analysis.live_types do
compiler.declare_runtimeclass(t)
end
compiler.compile_class_names
# Init instance code (allocate and init-arguments)
for t in runtime_type_analysis.live_types do
if not t.is_c_primitive then
compiler.generate_init_instance(t)
if t.mclass.kind == extern_kind then
compiler.generate_box_instance(t)
end
else
compiler.generate_box_instance(t)
end
end
# The main function of the C
compiler.compile_nitni_global_ref_functions
compiler.compile_main_function
# Compile until all runtime_functions are visited
while not compiler.todos.is_empty do
var m = compiler.todos.shift
modelbuilder.toolcontext.info("Compile {m} ({compiler.seen.length-compiler.todos.length}/{compiler.seen.length})", 3)
m.compile_to_c(compiler)
end
modelbuilder.toolcontext.info("Total methods to compile to C: {compiler.seen.length}", 2)
end
# Compile class names (for the class_name and output_class_name methods)
protected fun compile_class_names do
var v = new_visitor
self.header.add_decl("extern const char *class_names[];")
v.add("const char *class_names[] = \{")
for t in self.runtime_type_analysis.live_types do
v.add("\"{t}\", /* {self.classid(t)} */")
end
v.add("\};")
end
# Return the C symbol associated to a live type runtime
# REQUIRE: self.runtime_type_analysis.live_types.has(mtype)
fun classid(mtype: MClassType): String
do
if self.classids.has_key(mtype) then
return self.classids[mtype]
end
print_error "No classid for {mtype}"
abort
end
# Cache for classid
protected var classids: HashMap[MClassType, String] = new HashMap[MClassType, String]
# Declaration of structures the live Nit types
# Each live type is generated as an independent C `struct` type.
# They only share a common first field `classid` used to implement the polymorphism.
# Usualy, all C variables that refers to a Nit object are typed on the abstract struct `val` that contains only the `classid` field.
redef fun compile_header_structs do
self.header.add_decl("typedef struct \{int classid;\} val; /* general C type representing a Nit instance. */")
end
# Subset of runtime_type_analysis.live_types that contains only primitive types
# Used to implement the equal test
var live_primitive_types: Array[MClassType] is noinit
# Add a new todo task
fun todo(m: AbstractRuntimeFunction)
do
if seen.has(m) then return
todos.add(m)
seen.add(m)
end
# runtime_functions that need to be compiled
private var todos: List[AbstractRuntimeFunction] = new List[AbstractRuntimeFunction]
# runtime_functions already seen (todo or done)
private var seen: HashSet[AbstractRuntimeFunction] = new HashSet[AbstractRuntimeFunction]
# Declare C structures and identifiers for a runtime class
fun declare_runtimeclass(mtype: MClassType)
do
var v = self.header
assert self.runtime_type_analysis.live_types.has(mtype)
v.add_decl("/* runtime class {mtype} */")
var idnum = classids.length
var idname = "ID_" + mtype.c_name
self.classids[mtype] = idname
v.add_decl("#define {idname} {idnum} /* {mtype} */")
v.add_decl("struct {mtype.c_name} \{")
v.add_decl("int classid; /* must be {idname} */")
if mtype.mclass.name == "NativeArray" then
# NativeArrays are just a instance header followed by an array of values
v.add_decl("int length;")
v.add_decl("{mtype.arguments.first.ctype} values[1];")
end
if all_routine_types_name.has(mtype.mclass.name) then
v.add_decl("val* recv;")
var c_args = ["val* self"]
var c_ret = "void"
var k = mtype.arguments.length
if mtype.mclass.name.has("Fun") then
c_ret = mtype.arguments.last.ctype
k -= 1
end
for i in [0..k[ do
var t = mtype.arguments[i]
c_args.push("{t.ctype} p{i}")
end
var c_sig = c_args.join(", ")
v.add_decl("{c_ret} (*method)({c_sig});")
end
if mtype.ctype_extern != "val*" then
# Is the Nit type is native then the struct is a box with two fields:
# * the `classid` to be polymorph
# * the `value` that contains the native value.
v.add_decl("{mtype.ctype_extern} value;")
end
# Collect all attributes and associate them a field in the structure.
# Note: we do not try to optimize the order and helps CC to optimize the client code.
for cd in mtype.collect_mclassdefs(self.mainmodule) do
for p in cd.intro_mproperties do
if not p isa MAttribute then continue
var t = p.intro.static_mtype.as(not null)
t = t.anchor_to(self.mainmodule, mtype)
v.add_decl("{t.ctype} {p.intro.c_name}; /* {p}: {t} */")
end
end
v.add_decl("\};")
end
# Generate the init-instance of a live type (allocate + init-instance)
fun generate_init_instance(mtype: MClassType)
do
assert self.runtime_type_analysis.live_types.has(mtype)
assert not mtype.is_c_primitive
var v = self.new_visitor
var is_native_array = mtype.mclass.name == "NativeArray"
var is_routine_ref = all_routine_types_name.has(mtype.mclass.name)
var sig
if is_native_array then
sig = "int length"
else
sig = "void"
end
if is_routine_ref then
var c_args = ["val* self"]
var c_ret = "void"
var k = mtype.arguments.length
if mtype.mclass.name.has("Fun") then
c_ret = mtype.arguments.last.ctype
k -= 1
end
for i in [0..k[ do
var t = mtype.arguments[i]
c_args.push("{t.ctype} p{i}")
end
# The underlying method signature
var method_sig = "{c_ret} (*method)({c_args.join(", ")})"
sig = "val* recv, {method_sig}"
end
self.header.add_decl("{mtype.ctype} NEW_{mtype.c_name}({sig});")
v.add_decl("/* allocate {mtype} */")
v.add_decl("{mtype.ctype} NEW_{mtype.c_name}({sig}) \{")
var res = v.new_var(mtype)
res.is_exact = true
if is_native_array then
v.add("{res} = nit_alloc(sizeof(struct {mtype.c_name}) + length*sizeof(val*));")
v.add("((struct {mtype.c_name}*){res})->length = length;")
else
v.add("{res} = nit_alloc(sizeof(struct {mtype.c_name}));")
end
if is_routine_ref then
v.add("((struct {mtype.c_name}*){res})->recv = recv;")
v.add("((struct {mtype.c_name}*){res})->method = method;")
end
v.add("{res}->classid = {self.classid(mtype)};")
self.generate_init_attr(v, res, mtype)
v.set_finalizer res
v.add("return {res};")
v.add("\}")
end
fun generate_box_instance(mtype: MClassType)
do
assert self.runtime_type_analysis.live_types.has(mtype)
var v = self.new_visitor
self.header.add_decl("val* BOX_{mtype.c_name}({mtype.ctype});")
v.add_decl("/* allocate {mtype} */")
v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype} value) \{")
v.add("struct {mtype.c_name}*res = nit_alloc(sizeof(struct {mtype.c_name}));")
v.add("res->classid = {self.classid(mtype)};")
v.add("res->value = value;")
v.add("return (val*)res;")
v.add("\}")
end
redef fun new_visitor do return new GlobalCompilerVisitor(self)
private var collect_types_cache: HashMap[MType, Array[MClassType]] = new HashMap[MType, Array[MClassType]]
redef fun compile_nitni_structs
do
self.header.add_decl """
struct nitni_instance \{
struct nitni_instance *next,
*prev; /* adjacent global references in global list */
int count; /* number of time this global reference has been marked */
val *value;
\};"""
super
end
end
src/compiler/global_compiler.nit:71,1--329,3