X-Git-Url: http://nitlanguage.org diff --git a/src/global_compiler.nit b/src/global_compiler.nit index 67d243a..2196071 100644 --- a/src/global_compiler.nit +++ b/src/global_compiler.nit @@ -26,6 +26,29 @@ module global_compiler import abstract_compiler import rapid_type_analysis +redef class ToolContext + # option --global + var opt_global = new OptionBool("Use global compilation", "--global") + + var global_compiler_phase = new GlobalCompilerPhase(self, null) + + redef init do + super + option_context.add_option(opt_global) + end +end + +class GlobalCompilerPhase + super Phase + redef fun process_mainmodule(mainmodule, given_mmodules) do + if not toolcontext.opt_global.value then return + + var modelbuilder = toolcontext.modelbuilder + var analysis = modelbuilder.do_rapid_type_analysis(mainmodule) + modelbuilder.run_global_compiler(mainmodule, analysis) + end +end + redef class ModelBuilder # Entry point to performs a global compilation on the AST of a complete program. # `mainmodule` is the main module of the program @@ -33,14 +56,16 @@ redef class ModelBuilder fun run_global_compiler(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis) do var time0 = get_time - self.toolcontext.info("*** COMPILING TO C ***", 1) + self.toolcontext.info("*** GENERATING C ***", 1) var compiler = new GlobalCompiler(mainmodule, self, runtime_type_analysis) compiler.compile_header - var v = compiler.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(v, t) + compiler.declare_runtimeclass(t) end compiler.compile_class_names @@ -49,13 +74,16 @@ redef class ModelBuilder for t in runtime_type_analysis.live_types do if t.ctype == "val*" then compiler.generate_init_instance(t) - compiler.generate_check_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 @@ -64,12 +92,12 @@ redef class ModelBuilder self.toolcontext.info("Compile {m} ({compiler.seen.length-compiler.todos.length}/{compiler.seen.length})", 3) m.compile_to_c(compiler) end - self.toolcontext.info("Total methods to compile to C: {compiler.visitors.length}", 2) + self.toolcontext.info("Total methods to compile to C: {compiler.seen.length}", 2) compiler.display_stats var time1 = get_time - self.toolcontext.info("*** END VISITING: {time1-time0} ***", 2) + self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2) write_and_make(compiler) end end @@ -89,11 +117,12 @@ class GlobalCompiler init(mainmodule: MModule, modelbuilder: ModelBuilder, runtime_type_analysis: RapidTypeAnalysis) do super(mainmodule, modelbuilder) - self.header = new_visitor + var file = new_file("{mainmodule.name}.nitgg") + self.header = new CodeWriter(file) self.runtime_type_analysis = runtime_type_analysis self.live_primitive_types = new Array[MClassType] for t in runtime_type_analysis.live_types do - if t.ctype != "val*" then + if t.ctype != "val*" or t.mclass.name == "Pointer" then self.live_primitive_types.add(t) end end @@ -101,12 +130,13 @@ class GlobalCompiler # Compile class names (for the class_name and output_class_name methods) protected fun compile_class_names do - self.header.add_decl("extern const char const * class_names[];") - self.header.add("const char const * class_names[] = \{") + 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 - self.header.add("\"{t}\", /* {self.classid(t)} */") + v.add("\"{t}\", /* {self.classid(t)} */") end - self.header.add("\};") + v.add("\};") end # Return the C symbol associated to a live type runtime @@ -124,9 +154,9 @@ class GlobalCompiler 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. + # 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. + # 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 @@ -150,8 +180,9 @@ class GlobalCompiler private var seen: HashSet[AbstractRuntimeFunction] = new HashSet[AbstractRuntimeFunction] # Declare C structures and identifiers for a runtime class - fun declare_runtimeclass(v: VISITOR, mtype: MClassType) + 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 @@ -164,14 +195,15 @@ class GlobalCompiler 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 mtype.ctype != "val*" then + 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} value;") + v.add_decl("{mtype.ctype_extern} value;") end # Collect all attributes and associate them a field in the structure. @@ -210,50 +242,49 @@ class GlobalCompiler res.is_exact = true if is_native_array then var mtype_elt = mtype.arguments.first - v.add("{res} = GC_MALLOC(sizeof(struct {mtype.c_name}) + length*sizeof({mtype_elt.ctype}));") + v.add("{res} = nit_alloc(sizeof(struct {mtype.c_name}) + length*sizeof({mtype_elt.ctype}));") + v.add("((struct {mtype.c_name}*){res})->length = length;") else - v.add("{res} = GC_MALLOC(sizeof(struct {mtype.c_name}));") + v.add("{res} = nit_alloc(sizeof(struct {mtype.c_name}));") 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 - redef fun generate_check_init_instance(mtype) - do - if self.modelbuilder.toolcontext.opt_no_check_initialization.value then return - - var v = self.new_visitor - var res = new RuntimeVariable("self", mtype, mtype) - self.header.add_decl("void CHECK_NEW_{mtype.c_name}({mtype.ctype});") - v.add_decl("/* allocate {mtype} */") - v.add_decl("void CHECK_NEW_{mtype.c_name}({mtype.ctype} {res}) \{") - self.generate_check_attr(v, res, mtype) - v.add("\}") - end - fun generate_box_instance(mtype: MClassType) do assert self.runtime_type_analysis.live_types.has(mtype) - assert mtype.ctype != "val*" 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 = GC_MALLOC(sizeof(struct {mtype.c_name}));") + 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 # A visitor on the AST of property definition that generate the C code. @@ -276,18 +307,48 @@ class GlobalCompilerVisitor var res = self.new_var(mtype) if not compiler.runtime_type_analysis.live_types.has(valtype) then self.add("/*no autobox from {value.mtype} to {mtype}: {value.mtype} is not live! */") - self.add("printf(\"Dead code executed!\\n\"); exit(1);") + self.add("PRINT_ERROR(\"Dead code executed!\\n\"); show_backtrace(1);") return res end self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */") return res + else if value.mtype.ctype == "void*" and mtype.ctype == "void*" then + return value else # Bad things will appen! var res = self.new_var(mtype) self.add("/* {res} left unintialized (cannot convert {value.mtype} to {mtype}) */") - self.add("printf(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); exit(1);") + self.add("PRINT_ERROR(\"Cast error: Cannot cast %s to %s.\\n\", \"{value.mtype}\", \"{mtype}\"); show_backtrace(1);") + return res + end + end + + redef fun unbox_extern(value, mtype) + do + if mtype isa MClassType and mtype.mclass.kind == extern_kind and + mtype.mclass.name != "NativeString" then + var res = self.new_var_extern(mtype) + self.add "{res} = ((struct {mtype.c_name}*){value})->value; /* unboxing {value.mtype} */" + return res + else + return value + end + end + + redef fun box_extern(value, mtype) + do + if not mtype isa MClassType or mtype.mclass.kind != extern_kind or + mtype.mclass.name == "NativeString" then return value + + var valtype = value.mtype.as(MClassType) + var res = self.new_var(mtype) + if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(value.mtype.as(MClassType)) then + self.add("/*no boxing of {value.mtype}: {value.mtype} is not live! */") + self.add("PRINT_ERROR(\"Dead code executed!\\n\"); show_backtrace(1);") return res end + self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */") + return res end # The runtime types that are acceptable for a given receiver. @@ -324,13 +385,22 @@ class GlobalCompilerVisitor else if pname == "[]=" then self.add("{recv}[{arguments[1]}]={arguments[2]};") return + else if pname == "length" then + self.ret(self.new_expr("((struct {arguments[0].mcasttype.c_name}*){arguments[0]})->length", ret_type.as(not null))) + return else if pname == "copy_to" then var recv1 = "((struct {arguments[1].mcasttype.c_name}*){arguments[1]})->values" - self.add("memcpy({recv1},{recv},{arguments[2]}*sizeof({elttype.ctype}));") + self.add("memmove({recv1},{recv},{arguments[2]}*sizeof({elttype.ctype}));") return end end + redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable + do + var ret_type = self.get_class("NativeArray").get_mtype([elttype]) + return self.new_expr("NEW_{ret_type.c_name}({length})", ret_type) + end + redef fun calloc_array(ret_type, arguments) do self.ret(self.new_expr("NEW_{ret_type.c_name}({arguments[1]})", ret_type)) @@ -364,7 +434,7 @@ class GlobalCompilerVisitor if res != null then self.assign(res, res2.as(not null)) return res end - var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_other.value or m.name == "==" or m.name == "!=" + var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_null.value or m.name == "==" or m.name == "!=" if args.first.mcasttype isa MNullableType or args.first.mcasttype isa MNullType and consider_null then # The reciever is potentially null, so we have to 3 cases: ==, != or NullPointerException self.add("if ({args.first} == NULL) \{ /* Special null case */") @@ -387,7 +457,7 @@ class GlobalCompilerVisitor self.add("{res} = 1; /* {args[1].inspect} cannot be null */") end else - self.add_abort("Reciever is null") + self.add_abort("Receiver is null") end self.add "\} else" end @@ -436,29 +506,114 @@ class GlobalCompilerVisitor abort end - redef fun call(m, recvtype, args) + # Subpart of old call function + # + # Checks if the type of the receiver is valid and corrects it if necessary + private fun get_recvtype(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): MClassType do check_valid_reciever(recvtype) #debug("call {m} on {recvtype} on {args.first}:{args.first.mtype}") - if m.mclassdef.mclass.name == "Object" and recvtype.ctype == "val*" then + if m.mproperty.is_toplevel then + # Do not customize top-level methods recvtype = m.mclassdef.bound_mtype end - var recv = self.autobox(args.first, recvtype) - recv = self.autoadapt(recv, recvtype) + return recvtype + end - args = args.to_a - self.varargize(m, m.msignature.as(not null), args) - if args.length != m.msignature.arity + 1 then # because of self - add("printf(\"NOT YET IMPLEMENTED: Invalid arity for {m}. {args.length} arguments given.\\n\"); exit(1);") - debug("NOT YET IMPLEMENTED: Invalid arity for {m}. {args.length} arguments given.") - return null - end + # Subpart of old call function + # Gets the receiver boxed and casted if necessary + private fun get_recv(recvtype: MClassType, args: Array[RuntimeVariable]): RuntimeVariable + do + return self.autoadapt(self.autobox(args.first, recvtype), recvtype) + end + + # Finalizes a call to a method ´m´ on type ´recvtype´ with arguments ´args´ + private fun finalize_call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable + do + assert args.length == m.msignature.arity + 1 else debug("Invalid arity for {m}. {args.length} arguments given.") - args.first = recv var rm = new CustomizedRuntimeFunction(m, recvtype) return rm.call(self, args) end + redef fun call(m, recvtype, args) + do + var recv_type = get_recvtype(m, recvtype, args) + var recv = get_recv(recv_type, args) + if m.is_extern then recv = unbox_extern(recv, recv_type) + var new_args = args.to_a + self.varargize(m, m.msignature.as(not null), new_args) + new_args.first = recv + return finalize_call(m, recv_type, new_args) + end + + # Does a call without encapsulating varargs into an array + # Avoids multiple encapsulation when calling a super in a variadic function + fun call_without_varargize(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable + do + var recv_type = get_recvtype(m, recvtype, args) + var recv = get_recv(recv_type, args) + if m.is_extern then recv = unbox_extern(recv, recv_type) + var new_args = args.to_a + new_args.first = recv + return finalize_call(m, recv_type, new_args) + end + + redef fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable + do + var types = self.collect_types(args.first) + + var res: nullable RuntimeVariable + var ret = m.mproperty.intro.msignature.return_mtype + if ret == null then + res = null + else + ret = self.resolve_for(ret, args.first) + res = self.new_var(ret) + end + + self.add("/* super {m} on {args.first.inspect} */") + if args.first.mtype.ctype != "val*" then + var mclasstype = args.first.mtype.as(MClassType) + if not self.compiler.runtime_type_analysis.live_types.has(mclasstype) then + self.add("/* skip, no method {m} */") + return res + end + var propdef = m.lookup_next_definition(self.compiler.mainmodule, mclasstype) + var res2 = self.call_without_varargize(propdef, mclasstype, args) + if res != null then self.assign(res, res2.as(not null)) + return res + end + + if types.is_empty then + self.add("\{") + self.add("/*BUG: no live types for {args.first.inspect} . {m}*/") + self.bugtype(args.first) + self.add("\}") + return res + end + + self.add("switch({args.first}->classid) \{") + var last = types.last + for t in types do + var propdef = m.lookup_next_definition(self.compiler.mainmodule, t) + if not self.compiler.hardening and t == last then + self.add("default: /* test {t} */") + else + self.add("case {self.compiler.classid(t)}: /* test {t} */") + end + var res2 = self.call_without_varargize(propdef, t, args) + if res != null then self.assign(res, res2.as(not null)) + self.add "break;" + end + if self.compiler.hardening then + self.add("default: /* bug */") + self.bugtype(args.first) + end + self.add("\}") + return res + end + redef fun adapt_signature(m, args) do var recv = args.first @@ -472,6 +627,19 @@ class GlobalCompilerVisitor end end + redef fun unbox_signature_extern(m, args) + do + var recv = args.first + for i in [0..m.msignature.arity[ do + var t = m.msignature.mparameters[i].mtype + if i == m.msignature.vararg_rank then + t = args[i+1].mtype + end + t = self.resolve_for(t, recv) + if m.is_extern then args[i+1] = self.unbox_extern(args[i+1], t) + end + end + # FIXME: this is currently buggy since recv is not exact redef fun vararg_instance(mpropdef, recv, varargs, elttype) do @@ -482,8 +650,8 @@ class GlobalCompilerVisitor fun bugtype(recv: RuntimeVariable) do if recv.mtype.ctype != "val*" then return - self.add("fprintf(stderr, \"BTD BUG: Dynamic type is %s, static type is %s\\n\", class_names[{recv}->classid], \"{recv.mcasttype}\");") - self.add("exit(1);") + self.add("PRINT_ERROR(\"BTD BUG: Dynamic type is %s, static type is %s\\n\", class_names[{recv}->classid], \"{recv.mcasttype}\");") + self.add("show_backtrace(1);") end redef fun isset_attribute(a, recv) @@ -557,7 +725,7 @@ class GlobalCompilerVisitor var ta = a.intro.static_mtype.as(not null) ta = self.resolve_for(ta, recv2) var res2 = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta) - if not ta isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_other.value then + if not ta isa MNullableType and not self.compiler.modelbuilder.toolcontext.opt_no_check_attr_isset.value then if ta.ctype == "val*" then self.add("if ({res2} == NULL) \{") self.add_abort("Uninitialized attribute {a.name}") @@ -625,10 +793,7 @@ class GlobalCompilerVisitor redef fun type_test(value, mtype, tag) do mtype = self.anchor(mtype) - var mclasstype = mtype - if mtype isa MNullableType then mclasstype = mtype.mtype - assert mclasstype isa MClassType - if not self.compiler.runtime_type_analysis.live_cast_types.has(mclasstype) then + if not self.compiler.runtime_type_analysis.live_cast_types.has(mtype) then debug "problem: {mtype} was detected cast-dead" abort end @@ -729,6 +894,15 @@ class GlobalCompilerVisitor if not t.is_subtype(self.compiler.mainmodule, null, value2.mcasttype) then continue s.add "({value1}->classid == {self.compiler.classid(t)} && ((struct {t.c_name}*){value1})->value == ((struct {t.c_name}*){value2})->value)" end + + if self.compiler.mainmodule.model.get_mclasses_by_name("Pointer") != null then + var pointer_type = self.compiler.mainmodule.pointer_type + if value1.mcasttype.is_subtype(self.compiler.mainmodule, null, pointer_type) or + value2.mcasttype.is_subtype(self.compiler.mainmodule, null, pointer_type) then + s.add "(((struct {pointer_type.c_name}*){value1})->value == ((struct {pointer_type.c_name}*){value2})->value)" + end + end + if s.is_empty then self.add("{res} = {value1} == {value2};") else @@ -738,18 +912,6 @@ class GlobalCompilerVisitor return res end - redef fun check_init_instance(recv, mtype) - do - if self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then return - - mtype = self.anchor(mtype).as(MClassType) - if not self.compiler.runtime_type_analysis.live_types.has(mtype) then - debug "problem: {mtype} was detected dead" - end - - self.add("CHECK_NEW_{mtype.c_name}({recv});") - end - redef fun array_instance(array, elttype) do elttype = self.anchor(elttype) @@ -765,7 +927,6 @@ class GlobalCompilerVisitor end var length = self.int_instance(array.length) self.send(self.get_property("with_native", arraytype), [res, nat, length]) - self.check_init_instance(res, arraytype) self.add("\}") return res end @@ -841,8 +1002,8 @@ private class CustomizedRuntimeFunction var frame = new Frame(v, mmethoddef, recv, arguments) v.frame = frame - var sig = new Buffer - var comment = new Buffer + var sig = new FlatBuffer + var comment = new FlatBuffer var ret = mmethoddef.msignature.return_mtype if ret != null then ret = v.resolve_for(ret, selfvar) @@ -890,6 +1051,7 @@ private class CustomizedRuntimeFunction v.add("return {frame.returnvar.as(not null)};") end v.add("\}") + if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})" end redef fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable @@ -927,4 +1089,4 @@ private class CustomizedRuntimeFunction return res end end -end \ No newline at end of file +end