From 5f44a401a8b8dc0cbc92f8eacb96ab814df2e839 Mon Sep 17 00:00:00 2001 From: "Ben L. Titzer" Date: Wed, 19 Jun 2024 18:43:33 -0400 Subject: [PATCH] Fixing devirtualization of ClassGetVirtual for vacuous case --- aeneas/src/ir/Normalization.v3 | 2 +- aeneas/src/ir/SsaNormalizer.v3 | 5 +- aeneas/src/mach/MachBackend.v3 | 1 + aeneas/src/main/Version.v3 | 2 +- aeneas/src/x86-64/X86_64Backend.v3 | 6 ++- test/core/zcha00.v3 | 54 +++++++++++++++++++ test/core/zcha01.v3 | 40 ++++++++++++++ test/core/zcha02.v3 | 50 ++++++++++++++++++ test/core/zcha03.v3 | 46 +++++++++++++++++ test/core/zcha04.v3 | 45 ++++++++++++++++ test/core/zcha05.v3 | 41 +++++++++++++++ test/core/zcha06.v3 | 45 ++++++++++++++++ test/core/zcha07.v3 | 37 +++++++++++++ test/core/zcha08.v3 | 36 +++++++++++++ test/core/zcha09.v3 | 37 +++++++++++++ test/core/zcha10.v3 | 37 +++++++++++++ test/core/zcha11.v3 | 37 +++++++++++++ test/core/zcha13.v3 | 39 ++++++++++++++ test/core/zcha14.v3 | 33 ++++++++++++ test/core/zcha15.v3 | 27 ++++++++++ test/core/zcha16.v3 | 23 +++++++++ test/core/zhost00.v3 | 83 ++++++++++++++++++++++++++++++ 22 files changed, 722 insertions(+), 4 deletions(-) create mode 100644 test/core/zcha00.v3 create mode 100644 test/core/zcha01.v3 create mode 100644 test/core/zcha02.v3 create mode 100644 test/core/zcha03.v3 create mode 100644 test/core/zcha04.v3 create mode 100644 test/core/zcha05.v3 create mode 100644 test/core/zcha06.v3 create mode 100644 test/core/zcha07.v3 create mode 100644 test/core/zcha08.v3 create mode 100644 test/core/zcha09.v3 create mode 100644 test/core/zcha10.v3 create mode 100644 test/core/zcha11.v3 create mode 100644 test/core/zcha13.v3 create mode 100644 test/core/zcha14.v3 create mode 100644 test/core/zcha15.v3 create mode 100644 test/core/zcha16.v3 create mode 100644 test/core/zhost00.v3 diff --git a/aeneas/src/ir/Normalization.v3 b/aeneas/src/ir/Normalization.v3 index be4f5b181..97af139e8 100644 --- a/aeneas/src/ir/Normalization.v3 +++ b/aeneas/src/ir/Normalization.v3 @@ -500,7 +500,7 @@ class ReachabilityNormalizer(config: NormalizerConfig, ra: ReachabilityAnalyzer) if (rv.mtable != null) return; var rm = rv.raMethod, rc = ra.getClass(rm.receiver); var size = rc.maxClassId - rc.minClassId; - if (ra.compiler.RaDevirtualize && size < 2) return; // no need for an mtable + if (ra.compiler.RaDevirtualize && size == 1) return; // no need for an mtable var table = Array.new(size), mtable = IrMtable.new(rm.norm, rc.minClassId, table); rv.mtable = mtable; diff --git a/aeneas/src/ir/SsaNormalizer.v3 b/aeneas/src/ir/SsaNormalizer.v3 index d3a32b9cf..d26daa39b 100644 --- a/aeneas/src/ir/SsaNormalizer.v3 +++ b/aeneas/src/ir/SsaNormalizer.v3 @@ -198,11 +198,14 @@ class SsaRaNormalizer extends SsaRebuilder { mapN(i_old, [funcRef(extractMethodRef(orig, method).1), obj]); } ClassGetVirtual(method) => { - var t = extractVirtualRef(orig, method), obj = genRef1(args[0]); + var t = extractVirtualRef(orig, method), obj = genRef1(args[0]), m = t.1; if (t.2) { // still a virtual dispatch mapN(i_old, [curBlock.opGetSelector(t.1, obj), obj]); } else { addNullCheck(i_old, obj); + if (m.member.facts.M_ABSTRACT) { + return mapN(i_old, [newGraph.nullConst(AnyFunction.TYPE), newGraph.nullConst(AnyRef.TYPE)]); + } mapN(i_old, [funcRef(t.1), obj]); } } diff --git a/aeneas/src/mach/MachBackend.v3 b/aeneas/src/mach/MachBackend.v3 index dda5f4a56..72fa06596 100644 --- a/aeneas/src/mach/MachBackend.v3 +++ b/aeneas/src/mach/MachBackend.v3 @@ -341,6 +341,7 @@ class SsaMachGen(context: SsaContext, mach: MachProgram, regSet: MachRegSet, w: var render: StringBuilder -> StringBuilder; if (context.spec != null) context.spec.render(out); else if (context.method != null) context.method.renderLong(out); + if (CLOptions.PRINT_PATCH.val) out.put1(" @ 0x%x", w.posAddr()); out.outln(); // print instructions var indent = 1; diff --git a/aeneas/src/main/Version.v3 b/aeneas/src/main/Version.v3 index 9835677bf..325defb97 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.1739"; + def version: string = "III-7.1740"; var buildData: string; } diff --git a/aeneas/src/x86-64/X86_64Backend.v3 b/aeneas/src/x86-64/X86_64Backend.v3 index 15311ce35..d15ecd3a8 100644 --- a/aeneas/src/x86-64/X86_64Backend.v3 +++ b/aeneas/src/x86-64/X86_64Backend.v3 @@ -300,13 +300,17 @@ class X86_64Backend extends MachBackend { return frame; } def patchCodeAddr(w: DataWriter, a: Addr, posAddr: int) { - var abs = mach.absolute(a); if (CLOptions.PRINT_PATCH.val) { TerminalBuffer.new() .puts("patch-code @0x") .putx(posAddr) .puts(" <- ") .putcv(a, null) + .outt(); + } + var abs = mach.absolute(a); + if (CLOptions.PRINT_PATCH.val) { + TerminalBuffer.new() .puts(" = 0x") .putx(abs) .outln(); diff --git a/test/core/zcha00.v3 b/test/core/zcha00.v3 new file mode 100644 index 000000000..d9ff4f89f --- /dev/null +++ b/test/core/zcha00.v3 @@ -0,0 +1,54 @@ +//@execute 0=18 + +var global = 0; + +class Base { + def m() { global = 18; } +} + +class A extends Base { +} + +class A1 extends A { + def m() { global = 19; } +} + +class A2 extends A { +} + +class B extends Base { + def m() { global = 20; } +} + +class B1 extends B { + def m() { global = 21; } +} + +class B2 extends B { +} + +def run(f: void -> void) { + f(); +} + +def dispatch(obj: Base) { + match (obj) { + x: A1 => run(x.m); + x: A2 => run(x.m); + x: A => run(x.m); + x: B1 => run(x.m); + x: B2 => run(x.m); + x: B => run(x.m); + x: Base => run(x.m); + } +} + +def objs = [ + Base.new() +]; + +def main(a: int) -> int { + global = 0; + dispatch(objs[a]); + return global; +} diff --git a/test/core/zcha01.v3 b/test/core/zcha01.v3 new file mode 100644 index 000000000..64130977e --- /dev/null +++ b/test/core/zcha01.v3 @@ -0,0 +1,40 @@ +//@execute 0=18 + +var global = 0; + +class Base { + def m() { global = 18; } +} + +class A extends Base { +} + +class A1 extends A { + def m() { global = 19; } +} + +class A2 extends A { +} + +def run(f: void -> void) { + f(); +} + +def dispatch(obj: Base) { + match (obj) { + x: A1 => run(x.m); + x: A2 => run(x.m); + x: A => run(x.m); + x: Base => run(x.m); + } +} + +def objs = [ + Base.new() +]; + +def main(a: int) -> int { + global = 0; + dispatch(objs[a]); + return global; +} diff --git a/test/core/zcha02.v3 b/test/core/zcha02.v3 new file mode 100644 index 000000000..6be86389a --- /dev/null +++ b/test/core/zcha02.v3 @@ -0,0 +1,50 @@ +//@execute 0=18 + +var global = 0; + +class Base { + def m() { global = 18; } +} + +class A extends Base { +} + +class A1 extends A { + def m() { global = 19; } +} + +class A2 extends A { +} + +class B extends Base { + def m() { global = 20; } +} + +class B1 extends B { + def m() { global = 21; } +} + +def run(f: void -> void) { + f(); +} + +def dispatch(obj: Base) { + match (obj) { + x: A1 => run(x.m); + x: A2 => run(x.m); + x: A => run(x.m); + x: B1 => run(x.m); + x: B => run(x.m); + x: Base => run(x.m); + } +} + +def objs = [ + Base.new() +]; + +def main(a: int) -> int { + global = 0; + dispatch(objs[a]); + return global; +} diff --git a/test/core/zcha03.v3 b/test/core/zcha03.v3 new file mode 100644 index 000000000..b28923eb3 --- /dev/null +++ b/test/core/zcha03.v3 @@ -0,0 +1,46 @@ +//@execute 0=18 + +var global = 0; + +class Base { + def m() { global = 18; } +} + +class A extends Base { +} + +class A1 extends A { + def m() { global = 19; } +} + +class B extends Base { + def m() { global = 20; } +} + +class B1 extends B { + def m() { global = 21; } +} + +def run(f: void -> void) { + f(); +} + +def dispatch(obj: Base) { + match (obj) { + x: A1 => run(x.m); + x: A => run(x.m); + x: B1 => run(x.m); + x: B => run(x.m); + x: Base => run(x.m); + } +} + +def objs = [ + Base.new() +]; + +def main(a: int) -> int { + global = 0; + dispatch(objs[a]); + return global; +} diff --git a/test/core/zcha04.v3 b/test/core/zcha04.v3 new file mode 100644 index 000000000..96d6e6f1c --- /dev/null +++ b/test/core/zcha04.v3 @@ -0,0 +1,45 @@ +//@execute 0=18 + +var global = 0; + +class Base { + def m() { global = 18; } +} + +class A extends Base { +} + +class A1 extends A { + def m() { global = 19; } +} + +class B extends Base { + def m() { global = 20; } +} + +class B1 extends B { +} + +def run(f: void -> void) { + f(); +} + +def dispatch(obj: Base) { + match (obj) { + x: A1 => run(x.m); + x: A => run(x.m); + x: B1 => run(x.m); + x: B => run(x.m); + x: Base => run(x.m); + } +} + +def objs = [ + Base.new() +]; + +def main(a: int) -> int { + global = 0; + dispatch(objs[a]); + return global; +} diff --git a/test/core/zcha05.v3 b/test/core/zcha05.v3 new file mode 100644 index 000000000..8360f69f7 --- /dev/null +++ b/test/core/zcha05.v3 @@ -0,0 +1,41 @@ +//@execute 0=18 + +var global = 0; + +class Base { + def m() { global = 18; } +} + +class A extends Base { +} + +class A1 extends A { + def m() { global = 19; } +} + +class B extends Base { + def m() { global = 20; } +} + +def run(f: void -> void) { + f(); +} + +def dispatch(obj: Base) { + match (obj) { + x: A1 => run(x.m); + x: A => run(x.m); + x: B => run(x.m); + x: Base => run(x.m); + } +} + +def objs = [ + Base.new() +]; + +def main(a: int) -> int { + global = 0; + dispatch(objs[a]); + return global; +} diff --git a/test/core/zcha06.v3 b/test/core/zcha06.v3 new file mode 100644 index 000000000..ca9763fae --- /dev/null +++ b/test/core/zcha06.v3 @@ -0,0 +1,45 @@ +//@execute 0=18 + +var global = 0; + +class Base { + def m() { global = 18; } +} + +class A extends Base { +} + +class A1 extends A { + def m() { global = 19; } +} + +class B extends Base { +} + +class B1 extends B { + def m() { global = 21; } +} + +def run(f: void -> void) { + f(); +} + +def dispatch(obj: Base) { + match (obj) { + x: A1 => run(x.m); + x: A => run(x.m); + x: B1 => run(x.m); + x: B => run(x.m); + x: Base => run(x.m); + } +} + +def objs = [ + Base.new() +]; + +def main(a: int) -> int { + global = 0; + dispatch(objs[a]); + return global; +} diff --git a/test/core/zcha07.v3 b/test/core/zcha07.v3 new file mode 100644 index 000000000..856498e69 --- /dev/null +++ b/test/core/zcha07.v3 @@ -0,0 +1,37 @@ +//@execute 0=18 + +var global = 0; + +class Base { + def m() { global = 18; } +} + +class B extends Base { + def m() { global = 20; } +} + +class B1 extends B { + def m() { global = 21; } +} + +def run(f: void -> void) { + f(); +} + +def dispatch(obj: Base) { + match (obj) { + x: B1 => run(x.m); + x: B => run(x.m); + x: Base => run(x.m); + } +} + +def objs = [ + Base.new() +]; + +def main(a: int) -> int { + global = 0; + dispatch(objs[a]); + return global; +} diff --git a/test/core/zcha08.v3 b/test/core/zcha08.v3 new file mode 100644 index 000000000..d87a27605 --- /dev/null +++ b/test/core/zcha08.v3 @@ -0,0 +1,36 @@ +//@execute 0=18 + +var global = 0; + +class Base { + def m() { global = 18; } +} + +class B extends Base { +} + +class B1 extends B { + def m() { global = 21; } +} + +def run(f: void -> void) { + f(); +} + +def dispatch(obj: Base) { + match (obj) { + x: B1 => run(x.m); + x: B => run(x.m); + x: Base => run(x.m); + } +} + +def objs = [ + Base.new() +]; + +def main(a: int) -> int { + global = 0; + dispatch(objs[a]); + return global; +} diff --git a/test/core/zcha09.v3 b/test/core/zcha09.v3 new file mode 100644 index 000000000..fa9a74505 --- /dev/null +++ b/test/core/zcha09.v3 @@ -0,0 +1,37 @@ +//@execute 0=18; 1=20; 2=21 + +var global = 0; + +class Base { + def m() { global = 18; } +} + +class B extends Base { + def m() { global = 20; } +} + +class B1 extends B { + def m() { global = 21; } +} + +def run(f: void -> void) { + f(); +} + +def dispatch(obj: Base) { + match (obj) { + x: B1 => run(x.m); + x: B => run(x.m); + x: Base => run(x.m); + } +} + +def objs = [ + Base.new(), B.new(), B1.new() +]; + +def main(a: int) -> int { + global = 0; + dispatch(objs[a]); + return global; +} diff --git a/test/core/zcha10.v3 b/test/core/zcha10.v3 new file mode 100644 index 000000000..efdb7ff44 --- /dev/null +++ b/test/core/zcha10.v3 @@ -0,0 +1,37 @@ +//@execute 0=18; 1=21 + +var global = 0; + +class Base { + def m() { global = 18; } +} + +class B extends Base { + def m() { global = 20; } +} + +class B1 extends B { + def m() { global = 21; } +} + +def run(f: void -> void) { + f(); +} + +def dispatch(obj: Base) { + match (obj) { + x: B1 => run(x.m); + x: B => run(x.m); + x: Base => run(x.m); + } +} + +def objs = [ + Base.new(), B1.new() +]; + +def main(a: int) -> int { + global = 0; + dispatch(objs[a]); + return global; +} diff --git a/test/core/zcha11.v3 b/test/core/zcha11.v3 new file mode 100644 index 000000000..49e7f6c9d --- /dev/null +++ b/test/core/zcha11.v3 @@ -0,0 +1,37 @@ +//@execute 0=18; 1=20 + +var global = 0; + +class Base { + def m() { global = 18; } +} + +class B extends Base { + def m() { global = 20; } +} + +class B1 extends B { + def m() { global = 21; } +} + +def run(f: void -> void) { + f(); +} + +def dispatch(obj: Base) { + match (obj) { + x: B1 => run(x.m); + x: B => run(x.m); + x: Base => run(x.m); + } +} + +def objs = [ + Base.new(), B.new() +]; + +def main(a: int) -> int { + global = 0; + dispatch(objs[a]); + return global; +} diff --git a/test/core/zcha13.v3 b/test/core/zcha13.v3 new file mode 100644 index 000000000..078e25aab --- /dev/null +++ b/test/core/zcha13.v3 @@ -0,0 +1,39 @@ +//@execute 0=!NullCheckException; 1=!NullCheckException; 2=!NullCheckException + +class Base { + def m() -> int { return 18; } +} + +class A extends Base { + def m() -> int { return 19; } +} + +class A1 extends A { + def m() -> int { return 20; } +} + +def get_Base(o: Base) -> void -> int { + return o.m; +} + +def get_A(o: A) -> void -> int { + return o.m; +} + +def get_A1(o: A1) -> void -> int { + return o.m; +} + +def obj_Base: Base; +def obj_A: A; +def obj_A1: A1; + +def main(a: int) -> int { + var f: void -> int; + match (a) { + 0 => f = get_Base(obj_Base); + 1 => f = get_A(obj_A); + 2 => f = get_A1(obj_A1); + } + return f(); +} diff --git a/test/core/zcha14.v3 b/test/core/zcha14.v3 new file mode 100644 index 000000000..abbc11d03 --- /dev/null +++ b/test/core/zcha14.v3 @@ -0,0 +1,33 @@ +//@execute 0=!NullCheckException; 1=!NullCheckException; 2=!NullCheckException + +class Base { + def m() -> int { return 18; } +} + +class A extends Base { + def m() -> int { return 19; } +} + +class A1 extends A { + def m() -> int { return 20; } +} + +def get_Base(o: Base) -> void -> int { + return o.m; +} + +def get_A1(o: A1) -> void -> int { + return o.m; +} + +def obj_Base: Base; +def obj_A1: A1; + +def main(a: int) -> int { + var f: void -> int; + match (a) { + 0 => f = get_Base(obj_Base); + 2 => f = get_A1(obj_A1); + } + return f(); +} diff --git a/test/core/zcha15.v3 b/test/core/zcha15.v3 new file mode 100644 index 000000000..2d9479da5 --- /dev/null +++ b/test/core/zcha15.v3 @@ -0,0 +1,27 @@ +//@execute 0=!NullCheckException; 1=!NullCheckException; 2=!NullCheckException + +class Base { + def m() -> int { return 18; } +} + +class A extends Base { + def m() -> int { return 19; } +} + +class A1 extends A { + def m() -> int { return 20; } +} + +def get_Base(o: Base) -> void -> int { + return o.m; +} + +def obj_Base: Base; + +def main(a: int) -> int { + var f: void -> int; + match (a) { + 0 => f = get_Base(obj_Base); + } + return f(); +} diff --git a/test/core/zcha16.v3 b/test/core/zcha16.v3 new file mode 100644 index 000000000..a322e1103 --- /dev/null +++ b/test/core/zcha16.v3 @@ -0,0 +1,23 @@ +//@execute 0=!NullCheckException; 1=!NullCheckException; 2=!NullCheckException + +class Base { + def m() -> int { return 18; } +} + +class A extends Base { + def m() -> int { return 19; } +} + +def get_Base(o: Base) -> void -> int { + return o.m; +} + +def obj_Base: Base; + +def main(a: int) -> int { + var f: void -> int; + match (a) { + 0 => f = get_Base(obj_Base); + } + return f(); +} diff --git a/test/core/zhost00.v3 b/test/core/zhost00.v3 new file mode 100644 index 000000000..10301075b --- /dev/null +++ b/test/core/zhost00.v3 @@ -0,0 +1,83 @@ +//@execute 0=0; 1=0; 2=0; 3=0; 4=0; 5=0; 6=0 +class Exportable { + def render(buf: StringBuilder) -> StringBuilder { + return buf.puts(""); + } +} +class SigDecl { } +class Function extends Object { } +class WasmFunction(decl: FuncDecl) extends Function { } +class Object extends Exportable { } +type Value { + case Ref(val: Object); + case I31(val: u31); + case I32(val: u32); + case I64(val: u64); + case F32(bits: u32); + case F64(bits: u64); + case V128(low: u64, high: u64); +} +component Values { + def render(v: Value, buf: StringBuilder) -> StringBuilder { + match (v) { + Ref(val) => match (val) { + x: HostObject => buf.put1("", x.render); + x: WasmFunction => buf.put1("", x.decl.func_index); + x: HeapStruct => { + var id = if(x.decl == null, -1, x.decl.heaptype_index); + buf.put1("", id); + } + x: HeapArray => { + var id = if(x.decl == null, -1, x.decl.heaptype_index); + buf.put1("", id); + } + x: Object => x.render(buf); + null => buf.puts(""); + } + I31(val) => buf.put1("i31:%d", u32.view(val)); + I32(val) => buf.put1("%d", val); + I64(val) => buf.put1("%duL", val); + F32(val) => buf.put1("f32:%x", val); + F64(val) => buf.put1("f64:%x", val); + V128(low, high) => buf.puts("v128:").putx_64(high).putx_64(low); + } + return buf; + } +} +class StringBuilder { + def puts(str: string) -> this { } + def put1(fmt: string, arg: T) -> this { } + def putx_64(v: u64) -> this { } +} +class HostObject extends Object { + def render(buf: StringBuilder) -> StringBuilder { + return buf.puts(""); + } +} +class HeapObject(decl: HeapTypeDecl, vals: Array) extends Object {} +class HeapStruct extends HeapObject { + new(decl: StructDecl, vals: Array) super(decl, vals) { } +} +class HeapArray extends HeapObject { + new(decl: ArrayDecl, vals: Array) super(decl, vals) { } // XXX: unboxed prim arrays +} + +class HeapTypeDecl(heaptype_index: int) { } +class FuncDecl(func_index: int) { } +class StructDecl extends HeapTypeDecl(-2) { } +class ArrayDecl extends HeapTypeDecl(-3) { } + +def values = [ + Value.I32(11), Value.I32(12), + Value.I64(21), Value.I64(22), + Value.F32(31), Value.F32(32), + Value.F64(41), Value.F64(42), + Value.V128(51, 52), Value.V128(53, 54), + Value.Ref(WasmFunction.new(FuncDecl.new(11))) +]; + + +def main(a: int) -> int { + Values.render(values[a], StringBuilder.new()); + return 0; +}