Property definitions

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