From: Jean Privat Date: Sun, 26 Jul 2009 00:32:21 +0000 (-0400) Subject: icode: introduce intermediate code representation X-Git-Tag: v0.3~136 X-Git-Url: http://nitlanguage.org icode: introduce intermediate code representation Missing parts are mainly: * identify bests ICode classes * keep casted variable static type in icode * remove MMSrc* classes dependency on AST Signed-off-by: Jean Privat --- diff --git a/src/analysis/allocate_iregister_slots.nit b/src/analysis/allocate_iregister_slots.nit new file mode 100644 index 0000000..3d22c30 --- /dev/null +++ b/src/analysis/allocate_iregister_slots.nit @@ -0,0 +1,294 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2009 Jean Privat +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# iregisters slot allocation +package allocate_iregister_slots + +import icode + +# The main class that performs the iregister slot allocation +# The algorithm is quite naive but works +# Things TODO: +# * flow control +# * register aliasing +# * IMove optimization +class IRegisterSlotAllocationVisitor +special ICodeVisitor + # The visitor works in two pass: + # First pass is used to detect first and last iregisters occurences and slot groups + # Second pass is used to assign an effective slot to iregsiters + var _pass: Int = 0 + + redef fun visit_iregister_read(ic: ICode, r: IRegister) + do + var pass = _pass + if pass == 0 then + if r._is_local and r._local_iroutine != _current_ir then + if r._local_iroutine == null then + r._local_iroutine = _current_ir + else + r._is_local = false + end + end + _lasts[r] = ic + r._slot_index = null + else if pass == 1 and _lasts.has_key(r) and _lasts[r] == ic then + free(r) + end + end + + redef fun visit_iregister_write(ic: ICode, r: IRegister) + do + var pass = _pass + if pass == 0 then + # Process locality + if r._is_local and r._local_iroutine != _current_ir then + if r._local_iroutine == null then + r._local_iroutine = _current_ir + else + r._is_local = false + end + end + r._slot_index = null + # The first write make it live + if not _firsts.has_key(r) then _firsts[r] = ic + # A read iregistre is stile live on a write + if _lasts.has_key(r) then _lasts[r] = ic + else if pass == 1 then + if _firsts[r] == ic then + register(r) + else if _lasts.has_key(r) and _lasts[r] == ic then + free(r) + end + end + end + + # Iregister from outside a loop/closure cannot die inside the loop/closure + # So, the only register that can die in loops/closure are those born inside the loop/closure + + # Registers born in the current loop/closure + # So they can die immediatly if needed + var _live: HashSet[IRegister] = new HashSet[IRegister] + + # Registers born outsde the current loop/closure that wanted to die + # Thez will be effectively freed at the end of the loop/closure + var _deferred: HashSet[IRegister] = new HashSet[IRegister] + + # Free the deferred that can be freed + # "Can" because of neested loops/closures + # new_def will be cleared and used as the new _deferred attribute + fun deferred_free(new_def: HashSet[IRegister]) + do + var old_def = _deferred + if not old_def.is_empty then + new_def.clear + _deferred = new_def + for r in old_def do + free(r) + end + end + end + + redef fun visit_icode(ic) + do + if _pass == 1 and ic isa ILoop then + var old_live = _live + var new_live = new HashSet[IRegister] + _live = new_live + super + _live = old_live + deferred_free(new_live) + else + super + end + end + + redef fun visit_closure_defs(closdefs) + do + if _pass == 1 then + var old_live = _live + var new_live = new HashSet[IRegister] + _live = new_live + super + _live = old_live + deferred_free(new_live) + else + super + end + end + + # The current iroutine + # Used to detect locality of iregisters + var _current_ir: IRoutine + + redef fun visit_iroutine(ir) + do + var res = ir.result + if _pass == 0 then + var old_ir = _current_ir + _current_ir = ir + for r in ir.params do + _firsts[r] = self + end + super + if res != null then + _lasts[res] = self + end + _current_ir = old_ir + else + var old_bool_slots = _bool_slots + _bool_slots = new SlotGroup + var old_std_slots = _std_slots + if ir isa IClosureDef then + _std_slots = new SlotGroup + end + for r in ir.params do + register(r) + end + super + if res != null then free(res) + ir._bool_slots_nb = _bool_slots._next_index + _bool_slots = old_bool_slots + ir._std_slots_nb = _std_slots._next_index + _std_slots = old_std_slots + end + end + + # Global slots are used for non local registers and some main iroutine registers + var _global_slots: SlotGroup = new SlotGroup + + # Standad slots are used for local generic registers + # In main iroutine, _global_slots == _std_slots + var _std_slots: SlotGroup + + # Bool slots are used to store local boolean registers + var _bool_slots: SlotGroup = new SlotGroup + + # Assign a slot to a register according to its locality and its type + fun register(r: IRegister) + do + if not _lasts.has_key(r) then return + assert r._slot_index == null + _live.add(r) + if not r._is_local then + _global_slots.register(r) + else if (r.stype.local_class.name == once ("Bool".to_symbol)) then + r._in_bool_slots = true + _bool_slots.register(r) + else + _std_slots.register(r) + end + end + + # Release a register, thus its slot can be reused + fun free(r: IRegister) + do + var i = r._slot_index + if i == null then return + if not _live.has(r) then + _deferred.add(r) + else if _lasts.has_key(r) then + if r._in_bool_slots then + _bool_slots.free(r) + else if r._is_local then + _std_slots.free(r) + else + _global_slots.free(r) + end + _lasts.remove_at(r) # Free a register only once + end + end + + # What is the first occurences of iregisters + # So that a slot is not needed before + var _firsts: HashMap[IRegister, Object] = new HashMap[IRegister, Object] + + # What is the last occurences of iregisters + # So that a slot may be no more needed after + # ("may" because of loops/closure) + var _lasts: HashMap[IRegister, Object] = new HashMap[IRegister, Object] + + # Run the slot allocation + fun iroutine_slot_allocation + do + var ir = _current_ir + visit_iroutine(ir) + _pass = 1 + visit_iroutine(ir) + end + + init(ir: IRoutine) + do + _current_ir = ir + _std_slots = _global_slots + end +end + +# Group or equivalent slots shared by registers +class SlotGroup + # The free slots in the group + var _free: List[Int] = new List[Int] + + # The next free slot + var _next_index: Int = 0 + + # Assign a slot to the register + fun register(r: IRegister) + do + if _free.is_empty then + r._slot_index = _next_index + _next_index += 1 + else + r._slot_index = _free.pop + end + end + + # Reuse the slot of the register + fun free(r: IRegister) + do + _free.push(r._slot_index.as(not null)) + end +end + +redef class IRoutine + # The number of standard slots (stored in an array) + readable var _std_slots_nb: Int = 0 + + # The number of bool slots + readable var _bool_slots_nb: Int = 0 + + fun allocate_iregister_slots + do + var v = new IRegisterSlotAllocationVisitor(self) + v.iroutine_slot_allocation + end +end + +redef class IRegister + # The slot index of the register in its slot group + # Is null if the iregister is dead + # (or if iregister slot allocation is not performed yet) + readable writable var _slot_index: nullable Int + + # Is the register local to a single iroutine? + readable writable var _is_local: Bool = true + + # If is_local, then what iroutine + readable writable var _local_iroutine: nullable IRoutine + + # Is the register stored in the bool slot groups? + readable writable var _in_bool_slots: Bool = false +end diff --git a/src/analysis/analysis.nit b/src/analysis/analysis.nit new file mode 100644 index 0000000..7d9b028 --- /dev/null +++ b/src/analysis/analysis.nit @@ -0,0 +1,32 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2009 Jean Privat +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Intermediate code analysis and optimizations +package analysis + +import icode +import icode_dump +import allocate_iregister_slots +import inline_methods + +redef class IRoutine + # Perfom all optimizations + fun optimize + do + inline_methods + allocate_iregister_slots + end +end diff --git a/src/analysis/icode_dump.nit b/src/analysis/icode_dump.nit new file mode 100644 index 0000000..3554816 --- /dev/null +++ b/src/analysis/icode_dump.nit @@ -0,0 +1,356 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2009 Jean Privat +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Output the intermediate code unded a textual representation +package icode_dump + +import icode +import allocate_iregister_slots + +redef class IRoutine + # Output the intermediate code representation of the iroutine + fun dump(icd: ICodeDumper) + do + if not params.is_empty then + var a = new Array[String] + for p in params do + a.add(icd.register(p)) + end + icd.write "Parameters: {a.join(", ")}" + end + var r = result + if r != null then + icd.write "Result: {icd.register(r)}" + end + var closdecls = closure_decls + if closdecls != null then + for c in closdecls do + c.dump(icd) + end + end + body.dump(icd) + end +end + +redef class IClosureDecl + # Output the intermediate code representation of the iclosuredecl + fun dump(icd: ICodeDumper) + do + icd.write "Closure: {icd.closdecl(self)}" + if default != null then + icd.indent + default.dump(icd) + icd.unindent + end + end +end + +class ICodeDumper + var _ids: HashMap[Object, String] = new HashMap[Object, String] + var _last_value: Int = 0 + + # Return the name of e + # If e is unknown, a new name is gived + fun register(e: IRegister): String + do + if _ids.has_key(e) then + return _ids[e] + else + var i = e.slot_index + if i == null then + _last_value += 1 + var s = "r{_last_value}" + _ids[e] = s + return s + else + _last_value += 1 + var s = "REG{i}(r{_last_value})" + _ids[e] = s + return s + end + end + end + + # Returns the names of es (separated with comma) + fun register_all(es: nullable Collection[IRegister]): String + do + if es == null then return "" + var a = new Array[String] + for e in es do + a.add(register(e)) + end + return a.join(", ") + end + + var _last_clos: Int = 0 + + # Return the name of e + # If e is unknown, a new name is gived + fun closdecl(e: IClosureDecl): String + do + if _ids.has_key(e) then + return _ids[e] + else + _last_clos += 1 + var s = "clos{_last_clos}" + _ids[e] = s + return s + end + end + + var _last_label: Int = 0 + # Return the name of e + # If e is unknown, a new name is gived + fun lab(e: ISeq): String + do + if _ids.has_key(e) then + return _ids[e] + else + _last_label += 1 + var s = "[l{_last_label}]" + _ids[e] = s + return s + end + end + + var _last_line: Int = 0 + # Return the line index of e + fun line(e: ICode): String + do + if _ids.has_key(e) then + return _ids[e] + else + _last_line += 1 + var s = "{_last_line}" + _ids[e] = s + return s + end + end + + # Is the label e known? (because we goto to it) + fun has_lab(e: ISeq): Bool + do + return _ids.has_key(e) + end + + # Output something + fun write(s: String) + do + for i in [0.._indent_level[ do + printn " " + end + print s + end + + var _indent_level: Int = 0 + + # Indent the next writes + fun indent do _indent_level += 1 + + # Outdent the next writes + fun unindent do _indent_level -= 1 +end + +redef class ICode + # Output the intermediate code representation + fun dump(icd: ICodeDumper) + do + var result = result + var s = "" + var l = location + if l != null then + s = " ... {l}" + end + if result == null then + icd.write "{icd.line(self)}: {dump_intern(icd)}{s}" + else + icd.write "{icd.line(self)}: {icd.register(result)} := {dump_intern(icd)}{s}" + end + end + + # Output the intermediate code representation (inner method) + fun dump_intern(icd: ICodeDumper): String do return "???" +end + +redef class ICodeN + redef fun dump(icd: ICodeDumper) + do + super + var closure_defs = closure_defs + if closure_defs != null then + for clos in closure_defs do + if clos == null then + icd.write "CLOSURE = NULL" + else + icd.write "CLOSURE" + icd.indent + clos.dump(icd) + icd.unindent + end + end + end + end +end + +redef class ISeq + redef fun dump(icd) + do + for ic in icodes do + ic.dump(icd) + end + if icd.has_lab(self) then icd.write("{icd.lab(self)}:") + end +end + +redef class IIf + redef fun dump(icd) + do + icd.write "IF({icd.register(expr)}) \{" + icd.indent + then_seq.dump(icd) + icd.unindent + icd.write "} ELSE \{" + icd.indent + else_seq.dump(icd) + icd.unindent + icd.write "}" + end +end + +redef class ILoop + redef fun dump(icd) + do + icd.write "LOOP \{" + icd.indent + for ic in icodes do + ic.dump(icd) + end + icd.unindent + icd.write "}" + if icd.has_lab(self) then icd.write("{icd.lab(self)}:") + end +end + +redef class IEscape + redef fun dump_intern(icd) + do + return "ESCAPE {icd.lab(seq)}" + end +end + +redef class IAbort + redef fun dump_intern(icd) + do + var pl = property_location + if pl != null then + return "ABORT (\"{texts.join("\", \"")}\") in {pl.full_name}" + else + return "ABORT (\"{texts.join("\", \"")}\")" + end + end +end + +redef class ICall + redef fun dump_intern(icd) + do + return "CALL {property.full_name}({icd.register_all(exprs)})" + end +end + +redef class IClosCall + redef fun dump_intern(icd) + do + return "CLOS_CALL {icd.closdecl(closure_decl)}({icd.register_all(exprs)})" + end +end + +redef class IAttrRead + redef fun dump_intern(icd) + do + return "ATTR_READ {property.full_name}({icd.register(expr)})" + end +end + +redef class IAttrWrite + redef fun dump_intern(icd) + do + return "ATTR_WRITE {property.full_name}({icd.register(expr1)}) := {icd.register(expr2)}" + end +end + +redef class IAttrIsset + redef fun dump_intern(icd) + do + return "ATTR_ISSET {property.full_name}({icd.register(expr)})" + end +end + +redef class ITypeCheck + redef fun dump_intern(icd) + do + return "CHECKTYPE {icd.register(expr)} isa {stype}" + end +end + +redef class INative + redef fun dump_intern(icd) + do + if exprs.is_empty then + return "NATIVE \"{code}\"" + else + return "NATIVE \"{code}\"({icd.register_all(exprs)})" + end + end +end + +redef class IMove + redef fun dump_intern(icd) + do + return "{icd.register(expr)}" + end +end + +redef class IIs + redef fun dump_intern(icd) + do + return "{icd.register(expr1)} is {icd.register(expr2)}" + end +end + +redef class INot + redef fun dump_intern(icd) + do + return "NOT {icd.register(expr)}" + end +end + +redef class IOnce + redef fun dump(icd) + do + icd.write "{icd.register(result.as(not null))} := ONCE \{" + icd.indent + body.dump(icd) + icd.unindent + icd.write "}" + end +end + +redef class IHasClos + redef fun dump_intern(icd) + do + return "HASCLOS {icd.closdecl(closure_decl)}" + end +end diff --git a/src/analysis/inline_methods.nit b/src/analysis/inline_methods.nit new file mode 100644 index 0000000..80f0aac --- /dev/null +++ b/src/analysis/inline_methods.nit @@ -0,0 +1,66 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2009 Jean Privat +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Detect inlinable methods and inline them +package inline_methods + +import icode + +private class InlineMethodVisitor +special ICodeVisitor + var _pass: Int = 0 + + redef fun visit_icode(ic) + do + if ic isa ICall then + var m = ic.property + if m.iroutine != null and ic.is_inlinable then + var ir = m.iroutine.as(not null) + var seq = new ISeq + current_icode.insert_before(seq) + var e = ir.inline_in_seq(seq, ic.exprs) + var r = ic.result + if r != null then + assert e != null + current_icode.insert_before(new IMove(r, e)) + end + current_icode.delete + visit_icode(seq) + end + end + super + end +end + +redef class ICall + fun is_inlinable: Bool + do + var m = property + var mn = m.name + var cn = m.local_class.name + return (m.is_intern and cn != once ("Object".to_symbol)) or + (cn == (once ("Array".to_symbol)) and (mn == (once ("length".to_symbol)) or mn == (once ("[]".to_symbol)))) or + (cn == (once ("AbstractArrayRead".to_symbol)) and (mn == (once ("length".to_symbol)) or mn == (once ("[]".to_symbol)))) + end +end + +redef class IRoutine + fun inline_methods + do + var v = new InlineMethodVisitor + v.visit_iroutine(self) + end +end diff --git a/src/compiling/compiling.nit b/src/compiling/compiling.nit index 883e388..8dfd072 100644 --- a/src/compiling/compiling.nit +++ b/src/compiling/compiling.nit @@ -18,9 +18,8 @@ package compiling import compiling_base -private import compiling_methods private import compiling_global -private import syntax +private import compiling_icode redef class MMModule # Compile the program diff --git a/src/compiling/compiling_base.nit b/src/compiling/compiling_base.nit index 0e3d6f0..ec63914 100644 --- a/src/compiling/compiling_base.nit +++ b/src/compiling/compiling_base.nit @@ -17,7 +17,7 @@ # Common things for NIT compilation and C generation package compiling_base -import syntax +import mmloader private import utils import primitive_info diff --git a/src/compiling/compiling_global.nit b/src/compiling/compiling_global.nit index 31bd86b..b66c57a 100644 --- a/src/compiling/compiling_global.nit +++ b/src/compiling/compiling_global.nit @@ -17,9 +17,7 @@ # Compute and generate tables for classes and modules. package compiling_global -#import compiling_base -private import compiling_methods -private import syntax +private import compiling_icode # Something that store color of table elements class ColorContext @@ -493,7 +491,7 @@ redef class MMModule if not c isa MMConcreteClass then continue for pg in c.global_properties do var p = c[pg] - if p.local_class == c then + if p.local_class == c and p isa MMMethod then p.compile_property_to_c(v) end if pg.is_init_for(c) then @@ -867,57 +865,57 @@ redef class MMLocalClass var pi = primitive_info if pi == null then - v.cfc = new CFunctionContext(v) - v.nmc = new NitMethodContext(null) - var s = "val_t NEW_{name}(void)" - v.add_instr(s + " \{") - v.indent - var ctx_old = v.ctx - v.ctx = new CContext - - var self_var = new ParamVariable(once ("self".to_symbol), null) - var self_var_cname = v.cfc.register_variable(self_var) - v.nmc.method_params = [self_var] - - v.add_instr("obj_t obj;") - v.add_instr("obj = alloc(sizeof(val_t) * {itab.length});") - v.add_instr("obj->vft = (classtable_elt_t*)VFT_{name};") - v.add_assignment(self_var_cname, "OBJ2VAL(obj)") + var iself = new IRegister(get_type) + var iselfa = [iself] + var iroutine = new IRoutine(new Array[IRegister], iself) + var icb = new ICodeBuilder(module, iroutine, null) + var obj = new INative("OBJ2VAL(obj)", null) + obj.result = iself + icb.stmt(obj) for g in global_properties do var p = self[g] var t = p.signature.return_type if p isa MMAttribute and t != null then + var ir = p.iroutine + if ir == null then continue # FIXME: Not compatible with sep compilation - assert p isa MMSrcAttribute - var np = p.node - var ne = np.n_expr - if ne != null then - var e = ne.compile_expr(v) - v.add_instr("{p.global.attr_access}(obj) = {e};") - end + var e = ir.inline_in_seq(icb.seq, iselfa).as(not null) + icb.stmt(new IAttrWrite(p, iself, e)) end end - v.add_instr("return OBJ2VAL(obj);") - v.cfc.generate_var_decls + + var cname = "NEW_{name}" + var args = iroutine.compile_signature_to_c(v, cname, "new {name}", null, null) + var ctx_old = v.ctx + v.ctx = new CContext + v.add_decl("obj_t obj;") + v.add_instr("obj = alloc(sizeof(val_t) * {itab.length});") + v.add_instr("obj->vft = (classtable_elt_t*)VFT_{name};") + var r = iroutine.compile_to_c(v, cname, args).as(not null) + v.add_instr("return {r};") ctx_old.append(v.ctx) v.ctx = ctx_old v.unindent v.add_instr("}") # Compile CHECKNAME - var s = "void CHECKNEW_{name}(val_t self, char *from)" - v.add_instr(s + " \{") - v.indent - var ctx_old = v.ctx - v.ctx = new CContext + var iself = new IRegister(get_type) + var iselfa = [iself] + var iroutine = new IRoutine(iselfa, null) + var icb = new ICodeBuilder(module, iroutine, null) for g in global_properties do var p = self[g] var t = p.signature.return_type if p isa MMAttribute and t != null and not t.is_nullable then - v.add_instr("if ({p.global.attr_access}(self) == NIT_NULL) \{ fprintf(stderr, \"Uninitialized attribute %s at %s.\\n\", \"{p.full_name}\", from); nit_exit(1); \}") + icb.add_attr_check(p, iself) end end + var cname = "CHECKNEW_{name}" + var args = iroutine.compile_signature_to_c(v, cname, "check new {name}", null, null) + var ctx_old = v.ctx + v.ctx = new CContext + iroutine.compile_to_c(v, cname, args) ctx_old.append(v.ctx) v.ctx = ctx_old v.unindent @@ -930,21 +928,32 @@ redef class MMLocalClass var p = self[g] # FIXME skip invisible constructors if not p.global.is_init_for(self) then continue - var params = new Array[String] - var args = ["self"] - for i in [0..p.signature.arity[ do - params.add("val_t p{i}") - args.add("p{i}") - end - args.add("init_table") - var s = "val_t NEW_{self}_{p.global.intro.cname}({params.join(", ")}) \{" - v.add_instr(s) - v.indent + assert p isa MMMethod + + var iself = new IRegister(get_type) + var iparams = new Array[IRegister] + for i in [0..p.signature.arity[ do iparams.add(new IRegister(p.signature[i])) + var iroutine = new IRoutine(iparams, iself) + iroutine.location = p.iroutine.location + var icb = new ICodeBuilder(module, iroutine, p) + + var inew = new INative("NEW_{name}()", null) + inew.result = iself + icb.stmt(inew) + var iargs = [iself] + iargs.add_all(iparams) + icb.stmt(new INative("{p.cname}(@@@{", @@@"*iparams.length}, init_table)", iargs)) + icb.stmt(new INative("CHECKNEW_{name}(@@@)", [iself])) + + var cname = "NEW_{self}_{p.global.intro.cname}" + var new_args = iroutine.compile_signature_to_c(v, cname, "new {self} {p.full_name}", null, null) + var ctx_old = v.ctx + v.ctx = new CContext v.add_instr(init_table_decl) - v.add_instr("val_t self = NEW_{name}();") - v.add_instr("{p.cname}({args.join(", ")});") - v.add_instr("CHECKNEW_{name}(self, \"{p.full_name} for {self}\");") - v.add_instr("return self;") + var e = iroutine.compile_to_c(v, cname, new_args).as(not null) + v.add_instr("return {e};") + ctx_old.append(v.ctx) + v.ctx = ctx_old v.unindent v.add_instr("}") end @@ -963,3 +972,44 @@ redef class MMLocalClass end end +redef class MMMethod + fun compile_property_to_c(v: CompilerVisitor) + do + var ir = iroutine + assert ir != null + + var more_params: nullable String = null + if global.is_init then more_params = "int* init_table" + var args = ir.compile_signature_to_c(v, cname, full_name, null, more_params) + var ctx_old = v.ctx + v.ctx = new CContext + + v.out_contexts.clear + + var itpos: nullable String = null + if global.is_init then + itpos = "itpos{v.new_number}" + v.add_decl("int {itpos} = VAL2OBJ({args.first})->vft[{local_class.global.init_table_pos_id}].i;") + v.add_instr("if (init_table[{itpos}]) return;") + end + + var s = ir.compile_to_c(v, cname, args) + + if itpos != null then + v.add_instr("init_table[{itpos}] = 1;") + end + if s == null then + v.add_instr("return;") + else + v.add_instr("return ", s, ";") + end + + ctx_old.append(v.ctx) + v.ctx = ctx_old + v.unindent + v.add_instr("}") + + for ctx in v.out_contexts do v.ctx.merge(ctx) + end +end + diff --git a/src/compiling/compiling_icode.nit b/src/compiling/compiling_icode.nit new file mode 100644 index 0000000..5f44ca1 --- /dev/null +++ b/src/compiling/compiling_icode.nit @@ -0,0 +1,803 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2009 Jean Privat +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generate C code from intermediate code representation +package compiling_icode + +import icode +private import analysis +import compiling_base + +# Compiler context from ICode to C +class I2CCompilerVisitor + # Associate things + var _ids: HashMap[Object, String] = new HashMap[Object, String] + # Associate other things + var _ids2: HashMap[Object, String] = new HashMap[Object, String] + + # Return the string associated with a register + fun register(e: IRegister): String + do + if e.stype isa MMTypeNone then return "NIT_NULL" + var ids = _ids + if closure and not e.is_local then ids = _ids2 + if ids.has_key(e) then + return ids[e] + else + var i = e.slot_index + if i == null then + # The register is dead + var s = "NIT_NULL" + ids[e] = s + return s + else + var s: String + var strs: HashMap[Int, String] + if e.in_bool_slots then + strs = once new HashMap[Int, String] + if not strs.has_key(i) then strs[i] = "REGB{i}" + else if closure and not e.is_local then + strs = once new HashMap[Int, String] + if not strs.has_key(i) then strs[i] = "closctx->variable[{i}]" + else + strs = once new HashMap[Int, String] + if not strs.has_key(i) then strs[i] = "REG[{i}]" + end + s = strs[i] + ids[e] = s + return s + end + end + end + + # Return the strings associated with registers + fun registers(a: Collection[IRegister]): Array[String] + do + var r = new Array[String].with_capacity(a.length) + for e in a do + r.add(register(e)) + end + return r + end + + var _last_number: Int = 0 + # Give a new unique number (unique for the visitor) + fun new_number: Int + do + _last_number += 1 + return _last_number + end + + # Return the string associated with a escape label + fun lab(e: ISeq): String + do + if _ids.has_key(e) then + return _ids[e] + else + var s = "label{new_number}" + _ids[e] = s + return s + end + end + + # The rank (number) of each closure + readable var _closures: HashMap[IClosureDecl, Int] = new HashMap[IClosureDecl, Int] + + # The functionnal type of each closure + readable var _clostypes: HashMap[IClosureDecl, String] = new HashMap[IClosureDecl, String] + + # label locally accessibles + readable writable var _local_labels: HashSet[ISeq] = new HashSet[ISeq] + + # Not local escaped labels + # The integer value is an index identifying the label + readable writable var _escaped_labels: HashMap[ISeq, Int] = new HashMap[ISeq, Int] + + # Register a escape to a non local label and return an index identifying the label + fun register_escape_label(e: ISeq): Int + do + if _escaped_labels.has_key(e) then + return _escaped_labels[e] + else + var res = _escaped_labels.length + 1 + _escaped_labels[e] = res + return res + end + end + + # Add a C label mark (if needed) + fun add_label(e: ISeq) + do + if _ids.has_key(e) then + add_instr("{_ids[e]}: while(0);") + end + end + + # Add a goto to a label (even outside a closure) + fun add_goto(seq: ISeq) + do + if local_labels.has(seq) then + add_instr("goto {lab(seq)};") + else + assert closure + var ind = register_escape_label(seq) + add_instr("closctx->has_broke = (val_t*){ind};") + add_instr("goto {lab(return_label.as(not null))};") + end + end + + # Are we in a closure ? + readable writable var _closure: Bool = false + + # The current compiler visitor + readable var _visitor: CompilerVisitor + + # The current compiled iroutine + readable var _iroutine: IRoutine + + # The return label of the current compiling C function + readable writable var _return_label: nullable ISeq = null + + fun add_decl(s: String) + do + visitor.add_decl(s) + end + + fun add_instr(s: String) + do + var l = _next_location + if l != null then + visitor.add_instr("/* ", l.file, ":", l.line_start.to_s, " */") + _next_location = null + end + visitor.add_instr(s) + end + + fun indent + do + visitor.indent + end + + fun unindent + do + visitor.unindent + end + + fun add_assignment(to, from: String) + do + visitor.add_assignment(to, from) + end + + var _last_location: nullable Location = null + var _next_location: nullable Location = null + + # Add location information in a comment + # Do nothing if the last location added is the same + fun add_location(l: nullable Location) + do + var last = _last_location + if last == l or l == null then return + _last_location = l + if last != null and last.file == l.file and last.line_start == l.line_start then + return + else + _next_location = l + end + end + + # The C fonction name of the iroutine + readable var _basecname: String + + init(v: CompilerVisitor, ir: IRoutine, cname: String) + do + _visitor = v + _iroutine = ir + _basecname = cname + end +end + +redef class IRoutine + # Declare and start a C function that match the routine + # Return what must be given to compile_inside_to_c or to compile_to_c + # After the method, an openinig { and and indent is added. + # So, do not forget to add a sub_context, to unintent and to add a closing } + fun compile_signature_to_c(v: CompilerVisitor, cname: String, human_name: nullable String, before_params, after_params: nullable String): Array[String] + do + var cargs = new Array[String] + var cparams = new Array[String] + if before_params != null then cparams.add(before_params) + for i in [0..params.length[ do + cargs.add("p{i}") + cparams.add("val_t p{i}") + end + if closure_decls != null then + for i in [0..closure_decls.length[ do + var closcn = "CLOS_{cname}_{i}" + var cs = closure_decls[i].closure.signature + var subparams = new Array[String] # Parameters of the closure + subparams.add("struct WBT_ *") + for j in [0..cs.arity[ do + subparams.add("val_t") + end + var rr = "void" + if cs.return_type != null then rr = "val_t" + v.add_decl("typedef {rr} (*{closcn})({subparams.join(", ")});") + cargs.add("wd{i}") + cparams.add("struct WBT_ *wd{i}") + end + end + if after_params != null then cparams.add(after_params) + var r = "void" + if result != null then r = "val_t" + var p: String + if cparams.is_empty then + p = "void" + else + p = cparams.join(", ") + end + if human_name != null then v.add_decl("#define LOCATE_", cname, " \"", human_name, "\"") + v.add_decl(r, " ", cname, "(", p, ");") + v.add_decl("typedef ", r, " (*", cname, "_t)(", p, ");") + v.add_instr(r, " ", cname, "(", p, ")\{") + v.indent + return cargs + end + + # Compile the body of the routine, return the result value is any + fun compile_inside_to_c(v: I2CCompilerVisitor, args: Array[String]): nullable String + do + # Add the trace + var ll = 0 + if location != null then + ll = location.line_start + end + v.add_decl("struct trace_t trace = \{NULL, NULL, {ll}, LOCATE_{v.basecname}\};") + v.add_instr("trace.prev = tracehead; tracehead = &trace;") + v.add_instr("trace.file = LOCATE_{v.visitor.module.name};") + + # Add local variables + if std_slots_nb == 0 then + v.add_decl("val_t *REG = NULL;") + else + v.add_decl("val_t REG[{std_slots_nb}];") + end + for i in [0..bool_slots_nb[ do + v.add_decl("val_t REGB{i};") + end + var iclosdecls = closure_decls + if iclosdecls != null then + v.add_decl("struct WBT_ *CREG[{iclosdecls.length}];") + else + v.add_decl("struct WBT_ **CREG = NULL;") + end + var i = 0 + for r in params do + if r.slot_index != null then v.add_assignment(v.register(r), args[i]) + i += 1 + end + var iclosdecls = closure_decls + if iclosdecls != null then + for i in [0..iclosdecls.length[ do + var iclosdecl = iclosdecls[i] + v.add_instr("CREG[{i}] = {args[params.length+i]};") + v.closures[iclosdecl] = i + var cs = iclosdecl.closure.signature # Closure signature + var subparams = new Array[String] # Parameters of the closure + subparams.add("struct WBT_ *") + for j in [0..cs.arity[ do + var p = "val_t" + subparams.add(p) + end + var r = "void" + if cs.return_type != null then r = "val_t" + v.clostypes[iclosdecl] = "{r} (*)({subparams.join(", ")})" + end + end + v.add_decl("val_t tmp;") + + # Prepare return + var old_rl = v.return_label + v.return_label = body + + # Compile body + body.compile_to_c(v) + + v.add_instr("tracehead = trace.prev;") + v.return_label = old_rl + var r = result + if r != null then + return v.register(r) + else + return null + end + end + + # Full compilation of the routine + # Including optimization and other stuff. + # cv must be in the correct function + fun compile_to_c(cv: CompilerVisitor, cname: String, args: Array[String]): nullable String + do + optimize + var v = new I2CCompilerVisitor(cv, self, cname) + return compile_inside_to_c(v, args) + end +end + +redef class ICode + # Full compilation of the icode + fun compile_to_c(v: I2CCompilerVisitor) + do + v.add_location(location) + store_result(v, inner_compile_to_c(v)) + end + + # Is a result really needed + private fun need_result: Bool + do + var r = result + return r != null and r.slot_index != null + end + + # Store s in the result value of self + private fun store_result(v: I2CCompilerVisitor, s: nullable String) + do + var r = result + if r != null and r.slot_index != null then + assert s != null + v.add_assignment(v.register(r), s) + else if s != null and not self isa IMove then + v.add_instr(s + ";") + end + end + + # Compilation of without the result assigment + # Return the right value is case of expression + # Return the full expression (witout ;) in case of statement + private fun inner_compile_to_c(v: I2CCompilerVisitor): nullable String is abstract +end + +redef class ISeq + redef fun inner_compile_to_c(v) + do + v.local_labels.add(self) + for ic in icodes do + ic.compile_to_c(v) + end + v.add_label(self) + return null + end +end + +redef class IIf + redef fun inner_compile_to_c(v) + do + v.add_instr("if (UNTAG_Bool({v.register(expr)})) \{") + if not then_seq.icodes.is_empty then + v.indent + then_seq.inner_compile_to_c(v) + v.unindent + end + if not else_seq.icodes.is_empty then + v.add_instr("} else \{") + v.indent + else_seq.inner_compile_to_c(v) + v.unindent + end + v.add_instr("}") + return null + end +end + +redef class ILoop + redef fun inner_compile_to_c(v) + do + v.local_labels.add(self) + v.add_instr("while(1) \{") + v.indent + for ic in icodes do + ic.compile_to_c(v) + end + v.unindent + v.add_instr("}") + v.add_label(self) + return null + end +end + +redef class IEscape + redef fun inner_compile_to_c(v) + do + v.add_goto(seq) + return null + end +end + +redef class IAbsCall + redef fun compile_to_c(v) + do + v.add_location(location) + var args = v.registers(exprs) + + # Compile closure definitions + var old_el = v.escaped_labels + var closdefs = closure_defs + var closcns: nullable Array[String] = null + if closdefs != null then + v.escaped_labels = new HashMap[ISeq, Int] + closcns = new Array[String] + for cd in closdefs do + if cd != null then + var cn = cd.compile_closure(v) + args.add(cn) + closcns.add(cn) + else + args.add("NULL") + end + end + end + + var s = compile_call_to_c(v, args) + var r: nullable String = s + + # Intercept escapes + if closcns != null then + var els = v.escaped_labels + v.escaped_labels = old_el + # Is there possible escapes? + if not els.is_empty then + # Call in a tmp variable to avoid 'break' overwrite + if need_result then + r = "tmp" + v.add_assignment(r, s) + else + r = null + v.add_instr(s + ";") + end + # What is the escape index (if any?) + # It's computed as the union of has_broke + var switcha = new Array[String] + for cn in closcns do + switcha.add("((int)({cn}->has_broke))") + end + var switch = switcha.join(" | ") + # What are the expected escape indexes + v.add_instr("switch ({switch}) \{") + v.indent + # No escape occured, continue as usual + v.add_instr("case 0: break;") + var lls = v.local_labels + var iels = els.iterator + var forward_escape = false + while iels.is_ok do + var seq = iels.key + if lls.has(seq) then + # Local escape occured + v.add_instr("case {iels.item}: goto {v.lab(seq)};") + else + # Forward escape occured: register the escape label + assert v.closure + v.register_escape_label(seq) + forward_escape = true + end + iels.next + end + # Forward escape occured, just pass to the next one + if forward_escape then + v.add_instr("default: closctx->has_broke = (val_t*)({switch}); goto {v.lab(v.return_label.as(not null))};") + end + v.unindent + v.add_instr("\}") + end + end + + store_result(v, r) + end + + redef fun inner_compile_to_c(v) do abort + + # The single invocation witout fancy stuffs + private fun compile_call_to_c(v: I2CCompilerVisitor, args: Array[String]): String is abstract +end + +redef class ICall + redef fun compile_call_to_c(v, args) + do + var prop = property + if prop.global.is_init then args.add("init_table") + if prop.name == (once ("add".to_symbol)) and prop.local_class.name == (once ("Array".to_symbol)) then + return "{prop.cname}({args.join(", ")})" + else + return "{prop.global.meth_call}({args[0]})({args.join(", ")})" + end + end +end + +redef class ISuper + redef fun compile_call_to_c(v, args) + do + var prop = property + if prop.global.is_init then args.add("init_table") + return "{prop.super_meth_call}({args[0]})({args.join(", ")})" + end +end + +redef class INew + redef fun compile_call_to_c(v, args) + do + return "NEW_{stype.local_class}_{property.global.intro.cname}({args.join(", ")})" + end +end + +redef class INative + redef fun inner_compile_to_c(v) + do + if exprs.is_empty then + return code + else + var res = new Buffer + var i = 0 + var c = code.split_with("@@@") + for s in c do + res.append(s) + if i < exprs.length and i < c.length-1 then + res.append(v.register(exprs[i])) + end + i += 1 + end + return res.to_s + end + end +end + +redef class IAbort + redef fun inner_compile_to_c(v) + do + var s = new Buffer.from("fprintf(stderr") + for t in texts do + s.append(", \"{t}\"") + end + s.append(");") + v.add_instr(s.to_s) + + var ll = location + var pl = property_location + s = new Buffer.from("fprintf(stderr, \"") + if pl != null then s.append(" in %s") + s.append(" (%s") + if ll != null then + s.append(":%d") + end + s.append(")\\n\", ") + if pl != null then s.append("LOCATE_{pl.cname}, ") + s.append("LOCATE_{module_location.name}") + if ll != null then + s.append(", {ll.line_start}") + end + s.append(");") + v.add_instr(s.to_s) + + v.add_instr("nit_exit(1);") + return null + end +end + +redef class IMove + redef fun inner_compile_to_c(v) + do + return v.register(expr) + end +end + +redef class IAttrRead + redef fun inner_compile_to_c(v) + do + return "{property.global.attr_access}({v.register(expr)})" + end +end + +redef class IAttrIsset + redef fun inner_compile_to_c(v) + do + return "TAG_Bool({property.global.attr_access}({v.register(expr)})!=NIT_NULL)" + end +end + +redef class IAttrWrite + redef fun inner_compile_to_c(v) + do + v.add_instr("{property.global.attr_access}({v.register(expr1)}) = {v.register(expr2)};") + return null + end +end + +redef class ITypeCheck + redef fun inner_compile_to_c(v) + do + # FIXME handle formaltypes + var g = stype.local_class.global + var recv = v.register(expr) + var s = "" + if expr.stype.is_nullable then + if stype.is_nullable then + s = "({recv}==NIT_NULL) || " + else if stype.as_nullable == expr.stype then + return "TAG_Bool({recv}!=NIT_NULL)" + else + s = "({recv}!=NIT_NULL) && " + end + end + return "TAG_Bool({s}VAL_ISA({recv}, {g.color_id}, {g.id_id})) /*cast {stype}*/" + end +end + +redef class IIs + redef fun inner_compile_to_c(v) + do + var t1 = expr1.stype + var t2 = expr2.stype + if t1 isa MMTypeNone then + if t2 isa MMTypeNone then + return "TAG_Bool(1)" + else if t2.is_nullable then + return "TAG_Bool({v.register(expr2)}==NIT_NULL)" + else + return "TAG_Bool(0)" + end + else if t1.is_nullable then + if t2 isa MMTypeNone then + return "TAG_Bool({v.register(expr1)}==NIT_NULL)" + else if t2.is_nullable then + return "TAG_Bool(IS_EQUAL_NN({v.register(expr1)},{v.register(expr2)}))" + else + return "TAG_Bool(IS_EQUAL_ON({v.register(expr2)},{v.register(expr1)}))" + end + else + if t2 isa MMTypeNone then + return "TAG_Bool(0)" + else if t2.is_nullable then + return "TAG_Bool(IS_EQUAL_ON({v.register(expr1)},{v.register(expr2)}))" + else + return "TAG_Bool(IS_EQUAL_OO({v.register(expr1)},{v.register(expr2)}))" + end + end + end +end + +redef class INot + redef fun inner_compile_to_c(v) + do + return "TAG_Bool(!UNTAG_Bool({v.register(expr)}))" + end +end + +redef class IOnce + redef fun inner_compile_to_c(v) + do + var i = v.new_number + var res = result.as(not null) + if res.stype.is_nullable then + v.add_decl("static val_t once_value_{i}; static int once_bool_{i}; /* Once value */") + v.add_instr("if (!once_bool_{i}) \{") + else + # Since the value is not nullable, we use the null value to represent the boolean + v.add_decl("static val_t once_value_{i}; /* Once value */") + v.add_instr("if (!once_value_{i}) \{") + end + v.indent + body.compile_to_c(v) + var e = v.register(result.as(not null)) + v.add_instr("once_value_{i} = {e};") + if res.stype.is_nullable then v.add_instr("once_bool_{i} = true;") + v.unindent + v.add_instr("} else {e} = once_value_{i};") + return e + end +end + +redef class IClosCall + redef fun compile_to_c(v: I2CCompilerVisitor) + do + v.add_location(location) + var ivar: String + if v.closure then + ivar = "closctx->closurevariable[{v.closures[closure_decl]}]" + else + ivar = "CREG[{v.closures[closure_decl]}]" + end + var args = [ivar] + args.append(v.registers(exprs)) + + var s = "(({v.clostypes[closure_decl]})({ivar}->fun))({args.join(", ")})" + store_result(v, s) + + v.add_instr("if ({ivar}->has_broke) \{") + v.indent + var bs = break_seq + if bs != null then + bs.compile_to_c(v) + end + v.add_goto(v.iroutine.body) + v.unindent + v.add_instr("\}") + end + + redef fun inner_compile_to_c(v) do abort +end + +redef class IHasClos + redef fun inner_compile_to_c(v) + do + var ivar: String + if v.closure then + ivar = "closctx->closurevariable[{v.closures[closure_decl]}]" + else + ivar = "CREG[{v.closures[closure_decl]}]" + end + return "TAG_Bool({ivar} != NULL)" + end +end + + +redef class IClosureDef + fun compile_closure(v: I2CCompilerVisitor): String + do + var cfc_old = v.closure + v.closure = true + var lls_old = v.local_labels + v.local_labels = new HashSet[ISeq] + + var cv = v.visitor + var ctx_old = cv.ctx + cv.ctx = new CContext + cv.out_contexts.add(cv.ctx) + + var cname = "OC_{v.basecname}_{v.new_number}" + var args = compile_signature_to_c(v.visitor, cname, null, "struct WBT_ *closctx", null) + var ctx_old2 = cv.ctx + cv.ctx = new CContext + + var s = compile_inside_to_c(v, args) + if s == null then + v.add_instr("return;") + else + v.add_instr("return {s};") + end + + ctx_old2.append(cv.ctx) + cv.ctx = ctx_old2 + v.unindent + v.add_instr("}") + cv.ctx = ctx_old + + v.closure = cfc_old + + # Build closure + var closcnv = "wbclos{v.new_number}" + v.add_decl("struct WBT_ {closcnv};") + v.add_instr("{closcnv}.fun = (fun_t){cname};") + v.add_instr("{closcnv}.has_broke = NULL;") + if cfc_old then + v.add_instr("{closcnv}.variable = closctx->variable;") + v.add_instr("{closcnv}.closurevariable = closctx->closurevariable;") + else + v.add_instr("{closcnv}.variable = REG;") + v.add_instr("{closcnv}.closurevariable = CREG;") + end + + v.local_labels = lls_old + return "(&{closcnv})" + end +end diff --git a/src/compiling/compiling_methods.nit b/src/compiling/compiling_methods.nit deleted file mode 100644 index f189355..0000000 --- a/src/compiling/compiling_methods.nit +++ /dev/null @@ -1,1837 +0,0 @@ -# This file is part of NIT ( http://www.nitlanguage.org ). -# -# Copyright 2008 Jean Privat -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Compile method bodies, statments and expressions to C. -package compiling_methods - -import compiling_base -private import syntax - -redef class CompilerVisitor - # Compile a statment node - fun compile_stmt(n: nullable PExpr) - do - if n == null then return - #add_instr("/* Compile stmt {n.locate} */") - n.prepare_compile_stmt(self) - var i = cfc._variable_index - n.compile_stmt(self) - cfc._variable_index = i - end - - # Compile is expression node - fun compile_expr(n: PExpr): String - do - #add_instr("/* Compile expr {n.locate} */") - var i = cfc._variable_index - var s = n.compile_expr(self) - cfc._variable_index = i - if s[0] == ' ' or cfc.is_valid_variable(s) then - return s - end - var v = cfc.get_var("Result") - add_assignment(v, s) - return v - end - - # Ensure that a c expression is a var - fun ensure_var(s: String, comment: String): String - do - if cfc.is_valid_variable(s) then - add_instr("/* Ensure var {s}: {comment}*/") - return s - end - var v = cfc.get_var(null) - add_assignment(v, "{s} /* Ensure var: {comment}*/") - return v - end - - readable writable var _cfc: nullable CFunctionContext - - readable writable var _nmc: nullable NitMethodContext - - # Generate an fprintf to display an error location - fun printf_locate_error(node: PNode): String - do - var s = new Buffer.from("fprintf(stderr, \"") - if nmc != null then s.append(" in %s") - s.append(" (%s:%d)\\n\", ") - if nmc != null then s.append("LOCATE_{nmc.method.cname}, ") - s.append("LOCATE_{module.name}, {node.location.line_start});") - return s.to_s - end - - fun invoke_super_init_calls_after(start_prop: nullable MMMethod) - do - var n = nmc.method.node - assert n isa AConcreteInitPropdef - - if n.super_init_calls.is_empty then return - var i = 0 - var j = 0 - #var s = "" - if start_prop != null then - while n.super_init_calls[i] != start_prop do - #s.append(" {n.super_init_calls[i]}") - i += 1 - end - i += 1 - #s.append(" {start_prop}") - - while n.explicit_super_init_calls[j] != start_prop do - j += 1 - end - j += 1 - end - var stop_prop: nullable MMMethod = null - if j < n.explicit_super_init_calls.length then - stop_prop = n.explicit_super_init_calls[j] - end - var l = n.super_init_calls.length - #s.append(" [") - while i < l do - var p = n.super_init_calls[i] - if p == stop_prop then break - var cargs = new Array[String] - if p.signature.arity == 0 then - cargs.add(cfc.varname(nmc.method_params[0])) - else - for va in nmc.method_params.as(not null) do - cargs.add(cfc.varname(va)) - end - end - #s.append(" {p}") - p.compile_stmt_call(self, cargs) - i += 1 - end - #s.append(" ]") - #while i < l do - # s.append(" {n.super_init_calls[i]}") - # i += 1 - #end - #if stop_prop != null then s.append(" (stop at {stop_prop})") - #n.printl("implicit calls in {n.method}: {s}") - end -end - -# A C function currently written -class CFunctionContext - readable var _visitor: CompilerVisitor - - # Next available variable number - var _variable_index: Int = 0 - - # Total number of variable - var _variable_index_max: Int = 0 - - # Association between nit variable and the corrsponding c variable index - var _varindexes: Map[Variable, Int] = new HashMap[Variable, Int] - - # Are we currenlty in a closure definition? - readable writable var _closure: nullable NitMethodContext = null - - # Return the cvariable of a Nit variable - fun varname(v: Variable): String - do - if v isa ClosureVariable then - return closure_variable(_varindexes[v]) - else - return variable(_varindexes[v]) - end - end - - # Return the next available variable - fun get_var(comment: nullable String): String - do - var v = variable(_variable_index) - _variable_index = _variable_index + 1 - if _variable_index > _variable_index_max then - _variable_index_max = _variable_index - end - if comment != null then - visitor.add_instr("/* Register {v}: {comment} */") - end - return v - end - - fun register_variable(v: Variable): String - do - _varindexes[v] = _variable_index - var s = get_var("Local variable") - return s - end - - # Next available closure variable number - var _closurevariable_index: Int = 0 - - fun register_closurevariable(v: ClosureVariable): String - do - var s = "closurevariable[{_closurevariable_index}]" - _varindexes[v] = _closurevariable_index - _closurevariable_index += 1 - if _closure != null then - return "(closctx->{s})" - else - return s - end - end - - # Return the ith cvariable - protected fun variable(i: Int): String - do - if closure != null then - var vn = once new Array[String] - if vn.length <= i then - for j in [vn.length..i] do - vn[j] = "(closctx->variable[{j}])" - end - end - return vn[i] - else - var vn = once new Array[String] - if vn.length <= i then - for j in [vn.length..i] do - vn[j] = "variable[{j}]" - end - end - return vn[i] - end - end - - # Return the ith closurevariable - protected fun closure_variable(i: Int): String - do - if closure != null then - return "(closctx->closurevariable[{i}])" - else - return "closurevariable[{i}]" - end - end - - # Is s a valid variable - protected fun is_valid_variable(s: String): Bool - do - for i in [0.._variable_index[ do - if s == variable(i) then return true - end - return false - end - - # Mark the variable available - fun free_var(v: String) - do - # FIXME: So ugly.. - if v == variable(_variable_index-1) then - _variable_index = _variable_index - 1 - end - end - - # Generate the local variable declarations - # To use at the end of the C function once all variables are known - fun generate_var_decls - do - if _variable_index_max > 0 then - visitor.add_decl("val_t variable[{_variable_index_max}];") - else - visitor.add_decl("val_t *variable = NULL;") - end - if _closurevariable_index > 0 then - visitor.add_decl("struct WBT_ *closurevariable[{_closurevariable_index}];") - else - visitor.add_decl("struct WBT_ **closurevariable = NULL;") - end - end - - init(v: CompilerVisitor) do _visitor = v -end - -# A Nit method currenlty compiled -class NitMethodContext - # Current method compiled - readable var _method: nullable MMMethod - - # Association between parameters and the corresponding variables - readable writable var _method_params: nullable Array[ParamVariable] - - # Where a nit return must branch - readable writable var _return_label: nullable String - - # Where a nit break must branch - readable writable var _break_label: nullable String - - # Where a nit continue must branch - readable writable var _continue_label: nullable String - - # Variable where a functionnal nit return must store its value - readable writable var _return_value: nullable String - - # Variable where a functionnal nit break must store its value - readable writable var _break_value: nullable String - - # Variable where a functionnal nit continue must store its value - readable writable var _continue_value: nullable String - - init(method: nullable MMMethod) - do - _method = method - end -end - -############################################################################### - -redef class ClosureVariable - readable writable var _ctypename: nullable String -end - -redef class MMAttribute - # Compile a read acces on selffor a given reciever. - fun compile_isset(v: CompilerVisitor, n: PNode, recv: String): String - do - return "TAG_Bool({global.attr_access}({recv})!=NIT_NULL) /* isset {local_class}::{name}*/" - end - - # Compile a read acces on selffor a given reciever. - fun compile_read_access(v: CompilerVisitor, n: PNode, recv: String): String - do - var res = "{global.attr_access}({recv}) /*{local_class}::{name}*/" - if not signature.return_type.is_nullable then - res = v.ensure_var(res, "{local_class}::{name}") - v.add_instr("if ({res} == NIT_NULL) \{ fprintf(stderr, \"Uninitialized attribute %s\", \"{name}\"); {v.printf_locate_error(n)} nit_exit(1); } /* implicit isset */;") - end - return res - end - - # Compile a write acces on selffor a given reciever. - fun compile_write_access(v: CompilerVisitor, n: nullable PNode, recv: String, value: String) - do - v.add_instr("{global.attr_access}({recv}) /*{local_class}::{name}*/ = {value};") - end -end - -redef class MMLocalProperty - # Compile the property as a C property - fun compile_property_to_c(v: CompilerVisitor) do end -end - -redef class MMMethod - # Compile as an expression. - # require that signature.return_type != null - fun compile_expr_call(v: CompilerVisitor, cargs: Array[String]): String - do - assert signature.return_type != null - var s = intern_compile_call(v, cargs) - assert s != null - return s - end - - # Compile as a statement. - # require that signature.return_type == null - fun compile_stmt_call(v: CompilerVisitor, cargs: Array[String]) - do - assert signature.return_type == null - var s = intern_compile_call(v, cargs) - assert s == null - end - - # Compile a call on self for given arguments - # Most calls are compiled with a table access, - # primitive calles are inlined - # == and != are guarded and possibly inlined - private fun intern_compile_call(v: CompilerVisitor, cargs: Array[String]): nullable String - do - var i = self - if i isa MMSrcMethod then - if i isa MMMethSrcMethod and i.node isa AInternMethPropdef or - (i.local_class.name == (once "Array".to_symbol) and name == (once "[]".to_symbol)) - then - var e = i.do_compile_inside(v, cargs) - return e - end - end - var ee = once "==".to_symbol - var ne = once "!=".to_symbol - if name == ne then - var eqp = signature.recv.local_class.select_method(ee) - var eqcall = eqp.compile_expr_call(v, cargs) - return "TAG_Bool(!UNTAG_Bool({eqcall}))" - end - if global.is_init then - cargs = cargs.to_a - cargs.add("init_table /*YYY*/") - end - - var m = "{global.meth_call}({cargs[0]})" - var vcall = "{m}({cargs.join(", ")}) /*{local_class}::{name}*/" - if name == ee then - vcall = "UNTAG_Bool({vcall})" - var obj = once "Object".to_symbol - if i.local_class.name == obj then - vcall = "(({m}=={i.cname})?(IS_EQUAL_NN({cargs[0]},{cargs[1]})):({vcall}))" - end - vcall = "TAG_Bool(({cargs.first} == {cargs[1]}) || (({cargs.first} != NIT_NULL) && {vcall}))" - end - if signature.return_type != null then - return vcall - else - v.add_instr(vcall + ";") - return null - end - end - - # Compile a call on self for given arguments and given closures - fun compile_call_and_closures(v: CompilerVisitor, cargs: Array[String], clos_defs: nullable Array[PClosureDef]): nullable String - do - var ve: String - var arity = 0 - if clos_defs != null then arity = clos_defs.length - - # Prepare result value. - # In case of procedure, the return value is still used to intercept breaks - var old_bv = v.nmc.break_value - ve = v.cfc.get_var("Closure return value and escape marker") - v.nmc.break_value = ve - - # Compile closure to c function - var realcargs = new Array[String] # Args to pass to the C function call - var closcns = new Array[String] # Closure C structure names - realcargs.add_all(cargs) - for i in [0..arity[ do - var cn = clos_defs[i].compile_closure(v, closure_cname(i)) - closcns.add(cn) - realcargs.add(cn) - end - for i in [arity..signature.closures.length[ do - realcargs.add("NULL") - end - - v.nmc.break_value = old_bv - - # Call - var e = intern_compile_call(v, realcargs) - if e != null then - v.add_assignment(ve, e) - e = ve - end - - # Intercept returns and breaks - for i in [0..arity[ do - # A break or a return is intercepted - v.add_instr("if ({closcns[i]}->has_broke != NULL) \{") - v.indent - # A passtrought break or a return is intercepted: go the the next closure - v.add_instr("if ({closcns[i]}->has_broke != &({ve})) \{") - v.indent - if v.cfc.closure == v.nmc then v.add_instr("closctx->has_broke = {closcns[i]}->has_broke; closctx->broke_value = {closcns[i]}->broke_value;") - v.add_instr("goto {v.nmc.return_label};") - v.unindent - # A direct break is interpected - if e != null then - # overwrite the returned value in a function - v.add_instr("\} else {ve} = {closcns[i]}->broke_value;") - else - # Do nothing in a procedure - v.add_instr("\}") - end - v.unindent - v.add_instr("\}") - end - return e - end - - # Compile a call as constructor with given args - fun compile_constructor_call(v: CompilerVisitor, recvtype: MMType, cargs: Array[String]): String - do - return "NEW_{recvtype.local_class}_{global.intro.cname}({cargs.join(", ")}) /*new {recvtype}*/" - end - - # Compile a call as call-next-method on self with given args - fun compile_super_call(v: CompilerVisitor, cargs: Array[String]): String - do - if global.is_init then cargs.add("init_table") - var m = "{super_meth_call}({cargs[0]})" - var vcall = "{m}({cargs.join(", ")}) /*super {local_class}::{name}*/" - return vcall - end - - # Cname of the i-th closure C struct type - protected fun closure_cname(i: Int): String - do - return "FWBT_{cname}_{i}" - end - - # Compile and declare the signature to C - protected fun decl_csignature(v: CompilerVisitor, args: Array[String]): String - do - var params = new Array[String] - params.add("val_t {args[0]}") - for i in [0..signature.arity[ do - var p = "val_t {args[i+1]}" - params.add(p) - end - - var first_closure_index = signature.arity + 1 # Wich parameter is the first closure - for i in [0..signature.closures.length[ do - var closcn = closure_cname(i) - var cs = signature.closures[i].signature # Closure signature - var subparams = new Array[String] # Parameters of the closure - subparams.add("struct WBT_ *") - for j in [0..cs.arity[ do - var p = "val_t" - subparams.add(p) - end - var r = "void" - if cs.return_type != null then r = "val_t" - params.add("struct WBT_ *{args[first_closure_index+i]}") - v.add_decl("typedef {r} (*{closcn})({subparams.join(", ")});") - end - - if global.is_init then - params.add("int* init_table") - end - - var ret: String - if signature.return_type != null then - ret = "val_t" - else - ret = "void" - end - - var p = params.join(", ") - var s = "{ret} {cname}({p})" - v.add_decl("typedef {ret} (* {cname}_t)({p});") - v.add_decl(s + ";") - return s - end - - redef fun compile_property_to_c(v) - do - v.cfc = new CFunctionContext(v) - - var args = new Array[String] - args.add(" self") - for i in [0..signature.arity[ do - args.add(" param{i}") - end - for i in [0..signature.closures.length[ do - args.add(" wd{i}") - end - var cs = decl_csignature(v, args) - v.add_decl("#define LOCATE_{cname} \"{full_name}\"") - - v.add_instr("{cs} \{") - v.indent - var ctx_old = v.ctx - v.ctx = new CContext - - v.out_contexts.clear - - var itpos: nullable String = null - if global.is_init then - itpos = "itpos{v.new_number}" - v.add_decl("int {itpos} = VAL2OBJ(self)->vft[{local_class.global.init_table_pos_id}].i;") - v.add_instr("if (init_table[{itpos}]) return;") - end - - var ln = 0 - var s = self - if s.node != null then ln = s.node.location.line_start - v.add_decl("struct trace_t trace = \{NULL, NULL, {ln}, LOCATE_{cname}};") - v.add_instr("trace.prev = tracehead; tracehead = &trace;") - v.add_instr("trace.file = LOCATE_{module.name};") - - var s = do_compile_inside(v, args) - - if itpos != null then - v.add_instr("init_table[{itpos}] = 1;") - end - - v.add_instr("tracehead = trace.prev;") - if s == null then - v.add_instr("return;") - else - v.add_instr("return {s};") - end - - v.cfc.generate_var_decls - - ctx_old.append(v.ctx) - v.ctx = ctx_old - v.unindent - v.add_instr("}") - - for ctx in v.out_contexts do v.ctx.merge(ctx) - end - - # Compile the method body inline - fun do_compile_inside(v: CompilerVisitor, params: Array[String]): nullable String is abstract -end - -redef class MMReadImplementationMethod - redef fun do_compile_inside(v, params) - do - return node.prop.compile_read_access(v, node, params[0]) - end -end - -redef class MMWriteImplementationMethod - redef fun do_compile_inside(v, params) - do - node.prop.compile_write_access(v, node, params[0], params[1]) - return null - end -end - -redef class MMMethSrcMethod - redef fun do_compile_inside(v, params) - do - return node.do_compile_inside(v, self, params) - end -end - -redef class MMImplicitInit - redef fun do_compile_inside(v, params) - do - var f = params.length - unassigned_attributes.length - var recv = params.first - for sp in super_inits do - assert sp isa MMMethod - var args_recv = [recv] - if sp == super_init then - var args = new Array[String].with_capacity(f) - args.add(recv) - for i in [1..f[ do - args.add(params[i]) - end - sp.compile_stmt_call(v, args) - else - sp.compile_stmt_call(v, args_recv) - end - end - for i in [f..params.length[ do - var attribute = unassigned_attributes[i-f] - attribute.compile_write_access(v, null, recv, params[i]) - end - return null - end -end - -redef class MMType - # Compile a subtype check to self - # Return a NIT Bool - fun compile_cast(v: CompilerVisitor, recv: String, fromtype: MMType): String - do - # Fixme: handle formaltypes - var g = local_class.global - var s = "" - if fromtype.is_nullable then - if self.is_nullable then - s = "({recv}==NIT_NULL) || " - else - s = "({recv}!=NIT_NULL) && " - end - else - # FIXME This is used to not break code without the nullable KW - s = "({recv}==NIT_NULL) || " - end - return "TAG_Bool({s}VAL_ISA({recv}, {g.color_id}, {g.id_id})) /*cast {self}*/" - end - - # Compile a cast assertion - fun compile_type_check(v: CompilerVisitor, recv: String, n: PNode, fromtype: MMType) - do - # Fixme: handle formaltypes - var g = local_class.global - var s = "" - if fromtype.is_nullable then - if self.is_nullable then - s = "({recv}!=NIT_NULL) && " - else - s = "({recv}==NIT_NULL) || " - end - else - # FIXME This is used to not break code without the nullable KW - s = "({recv}!=NIT_NULL) && " - end - v.add_instr("if ({s}!VAL_ISA({recv}, {g.color_id}, {g.id_id})) \{ fprintf(stderr, \"Cast failed\"); {v.printf_locate_error(n)} nit_exit(1); } /*cast {self}*/;") - end - - # Compile a notnull cast assertion - fun compile_notnull_check(v: CompilerVisitor, recv: String, n: PNode) - do - if is_nullable then - v.add_instr("if (({recv}==NIT_NULL)) \{ fprintf(stderr, \"Cast failed\"); {v.printf_locate_error(n)} nit_exit(1); } /*cast {self}*/;") - end - end -end - -############################################################################### - -redef class AMethPropdef - # Compile the method body - fun do_compile_inside(v: CompilerVisitor, method: MMMethod, params: Array[String]): nullable String is abstract -end - -redef class PSignature - fun compile_parameters(v: CompilerVisitor, orig_sig: MMSignature, params: Array[String]) is abstract -end - -redef class ASignature - redef fun compile_parameters(v: CompilerVisitor, orig_sig: MMSignature, params: Array[String]) - do - for ap in n_params do - var cname = v.cfc.register_variable(ap.variable) - v.nmc.method_params.add(ap.variable) - var orig_type = orig_sig[ap.position] - if not orig_type < ap.variable.stype.as(not null) then - # FIXME: do not test always - # FIXME: handle formal types - v.add_instr("/* check if p<{ap.variable.stype} with p:{orig_type} */") - ap.variable.stype.compile_type_check(v, params[ap.position], ap, orig_type) - end - v.add_assignment(cname, params[ap.position]) - end - for i in [0..n_closure_decls.length[ do - var wd = n_closure_decls[i] - var cname = v.cfc.register_closurevariable(wd.variable) - wd.variable.ctypename = v.nmc.method.closure_cname(i) - v.add_assignment(cname, "{params[orig_sig.arity + i]}") - end - end -end - -redef class AConcreteMethPropdef - redef fun do_compile_inside(v, method, params) - do - var old_nmc = v.nmc - v.nmc = new NitMethodContext(method) - - var selfcname = v.cfc.register_variable(self_var) - v.add_assignment(selfcname, params[0]) - params.shift - v.nmc.method_params = [self_var] - - var orig_meth: MMLocalProperty = method.global.intro - var orig_sig = orig_meth.signature_for(method.signature.recv) - if n_signature != null then - n_signature.compile_parameters(v, orig_sig, params) - end - - v.nmc.return_label = "return_label{v.new_number}" - v.nmc.return_value = v.cfc.get_var("Method return value and escape marker") - if self isa AConcreteInitPropdef then - v.invoke_super_init_calls_after(null) - end - v.compile_stmt(n_block) - v.add_instr("{v.nmc.return_label}: while(false);") - - var ret: nullable String = null - if method.signature.return_type != null then - ret = v.nmc.return_value - end - - v.nmc = old_nmc - return ret - end -end - -redef class ADeferredMethPropdef - redef fun do_compile_inside(v, method, params) - do - v.add_instr("fprintf(stderr, \"Deferred method called\");") - v.add_instr(v.printf_locate_error(self)) - v.add_instr("nit_exit(1);") - if method.signature.return_type != null then - return("NIT_NULL") - else - return null - end - end -end - -redef class AExternMethPropdef - redef fun do_compile_inside(v, method, params) - do - var ename = "{method.module.name}_{method.local_class.name}_{method.local_class.name}_{method.name}_{method.signature.arity}" - if n_extern != null then - ename = n_extern.text - ename = ename.substring(1, ename.length-2) - end - var sig = method.signature - if params.length != sig.arity + 1 then - printl("par:{params.length} sig:{sig.arity}") - end - var args = new Array[String] - args.add(sig.recv.unboxtype(params[0])) - for i in [0..sig.arity[ do - args.add(sig[i].unboxtype(params[i+1])) - end - var s = "{ename}({args.join(", ")})" - if sig.return_type != null then - return sig.return_type.boxtype(s) - else - v.add_instr("{s};") - return null - end - end -end - -redef class AInternMethPropdef - redef fun do_compile_inside(v, method, p) - do - var c = method.local_class.name - var n = method.name - var s: nullable String = null - if c == once "Int".to_symbol then - if n == once "object_id".to_symbol then - s = "{p[0]}" - else if n == once "unary -".to_symbol then - s = "TAG_Int(-UNTAG_Int({p[0]}))" - else if n == once "output".to_symbol then - v.add_instr("printf(\"%ld\\n\", UNTAG_Int({p[0]}));") - else if n == once "ascii".to_symbol then - s = "TAG_Char(UNTAG_Int({p[0]}))" - else if n == once "succ".to_symbol then - s = "TAG_Int(UNTAG_Int({p[0]})+1)" - else if n == once "prec".to_symbol then - s = "TAG_Int(UNTAG_Int({p[0]})-1)" - else if n == once "to_f".to_symbol then - s = "BOX_Float((float)UNTAG_Int({p[0]}))" - else if n == once "+".to_symbol then - s = "TAG_Int(UNTAG_Int({p[0]})+UNTAG_Int({p[1]}))" - else if n == once "-".to_symbol then - s = "TAG_Int(UNTAG_Int({p[0]})-UNTAG_Int({p[1]}))" - else if n == once "*".to_symbol then - s = "TAG_Int(UNTAG_Int({p[0]})*UNTAG_Int({p[1]}))" - else if n == once "/".to_symbol then - s = "TAG_Int(UNTAG_Int({p[0]})/UNTAG_Int({p[1]}))" - else if n == once "%".to_symbol then - s = "TAG_Int(UNTAG_Int({p[0]})%UNTAG_Int({p[1]}))" - else if n == once "<".to_symbol then - s = "TAG_Bool(UNTAG_Int({p[0]})".to_symbol then - s = "TAG_Bool(UNTAG_Int({p[0]})>UNTAG_Int({p[1]}))" - else if n == once "<=".to_symbol then - s = "TAG_Bool(UNTAG_Int({p[0]})<=UNTAG_Int({p[1]}))" - else if n == once ">=".to_symbol then - s = "TAG_Bool(UNTAG_Int({p[0]})>=UNTAG_Int({p[1]}))" - else if n == once "lshift".to_symbol then - s = "TAG_Int(UNTAG_Int({p[0]})<".to_symbol then - s = "TAG_Bool(UNBOX_Float({p[0]})>UNBOX_Float({p[1]}))" - else if n == once "<=".to_symbol then - s = "TAG_Bool(UNBOX_Float({p[0]})<=UNBOX_Float({p[1]}))" - else if n == once ">=".to_symbol then - s = "TAG_Bool(UNBOX_Float({p[0]})>=UNBOX_Float({p[1]}))" - end - else if c == once "Char".to_symbol then - if n == once "object_id".to_symbol then - s = "TAG_Int(UNTAG_Char({p[0]}))" - else if n == once "unary -".to_symbol then - s = "TAG_Char(-UNTAG_Char({p[0]}))" - else if n == once "output".to_symbol then - v.add_instr("printf(\"%c\", (unsigned char)UNTAG_Char({p[0]}));") - else if n == once "ascii".to_symbol then - s = "TAG_Int((unsigned char)UNTAG_Char({p[0]}))" - else if n == once "succ".to_symbol then - s = "TAG_Char(UNTAG_Char({p[0]})+1)" - else if n == once "prec".to_symbol then - s = "TAG_Char(UNTAG_Char({p[0]})-1)" - else if n == once "to_i".to_symbol then - s = "TAG_Int(UNTAG_Char({p[0]})-'0')" - else if n == once "+".to_symbol then - s = "TAG_Char(UNTAG_Char({p[0]})+UNTAG_Char({p[1]}))" - else if n == once "-".to_symbol then - s = "TAG_Char(UNTAG_Char({p[0]})-UNTAG_Char({p[1]}))" - else if n == once "*".to_symbol then - s = "TAG_Char(UNTAG_Char({p[0]})*UNTAG_Char({p[1]}))" - else if n == once "/".to_symbol then - s = "TAG_Char(UNTAG_Char({p[0]})/UNTAG_Char({p[1]}))" - else if n == once "%".to_symbol then - s = "TAG_Char(UNTAG_Char({p[0]})%UNTAG_Char({p[1]}))" - else if n == once "<".to_symbol then - s = "TAG_Bool(UNTAG_Char({p[0]})".to_symbol then - s = "TAG_Bool(UNTAG_Char({p[0]})>UNTAG_Char({p[1]}))" - else if n == once "<=".to_symbol then - s = "TAG_Bool(UNTAG_Char({p[0]})<=UNTAG_Char({p[1]}))" - else if n == once ">=".to_symbol then - s = "TAG_Bool(UNTAG_Char({p[0]})>=UNTAG_Char({p[1]}))" - else if n == once "==".to_symbol then - s = "TAG_Bool(({p[0]})==({p[1]}))" - else if n == once "!=".to_symbol then - s = "TAG_Bool(({p[0]})!=({p[1]}))" - end - else if c == once "Bool".to_symbol then - if n == once "object_id".to_symbol then - s = "TAG_Int(UNTAG_Bool({p[0]}))" - else if n == once "unary -".to_symbol then - s = "TAG_Bool(-UNTAG_Bool({p[0]}))" - else if n == once "output".to_symbol then - v.add_instr("(void)printf(UNTAG_Bool({p[0]})?\"true\\n\":\"false\\n\");") - else if n == once "ascii".to_symbol then - s = "TAG_Bool(UNTAG_Bool({p[0]}))" - else if n == once "to_i".to_symbol then - s = "TAG_Int(UNTAG_Bool({p[0]}))" - else if n == once "==".to_symbol then - s = "TAG_Bool(({p[0]})==({p[1]}))" - else if n == once "!=".to_symbol then - s = "TAG_Bool(({p[0]})!=({p[1]}))" - end - else if c == once "NativeArray".to_symbol then - if n == once "object_id".to_symbol then - s = "TAG_Int(UNBOX_NativeArray({p[0]}))" - else if n == once "[]".to_symbol then - s = "UNBOX_NativeArray({p[0]})[UNTAG_Int({p[1]})]" - else if n == once "[]=".to_symbol then - v.add_instr("UNBOX_NativeArray({p[0]})[UNTAG_Int({p[1]})]={p[2]};") - else if n == once "copy_to".to_symbol then - v.add_instr("(void)memcpy(UNBOX_NativeArray({p[1]}), UNBOX_NativeArray({p[0]}), UNTAG_Int({p[2]})*sizeof(val_t));") - end - else if c == once "NativeString".to_symbol then - if n == once "object_id".to_symbol then - s = "TAG_Int(UNBOX_NativeString({p[0]}))" - else if n == once "atoi".to_symbol then - s = "TAG_Int(atoi(UNBOX_NativeString({p[0]})))" - else if n == once "[]".to_symbol then - s = "TAG_Char(UNBOX_NativeString({p[0]})[UNTAG_Int({p[1]})])" - else if n == once "[]=".to_symbol then - v.add_instr("UNBOX_NativeString({p[0]})[UNTAG_Int({p[1]})]=UNTAG_Char({p[2]});") - else if n == once "copy_to".to_symbol then - v.add_instr("(void)memcpy(UNBOX_NativeString({p[1]})+UNTAG_Int({p[4]}), UNBOX_NativeString({p[0]})+UNTAG_Int({p[3]}), UNTAG_Int({p[2]}));") - end - else if n == once "object_id".to_symbol then - s = "TAG_Int((bigint){p[0]})" - else if n == once "sys".to_symbol then - s = "(G_sys)" - else if n == once "is_same_type".to_symbol then - s = "TAG_Bool((VAL2VFT({p[0]})==VAL2VFT({p[1]})))" - else if n == once "exit".to_symbol then - v.add_instr("exit(UNTAG_Int({p[1]}));") - else if n == once "calloc_array".to_symbol then - s = "BOX_NativeArray((val_t*)malloc((UNTAG_Int({p[1]}) * sizeof(val_t))))" - else if n == once "calloc_string".to_symbol then - s = "BOX_NativeString((char*)malloc((UNTAG_Int({p[1]}) * sizeof(char))))" - - else - stderr.write("{location}: Fatal error: unknown intern method {method.full_name}.\n") - exit(1) - end - if method.signature.return_type != null and s == null then - s = "NIT_NULL /*stub*/" - end - return s - end -end - -############################################################################### - -redef class PExpr - # Compile the node as an expression - # Only the visitor should call it - fun compile_expr(v: CompilerVisitor): String is abstract - - # Prepare a call of node as a statement - # Only the visitor should call it - # It's used for local variable managment - fun prepare_compile_stmt(v: CompilerVisitor) do end - - # Compile the node as a statement - # Only the visitor should call it - fun compile_stmt(v: CompilerVisitor) do printl("Error!") -end - -redef class ABlockExpr - redef fun compile_stmt(v) - do - for n in n_expr do - v.compile_stmt(n) - end - end -end - -redef class AVardeclExpr - redef fun prepare_compile_stmt(v) - do - v.cfc.register_variable(variable) - end - - redef fun compile_stmt(v) - do - var cname = v.cfc.varname(variable) - if n_expr == null then - v.add_instr("/*{cname} is variable {variable.name}*/") - else - var e = v.compile_expr(n_expr.as(not null)) - v.add_assignment(cname, e) - end - end -end - -redef class AReturnExpr - redef fun compile_stmt(v) - do - if n_expr != null then - var e = v.compile_expr(n_expr.as(not null)) - v.add_assignment(v.nmc.return_value.as(not null), e) - end - if v.cfc.closure == v.nmc then v.add_instr("closctx->has_broke = &({v.nmc.return_value});") - v.add_instr("goto {v.nmc.return_label};") - end -end - -redef class ABreakExpr - redef fun compile_stmt(v) - do - if n_expr != null then - var e = v.compile_expr(n_expr.as(not null)) - v.add_assignment(v.nmc.break_value.as(not null), e) - end - if v.cfc.closure == v.nmc then v.add_instr("closctx->has_broke = &({v.nmc.break_value}); closctx->broke_value = *closctx->has_broke;") - v.add_instr("goto {v.nmc.break_label};") - end -end - -redef class AContinueExpr - redef fun compile_stmt(v) - do - if n_expr != null then - var e = v.compile_expr(n_expr.as(not null)) - v.add_assignment(v.nmc.continue_value.as(not null), e) - end - v.add_instr("goto {v.nmc.continue_label};") - end -end - -redef class AAbortExpr - redef fun compile_stmt(v) - do - v.add_instr("fprintf(stderr, \"Aborted\"); {v.printf_locate_error(self)} nit_exit(1);") - end -end - -redef class ADoExpr - redef fun compile_stmt(v) - do - v.compile_stmt(n_block) - end -end - -redef class AIfExpr - redef fun compile_stmt(v) - do - var e = v.compile_expr(n_expr) - v.add_instr("if (UNTAG_Bool({e})) \{ /*if*/") - v.cfc.free_var(e) - if n_then != null then - v.indent - v.compile_stmt(n_then) - v.unindent - end - if n_else != null then - v.add_instr("} else \{ /*if*/") - v.indent - v.compile_stmt(n_else) - v.unindent - end - v.add_instr("}") - end -end - -redef class AIfexprExpr - redef fun compile_expr(v) - do - var e = v.compile_expr(n_expr) - v.add_instr("if (UNTAG_Bool({e})) \{ /*if*/") - v.cfc.free_var(e) - v.indent - var e = v.ensure_var(v.compile_expr(n_then), "Then value") - v.unindent - v.add_instr("} else \{ /*if*/") - v.cfc.free_var(e) - v.indent - var e2 = v.ensure_var(v.compile_expr(n_else), "Else value") - v.add_assignment(e, e2) - v.unindent - v.add_instr("}") - return e - end -end - -class AControlableBlock -special PExpr - fun compile_inside_block(v: CompilerVisitor) is abstract - redef fun compile_stmt(v) - do - var old_break_label = v.nmc.break_label - var old_continue_label = v.nmc.continue_label - var id = v.new_number - v.nmc.break_label = "break_{id}" - v.nmc.continue_label = "continue_{id}" - - compile_inside_block(v) - - - v.nmc.break_label = old_break_label - v.nmc.continue_label = old_continue_label - end -end - -redef class AWhileExpr -special AControlableBlock - redef fun compile_inside_block(v) - do - v.add_instr("while (true) \{ /*while*/") - v.indent - var e = v.compile_expr(n_expr) - v.add_instr("if (!UNTAG_Bool({e})) break; /* while*/") - v.cfc.free_var(e) - v.compile_stmt(n_block) - v.add_instr("{v.nmc.continue_label}: while(0);") - v.unindent - v.add_instr("}") - v.add_instr("{v.nmc.break_label}: while(0);") - end -end - -redef class AForExpr -special AControlableBlock - redef fun compile_inside_block(v) - do - var e = v.compile_expr(n_expr) - var ittype = meth_iterator.signature.return_type - v.cfc.free_var(e) - var iter = v.cfc.get_var("For iterator") - v.add_assignment(iter, meth_iterator.compile_expr_call(v, [e])) - v.add_instr("while (true) \{ /*for*/") - v.indent - var ok = v.cfc.get_var("For 'is_ok' result") - v.add_assignment(ok, meth_is_ok.compile_expr_call(v, [iter])) - v.add_instr("if (!UNTAG_Bool({ok})) break; /*for*/") - v.cfc.free_var(ok) - var e = meth_item.compile_expr_call(v, [iter]) - e = v.ensure_var(e, "For item") - var cname = v.cfc.register_variable(variable) - v.add_assignment(cname, e) - v.compile_stmt(n_block) - v.add_instr("{v.nmc.continue_label}: while(0);") - meth_next.compile_stmt_call(v, [iter]) - v.unindent - v.add_instr("}") - v.add_instr("{v.nmc.break_label}: while(0);") - end -end - -redef class AAssertExpr - redef fun compile_stmt(v) - do - var e = v.compile_expr(n_expr) - var s = "" - if n_id != null then - s = " '{n_id.text}' " - end - v.add_instr("if (!UNTAG_Bool({e})) \{ fprintf(stderr, \"Assert%s failed\", \"{s}\"); {v.printf_locate_error(self)} nit_exit(1);}") - end -end - -redef class AVarExpr - redef fun compile_expr(v) - do - return " {v.cfc.varname(variable)} /*{variable.name}*/" - end -end - -redef class AVarAssignExpr - redef fun compile_stmt(v) - do - var e = v.compile_expr(n_value) - v.add_assignment(v.cfc.varname(variable), "{e} /*{variable.name}=*/") - end -end - -redef class AVarReassignExpr - redef fun compile_stmt(v) - do - var e1 = v.cfc.varname(variable) - var e2 = v.compile_expr(n_value) - var e3 = assign_method.compile_expr_call(v, [e1, e2]) - v.add_assignment(v.cfc.varname(variable), "{e3} /*{variable.name}*/") - end -end - -redef class ASelfExpr - redef fun compile_expr(v) - do - return v.cfc.varname(v.nmc.method_params[0]) - end -end - -redef class AOrExpr - redef fun compile_expr(v) - do - var e = v.ensure_var(v.compile_expr(n_expr), "Left 'or' operand") - v.add_instr("if (!UNTAG_Bool({e})) \{ /* or */") - v.cfc.free_var(e) - v.indent - var e2 = v.compile_expr(n_expr2) - v.add_assignment(e, e2) - v.unindent - v.add_instr("}") - return e - end -end - -redef class AAndExpr - redef fun compile_expr(v) - do - var e = v.ensure_var(v.compile_expr(n_expr), "Left 'and' operand") - v.add_instr("if (UNTAG_Bool({e})) \{ /* and */") - v.cfc.free_var(e) - v.indent - var e2 = v.compile_expr(n_expr2) - v.add_assignment(e, e2) - v.unindent - v.add_instr("}") - return e - end -end - -redef class ANotExpr - redef fun compile_expr(v) - do - return " TAG_Bool(!UNTAG_Bool({v.compile_expr(n_expr)}))" - end -end - -redef class AEeExpr - redef fun compile_expr(v) - do - var e = v.compile_expr(n_expr) - var e2 = v.compile_expr(n_expr2) - return "TAG_Bool(IS_EQUAL_NN({e},{e2}))" - end -end - -redef class AIsaExpr - redef fun compile_expr(v) - do - var e = v.compile_expr(n_expr) - return n_type.stype.compile_cast(v, e, n_expr.stype) - end -end - -redef class AAsCastExpr - redef fun compile_expr(v) - do - var e = v.compile_expr(n_expr) - n_type.stype.compile_type_check(v, e, self, n_expr.stype) - return e - end -end - -redef class AAsNotnullExpr - redef fun compile_expr(v) - do - var e = v.compile_expr(n_expr) - n_expr.stype.compile_notnull_check(v, e, self) - return e - end -end - -redef class ATrueExpr - redef fun compile_expr(v) - do - return " TAG_Bool(true)" - end -end - -redef class AFalseExpr - redef fun compile_expr(v) - do - return " TAG_Bool(false)" - end -end - -redef class AIntExpr - redef fun compile_expr(v) - do - return " TAG_Int({n_number.text})" - end -end - -redef class AFloatExpr - redef fun compile_expr(v) - do - return "BOX_Float({n_float.text})" - end -end - -redef class ACharExpr - redef fun compile_expr(v) - do - return " TAG_Char({n_char.text})" - end -end - -redef class AStringFormExpr - redef fun compile_expr(v) - do - compute_string_info - var i = v.new_number - var cvar = v.cfc.get_var("Once String constant") - v.add_decl("static val_t once_value_{i} = NIT_NULL; /* Once value for string {cvar}*/") - v.add_instr("if (once_value_{i} != NIT_NULL) {cvar} = once_value_{i};") - v.add_instr("else \{") - v.indent - v.cfc.free_var(cvar) - var e = meth_with_native.compile_constructor_call(v, stype, ["BOX_NativeString(\"{_cstring}\")", "TAG_Int({_cstring_length})"]) - v.add_assignment(cvar, e) - v.add_instr("once_value_{i} = {cvar};") - v.unindent - v.add_instr("}") - return cvar - end - - # The raw string value - protected fun string_text: String is abstract - - # The string in a C native format - protected var _cstring: nullable String - - # The string length in bytes - protected var _cstring_length: nullable Int - - # Compute _cstring and _cstring_length using string_text - protected fun compute_string_info - do - var len = 0 - var str = string_text - var res = new Buffer - var i = 0 - while i < str.length do - var c = str[i] - if c == '\\' then - i = i + 1 - var c2 = str[i] - if c2 != '{' and c2 != '}' then - res.add(c) - end - c = c2 - end - len = len + 1 - res.add(c) - i = i + 1 - end - _cstring = res.to_s - _cstring_length = len - end -end - -redef class AStringExpr - redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2) -end -redef class AStartStringExpr - redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2) -end -redef class AMidStringExpr - redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2) -end -redef class AEndStringExpr - redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2) -end - -redef class ASuperstringExpr - redef fun compile_expr(v) - do - var array = meth_with_capacity.compile_constructor_call(v, atype, ["TAG_Int({n_exprs.length})"]) - array = v.ensure_var(array, "Array (for super-string)") - - for ne in n_exprs do - var e = v.ensure_var(v.compile_expr(ne), "super-string element") - if ne.stype != stype then - v.cfc.free_var(e) - e = meth_to_s.compile_expr_call(v, [e]) - end - v.cfc.free_var(e) - meth_add.compile_stmt_call(v, [array, e]) - end - - return meth_to_s.compile_expr_call(v, [array]) - end -end - -redef class ANullExpr - redef fun compile_expr(v) - do - return " NIT_NULL /*null*/" - end -end - -redef class AArrayExpr - redef fun compile_expr(v) - do - var recv = meth_with_capacity.compile_constructor_call(v, stype, ["TAG_Int({n_exprs.length})"]) - recv = v.ensure_var(recv, "Literal array") - - for ne in n_exprs do - var e = v.compile_expr(ne) - meth_add.compile_stmt_call(v, [recv, e]) - end - return recv - end -end - -redef class ARangeExpr - redef fun compile_expr(v) - do - var e = v.compile_expr(n_expr) - var e2 = v.compile_expr(n_expr2) - return meth_init.compile_constructor_call(v, stype, [e, e2]) - end -end - -redef class ASuperExpr - redef fun compile_stmt(v) - do - var e = intern_compile_call(v) - if e != null then - v.add_instr(e + ";") - end - end - - redef fun compile_expr(v) - do - var e = intern_compile_call(v) - assert e != null - return e - end - - private fun intern_compile_call(v: CompilerVisitor): nullable String - do - var arity = v.nmc.method_params.length - 1 - if init_in_superclass != null then - arity = init_in_superclass.signature.arity - end - var args = new Array[String].with_capacity(arity + 1) - args.add(v.cfc.varname(v.nmc.method_params[0])) - if n_args.length != arity then - for i in [0..arity[ do - args.add(v.cfc.varname(v.nmc.method_params[i + 1])) - end - else - for na in n_args do - args.add(v.compile_expr(na)) - end - end - #return "{prop.cname}({args.join(", ")}) /*super {prop.local_class}::{prop.name}*/" - if init_in_superclass != null then - return init_in_superclass.intern_compile_call(v, args) - else - return prop.compile_super_call(v, args) - end - end -end - -redef class AAttrExpr - redef fun compile_expr(v) - do - var e = v.compile_expr(n_expr) - return prop.compile_read_access(v, n_id, e) - end -end - -redef class AAttrAssignExpr - redef fun compile_stmt(v) - do - var e = v.compile_expr(n_expr) - var e2 = v.compile_expr(n_value) - prop.compile_write_access(v, n_id, e, e2) - end -end -redef class AAttrReassignExpr - redef fun compile_stmt(v) - do - var e1 = v.compile_expr(n_expr) - var e2 = prop.compile_read_access(v, n_id, e1) - var e3 = v.compile_expr(n_value) - var e4 = assign_method.compile_expr_call(v, [e2, e3]) - prop.compile_write_access(v, n_id, e1, e4) - end -end - -redef class AIssetAttrExpr - redef fun compile_expr(v) - do - var e = v.compile_expr(n_expr) - return prop.compile_isset(v, n_id, e) - end -end - -redef class AAbsAbsSendExpr - # Compile each argument and add them to the array - fun compile_arguments_in(v: CompilerVisitor, cargs: Array[String]) - do - for a in arguments do - cargs.add(v.compile_expr(a)) - end - end - -end - -redef class ASendExpr - private fun intern_compile_call(v: CompilerVisitor): nullable String - do - var recv = v.compile_expr(n_expr) - var cargs = new Array[String] - cargs.add(recv) - compile_arguments_in(v, cargs) - - var e: nullable String - if prop_signature.closures.is_empty then - e = prop.intern_compile_call(v, cargs) - else - e = prop.compile_call_and_closures(v, cargs, closure_defs) - end - - if prop.global.is_init then - v.invoke_super_init_calls_after(prop) - end - return e - end - - redef fun compile_expr(v) - do - var e = intern_compile_call(v) - assert e != null - return e - end - - redef fun compile_stmt(v) - do - var e = intern_compile_call(v) - if e != null then - v.add_instr(e + ";") - end - end -end - -redef class ASendReassignExpr - redef fun compile_expr(v) do abort - - redef fun compile_stmt(v) - do - var recv = v.compile_expr(n_expr) - var cargs = new Array[String] - cargs.add(recv) - compile_arguments_in(v, cargs) - - var e2 = read_prop.compile_expr_call(v, cargs) - var e3 = v.compile_expr(n_value) - var e4 = assign_method.compile_expr_call(v, [e2, e3]) - cargs.add(e4) - prop.compile_stmt_call(v, cargs) - end -end - -redef class ANewExpr - redef fun compile_expr(v) - do - var cargs = new Array[String] - compile_arguments_in(v, cargs) - return prop.compile_constructor_call(v, stype, cargs) - end - - redef fun compile_stmt(v) do abort -end - -redef class PClosureDef - # Compile the closure definition as a function in v.out_contexts - # Return the cname of the function - fun compile_closure(v: CompilerVisitor, closcn: String): String is abstract - - # Compile the closure definition inside the current C function. - fun do_compile_inside(v: CompilerVisitor, params: nullable Array[String]): nullable String is abstract -end - -redef class AClosureDef - # The cname of the function - readable var _cname: nullable String - - redef fun compile_closure(v, closcn) - do - var ctx_old = v.ctx - v.ctx = new CContext - v.out_contexts.add(v.ctx) - - var cfc_old = v.cfc.closure - v.cfc.closure = v.nmc - - var old_rv = v.nmc.return_value - var old_bv = v.nmc.break_value - if cfc_old == null then - v.nmc.return_value = "closctx->{old_rv}" - v.nmc.break_value = "closctx->{old_bv}" - end - - var cname = "OC_{v.nmc.method.cname}_{v.out_contexts.length}" - _cname = cname - var args = new Array[String] - for i in [0..closure.signature.arity[ do - args.add(" param{i}") - end - - var cs = decl_csignature(v, args, closcn) - - v.add_instr("{cs} \{") - v.indent - var ctx_old2 = v.ctx - v.ctx = new CContext - - v.add_decl("struct trace_t trace = \{NULL, NULL, {location.line_start}, LOCATE_{v.nmc.method.cname}};") - v.add_instr("trace.prev = tracehead; tracehead = &trace;") - - v.add_instr("trace.file = LOCATE_{v.module.name};") - var s = do_compile_inside(v, args) - - v.add_instr("{v.nmc.return_label}:") - v.add_instr("tracehead = trace.prev;") - if s == null then - v.add_instr("return;") - else - v.add_instr("return {s};") - end - - ctx_old2.append(v.ctx) - v.ctx = ctx_old2 - v.unindent - v.add_instr("}") - v.ctx = ctx_old - - v.cfc.closure = cfc_old - v.nmc.return_value = old_rv - v.nmc.break_value = old_bv - - # Build closure - var closcnv = "wbclos{v.new_number}" - v.add_decl("struct WBT_ {closcnv};") - v.add_instr("{closcnv}.fun = (fun_t){cname};") - v.add_instr("{closcnv}.has_broke = NULL;") - if cfc_old != null then - v.add_instr("{closcnv}.variable = closctx->variable;") - v.add_instr("{closcnv}.closurevariable = closctx->closurevariable;") - else - v.add_instr("{closcnv}.variable = variable;") - v.add_instr("{closcnv}.closurevariable = closurevariable;") - end - - return "(&{closcnv})" - end - - protected fun decl_csignature(v: CompilerVisitor, args: Array[String], closcn: String): String - do - var params = new Array[String] - params.add("struct WBT_ *closctx") - for i in [0..closure.signature.arity[ do - var p = "val_t {args[i]}" - params.add(p) - end - var ret: String - if closure.signature.return_type != null then - ret = "val_t" - else - ret = "void" - end - var p = params.join(", ") - var s = "{ret} {cname}({p})" - v.add_decl("typedef {ret} (* {cname}_t)({p});") - v.add_decl(s + ";") - return s - end - - redef fun do_compile_inside(v, params) - do - for i in [0..variables.length[ do - var vacname = v.cfc.register_variable(variables[i]) - v.add_assignment(vacname, params[i]) - end - - var old_cv = v.nmc.continue_value - var old_cl = v.nmc.continue_label - var old_bl = v.nmc.break_label - - v.nmc.continue_value = v.cfc.get_var("Continue value and escape marker") - v.nmc.continue_label = "continue_label{v.new_number}" - v.nmc.break_label = v.nmc.return_label - - v.compile_stmt(n_expr) - - v.add_instr("{v.nmc.continue_label}: while(false);") - - var ret: nullable String = null - if closure.signature.return_type != null then ret = v.nmc.continue_value - - v.nmc.continue_value = old_cv - v.nmc.continue_label = old_cl - v.nmc.break_label = old_bl - - return ret - end -end - -redef class PClosureDecl - fun do_compile_inside(v: CompilerVisitor, params: Array[String]): nullable String is abstract -end -redef class AClosureDecl - redef fun do_compile_inside(v, params) - do - n_signature.compile_parameters(v, variable.closure.signature, params) - - var old_cv = v.nmc.continue_value - var old_cl = v.nmc.continue_label - var old_bl = v.nmc.break_label - - v.nmc.continue_value = v.cfc.get_var("Continue value and escape marker") - v.nmc.continue_label = "continue_label{v.new_number}" - v.nmc.break_label = v.nmc.return_label - - v.compile_stmt(n_expr) - - v.add_instr("{v.nmc.continue_label}: while(false);") - - var ret: nullable String = null - if variable.closure.signature.return_type != null then ret = v.nmc.continue_value - - v.nmc.continue_value = old_cv - v.nmc.continue_label = old_cl - v.nmc.break_label = old_bl - - return ret - end -end - -redef class AClosureCallExpr - fun intern_compile_call(v: CompilerVisitor): nullable String - do - var cargs = new Array[String] - compile_arguments_in(v, cargs) - var va: nullable String = null - if variable.closure.signature.return_type != null then va = v.cfc.get_var("Closure call result value") - - if variable.closure.is_optional then - v.add_instr("if({v.cfc.varname(variable)}==NULL) \{") - v.indent - var n = variable.decl - assert n isa AClosureDecl - var s = n.do_compile_inside(v, cargs) - if s != null then v.add_assignment(va.as(not null), s) - v.unindent - v.add_instr("} else \{") - v.indent - end - - var ivar = v.cfc.varname(variable) - var cargs2 = [ivar] - cargs2.append(cargs) - var s = "(({variable.ctypename})({ivar}->fun))({cargs2.join(", ")}) /* Invoke closure {variable} */" - if va != null then - v.add_assignment(va, s) - else - v.add_instr("{s};") - end - v.add_instr("if ({ivar}->has_broke) \{") - v.indent - if n_closure_defs.length == 1 then do - n_closure_defs.first.do_compile_inside(v, null) - end - if v.cfc.closure == v.nmc then v.add_instr("if ({ivar}->has_broke) \{ closctx->has_broke = {ivar}->has_broke; closctx->broke_value = {ivar}->broke_value;\}") - v.add_instr("goto {v.nmc.return_label};") - v.unindent - v.add_instr("\}") - - if variable.closure.is_optional then - v.unindent - v.add_instr("\}") - end - return va - end - - redef fun compile_expr(v) - do - var e = intern_compile_call(v) - assert e != null - return e - end - - redef fun compile_stmt(v) - do - var e = intern_compile_call(v) - if e != null then - v.add_instr(e + ";") - end - end -end - -redef class AProxyExpr - redef fun compile_expr(v) - do - return v.compile_expr(n_expr) - end -end - -redef class AOnceExpr - redef fun compile_expr(v) - do - var i = v.new_number - var cvar = v.cfc.get_var("Once expression result") - v.add_decl("static val_t once_value_{i}; static int once_bool_{i}; /* Once value for {cvar}*/") - v.add_instr("if (once_bool_{i}) {cvar} = once_value_{i};") - v.add_instr("else \{") - v.indent - v.cfc.free_var(cvar) - var e = v.compile_expr(n_expr) - v.add_assignment(cvar, e) - v.add_instr("once_value_{i} = {cvar};") - v.add_instr("once_bool_{i} = true;") - v.unindent - v.add_instr("}") - return cvar - end -end diff --git a/src/icode/icode.nit b/src/icode/icode.nit new file mode 100644 index 0000000..ec6ddbf --- /dev/null +++ b/src/icode/icode.nit @@ -0,0 +1,20 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2009 Jean Privat +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Nit intermediate code representation +import icode_base +import icode_tools +import icode_builder diff --git a/src/icode/icode_base.nit b/src/icode/icode_base.nit new file mode 100644 index 0000000..f46ff5b --- /dev/null +++ b/src/icode/icode_base.nit @@ -0,0 +1,399 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2009 Jean Privat +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Base classes for Nit intermediate code representation +package icode_base + +import metamodel +import mmloader + +## UTILITY CLASSES ## + +# Register store local variable and intermediate results +class IRegister + # The static type + readable var _stype: MMType + init(s: MMType) + do + _stype = s + end +end + +# A Closure declaration +class IClosureDecl + # The associated closure definition + readable var _closure: MMClosure + + # The default implementation + readable writable var _default: nullable IRoutine + + init(c: MMClosure) do _closure = c +end + +# A routine is a sequence of icodes with entry iregisters (params) and an exit iregister (result) +class IRoutine + # The parameters of the routine + readable var _params: IndexedCollection[IRegister] + + # The closure declared + readable writable var _closure_decls: nullable IndexedCollection[IClosureDecl] = null + + # The result of the routine + readable var _result: nullable IRegister + + # The sequence of icode + readable var _body: ISeq = new ISeq + + # The location of the iroutine (if any) + readable writable var _location: nullable Location = null + + init(p: IndexedCollection[IRegister], r: nullable IRegister) + do + _params = p.to_a + _result = r + end +end + +# A closure definition in a iroutine body +class IClosureDef +special IRoutine + init(p: Array[IRegister], r: nullable IRegister) + do + super(p, r) + end +end + +## INTERMEDIATE CODE ## + +# The root of the intermediate code representation +abstract class ICode + # The number of registers used by the icode + fun arity: Int is abstract + + # The result of the icode (if any) + readable writable var _result: nullable IRegister = null + + # The location of the icode (if any) + readable writable var _location: nullable Location = null +end + +# An icode that uses no registers (no args) +abstract class ICode0 +special ICode + redef fun arity do return 0 +end + +# An icode that uses a single register (1 arg) +abstract class ICode1 +special ICode + redef fun arity do return 1 + + # The single argument + readable var _expr: IRegister + + init(e: IRegister) do _expr = e +end + +# An icode that uses two single registers (2 args) +abstract class ICode2 +special ICode + redef fun arity do return 2 + + # The first argument + readable var _expr1: IRegister + + # The second argument + readable var _expr2: IRegister + + init(e1, e2: IRegister) + do + _expr1 = e1 + _expr2 = e2 + end +end + +# An icode that uses a variable number of registers (n args) and a variable number of closure definitions +abstract class ICodeN +special ICode + redef fun arity do return _exprs.length + + # All arguments + readable var _exprs: IndexedCollection[IRegister] + + # All closure definition + readable writable var _closure_defs: nullable IndexedCollection[nullable IClosureDef] + + init(e: nullable IndexedCollection[IRegister]) + do + if e == null then + _exprs = new Array[IRegister] + else + _exprs = e + end + end +end + +################################################# + +# A linear sequence of ICode +class ISeq +special ICode0 + # The sequence of icode + readable var _icodes: List[ICode] = new List[ICode] + init do end +end + +# An infinite loop of ICode +# Use IEscape to exit +class ILoop +special ISeq + init do end +end + +# A Condidianal if-then-else statement +# expr is the condition +class IIf +special ICode1 + # The 'then' sequence of icode + readable var _then_seq: ISeq = new ISeq + # The 'else' sequence of icode + readable var _else_seq: ISeq = new ISeq + init(e: IRegister) do super +end + +# Escape to to end of a parent sequence +class IEscape +special ICode0 + # The seqeuence to escape + # The control flow continues at the next icode after the sequence + readable var _seq: ISeq + init(seq: ISeq) do _seq = seq +end + +# An abort statement +class IAbort +special ICode0 + # The reason the abort occured + # tests.first is the format + readable var _texts: Array[String] + # The local property that has the abort (if any) + readable var _property_location: nullable MMLocalProperty + # The module that has the abort + readable var _module_location: MMModule + init(t: Array[String], pl: nullable MMLocalProperty, ml: MMModule) + do + _texts = t + _property_location = pl + _module_location = ml + end +end + +################################################# + +# The root of all method invocations +abstract class IAbsCall +special ICodeN + # The called method + readable var _property: MMMethod + + init(p: MMMethod, e: IndexedCollection[IRegister]) + do + super(e) + _property = p + end +end + +# A simple procedure or function call +# exprs.first is the reciever, other are arguments +class ICall +special IAbsCall + init(p, e) do super +end + +# A super method call +# exprs.first is the reciever, other are arguments +class ISuper +special IAbsCall + init(p, e) do super +end + +# An instantiation +# no reciever, all exprs are arguments +class INew +special ICall + # The type to instantiate + readable var _stype: MMType + init(t: MMType, p: MMMethod, a: IndexedCollection[IRegister]) + do + super(p, a) + _stype = t + end +end + +# A closure call +# exprs are the arguments +class IClosCall +special ICodeN + # The called closure + readable var _closure_decl: IClosureDecl + + # The !break sequence (if any) + readable writable var _break_seq: nullable ISeq = null + + init(c: IClosureDecl, e: IndexedCollection[IRegister]) + do + super(e) + _closure_decl = c + end +end + +# A native C code +# Mainly used to implements things that do not have a specific ICode yet +# expr are the arguments +class INative +special ICodeN + # The native C code + # Special character sequence '@@@' will be substitued in order with the arguments + readable var _code: String + + init(c: String, e: nullable IndexedCollection[IRegister]) + do + super(e) + _code = c + end +end + +# A register assigment +# expr is the assigned value +# result is the register assigned +class IMove +special ICode1 + init(r: IRegister, e: IRegister) + do + super(e) + _result = r + end +end + +# An attribute read access +# expr is the reciever +class IAttrRead +special ICode1 + # The accessed attribute + readable var _property: MMAttribute + + init(p: MMAttribute, r: IRegister) + do + super(r) + _property = p + end +end + +# An attribute assignment +# expr1 is the receiver, expr2 is the assigned value +class IAttrWrite +special ICode2 + # The accessed attribute + readable var _property: MMAttribute + + init(p: MMAttribute, r: IRegister, v: IRegister) + do + super(r, v) + _property = p + end +end + + +# An attribute is_set check +# expr is the reciever +class IAttrIsset +special ICode1 + # The accessed attribute + readable var _property: MMAttribute + + init(p: MMAttribute, r: IRegister) + do + super(r) + _property = p + end +end + +# A type check +# expr is the expression checked +class ITypeCheck +special ICode1 + # The static type checkes to + readable var _stype: MMType + + init(e: IRegister, t: MMType) + do + super(e) + _stype = t + end +end + +# The 'is' operator +# expr1 and expr2 are both operands +class IIs +special ICode2 + init(e1, e2: IRegister) + do + super + end +end + +# The unary 'not' operation +# expr is the operand +class INot +special ICode1 + init(e: IRegister) + do + super + end +end + +# Evaluate body once them return the same value again and again +# if result is not null, then it must also be assigned in the body +class IOnce +special ICode0 + readable var _body: ISeq = new ISeq + init do end +end + +# Is a closure given as a parameter? +class IHasClos +special ICode0 + # The called closure + readable var _closure_decl: IClosureDecl + + init(c: IClosureDecl) + do + _closure_decl = c + end +end + +################################################# + +redef class MMAttribute + # The attached initialisation iroutine if any + # To call between the allocate-instance and the initialize-instance + fun iroutine: nullable IRoutine is abstract +end + +redef class MMMethod + # The attached body iroutine if any + fun iroutine: nullable IRoutine is abstract +end diff --git a/src/icode/icode_builder.nit b/src/icode/icode_builder.nit new file mode 100644 index 0000000..01cdde9 --- /dev/null +++ b/src/icode/icode_builder.nit @@ -0,0 +1,242 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2009 Jean Privat +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Help generation of icode +package icode_builder + +import icode_base + +# Helps to generate icodes in a iroutine +class ICodeBuilder + # Add a new statment in the current icode sequence + # Can be used with expression if the result is ignored of if the result is already set + fun stmt(s: ICode) + do + s.location = _current_location + _seq.icodes.add(s) + end + + # Add a new expression in the current icode sequence and return a new associated result register + fun expr(e: ICode, s: MMType): IRegister + do + stmt(e) + assert e.result == null + var reg = new_register(s) + e.result = reg + return reg + end + + # Add an assignement (IMove) in the current icode sequence + fun add_assignment(reg: IRegister, v: IRegister) + do + stmt(new IMove(reg, v)) + end + + # Check that the reciever e is not null (IIs + IAbort) + fun add_null_reciever_check(e: IRegister) + do + var nul = new_register(module.type_none) + var c = expr(new IIs(e, nul), module.type_bool) + var iif = new IIf(c) + stmt(iif) + var old_seq = seq + seq = iif.then_seq + add_abort("Reciever is null") + seq = old_seq + end + + # Add a type cast (ITypeCheck + IAbort) in the current icode sequence + fun add_type_cast(e: IRegister, stype: MMType) + do + var c = expr(new ITypeCheck(e, stype), module.type_bool) + var iif = new IIf(c) + stmt(iif) + var old_seq = seq + seq = iif.else_seq + add_abort("Cast failed") + seq = old_seq + end + + # Add an attr check (IAttrIsset + IAbort) in the current icode sequence + fun add_attr_check(prop: MMAttribute, e: IRegister) + do + if not prop.signature.return_type.is_nullable then + var cond = expr(new IAttrIsset(prop, e), module.type_bool) + var iif = new IIf(cond) + stmt(iif) + var seq_old = seq + seq = iif.else_seq + add_abort("Uninitialized attribute %s", prop.name.to_s) + seq = seq_old + end + end + + # Add an IAttrRead guarded by an add_attr_check in the current icode sequence + fun add_attr_read(prop: MMAttribute, e: IRegister): IRegister + do + add_attr_check(prop, e) + return expr(new IAttrRead(prop, e), prop.signature.return_type.as(not null)) + end + + # Add a localized IAbort + fun add_abort(s: String...) + do + stmt(new IAbort(s, method, module)) + end + + # Add an assigment to the iroutine return value + # Beware, no jump is generated + fun add_return_value(reg: IRegister) + do + add_assignment(iroutine.result.as(not null), reg) + end + + # Add an ICall with possible simple inlining in the current icode sequence + fun add_call(prop: MMMethod, args: Array[IRegister], closcns: nullable Array[nullable IClosureDef]): nullable IRegister + do + var ee = once "==".to_symbol + + # Inline "x!=y" as "not x==y" + var ne = once "!=".to_symbol + if prop.name == ne then + var eqp = prop.signature.recv.local_class.select_method(ee) + var eqcall = add_call(eqp, args, closcns).as(not null) + return expr(new INot(eqcall), module.type_bool) + end + + # TODO: Inline x==y as "x is y or (x != null and (== is not the Object one) and x.==(y))" + # inline "x==y" as "x is y or x != null and x.==(y)" + var icall = new ICall(prop, args) + icall.closure_defs = closcns + if prop.name == ee then + # Prepare the result + var reg = new_register(module.type_bool) + # "x is y" + var cond = expr(new IIs(args[0], args[1]), module.type_bool) + var iif = new IIf(cond) + stmt(iif) + var seq_old = seq + seq = iif.then_seq + add_assignment(reg, cond) + # "or" + seq = iif.else_seq + # Do the "x != null" part iff x is nullable + if args[0].stype.is_nullable then + var nul = new_register(module.type_none) + cond = expr(new IIs(args[0], nul), module.type_bool) + iif = new IIf(cond) + stmt(iif) + seq = iif.then_seq + add_assignment(reg, expr(new INative("TAG_Bool(false)", null), module.type_bool)) + seq = iif.else_seq + end + # "x.==(y)" + add_assignment(reg, expr(icall, module.type_bool)) + seq = seq_old + return reg + end + + var rtype = prop.signature.return_type + if rtype != null then + return expr(icall, rtype) + else + stmt(icall) + return null + end + end + + # Get a new register + fun new_register(s: MMType): IRegister + do + return new IRegister(s) + end + + # The module where classes and types are extracted + readable var _module: MMModule + + # The current iroutine build + readable var _iroutine: IRoutine + + # The current sequence of icodes + readable writable var _seq: ISeq + + # The method associated to the iroutine (if any) + readable var _method: nullable MMMethod + + init(module: MMModule, r: IRoutine, m: nullable MMMethod) + do + _module = module + _current_location = r.location + _iroutine = r + _seq = r.body + _method = m + end + + # New ICodes are set to this location + readable writable var _current_location: nullable Location = null +end + +redef class MMSignature + # Create an empty IRoutine that match the signature + fun generate_empty_iroutine: IRoutine + do + var args = new Array[IRegister] + args.add(new IRegister(recv)) # Self + for i in [0..arity[ do + args.add(new IRegister(self[i])) + end + var res: nullable IRegister = null + var rtype = return_type + if rtype != null then + res = new IRegister(rtype) + end + var iroutine = new IRoutine(args, res) + var clos: nullable Array[IClosureDecl] = null + if not closures.is_empty then + clos = new Array[IClosureDecl] + for c in closures do + clos.add(new IClosureDecl(c)) + end + iroutine.closure_decls = clos + end + return iroutine + end + + # Create an empty IClosureDef that match the signature + fun generate_empty_iclosuredef: IClosureDef + do + var args = new Array[IRegister] + for i in [0..arity[ do + args.add(new IRegister(self[i])) + end + var res: nullable IRegister = null + var rtype = return_type + if rtype != null then + res = new IRegister(rtype) + end + var iroutine = new IClosureDef(args, res) + var clos: nullable Array[IClosureDecl] = null + if not closures.is_empty then + clos = new Array[IClosureDecl] + for c in closures do + clos.add(new IClosureDecl(c)) + end + iroutine.closure_decls = clos + end + return iroutine + end +end + diff --git a/src/icode/icode_tools.nit b/src/icode/icode_tools.nit new file mode 100644 index 0000000..efdc0b7 --- /dev/null +++ b/src/icode/icode_tools.nit @@ -0,0 +1,328 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2009 Jean Privat +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Tools to manipulate intermediace nit code representation +import icode_base + +# A simple visitor to visit icode structures +class ICodeVisitor + # Called when a iregister is read in a icode + fun visit_iregister_read(ic: ICode, r: IRegister) do end + + # Called when a iregister is wrote in a icode + fun visit_iregister_write(ic: ICode, r: IRegister) do end + + # The current icode iterator. + # Can be used to insert_before, used to change the item or deleted + readable var _current_icode: nullable ListIterator[ICode] = null + + # Called when a icode is visited + # Automatically visits iregisters and sub-icodes + fun visit_icode(ic: nullable ICode) + do + if ic == null then return + if ic isa ISeq then + var old_icode = _current_icode + var cur = ic.icodes.iterator + while cur.is_ok do + _current_icode = cur + var ic2 = cur.item + visit_icode(ic2) + cur.next + end + _current_icode = old_icode + else if ic isa IIf then + visit_iregister_read(ic, ic.expr) + visit_icode(ic.then_seq) + visit_icode(ic.else_seq) + else if ic isa IOnce then + visit_icode(ic.body) + else if ic isa ICode1 then + visit_iregister_read(ic, ic.expr) + else if ic isa ICode2 then + visit_iregister_read(ic, ic.expr1) + visit_iregister_read(ic, ic.expr2) + else if ic isa ICodeN then + for e in ic.exprs do + visit_iregister_read(ic, e) + end + var closdefs = ic.closure_defs + if ic isa IClosCall then + visit_icode(ic.break_seq) + end + if closdefs != null then + visit_closure_defs(closdefs) + end + end + var r = ic.result + if r != null then visit_iregister_write(ic, r) + end + + # Called when closure definitions are visited + # Automatically visits each closure definition + fun visit_closure_defs(closdefs: Collection[nullable IClosureDef]) + do + for e in closdefs do + if e != null then + visit_iroutine(e) + end + end + end + + # Called when an iroutine is visited + # Automatically visits the body + # Warning: parameters of result registers are not visited + fun visit_iroutine(ir: IRoutine) + do + visit_icode(ir.body) + end +end + +redef class IRoutine + # Inline an iroutine in an icode sequence + fun inline_in_seq(seq: ISeq, args: IndexedCollection[IRegister]): nullable IRegister + do + var d = new ICodeDupContext + if args.length != params.length then print "{args.length} != {params.length}" + assert args.length == params.length + for i in [0..args.length[ do + # FIXME The following assumes that params are readonly. + # The alternative is safe but add one move :/ + d._registers[params[i]] = args[i] + #seq.icodes.add(new IMove(d.dup_ireg(params[i]), args[i])) + end + seq.icodes.add(body.dup_with(d)) + var r = result + if r != null then r = d.dup_ireg(r) + return r + end +end + +# This class stores reference to allow correct duplication of icodes +private class ICodeDupContext + # Duplicate one register + # Subsequent invocation will return the same register + fun dup_ireg(r: IRegister): IRegister + do + var rs = _registers + if rs.has_key(r) then + return rs[r] + else + var r2 = new IRegister(r.stype) + rs[r] = r2 + return r2 + end + end + + # Duplicate a bunch of registers + # Subsequent invocation will return the same registers + fun dup_iregs(regs: IndexedCollection[IRegister]): IndexedCollection[IRegister] + do + var a = new Array[IRegister].with_capacity(regs.length) + for r in regs do + a.add(dup_ireg(r)) + end + return a + end + + # The associoation between old_seq and new_seq + # Directly used by the IEscape + var _seqs: Map[ISeq, ISeq] = new HashMap[ISeq, ISeq] + + # The assocation between old_ireg and new_ireg + # Used by dup_ireg + var _registers: Map[IRegister, IRegister] = new HashMap[IRegister, IRegister] +end + +redef class ICode + # Duplicate the current icode (generic part) + private fun dup_with(d: ICodeDupContext): ICode + do + var c = inner_dup_with(d) + var r = result + if r != null then c.result = d.dup_ireg(r) + c.location = location + return c + end + + # Duplicate the current icode (specific part) + private fun inner_dup_with(d: ICodeDupContext): ICode is abstract +end + +redef class ISeq + redef fun inner_dup_with(d) + do + var c2 = new ISeq + dup_seq_to(d, c2) + return c2 + end + + # Duplicate each icode and store them in dest + # Note: dest must be empty and not modified afted duplication or IEscapes may be wrongly duplicated + private fun dup_seq_to(d: ICodeDupContext, dest: ISeq) + do + d._seqs[self] = dest + for c in icodes do + dest.icodes.add(c.dup_with(d)) + end + end +end + +redef class ILoop + redef fun inner_dup_with(d) + do + var c2 = new ILoop + dup_seq_to(d, c2) + return c2 + end +end + +redef class IIf + redef fun inner_dup_with(d) + do + var c2 = new IIf(d.dup_ireg(expr)) + then_seq.dup_seq_to(d, c2.then_seq) + else_seq.dup_seq_to(d, c2.else_seq) + return c2 + end +end + +redef class IEscape + redef fun inner_dup_with(d) + do + if d._seqs.has_key(seq) then + # Jump to a duplicated sequence + return new IEscape(d._seqs[seq]) + else + # Jump to an englobing unduplicated sequence + return new IEscape(seq) + end + end +end + +redef class IAbort + redef fun inner_dup_with(d) + do + return new IAbort(texts, property_location, module_location) + end +end + +redef class ICall + redef fun inner_dup_with(d) + do + return new ICall(property, d.dup_iregs(exprs)) + end +end + +redef class ISuper + redef fun inner_dup_with(d) + do + return new ISuper(property, d.dup_iregs(exprs)) + end +end + +redef class INew + redef fun inner_dup_with(d) + do + return new INew(stype, property, d.dup_iregs(exprs)) + end +end + +redef class IClosCall + redef fun inner_dup_with(d) + do + var c2 = new IClosCall(closure_decl, d.dup_iregs(exprs)) + var bs = break_seq + if bs != null then + var bs2 = new ISeq + c2.break_seq = bs2 + bs.dup_seq_to(d, bs2) + end + return c2 + end +end + +redef class INative + redef fun inner_dup_with(d) + do + return new INative(code, d.dup_iregs(exprs)) + end +end + +redef class IMove + redef fun inner_dup_with(d) + do + return new IMove(d.dup_ireg(result.as(not null)), d.dup_ireg(expr)) + end +end + +redef class IAttrRead + redef fun inner_dup_with(d) + do + return new IAttrRead(property, d.dup_ireg(expr)) + end +end + +redef class IAttrWrite + redef fun inner_dup_with(d) + do + return new IAttrWrite(property, d.dup_ireg(expr1), d.dup_ireg(expr2)) + end +end + +redef class IAttrIsset + redef fun inner_dup_with(d) + do + return new IAttrIsset(property, d.dup_ireg(expr)) + end +end + +redef class ITypeCheck + redef fun inner_dup_with(d) + do + return new ITypeCheck(d.dup_ireg(expr), stype) + end +end + +redef class IIs + redef fun inner_dup_with(d) + do + return new IIs(d.dup_ireg(expr1), d.dup_ireg(expr2)) + end +end + +redef class INot + redef fun inner_dup_with(d) + do + return new INot(d.dup_ireg(expr)) + end +end + +redef class IOnce + redef fun inner_dup_with(d) + do + var c2 = new IOnce + body.dup_seq_to(d, c2.body) + return c2 + end +end + +redef class IHasClos + redef fun inner_dup_with(d) + do + return new IHasClos(closure_decl) + end +end diff --git a/src/metamodel/static_type.nit b/src/metamodel/static_type.nit index fa9a4d8..541e3e5 100644 --- a/src/metamodel/static_type.nit +++ b/src/metamodel/static_type.nit @@ -484,4 +484,10 @@ end redef class MMModule # The type of null readable var _type_none: MMTypeNone = new MMTypeNone(self) + + # The type of bool + fun type_bool: MMType + do + return class_by_name(once ("Bool".to_symbol)).get_type + end end diff --git a/src/nitc.nit b/src/nitc.nit index 6e7657f..eef3c56 100644 --- a/src/nitc.nit +++ b/src/nitc.nit @@ -18,7 +18,9 @@ package nitc import abstracttool +import analysis private import compiling +private import syntax # The main class of the nitcompiler program class NitCompiler @@ -31,11 +33,12 @@ special AbstractCompiler readable var _opt_bindir: OptionString = new OptionString("NIT tools directory", "--bindir") readable var _opt_compdir: OptionString = new OptionString("Intermediate compilation directory", "--compdir") readable var _opt_extension_prefix: OptionString = new OptionString("Append prefix to file extension", "-p", "--extension-prefix") + readable var _opt_dump: OptionBool = new OptionBool("Dump intermediate code", "--dump") init do super("nitc") - option_context.add_option(opt_output, opt_boost, opt_no_cc, opt_global, opt_clibdir, opt_bindir, opt_compdir, opt_extension_prefix) + option_context.add_option(opt_output, opt_boost, opt_no_cc, opt_global, opt_clibdir, opt_bindir, opt_compdir, opt_extension_prefix, opt_dump) end redef fun process_options @@ -90,8 +93,36 @@ special AbstractCompiler end end + fun dump_intermediate_code(mods: Collection[MMModule]) + do + for mod in mods do + for c in mod.local_classes do + if not c isa MMConcreteClass then continue + for p in c.local_local_properties do + var routine: nullable IRoutine = null + if p isa MMAttribute then + routine = p.iroutine + else if p isa MMMethod then + routine = p.iroutine + end + if routine == null then continue + print "**** Property {p.full_name} ****" + var icd = new ICodeDumper + routine.dump(icd) + print "**** OPTIMIZE {p.full_name} ****" + routine.optimize + icd = new ICodeDumper + routine.dump(icd) + end + end + end + end + redef fun perform_work(mods) do + if opt_dump.value then + dump_intermediate_code(mods) + end for mod in mods do mod.compile_prog_to_c(self) end diff --git a/src/syntax/icode_generation.nit b/src/syntax/icode_generation.nit new file mode 100644 index 0000000..719eba8 --- /dev/null +++ b/src/syntax/icode_generation.nit @@ -0,0 +1,1403 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2009 Jean Privat +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Things needed by typing.nit to generate intermediate code from AST +package icode_generation + +import icode +import syntax_base +private import typing +private import primitive_info + +# An AST2ICode context stores the currently built icode informations +class A2IContext +special ICodeBuilder + redef fun stmt(s: ICode) + do + if _current_node != null then + current_location = _current_node.location + else if visitor.current_node != null then + current_location = visitor.current_node.location + end + super + end + + # Prepare a new array of length item + fun add_new_array(stype: MMType, length: Int): IRegister + do + var prop = visitor.get_method(stype, once "with_capacity".to_symbol) + var ni = expr(new INative("TAG_Int({length})", null), visitor.type_int) + return expr(new INew(stype, prop, [ni]), stype) + end + + # Add an array add + fun add_call_array_add(recv, item: IRegister) + do + var stype = recv.stype + var prop = visitor.get_method(stype, once "add".to_symbol) + stmt(new ICall(prop, [recv, item])) + end + + # Get the iregister associated with a variable + # Or assign one if none exists + fun variable(v: Variable): IRegister + do + if _variables.has_key(v) then + return _variables[v] + else + var reg = new_register(v.stype.as(not null)) + _variables[v] = reg + return reg + end + end + + # Current registered variable + var _variables: HashMap[Variable, IRegister] = new HashMap[Variable, IRegister] + + # Current registered closurevariables + readable var _closurevariables: HashMap[ClosureVariable, IClosureDecl] = new HashMap[ClosureVariable, IClosureDecl] + + # The current syntax visitor + readable var _visitor: AbsSyntaxVisitor + + # Where a nit return must branch + readable writable var _return_seq: nullable ISeq + + # Register where a functionnal nit return must store its value + readable writable var _return_value: nullable IRegister + + init(visitor: AbsSyntaxVisitor, r: IRoutine, m: nullable MMMethod) + do + super(visitor.module, r, m) + _visitor = visitor + _return_seq = r.body + _return_value = r.result + end + + # Insert implicit super init calls + fun invoke_super_init_calls_after(start_prop: nullable MMMethod) + do + var p = method + assert p isa MMSrcMethod + var n = p.node + assert n isa AConcreteInitPropdef + + if n.super_init_calls.is_empty then return + var i = 0 + var j = 0 + if start_prop != null then + while n.super_init_calls[i] != start_prop do + i += 1 + end + i += 1 + + while n.explicit_super_init_calls[j] != start_prop do + j += 1 + end + j += 1 + end + var stop_prop: nullable MMMethod = null + if j < n.explicit_super_init_calls.length then + stop_prop = n.explicit_super_init_calls[j] + end + var l = n.super_init_calls.length + while i < l do + var p = n.super_init_calls[i] + if p == stop_prop then break + var cargs = new Array[IRegister] + if p.signature.arity == 0 then + cargs.add(iroutine.params.first) + else + for va in iroutine.params do + cargs.add(va) + end + end + stmt(new ICall(p, cargs)) + i += 1 + end + end + + # The current PExpr + var _current_node: nullable PExpr = null + + # Generate icode in the current sequence from a statement + fun generate_stmt(n: nullable PExpr) + do + if n == null then return + var old = _current_node + _current_node = n + n.generate_icode(self) + _current_node = old + end + + # Generate icode in the current sequence from an expression + fun generate_expr(n: PExpr): IRegister + do + var old = _current_node + _current_node = n + var reg = n.generate_icode(self).as(not null) + _current_node = old + return reg + end +end + +redef class EscapableBlock + # Where a nit break must branch + readable writable var _break_seq: nullable ISeq + + # Where a nit continue must branch + readable writable var _continue_seq: nullable ISeq + + # Register where a functionnal nit break must store its value + readable writable var _break_value: nullable IRegister + + # Register where a functionnal nit continue must store its value + readable writable var _continue_value: nullable IRegister +end + +redef class MMSrcModule + # Generate icode for method bodies + fun generate_icode(tc: ToolContext) + do + var v = new A2IVisitor(tc, self) + for c in src_local_classes do + for p in c.src_local_properties do + if p isa MMSrcMethod then + p.generate_iroutine(v) + else if p isa MMSrcAttribute then + p.generate_iroutine(v) + end + end + end + end +end + +redef class MMSrcAttribute + redef readable writable var _iroutine: nullable IRoutine + + # Generate the initialization iroutine + fun generate_iroutine(visitor: A2IVisitor) + do + if node.n_expr != null then + var iroutine = signature.generate_empty_iroutine + iroutine.location = node.location + var v = new A2IContext(visitor, iroutine, null) + visitor.icode_ctx = v + visitor.enter_visit(node) + visitor.icode_ctx = null + _iroutine = iroutine + end + end +end + +redef class MMSrcMethod + redef readable writable var _iroutine: nullable IRoutine + + # Generate the body iroutine + fun generate_iroutine(visitor: A2IVisitor) + do + var iroutine = signature.generate_empty_iroutine + if node != null then + iroutine.location = node.location + end + var v = new A2IContext(visitor, iroutine, self) + visitor.icode_ctx = v + inner_generate_iroutine(v) + visitor.icode_ctx = null + _iroutine = iroutine + end + + # Generate the body iroutine (specific part) + fun inner_generate_iroutine(v: A2IContext) is abstract +end + +redef class MMReadImplementationMethod + redef fun inner_generate_iroutine(v) + do + var e = v.add_attr_read(node.prop, v.iroutine.params.first) + v.add_return_value(e) + end +end + +redef class MMWriteImplementationMethod + redef fun inner_generate_iroutine(v) + do + var params = v.iroutine.params + v.stmt(new IAttrWrite(node.prop, params[0], params[1])) + end +end + +redef class MMMethSrcMethod + redef fun inner_generate_iroutine(v) + do + v.visitor.enter_visit(node) + end +end + +redef class MMImplicitInit + redef fun inner_generate_iroutine(v) + do + var params = v.iroutine.params + var f = params.length - unassigned_attributes.length + var recv = params.first + for sp in super_inits do + assert sp isa MMMethod + var args_recv = [recv] + if sp == super_init then + var args = new Array[IRegister].with_capacity(f) + args.add(recv) + for i in [1..f[ do + args.add(params[i]) + end + v.stmt(new ICall(sp, args)) + else + v.stmt(new ICall(sp, args_recv)) + end + end + for i in [f..params.length[ do + var attribute = unassigned_attributes[i-f] + v.stmt(new IAttrWrite(attribute, recv, params[i])) + end + end +end + +class A2IVisitor +special AbsSyntaxVisitor + writable var _icode_ctx: nullable A2IContext + fun icode_ctx: A2IContext do return _icode_ctx.as(not null) + redef fun visit(n) do n.accept_icode_generation(self) + init(tc, m) do super +end + + +############################################################################### + +redef class PNode + fun accept_icode_generation(v: A2IVisitor) do accept_abs_syntax_visitor(v) end +end + +redef class AAttrPropdef + redef fun accept_icode_generation(vv) + do + var v = vv.icode_ctx + v.stmt(new IMove(v.variable(self_var), v.iroutine.params.first)) + super + var ne = n_expr + if ne != null then + v.stmt(new IMove(v.iroutine.result.as(not null), v.generate_expr(ne))) + end + end +end + +redef class AMethPropdef + redef fun accept_icode_generation(vv) + do + super + fill_iroutine(vv.icode_ctx, method) + end + + # Compile the method body common preambule (before specific body stuff if any) + fun fill_iroutine(v: A2IContext, method: MMSrcMethod) is abstract +end + +redef class PSignature + fun fill_iroutine_parameters(v: A2IContext, orig_sig: MMSignature, params: IndexedCollection[IRegister], closdecls: nullable IndexedCollection[IClosureDecl]) is abstract +end + +redef class ASignature + redef fun fill_iroutine_parameters(v: A2IContext, orig_sig: MMSignature, params: IndexedCollection[IRegister], closdecls: nullable IndexedCollection[IClosureDecl]) + do + for ap in n_params do + var reg = v.variable(ap.variable) + var orig_type = orig_sig[ap.position] + var apst = ap.variable.stype.as(not null) + if not orig_type < apst then + v.add_type_cast(params[ap.position], apst) + end + v.stmt(new IMove(reg, params[ap.position])) + end + for i in [0..n_closure_decls.length[ do + var wd = n_closure_decls[i] + v.closurevariables[wd.variable] = closdecls[i] + end + end +end + +redef class AClosureDecl + redef fun accept_icode_generation(vv) + do + var v = vv.icode_ctx + var iclos = variable.closure.signature.generate_empty_iclosuredef + var old_seq = v.seq + v.seq = iclos.body + escapable.continue_seq = iclos.body + escapable.continue_value = iclos.result + n_signature.fill_iroutine_parameters(v, variable.closure.signature, iclos.params, null) + + if n_expr != null then + v.generate_stmt(n_expr) + v.iroutine.closure_decls[position].default = iclos + end + v.seq = old_seq + end +end + +redef class AConcreteMethPropdef + redef fun fill_iroutine(v, method) + do + var params = v.iroutine.params.to_a + var selfreg = v.variable(self_var) + v.stmt(new IMove(selfreg, params[0])) + params.shift + + var orig_meth: MMLocalProperty = method.global.intro + var orig_sig = orig_meth.signature_for(method.signature.recv) + if n_signature != null then + n_signature.fill_iroutine_parameters(v, orig_sig, params, v.iroutine.closure_decls) + end + + if self isa AConcreteInitPropdef then + v.invoke_super_init_calls_after(null) + end + + if n_block != null then + v.generate_stmt(n_block) + end + end +end + +redef class ADeferredMethPropdef + redef fun fill_iroutine(v, method) + do + v.add_abort("Deferred method called") + end +end + +redef class AExternMethPropdef + redef fun fill_iroutine(v, method) + do + var params = v.iroutine.params + var ename = "{method.module.name}_{method.local_class.name}_{method.local_class.name}_{method.name}_{method.signature.arity}" + if n_extern != null then + ename = n_extern.text + ename = ename.substring(1, ename.length-2) + end + var sig = method.signature + assert params.length == sig.arity + 1 + var args = new Array[String] + args.add(sig.recv.unboxtype("@@@")) + for i in [0..sig.arity[ do + args.add(sig[i].unboxtype("@@@")) + end + var s = "{ename}({args.join(", ")})" + var rtype = sig.return_type + if rtype != null then + s = rtype.boxtype(s) + v.add_return_value(v.expr(new INative(s, params), rtype)) + else + v.stmt(new INative(s, params)) + end + end +end + +redef class AInternMethPropdef + redef fun fill_iroutine(v, method) + do + var p = v.iroutine.params.to_a + var c = method.local_class.name + var n = method.name + var s: nullable String = null + if c == once "Int".to_symbol then + if n == once "object_id".to_symbol then + s = "@@@" + else if n == once "unary -".to_symbol then + s = "TAG_Int(-UNTAG_Int(@@@))" + else if n == once "output".to_symbol then + s = "printf(\"%ld\\n\", UNTAG_Int(@@@));" + else if n == once "ascii".to_symbol then + s = "TAG_Char(UNTAG_Int(@@@))" + else if n == once "succ".to_symbol then + s = "TAG_Int(UNTAG_Int(@@@)+1)" + else if n == once "prec".to_symbol then + s = "TAG_Int(UNTAG_Int(@@@)-1)" + else if n == once "to_f".to_symbol then + s = "BOX_Float((float)UNTAG_Int(@@@))" + else if n == once "+".to_symbol then + s = "TAG_Int(UNTAG_Int(@@@)+UNTAG_Int(@@@))" + else if n == once "-".to_symbol then + s = "TAG_Int(UNTAG_Int(@@@)-UNTAG_Int(@@@))" + else if n == once "*".to_symbol then + s = "TAG_Int(UNTAG_Int(@@@)*UNTAG_Int(@@@))" + else if n == once "/".to_symbol then + s = "TAG_Int(UNTAG_Int(@@@)/UNTAG_Int(@@@))" + else if n == once "%".to_symbol then + s = "TAG_Int(UNTAG_Int(@@@)%UNTAG_Int(@@@))" + else if n == once "<".to_symbol then + s = "TAG_Bool(UNTAG_Int(@@@)".to_symbol then + s = "TAG_Bool(UNTAG_Int(@@@)>UNTAG_Int(@@@))" + else if n == once "<=".to_symbol then + s = "TAG_Bool(UNTAG_Int(@@@)<=UNTAG_Int(@@@))" + else if n == once ">=".to_symbol then + s = "TAG_Bool(UNTAG_Int(@@@)>=UNTAG_Int(@@@))" + else if n == once "lshift".to_symbol then + s = "TAG_Int(UNTAG_Int(@@@)<".to_symbol then + s = "TAG_Bool(UNBOX_Float(@@@)>UNBOX_Float(@@@))" + else if n == once "<=".to_symbol then + s = "TAG_Bool(UNBOX_Float(@@@)<=UNBOX_Float(@@@))" + else if n == once ">=".to_symbol then + s = "TAG_Bool(UNBOX_Float(@@@)>=UNBOX_Float(@@@))" + end + else if c == once "Char".to_symbol then + if n == once "object_id".to_symbol then + s = "TAG_Int(UNTAG_Char(@@@))" + else if n == once "unary -".to_symbol then + s = "TAG_Char(-UNTAG_Char(@@@))" + else if n == once "output".to_symbol then + s = "printf(\"%c\", (unsigned char)UNTAG_Char(@@@));" + else if n == once "ascii".to_symbol then + s = "TAG_Int((unsigned char)UNTAG_Char(@@@))" + else if n == once "succ".to_symbol then + s = "TAG_Char(UNTAG_Char(@@@)+1)" + else if n == once "prec".to_symbol then + s = "TAG_Char(UNTAG_Char(@@@)-1)" + else if n == once "to_i".to_symbol then + s = "TAG_Int(UNTAG_Char(@@@)-'0')" + else if n == once "+".to_symbol then + s = "TAG_Char(UNTAG_Char(@@@)+UNTAG_Char(@@@))" + else if n == once "-".to_symbol then + s = "TAG_Char(UNTAG_Char(@@@)-UNTAG_Char(@@@))" + else if n == once "*".to_symbol then + s = "TAG_Char(UNTAG_Char(@@@)*UNTAG_Char(@@@))" + else if n == once "/".to_symbol then + s = "TAG_Char(UNTAG_Char(@@@)/UNTAG_Char(@@@))" + else if n == once "%".to_symbol then + s = "TAG_Char(UNTAG_Char(@@@)%UNTAG_Char(@@@))" + else if n == once "<".to_symbol then + s = "TAG_Bool(UNTAG_Char(@@@)".to_symbol then + s = "TAG_Bool(UNTAG_Char(@@@)>UNTAG_Char(@@@))" + else if n == once "<=".to_symbol then + s = "TAG_Bool(UNTAG_Char(@@@)<=UNTAG_Char(@@@))" + else if n == once ">=".to_symbol then + s = "TAG_Bool(UNTAG_Char(@@@)>=UNTAG_Char(@@@))" + else if n == once "==".to_symbol then + s = "TAG_Bool((@@@)==(@@@))" + else if n == once "!=".to_symbol then + s = "TAG_Bool((@@@)!=(@@@))" + end + else if c == once "Bool".to_symbol then + if n == once "object_id".to_symbol then + s = "TAG_Int(UNTAG_Bool(@@@))" + else if n == once "unary -".to_symbol then + s = "TAG_Bool(-UNTAG_Bool(@@@))" + else if n == once "output".to_symbol then + s = "(void)printf(UNTAG_Bool(@@@)?\"true\\n\":\"false\\n\");" + else if n == once "ascii".to_symbol then + s = "TAG_Bool(UNTAG_Bool(@@@))" + else if n == once "to_i".to_symbol then + s = "TAG_Int(UNTAG_Bool(@@@))" + else if n == once "==".to_symbol then + s = "TAG_Bool((@@@)==(@@@))" + else if n == once "!=".to_symbol then + s = "TAG_Bool((@@@)!=(@@@))" + end + else if c == once "NativeArray".to_symbol then + if n == once "object_id".to_symbol then + s = "TAG_Int(UNBOX_NativeArray(@@@))" + else if n == once "[]".to_symbol then + s = "UNBOX_NativeArray(@@@)[UNTAG_Int(@@@)]" + else if n == once "[]=".to_symbol then + s = "UNBOX_NativeArray(@@@)[UNTAG_Int(@@@)]=@@@;" + else if n == once "copy_to".to_symbol then + var t = p[0] + p[0] = p[1] + p[1] = t + s = "(void)memcpy(UNBOX_NativeArray(@@@), UNBOX_NativeArray(@@@), UNTAG_Int(@@@)*sizeof(val_t));" + end + else if c == once "NativeString".to_symbol then + if n == once "object_id".to_symbol then + s = "TAG_Int(UNBOX_NativeString(@@@))" + else if n == once "atoi".to_symbol then + s = "TAG_Int(atoi(UNBOX_NativeString(@@@)))" + else if n == once "[]".to_symbol then + s = "TAG_Char(UNBOX_NativeString(@@@)[UNTAG_Int(@@@)])" + else if n == once "[]=".to_symbol then + s = "UNBOX_NativeString(@@@)[UNTAG_Int(@@@)]=UNTAG_Char(@@@);" + else if n == once "copy_to".to_symbol then + var t = p[0] + p[0] = p[1] + p[1] = p[4] + p[4] = p[2] + p[2] = t + s = "(void)memcpy(UNBOX_NativeString(@@@)+UNTAG_Int(@@@), UNBOX_NativeString(@@@)+UNTAG_Int(@@@), UNTAG_Int(@@@));" + end + else if n == once "object_id".to_symbol then + s = "TAG_Int((bigint)@@@)" + else if n == once "sys".to_symbol then + s = "(G_sys)" + else if n == once "is_same_type".to_symbol then + s = "TAG_Bool((VAL2VFT(@@@)==VAL2VFT(@@@)))" + else if n == once "exit".to_symbol then + p[0] = p[1] + s = "exit(UNTAG_Int(@@@));" + else if n == once "calloc_array".to_symbol then + p[0] = p[1] + s = "BOX_NativeArray((val_t*)malloc((UNTAG_Int(@@@) * sizeof(val_t))))" + else if n == once "calloc_string".to_symbol then + p[0] = p[1] + s = "BOX_NativeString((char*)malloc((UNTAG_Int(@@@) * sizeof(char))))" + end + if s == null then + v.visitor.error(self, "Fatal error: unknown intern method {method.full_name}.") + s = "NIT_NULL" + end + var rtype = method.signature.return_type + if rtype != null then + v.add_return_value(v.expr(new INative(s, p), rtype)) + else + v.stmt(new INative(s, p)) + end + end +end + +############################################################################### + +redef class PExpr + redef fun accept_icode_generation(v) do end + + # Generate icode sequence in the current A2IContext + # This method should not be called direclty: use generate_expr and generate_stmt from A2IContext instead + protected fun generate_icode(v: A2IContext): nullable IRegister is abstract +end + +redef class AVardeclExpr + redef fun generate_icode(v) + do + var reg = v.variable(variable) + var ne = n_expr + if ne != null then + v.add_assignment(reg, v.generate_expr(ne)) + end + return null + end +end + +redef class ABlockExpr + redef fun generate_icode(v) + do + for ne in n_expr do v.generate_stmt(ne) + return null + end +end + +redef class ADoExpr + redef fun generate_icode(v) + do + v.generate_stmt(n_block) + return null + end +end + +redef class AReturnExpr + redef fun generate_icode(v) + do + var ne = n_expr + if ne != null then + v.add_assignment(v.return_value.as(not null), v.generate_expr(ne)) + end + v.stmt(new IEscape(v.return_seq.as(not null))) + return null + end +end + +redef class ABreakExpr + redef fun generate_icode(v) + do + var ne = n_expr + if ne != null then + v.add_assignment(escapable.break_value.as(not null), v.generate_expr(ne)) + end + v.stmt(new IEscape(escapable.break_seq.as(not null))) + return null + end +end + +redef class AContinueExpr + redef fun generate_icode(v) + do + var ne = n_expr + if ne != null then + v.add_assignment(escapable.continue_value.as(not null), v.generate_expr(ne)) + end + v.stmt(new IEscape(escapable.continue_seq.as(not null))) + return null + end +end + +redef class AAbortExpr + redef fun generate_icode(v) + do + v.add_abort("Aborted") + return null + end +end + +redef class AIfExpr + redef fun generate_icode(v) + do + var iif = new IIf(v.generate_expr(n_expr)) + v.stmt(iif) + var seq_old = v.seq + + if n_then != null then + v.seq = iif.then_seq + v.generate_stmt(n_then) + end + + if n_else != null then + v.seq = iif.else_seq + v.generate_stmt(n_else) + end + + v.seq = seq_old + return null + end +end + +redef class AWhileExpr + redef fun generate_icode(v) + do + var seq_old = v.seq + var iloop = new ILoop + v.stmt(iloop) + escapable.break_seq = iloop + v.seq = iloop + + # Process condition + var iif = new IIf(v.generate_expr(n_expr)) + v.stmt(iif) + + # Process inside (condition is true) + if n_block != null then + v.seq = iif.then_seq + escapable.continue_seq = iif.then_seq + v.generate_stmt(n_block) + end + + # Process escape (condition is false) + v.seq = iif.else_seq + v.stmt(new IEscape(iloop)) + + v.seq = seq_old + return null + end +end + +redef class AForExpr + redef fun generate_icode(v) + do + var expr_type = n_expr.stype + + # Get iterator + var meth_iterator = v.visitor.get_method(expr_type, once "iterator".to_symbol) + + var iter_type = meth_iterator.signature_for(expr_type).return_type.as(not null) + var ireg_iter = v.expr(new ICall(meth_iterator, [v.generate_expr(n_expr)]), iter_type) + + # Enter loop + var seq_old = v.seq + var iloop = new ILoop + v.stmt(iloop) + escapable.break_seq = iloop + v.seq = iloop + + # Condition evaluation + var meth_is_ok = v.visitor.get_method(iter_type, once ("is_ok".to_symbol)) + var ireg_isok = v.expr(new ICall(meth_is_ok, [ireg_iter]), v.visitor.type_bool) + var iif = new IIf(ireg_isok) + + # Process insite the loop (condition is true) + v.stmt(iif) + v.seq = iif.then_seq + escapable.continue_seq = iif.then_seq + + # Automatic variable assignment + var meth_item = v.visitor.get_method(iter_type, once ("item".to_symbol)) + var va_stype = variable.stype.as(not null) + var ireg_item = v.expr(new ICall(meth_item, [ireg_iter]), va_stype) + var ireg_va = v.variable(variable) + v.add_assignment(ireg_va, ireg_item) + + # Body evaluation + v.generate_stmt(n_block) + + # Exit contition (condition is false) + v.seq = iif.else_seq + v.stmt(new IEscape(iloop)) + + # Next step + var meth_next = v.visitor.get_method(iter_type, once ("next".to_symbol)) + v.seq = iloop + v.stmt(new ICall(meth_next, [ireg_iter])) + + v.seq = seq_old + return null + end +end + +redef class AAssertExpr + redef fun generate_icode(v) + do + var e = v.generate_expr(n_expr) + var iif = new IIf(e) + v.stmt(iif) + var seq_old = v.seq + v.seq = iif.else_seq + var id = n_id + if id == null then + v.add_abort("Assert failed") + else + v.add_abort("Assert %s failed", id.to_s) + end + v.seq = seq_old + return null + end +end + +redef class AVarExpr + redef fun generate_icode(v) + do + return v.variable(variable) + end +end + +redef class AVarAssignExpr + redef fun generate_icode(v) + do + var e = v.generate_expr(n_value) + v.add_assignment(v.variable(variable), e) + return null + end +end + +redef class AVarReassignExpr + redef fun generate_icode(v) + do + var e1 = v.variable(variable) + var e2 = v.generate_expr(n_value) + var e3 = v.expr(new ICall(assign_method, [e1, e2]), assign_method.signature.return_type.as(not null)) + v.add_assignment(e1, e3) + return null + end +end + +redef class ASelfExpr + redef fun generate_icode(v) + do + return v.variable(variable) + end +end + +redef class AIfexprExpr + redef fun generate_icode(v) + do + # Process condition + var iif = new IIf(v.generate_expr(n_expr)) + v.stmt(iif) + var seq_old = v.seq + + # Prepare result + var reg = v.new_register(stype) + + # Process 'then' + v.seq = iif.then_seq + v.add_assignment(reg, v.generate_expr(n_then)) + + # Process 'else' + v.seq = iif.else_seq + v.add_assignment(reg, v.generate_expr(n_else)) + + v.seq = seq_old + return reg + end +end + +redef class AEeExpr + redef fun generate_icode(v) + do + var e = v.generate_expr(n_expr) + var e2 = v.generate_expr(n_expr2) + return v.expr(new IIs(e, e2), stype) + end +end + +redef class AOrExpr + redef fun generate_icode(v) + do + # Prepare result + var reg = v.new_register(stype) + + # Process left operand (in a if/then) + var iif = new IIf(v.generate_expr(n_expr)) + v.stmt(iif) + var seq_old = v.seq + v.seq = iif.then_seq + v.add_assignment(reg, v.generate_expr(n_expr)) + + # Process right operand (in the else) + v.seq = iif.else_seq + v.add_assignment(reg, v.generate_expr(n_expr2)) + + v.seq = seq_old + return reg + end +end + +redef class AAndExpr + redef fun generate_icode(v) + do + # Prepare result + var reg = v.new_register(stype) + + # Process left operand (in a if/else) + var iif = new IIf(v.generate_expr(n_expr)) + v.stmt(iif) + var seq_old = v.seq + v.seq = iif.else_seq + v.add_assignment(reg, v.generate_expr(n_expr)) + + # Process right operand (in the then) + v.seq = iif.then_seq + v.add_assignment(reg, v.generate_expr(n_expr2)) + + v.seq = seq_old + return reg + end +end + +redef class ANotExpr + redef fun generate_icode(v) + do + var e = v.generate_expr(n_expr) + return v.expr(new INot(e), stype) + end +end + +redef class AIsaExpr + redef fun generate_icode(v) + do + var e = v.generate_expr(n_expr) + return v.expr(new ITypeCheck(e, n_type.stype), stype) + end +end + +redef class AAsCastExpr + redef fun generate_icode(v) + do + var e = v.generate_expr(n_expr) + v.add_type_cast(e, stype) + return e + end +end + +redef class AAsNotnullExpr + redef fun generate_icode(v) + do + var e = v.generate_expr(n_expr) + v.add_type_cast(e, stype) + return e + end +end + +redef class ATrueExpr + redef fun generate_icode(v) + do + return v.expr(new INative("TAG_Bool(true)", null), stype) + end +end + +redef class AFalseExpr + redef fun generate_icode(v) + do + return v.expr(new INative("TAG_Bool(false)", null), stype) + end +end + +redef class AIntExpr + redef fun generate_icode(v) + do + return v.expr(new INative("TAG_Int({n_number.text})", null), stype) + end +end + +redef class AFloatExpr + redef fun generate_icode(v) + do + return v.expr(new INative("BOX_Float({n_float.text})", null), stype) + end +end + +redef class ACharExpr + redef fun generate_icode(v) + do + return v.expr(new INative("TAG_Char({n_char.text})", null), stype) + end +end + +redef class AStringFormExpr + redef fun generate_icode(v) + do + compute_string_infos + var old_seq = v.seq + var ionce = new IOnce + var reg = v.expr(ionce, stype) + v.seq = ionce.body + var ns = v.expr(new INative("BOX_NativeString(\"{_cstring}\")", null), v.visitor.type_nativestring) + var ni = v.expr(new INative("TAG_Int({_cstring_length})", null), v.visitor.type_int) + var prop = v.visitor.get_method(stype, once "with_native".to_symbol) + var e = v.expr(new INew(stype, prop, [ns, ni]), stype) + v.add_assignment(reg, e) + v.seq = old_seq + return reg + end + + # The raw string value + protected fun string_text: String is abstract + + # The string in a C native format + protected var _cstring: nullable String + + # The string length in bytes + protected var _cstring_length: nullable Int + + # Compute _cstring and _cstring_length using string_text + protected fun compute_string_infos + do + var len = 0 + var str = string_text + var res = new Buffer + var i = 0 + while i < str.length do + var c = str[i] + if c == '\\' then + i = i + 1 + var c2 = str[i] + if c2 != '{' and c2 != '}' then + res.add(c) + end + c = c2 + end + len = len + 1 + res.add(c) + i = i + 1 + end + _cstring = res.to_s + _cstring_length = len + end +end + +redef class AStringExpr + redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2) +end +redef class AStartStringExpr + redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2) +end +redef class AMidStringExpr + redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2) +end +redef class AEndStringExpr + redef fun string_text do return n_string.text.substring(1, n_string.text.length - 2) +end + +redef class ASuperstringExpr + redef fun generate_icode(v) + do + var array = v.add_new_array(atype, n_exprs.length) + var prop_to_s = v.visitor.get_method(v.visitor.type_object, once "to_s".to_symbol) + for ne in n_exprs do + var e = v.generate_expr(ne) + if ne.stype != stype then + e = v.expr(new ICall(prop_to_s, [e]), stype) + end + v.add_call_array_add(array, e) + end + return v.expr(new ICall(prop_to_s, [array]), stype) + end +end + +redef class ANullExpr + redef fun generate_icode(v) + do + return v.new_register(stype) + end +end + +redef class AArrayExpr + redef fun generate_icode(v) + do + var recv = v.add_new_array(stype, n_exprs.length) + for ne in n_exprs do + var e = v.generate_expr(ne) + v.add_call_array_add(recv, e) + end + return recv + end +end + +redef class ACrangeExpr + redef fun generate_icode(v) + do + var e = v.generate_expr(n_expr) + var e2 = v.generate_expr(n_expr2) + var prop = v.visitor.get_method(stype, once "init".to_symbol) + return v.expr(new INew(stype, prop, [e, e2]), stype) + end +end + +redef class AOrangeExpr + redef fun generate_icode(v) + do + var e = v.generate_expr(n_expr) + var e2 = v.generate_expr(n_expr2) + var prop = v.visitor.get_method(stype, once "without_last".to_symbol) + return v.expr(new INew(stype, prop, [e, e2]), stype) + end +end + +redef class ASuperExpr + redef fun generate_icode(v) + do + var arity = v.iroutine.params.length - 1 + if init_in_superclass != null then + arity = init_in_superclass.signature.arity + end + var args = new Array[IRegister].with_capacity(arity + 1) + args.add(v.iroutine.params[0]) + if n_args.length != arity then + for i in [0..arity[ do + args.add(v.iroutine.params[i + 1]) + end + else + for na in n_args do + args.add(v.generate_expr(na)) + end + end + var p = init_in_superclass + if p != null then + var rtype = p.signature.return_type + if rtype != null then + return v.expr(new ICall(p, args), rtype) + else + v.stmt(new ICall(p, args)) + return null + end + else + var p = prop + var rtype = p.signature.return_type + if rtype == null then + v.stmt(new ISuper(p, args)) + return null + else + return v.expr(new ISuper(p, args), rtype) + end + end + end +end + +redef class AAttrExpr + redef fun generate_icode(v) + do + var e = v.generate_expr(n_expr) + return v.add_attr_read(prop, e) + end +end + +redef class AAttrAssignExpr + redef fun generate_icode(v) + do + var e = v.generate_expr(n_expr) + var e2 = v.generate_expr(n_value) + v.stmt(new IAttrWrite(prop, e, e2)) + return null + end +end +redef class AAttrReassignExpr + redef fun generate_icode(v) + do + var e1 = v.generate_expr(n_expr) + var e2 = v.expr(new IAttrRead(prop, e1), attr_type) + var e3 = v.generate_expr(n_value) + var e4 = v.expr(new ICall(assign_method, [e2, e3]), attr_type) + v.stmt(new IAttrWrite(prop, e1, e4)) + return null + end +end + +redef class AIssetAttrExpr + redef fun generate_icode(v) + do + var e = v.generate_expr(n_expr) + return v.expr(new IAttrIsset(prop, e), stype) + end +end + +redef class AAbsAbsSendExpr + # Compile each argument and add them to the array + fun generate_icode_for_arguments_in(v: A2IContext, args: Array[IRegister]) + do + for a in arguments do + args.add(v.generate_expr(a)) + end + end +end + +redef class ASendExpr + redef fun generate_icode(v) + do + var recv = v.generate_expr(n_expr) + var args = new Array[IRegister] + args.add(recv) + generate_icode_for_arguments_in(v, args) + var prop = prop + var r: nullable IRegister = null # The full result of the send (raw call + breaks) + var r2: nullable IRegister # The raw result of the call + + # Prepare closures + var seq_old = v.seq + var closcns: nullable Array[nullable IClosureDef] = null + if not prop_signature.closures.is_empty then + var rtype = prop_signature.return_type + if rtype != null then + r = v.new_register(rtype) + end + var seq = new ISeq + v.stmt(seq) + v.seq = seq + closcns = new Array[nullable IClosureDef] + var cdarity = 0 + if closure_defs != null then cdarity = closure_defs.length + for i in [0..cdarity[ do + closure_defs[i].escapable.break_seq = seq + closure_defs[i].escapable.break_value = r + var cn = closure_defs[i].generate_iclosuredef(v) + closcns.add(cn) + end + for i in [cdarity..prop_signature.closures.length[ do + closcns.add(null) + end + end + + r2 = v.add_call(prop, args, closcns) + + # Closure work + if not prop_signature.closures.is_empty then + if r != null and r2 != null then v.add_assignment(r, r2) + v.seq = seq_old + else + r = r2 + end + + if prop.global.is_init then + v.invoke_super_init_calls_after(prop) + end + return r + end +end + +redef class ASendReassignExpr + redef fun generate_icode(v) + do + var recv = v.generate_expr(n_expr) + var args = new Array[IRegister] + args.add(recv) + generate_icode_for_arguments_in(v, args) + + var e2 = v.expr(new ICall(read_prop, args), read_prop.signature.return_type.as(not null)) + var e3 = v.generate_expr(n_value) + var e4 = v.expr(new ICall(assign_method, [e2, e3]), assign_method.signature.return_type.as(not null)) + var args2 = args.to_a + args2.add(e4) + v.stmt(new ICall(prop, args2)) + return null + end +end + +redef class ANewExpr + redef fun generate_icode(v) + do + var args = new Array[IRegister] + generate_icode_for_arguments_in(v, args) + return v.expr(new INew(stype, prop, args), stype) + end +end + +redef class AProxyExpr + redef fun generate_icode(v) + do + return v.generate_expr(n_expr) + end +end + +redef class AOnceExpr + redef fun generate_icode(v) + do + var ionce = new IOnce + var reg = v.expr(ionce, stype) + var old_seq = v.seq + v.seq = ionce.body + + var e = v.generate_expr(n_expr) + v.add_assignment(reg, e) + + v.seq = old_seq + return reg + end +end + + +redef class PClosureDef + var _iclosure_def: nullable IClosureDef + + fun generate_iclosuredef(v: A2IContext): IClosureDef is abstract +end + +redef class AClosureDef + redef fun generate_iclosuredef(v) + do + # Prepare signature + var args = new Array[IRegister] + var sig = closure.signature + for i in [0..sig.arity[ do + args.add(v.new_register(sig[i])) + end + var ret: nullable IRegister = null + var rtype = sig.return_type + if rtype != null then + ret = v.new_register(rtype) + end + + var iclos = new IClosureDef(args, ret) + iclos.location = location + + # Prepare env + var seq_old = v.seq + v.seq = iclos.body + escapable.continue_seq = iclos.body + escapable.continue_value = iclos.result + + # Assign parameters + for i in [0..variables.length[ do + var res = v.variable(variables[i]) + v.add_assignment(res, iclos.params[i]) + end + + v.generate_stmt(n_expr) + + v.seq = seq_old + _iclosure_def = iclos + return iclos + end +end + +redef class AClosureCallExpr + redef fun generate_icode(v) + do + # Geneate arguments + var args = new Array[IRegister] + generate_icode_for_arguments_in(v, args) + + # Prepare icall + var closdecl = v.closurevariables[variable] + var icall = new IClosCall(closdecl, args) + var seq_old = v.seq + + # Fill break of ical + if n_closure_defs.length == 1 then do + var iseq = new ISeq + icall.break_seq = iseq + v.seq = iseq + v.generate_stmt(n_closure_defs.first.as(AClosureDef).n_expr) + v.seq = seq_old + end + + # Prepare in case of default block + var iif: nullable IIf = null # The iif of default block + var closdecl_default = closdecl.default # The default (if any) + if closdecl_default != null then + iif = new IIf(v.expr(new IHasClos(closdecl), v.visitor.type_bool)) + v.stmt(iif) + v.seq = iif.then_seq + end + + # Add the icall + var r2: nullable IRegister = null # the result of the icall + var rtype = variable.closure.signature.return_type + if rtype == null then + v.stmt(icall) + else + r2 = v.expr(icall, rtype) + end + + # Process the case of default block + var r: nullable IRegister = null # the real result + if closdecl_default != null then + assert iif != null + if r2 != null then + assert rtype != null + r = v.new_register(rtype) + v.add_assignment(r, r2) + end + v.seq = iif.else_seq + var r3 = closdecl_default.inline_in_seq(iif.else_seq, args) + if r != null then + assert r3 != null + v.add_assignment(r, r3) + end + v.seq = seq_old + else + r = r2 + end + return r + end +end diff --git a/src/syntax/syntax.nit b/src/syntax/syntax.nit index 8f38abd..68a1ebb 100644 --- a/src/syntax/syntax.nit +++ b/src/syntax/syntax.nit @@ -21,6 +21,7 @@ package syntax import mmloader import mmbuilder import typing +import icode_generation # Loader of nit source files class SrcModuleLoader @@ -72,6 +73,9 @@ redef class MMSrcModule do_typing(tc) tc.check_errors + + generate_icode(tc) + tc.check_errors end end diff --git a/src/syntax/syntax_base.nit b/src/syntax/syntax_base.nit index cc626c1..5d9c0a8 100644 --- a/src/syntax/syntax_base.nit +++ b/src/syntax/syntax_base.nit @@ -783,26 +783,10 @@ redef class AAttrFormExpr fun attr_type: MMType is abstract end -redef class AStringFormExpr - fun meth_with_native: MMMethod is abstract -end - redef class ASuperstringExpr - fun meth_with_capacity: MMMethod is abstract - fun meth_add: MMMethod is abstract - fun meth_to_s: MMMethod is abstract fun atype: MMType is abstract end -redef class AArrayExpr - fun meth_with_capacity: MMMethod is abstract - fun meth_add: MMMethod is abstract -end - -redef class ARangeExpr - fun meth_init: MMMethod is abstract -end - redef class AVardeclExpr # Assiociated local variable fun variable: VarVariable is abstract @@ -812,34 +796,26 @@ end redef class AForExpr # Associated automatic local variable fun variable: AutoVariable is abstract - fun meth_iterator: MMMethod is abstract - fun meth_is_ok: MMMethod is abstract - fun meth_item: MMMethod is abstract - fun meth_next: MMMethod is abstract end redef class ASelfExpr # Associated local variable fun variable: ParamVariable is abstract - #readable writable var _variable: nullable ParamVariable end redef class AVarFormExpr # Associated local variable fun variable: Variable is abstract - #readable writable var _variable: nullable Variable end redef class AClosureCallExpr special AAbsAbsSendExpr # Associated closure variable fun variable: ClosureVariable is abstract - #readable writable var _variable: nullable ClosureVariable end redef class PClosureDef # Associated closure - #readable writable var _closure: nullable MMClosure fun closure: MMClosure is abstract # Automatic variables diff --git a/src/syntax/typing.nit b/src/syntax/typing.nit index e2e6a2f..e89d9e2 100644 --- a/src/syntax/typing.nit +++ b/src/syntax/typing.nit @@ -535,14 +535,6 @@ redef class AForExpr # The corresponding escapable block readable var _escapable: nullable EscapableBlock - var _meth_iterator: nullable MMMethod - redef fun meth_iterator: MMMethod do return _meth_iterator.as(not null) - var _meth_is_ok: nullable MMMethod - redef fun meth_is_ok: MMMethod do return _meth_is_ok.as(not null) - var _meth_item: nullable MMMethod - redef fun meth_item: MMMethod do return _meth_item.as(not null) - var _meth_next: nullable MMMethod - redef fun meth_next: MMMethod do return _meth_next.as(not null) redef fun accept_typing(v) do var escapable = new EscapableBlock(self) @@ -563,30 +555,13 @@ redef class AForExpr if not v.check_conform_expr(n_expr, v.type_collection) then return var expr_type = n_expr.stype - _meth_iterator = expr_type.local_class.select_method(once ("iterator".to_symbol)) - if _meth_iterator == null then - v.error(self, "Error: Collection MUST have an iterate method") - return - end - var iter_type = _meth_iterator.signature_for(expr_type).return_type.as(not null) - _meth_is_ok = iter_type.local_class.select_method(once ("is_ok".to_symbol)) - if _meth_is_ok == null then - v.error(self, "Error: {iter_type} MUST have an is_ok method") - return - end - _meth_item = iter_type.local_class.select_method(once ("item".to_symbol)) - if _meth_item == null then - v.error(self, "Error: {iter_type} MUST have an item method") - return - end - _meth_next = iter_type.local_class.select_method(once ("next".to_symbol)) - if _meth_next == null then - v.error(self, "Error: {iter_type} MUST have a next method") - return - end - var t = _meth_item.signature_for(iter_type).return_type - if not n_expr.is_self then t = t.not_for_self - va.stype = t + # Get iterator + var meth_iterator = v.get_method(expr_type, once "iterator".to_symbol) + var iter_type = meth_iterator.signature_for(expr_type).return_type.as(not null) + var meth_item = v.get_method(iter_type, once ("item".to_symbol)) + var va_stype = meth_item.signature_for(iter_type).return_type.as(not null) + if not n_expr.is_self then va_stype = va_stype.not_for_self + va.stype = va_stype # Body evaluation if n_block != null then v.enter_visit(n_block) @@ -854,24 +829,14 @@ redef class ACharExpr end redef class AStringFormExpr - var _meth_with_native: nullable MMMethod - redef fun meth_with_native: MMMethod do return _meth_with_native.as(not null) redef fun after_typing(v) do _stype = v.type_string _is_typed = true - _meth_with_native = _stype.local_class.select_method(once "with_native".to_symbol) - if _meth_with_native == null then v.error(self, "{_stype} MUST have a with_native method.") end end redef class ASuperstringExpr - redef fun meth_with_capacity: MMMethod do return _meth_with_capacity.as(not null) - var _meth_with_capacity: nullable MMMethod - redef fun meth_add: MMMethod do return _meth_add.as(not null) - var _meth_add: nullable MMMethod - redef fun meth_to_s: MMMethod do return _meth_to_s.as(not null) - var _meth_to_s: nullable MMMethod redef fun atype do return _atype.as(not null) var _atype: nullable MMType redef fun after_typing(v) @@ -880,12 +845,6 @@ redef class ASuperstringExpr _stype = stype var atype = v.type_array(stype) _atype = atype - _meth_with_capacity = atype.local_class.select_method(once "with_capacity".to_symbol) - if _meth_with_capacity == null then v.error(self, "{_atype} MUST have a with_capacity method.") - _meth_add = atype.local_class.select_method(once "add".to_symbol) - if _meth_add == null then v.error(self, "{_atype} MUST have an add method.") - _meth_to_s = v.type_object.local_class.select_method(once "to_s".to_symbol) - if _meth_to_s == null then v.error(self, "Object MUST have a to_s method.") _is_typed = true end end @@ -899,11 +858,6 @@ redef class ANullExpr end redef class AArrayExpr - redef fun meth_with_capacity: MMMethod do return _meth_with_capacity.as(not null) - var _meth_with_capacity: nullable MMMethod - redef fun meth_add: MMMethod do return _meth_add.as(not null) - var _meth_add: nullable MMMethod - redef fun after_typing(v) do var stype = v.check_conform_multiexpr(null, n_exprs) @@ -913,19 +867,11 @@ redef class AArrayExpr private fun do_typing(v: TypingVisitor, element_type: MMType) do _stype = v.type_array(element_type) - - _meth_with_capacity = _stype.local_class.select_method(once "with_capacity".to_symbol) - if _meth_with_capacity == null then v.error(self, "{_stype} MUST have a with_capacity method.") - _meth_add = _stype.local_class.select_method(once "add".to_symbol) - if _meth_add == null then v.error(self, "{_stype} MUST have an add method.") - _is_typed = true end end redef class ARangeExpr - redef fun meth_init: MMMethod do return _meth_init.as(not null) - var _meth_init: nullable MMMethod redef fun after_typing(v) do if not v.check_expr(n_expr) or not v.check_expr(n_expr2) then return @@ -944,24 +890,6 @@ redef class ARangeExpr end end -redef class ACrangeExpr - redef fun after_typing(v) - do - super - if not is_typed then return - _meth_init = stype.local_class.select_method(once "init".to_symbol) - end -end -redef class AOrangeExpr - redef fun after_typing(v) - do - super - if not is_typed then return - _meth_init = stype.local_class.select_method(once "without_last".to_symbol) - end -end - - redef class ASuperExpr redef readable var _init_in_superclass: nullable MMMethod redef fun after_typing(v) diff --git a/tests/sav/base_attr_isset_alt3.sav b/tests/sav/base_attr_isset_alt3.sav index 6033ed3..113b78b 100644 --- a/tests/sav/base_attr_isset_alt3.sav +++ b/tests/sav/base_attr_isset_alt3.sav @@ -7,7 +7,9 @@ false 3 true false -Uninitialized attribute base_attr_isset_alt3::Foo::_a2 at base_attr_isset_alt3::Foo::init for Foo. +Uninitialized attribute _a2 (alt/base_attr_isset_alt3.nit) ,---- Stack trace -- - - - +| check new Foo (alt/base_attr_isset_alt3.nit:0) +| new Foo base_attr_isset_alt3::Foo::init (alt/base_attr_isset_alt3.nit:53) | base_attr_isset_alt3::Sys::main (alt/base_attr_isset_alt3.nit:100) `------------------- - - - diff --git a/tests/sav/base_attr_isset_alt4.sav b/tests/sav/base_attr_isset_alt4.sav index 514d4ae..15edaf3 100644 --- a/tests/sav/base_attr_isset_alt4.sav +++ b/tests/sav/base_attr_isset_alt4.sav @@ -7,7 +7,9 @@ false 3 true false -Uninitialized attribute base_attr_isset_alt4::Foo::_a2 at base_attr_isset_alt4::Foo::init for Baz. +Uninitialized attribute _a2 (alt/base_attr_isset_alt4.nit) ,---- Stack trace -- - - - +| check new Baz (alt/base_attr_isset_alt4.nit:0) +| new Baz base_attr_isset_alt4::Foo::init (alt/base_attr_isset_alt4.nit:53) | base_attr_isset_alt4::Sys::main (alt/base_attr_isset_alt4.nit:99) `------------------- - - - diff --git a/tests/sav/base_attr_nullable_alt1.sav b/tests/sav/base_attr_nullable_alt1.sav index ca81b10..c3800b8 100644 --- a/tests/sav/base_attr_nullable_alt1.sav +++ b/tests/sav/base_attr_nullable_alt1.sav @@ -2,5 +2,6 @@ Uninitialized attribute _a1 in base_attr_nullable_alt1::Foo::run (alt/base_attr_ ,---- Stack trace -- - - - | base_attr_nullable_alt1::Foo::run (alt/base_attr_nullable_alt1.nit:36) | base_attr_nullable_alt1::Foo::init (alt/base_attr_nullable_alt1.nit:48) +| new Foo base_attr_nullable_alt1::Foo::init (alt/base_attr_nullable_alt1.nit:48) | base_attr_nullable_alt1::Sys::main (alt/base_attr_nullable_alt1.nit:81) `------------------- - - - diff --git a/tests/sav/base_attr_nullable_alt2.sav b/tests/sav/base_attr_nullable_alt2.sav index 999e869..428d2f8 100644 --- a/tests/sav/base_attr_nullable_alt2.sav +++ b/tests/sav/base_attr_nullable_alt2.sav @@ -1,8 +1,9 @@ 1 -Uninitialized attribute _a2 (alt/base_attr_nullable_alt2.nit:35) +Uninitialized attribute _a2 in base_attr_nullable_alt2::Foo::a2 (alt/base_attr_nullable_alt2.nit:35) ,---- Stack trace -- - - - | base_attr_nullable_alt2::Foo::a2 (alt/base_attr_nullable_alt2.nit:35) | base_attr_nullable_alt2::Foo::run (alt/base_attr_nullable_alt2.nit:36) | base_attr_nullable_alt2::Foo::init (alt/base_attr_nullable_alt2.nit:48) +| new Foo base_attr_nullable_alt2::Foo::init (alt/base_attr_nullable_alt2.nit:48) | base_attr_nullable_alt2::Sys::main (alt/base_attr_nullable_alt2.nit:81) `------------------- - - - diff --git a/tests/sav/base_attr_nullable_alt3.sav b/tests/sav/base_attr_nullable_alt3.sav index 0ee9fff..0888aad 100644 --- a/tests/sav/base_attr_nullable_alt3.sav +++ b/tests/sav/base_attr_nullable_alt3.sav @@ -2,5 +2,6 @@ Uninitialized attribute _a1 in base_attr_nullable_alt3::Bar::(base_attr_nullable ,---- Stack trace -- - - - | base_attr_nullable_alt3::Bar::(base_attr_nullable_alt3::Foo::run) (alt/base_attr_nullable_alt3.nit:62) | base_attr_nullable_alt3::Bar::init (alt/base_attr_nullable_alt3.nit:69) +| new Bar base_attr_nullable_alt3::Bar::init (alt/base_attr_nullable_alt3.nit:69) | base_attr_nullable_alt3::Sys::main (alt/base_attr_nullable_alt3.nit:81) `------------------- - - - diff --git a/tests/sav/base_attr_nullable_alt4.sav b/tests/sav/base_attr_nullable_alt4.sav index 2a4dd60..b387535 100644 --- a/tests/sav/base_attr_nullable_alt4.sav +++ b/tests/sav/base_attr_nullable_alt4.sav @@ -3,5 +3,6 @@ Uninitialized attribute _a2 in base_attr_nullable_alt4::Foo::run_other (alt/base ,---- Stack trace -- - - - | base_attr_nullable_alt4::Foo::run_other (alt/base_attr_nullable_alt4.nit:42) | base_attr_nullable_alt4::Bar::init (alt/base_attr_nullable_alt4.nit:69) +| new Bar base_attr_nullable_alt4::Bar::init (alt/base_attr_nullable_alt4.nit:69) | base_attr_nullable_alt4::Sys::main (alt/base_attr_nullable_alt4.nit:81) `------------------- - - - diff --git a/tests/sav/base_attr_nullable_alt5.sav b/tests/sav/base_attr_nullable_alt5.sav index 07bfb3c..798d8d7 100644 --- a/tests/sav/base_attr_nullable_alt5.sav +++ b/tests/sav/base_attr_nullable_alt5.sav @@ -4,5 +4,6 @@ Uninitialized attribute _a3 in base_attr_nullable_alt5::Bar::(base_attr_nullable ,---- Stack trace -- - - - | base_attr_nullable_alt5::Bar::(base_attr_nullable_alt5::Foo::run) (alt/base_attr_nullable_alt5.nit:62) | base_attr_nullable_alt5::Bar::init (alt/base_attr_nullable_alt5.nit:69) +| new Bar base_attr_nullable_alt5::Bar::init (alt/base_attr_nullable_alt5.nit:69) | base_attr_nullable_alt5::Sys::main (alt/base_attr_nullable_alt5.nit:81) `------------------- - - - diff --git a/tests/sav/base_attr_nullable_int_alt1.sav b/tests/sav/base_attr_nullable_int_alt1.sav index c8d5ccf..32184bb 100644 --- a/tests/sav/base_attr_nullable_int_alt1.sav +++ b/tests/sav/base_attr_nullable_int_alt1.sav @@ -2,5 +2,6 @@ Uninitialized attribute _a1 in base_attr_nullable_int_alt1::Foo::run (alt/base_a ,---- Stack trace -- - - - | base_attr_nullable_int_alt1::Foo::run (alt/base_attr_nullable_int_alt1.nit:30) | base_attr_nullable_int_alt1::Foo::init (alt/base_attr_nullable_int_alt1.nit:42) +| new Foo base_attr_nullable_int_alt1::Foo::init (alt/base_attr_nullable_int_alt1.nit:42) | base_attr_nullable_int_alt1::Sys::main (alt/base_attr_nullable_int_alt1.nit:75) `------------------- - - - diff --git a/tests/sav/base_attr_nullable_int_alt2.sav b/tests/sav/base_attr_nullable_int_alt2.sav index 61a7d03..42bbe46 100644 --- a/tests/sav/base_attr_nullable_int_alt2.sav +++ b/tests/sav/base_attr_nullable_int_alt2.sav @@ -1,8 +1,9 @@ 1 -Uninitialized attribute _a2 (alt/base_attr_nullable_int_alt2.nit:29) +Uninitialized attribute _a2 in base_attr_nullable_int_alt2::Foo::a2 (alt/base_attr_nullable_int_alt2.nit:29) ,---- Stack trace -- - - - | base_attr_nullable_int_alt2::Foo::a2 (alt/base_attr_nullable_int_alt2.nit:29) | base_attr_nullable_int_alt2::Foo::run (alt/base_attr_nullable_int_alt2.nit:30) | base_attr_nullable_int_alt2::Foo::init (alt/base_attr_nullable_int_alt2.nit:42) +| new Foo base_attr_nullable_int_alt2::Foo::init (alt/base_attr_nullable_int_alt2.nit:42) | base_attr_nullable_int_alt2::Sys::main (alt/base_attr_nullable_int_alt2.nit:75) `------------------- - - - diff --git a/tests/sav/base_attr_nullable_int_alt3.sav b/tests/sav/base_attr_nullable_int_alt3.sav index 7587dfd..40863a2 100644 --- a/tests/sav/base_attr_nullable_int_alt3.sav +++ b/tests/sav/base_attr_nullable_int_alt3.sav @@ -2,5 +2,6 @@ Uninitialized attribute _a1 in base_attr_nullable_int_alt3::Bar::(base_attr_null ,---- Stack trace -- - - - | base_attr_nullable_int_alt3::Bar::(base_attr_nullable_int_alt3::Foo::run) (alt/base_attr_nullable_int_alt3.nit:56) | base_attr_nullable_int_alt3::Bar::init (alt/base_attr_nullable_int_alt3.nit:63) +| new Bar base_attr_nullable_int_alt3::Bar::init (alt/base_attr_nullable_int_alt3.nit:63) | base_attr_nullable_int_alt3::Sys::main (alt/base_attr_nullable_int_alt3.nit:75) `------------------- - - - diff --git a/tests/sav/base_attr_nullable_int_alt4.sav b/tests/sav/base_attr_nullable_int_alt4.sav index 7880ac1..92447fc 100644 --- a/tests/sav/base_attr_nullable_int_alt4.sav +++ b/tests/sav/base_attr_nullable_int_alt4.sav @@ -3,5 +3,6 @@ Uninitialized attribute _a2 in base_attr_nullable_int_alt4::Foo::run_other (alt/ ,---- Stack trace -- - - - | base_attr_nullable_int_alt4::Foo::run_other (alt/base_attr_nullable_int_alt4.nit:36) | base_attr_nullable_int_alt4::Bar::init (alt/base_attr_nullable_int_alt4.nit:63) +| new Bar base_attr_nullable_int_alt4::Bar::init (alt/base_attr_nullable_int_alt4.nit:63) | base_attr_nullable_int_alt4::Sys::main (alt/base_attr_nullable_int_alt4.nit:75) `------------------- - - - diff --git a/tests/sav/base_attr_nullable_int_alt5.sav b/tests/sav/base_attr_nullable_int_alt5.sav index cbe4cde..f5e49d8 100644 --- a/tests/sav/base_attr_nullable_int_alt5.sav +++ b/tests/sav/base_attr_nullable_int_alt5.sav @@ -4,5 +4,6 @@ Uninitialized attribute _a3 in base_attr_nullable_int_alt5::Bar::(base_attr_null ,---- Stack trace -- - - - | base_attr_nullable_int_alt5::Bar::(base_attr_nullable_int_alt5::Foo::run) (alt/base_attr_nullable_int_alt5.nit:56) | base_attr_nullable_int_alt5::Bar::init (alt/base_attr_nullable_int_alt5.nit:63) +| new Bar base_attr_nullable_int_alt5::Bar::init (alt/base_attr_nullable_int_alt5.nit:63) | base_attr_nullable_int_alt5::Sys::main (alt/base_attr_nullable_int_alt5.nit:75) `------------------- - - - diff --git a/tests/sav/base_closure_raf_alt15.fail b/tests/sav/base_closure_raf_alt15.fail index 7f85acf..1b5b3d1 100644 --- a/tests/sav/base_closure_raf_alt15.fail +++ b/tests/sav/base_closure_raf_alt15.fail @@ -1 +1,7 @@ -Compilation error +0 +10 +20 +21 +40 +5 +6 diff --git a/tests/sav/test_id.sav b/tests/sav/test_id.sav index 843c7f0..a87fb02 100644 --- a/tests/sav/test_id.sav +++ b/tests/sav/test_id.sav @@ -4,7 +4,7 @@ true true true -false +true true true