X-Git-Url: http://nitlanguage.org diff --git a/src/global_compiler.nit b/src/global_compiler.nit index e448436..f6e7a8a 100644 --- a/src/global_compiler.nit +++ b/src/global_compiler.nit @@ -35,10 +35,13 @@ redef class ToolContext # --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") + redef init do super - self.option_context.add_option(self.opt_output, self.opt_no_cc) + self.option_context.add_option(self.opt_output, self.opt_no_cc, self.opt_hardening) end end @@ -56,8 +59,8 @@ redef class ModelBuilder v.add_decl("#include ") # TODO: Better way to activate the GC - #v.add_decl("#include ") - v.add_decl("#define GC_MALLOC(x) calloc(1, (x))") + v.add_decl("#include ") + #v.add_decl("#define GC_MALLOC(x) calloc(1, (x))") # Declare structure for each live type @@ -175,13 +178,23 @@ redef class ModelBuilder 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) -I .nit_compile -I ../clib -c -o {o} {f}\n\n") + makefile.write("{o}: {f}\n\t$(CC) $(CFLAGS) -D NONITCNI -c -o {o} {f}\n\n") ofiles.add(o) end - - makefile.write("{outname}: {ofiles.join(" ")} {compiler.extern_bodies.join(" ")}\n\t$(CC) -Wl,--warn-unresolved-symbols $(CFLAGS) $(LDFLAGS) $(LDLIBS) -I .nit_compile -I ../clib -o {outname} {ofiles.join(" ")} {compiler.extern_bodies.join(" ")}\n\n") + # 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) $(LDLIBS) -o {outname} {ofiles.join(" ")}\n\n") + # Clean + makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n\n") makefile.close self.toolcontext.info("Generated makefile: {makename}", 2) @@ -222,6 +235,9 @@ private class GlobalCompiler # 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) do self.mainmodule = mainmodule @@ -313,6 +329,7 @@ private class GlobalCompiler v.add_decl("/* allocate {mtype} */") v.add_decl("{mtype.ctype} NEW_{mtype.c_name}(void) \{") var res = v.new_var(mtype) + res.is_exact = true v.add("{res} = GC_MALLOC(sizeof(struct {mtype.c_name}));") v.add("{res}->classid = {self.classid(mtype)};") @@ -350,7 +367,6 @@ private class GlobalCompiler if tryfile.file_exists then self.extern_bodies.add(tryfile) end - #(new OFStream.open("{file.basename("")}._nitni.h")).close end end @@ -584,9 +600,28 @@ private class RuntimeVariable # The current casted type of the variable (as known in Nit) var mcasttype: MType + # If the variable exaclty a mcasttype? + # false (usual value) means that the variable is a mcasttype or a subtype. + var is_exact: Bool = false + redef fun to_s do return name - redef fun inspect do return "<{inspect_head} {name}:{mtype}({mcasttype})>" + 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 # Visit the AST to generate the C code. @@ -788,6 +823,20 @@ private class GlobalCompilerVisitor 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 @@ -822,6 +871,12 @@ private class GlobalCompilerVisitor 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.collect_types_cache if cache.has_key(mtype) then return cache[mtype] @@ -864,14 +919,14 @@ private class GlobalCompilerVisitor end if types.is_empty then - self.add("/*BUG: no live types for {args.first.mtype} . {m}*/") + self.add("/*BUG: no live types for {args.first.inspect} . {m}*/") return res end - self.add("/* send {m} on {args.first}: {args.first.mcasttype} (declared {args.first.mtype}) */") + self.add("/* send {m} on {args.first.inspect} */") if args.first.mtype.ctype != "val*" then var propdefs = m.lookup_definitions(self.compiler.mainmodule, args.first.mtype) if propdefs.length == 0 then - self.add("/* skip {args.first.mcasttype}, no method {m} */") + self.add("/* skip, no method {m} */") return res end assert propdefs.length == 1 @@ -887,14 +942,14 @@ private class GlobalCompilerVisitor if args[1].mcasttype.ctype == "val*" then self.add("{res} = ({args[1]} == NULL);") else - self.add("{res} = 0; /* {args[1]}: {args[1].mcasttype} cannot be null */") + self.add("{res} = 0; /* {args[1].inspect} cannot be null */") end else if m.name == "!=" then assert res != null if args[1].mcasttype.ctype == "val*" then self.add("{res} = ({args[1]} != NULL);") else - self.add("{res} = 1; /* {args[1]}: {args[1].mcasttype} cannot be null */") + self.add("{res} = 1; /* {args[1].inspect} cannot be null */") end else self.add_abort("Reciever is null") @@ -902,6 +957,7 @@ private class GlobalCompilerVisitor self.add "\} else" 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) @@ -914,7 +970,11 @@ private class GlobalCompilerVisitor defaultpropdef = propdef continue end - self.add("case {self.compiler.classid(t)}: /* test {t} */") + 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;" @@ -923,7 +983,7 @@ private class GlobalCompilerVisitor 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 + else if self.compiler.hardening then self.add("default: /* bug */") self.bugtype(args.first) end @@ -966,7 +1026,7 @@ private class GlobalCompilerVisitor 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 = m.msignature.parameter_mtypes[vararg_rank] + var elttype = m.msignature.mparameters[vararg_rank].mtype elttype = self.resolve_for(elttype, recv) args.add(self.array_instance(vararg, elttype)) @@ -987,7 +1047,7 @@ private class GlobalCompilerVisitor do var recv = args.first for i in [0..m.msignature.arity[ do - var t = m.msignature.parameter_mtypes[i] + var t = m.msignature.mparameters[i].mtype if i == m.msignature.vararg_rank then t = args[i+1].mtype end @@ -1012,13 +1072,18 @@ private class GlobalCompilerVisitor var res = self.new_var(ret) if types.is_empty then - self.add("/*BUG: no live types for {recv.mtype} . {a}*/") + self.add("/*BUG: no live types for {recv.inspect} . {a}*/") return res end - self.add("/* read {a} on {recv.mcasttype} */") + self.add("/* read {a} on {recv.inspect} */") self.add("switch({recv}->classid) \{") + var last = types.last for t in types do - self.add("case {self.compiler.classid(t)}:") + 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) @@ -1035,8 +1100,10 @@ private class GlobalCompilerVisitor self.assign(res, res2) self.add("break;") end - self.add("default: /* Bug */") - self.bugtype(recv) + if self.compiler.hardening then + self.add("default: /* Bug */") + self.bugtype(recv) + end self.add("\}") return res @@ -1048,21 +1115,28 @@ private class GlobalCompilerVisitor var types = self.collect_types(recv) if types.is_empty then - self.add("/*BUG: no live types for {recv.mtype} . {a}*/") + self.add("/*BUG: no live types for {recv.inspect} . {a}*/") return end - self.add("/* write {a} on {recv.mcasttype} */") + self.add("/* write {a} on {recv.inspect} */") self.add("switch({recv}->classid) \{") + var last = types.last for t in types do - self.add("case {self.compiler.classid(t)}:") + 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 - self.add("default: /* Bug*/") - self.bugtype(recv) + if self.compiler.hardening then + self.add("default: /* Bug*/") + self.bugtype(recv) + end self.add("\}") end @@ -1074,6 +1148,7 @@ private class GlobalCompilerVisitor debug "problem: {mtype} was detected dead" end var res = self.new_expr("NEW_{mtype.c_name}()", mtype) + res.is_exact = true return res end @@ -1085,7 +1160,7 @@ private class GlobalCompilerVisitor var res = self.new_var(bool_type) - self.add("/* isa {mtype} on {value.mcasttype} */") + self.add("/* isa {mtype} on {value.inspect} */") if value.mcasttype isa MNullableType then self.add("if ({value} == NULL) \{") if mtype isa MNullableType then @@ -1168,6 +1243,7 @@ private class GlobalCompilerVisitor var res = self.init_instance(self.get_class("Array").get_mtype([elttype])) 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} = GC_MALLOC({array.length} * sizeof({elttype.ctype}));") for i in [0..array.length[ do var r = self.autobox(array[i], elttype) @@ -1183,12 +1259,22 @@ private class GlobalCompilerVisitor # 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 res = self.init_instance(self.get_class("String").mclass_type) + var res2 = self.init_instance(mtype) + self.add("{res} = {res2};") var length = self.int_instance(string.length) self.send(self.get_property("with_native", res.mtype), [res, nat, length]) self.check_init_instance(res) + self.add("{name} = {res};") + self.add("\}") return res end @@ -1197,9 +1283,9 @@ private class GlobalCompilerVisitor fun add_abort(message: String) do if self.current_node != null and self.current_node.location.file != null then - self.add("fprintf(stderr, \"%s (%s:%d)\\n\", \"{message.escape_to_c}\", \"{self.current_node.location.file.filename.escape_to_c}\", {current_node.location.line_start});") + 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, \"%s\\n\", \"{message.escape_to_c}\");") + self.add("fprintf(stderr, \"Runtime error: %s\\n\", \"{message.escape_to_c}\");") end self.add("exit(1);") end @@ -1251,7 +1337,8 @@ redef class MMethodDef var npropdef = modelbuilder.mpropdef2npropdef[self] return npropdef.can_inline else if self.mproperty.name == "init" then - return false + # Automatic free init is always inlined since it is empty or contains only attribtes assigments + return true else abort end @@ -1283,6 +1370,9 @@ redef class MMethodDef var v = new GlobalCompilerVisitor(compiler) 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, self, recv, arguments) v.frame = frame @@ -1307,7 +1397,7 @@ redef class MMethodDef comment.append("(self: {recv}") arguments.add(selfvar) for i in [0..self.msignature.arity[ do - var mtype = self.msignature.parameter_mtypes[i] + var mtype = self.msignature.mparameters[i].mtype if i == self.msignature.vararg_rank then mtype = v.get_class("Array").get_mtype([mtype]) end @@ -1353,7 +1443,7 @@ end redef class APropdef private fun compile_to_c(v: GlobalCompilerVisitor, mpropdef: MMethodDef, arguments: Array[RuntimeVariable]) do - v.add("printf(\"Not implemented {class_name} {mpropdef} at {location.to_s}\\n\");") + v.add("printf(\"NOT YET IMPLEMENTED {class_name} {mpropdef} at {location.to_s}\\n\");") debug("Not yet implemented") end @@ -1389,7 +1479,7 @@ redef class AConcreteMethPropdef 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 == 1 then return true + if nblock isa ABlockExpr and nblock.n_expr.length == 0 then return true return false end end @@ -1657,7 +1747,7 @@ redef class AInternMethPropdef end return end - v.add("printf(\"Not implemented {class_name}:{mpropdef} at {location.to_s}\\n\");") + v.add("printf(\"NOT IMPLEMENTED {class_name}:{mpropdef} at {location.to_s}\\n\");") debug("Not implemented {mpropdef}") end end @@ -1768,13 +1858,23 @@ redef class AClassdef end end +redef class ADeferredMethPropdef + redef fun compile_to_c(v, mpropdef, arguments) + do + v.add("printf(\"Not implemented {class_name} {mpropdef} at {location.to_s}\\n\");") + v.add("exit(1);") + 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 debug("Unimplemented expr {class_name}") - v.add("printf(\"Not implemented {class_name}:{location.to_s}\\n\");") + v.add("printf(\"NOT YET IMPLEMENTED {class_name}:{location.to_s}\\n\");") var mtype = self.mtype if mtype == null then return null @@ -1856,14 +1956,14 @@ end redef class AContinueExpr redef fun stmt(v) do - v.add("goto CONTINUE_{self.escapemark.object_id};") + v.add("goto CONTINUE_{v.escapemark_name(self.escapemark)};") end end redef class ABreakExpr redef fun stmt(v) do - v.add("goto BREAK_{self.escapemark.object_id};") + v.add("goto BREAK_{v.escapemark_name(self.escapemark)};") end end @@ -1919,7 +2019,7 @@ redef class ADoExpr v.stmt(self.n_block) var escapemark = self.escapemark if escapemark != null then - v.add("BREAK_{escapemark.object_id}: (void)0;") + v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") end end end @@ -1931,9 +2031,9 @@ redef class AWhileExpr var cond = v.expr_bool(self.n_expr) v.add("if (!{cond}) break;") v.stmt(self.n_block) - v.add("CONTINUE_{escapemark.object_id}: (void)0;") + v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") v.add("\}") - v.add("BREAK_{escapemark.object_id}: (void)0;") + v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") end end @@ -1942,9 +2042,9 @@ redef class ALoopExpr do v.add("for(;;) \{") v.stmt(self.n_block) - v.add("CONTINUE_{escapemark.object_id}: (void)0;") + v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") v.add("\}") - v.add("BREAK_{escapemark.object_id}: (void)0;") + v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") end end @@ -1958,14 +2058,25 @@ redef class AForExpr var ok = v.send(v.get_property("is_ok", it.mtype), [it]) assert ok != null v.add("if(!{ok}) break;") - var i = v.send(v.get_property("item", it.mtype), [it]) - assert i != null - v.assign(v.variable(variables.first), i) + if self.variables.length == 1 then + var i = v.send(v.get_property("item", it.mtype), [it]) + assert i != null + v.assign(v.variable(variables.first), i) + else if self.variables.length == 2 then + var i = v.send(v.get_property("key", it.mtype), [it]) + assert i != null + v.assign(v.variable(variables[0]), i) + i = v.send(v.get_property("item", it.mtype), [it]) + assert i != null + v.assign(v.variable(variables[1]), i) + else + abort + end v.stmt(self.n_block) - v.add("CONTINUE_{escapemark.object_id}: (void)0;") + v.add("CONTINUE_{v.escapemark_name(escapemark)}: (void)0;") v.send(v.get_property("next", it.mtype), [it]) v.add("\}") - v.add("BREAK_{escapemark.object_id}: (void)0;") + v.add("BREAK_{v.escapemark_name(escapemark)}: (void)0;") end end @@ -2093,6 +2204,7 @@ redef class ASuperstringExpr 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) end @@ -2212,7 +2324,7 @@ redef class ASendExpr do var recv = v.expr(self.n_expr, null) var args = [recv] - for a in compute_raw_arguments do + for a in self.raw_arguments.as(not null) do args.add(v.expr(a, null)) end var mproperty = self.mproperty.as(not null) @@ -2225,7 +2337,7 @@ redef class ASendReassignFormExpr do var recv = v.expr(self.n_expr, null) var args = [recv] - for a in compute_raw_arguments do + for a in self.raw_arguments.as(not null) do args.add(v.expr(a, null)) end var value = v.expr(self.n_value, null)