Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 94 additions & 17 deletions aeneas/src/core/Eval.v3
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ component Eval {
return b == null;
}
CLASS_QUERY, VARIANT_QUERY => {
if (V3.isEnum(tt)) {
// enum subtype query: check integer tag is in [tagLo, tagHi]
var decl = V3.getDecl(tt);
var tagVal = Int.unbox(val);
return tagVal >= decl.tagLo && tagVal <= decl.tagHi;
}
if (val == null) return false;
if (tt.open()) return false;
return Record.?(val) && TypeSystem.isSubtype(Record.!(val).rtype, tt);
Expand Down Expand Up @@ -810,19 +816,34 @@ def evalOp(op: Operator, args: Arguments) -> Result {
return if(object != null, object.values[field.index]);
}
VariantGetMethod(method) => {
var ta = args.getTypeArgs();
if (V3.isEnum(ta[0])) {
var spec = lookupEnumVirtual(args, method);
return Closure.new(args.vals[0], spec);
}
var object = args.r(0);
var spec = args.getClosedIrSpec(method);
if (spec == null) return args.notFoldable(null);
return Closure.new(object, spec);
}
VariantGetVirtual(method) => {
var ta = args.getTypeArgs();
if (V3.isEnum(ta[0])) {
var spec = lookupEnumVirtual(args, method);
return Closure.new(args.vals[0], spec);
}
var object = getRecordReceiver(args);
var spec = args.getClosedIrSpec(method);
if (spec == null) return args.notFoldable(null);
spec = lookupVariantVirtual(args, object, spec);
return Closure.new(object, spec);
}
VariantGetSelector(selector) => {
var ta = args.getTypeArgs();
if (V3.isEnum(ta[0])) {
var spec = lookupEnumVirtual(args, selector);
return FuncVal.new(spec);
}
var object = getRecordReceiver(args);
var spec = args.getClosedIrSpec(selector);
if (spec == null) return args.notFoldable(null);
Expand All @@ -831,8 +852,9 @@ def evalOp(op: Operator, args: Arguments) -> Result {
}
//----------------------------------------------------------------------------
NullCheck => {
var object = args.r(0);
if (object == null) return args.throw(V3Exception.NullCheck, null);
var val = args.vals[0];
if (val == null) return args.throw(V3Exception.NullCheck, null);
if (!Record.?(val)) return Values.BOTTOM; // non-ref values (e.g. enums) are never null
return Values.BOTTOM;
}
BoundsCheck => {
Expand Down Expand Up @@ -868,6 +890,7 @@ def evalOp(op: Operator, args: Arguments) -> Result {
if (ft == null) return args.notFoldable(null);
if (tt == null) return args.notFoldable(null);
var val = args.vals[0];
if (tt == Oop.TYPE || ft == Oop.TYPE) return val;
var r = Eval.doCast0(ft, tt, val);
if (r.0) return r.1;
return args.throw(V3Exception.InternalError, "subsume should never fail");
Expand Down Expand Up @@ -914,13 +937,23 @@ def evalOp(op: Operator, args: Arguments) -> Result {
return args.tailCall(spec, object, 1, args.vals.length);
}
CallVariantVirtual(method) => {
var ta = args.getTypeArgs();
if (V3.isEnum(ta[0])) {
var spec = lookupEnumVirtual(args, method);
return args.tailCall(spec, args.vals[0], 1, args.vals.length);
}
var object = getRecordReceiver(args);
var spec = args.getClosedIrSpec(method);
if (spec == null) return args.notFoldable(null);
spec = lookupVariantVirtual(args, object, spec);
return args.tailCall(spec, object, 1, args.vals.length);
}
CallVariantSelector(selector) => {
var ta = args.getTypeArgs();
if (V3.isEnum(ta[0])) {
var spec = lookupEnumVirtual(args, selector);
return args.tailCall(spec, args.vals[0], 1, args.vals.length);
}
var object = getRecordReceiver(args);
var spec = args.getClosedIrSpec(selector);
if (spec == null) return args.notFoldable(null);
Expand Down Expand Up @@ -959,11 +992,11 @@ def evalOp(op: Operator, args: Arguments) -> Result {
}
RefLayoutGetField(offset, order) => {
var ref = args.ref(0);
return doRefLayoutGetField(args, args.getPrimTypeArg(1), ref, offset, order);
return doRefLayoutGetField(args, args.getTypeArg(1), ref, offset, order);
}
RefLayoutSetField(offset, order) => {
var ref = args.ref(0);
return doRefLayoutSetField(args, args.getPrimTypeArg(1), ref, offset, order, args.vals[1]);
return doRefLayoutSetField(args, args.getTypeArg(1), ref, offset, order, args.vals[1]);
}
RefLayoutAtRepeatedField(offset, scale, max) => {
var ref = args.ref(0);
Expand All @@ -977,25 +1010,25 @@ def evalOp(op: Operator, args: Arguments) -> Result {
var ref = args.ref(0);
var index = args.i(1);
if (u32.view(index) >= u32.view(max)) return args.throw(V3Exception.BoundsCheck, null);
return doRefLayoutGetField(args, args.getPrimTypeArg(1), ref, offset + scale * index, order);
return doRefLayoutGetField(args, args.getTypeArg(1), ref, offset + scale * index, order);
}
RefLayoutSetRepeatedField(offset, scale, max, order) => {
var ref = args.ref(0);
var index = args.i(1);
if (u32.view(index) >= u32.view(max)) return args.throw(V3Exception.BoundsCheck, null);
return doRefLayoutSetField(args, args.getPrimTypeArg(1), ref, offset + scale * index, order, args.vals[2]);
return doRefLayoutSetField(args, args.getTypeArg(1), ref, offset + scale * index, order, args.vals[2]);
}
ByteArrayGetField(offset, order) => {
var array = args.r(0);
var i_offset = if(ArrayRangeStart.?(args.vals[1]), ArrayRangeStart.!(args.vals[1]).start, args.i(1));
// XXX: Refactor so no intermediate ByteArrayOffset object needed
return doRefLayoutGetField(args, args.getPrimTypeArg(0), ByteArrayOffset.new(array, offset), i_offset, order);
return doRefLayoutGetField(args, args.getTypeArg(0), ByteArrayOffset.new(array, offset), i_offset, order);
}
ByteArraySetField(offset, order) => {
var array = args.r(0);
var i_offset = if(ArrayRangeStart.?(args.vals[1]), ArrayRangeStart.!(args.vals[1]).start, args.i(1));
// XXX: Refactor so no intermediate ByteArrayOffset object needed
return doRefLayoutSetField(args, args.getPrimTypeArg(0), ByteArrayOffset.new(array, offset), i_offset, order, args.vals[2]);
return doRefLayoutSetField(args, args.getTypeArg(0), ByteArrayOffset.new(array, offset), i_offset, order, args.vals[2]);
}

//----------------------------------------------------------------------------
Expand Down Expand Up @@ -1047,9 +1080,9 @@ def evalOp(op: Operator, args: Arguments) -> Result {
return args.unimplemented();
}

def doRefLayoutGetField(args: Arguments, fieldType: PrimType, ref: ByteArrayOffset, offset: int, order: ByteOrder) -> Result {
def doRefLayoutGetField(args: Arguments, fieldType: Type, ref: ByteArrayOffset, offset: int, order: ByteOrder) -> Result {
if (ref == null || ref.array == null) return args.throw(V3Exception.NullCheck, null);
var v = ref.read(order, offset, fieldType.packedByteSize);
var v = ref.read(order, offset, if(PrimType.?(fieldType), PrimType.!(fieldType).packedByteSize, ClassType.!(fieldType).byteSize));
match (fieldType) {
x: BoolType => {
return Bool.box((v & 1) != 0);
Expand All @@ -1064,18 +1097,20 @@ def doRefLayoutGetField(args: Arguments, fieldType: PrimType, ref: ByteArrayOffs
I64, U64 => return Long.box(long.view(v));
}
}
x: EnumType => {
if (v >= x.enumDecl.cases.length) v = 0; // out-of-bounds tag => 0
return Int.box(int.view(v)); // note: no long enum values
}
x: FloatType => {
if (x.is64) return Float64Val.new(v);
else return Float32Val.new(u32.view(v));
}
_ => return args.throw("EvalException", Strings.format1("invalid RefLayoutField type %q", fieldType.render));
_ => {
if (V3.isEnum(fieldType)) {
if (v >= ClassType.!(fieldType).classDecl.cases.length) v = 0;
return Int.box(int.view(v));
}
return args.throw("EvalException", Strings.format1("invalid RefLayoutField type %q", fieldType.render));
}
}
}
def doRefLayoutSetField(args: Arguments, fieldType: PrimType, ref: ByteArrayOffset, offset: int, order: ByteOrder, val: Val) -> Result {
def doRefLayoutSetField(args: Arguments, fieldType: Type, ref: ByteArrayOffset, offset: int, order: ByteOrder, val: Val) -> Result {
if (ref == null || ref.array == null) return args.throw(V3Exception.NullCheck, null);
var bits: u64 = 0, signed = false;
match (val) {
Expand All @@ -1085,7 +1120,8 @@ def doRefLayoutSetField(args: Arguments, fieldType: PrimType, ref: ByteArrayOffs
v: Float32Val => bits = v.bits;
v: Float64Val => bits = v.bits;
}
ref.write(order, offset, fieldType.packedByteSize, bits);
var size = if(PrimType.?(fieldType), PrimType.!(fieldType).packedByteSize, ClassType.!(fieldType).byteSize);
ref.write(order, offset, size, bits);
return Values.BOTTOM;
}

Expand Down Expand Up @@ -1147,6 +1183,47 @@ def lookupClassVirtual(args: Arguments, object: Record, spec: IrSpec) -> IrSpec
args.throw(V3Exception.NullCheck, null);
return null;
}
def lookupEnumVirtual(args: Arguments, member: IrMember) -> IrSpec {
var ta = args.getTypeArgs();
var enumType = ClassType.!(ta[0]);
var tag = Int.unbox(args.vals[0]);
var targetDecl = findEnumDeclForTag(enumType.classDecl, tag);
var ir = args.getProgram().ir;
var m: IrMethod;
// Check synthetic case decl first (Strategy B per-case overrides).
if (targetDecl.cases != null) {
for (c in targetDecl.cases) {
if (c.decl != null && c.decl.isSynthetic && c.enumTag == tag) {
var caseIc = ir.getIrClass(c.decl.typeCon.create0());
if (caseIc != null && member.index < caseIc.methods.length) {
var candidate = caseIc.methods[member.index];
if (candidate != null) { m = candidate; break; }
}
}
}
}
if (m == null) {
var vc = targetDecl;
while (vc != null) {
var ic = ir.makeIrClass(vc.getDeclaredType());
if (ic != null && member.index < ic.methods.length) {
var candidate = ic.methods[member.index];
if (candidate != null) { m = candidate; break; }
}
vc = vc.parentEnum;
}
}
if (m == null) m = IrMethod.!(member);
return IrSpec.new(ta[0], ta, m);
}
def findEnumDeclForTag(decl: VstClass, tag: int) -> VstClass {
for (l = decl.subtypeOrder; l != null; l = l.tail) {
if (l.head.tagLo <= tag && tag <= l.head.tagHi) {
return findEnumDeclForTag(l.head, tag);
}
}
return decl;
}
def lookupVariantVirtual(args: Arguments, object: Record, spec: IrSpec) -> IrSpec {
if (object != null) return args.getProgram().ir.resolveMethodImpl(object.rtype, spec);
return args.getProgram().ir.resolveVariantDefaultMethodImpl(spec);
Expand Down
6 changes: 6 additions & 0 deletions aeneas/src/core/Operator.v3
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,12 @@ component V3Op {
var paramTypes = Arrays.prepend(ftype, Function.getParamTypeArray(ftype));
return newOp0(Opcode.CallFunction, [ftype], paramTypes, Function.getReturnType(ftype));
}
def newCallFunctionDirect(ftype: Type) -> Operator {
ftype = Function.funcRefType(ftype);
if (ftype.typeCon.kind != Kind.FUNCREF) return V3.fail("only function types allowed");
var paramTypes = Arrays.prepend(ftype, Function.getParamTypeArray(ftype));
return newOp0(Opcode.CallFunction, [ftype], paramTypes, Function.getReturnType(ftype));
}
def newCreateClosure(methodRef: IrSpec, closure: Type) -> Operator {
var typeArgs = methodRef.typeArgs;
return newOp0(Opcode.CreateClosure(methodRef.asMethod()), typeArgs, [closure], methodRef.getBoundType());
Expand Down
9 changes: 9 additions & 0 deletions aeneas/src/core/Value.v3
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ class Val extends Result {
def equals(val: Val) -> bool { return this == val; }
def hash() -> int { return 0; }
}
// An OopInt wraps an integer that has been boxed to Oop (e.g. an enum tag).
// Distinct from null so that emitters can produce Integer.valueOf(v) on JVM
// or ref.i31(v) on wasm-gc, rather than a null reference.
class OopInt(v: int) extends Val {
def equals(other: Val) -> bool {
return OopInt.?(other) && OopInt.!(other).v == v;
}
def hash() -> int { return v; }
}
// An Exception represents an exceptional result (i.e. not a value), for example a
// safety violation such as !NullCheckException or !DivideByZeroException.
class Exception(error: string, msg: string, trace: List<Source>) extends Result { }
Expand Down
9 changes: 4 additions & 5 deletions aeneas/src/debug/Dwarf.v3
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,7 @@ class DwarfInfoSection(abbrev: DwarfAbbrevSection) {
encoding = if (V3.isSigned(x), DW.DW_ATE_signed, DW.DW_ATE_unsigned);
emitBaseType(t, byteSize, encoding);
}
x: ClassType => emitClassType(x);
x: EnumType => emitEnumType(x);
x: ClassType => if (x.classDecl.isEnum()) emitEnumType(x); else emitClassType(x);
x: ArrayType => emitArrayType(x);
_ => emitUnspecifiedType(t);
}
Expand Down Expand Up @@ -353,20 +352,20 @@ class DwarfInfoSection(abbrev: DwarfAbbrevSection) {
_ => ;
}
}
def emitEnumType(t: EnumType) {
def emitEnumType(t: ClassType) {
w.put_uleb32(abbrev.getAbbrev(DwarfAbbrevTag.EnumurationType));
buf.reset();
t.render(buf);
buf.send(w.putr);
w.putb(0);
w.putb(t.byteSize);
for (c in t.enumDecl.cases) {
for (c in t.classDecl.cases) {
w.put_uleb32(abbrev.getAbbrev(DwarfAbbrevTag.Enumurator));
buf.reset();
c.render(buf);
buf.send(w.putr);
w.putb(0);
w.put_uleb32(u32.!(c.tag));
w.put_uleb32(u32.!(c.localTag));
}
w.putb(0);
}
Expand Down
1 change: 1 addition & 0 deletions aeneas/src/ir/FunctionWrappers.v3
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,7 @@ class FunctionWrappers {
for (l = rc.subtypes; l != null; l = l.tail) {
var rcThis = l.head;
var impl = rn.resolveMethodImpl(rcThis, rm);
if (impl == null) continue;
var orig = impl.orig;
var origType = if(impl.spec == null, impl.orig.getMethodType(), impl.spec.getMethodType());
var origSig = FuncType.!(origType).sig();
Expand Down
17 changes: 11 additions & 6 deletions aeneas/src/ir/Ir.v3
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ class IrSelector extends IrMember {
// indexed by class ID, where class IDs are assigned during Reachability analysis.
class IrMtable(root: IrMethod, rootId: int, table: Array<IrMethod>) extends IrItem {
var record: Record;
var compactRecord: Record;

def render(buf: StringBuilder) -> StringBuilder {
buf.puts("[");
Expand Down Expand Up @@ -353,8 +354,10 @@ class IrModule {
}
def resolveMethodImpl(classType: Type, irSpec: IrSpec) -> IrSpec {
var ic = makeIrClass(classType);
var m = ic.methods[irSpec.member.index];
if (m == irSpec.member) return irSpec;
var idx = irSpec.member.index;
if (idx >= ic.methods.length) return irSpec;
var m = ic.methods[idx];
if (m == null || m == irSpec.member) return irSpec;
while (ic.inherits(m)) {
classType = V3.getSuperType(classType);
ic = ic.parent;
Expand Down Expand Up @@ -388,14 +391,16 @@ class IrModule {
if (ctype == null) return null;
var ic = classMap[ctype];
if (ic == null) {
if (V3.isClass(ctype)) {
if (V3.isEnum(ctype)) {
var decl = V3.getDecl(ctype);
var sc = if(decl.parentEnum != null, makeIrClass(decl.parentEnum.getDeclaredType()));
ic = newIrClass(ctype, sc, decl);
classMap[ctype] = ic;
} else if (V3.isClass(ctype)) {
ic = newIrClassWithSuper(ctype, V3.classDecl(ctype));
} else if (V3.isComponent(ctype)) {
ic = newIrClass(ctype, null, V3.componentDecl(ctype));
classMap[ctype] = ic;
} else if (EnumType.?(ctype)) {
ic = newIrClass(ctype, null, EnumType.!(ctype).enumDecl);
classMap[ctype] = ic;
}
}
return ic;
Expand Down
Loading
Loading