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
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
for t in runtime_type_analysis.live_types do
- compiler.declare_runtimeclass(v, t)
+ compiler.declare_runtimeclass(t)
end
compiler.compile_class_names
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)
else
compiler.generate_box_instance(t)
end
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
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
# 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 const * class_names[];")
- self.header.add("const char const * class_names[] = \{")
+ v.add("const char const * 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
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
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
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
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)};")
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)
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 \{ val *value; \};")
+ end
end
# A visitor on the AST of property definition that generate the C code.
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
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}));")
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))
self.add("/* skip, no method {m} */")
return res
end
- var propdefs = m.lookup_definitions(self.compiler.mainmodule, mclasstype)
- if propdefs.length == 0 then
- self.add("/* skip, no method {m} */")
- return res
- end
- assert propdefs.length == 1
- var propdef = propdefs.first
+ var propdef = m.lookup_first_definition(self.compiler.mainmodule, mclasstype)
var res2 = self.call(propdef, mclasstype, args)
if res != null then self.assign(res, res2.as(not null))
return res
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
var last = types.last
var defaultpropdef: nullable MMethodDef = null
for t in types do
- var propdefs = m.lookup_definitions(self.compiler.mainmodule, t)
- if propdefs.length == 0 then
- self.add("/* skip {t}, no method {m} */")
- continue
- end
- if propdefs.length > 1 then
- self.debug("NOT YET IMPLEMENTED conflict for {t}.{m}: {propdefs.join(" ")}. choose the first")
- end
- var propdef = propdefs.first
+ var propdef = m.lookup_first_definition(self.compiler.mainmodule, t)
if propdef.mclassdef.mclass.name == "Object" and t.ctype == "val*" then
defaultpropdef = propdef
continue
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
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)
+ 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)
+ 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
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)
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
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)
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
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)
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
return res
end
end
-end
\ No newline at end of file
+end