From d47da35f9512909444ff4ff4eb3c75a67f2489bc Mon Sep 17 00:00:00 2001 From: "Ben L. Titzer" Date: Tue, 19 Nov 2024 21:04:08 -0500 Subject: [PATCH] [unboxing] Fix bugs with enum sets (#301) --- aeneas/src/ir/Normalization.v3 | 9 ++++- aeneas/src/jvm/JvmTarget.v3 | 6 ++++ aeneas/src/os/Linux.v3 | 5 +++ aeneas/src/v3/V3Enum.v3 | 4 +-- aeneas/src/wasm/WasmTarget.v3 | 2 ++ aeneas/src/x86-64/X86_64Darwin.v3 | 2 ++ aeneas/src/x86/X86Darwin.v3 | 4 +++ test/variants/unboxed_enum_set00.v3 | 26 +++++++++++++++ test/variants/unboxed_enum_set01.v3 | 27 +++++++++++++++ test/variants/unboxed_enum_set02.v3 | 29 ++++++++++++++++ test/variants/unboxed_enum_set03.v3 | 44 +++++++++++++++++++++++++ test/variants/unboxed_enum_set04.v3 | 44 +++++++++++++++++++++++++ test/variants/unboxed_enum_set05.v3 | 51 +++++++++++++++++++++++++++++ test/variants/unboxed_enum_set06.v3 | 42 ++++++++++++++++++++++++ test/variants/unboxed_enum_set07.v3 | 47 ++++++++++++++++++++++++++ test/variants/unboxed_enum_set08.v3 | 48 +++++++++++++++++++++++++++ test/variants/unboxed_enum_set09.v3 | 50 ++++++++++++++++++++++++++++ 17 files changed, 437 insertions(+), 3 deletions(-) create mode 100644 test/variants/unboxed_enum_set00.v3 create mode 100644 test/variants/unboxed_enum_set01.v3 create mode 100644 test/variants/unboxed_enum_set02.v3 create mode 100644 test/variants/unboxed_enum_set03.v3 create mode 100644 test/variants/unboxed_enum_set04.v3 create mode 100644 test/variants/unboxed_enum_set05.v3 create mode 100644 test/variants/unboxed_enum_set06.v3 create mode 100644 test/variants/unboxed_enum_set07.v3 create mode 100644 test/variants/unboxed_enum_set08.v3 create mode 100644 test/variants/unboxed_enum_set09.v3 diff --git a/aeneas/src/ir/Normalization.v3 b/aeneas/src/ir/Normalization.v3 index 6eb9b1b39..609364547 100644 --- a/aeneas/src/ir/Normalization.v3 +++ b/aeneas/src/ir/Normalization.v3 @@ -24,9 +24,14 @@ class NormalizerConfig { if (maxr < MaxReturnValues) MaxReturnValues = maxr; } } +def getB32Or64(width: byte) -> Scalar.set { + return if(width <= 32, Scalar.B32 | Scalar.B64, Scalar.B64); +} def defaultGetScalar(compiler: Compiler, prog: Program, t: Type) -> Scalar.set { match (t) { - x: IntType => return if(x.width <= 32, Scalar.B32 | Scalar.B64, Scalar.B64); // XXX: Scalar.R32, once packed refs + x: EnumType => return getB32Or64(x.enumDecl.tagType.width); + x: EnumSetType => return getB32Or64(x.repType.width); + x: IntType => return getB32Or64(x.width); x: FloatType => return if(x.is64, Scalar.F64, Scalar.F32); x: BoolType => return Scalar.B32 | Scalar.B64; _ => return Scalar.Ref; @@ -35,6 +40,8 @@ def defaultGetScalar(compiler: Compiler, prog: Program, t: Type) -> Scalar.set { def defaultGetBitWidth(compiler: Compiler, prog: Program, t: Type) -> byte { var target = compiler.target; match (t) { + x: EnumType => return x.enumDecl.tagType.width; + x: EnumSetType => return x.repType.width; x: IntType => return x.width; x: FloatType => return x.total_width; x: BoolType => return 1; diff --git a/aeneas/src/jvm/JvmTarget.v3 b/aeneas/src/jvm/JvmTarget.v3 index 4e45698f3..be4c95e5c 100644 --- a/aeneas/src/jvm/JvmTarget.v3 +++ b/aeneas/src/jvm/JvmTarget.v3 @@ -49,6 +49,12 @@ class JvmTarget extends Target { match (t) { x: BoolType => return Scalar.B32 | Scalar.B64; x: IntType => return if(x.width <= 32, Scalar.B32 | Scalar.B64, Scalar.B64); + x: EnumType => { + return Scalar.B32 | Scalar.B64; + } + x: EnumSetType => { + return if(x.repType.width <= 32, Scalar.B32 | Scalar.B64, Scalar.B64); + } x: FloatType => return if(x.is64, Scalar.F64, Scalar.F32); _ => return Scalar.Ref; } diff --git a/aeneas/src/os/Linux.v3 b/aeneas/src/os/Linux.v3 index 592f6b9d2..2471c5737 100644 --- a/aeneas/src/os/Linux.v3 +++ b/aeneas/src/os/Linux.v3 @@ -32,12 +32,17 @@ class LinuxTarget extends Target { match (t) { x: BoolType => return Scalar.B32 | Scalar.B64; x: IntType => return if(x.width <= 32, Scalar.B32 | Scalar.B64, Scalar.B64); // XXX: Scalar.R64, once packed refs + x: EnumSetType => { + return if(x.repType.width <= 32, Scalar.B32 | Scalar.B64, Scalar.B64); // XXX: Scalar.R64, once packed refs + } x: FloatType => return if(x.is64, Scalar.F64 | Scalar.B64, Scalar.F32 | Scalar.B32); _ => return Scalar.R32; } } else { match (t) { x: BoolType => return Scalar.B64; + x: EnumType, + x: EnumSetType, x: IntType => return Scalar.B64; // XXX: Scalar.R64, once packed refs x: FloatType => return Scalar.F64 | Scalar.B64; _ => return Scalar.R64; diff --git a/aeneas/src/v3/V3Enum.v3 b/aeneas/src/v3/V3Enum.v3 index 39d9b83cf..c06cc3721 100644 --- a/aeneas/src/v3/V3Enum.v3 +++ b/aeneas/src/v3/V3Enum.v3 @@ -32,10 +32,10 @@ class EnumType extends Type { return names; } def byteSize() -> int { - return IntType.!(enumDecl.tagType).byteSize(); + return enumDecl.tagType.byteSize(); } def packedByteSize() -> int { - return IntType.!(enumDecl.tagType).packedByteSize(); + return enumDecl.tagType.packedByteSize(); } } // The type for user-declared enums' sets. diff --git a/aeneas/src/wasm/WasmTarget.v3 b/aeneas/src/wasm/WasmTarget.v3 index eb111d104..ea45ca7e5 100644 --- a/aeneas/src/wasm/WasmTarget.v3 +++ b/aeneas/src/wasm/WasmTarget.v3 @@ -93,6 +93,8 @@ class WasmTarget extends Target { private def getScalar(compiler: Compiler, prog: Program, t: Type) -> Scalar.set { var none: Scalar.set; match (t) { + x: EnumType => return if(x.enumDecl.tagType.width <= 32, Scalar.B32 | Scalar.B64, Scalar.B64); // XXX: Scalar.R32, once packed refs + x: EnumSetType => return if(x.repType.width <= 32, Scalar.B32 | Scalar.B64, Scalar.B64); // XXX: Scalar.R32, once packed refs x: BoolType => return Scalar.B32 | Scalar.B64; x: IntType => return if(x.width <= 32, Scalar.B32 | Scalar.B64, Scalar.B64); // XXX: Scalar.R64, once packed refs x: FloatType => return if(x.is64, Scalar.F64 | Scalar.B64, Scalar.F32 | Scalar.B32); diff --git a/aeneas/src/x86-64/X86_64Darwin.v3 b/aeneas/src/x86-64/X86_64Darwin.v3 index a7d609831..1391b2ebf 100644 --- a/aeneas/src/x86-64/X86_64Darwin.v3 +++ b/aeneas/src/x86-64/X86_64Darwin.v3 @@ -37,6 +37,8 @@ class X86_64DarwinTarget extends Target { } private def getScalar(compiler: Compiler, prog: Program, t: Type) -> Scalar.set { match (t) { + x: EnumType => return Scalar.B64; + x: EnumSetType => return Scalar.B64; x: BoolType => return Scalar.B64; x: IntType => return Scalar.B64; // XXX: Scalar.R64, once packed refs x: FloatType => return Scalar.F64 | Scalar.B64; diff --git a/aeneas/src/x86/X86Darwin.v3 b/aeneas/src/x86/X86Darwin.v3 index 7bbbf3f7e..4f4c010d4 100644 --- a/aeneas/src/x86/X86Darwin.v3 +++ b/aeneas/src/x86/X86Darwin.v3 @@ -23,6 +23,8 @@ class X86DarwinTarget extends Target { } private def getScalar(compiler: Compiler, prog: Program, t: Type) -> Scalar.set { match (t) { + x: EnumType => return if(x.enumDecl.tagType.width <= 32, Scalar.B32 | Scalar.B64, Scalar.B64); // XXX: Scalar.R32, once packed refs + x: EnumSetType => return if(x.repType.width <= 32, Scalar.B32 | Scalar.B64, Scalar.B64); // XXX: Scalar.R32, once packed refs x: BoolType => return Scalar.B32 | Scalar.B64; x: IntType => return if(x.width <= 32, Scalar.B32 | Scalar.B64, Scalar.B64); // XXX: Scalar.R32, once packed refs x: FloatType => return if(x.is64, Scalar.F64 | Scalar.B64, Scalar.F32 | Scalar.B32); @@ -32,6 +34,8 @@ class X86DarwinTarget extends Target { private def getBitWidth(compiler: Compiler, prog: Program, t: Type) -> byte { match (t) { x: BoolType => return 1; + x: EnumSetType => return x.repType.width; + x: EnumType => return x.enumDecl.tagType.width; x: IntType => return x.width; x: FloatType => return x.total_width; _ => return 32; diff --git a/test/variants/unboxed_enum_set00.v3 b/test/variants/unboxed_enum_set00.v3 new file mode 100644 index 000000000..829937977 --- /dev/null +++ b/test/variants/unboxed_enum_set00.v3 @@ -0,0 +1,26 @@ +//@execute 0=0; -1=16842753; 16842753=16842753 +enum E { + A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, Aa, Ab, Ac, Ad, Ae, Af, + B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, Ba, Bb, Bc, Bd, Be, Bf +} + +type T(s: E.set) #unboxed { + def intersect(that: T) -> T { + return T(this.s & that.s); + } +} + +def mask = T(E.A0 | E.B0 | E.B8); + +def main(a: int) -> int { + var x: E.set; + for (e in E) { + if ((a & (1 << e.tag)) != 0) x |= e; + } + var t = T(x).intersect(mask); + var r: int; + for (e in t.s) { + r |= (1 << e.tag); + } + return r; +} diff --git a/test/variants/unboxed_enum_set01.v3 b/test/variants/unboxed_enum_set01.v3 new file mode 100644 index 000000000..6d7693e37 --- /dev/null +++ b/test/variants/unboxed_enum_set01.v3 @@ -0,0 +1,27 @@ +//@execute 0=0; -1=16842753; 16842753=16842753 +enum E { + A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, Aa, Ab, Ac, Ad, Ae, Af, + B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, Ba, Bb, Bc, Bd, Be, Bf, + C0 +} + +type T(s: E.set) #unboxed { + def intersect(that: T) -> T { + return T(this.s & that.s); + } +} + +def mask = T(E.A0 | E.B0 | E.B8); + +def main(a: int) -> int { + var x: E.set; + for (e in E) { + if ((a & (1 << e.tag)) != 0) x |= e; + } + var t = T(x).intersect(mask); + var r: int; + for (e in t.s) { + r |= (1 << e.tag); + } + return r; +} diff --git a/test/variants/unboxed_enum_set02.v3 b/test/variants/unboxed_enum_set02.v3 new file mode 100644 index 000000000..48a9ea5b3 --- /dev/null +++ b/test/variants/unboxed_enum_set02.v3 @@ -0,0 +1,29 @@ +//@execute 0=0; -1=16842753; 16842753=16842753 +enum E { + A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, Aa, Ab, Ac, Ad, Ae, Af, + B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, Ba, Bb, Bc, Bd, Be, Bf, + C0 +} + +class C { } + +type T(c: C, s: E.set) #unboxed { + def intersect(that: T) -> T { + return T(C.new(), this.s & that.s); + } +} + +def mask = T(C.new(), E.A0 | E.B0 | E.B8); + +def main(a: int) -> int { + var x: E.set; + for (e in E) { + if ((a & (1 << e.tag)) != 0) x |= e; + } + var t = T(C.new(), x).intersect(mask); + var r: int; + for (e in t.s) { + r |= (1 << e.tag); + } + return r; +} diff --git a/test/variants/unboxed_enum_set03.v3 b/test/variants/unboxed_enum_set03.v3 new file mode 100644 index 000000000..858e77dc3 --- /dev/null +++ b/test/variants/unboxed_enum_set03.v3 @@ -0,0 +1,44 @@ +//@execute 0=0; -1=16842753; 16842753=16842753 +enum E { + A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, Aa, Ab, Ac, Ad, Ae, Af, + B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, Ba, Bb, Bc, Bd, Be, Bf, + C0 +} + +class C { } + +type T #unboxed { + case None(c0: C, c1: C); + case Set(c0: C, s: E.set); + + def intersect(that: T) -> T { + match (this) { + None => return this; + Set(c0, set0) => match (that) { + None => return that; + Set(_, set1) => return T.Set(c0, set0 & set1); + } + } + } + def set() -> E.set { + return if(T.Set.?(this), T.Set.!(this).s); + } +} + + + +def mask = T.Set(C.new(), E.A0 | E.B0 | E.B8); + +def main(a: int) -> int { + var x: E.set; + for (e in E) { + if ((a & (1 << e.tag)) != 0) x |= e; + } + var t = T.Set(C.new(), x).intersect(mask); + if (a == 9999) t = T.None(C.new(), C.new()); + var r: int; + for (e in t.set()) { + r |= (1 << e.tag); + } + return r; +} diff --git a/test/variants/unboxed_enum_set04.v3 b/test/variants/unboxed_enum_set04.v3 new file mode 100644 index 000000000..2059657a5 --- /dev/null +++ b/test/variants/unboxed_enum_set04.v3 @@ -0,0 +1,44 @@ +//@execute 0=0; -1=16842753; 16842753=16842753 +enum E { + A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, Aa, Ab, Ac, Ad, Ae, Af, + B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, Ba, Bb, Bc, Bd, Be, Bf, + C0 +} + +class C { } + +type T #unboxed { + case None(c0: C, c1: C); + case Set(c0: C, s: E.set); + + def intersect(that: T) -> T { + match (this) { + None(c0, c1) => return if(c0 == c1, this, this); + Set(c0, set0) => match (that) { + None => return that; + Set(_, set1) => return T.Set(c0, set0 & set1); + } + } + } + def set() -> E.set { + return if(T.Set.?(this), T.Set.!(this).s); + } +} + + + +def mask = T.Set(C.new(), E.A0 | E.B0 | E.B8); + +def main(a: int) -> int { + var x: E.set; + for (e in E) { + if ((a & (1 << e.tag)) != 0) x |= e; + } + var t = T.Set(C.new(), x).intersect(mask); + if (a == 9999) t = T.None(C.new(), C.new()); + var r: int; + for (e in t.set()) { + r |= (1 << e.tag); + } + return r; +} diff --git a/test/variants/unboxed_enum_set05.v3 b/test/variants/unboxed_enum_set05.v3 new file mode 100644 index 000000000..e1434eb78 --- /dev/null +++ b/test/variants/unboxed_enum_set05.v3 @@ -0,0 +1,51 @@ +//@execute 0=0; -1=16842753; 16842753=16842753 +enum E { + A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, Aa, Ab, Ac, Ad, Ae, Af, + B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, Ba, Bb, Bc, Bd, Be, Bf, + C0 +} + +class C { } + +type T #unboxed { + case None(c0: C); + case Set(s: E.set); + + def intersect(that: T) -> T { + match (this) { + None(c0) => return this; + Set(s0) => match (that) { + None => return that; + Set(s1) => return T.Set(s0 & s1); + } + } + } + def set() -> E.set { + return if(T.Set.?(this), T.Set.!(this).s); + } + def bits() -> int { + if (T.None.?(this)) return 0; + var s = T.Set.!(this).s; + var r: int; + for (e in s) r |= (1 << e.tag); + return r; + } +} + + + +def some = [T.Set(E.A0 | E.B0 | E.B8), T.None(C.new())]; + +def make(a: int) -> E.set { + var x: E.set; + for (e in E) { + if ((a & (1 << e.tag)) != 0) x |= e; + } + return x; +} + +def main(a: int) -> int { + var x = make(a); + var t = T.Set(x).intersect(some[0]); + return t.bits(); +} diff --git a/test/variants/unboxed_enum_set06.v3 b/test/variants/unboxed_enum_set06.v3 new file mode 100644 index 000000000..6c36b80fe --- /dev/null +++ b/test/variants/unboxed_enum_set06.v3 @@ -0,0 +1,42 @@ +//@execute 0=0; -1=16842753; 16842753=16842753 +enum E { + A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, Aa, Ab, Ac, Ad, Ae, Af, + B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, Ba, Bb, Bc, Bd, Be, Bf +} + +class C { } + +type T #unboxed { + case None(c0: C, c1: C); + case Set(c0: C, s: E.set); + + def intersect(that: T) -> T { + match (this) { + None(c0, c1) => return if(c0 == c1, this, this); + Set(c0, set0) => match (that) { + None => return that; + Set(_, set1) => return T.Set(c0, set0 & set1); + } + } + } + def set() -> E.set { + return if(T.Set.?(this), T.Set.!(this).s); + } +} + + + +def some = [T.Set(C.new(), E.A0 | E.B0 | E.B8), T.None(C.new(), C.new())]; + +def main(a: int) -> int { + var x: E.set; + for (e in E) { + if ((a & (1 << e.tag)) != 0) x |= e; + } + var t = T.Set(C.new(), x).intersect(some[0]); + var r: int; + for (e in t.set()) { + r |= (1 << e.tag); + } + return r; +} diff --git a/test/variants/unboxed_enum_set07.v3 b/test/variants/unboxed_enum_set07.v3 new file mode 100644 index 000000000..49178232d --- /dev/null +++ b/test/variants/unboxed_enum_set07.v3 @@ -0,0 +1,47 @@ +//@execute 0=0; -1=16842753; 16842753=16842753 +enum E { + A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, Aa, Ab, Ac, Ad, Ae, Af, + B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, Ba, Bb, Bc, Bd, Be, Bf +} + +class C { } + +type T #unboxed { + case None(c0: C, c1: C); + case Set(s: E.set); + + def intersect(that: T) -> T { + match (this) { + None(c0, c1) => return if(c0 == c1, this, this); + Set(set0) => match (that) { + None => return that; + Set(set1) => return T.Set(set0 & set1); + } + } + } + def set() -> E.set { + return if(T.Set.?(this), T.Set.!(this).s); + } +} + + + +def some = [T.Set(E.A0 | E.B0 | E.B8), T.None(C.new(), C.new())]; + +def make(a: int) -> E.set { + var x: E.set; + for (e in E) { + if ((a & (1 << e.tag)) != 0) x |= e; + } + return x; +} + +def main(a: int) -> int { + var x = make(a); + var t = T.Set(x).intersect(some[0]); + var r: int; + for (e in t.set()) { + r |= (1 << e.tag); + } + return r; +} diff --git a/test/variants/unboxed_enum_set08.v3 b/test/variants/unboxed_enum_set08.v3 new file mode 100644 index 000000000..01da7fa39 --- /dev/null +++ b/test/variants/unboxed_enum_set08.v3 @@ -0,0 +1,48 @@ +//@execute 0=0; -1=16842753; 16842753=16842753 +enum E { + A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, Aa, Ab, Ac, Ad, Ae, Af, + B0, B1, B2, B3, B4, B5, B6, B7, B8, B9, Ba, Bb, Bc, Bd, Be, Bf +} + +class C { } + +type T #unboxed { + case None(c0: C, c1: C); + case Set(s: E.set); + + def intersect(that: T) -> T { + match (this) { + None(c0, c1) => return if(c0 == c1, this, this); + Set(set0) => match (that) { + None => return that; + Set(set1) => return T.Set(set0 & set1); + } + } + } + def set() -> E.set { + return if(T.Set.?(this), T.Set.!(this).s); + } + def bits() -> int { + if (T.None.?(this)) return 0; + var s = T.Set.!(this).s; + var r: int; + for (e in s) r |= (1 << e.tag); + return r; + } +} + + + +def some = [T.Set(E.A0 | E.B0 | E.B8), T.None(C.new(), C.new())]; + +def make(a: int) -> E.set { + var x: E.set; + for (e in E) if ((a & (1 << e.tag)) != 0) x |= e; + return x; +} + +def main(a: int) -> int { + var x = make(a); + var t = T.Set(x).intersect(some[0]); + return t.bits(); +} diff --git a/test/variants/unboxed_enum_set09.v3 b/test/variants/unboxed_enum_set09.v3 new file mode 100644 index 000000000..680c87b54 --- /dev/null +++ b/test/variants/unboxed_enum_set09.v3 @@ -0,0 +1,50 @@ +//@execute 0=0; -1=7; 16842753=1 +enum E { + A0, A1, A2 +} + +class C { } + +type T #unboxed { + case None(c0: C, c1: C); + case Blah; + case Set(s: E.set); + + def intersect(that: T) -> T { + match (this) { + None(c0, c1) => return if(c0 == c1, this, this); + Blah => return this; + Set(set0) => match (that) { + None => return that; + Blah => return that; + Set(set1) => return T.Set(set0 & set1); + } + } + } + def set() -> E.set { + return if(T.Set.?(this), T.Set.!(this).s); + } + def bits() -> int { + if (T.None.?(this)) return 0; + var s = T.Set.!(this).s; + var r: int; + for (e in s) r |= (1 << e.tag); + return r; + } +} + + + +def some = [T.Set(E.A0 | E.A1 | E.A2), T.None(C.new(), C.new())]; + +def make(a: int) -> E.set { + var x: E.set; + for (e in E) if ((a & (1 << e.tag)) != 0) x |= e; + return x; +} + +def main(a: int) -> int { + var x = make(a); + var t = T.Set(x).intersect(some[0]); + return t.bits(); +}