diff --git a/aeneas/src/mach/LinearScanRegAlloc.v3 b/aeneas/src/mach/LinearScanRegAlloc.v3 index 37e70c68f..961489e0a 100644 --- a/aeneas/src/mach/LinearScanRegAlloc.v3 +++ b/aeneas/src/mach/LinearScanRegAlloc.v3 @@ -100,6 +100,10 @@ class LinearScanRegAlloc { } def recordLive(index: int) { for (l = activeList; l != null; l = l.next) { + if (l == l.next) { + gen.mach.prog.ERROR.fail( + Strings.format2("VReg %d from SSAInstr %d points to itself!", l.varNum, l.ssa.uid)); + } livemap[index, l.varNum] = true; } } @@ -111,6 +115,12 @@ class LinearScanRegAlloc { // add to the active list // if (Debug.PARANOID && (vreg.next != null || vreg.prev != null)) fail("var %d should not be in list", vreg.varNum); var prev = activeList; + if (prev == vreg) { + var b = StringBuilder.new(); + b.put3("Making a circular linked list with VRegs %d and %d from SSAInstrs %d and ", + prev.varNum, vreg.varNum, prev.ssa.uid).put1("%d respectively", vreg.ssa.uid); + gen.mach.prog.ERROR.fail(b.toString()); + } vreg.next = prev; activeList = vreg; if (prev != null) prev.prev = vreg; @@ -121,6 +131,12 @@ class LinearScanRegAlloc { var vreg = p.vreg; if (vreg == null) return; // nothing to do var pos = p.pos, p = vreg.prev, n = vreg.next; + if (p == n && p != null) { + var b = StringBuilder.new(); + b.put3("Have a circular linked list with VRegs %d and %d from SSAInstrs %d and ", + p.varNum, vreg.varNum, p.ssa.uid).put1("%d respectively", vreg.ssa.uid); + gen.mach.prog.ERROR.fail(b.toString()); + } if (p != null) p.next = n; if (n != null) n.prev = p; vreg.prev = null; @@ -448,7 +464,10 @@ class IntervalBuilder(gen: OldCodeGen, regSet: MachRegSet) { for (i < brow.length) { var vnum = i * 32, bb = brow[i], pb = prow[i]; for (bits = bb & (-1 ^ pb); bits != 0; bits = bits >>> 1) { - if ((bits & 1) != 0) insertHead(LsraStart.new(b.start * 2, vars[vnum])); + if ((bits & 1) != 0) { + insertHead(LsraStart.new(b.start * 2, vars[vnum])); + vars[vnum].activeInFuture = true; + } vnum++; } } @@ -457,7 +476,10 @@ class IntervalBuilder(gen: OldCodeGen, regSet: MachRegSet) { for (i < brow.length) { var vnum = i * 32, bb = brow[i], pb = prow[i]; for (bits = (-1 ^ bb) & pb; bits != 0; bits = bits >>> 1) { - if ((bits & 1) != 0) insertHead(LsraEnd.new(b.start * 2, vars[vnum])); + if ((bits & 1) != 0) { + insertHead(LsraEnd.new(b.start * 2, vars[vnum])); + vars[vnum].activeInFuture = false; + } vnum++; } } @@ -481,7 +503,10 @@ class IntervalBuilder(gen: OldCodeGen, regSet: MachRegSet) { var vnum = u >>> gen.VAR_SHIFT, vreg = vars[vnum]; if (usePoint == null) insertHead(usePoint = LsraUse.new(pos, i)); if (!vreg.isConst()) { - if (!livein.set(blindex, vnum)) insertHead(LsraEnd.new(pos, vreg)); + if (!livein.set(blindex, vnum)) { + insertHead(LsraEnd.new(pos, vreg)); + vreg.activeInFuture = false; + } if (vreg.hint == 0 && regSet.isReg(fixed)) vreg.hint = byte.!(fixed); if (pos > vreg.endPos) vreg.endPos = pos; } @@ -527,8 +552,18 @@ class IntervalBuilder(gen: OldCodeGen, regSet: MachRegSet) { while (l != null && l.pos < loopEndPos) { if (l.vreg != null && livein[0, l.vreg.varNum]) { // remove all starts and ends within the loop - if (LsraStart.?(l)) { l = removePoint(l); continue; } - if (LsraEnd.?(l)) { l = removePoint(l); continue; } + if (LsraStart.?(l)) { + // Since we're removing an LsraStart, the VReg will not be active in the future anymore + l.vreg.activeInFuture = false; + l = removePoint(l); + continue; + } + if (LsraEnd.?(l)) { + // Since we're removing an LsraEnd, the VReg will be active in the future + l.vreg.activeInFuture = true; + l = removePoint(l); + continue; + } } p = l; l = l.next; @@ -554,29 +589,60 @@ class IntervalBuilder(gen: OldCodeGen, regSet: MachRegSet) { p = l; l = l.next; } + if (insertPoint == null) { // if there are no points after the loop end, add a dummy one insertPoint = LsraEnd.new(-1, null); insertPoint.prev = p; if (p != null) p.next = insertPoint; } + + var vregsActiveInFuture = Vector.new(); // if the last block of the loop is a predecessor of the block after // the loop, then a correct live range hole was already created var order = gen.blocks.order, last = order[b.loop.end - 1].block; if (b.loop.end < order.length) { var next = order[b.loop.end].block; + var isPred = false; for (s in last.succs()) { - if (s.dest == next) return; + if (s.dest == next) { + isPred = true; + break; + } + } + + if (isPred) { + // XXX: If any variable will made active in the future, we need to + // ensure we insert LsraEnds to create live range holes + for (v < vars.length) { + if (livein[0, v] && vars[v].activeInFuture) { + vregsActiveInFuture.put(vars[v]); + } + } + if (vregsActiveInFuture.length == 0) { + return; + } } } + // create a live range hole by inserting ends for all remaining variables - livein.row(0).apply(insertEndVarAtLoopEnd, (loopEndPos, insertPoint)); + if (vregsActiveInFuture.length > 0) { + // XXX: Use a Vector instead of calling `livein.row(0).apply` since there + // may be cases where only certain variables are made active in the + // future, instead of everything livein to the loop + for (vreg in vregsActiveInFuture.extract()) { + insertEndVarAtLoopEnd(vreg.varNum, (loopEndPos, insertPoint)); + } + } else { + livein.row(0).apply(insertEndVarAtLoopEnd, (loopEndPos, insertPoint)); + } // remove dummy point if (insertPoint.pos == -1) removePoint(insertPoint); } def insertEndVarAtLoopEnd(vnum: int, t: (int, LsraPoint)) { var vreg = vars[vnum], loopEndPos = t.0, insertPoint = t.1; insertBefore(LsraEnd.new(loopEndPos, vreg), insertPoint); + vreg.activeInFuture = false; if (loopEndPos > vreg.endPos) vreg.endPos = loopEndPos; } def insertHead(n: LsraPoint) { @@ -624,6 +690,7 @@ class IntervalPrinter( var state = Array.new(1 + gen.vars.length); var live = Array.new(1 + gen.vars.length); var map = Array.new(gen.vars.length); + var sb = StringBuilder.new(); var vnum: int; new() { @@ -724,8 +791,16 @@ class IntervalPrinter( def pointType(l: LsraPoint) -> string { if (LsraStart.?(l)) return ("start"); if (LsraEnd.?(l)) return ("end"); - if (LsraLive.?(l)) return ("live"); - if (LsraKill.?(l)) return ("kill"); + if (LsraLive.?(l)) { + sb.reset(); + sb.put1("live idx %d", LsraLive.!(l).index); + return (sb.toString()); + } + if (LsraKill.?(l)) { + sb.reset(); + sb.put1("kill regset 0x%x", LsraKill.!(l).regset); + return (sb.toString()); + } if (LsraUse.?(l)) return ("use"); if (LsraDef.?(l)) return ("def"); return "unknown"; diff --git a/aeneas/src/mach/MachBackend.v3 b/aeneas/src/mach/MachBackend.v3 index 72fa06596..84746d426 100644 --- a/aeneas/src/mach/MachBackend.v3 +++ b/aeneas/src/mach/MachBackend.v3 @@ -48,6 +48,7 @@ class VReg { var endPos: int; // last live position var notSpillable: bool; // true if this var cannot be spill anymore var reloadFrom: VReg; // var that this var is spilled from + var activeInFuture: bool; // Will the VReg be made active (i.e. starting a new live range) in the future? // -- state for stackifier ----------------------------- var usage = Usage.NONE; var parmoveState: int; diff --git a/aeneas/src/main/Version.v3 b/aeneas/src/main/Version.v3 index 3d726b210..8d73d4b16 100644 --- a/aeneas/src/main/Version.v3 +++ b/aeneas/src/main/Version.v3 @@ -3,6 +3,6 @@ // Updated by VCS scripts. DO NOT EDIT. component Version { - def version: string = "III-7.1776"; + def version: string = "III-7.1777"; var buildData: string; } diff --git a/test/core/zjson00.v3 b/test/core/zjson00.v3 new file mode 100644 index 000000000..7ae48c24e --- /dev/null +++ b/test/core/zjson00.v3 @@ -0,0 +1,70 @@ +//@execute 0=true +type JsonValue { + case String(v: string); + case Number(v: int); // TODO: float + case Bool(v: bool); + case Null; + case JArray(v: Array); + case JObject(v: HashMap); +} + +class Vector { + def put(v: T) { } + def extract() -> Array { return null; } +} +class HashMap { +} + +def ERR_RET = JsonValue.Null; +class JsonParser { + var ok: bool; + var state: int; + + def parse_value() -> JsonValue { + match (state++) { + 0 => return ERR_RET; + 1 => return parse_string(); + 2 => return parse_number(); + 3 => return JsonValue.Null; + 4 => return JsonValue.Bool(true); + 5 => return JsonValue.Bool(false); + 6 => return parse_array(); + 7 => return parse_object(); + } + return ERR_RET; + } + def parse_string() -> JsonValue { + return JsonValue.String(if(state++ == 8, "", "a")); + } + def parse_number() -> JsonValue { + return JsonValue.Number(state++); + } + def parse_object() -> JsonValue { + return JsonValue.JObject(null); + } + def parse_array() -> JsonValue { + var vals = Vector.new(); + if (req1('[') == -1) return ERR_RET; + vals.put(parse_value()); + if (!ok) return ERR_RET; + while (opt1(',') != -1) { + vals.put(parse_value()); + if (!ok) return ERR_RET; + } + if (req1(']') == -1) return ERR_RET; + return JsonValue.JArray(vals.extract()); + } + def req1(ch: byte) -> int { + ok = ch == '1'; + return if(ch == '0', 1, -1); + } + def opt1(ch: byte) -> int { + ok = ch == '2'; + return -1; + } +} + +def main(a: int) -> bool { + var x = JsonParser.new(); + return x.parse_array() == ERR_RET; +} diff --git a/test/core/zjson01.v3 b/test/core/zjson01.v3 new file mode 100644 index 000000000..77e12beed --- /dev/null +++ b/test/core/zjson01.v3 @@ -0,0 +1,70 @@ +//@execute 0=true; 1=false; 2=false +type JsonValue { + case String(v: string); + case Number(v: int); // TODO: float + case Bool(v: bool); + case Null; + case JArray(v: Array); + case JObject(v: HashMap); +} + +class Vector { + def put(v: T) { } + def extract() -> Array { return null; } +} +class HashMap { +} + +def ERR_RET = JsonValue.Null; +class JsonParser { + var ok: bool; + var state: int; + + def parse_value() -> JsonValue { + match (state++) { + 0 => return ERR_RET; + 1 => return parse_string(); + 2 => return parse_number(); + 3 => return JsonValue.Null; + 4 => return JsonValue.Bool(true); + 5 => return JsonValue.Bool(false); + 6 => return parse_array(); + 7 => return parse_object(); + } + return ERR_RET; + } + def parse_string() -> JsonValue { + return JsonValue.String(if(state++ == 8, "", "a")); + } + def parse_number() -> JsonValue { + return JsonValue.Number(state++); + } + def parse_object() -> JsonValue { + return JsonValue.JObject(null); + } + def parse_array() -> JsonValue { + var vals = Vector.new(); + if (req1('[') == -1) return ERR_RET; + vals.put(parse_value()); + if (!ok) return ERR_RET; + while (opt1(',') != -1) { + vals.put(parse_value()); + if (!ok) return ERR_RET; + } + if (req1(']') == -1) return ERR_RET; + return JsonValue.JArray(vals.extract()); + } + def req1(ch: byte) -> int { + return 1; + } + def opt1(ch: byte) -> int { + return -1; + } +} + +def main(a: int) -> bool { + var x = JsonParser.new(); + x.state = a; + x.ok = a > 0; + return x.parse_array() == ERR_RET; +} diff --git a/test/core/zjson02.v3 b/test/core/zjson02.v3 new file mode 100644 index 000000000..d0a610435 --- /dev/null +++ b/test/core/zjson02.v3 @@ -0,0 +1,51 @@ +//@execute 0=true; 1=false; 2=false +type JsonValue { + case String(v: string); + case Number(v: int); // TODO: float + case Bool(v: bool); + case Null; + case JArray(v: Array); + case JObject(v: HashMap); +} + +class Vector { + def put(v: T) { } + def extract() -> Array { return null; } +} +class HashMap { +} + +def ERR_RET = JsonValue.Null; +class JsonParser { + var ok: bool; + var state: int; + + def parse_value() -> JsonValue { + return JsonValue.String(if(state++ == 8, "", "a")); + } + def parse_array() -> JsonValue { + var vals = Vector.new(); + if (req1() == -1) return ERR_RET; + vals.put(parse_value()); + if (!ok) return ERR_RET; + while (opt1(',') != -1) { + vals.put(parse_value()); + if (!ok) return ERR_RET; + } + if (req1() == -1) return ERR_RET; + return JsonValue.JArray(vals.extract()); + } + def req1() -> int { + return 1; + } + def opt1(ch: byte) -> int { + return -1; + } +} + +def main(a: int) -> bool { + var x = JsonParser.new(); + x.state = a; + x.ok = a > 0; + return x.parse_array() == ERR_RET; +} diff --git a/test/core/zjson03.v3 b/test/core/zjson03.v3 new file mode 100644 index 000000000..6c2066f40 --- /dev/null +++ b/test/core/zjson03.v3 @@ -0,0 +1,38 @@ +//@execute 0=true; 1=false; 2=false +type JsonValue { + case String(v: string); + case Null; + case JArray(v: Array); +} + +class Vector { + def extract() -> Array { return null; } +} + +def ERR_RET = JsonValue.Null; +class JsonParser { + var ok: bool; + + def parse_array() -> JsonValue { + var vals = Vector.new(); + if (req1()) return ERR_RET; + if (!ok) return ERR_RET; + while (opt1(',') != -1) { + if (!ok) return ERR_RET; + } + if (req1()) return ERR_RET; + return JsonValue.JArray(vals.extract()); + } + def req1() -> bool { + return false; + } + def opt1(ch: byte) -> int { + return -1; + } +} + +def main(a: int) -> bool { + var x = JsonParser.new(); + x.ok = a > 0; + return x.parse_array() == ERR_RET; +} diff --git a/test/core/zjson04.v3 b/test/core/zjson04.v3 new file mode 100644 index 000000000..6d04bff52 --- /dev/null +++ b/test/core/zjson04.v3 @@ -0,0 +1,32 @@ +//@execute 0=true; 1=false; 2=false +type JsonValue { + case Null; + case JArray(v: Array); +} + +def ERR_RET = JsonValue.Null; +def ARRAY = JsonValue.JArray([]); +class JsonParser { + var ok: bool; + + def parse_array() -> JsonValue { + if (req1()) return ERR_RET; + while (opt1(',') != -1) { + if (!ok) return ERR_RET; + } + if (req1()) return ERR_RET; + return ARRAY; + } + def req1() -> bool { + return !ok; + } + def opt1(ch: byte) -> int { + return -1; + } +} + +def main(a: int) -> bool { + var x = JsonParser.new(); + x.ok = a > 0; + return x.parse_array() == ERR_RET; +} diff --git a/test/core/zjson05.v3 b/test/core/zjson05.v3 new file mode 100644 index 000000000..891c9f96d --- /dev/null +++ b/test/core/zjson05.v3 @@ -0,0 +1,25 @@ +//@execute 0=true; 1=false; 2=false +class C { + var ok: bool; + + def parse_array() -> int { + if (req1()) return -1; + while (opt1(',') != -1) { + if (!ok) return -2; + } + if (req1()) return -3; + return 4; + } + def req1() -> bool { + return !ok; + } + def opt1(ch: byte) -> int { + return -1; + } +} + +def main(a: int) -> bool { + var x = C.new(); + x.ok = a > 0; + return x.parse_array() != 4; +} diff --git a/test/core/zjson06.v3 b/test/core/zjson06.v3 new file mode 100644 index 000000000..5fa58edb6 --- /dev/null +++ b/test/core/zjson06.v3 @@ -0,0 +1,24 @@ +//@execute 0=true; 1=false; 2=false +class C { + def parse_array() -> int { + if (req1()) return -1; + while (opt1(',') != -1) { + if (ok) return -2; + } + if (req1()) return -3; + return 4; + } + def req1() -> bool { + return ok; + } + def opt1(ch: byte) -> int { + return -1; + } +} + +var ok: bool; +def main(a: int) -> bool { + var x = C.new(); + ok = a <= 0; + return x.parse_array() != 4; +} diff --git a/test/core/zjson07.v3 b/test/core/zjson07.v3 new file mode 100644 index 000000000..df86af8c0 --- /dev/null +++ b/test/core/zjson07.v3 @@ -0,0 +1,18 @@ +//@execute 0=4; 1=-1; 2=4 +def parse_array(a: bool) -> int { + if (a) return -1; + while (cond(a)) { + if (ok) return -2; + } + if (a) return -3; + return 4; +} +def cond(a: bool) -> bool { + return false; +} + +var ok: bool; +def main(a: int) -> int { + ok = a > 0; + return parse_array(a == 1); +} diff --git a/test/core/zjson08.v3 b/test/core/zjson08.v3 new file mode 100644 index 000000000..1add0726e --- /dev/null +++ b/test/core/zjson08.v3 @@ -0,0 +1,16 @@ +//@execute 0=true; 1=false; 2=false +def parse_array(a: int) -> int { + if (a <= 0) return -1; + while (opt1(a) != -1) { + if (a <= 0) return -2; + } + if (a <= 0) return -3; + return 4; +} +def opt1(a: int) -> int { + return -1; +} + +def main(a: int) -> bool { + return parse_array(a) != 4; +}