X-Git-Url: http://nitlanguage.org diff --git a/src/global_compiler.nit b/src/global_compiler.nit index aec5002..cd2eaa2 100644 --- a/src/global_compiler.nit +++ b/src/global_compiler.nit @@ -23,41 +23,9 @@ # * inlining module global_compiler -import literal -import typing -import auto_super_init +import abstract_compiler import rapid_type_analysis -redef class ToolContext - # --output - var opt_output: OptionString = new OptionString("Output file", "-o", "--output") - - # --no-cc - var opt_no_cc: OptionBool = new OptionBool("Do not invoke C compiler", "--no-cc") - - # --hardening - var opt_hardening: OptionBool = new OptionBool("Generate contracts in the C code against bugs in the compiler", "--hardening") - - # --no-check-covariance - var opt_no_check_covariance: OptionBool = new OptionBool("Disable type tests of covariant parameters (dangerous)", "--no-check-covariance") - - # --no-check-initialization - var opt_no_check_initialization: OptionBool = new OptionBool("Disable isset tests at the end of constructors (dangerous)", "--no-check-initialization") - - # --no-check-assert - var opt_no_check_assert: OptionBool = new OptionBool("Disable the evaluation of explicit 'assert' and 'as' (dangerous)", "--no-check-assert") - - # --no-check-other - var opt_no_check_other: OptionBool = new OptionBool("Disable implicit tests: unset attribute, null receiver (dangerous)", "--no-check-other") - - redef init - do - super - self.option_context.add_option(self.opt_output, self.opt_no_cc, self.opt_hardening) - self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_initialization, self.opt_no_check_assert, self.opt_no_check_other) - 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 @@ -65,50 +33,22 @@ 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) - - var compiler = new GlobalCompiler(mainmodule, runtime_type_analysis, self) - var v = compiler.new_visitor - - v.add_decl("#include ") - v.add_decl("#include ") - v.add_decl("#include ") + self.toolcontext.info("*** GENERATING C ***", 1) - # TODO: Better way to activate the GC - v.add_decl("#include ") - #v.add_decl("#define GC_MALLOC(x) calloc(1, (x))") + var compiler = new GlobalCompiler(mainmodule, self, runtime_type_analysis) + compiler.compile_header - # 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. - - v.add_decl("typedef struct \{int classid;\} val; /* general C type representing a Nit instance. */") for t in runtime_type_analysis.live_types do - compiler.declare_runtimeclass(v, t) + compiler.declare_runtimeclass(t) end - # Global variable used by the legacy native interface - - v.add_decl("extern int glob_argc;") - v.add_decl("extern char **glob_argv;") - v.add_decl("extern val *glob_sys;") - - # Class names (for the class_name and output_class_name methods) - - v.add_decl("extern const char const * class_names[];") - v.add("const char const * class_names[] = \{") - for t in runtime_type_analysis.live_types do - v.add("\"{t}\", /* {compiler.classid(t)} */") - end - v.add("\};") - compiler.header = v + compiler.compile_class_names # Init instance code (allocate and init-arguments) - 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 @@ -118,148 +58,39 @@ redef class ModelBuilder compiler.compile_main_function # Compile until all runtime_functions are visited - while not compiler.todos.is_empty do var m = compiler.todos.shift 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) - - var time1 = get_time - self.toolcontext.info("*** END VISITING: {time1-time0} ***", 2) - write_and_make(compiler) - end - - protected fun write_and_make(compiler: GlobalCompiler) - do - var mainmodule = compiler.mainmodule - - # Generate the .h and .c files - # A single C file regroups many compiled rumtime functions - # Note that we do not try to be clever an a small change in a Nit source file may change the content of all the generated .c files - var time0 = get_time - - var outname = self.toolcontext.opt_output.value - if outname == null then - outname = "{mainmodule.name}.bin" - end - - var hfilename = ".nit_compile/{mainmodule.name}.1.h" - var h = new OFStream.open(hfilename) - for l in compiler.header.decl_lines do - h.write l - h.write "\n" - end - h.close - - var cfiles = new Array[String] - - var file: nullable OFStream = null - var count = 0 - - ".nit_compile".mkdir - var i = 0 - for vis in compiler.visitors do - count += vis.lines.length - if file == null or count > 10000 then - i += 1 - if file != null then file.close - var cfilename = ".nit_compile/{mainmodule.name}.{i}.c" - cfiles.add(cfilename) - file = new OFStream.open(cfilename) - file.write "#include \"{mainmodule.name}.1.h\"\n" - count = vis.lines.length - end - if vis != compiler.header then - for l in vis.decl_lines do - file.write l - file.write "\n" - end - end - for l in vis.lines do - file.write l - file.write "\n" - end - end - if file != null then file.close - - self.toolcontext.info("Total C source files to compile: {cfiles.length}", 2) - - # Generate the Makefile - - var makename = ".nit_compile/{mainmodule.name}.mk" - var makefile = new OFStream.open(makename) + self.toolcontext.info("Total methods to compile to C: {compiler.seen.length}", 2) - makefile.write("CC = ccache cc\nCFLAGS = -g -O2\nLDFLAGS ?= \nLDLIBS ?= -lm -lgc\n\n") - makefile.write("all: {outname}\n\n") - - var ofiles = new Array[String] - # Compile each generated file - for f in cfiles do - var o = f.strip_extension(".c") + ".o" - makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -D NONITCNI -c -o {o} {f}\n\n") - ofiles.add(o) - end - # Compile each required extern body into a specific .o - for f in compiler.extern_bodies do - i += 1 - var o = ".nit_compile/{mainmodule.name}.{i}.o" - makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -D NONITCNI -c -o {o} {f}\n\n") - ofiles.add(o) - end - # Link edition - makefile.write("{outname}: {ofiles.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outname} {ofiles.join(" ")} $(LDLIBS)\n\n") - # Clean - makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n\n") - makefile.close - self.toolcontext.info("Generated makefile: {makename}", 2) + compiler.display_stats var time1 = get_time - self.toolcontext.info("*** END COMPILING TO C: {time1-time0} ***", 2) - - # Execute the Makefile - - if self.toolcontext.opt_no_cc.value then return - - time0 = time1 - self.toolcontext.info("*** COMPILING C ***", 1) - self.toolcontext.info("make -B -f {makename} -j 4", 2) - - var res - if self.toolcontext.verbose_level >= 3 then - res = sys.system("make -B -f {makename} -j 4 2>&1") - else - res = sys.system("make -B -f {makename} -j 4 2>&1 >/dev/null") - end - if res != 0 then - toolcontext.error(null, "make failed! Error code: {res}.") - end - - time1 = get_time - self.toolcontext.info("*** END COMPILING C: {time1-time0} ***", 2) + self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2) + write_and_make(compiler) end end -# Singleton that store the knowledge about the compilation process +# Compiler that use global compilation and perform hard optimisations like: +# * customization +# * switch dispatch +# * inlining class GlobalCompiler - # The main module of the program - var mainmodule: MModule + super AbstractCompiler + + redef type VISITOR: GlobalCompilerVisitor # The result of the RTA (used to know live types and methods) var runtime_type_analysis: RapidTypeAnalysis - # The modeulbuilder used to know the model and the AST - var modelbuilder: ModelBuilder - - # Is hardening asked? (see --hardening) - fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value - - init(mainmodule: MModule, runtime_type_analysis: RapidTypeAnalysis, modelbuilder: ModelBuilder) + init(mainmodule: MModule, modelbuilder: ModelBuilder, runtime_type_analysis: RapidTypeAnalysis) do - self.mainmodule = mainmodule + super(mainmodule, modelbuilder) + var file = new_file(mainmodule.name) + self.header = new CodeWriter(file) self.runtime_type_analysis = runtime_type_analysis - self.modelbuilder = modelbuilder self.live_primitive_types = new Array[MClassType] for t in runtime_type_analysis.live_types do if t.ctype != "val*" then @@ -268,36 +99,17 @@ class GlobalCompiler end 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] - - # 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] - fun todo(m: AbstractRuntimeFunction) - do - if seen.has(m) then return - todos.add(m) - seen.add(m) + # 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[];") + v.add("const char const * class_names[] = \{") + for t in self.runtime_type_analysis.live_types do + v.add("\"{t}\", /* {self.classid(t)} */") + end + v.add("\};") end - # Where global declaration are stored (the main .h) - # - # FIXME: should not be a vistor but just somewhere to store lines - # FIXME: should not have a global .h since it does not help recompilations - var header: nullable GlobalCompilerVisitor writable = null - - # The list of all associated visitors - # Used to generate .c files - # FIXME: should not be vistors but just somewhere to store lines - private var visitors: List[GlobalCompilerVisitor] = new List[GlobalCompilerVisitor] - - # List of additional .c files required to compile (native interface) - var extern_bodies = new ArraySet[String] - # Return the C symbol associated to a live type runtime # REQUIRE: self.runtime_type_analysis.live_types.has(mtype) fun classid(mtype: MClassType): String @@ -309,12 +121,39 @@ class GlobalCompiler abort end - # Cache for classid (computed by declare_runtimeclass) + # 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] + + # 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(v: GlobalCompilerVisitor, 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 @@ -373,25 +212,30 @@ 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}));") 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)};") - for cd in mtype.collect_mclassdefs(self.mainmodule) - do - var n = self.modelbuilder.mclassdef2nclassdef[cd] - for npropdef in n.n_propdefs do - if npropdef isa AAttrPropdef then - npropdef.init_expr(v, res) - end - end - end + self.generate_init_attr(v, res, mtype) 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) @@ -401,7 +245,7 @@ class GlobalCompiler 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;") @@ -409,2452 +253,735 @@ class GlobalCompiler end - # look for a needed .h and .c file for a given .nit source-file - # FIXME: bad API, parameter should be a MModule, not its source-file - fun add_extern(file: String) - do - file = file.strip_extension(".nit") - var tryfile = file + ".nit.h" - if tryfile.file_exists then - self.header.add_decl("#include \"{"..".join_path(tryfile)}\"") - end - tryfile = file + "_nit.h" - if tryfile.file_exists then - self.header.add_decl("#include \"{"..".join_path(tryfile)}\"") - end - tryfile = file + ".nit.c" - if tryfile.file_exists then - self.extern_bodies.add(tryfile) - end - tryfile = file + "_nit.c" - if tryfile.file_exists then - self.extern_bodies.add(tryfile) - end - end - - # Initialize a visitor specific for a compiler engine - fun new_visitor: GlobalCompilerVisitor do return new GlobalCompilerVisitor(self) - - # 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("int glob_argc;") - v.add_decl("char **glob_argv;") - v.add_decl("val *glob_sys;") - v.add_decl("int main(int argc, char** argv) \{") - v.add("glob_argc = argc; glob_argv = argv;") - 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) - if main_init != null then - v.send(main_init, [glob_sys]) - end - var main_method = mainmodule.try_get_primitive_method("main", main_type) - if main_method != null then - v.send(main_method, [glob_sys]) - end - end - v.add("return 0;") - 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]] end -redef class String - # Mangle a string to be a unique valid C identifier - fun to_cmangle: String - do - var res = new Buffer - var underscore = false - for c in self do - if (c >= 'a' and c <= 'z') or (c >='A' and c <= 'Z') then - res.add(c) - underscore = false - continue - end - if underscore then - res.append('_'.ascii.to_s) - res.add('d') - end - if c >= '0' and c <= '9' then - res.add(c) - underscore = false - else if c == '_' then - res.add(c) - underscore = true - else - res.add('_') - res.append(c.ascii.to_s) - res.add('d') - underscore = false - end - end - return res.to_s - end - - # Escape " \ ' and non printable characters for literal C strings or characters - fun escape_to_c: String - do - var b = new Buffer - for c in self do - if c == '\n' then - b.append("\\n") - else if c == '\0' then - b.append("\\0") - else if c == '"' then - b.append("\\\"") - else if c == '\'' then - b.append("\\\'") - else if c == '\\' then - b.append("\\\\") - else if c.ascii < 32 then - b.append("\\{c.ascii.to_base(8, false)}") - else - b.add(c) - end - end - return b.to_s - end -end - -redef class MType - # Return the C type associated to a given Nit static type - fun ctype: String - do - return "val*" - end - - # Return the name of the C structure associated to a Nit live type - # FIXME: move to GlobalCompiler so we can check that self is a live type - fun c_name: String is abstract - protected var c_name_cache: nullable String protected writable -end +# A visitor on the AST of property definition that generate the C code. +# Because of inlining, a visitor can visit more than one property. +class GlobalCompilerVisitor + super AbstractCompilerVisitor -redef class MClassType - redef fun c_name - do - var res = self.c_name_cache - if res != null then return res - res = "{mclass.intro_mmodule.name.to_cmangle}__{mclass.name.to_cmangle}" - self.c_name_cache = res - return res - end + redef type COMPILER: GlobalCompiler - redef fun ctype: String + redef fun autobox(value, mtype) do - if mclass.name == "Int" then - return "long" - else if mclass.name == "Bool" then - return "short int" - else if mclass.name == "Char" then - return "char" - else if mclass.name == "Float" then - return "double" - else if mclass.name == "NativeString" then - return "char*" - else if mclass.name == "NativeArray" then - #return "{self.arguments.first.ctype}*" - return "val*" - else if mclass.kind == extern_kind then - return "void*" + if value.mtype == mtype then + return value + else if value.mtype.ctype == "val*" and mtype.ctype == "val*" then + return value + else if value.mtype.ctype == "val*" then + return self.new_expr("((struct {mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype) + else if mtype.ctype == "val*" then + var valtype = value.mtype.as(MClassType) + 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);") + return res + end + self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */") + return res else - return "val*" + # 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);") + return res end end -end -redef class MGenericType - redef fun c_name + # The runtime types that are acceptable for a given receiver. + fun collect_types(recv: RuntimeVariable): Array[MClassType] do - var res = self.c_name_cache - if res != null then return res - res = super - for t in self.arguments do - res = res + t.c_name + var mtype = recv.mcasttype + if recv.is_exact then + assert mtype isa MClassType + assert self.compiler.runtime_type_analysis.live_types.has(mtype) + var types = [mtype] + return types end - self.c_name_cache = res - return res + var cache = self.compiler.collect_types_cache + if cache.has_key(mtype) then + return cache[mtype] + end + var types = new Array[MClassType] + var mainmodule = self.compiler.mainmodule + for t in self.compiler.runtime_type_analysis.live_types do + if not t.is_subtype(mainmodule, null, mtype) then continue + types.add(t) + end + cache[mtype] = types + return types end -end -redef class MParameterType - redef fun c_name + redef fun native_array_def(pname, ret_type, arguments) do - var res = self.c_name_cache - if res != null then return res - res = "FT{self.rank}" - self.c_name_cache = res - return res + var elttype = arguments.first.mtype + var recv = "((struct {arguments[0].mcasttype.c_name}*){arguments[0]})->values" + if pname == "[]" then + self.ret(self.new_expr("{recv}[{arguments[1]}]", ret_type.as(not null))) + return + else if pname == "[]=" then + self.add("{recv}[{arguments[1]}]={arguments[2]};") + 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}));") + return + end end -end -redef class MNullableType - redef fun c_name + redef fun calloc_array(ret_type, arguments) do - var res = self.c_name_cache - if res != null then return res - res = "nullable_{self.mtype.c_name}" - self.c_name_cache = res - return res + self.ret(self.new_expr("NEW_{ret_type.c_name}({arguments[1]})", ret_type)) end -end - -# A C function associated to a Nit method -# Because of customization, a given Nit method can be compiler more that once -abstract class AbstractRuntimeFunction - # The associated Nit method - var mmethoddef: MMethodDef - # The mangled c name of the runtime_function - # Subclasses should redefine `build_c_name` instead - fun c_name: String + redef fun send(m, args) do - var res = self.c_name_cache - if res != null then return res - res = self.build_c_name - self.c_name_cache = res - return res - end - - # Non cached version of `c_name` - protected fun build_c_name: String is abstract - - private var c_name_cache: nullable String = null - - # Implements a call of the runtime_function - # May inline the body or generate a C function call - fun call(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]): nullable RuntimeVariable is abstract - - # Generate the code for the RuntimeFunction - # Warning: compile more than once compilation makes CC unhappy - fun compile_to_c(compiler: GlobalCompiler) is abstract -end - -# A runtime function customized on a specific monomrph receiver type -private class CustomizedRuntimeFunction - super AbstractRuntimeFunction + var types = self.collect_types(args.first) - # The considered reciever - # (usually is a live type but no strong guarantee) - var recv: MClassType + var res: nullable RuntimeVariable + var ret = m.intro.msignature.return_mtype + if m.is_new then + ret = args.first.mtype + res = self.new_var(ret) + else if ret == null then + res = null + else + ret = self.resolve_for(ret, args.first) + res = self.new_var(ret) + end - init(mmethoddef: MMethodDef, recv: MClassType) - do - super(mmethoddef) - self.recv = recv - end - - redef fun build_c_name: String - do - var res = self.c_name_cache - if res != null then return res - if self.mmethoddef.mclassdef.bound_mtype == self.recv then - res = self.mmethoddef.c_name - else - res = "{mmethoddef.c_name}__{recv.c_name}" - end - self.c_name_cache = res - return res - end - - redef fun ==(o) - # used in the compiler worklist - do - if not o isa CustomizedRuntimeFunction then return false - if self.mmethoddef != o.mmethoddef then return false - if self.recv != o.recv then return false - return true - end - - redef fun hash - # used in the compiler work-list - do - var res = self.mmethoddef.hash + self.recv.hash - return res - end - - redef fun to_s - do - if self.mmethoddef.mclassdef.bound_mtype == self.recv then - return self.mmethoddef.to_s - else - return "{self.mmethoddef}@{self.recv}" - end - end - - # compile the code customized for the reciever - redef fun compile_to_c(compiler) - do - var recv = self.recv - var mmethoddef = self.mmethoddef - if not recv.is_subtype(compiler.mainmodule, null, mmethoddef.mclassdef.bound_mtype) then - print("problem: why do we compile {self} for {recv}?") - abort - end - - var v = compiler.new_visitor - var selfvar = new RuntimeVariable("self", recv, recv) - if compiler.runtime_type_analysis.live_types.has(recv) then - selfvar.is_exact = true - end - var arguments = new Array[RuntimeVariable] - var frame = new Frame(v, mmethoddef, recv, arguments) - v.frame = frame - - var sig = new Buffer - var comment = new Buffer - var ret = mmethoddef.msignature.return_mtype - if ret != null then - ret = v.resolve_for(ret, selfvar) - sig.append("{ret.ctype} ") - else if mmethoddef.mproperty.is_new then - ret = recv - sig.append("{ret.ctype} ") - else - sig.append("void ") - end - sig.append(self.c_name) - sig.append("({recv.ctype} {selfvar}") - comment.append("(self: {recv}") - arguments.add(selfvar) - for i in [0..mmethoddef.msignature.arity[ do - var mtype = mmethoddef.msignature.mparameters[i].mtype - if i == mmethoddef.msignature.vararg_rank then - mtype = v.get_class("Array").get_mtype([mtype]) - end - mtype = v.resolve_for(mtype, selfvar) - comment.append(", {mtype}") - sig.append(", {mtype.ctype} p{i}") - var argvar = new RuntimeVariable("p{i}", mtype, mtype) - arguments.add(argvar) - end - sig.append(")") - comment.append(")") - if ret != null then - comment.append(": {ret}") - end - compiler.header.add_decl("{sig};") - - v.add_decl("/* method {self} for {comment} */") - v.add_decl("{sig} \{") - #v.add("printf(\"method {self} for {comment}\\n\");") - if ret != null then - frame.returnvar = v.new_var(ret) - end - frame.returnlabel = v.get_name("RET_LABEL") - - mmethoddef.compile_inside_to_c(v, arguments) - - v.add("{frame.returnlabel.as(not null)}:;") - if ret != null then - v.add("return {frame.returnvar.as(not null)};") - end - v.add("\}") - end - - redef fun call(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]): nullable RuntimeVariable - do - var ret = self.mmethoddef.msignature.return_mtype - if self.mmethoddef.mproperty.is_new then - ret = recv - end - if ret != null then - ret = v.resolve_for(ret, arguments.first) - end - if self.mmethoddef.can_inline(v) then - var frame = new Frame(v, self.mmethoddef, self.recv, arguments) - frame.returnlabel = v.get_name("RET_LABEL") - if ret != null then - frame.returnvar = v.new_var(ret) - end - var old_frame = v.frame - v.frame = frame - v.add("\{ /* Inline {self} ({arguments.join(",")}) */") - self.mmethoddef.compile_inside_to_c(v, arguments) - v.add("{frame.returnlabel.as(not null)}:(void)0;") - v.add("\}") - v.frame = old_frame - return frame.returnvar - end - v.adapt_signature(self.mmethoddef, arguments) - v.compiler.todo(self) - if ret == null then - v.add("{self.c_name}({arguments.join(",")});") - return null - else - var res = v.new_var(ret) - v.add("{res} = {self.c_name}({arguments.join(",")});") - return res - end - end -end - -# A runtime variable hold a runtime value in C. -# Runtime variables are associated to Nit local variables and intermediate results in Nit expressions. -# -# The tricky point is that a single C variable can be associated to more than one RuntimeVariable because the static knowledge of the type of an expression can vary in the C code. -class RuntimeVariable - # The name of the variable in the C code - var name: String - - # The static type of the variable (as declard in C) - var mtype: MType - - # The current casted type of the variable (as known in Nit) - var mcasttype: MType writable - - # If the variable exaclty a mcasttype? - # false (usual value) means that the variable is a mcasttype or a subtype. - var is_exact: Bool writable = false - - init(name: String, mtype: MType, mcasttype: MType) - do - self.name = name - self.mtype = mtype - self.mcasttype = mcasttype - assert not mtype.need_anchor - assert not mcasttype.need_anchor - end - - redef fun to_s do return name - - redef fun inspect - do - var exact_str - if self.is_exact then - exact_str = " exact" - else - exact_str = "" - end - var type_str - if self.mtype == self.mcasttype then - type_str = "{mtype}{exact_str}" - else - type_str = "{mtype}({mcasttype}{exact_str})" - end - return "<{name}:{type_str}>" - end -end - -# A visitor on the AST of property definition that generate the C code. -# Because of inlining, a visitor can visit more than one property. -class GlobalCompilerVisitor - # The associated compiler - var compiler: GlobalCompiler - - init(compiler: GlobalCompiler) - do - self.compiler = compiler - compiler.visitors.add(self) - end - - # Alias for self.compiler.mainmodule.object_type - fun object_type: MClassType do return self.compiler.mainmodule.object_type - - # Alias for self.compiler.mainmodule.bool_type - fun bool_type: MClassType do return self.compiler.mainmodule.bool_type - - # Force to get the primitive class named `name' or abort - fun get_class(name: String): MClass - do - return self.compiler.mainmodule.get_primitive_class(name) - end - - # Force to get the primitive property named `name' in the instance `recv' or abort - fun get_property(name: String, recv: MType): MMethod - do - return self.compiler.modelbuilder.force_get_primitive_method(self.current_node.as(not null), name, recv, self.compiler.mainmodule) - end - - # The current Frame - var frame: nullable Frame writable - - # Anchor a type to the main module and the current receiver - fun anchor(mtype: MType): MType - do - if not mtype.need_anchor then return mtype - #debug("anchor {mtype} to {self.reciever.as(not null)}:{self.reciever.mtype}") - return mtype.anchor_to(self.compiler.mainmodule, self.frame.receiver) - end - - # Add a line in the main part of the generated C - fun add(s: String) - do - self.lines.add(s) - end - - # Add a line in the - # (used for local or global declaration) - fun add_decl(s: String) - do - self.decl_lines.add(s) - end - - private var lines: List[String] = new List[String] - private var decl_lines: List[String] = new List[String] - - # The current visited AST node - var current_node: nullable ANode = null - - # Compile an expression an return its result - # `mtype` is the expected return type, pass null if no specific type is expected. - fun expr(nexpr: AExpr, mtype: nullable MType): RuntimeVariable - do - var old = self.current_node - self.current_node = nexpr - var res = nexpr.expr(self).as(not null) - if mtype != null then - mtype = self.anchor(mtype) - res = self.autobox(res, mtype) - end - self.current_node = old - return res - end - - # Unsafely cast a value to a new type - # ie the result share the same C variable but my have a different mcasttype - # NOTE: if the adaptation is useless then `value' is returned as it. - # ENSURE: return.name == value.name - fun autoadapt(value: RuntimeVariable, mtype: MType): RuntimeVariable - do - mtype = self.anchor(mtype) - var valmtype = value.mcasttype - if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then - return value - end - - if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then - var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype) - return res - else - var res = new RuntimeVariable(value.name, valmtype, mtype) - return res - end - end - - # Box or unbox a value to another type iff a C type conversion is needed - # ENSURE: result.mtype.ctype == mtype.ctype - fun autobox(value: RuntimeVariable, mtype: MType): RuntimeVariable - do - if value.mtype.ctype == mtype.ctype then - return value - else if value.mtype.ctype == "val*" then - return self.new_expr("((struct {mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype) - else if mtype.ctype == "val*" then - var valtype = value.mtype.as(MClassType) - 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);") - return res - end - self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */") - return res - 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);") - return res - end - end - - # Correctly assign a left and a right value - # Boxing and unboxing is performed if required - fun assign(left, right: RuntimeVariable) - do - right = self.autobox(right, left.mtype) - self.add("{left} = {right};") - end - - - # Alias for `self.expr(nexpr, self.bool_type)' - fun expr_bool(nexpr: AExpr): RuntimeVariable - do - return expr(nexpr, bool_type) - end - - # Compile a statement (if any) - fun stmt(nexpr: nullable AExpr) - do - if nexpr == null then return - var old = self.current_node - self.current_node = nexpr - nexpr.stmt(self) - self.current_node = old - end - - # Safely show a debug message on the current node and repeat the message in the C code as a comment - fun debug(message: String) - do - var node = self.current_node - if node == null then - print "?: {message}" - else - node.debug(message) - end - self.add("/* DEBUG: {message} */") - end - - # Return a new uninitialized local runtime_variable - fun new_var(mtype: MType): RuntimeVariable - do - mtype = self.anchor(mtype) - var name = self.get_name("var") - var res = new RuntimeVariable(name, mtype, mtype) - self.add_decl("{mtype.ctype} {name} /* : {mtype} */;") - return res - end - - # Return a new uninitialized named runtime_variable - fun new_named_var(mtype: MType, name: String): RuntimeVariable - do - mtype = self.anchor(mtype) - var res = new RuntimeVariable(name, mtype, mtype) - self.add_decl("{mtype.ctype} {name} /* : {mtype} */;") - return res - end - - # Return a new local runtime_variable initialized with the C expression `cexpr'. - fun new_expr(cexpr: String, mtype: MType): RuntimeVariable - do - var res = new_var(mtype) - self.add("{res} = {cexpr};") - return res - end - - # Return the local runtime_variable associated to a Nit local variable - fun variable(variable: Variable): RuntimeVariable - do - if self.variables.has_key(variable) then - return self.variables[variable] - else - var name = self.get_name("var_{variable.name}") - var mtype = variable.declared_type.as(not null) - mtype = self.anchor(mtype) - var res = new RuntimeVariable(name, mtype, mtype) - self.add_decl("{mtype.ctype} {name} /* var {variable}: {mtype} */;") - self.variables[variable] = res - return res - end - end - - private var variables: HashMap[Variable, RuntimeVariable] = new HashMap[Variable, RuntimeVariable] - - # Return an unique and stable identifier associated with an escapemark - fun escapemark_name(e: nullable EscapeMark): String - do - assert e != null - if escapemark_names.has_key(e) then return escapemark_names[e] - var name = e.name - if name == null then name = "label" - name = get_name(name) - escapemark_names[e] = name - return name - end - - private var escapemark_names = new HashMap[EscapeMark, String] - - # Return a new name based on `s' and unique in the visitor - fun get_name(s: String): String - do - if not self.names.has(s) then - self.names.add(s) - return s - end - var i = self.last + 1 - loop - var s2 = s + i.to_s - if not self.names.has(s2) then - self.last = i - self.names.add(s2) - return s2 - end - i = i + 1 - end - end - - private var last: Int = 0 - - private var names: HashSet[String] = new HashSet[String] - - # Generate a return with the value `s' - fun ret(s: RuntimeVariable) - do - self.assign(self.frame.returnvar.as(not null), s) - self.add("goto {self.frame.returnlabel.as(not null)};") - end - - # The runtime types that are acceptable for a given receiver. - fun collect_types(recv: RuntimeVariable): Array[MClassType] - do - var mtype = recv.mcasttype - if recv.is_exact then - assert mtype isa MClassType - assert self.compiler.runtime_type_analysis.live_types.has(mtype) - var types = [mtype] - return types - end - var cache = self.compiler.collect_types_cache - if cache.has_key(mtype) then - return cache[mtype] - end - var types = new Array[MClassType] - var mainmodule = self.compiler.mainmodule - for t in self.compiler.runtime_type_analysis.live_types do - if not t.is_subtype(mainmodule, null, mtype) then continue - types.add(t) - end - cache[mtype] = types - return types - end - - fun resolve_for(mtype: MType, recv: RuntimeVariable): MType - do - if not mtype.need_anchor then return mtype - #debug("resolve for {mtype} to {recv}:{recv.mcasttype}(declared as {recv.mtype}) (in {self.reciever.to_s}:{self.reciever.mtype})") - var res = mtype.resolve_for(recv.mcasttype, self.frame.receiver, self.compiler.mainmodule, true) - return res - end - - fun native_array_def(pname: String, ret_type: nullable MType, arguments: Array[RuntimeVariable]) - do - var elttype = arguments.first.mtype - var recv = "((struct {arguments[0].mcasttype.c_name}*){arguments[0]})->values" - if pname == "[]" then - self.ret(self.new_expr("{recv}[{arguments[1]}]", ret_type.as(not null))) - return - else if pname == "[]=" then - self.add("{recv}[{arguments[1]}]={arguments[2]};") - 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}));") - return - end - end - - fun calloc_array(ret_type: MType, arguments: Array[RuntimeVariable]) - do - self.ret(self.new_expr("NEW_{ret_type.c_name}({arguments[1]})", ret_type)) - end - - # Add a check and an abort for a null reciever is needed - fun check_recv_notnull(recv: RuntimeVariable) - do - if self.compiler.modelbuilder.toolcontext.opt_no_check_other.value then return - - var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType - if maybenull then - self.add("if ({recv} == NULL) \{") - self.add_abort("Reciever is null") - self.add("\}") - end - end - - # Generate a polymorphic send for the method `m' and the arguments `args' - fun send(m: MMethod, args: Array[RuntimeVariable]): nullable RuntimeVariable - do - var types = self.collect_types(args.first) - - var res: nullable RuntimeVariable - var ret = m.intro.msignature.return_mtype - if m.is_new then - ret = args.first.mtype - res = self.new_var(ret) - else if ret == null then - res = null - else - ret = self.resolve_for(ret, args.first) - res = self.new_var(ret) - end - - self.add("/* send {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 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 res2 = self.call(propdef, mclasstype, args) - 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 == "!=" - 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 */") - if m.name == "==" then - assert res != null - if args[1].mcasttype isa MNullableType then - self.add("{res} = ({args[1]} == NULL);") - else if args[1].mcasttype isa MNullType then - self.add("{res} = 1; /* is null */") - else - self.add("{res} = 0; /* {args[1].inspect} cannot be null */") - end - else if m.name == "!=" then - assert res != null - if args[1].mcasttype isa MNullableType then - self.add("{res} = ({args[1]} != NULL);") - else if args[1].mcasttype isa MNullType then - self.add("{res} = 0; /* is null */") - else - self.add("{res} = 1; /* {args[1].inspect} cannot be null */") - end - else - self.add_abort("Reciever is null") - end - self.add "\} else" - 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 - 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 - if propdef.mclassdef.mclass.name == "Object" and t.ctype == "val*" then - defaultpropdef = propdef - continue - end - if not self.compiler.hardening and t == last and defaultpropdef == null then - self.add("default: /* test {t} */") - else - self.add("case {self.compiler.classid(t)}: /* test {t} */") - end - var res2 = self.call(propdef, t, args) - if res != null then self.assign(res, res2.as(not null)) - self.add "break;" - end - if defaultpropdef != null then - self.add("default: /* default is Object */") - var res2 = self.call(defaultpropdef, defaultpropdef.mclassdef.bound_mtype, args) - if res != null then self.assign(res, res2.as(not null)) - else if self.compiler.hardening then - self.add("default: /* bug */") - self.bugtype(args.first) - end - self.add("\}") - return res - end - - # Generate a monomorphic send for the method `m', the type `t' and the arguments `args' - fun monomorphic_send(m: MMethod, t: MType, args: Array[RuntimeVariable]): nullable RuntimeVariable - do - assert t isa MClassType - var propdefs = m.lookup_definitions(self.compiler.mainmodule, t) - if propdefs.length == 0 then - abort - 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 - return self.call(propdef, t, args) - end - - fun check_valid_reciever(recvtype: MClassType) - do - if self.compiler.runtime_type_analysis.live_types.has(recvtype) or recvtype.mclass.name == "Object" then return - print "{recvtype} is not a live type" - abort - end - - # Generate a static call on a method definition - fun call(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable - 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) - - args = args.to_a - self.varargize(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 - - args.first = recv - var rm = new CustomizedRuntimeFunction(m, recvtype) - return rm.call(self, args) - end - - fun adapt_signature(m: MMethodDef, args: Array[RuntimeVariable]) - 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) - args[i+1] = self.autobox(args[i+1], t) - end - end - - # Transform varargs, in raw arguments, into a single argument of type Array - # Note: this method modify the given `args` - # If there is no vararg, then `args` is not modified. - fun varargize(msignature: MSignature, args: Array[RuntimeVariable]) - do - var recv = args.first - var vararg_rank = msignature.vararg_rank - if vararg_rank >= 0 then - assert args.length >= msignature.arity + 1 # because of self - var rawargs = args - args = new Array[RuntimeVariable] - - args.add(rawargs.first) # recv - - for i in [0..vararg_rank[ do - args.add(rawargs[i+1]) - end - - var vararg_lastrank = vararg_rank + rawargs.length-1-msignature.arity - var vararg = new Array[RuntimeVariable] - for i in [vararg_rank..vararg_lastrank] do - vararg.add(rawargs[i+1]) - end - # FIXME: its it to late to determine the vararg type, this should have been done during a previous analysis - var elttype = msignature.mparameters[vararg_rank].mtype - elttype = self.resolve_for(elttype, recv) - args.add(self.array_instance(vararg, elttype)) - - for i in [vararg_lastrank+1..rawargs.length-1[ do - args.add(rawargs[i+1]) + self.add("/* send {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 - rawargs.clear - rawargs.add_all(args) - end - end - - 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);") - end - - # Generate a polymorphic attribute is_set test - fun isset_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable - do - check_recv_notnull(recv) - - var types = self.collect_types(recv) - - var res = self.new_var(bool_type) - - if types.is_empty then - self.add("/*BUG: no live types for {recv.inspect} . {a}*/") - self.bugtype(recv) + 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 end - self.add("/* isset {a} on {recv.inspect} */") - self.add("switch({recv}->classid) \{") - var last = types.last - for t in types do - if not self.compiler.hardening and t == last then - self.add("default: /*{self.compiler.classid(t)}*/") - else - self.add("case {self.compiler.classid(t)}:") - end - var recv2 = self.autoadapt(recv, t) - var ta = a.intro.static_mtype.as(not null) - ta = self.resolve_for(ta, recv2) - var attr = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta) - if not ta isa MNullableType then - if ta.ctype == "val*" then - self.add("{res} = ({attr} != NULL);") + var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_other.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 */") + if m.name == "==" then + assert res != null + if args[1].mcasttype isa MNullableType then + self.add("{res} = ({args[1]} == NULL);") + else if args[1].mcasttype isa MNullType then + self.add("{res} = 1; /* is null */") else - self.add("{res} = 1; /*NOTYET isset on primitive attributes*/") + self.add("{res} = 0; /* {args[1].inspect} cannot be null */") end - end - self.add("break;") - end - if self.compiler.hardening then - self.add("default: /* Bug */") - self.bugtype(recv) - end - self.add("\}") - - return res - end - - # Generate a polymorphic attribute read - fun read_attribute(a: MAttribute, recv: RuntimeVariable): RuntimeVariable - do - check_recv_notnull(recv) - - var types = self.collect_types(recv) - - var ret = a.intro.static_mtype.as(not null) - ret = self.resolve_for(ret, recv) - var res = self.new_var(ret) - - if types.is_empty then - self.add("/*BUG: no live types for {recv.inspect} . {a}*/") - self.bugtype(recv) - return res - end - self.add("/* read {a} on {recv.inspect} */") - self.add("switch({recv}->classid) \{") - var last = types.last - for t in types do - if not self.compiler.hardening and t == last then - self.add("default: /*{self.compiler.classid(t)}*/") - else - self.add("case {self.compiler.classid(t)}:") - end - var recv2 = self.autoadapt(recv, t) - 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 ta.ctype == "val*" then - self.add("if ({res2} == NULL) \{") - self.add_abort("Uninitialized attribute {a.name}") - self.add("\}") + else if m.name == "!=" then + assert res != null + if args[1].mcasttype isa MNullableType then + self.add("{res} = ({args[1]} != NULL);") + else if args[1].mcasttype isa MNullType then + self.add("{res} = 0; /* is null */") else - self.add("/*NOTYET isset on primitive attributes*/") + self.add("{res} = 1; /* {args[1].inspect} cannot be null */") end - end - self.assign(res, res2) - self.add("break;") - end - if self.compiler.hardening then - self.add("default: /* Bug */") - self.bugtype(recv) - end - self.add("\}") - - return res - end - - # Generate a polymorphic attribute write - fun write_attribute(a: MAttribute, recv: RuntimeVariable, value: RuntimeVariable) - do - check_recv_notnull(recv) - - var types = self.collect_types(recv) - - if types.is_empty then - self.add("/*BUG: no live types for {recv.inspect} . {a}*/") - self.bugtype(recv) - return - end - self.add("/* write {a} on {recv.inspect} */") - self.add("switch({recv}->classid) \{") - var last = types.last - for t in types do - if not self.compiler.hardening and t == last then - self.add("default: /*{self.compiler.classid(t)}*/") - else - self.add("case {self.compiler.classid(t)}:") - end - var recv2 = self.autoadapt(recv, t) - var ta = a.intro.static_mtype.as(not null) - ta = self.resolve_for(ta, recv2) - self.add("((struct {t.c_name}*){recv})->{a.intro.c_name} = {self.autobox(value, ta)};") - self.add("break;") - end - if self.compiler.hardening then - self.add("default: /* Bug*/") - self.bugtype(recv) - end - self.add("\}") - end - - # Generate a alloc-instance + init-attributes - fun init_instance(mtype: MClassType): RuntimeVariable - do - 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 - var res = self.new_expr("NEW_{mtype.c_name}()", mtype) - res.is_exact = true - return res - end - - # Generate a polymorphic subtype test - fun type_test(value: RuntimeVariable, mtype: MType): RuntimeVariable - 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 - debug "problem: {mtype} was detected cast-dead" - abort - end - - var types = self.collect_types(value) - - var res = self.new_var(bool_type) - - self.add("/* isa {mtype} on {value.inspect} */") - if value.mtype.ctype != "val*" then - if value.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then - self.add("{res} = 1;") - else - self.add("{res} = 0;") - end - return res - end - if value.mcasttype isa MNullableType or value.mcasttype isa MNullType then - self.add("if ({value} == NULL) \{") - if mtype isa MNullableType then - self.add("{res} = 1; /* isa {mtype} */") - else - self.add("{res} = 0; /* not isa {mtype} */") - end - self.add("\} else ") - end - self.add("switch({value}->classid) \{") - for t in types do - if t.is_subtype(self.compiler.mainmodule, null, mtype) then - self.add("case {self.compiler.classid(t)}: /* {t} */") - end - end - self.add("{res} = 1;") - self.add("break;") - self.add("default:") - self.add("{res} = 0;") - self.add("\}") - - return res - end - - # Generate the code required to dynamically check if 2 objects share the same runtime type - fun is_same_type_test(value1, value2: RuntimeVariable): RuntimeVariable - do - var res = self.new_var(bool_type) - if value2.mtype.ctype == "val*" then - if value1.mtype.ctype == "val*" then - self.add "{res} = {value1}->classid == {value2}->classid;" - else - self.add "{res} = {self.compiler.classid(value1.mtype.as(MClassType))} == {value2}->classid;" - end - else - if value1.mtype.ctype == "val*" then - self.add "{res} = {value1}->classid == {self.compiler.classid(value2.mtype.as(MClassType))};" - else if value1.mcasttype == value2.mcasttype then - self.add "{res} = 1;" else - self.add "{res} = 0;" + self.add_abort("Reciever is null") end + self.add "\} else" end - return res - end - - # Return a "const char*" variable associated to the classname of the dynamic type of an object - # NOTE: we do not return a RuntimeVariable "NativeString" as the class may not exist in the module/program - fun class_name_string(value: RuntimeVariable): String - do - var res = self.get_name("var_class_name") - self.add_decl("const char* {res};") - if value.mtype.ctype == "val*" then - self.add "{res} = class_names[{value}->classid];" - else - self.add "{res} = class_names[{self.compiler.classid(value.mtype.as(MClassType))}];" + 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 - return res - end - # Generate a Nit "is" for two runtime_variables - fun equal_test(value1, value2: RuntimeVariable): RuntimeVariable - do - var res = self.new_var(bool_type) - if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then - var tmp = value1 - value1 = value2 - value2 = tmp - end - if value1.mtype.ctype != "val*" then - if value2.mtype.ctype == value1.mtype.ctype then - self.add("{res} = {value1} == {value2};") - else if value2.mtype.ctype != "val*" then - self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/") - else - var mtype1 = value1.mtype.as(MClassType) - self.add("{res} = ({value2} != NULL) && ({value2}->classid == {self.compiler.classid(mtype1)});") - self.add("if ({res}) \{") - self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});") - self.add("\}") - end - else - var s = new Array[String] - for t in self.compiler.live_primitive_types do - if not t.is_subtype(self.compiler.mainmodule, null, value1.mcasttype) then continue - 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)" + self.add("switch({args.first}->classid) \{") + var last = types.last + var defaultpropdef: nullable MMethodDef = null + for t in types do + var propdef = m.lookup_first_definition(self.compiler.mainmodule, t) + if propdef.mclassdef.mclass.name == "Object" and t.ctype == "val*" then + defaultpropdef = propdef + continue end - if s.is_empty then - self.add("{res} = {value1} == {value2};") + if not self.compiler.hardening and t == last and defaultpropdef == null then + self.add("default: /* test {t} */") else - self.add("{res} = {value1} == {value2} || ({value1} != NULL && {value2} != NULL && {value1}->classid == {value2}->classid && ({s.join(" || ")}));") - end - end - return res - end - - # Generate a check-init-instance - fun check_init_instance(recv: RuntimeVariable) - do - if self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then return - - var mtype = self.anchor(recv.mcasttype) - for cd in mtype.collect_mclassdefs(self.compiler.mainmodule) - do - var n = self.compiler.modelbuilder.mclassdef2nclassdef[cd] - for npropdef in n.n_propdefs do - if npropdef isa AAttrPropdef then - # Force read to check the initialization - self.read_attribute(npropdef.mpropdef.mproperty, recv) - end + self.add("case {self.compiler.classid(t)}: /* test {t} */") end + var res2 = self.call(propdef, t, args) + if res != null then self.assign(res, res2.as(not null)) + self.add "break;" end - end - - # Generate an integer value - fun int_instance(value: Int): RuntimeVariable - do - var res = self.new_var(self.get_class("Int").mclass_type) - self.add("{res} = {value};") - return res - end - - # Generate an array value - fun array_instance(array: Array[RuntimeVariable], elttype: MType): RuntimeVariable - do - elttype = self.anchor(elttype) - var arraytype = self.get_class("Array").get_mtype([elttype]) - var res = self.init_instance(arraytype) - self.add("\{ /* {res} = array_instance Array[{elttype}] */") - var nat = self.new_var(self.get_class("NativeArray").get_mtype([elttype])) - nat.is_exact = true - self.add("{nat} = NEW_{nat.mtype.c_name}({array.length});") - for i in [0..array.length[ do - var r = self.autobox(array[i], elttype) - self.add("((struct {nat.mtype.c_name}*) {nat})->values[{i}] = {r};") + if defaultpropdef != null then + self.add("default: /* default is Object */") + var res2 = self.call(defaultpropdef, defaultpropdef.mclassdef.bound_mtype, args) + if res != null then self.assign(res, res2.as(not null)) + else if self.compiler.hardening then + self.add("default: /* bug */") + self.bugtype(args.first) end - var length = self.int_instance(array.length) - self.send(self.get_property("with_native", arraytype), [res, nat, length]) - self.check_init_instance(res) self.add("\}") return res end - # Generate a string value - fun string_instance(string: String): RuntimeVariable - do - var mtype = self.get_class("String").mclass_type - var name = self.get_name("varonce") - self.add_decl("static {mtype.ctype} {name};") - var res = self.new_var(mtype) - self.add("if ({name}) \{") - self.add("{res} = {name};") - self.add("\} else \{") - var nat = self.new_var(self.get_class("NativeString").mclass_type) - self.add("{nat} = \"{string.escape_to_c}\";") - var res2 = self.init_instance(mtype) - self.add("{res} = {res2};") - var length = self.int_instance(string.length) - self.send(self.get_property("with_native", mtype), [res, nat, length]) - self.check_init_instance(res) - self.add("{name} = {res};") - self.add("\}") - return res - end - - # Generate generic abort - # used by aborts, asserts, casts, etc. - fun add_abort(message: String) - do - if self.current_node != null and self.current_node.location.file != null then - self.add("fprintf(stderr, \"Runtime error: %s (%s:%d)\\n\", \"{message.escape_to_c}\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});") - else - self.add("fprintf(stderr, \"Runtime error: %s\\n\", \"{message.escape_to_c}\");") - end - self.add("exit(1);") - end -end - -# A frame correspond to a visited property in a GlobalCompilerVisitor -class Frame - # The associated visitor - - var visitor: GlobalCompilerVisitor - - # The executed property. - # A Method in case of a call, an attribute in case of a default initialization. - var mpropdef: MPropDef - - # The static type of the receiver - var receiver: MClassType - - # Arguments of the method (the first is the receiver) - var arguments: Array[RuntimeVariable] - - # The runtime_variable associated to the return (in a function) - var returnvar: nullable RuntimeVariable writable = null - - # The label at the end of the property - var returnlabel: nullable String writable = null -end - -redef class MPropDef - private var c_name_cache: nullable String - - # The mangled name associated to the property - fun c_name: String - do - var res = self.c_name_cache - if res != null then return res - res = "{self.mclassdef.mmodule.name.to_cmangle}__{self.mclassdef.mclass.name.to_cmangle}__{self.mproperty.name.to_cmangle}" - self.c_name_cache = res - return res - end -end - -redef class MMethodDef - # Can the body be inlined? - fun can_inline(v: GlobalCompilerVisitor): Bool - do - var modelbuilder = v.compiler.modelbuilder - if modelbuilder.mpropdef2npropdef.has_key(self) then - var npropdef = modelbuilder.mpropdef2npropdef[self] - return npropdef.can_inline - else if self.mproperty.name == "init" then - # Automatic free init is always inlined since it is empty or contains only attribtes assigments - return true - else - abort - end - end - - # Inline the body in another visitor - fun compile_inside_to_c(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]): nullable RuntimeVariable - do - var modelbuilder = v.compiler.modelbuilder - if modelbuilder.mpropdef2npropdef.has_key(self) then - var npropdef = modelbuilder.mpropdef2npropdef[self] - var oldnode = v.current_node - v.current_node = npropdef - self.compile_parameter_check(v, arguments) - npropdef.compile_to_c(v, self, arguments) - v.current_node = oldnode - else if self.mproperty.name == "init" then - var nclassdef = modelbuilder.mclassdef2nclassdef[self.mclassdef] - var oldnode = v.current_node - v.current_node = nclassdef - self.compile_parameter_check(v, arguments) - nclassdef.compile_to_c(v, self, arguments) - v.current_node = oldnode - else - abort - end - return null - end - - # Generate type checks in the C code to check covariant parameters - fun compile_parameter_check(v: GlobalCompilerVisitor, arguments: Array[RuntimeVariable]) - do - if v.compiler.modelbuilder.toolcontext.opt_no_check_covariance.value then return - - for i in [0..msignature.arity[ do - # skip test for vararg since the array is instantiated with the correct polymorphic type - if msignature.vararg_rank == i then continue - - # skip if the cast is not required - var origmtype = self.mproperty.intro.msignature.mparameters[i].mtype - if not origmtype.need_anchor then continue - - # get the parameter type - var mtype = self.msignature.mparameters[i].mtype - - # generate the cast - # note that v decides if and how to implements the cast - v.add("/* Covariant cast for argument {i} ({self.msignature.mparameters[i].name}) {arguments[i+1].inspect} isa {mtype} */") - var cond = v.type_test( arguments[i+1], mtype) - v.add("if (!{cond}) \{") - #var x = v.class_name_string(arguments[i+1]) - #var y = v.class_name_string(arguments.first) - #v.add("fprintf(stderr, \"expected {mtype} (self is %s), got %s for {arguments[i+1].inspect}\\n\", {y}, {x});") - v.add_abort("Cast failed") - v.add("\}") - end - end -end - -redef class APropdef - fun compile_to_c(v: GlobalCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) - do - v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");") - debug("Not yet implemented") - end - - fun can_inline: Bool do return true -end - -redef class AConcreteMethPropdef - redef fun compile_to_c(v, mpropdef, arguments) - do - for i in [0..mpropdef.msignature.arity[ do - var variable = self.n_signature.n_params[i].variable.as(not null) - v.assign(v.variable(variable), arguments[i+1]) - end - # Call the implicit super-init - var auto_super_inits = self.auto_super_inits - if auto_super_inits != null then - var selfarg = [arguments.first] - for auto_super_init in auto_super_inits do - if auto_super_init.intro.msignature.arity == 0 then - v.send(auto_super_init, selfarg) - else - v.send(auto_super_init, arguments) - end - end - end - v.stmt(self.n_block) - end - - redef fun can_inline + fun check_valid_reciever(recvtype: MClassType) do - if self.auto_super_inits != null then return false - var nblock = self.n_block - if nblock == null then return true - if (mpropdef.mproperty.name == "==" or mpropdef.mproperty.name == "!=") and mpropdef.mclassdef.mclass.name == "Object" then return true - if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true - return false + if self.compiler.runtime_type_analysis.live_types.has(recvtype) or recvtype.mclass.name == "Object" then return + print "{recvtype} is not a live type" + abort end -end -redef class AInternMethPropdef - redef fun compile_to_c(v, mpropdef, arguments) + redef fun call(m, recvtype, args) do - var pname = mpropdef.mproperty.name - var cname = mpropdef.mclassdef.mclass.name - var ret = mpropdef.msignature.return_mtype - if ret != null then - ret = v.resolve_for(ret, arguments.first) - end - if pname != "==" and pname != "!=" then - v.adapt_signature(mpropdef, arguments) - end - if cname == "Int" then - if pname == "output" then - v.add("printf(\"%ld\\n\", {arguments.first});") - return - else if pname == "object_id" then - v.ret(arguments.first) - return - else if pname == "+" then - v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) - return - else if pname == "-" then - v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) - return - else if pname == "unary -" then - v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) - return - else if pname == "succ" then - v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null))) - return - else if pname == "prec" then - v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null))) - return - else if pname == "*" then - v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) - return - else if pname == "/" then - v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) - return - else if pname == "%" then - v.ret(v.new_expr("{arguments[0]} % {arguments[1]}", ret.as(not null))) - return - else if pname == "lshift" then - v.ret(v.new_expr("{arguments[0]} << {arguments[1]}", ret.as(not null))) - return - else if pname == "rshift" then - v.ret(v.new_expr("{arguments[0]} >> {arguments[1]}", ret.as(not null))) - return - else if pname == "==" then - v.ret(v.equal_test(arguments[0], arguments[1])) - return - else if pname == "!=" then - var res = v.equal_test(arguments[0], arguments[1]) - v.ret(v.new_expr("!{res}", ret.as(not null))) - return - else if pname == "<" then - v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) - return - else if pname == ">" then - v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) - return - else if pname == "<=" then - v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) - return - else if pname == ">=" then - v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) - return - else if pname == "to_f" then - v.ret(v.new_expr("(double){arguments[0]}", ret.as(not null))) - return - else if pname == "ascii" then - v.ret(v.new_expr("{arguments[0]}", ret.as(not null))) - return - end - else if cname == "Char" then - if pname == "output" then - v.add("printf(\"%c\", {arguments.first});") - return - else if pname == "object_id" then - v.ret(arguments.first) - return - else if pname == "+" then - v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) - return - else if pname == "-" then - v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) - return - else if pname == "==" then - v.ret(v.equal_test(arguments[0], arguments[1])) - return - else if pname == "!=" then - var res = v.equal_test(arguments[0], arguments[1]) - v.ret(v.new_expr("!{res}", ret.as(not null))) - return - else if pname == "succ" then - v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null))) - return - else if pname == "prec" then - v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null))) - return - else if pname == "<" then - v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) - return - else if pname == ">" then - v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) - return - else if pname == "<=" then - v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) - return - else if pname == ">=" then - v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) - return - else if pname == "to_i" then - v.ret(v.new_expr("{arguments[0]}-'0'", ret.as(not null))) - return - else if pname == "ascii" then - v.ret(v.new_expr("(unsigned char){arguments[0]}", ret.as(not null))) - return - end - else if cname == "Bool" then - if pname == "output" then - v.add("printf({arguments.first}?\"true\\n\":\"false\\n\");") - return - else if pname == "object_id" then - v.ret(arguments.first) - return - else if pname == "==" then - v.ret(v.equal_test(arguments[0], arguments[1])) - return - else if pname == "!=" then - var res = v.equal_test(arguments[0], arguments[1]) - v.ret(v.new_expr("!{res}", ret.as(not null))) - return - end - else if cname == "Float" then - if pname == "output" then - v.add("printf(\"%f\\n\", {arguments.first});") - return - else if pname == "object_id" then - v.ret(v.new_expr("(double){arguments.first}", ret.as(not null))) - return - else if pname == "+" then - v.ret(v.new_expr("{arguments[0]} + {arguments[1]}", ret.as(not null))) - return - else if pname == "-" then - v.ret(v.new_expr("{arguments[0]} - {arguments[1]}", ret.as(not null))) - return - else if pname == "unary -" then - v.ret(v.new_expr("-{arguments[0]}", ret.as(not null))) - return - else if pname == "succ" then - v.ret(v.new_expr("{arguments[0]}+1", ret.as(not null))) - return - else if pname == "prec" then - v.ret(v.new_expr("{arguments[0]}-1", ret.as(not null))) - return - else if pname == "*" then - v.ret(v.new_expr("{arguments[0]} * {arguments[1]}", ret.as(not null))) - return - else if pname == "/" then - v.ret(v.new_expr("{arguments[0]} / {arguments[1]}", ret.as(not null))) - return - else if pname == "==" then - v.ret(v.equal_test(arguments[0], arguments[1])) - return - else if pname == "!=" then - var res = v.equal_test(arguments[0], arguments[1]) - v.ret(v.new_expr("!{res}", ret.as(not null))) - return - else if pname == "<" then - v.ret(v.new_expr("{arguments[0]} < {arguments[1]}", ret.as(not null))) - return - else if pname == ">" then - v.ret(v.new_expr("{arguments[0]} > {arguments[1]}", ret.as(not null))) - return - else if pname == "<=" then - v.ret(v.new_expr("{arguments[0]} <= {arguments[1]}", ret.as(not null))) - return - else if pname == ">=" then - v.ret(v.new_expr("{arguments[0]} >= {arguments[1]}", ret.as(not null))) - return - else if pname == "to_i" then - v.ret(v.new_expr("(long){arguments[0]}", ret.as(not null))) - return - end - else if cname == "Char" then - if pname == "output" then - v.add("printf(\"%c\", {arguments.first});") - return - else if pname == "object_id" then - v.ret(arguments.first) - return - else if pname == "==" then - v.ret(v.equal_test(arguments[0], arguments[1])) - return - else if pname == "!=" then - var res = v.equal_test(arguments[0], arguments[1]) - v.ret(v.new_expr("!{res}", ret.as(not null))) - return - else if pname == "ascii" then - v.ret(v.new_expr("{arguments[0]}", ret.as(not null))) - return - end - else if cname == "NativeString" then - if pname == "[]" then - v.ret(v.new_expr("{arguments[0]}[{arguments[1]}]", ret.as(not null))) - return - else if pname == "[]=" then - v.add("{arguments[0]}[{arguments[1]}]={arguments[2]};") - return - else if pname == "copy_to" then - v.add("memcpy({arguments[1]}+{arguments[4]},{arguments[0]}+{arguments[3]},{arguments[2]});") - return - else if pname == "atoi" then - v.ret(v.new_expr("atoi({arguments[0]});", ret.as(not null))) - return - end - else if cname == "NativeArray" then - v.native_array_def(pname, ret, arguments) - return - end - if pname == "exit" then - v.add("exit({arguments[1]});") - return - else if pname == "sys" then - v.ret(v.new_expr("glob_sys", ret.as(not null))) - return - else if pname == "calloc_string" then - v.ret(v.new_expr("(char*)GC_MALLOC({arguments[1]})", ret.as(not null))) - return - else if pname == "calloc_array" then - v.calloc_array(ret.as(not null), arguments) - return - else if pname == "object_id" then - v.ret(v.new_expr("(long){arguments.first}", ret.as(not null))) - return - else if pname == "is_same_type" then - v.ret(v.is_same_type_test(arguments[0], arguments[1])) - return - else if pname == "output_class_name" then - var nat = v.class_name_string(arguments.first) - v.add("printf(\"%s\\n\", {nat});") - return - else if pname == "native_class_name" then - var nat = v.class_name_string(arguments.first) - v.ret(v.new_expr("(char*){nat}", ret.as(not null))) - return - else if pname == "force_garbage_collection" then - v.add("GC_gcollect();") - return + 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 - v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");") - debug("Not implemented {mpropdef}") - end -end + var recv = self.autobox(args.first, recvtype) + recv = self.autoadapt(recv, recvtype) -redef class AExternMethPropdef - redef fun compile_to_c(v, mpropdef, arguments) - do - var externname - var nextern = self.n_extern - if nextern == null then - v.add("fprintf(stderr, \"NOT YET IMPLEMENTED nitni for {mpropdef} at {location.to_s}\\n\");") - v.add("exit(1);") - return - end - externname = nextern.text.substring(1, nextern.text.length-2) - if location.file != null then - var file = location.file.filename - v.compiler.add_extern(file) - end - var res: nullable RuntimeVariable = null - var ret = mpropdef.msignature.return_mtype - if ret != null then - ret = v.resolve_for(ret, arguments.first) - res = v.new_var(ret) + 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 - v.adapt_signature(mpropdef, arguments) - if res == null then - v.add("{externname}({arguments.join(", ")});") - else - v.add("{res} = {externname}({arguments.join(", ")});") - v.ret(res) - end + args.first = recv + var rm = new CustomizedRuntimeFunction(m, recvtype) + return rm.call(self, args) end -end -redef class AExternInitPropdef - redef fun compile_to_c(v, mpropdef, arguments) + redef fun supercall(m: MMethodDef, recvtype: MClassType, args: Array[RuntimeVariable]): nullable RuntimeVariable do - var externname - var nextern = self.n_extern - if nextern == null then - debug("{mpropdef} need extern name") - return - end - externname = nextern.text.substring(1, nextern.text.length-2) - if location.file != null then - var file = location.file.filename - v.compiler.add_extern(file) - end - v.adapt_signature(mpropdef, arguments) - var ret = arguments.first.mtype - var res = v.new_var(ret) - - arguments.shift - - v.add("{res} = {externname}({arguments.join(", ")});") - v.ret(res) - end -end + var types = self.collect_types(args.first) -redef class AAttrPropdef - redef fun compile_to_c(v, mpropdef, arguments) - do - if arguments.length == 1 then - var res = v.read_attribute(self.mpropdef.mproperty, arguments.first) - v.assign(v.frame.returnvar.as(not null), res) + var res: nullable RuntimeVariable + var ret = m.mproperty.intro.msignature.return_mtype + if ret == null then + res = null else - v.write_attribute(self.mpropdef.mproperty, arguments.first, arguments[1]) - end - end - - fun init_expr(v: GlobalCompilerVisitor, recv: RuntimeVariable) - do - var nexpr = self.n_expr - if nexpr != null then - var old_frame = v.frame - var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv]) - v.frame = frame - var value = v.expr(nexpr, self.mpropdef.static_mtype) - v.write_attribute(self.mpropdef.mproperty, recv, value) - v.frame = old_frame + ret = self.resolve_for(ret, args.first) + res = self.new_var(ret) end - end -end -redef class AClassdef - private fun compile_to_c(v: GlobalCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) - do - if mpropdef == self.mfree_init then - var super_inits = self.super_inits - if super_inits != null then - assert arguments.length == 1 - for su in super_inits do - v.send(su, arguments) - end - return - end - var recv = arguments.first - var i = 1 - # Collect undefined attributes - for npropdef in self.n_propdefs do - if npropdef isa AAttrPropdef and npropdef.n_expr == null then - v.write_attribute(npropdef.mpropdef.mproperty, recv, arguments[i]) - i += 1 - 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 - else - abort + var propdef = m.lookup_next_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 end - end -end - -redef class ADeferredMethPropdef - redef fun compile_to_c(v, mpropdef, arguments) - do - v.add_abort("Deferred method called") - end - - redef fun can_inline do return true -end -redef class AExpr - # Try to compile self as an expression - # Do not call this method directly, use `v.expr' instead - private fun expr(v: GlobalCompilerVisitor): nullable RuntimeVariable - do - v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");") - var mtype = self.mtype - if mtype == null then - return null - else - var res = v.new_var(mtype) - v.add("/* {res} = NOT YET {class_name} */") + 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 - end - - # Try to compile self as a statement - # Do not call this method directly, use `v.stmt' instead - private fun stmt(v: GlobalCompilerVisitor) - do - var res = expr(v) - if res != null then v.add("{res};") - end - -end -redef class ABlockExpr - redef fun stmt(v) - do - for e in self.n_expr do - v.stmt(e) + 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(propdef, t, args) + if res != null then self.assign(res, res2.as(not null)) + self.add "break;" end - end -end - -redef class AVardeclExpr - redef fun stmt(v) - do - var variable = self.variable.as(not null) - var ne = self.n_expr - if ne != null then - var i = v.expr(ne, variable.declared_type) - v.assign(v.variable(variable), i) + if self.compiler.hardening then + self.add("default: /* bug */") + self.bugtype(args.first) end + self.add("\}") + return res end -end - -redef class AVarExpr - redef fun expr(v) - do - var res = v.variable(self.variable.as(not null)) - var mtype = self.mtype.as(not null) - return v.autoadapt(res, mtype) - end -end -redef class AVarAssignExpr - redef fun stmt(v) + redef fun adapt_signature(m, args) do - var variable = self.variable.as(not null) - var i = v.expr(self.n_value, variable.declared_type) - v.assign(v.variable(variable), i) + 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) + args[i+1] = self.autobox(args[i+1], t) + end end -end -redef class AVarReassignExpr - redef fun stmt(v) + # FIXME: this is currently buggy since recv is not exact + redef fun vararg_instance(mpropdef, recv, varargs, elttype) do - var variable = self.variable.as(not null) - var vari = v.variable(variable) - var value = v.expr(self.n_value, variable.declared_type) - var res = v.send(reassign_property.mproperty, [vari, value]) - assert res != null - v.assign(v.variable(variable), res) + elttype = self.resolve_for(elttype, recv) + return self.array_instance(varargs, elttype) end -end -redef class ASelfExpr - redef fun expr(v) + fun bugtype(recv: RuntimeVariable) do - return v.frame.arguments.first + 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);") end -end -redef class AContinueExpr - redef fun stmt(v) + redef fun isset_attribute(a, recv) do - v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};") - end -end + check_recv_notnull(recv) -redef class ABreakExpr - redef fun stmt(v) - do - v.add("goto BREAK_{v.escapemark_name(self.escapemark)};") - end -end + var types = self.collect_types(recv) + var res = self.new_var(bool_type) -redef class AReturnExpr - redef fun stmt(v) - do - var nexpr = self.n_expr - if nexpr != null then - var returnvar = v.frame.returnvar.as(not null) - var i = v.expr(nexpr, returnvar.mtype) - v.assign(returnvar, i) + if types.is_empty then + self.add("/*BUG: no live types for {recv.inspect} . {a}*/") + self.bugtype(recv) + return res end - v.add("goto {v.frame.returnlabel.as(not null)};") - end -end - -redef class AAbortExpr - redef fun stmt(v) - do - v.add_abort("Aborted") - end -end - -redef class AIfExpr - redef fun stmt(v) - do - var cond = v.expr_bool(self.n_expr) - v.add("if ({cond})\{") - v.stmt(self.n_then) - v.add("\} else \{") - v.stmt(self.n_else) - v.add("\}") - end -end + self.add("/* isset {a} on {recv.inspect} */") + self.add("switch({recv}->classid) \{") + var last = types.last + for t in types do + if not self.compiler.hardening and t == last then + self.add("default: /*{self.compiler.classid(t)}*/") + else + self.add("case {self.compiler.classid(t)}:") + end + var recv2 = self.autoadapt(recv, t) + var ta = a.intro.static_mtype.as(not null) + ta = self.resolve_for(ta, recv2) + var attr = self.new_expr("((struct {t.c_name}*){recv})->{a.intro.c_name}", ta) + if not ta isa MNullableType then + if ta.ctype == "val*" then + self.add("{res} = ({attr} != NULL);") + else + self.add("{res} = 1; /*NOTYET isset on primitive attributes*/") + end + end + self.add("break;") + end + if self.compiler.hardening then + self.add("default: /* Bug */") + self.bugtype(recv) + end + self.add("\}") -redef class AIfexprExpr - redef fun expr(v) - do - var res = v.new_var(self.mtype.as(not null)) - var cond = v.expr_bool(self.n_expr) - v.add("if ({cond})\{") - v.assign(res, v.expr(self.n_then, null)) - v.add("\} else \{") - v.assign(res, v.expr(self.n_else, null)) - v.add("\}") return res end -end -redef class ADoExpr - redef fun stmt(v) + redef fun read_attribute(a, recv) do - v.stmt(self.n_block) - var escapemark = self.escapemark - if escapemark != null then - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") - end - end -end + check_recv_notnull(recv) -redef class AWhileExpr - redef fun stmt(v) - do - v.add("for(;;) \{") - var cond = v.expr_bool(self.n_expr) - v.add("if (!{cond}) break;") - v.stmt(self.n_block) - v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") - v.add("\}") - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") - end -end + var types = self.collect_types(recv) -redef class ALoopExpr - redef fun stmt(v) - do - v.add("for(;;) \{") - v.stmt(self.n_block) - v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") - v.add("\}") - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") - end -end + var ret = a.intro.static_mtype.as(not null) + ret = self.resolve_for(ret, recv) + var res = self.new_var(ret) -redef class AForExpr - redef fun stmt(v) - do - var cl = v.expr(self.n_expr, null) - var it_meth = self.method_iterator - assert it_meth != null - var it = v.send(it_meth, [cl]) - assert it != null - v.add("for(;;) \{") - var isok_meth = self.method_is_ok - assert isok_meth != null - var ok = v.send(isok_meth, [it]) - assert ok != null - v.add("if(!{ok}) break;") - if self.variables.length == 1 then - var item_meth = self.method_item - assert item_meth != null - var i = v.send(item_meth, [it]) - assert i != null - v.assign(v.variable(variables.first), i) - else if self.variables.length == 2 then - var key_meth = self.method_key - assert key_meth != null - var i = v.send(key_meth, [it]) - assert i != null - v.assign(v.variable(variables[0]), i) - var item_meth = self.method_item - assert item_meth != null - i = v.send(item_meth, [it]) - assert i != null - v.assign(v.variable(variables[1]), i) - else - abort + if types.is_empty then + self.add("/*BUG: no live types for {recv.inspect} . {a}*/") + self.bugtype(recv) + return res end - v.stmt(self.n_block) - v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") - var next_meth = self.method_next - assert next_meth != null - v.send(next_meth, [it]) - v.add("\}") - v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") - end -end - -redef class AAssertExpr - redef fun stmt(v) - do - if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return - - var cond = v.expr_bool(self.n_expr) - v.add("if (!{cond}) \{") - v.stmt(self.n_else) - var nid = self.n_id - if nid != null then - v.add_abort("Assert '{nid.text}' failed") - else - v.add_abort("Assert failed") + self.add("/* read {a} on {recv.inspect} */") + self.add("switch({recv}->classid) \{") + var last = types.last + for t in types do + if not self.compiler.hardening and t == last then + self.add("default: /*{self.compiler.classid(t)}*/") + else + self.add("case {self.compiler.classid(t)}:") + end + var recv2 = self.autoadapt(recv, t) + 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 ta.ctype == "val*" then + self.add("if ({res2} == NULL) \{") + self.add_abort("Uninitialized attribute {a.name}") + self.add("\}") + else + self.add("/*NOTYET isset on primitive attributes*/") + end + end + self.assign(res, res2) + self.add("break;") end - v.add("\}") - end -end - -redef class AOrExpr - redef fun expr(v) - do - var res = v.new_var(self.mtype.as(not null)) - var i1 = v.expr_bool(self.n_expr) - v.add("if ({i1}) \{") - v.add("{res} = 1;") - v.add("\} else \{") - var i2 = v.expr_bool(self.n_expr2) - v.add("{res} = {i2};") - v.add("\}") - return res - end -end + if self.compiler.hardening then + self.add("default: /* Bug */") + self.bugtype(recv) + end + self.add("\}") -redef class AAndExpr - redef fun expr(v) - do - var res = v.new_var(self.mtype.as(not null)) - var i1 = v.expr_bool(self.n_expr) - v.add("if (!{i1}) \{") - v.add("{res} = 0;") - v.add("\} else \{") - var i2 = v.expr_bool(self.n_expr2) - v.add("{res} = {i2};") - v.add("\}") return res end -end -redef class ANotExpr - redef fun expr(v) + redef fun write_attribute(a, recv, value) do - var cond = v.expr_bool(self.n_expr) - return v.new_expr("!{cond}", self.mtype.as(not null)) - end -end + check_recv_notnull(recv) -redef class AOrElseExpr - redef fun expr(v) - do - var res = v.new_var(self.mtype.as(not null)) - var i1 = v.expr(self.n_expr, null) - v.add("if ({i1}!=NULL) \{") - v.assign(res, i1) - v.add("\} else \{") - var i2 = v.expr(self.n_expr2, null) - v.assign(res, i2) - v.add("\}") - return res - end -end + var types = self.collect_types(recv) -redef class AEeExpr - redef fun expr(v) - do - var value1 = v.expr(self.n_expr, null) - var value2 = v.expr(self.n_expr2, null) - return v.equal_test(value1, value2) + if types.is_empty then + self.add("/*BUG: no live types for {recv.inspect} . {a}*/") + self.bugtype(recv) + return + end + self.add("/* write {a} on {recv.inspect} */") + self.add("switch({recv}->classid) \{") + var last = types.last + for t in types do + if not self.compiler.hardening and t == last then + self.add("default: /*{self.compiler.classid(t)}*/") + else + self.add("case {self.compiler.classid(t)}:") + end + var recv2 = self.autoadapt(recv, t) + var ta = a.intro.static_mtype.as(not null) + ta = self.resolve_for(ta, recv2) + self.add("((struct {t.c_name}*){recv})->{a.intro.c_name} = {self.autobox(value, ta)};") + self.add("break;") + end + if self.compiler.hardening then + self.add("default: /* Bug*/") + self.bugtype(recv) + end + self.add("\}") end -end -redef class AIntExpr - redef fun expr(v) + redef fun init_instance(mtype) do - return v.new_expr("{self.n_number.text}", self.mtype.as(not null)) + 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 + var res = self.new_expr("NEW_{mtype.c_name}()", mtype) + res.is_exact = true + return res end -end -redef class AFloatExpr - redef fun expr(v) + redef fun type_test(value, mtype, tag) do - return v.new_expr("{self.n_float.text}", self.mtype.as(not null)) - end -end + 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 + debug "problem: {mtype} was detected cast-dead" + abort + end -redef class ACharExpr - redef fun expr(v) - do - return v.new_expr("{self.n_char.text}", self.mtype.as(not null)) - end -end + var types = self.collect_types(value) + var res = self.new_var(bool_type) -redef class AArrayExpr - redef fun expr(v) - do - var mtype = self.mtype.as(MClassType).arguments.first - var array = new Array[RuntimeVariable] - for nexpr in self.n_exprs.n_exprs do - var i = v.expr(nexpr, mtype) - array.add(i) + self.add("/* isa {mtype} on {value.inspect} */") + if value.mtype.ctype != "val*" then + if value.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then + self.add("{res} = 1;") + else + self.add("{res} = 0;") + end + return res + end + if value.mcasttype isa MNullableType or value.mcasttype isa MNullType then + self.add("if ({value} == NULL) \{") + if mtype isa MNullableType then + self.add("{res} = 1; /* isa {mtype} */") + else + self.add("{res} = 0; /* not isa {mtype} */") + end + self.add("\} else ") + end + self.add("switch({value}->classid) \{") + for t in types do + if t.is_subtype(self.compiler.mainmodule, null, mtype) then + self.add("case {self.compiler.classid(t)}: /* {t} */") + end end - return v.array_instance(array, mtype) - end -end + self.add("{res} = 1;") + self.add("break;") + self.add("default:") + self.add("{res} = 0;") + self.add("\}") -redef class AStringFormExpr - redef fun expr(v) - do - return v.string_instance(self.value.as(not null)) + return res end -end -redef class ASuperstringExpr - redef fun expr(v) + redef fun is_same_type_test(value1, value2) do - var array = new Array[RuntimeVariable] - for ne in self.n_exprs do - if ne isa AStringFormExpr and ne.value == "" then continue # skip empty sub-strings - var i = v.expr(ne, null) - array.add(i) + var res = self.new_var(bool_type) + if value2.mtype.ctype == "val*" then + if value1.mtype.ctype == "val*" then + self.add "{res} = {value1}->classid == {value2}->classid;" + else + self.add "{res} = {self.compiler.classid(value1.mtype.as(MClassType))} == {value2}->classid;" + end + else + if value1.mtype.ctype == "val*" then + self.add "{res} = {value1}->classid == {self.compiler.classid(value2.mtype.as(MClassType))};" + else if value1.mcasttype == value2.mcasttype then + self.add "{res} = 1;" + else + self.add "{res} = 0;" + end end - var a = v.array_instance(array, v.object_type) - var res = v.send(v.get_property("to_s", a.mtype), [a]) return res end -end -redef class ACrangeExpr - redef fun expr(v) + redef fun class_name_string(value) do - var i1 = v.expr(self.n_expr, null) - var i2 = v.expr(self.n_expr2, null) - var res = v.init_instance(self.mtype.as(MClassType)) - var it = v.send(v.get_property("init", res.mtype), [res, i1, i2]) - v.check_init_instance(res) + var res = self.get_name("var_class_name") + self.add_decl("const char* {res};") + if value.mtype.ctype == "val*" then + self.add "{res} = class_names[{value}->classid];" + else + self.add "{res} = class_names[{self.compiler.classid(value.mtype.as(MClassType))}];" + end return res end -end -redef class AOrangeExpr - redef fun expr(v) + redef fun equal_test(value1, value2) do - var i1 = v.expr(self.n_expr, null) - var i2 = v.expr(self.n_expr2, null) - var res = v.init_instance(self.mtype.as(MClassType)) - var it = v.send(v.get_property("without_last", res.mtype), [res, i1, i2]) - v.check_init_instance(res) + var res = self.new_var(bool_type) + if value2.mtype.ctype != "val*" and value1.mtype.ctype == "val*" then + var tmp = value1 + value1 = value2 + value2 = tmp + end + if value1.mtype.ctype != "val*" then + if value2.mtype == value1.mtype then + self.add("{res} = {value1} == {value2};") + else if value2.mtype.ctype != "val*" then + self.add("{res} = 0; /* incompatible types {value1.mtype} vs. {value2.mtype}*/") + else + var mtype1 = value1.mtype.as(MClassType) + self.add("{res} = ({value2} != NULL) && ({value2}->classid == {self.compiler.classid(mtype1)});") + self.add("if ({res}) \{") + self.add("{res} = ({self.autobox(value2, value1.mtype)} == {value1});") + self.add("\}") + end + else + var s = new Array[String] + for t in self.compiler.live_primitive_types do + if not t.is_subtype(self.compiler.mainmodule, null, value1.mcasttype) then continue + 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 s.is_empty then + self.add("{res} = {value1} == {value2};") + else + self.add("{res} = {value1} == {value2} || ({value1} != NULL && {value2} != NULL && {value1}->classid == {value2}->classid && ({s.join(" || ")}));") + end + end return res end -end -redef class ATrueExpr - redef fun expr(v) + redef fun check_init_instance(recv, mtype) do - return v.new_expr("1", self.mtype.as(not null)) - end -end + if self.compiler.modelbuilder.toolcontext.opt_no_check_initialization.value then return -redef class AFalseExpr - redef fun expr(v) - do - return v.new_expr("0", self.mtype.as(not null)) - end -end + 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 -redef class ANullExpr - redef fun expr(v) - do - var res = v.new_expr("NULL", self.mtype.as(not null)) - return res + self.add("CHECK_NEW_{mtype.c_name}({recv});") end -end -redef class AIsaExpr - redef fun expr(v) + redef fun array_instance(array, elttype) do - var i = v.expr(self.n_expr, null) - return v.type_test(i, self.cast_type.as(not null)) + elttype = self.anchor(elttype) + var arraytype = self.get_class("Array").get_mtype([elttype]) + var res = self.init_instance(arraytype) + self.add("\{ /* {res} = array_instance Array[{elttype}] */") + var nat = self.new_var(self.get_class("NativeArray").get_mtype([elttype])) + nat.is_exact = true + self.add("{nat} = NEW_{nat.mtype.c_name}({array.length});") + for i in [0..array.length[ do + var r = self.autobox(array[i], elttype) + self.add("((struct {nat.mtype.c_name}*) {nat})->values[{i}] = {r};") + 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 end -redef class AAsCastExpr - redef fun expr(v) - do - var i = v.expr(self.n_expr, null) - if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i - - var cond = v.type_test(i, self.mtype.as(not null)) - v.add("if (!{cond}) \{") - v.add_abort("Cast failed") - v.add("\}") - return i - end -end +# A runtime function customized on a specific monomrph receiver type +private class CustomizedRuntimeFunction + super AbstractRuntimeFunction -redef class AAsNotnullExpr - redef fun expr(v) - do - var i = v.expr(self.n_expr, null) - if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i + redef type COMPILER: GlobalCompiler + redef type VISITOR: GlobalCompilerVisitor - v.add("if ({i} == NULL) \{") - v.add_abort("Cast failed") - v.add("\}") - return i - end -end + # The considered reciever + # (usually is a live type but no strong guarantee) + var recv: MClassType -redef class AParExpr - redef fun expr(v) + init(mmethoddef: MMethodDef, recv: MClassType) do - return v.expr(self.n_expr, null) + super(mmethoddef) + self.recv = recv end -end -redef class AOnceExpr - redef fun expr(v) + redef fun build_c_name do - var mtype = self.mtype.as(not null) - var name = v.get_name("varonce") - var guard = v.get_name(name + "_guard") - v.add_decl("static {mtype.ctype} {name};") - v.add_decl("static int {guard};") - var res = v.new_var(mtype) - v.add("if ({guard}) \{") - v.add("{res} = {name};") - v.add("\} else \{") - var i = v.expr(self.n_expr, mtype) - v.add("{res} = {i};") - v.add("{name} = {res};") - v.add("{guard} = 1;") - v.add("\}") + var res = self.c_name_cache + if res != null then return res + if self.mmethoddef.mclassdef.bound_mtype == self.recv then + res = self.mmethoddef.c_name + else + res = "{mmethoddef.c_name}__{recv.c_name}" + end + self.c_name_cache = res return res end -end -redef class ASendExpr - redef fun expr(v) + # used in the compiler worklist + redef fun ==(o) do - var recv = v.expr(self.n_expr, null) - var args = [recv] - for a in self.raw_arguments.as(not null) do - args.add(v.expr(a, null)) - end - var mproperty = self.mproperty.as(not null) - return v.send(mproperty, args) + if not o isa CustomizedRuntimeFunction then return false + if self.mmethoddef != o.mmethoddef then return false + if self.recv != o.recv then return false + return true end -end -redef class ASendReassignFormExpr - redef fun stmt(v) + # used in the compiler work-list + redef fun hash do return self.mmethoddef.hash + self.recv.hash + + redef fun to_s do - var recv = v.expr(self.n_expr, null) - var args = [recv] - for a in self.raw_arguments.as(not null) do - args.add(v.expr(a, null)) + if self.mmethoddef.mclassdef.bound_mtype == self.recv then + return self.mmethoddef.to_s + else + return "{self.mmethoddef}@{self.recv}" end - var value = v.expr(self.n_value, null) - - var mproperty = self.mproperty.as(not null) - var left = v.send(mproperty, args) - assert left != null - - var res = v.send(reassign_property.mproperty, [left, value]) - assert res != null - - args.add(res) - v.send(self.write_mproperty.as(not null), args) end -end -redef class ASuperExpr - redef fun expr(v) + # compile the code customized for the reciever + redef fun compile_to_c(compiler) do - var recv = v.frame.arguments.first - var args = [recv] - for a in self.n_args.n_exprs do - args.add(v.expr(a, null)) - end - if args.length == 1 then - args = v.frame.arguments - end - - var mproperty = self.mproperty - if mproperty != null then - if mproperty.intro.msignature.arity == 0 then - args = [recv] - end - # Super init call - var res = v.send(mproperty, args) - return res + var recv = self.recv + var mmethoddef = self.mmethoddef + if not recv.is_subtype(compiler.mainmodule, null, mmethoddef.mclassdef.bound_mtype) then + print("problem: why do we compile {self} for {recv}?") + abort end - # stantard call-next-method - var mpropdef = v.frame.mpropdef - # FIXME: we do not want an ugly static call! - var mpropdefs = mpropdef.mproperty.lookup_super_definitions(mpropdef.mclassdef.mmodule, mpropdef.mclassdef.bound_mtype) - if mpropdefs.length != 1 then - v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");") - debug("MPRODFEFS for super {mpropdef} for {recv}: {mpropdefs.join(", ")}") + var v = compiler.new_visitor + var selfvar = new RuntimeVariable("self", recv, recv) + if compiler.runtime_type_analysis.live_types.has(recv) then + selfvar.is_exact = true end - mpropdef = mpropdefs.first - assert mpropdef isa MMethodDef - var res = v.call(mpropdef, recv.mtype.as(MClassType), args) - return res - end -end + var arguments = new Array[RuntimeVariable] + var frame = new Frame(v, mmethoddef, recv, arguments) + v.frame = frame -redef class ANewExpr - redef fun expr(v) - do - var mproperty = self.mproperty.as(not null) - var mtype = self.mtype.as(MClassType) - var recv - var ctype = mtype.ctype - if ctype == "val*" then - recv = v.init_instance(mtype) - else if ctype == "void*" then - recv = v.new_expr("NULL/*special!*/", mtype) + var sig = new Buffer + var comment = new Buffer + var ret = mmethoddef.msignature.return_mtype + if ret != null then + ret = v.resolve_for(ret, selfvar) + sig.append("{ret.ctype} ") + else if mmethoddef.mproperty.is_new then + ret = recv + sig.append("{ret.ctype} ") else - debug("cannot new {mtype}") - abort + sig.append("void ") end - var args = [recv] - for a in self.n_args.n_exprs do - args.add(v.expr(a, null)) + sig.append(self.c_name) + sig.append("({recv.ctype} {selfvar}") + comment.append("(self: {recv}") + arguments.add(selfvar) + for i in [0..mmethoddef.msignature.arity[ do + var mtype = mmethoddef.msignature.mparameters[i].mtype + if i == mmethoddef.msignature.vararg_rank then + mtype = v.get_class("Array").get_mtype([mtype]) + end + mtype = v.resolve_for(mtype, selfvar) + comment.append(", {mtype}") + sig.append(", {mtype.ctype} p{i}") + var argvar = new RuntimeVariable("p{i}", mtype, mtype) + arguments.add(argvar) end - var res2 = v.send(mproperty, args) - if res2 != null then - #self.debug("got {res2} from {mproperty}. drop {recv}") - return res2 + sig.append(")") + comment.append(")") + if ret != null then + comment.append(": {ret}") end - v.check_init_instance(recv) - return recv - end -end - -redef class AAttrExpr - redef fun expr(v) - do - var recv = v.expr(self.n_expr, null) - var mproperty = self.mproperty.as(not null) - return v.read_attribute(mproperty, recv) - end -end + compiler.header.add_decl("{sig};") -redef class AAttrAssignExpr - redef fun stmt(v) - do - var recv = v.expr(self.n_expr, null) - var i = v.expr(self.n_value, null) - var mproperty = self.mproperty.as(not null) - v.write_attribute(mproperty, recv, i) - end -end + v.add_decl("/* method {self} for {comment} */") + v.add_decl("{sig} \{") + #v.add("printf(\"method {self} for {comment}\\n\");") + if ret != null then + frame.returnvar = v.new_var(ret) + end + frame.returnlabel = v.get_name("RET_LABEL") -redef class AAttrReassignExpr - redef fun stmt(v) - do - var recv = v.expr(self.n_expr, null) - var value = v.expr(self.n_value, null) - var mproperty = self.mproperty.as(not null) - var attr = v.read_attribute(mproperty, recv) - var res = v.send(reassign_property.mproperty, [attr, value]) - assert res != null - v.write_attribute(mproperty, recv, res) - end -end + mmethoddef.compile_inside_to_c(v, arguments) -redef class AIssetAttrExpr - redef fun expr(v) - do - var recv = v.expr(self.n_expr, null) - var mproperty = self.mproperty.as(not null) - return v.isset_attribute(mproperty, recv) + v.add("{frame.returnlabel.as(not null)}:;") + if ret != null then + v.add("return {frame.returnvar.as(not null)};") + end + v.add("\}") end -end -redef class ADebugTypeExpr - redef fun stmt(v) + redef fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable do - # do nothing + var ret = self.mmethoddef.msignature.return_mtype + if self.mmethoddef.mproperty.is_new then + ret = recv + end + if ret != null then + ret = v.resolve_for(ret, arguments.first) + end + if self.mmethoddef.can_inline(v) then + var frame = new Frame(v, self.mmethoddef, self.recv, arguments) + frame.returnlabel = v.get_name("RET_LABEL") + if ret != null then + frame.returnvar = v.new_var(ret) + end + var old_frame = v.frame + v.frame = frame + v.add("\{ /* Inline {self} ({arguments.join(",")}) */") + self.mmethoddef.compile_inside_to_c(v, arguments) + v.add("{frame.returnlabel.as(not null)}:(void)0;") + v.add("\}") + v.frame = old_frame + return frame.returnvar + end + v.adapt_signature(self.mmethoddef, arguments) + v.compiler.todo(self) + if ret == null then + v.add("{self.c_name}({arguments.join(",")});") + return null + else + var res = v.new_var(ret) + v.add("{res} = {self.c_name}({arguments.join(",")});") + return res + end end end