From 61486a10f1da969d16be00317e945b6874da53c0 Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Mon, 18 Nov 2024 13:39:35 +1100 Subject: [PATCH 01/12] Small fix to the aeneas script to make it a little more robust to weird setting by people like me --- bin/dev/aeneas | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/dev/aeneas b/bin/dev/aeneas index d009da7df..9e533f5e5 100755 --- a/bin/dev/aeneas +++ b/bin/dev/aeneas @@ -1,10 +1,10 @@ #!/bin/bash -BIN=$(builtin cd $(dirname ${BASH_SOURCE[0]})/.. && builtin pwd) +BIN=$(builtin cd $(dirname ${BASH_SOURCE[0]})/.. >/dev/null && builtin pwd) JAR=$BIN/jar JAR_LINK=$BIN/Aeneas.jar V3C_LINK=$BIN/v3c -VIRGIL_LOC=${VIRGIL_LOC:=$(builtin cd $BIN/.. && builtin pwd)} +VIRGIL_LOC=${VIRGIL_LOC:=$(builtin cd $BIN/.. >/dev/null && builtin pwd)} AENEAS_SYS=${AENEAS_SYS:=${VIRGIL_LOC}/rt/darwin/*.v3} AENEAS_LOC=${AENEAS_LOC:=${VIRGIL_LOC}/aeneas/src} AENEAS_JVM_TUNING=${AENEAS_JVM_TUNING:="-client -Xms900m -Xmx900m -XX:+UseSerialGC"} From a3cefa5b77c66eaf95e9baed9265c6a2c179e370 Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Mon, 2 Dec 2024 23:17:36 -0500 Subject: [PATCH 02/12] Support for poitners to unboxed variant fields (that are not in arrays) --- aeneas/src/core/Opcode.v3 | 4 +- aeneas/src/core/Operator.v3 | 19 ++ aeneas/src/ir/Reachability.v3 | 11 + aeneas/src/ir/SsaNormalizer.v3 | 57 ++++- aeneas/src/ir/VstIr.v3 | 23 ++- aeneas/src/mach/MachLowering.v3 | 9 +- aeneas/src/mach/MachProgram.v3 | 15 ++ aeneas/src/main/Error.v3 | 3 + aeneas/src/ssa/SsaOptimizer.v3 | 15 ++ aeneas/src/ssa/VstSsaGen.v3 | 26 ++- aeneas/src/vst/Verifier.v3 | 56 +++++ aeneas/src/vst/Vst.v3 | 31 ++- aeneas/src/vst/VstPrinter.v3 | 26 ++- test/pointer/Pointer_atUnboxedField00.v3 | 70 +++++++ test/pointer/Pointer_atUnboxedField01.v3 | 29 +++ test/pointer/Pointer_atUnboxedField02.v3 | 195 ++++++++++++++++++ .../seman/Pointer_atFieldUnboxedBad00.v3 | 10 + .../seman/Pointer_atFieldUnboxedBad01.v3 | 13 ++ .../seman/Pointer_atFieldUnboxedBad02.v3 | 17 ++ .../seman/Pointer_atFieldUnboxedBad03.v3 | 10 + .../seman/Pointer_atFieldUnboxedBad04.v3 | 13 ++ .../seman/Pointer_atFieldUnboxedBad05.v3 | 18 ++ .../seman/Pointer_atFieldUnboxedBad06.v3 | 9 + .../seman/Pointer_atFieldUnboxedBad07.v3 | 11 + .../seman/Pointer_atFieldUnboxedBad08.v3 | 12 ++ .../seman/Pointer_atFieldUnboxedBad09.v3 | 10 + .../seman/Pointer_atFieldUnboxedBad10.v3 | 11 + 27 files changed, 706 insertions(+), 17 deletions(-) create mode 100644 test/pointer/Pointer_atUnboxedField00.v3 create mode 100644 test/pointer/Pointer_atUnboxedField01.v3 create mode 100644 test/pointer/Pointer_atUnboxedField02.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad00.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad01.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad02.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad03.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad04.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad05.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad06.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad07.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad08.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad09.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad10.v3 diff --git a/aeneas/src/core/Opcode.v3 b/aeneas/src/core/Opcode.v3 index c0677c5d5..1e7fc6499 100644 --- a/aeneas/src/core/Opcode.v3 +++ b/aeneas/src/core/Opcode.v3 @@ -161,6 +161,7 @@ type Opcode { case PtrAtComponentField(field: IrField); case PtrAtObjectField(field: IrField); case PtrAtRefLayoutField(offset: int); + case PtrAtUnboxedField(fields: List, bind: VarBinding); case PtrCmpSwp; case PtrLoad; case PtrStore; @@ -324,7 +325,8 @@ component Opcodes { t[Opcode.PtrAtRangeElem.tag] = F; t[Opcode.PtrAtArrayElem.tag] = F; t[Opcode.PtrAtComponentField.tag] = P; - t[Opcode.PtrAtObjectField.tag] = F; + t[Opcode.PtrAtObjectField.tag] = P; // EBM: was F + t[Opcode.PtrAtUnboxedField.tag] = P; t[Opcode.PtrLoad.tag] = NONE; t[Opcode.PtrStore.tag] = NONE; diff --git a/aeneas/src/core/Operator.v3 b/aeneas/src/core/Operator.v3 index a725117ff..1294ad51e 100644 --- a/aeneas/src/core/Operator.v3 +++ b/aeneas/src/core/Operator.v3 @@ -477,6 +477,10 @@ component V3Op { var ta = [refType]; return newOp0(Opcode.PtrAtRefLayoutField(offset), ta, ta, ptrType); } + def newPtrAtUnboxedField(specs: List, ptrType: Type, bind: VarBinding) -> Operator { + var ta = [specs.head.receiver]; + return newOp0(Opcode.PtrAtUnboxedField(specs, bind), ta, ta, ptrType); + } def newPtrCmpSwp(ptrType: Type, valueType: Type) -> Operator { return newOp0(Opcode.PtrCmpSwp, [ptrType, valueType], [ptrType, valueType, valueType], type_z); } @@ -583,6 +587,17 @@ component V3Op { } } +def renderList(sb: StringBuilder, lst: List, + rfunc: (T, StringBuilder) -> StringBuilder, sep: string) -> StringBuilder { + while (true) { + sb = rfunc(lst.head, sb); + lst = lst.tail; + if (lst == null) return sb; + sb = sb.puts(sep); + } + return sb; // keep compiler happy +} + def renderOp(op: Operator, buf: StringBuilder) -> StringBuilder { buf.puts(op.opcode.name); if (TerminalBuffer.?(buf)) TerminalBuffer.!(buf).green(); @@ -611,6 +626,10 @@ def renderOp(op: Operator, buf: StringBuilder) -> StringBuilder { ClassInitField(field) => rfunc = field.render; ClassSetField(field) => rfunc = field.render; VariantGetField(field) => rfunc = field.render; + PtrAtComponentField(field) => rfunc = field.render; + PtrAtObjectField(field) => rfunc = field.render; + PtrAtRefLayoutField(field) => rfunc = StringBuilder.put1(_, "%d", field); + PtrAtUnboxedField(specs, bind) => rfunc = renderList(_, specs, IrSpec.render, ","); ClassAlloc(method) => if(method != null) rfunc = method.render; ClassGetMethod(method) => rfunc = method.render; ClassGetVirtual(method) => rfunc = method.render; diff --git a/aeneas/src/ir/Reachability.v3 b/aeneas/src/ir/Reachability.v3 index 13167cd9e..b871fbefb 100644 --- a/aeneas/src/ir/Reachability.v3 +++ b/aeneas/src/ir/Reachability.v3 @@ -331,6 +331,7 @@ class ReachabilityAnalyzer(compilation: Compilation) { CallVariantVirtual(method) => getVirtual(opMethod(op, method, context)); PtrAtObjectField(field) => if (op.useList != null) getAndSetField(makeField(op, field, context)); PtrAtComponentField(field) => if (op.useList != null) getAndSetField(makeField(op, field, context)); + PtrAtUnboxedField(specs, bind) => if (op.useList != null) getAndSetUnboxedField(op, specs, context); _ => ; } } @@ -420,6 +421,16 @@ class ReachabilityAnalyzer(compilation: Compilation) { rf.writeFacts = none; rf.setFact(RaFact.RF_VAL_MANY); } + def getAndSetUnboxedField(op: SsaApplyOp, specs: List, context: IrSpec) { + var rf = makeField(op, specs.head.asField(), context); + getAndSetField(rf); + while (specs.tail != null) { + specs = specs.tail; + var spec = specs.head; + rf = makeField2(makeClass(rf.fieldType), spec.asField()); + getAndSetField(rf); + } + } def setField(op: SsaApplyOp, rf: RaField) { rf.raFacts |= RaFact.RF_WRITTEN; var val = op.input1(); diff --git a/aeneas/src/ir/SsaNormalizer.v3 b/aeneas/src/ir/SsaNormalizer.v3 index 67de1bce7..ce6c8cd56 100644 --- a/aeneas/src/ir/SsaNormalizer.v3 +++ b/aeneas/src/ir/SsaNormalizer.v3 @@ -559,6 +559,9 @@ class SsaRaNormalizer extends SsaRebuilder { normDefault(i_old, op); } } + PtrAtUnboxedField(specs, bind) => { + normPtrAtUnboxedField(i_old, specs, op, bind); + } SystemCall(syscall) => { // normalize a syscall operator var ptn = normType(Tuple.fromTypeArray(op.sig.paramTypes)); @@ -1295,11 +1298,10 @@ class SsaRaNormalizer extends SsaRebuilder { if (nf.length == 0) { // OPT: remove read of useless field // OPT: remove read of zero-width field - if (!isVariant) addNullCheck(i_old, ai_new[0]); return map0(i_old); } normType(raField.receiver); // XXX: normType() side-effect of flattening - if (isVariant && raField != null && rc.isUnboxed()) { + if (raField != null && rc.isUnboxed()) { // field of unboxed data type var vals = genVariantGetField(rc, raField, rc.variantNorm, ai_new); return mapNnf(i_old, vals); @@ -1318,6 +1320,42 @@ class SsaRaNormalizer extends SsaRebuilder { } return mapN(i_old, vals); } + def normPtrAtUnboxedField(i_old: SsaApplyOp, specs: List, op: Operator, bind: VarBinding) { + // XXX: propagate O_NO_NULL_CHECK and O_PURE + var ai_new = genRefs(i_old.inputs); + if (i_old.useList == null) { + // OPT: remove unused pointer construction + // TODO: EBM determine if addNullCheck is a good idea + // if (!isVariant) addNullCheck(i_old, ai_new[0]); + var result = SsaInstr.!(newGraph.intConst(0)); + return map1(i_old, result); + } + // Need to start with outermost field access to get initial set of fields + var raField = extractFieldRef(i_old, specs.head.asField()); + var rc = norm.ra.getClass(raField.receiver); + var ff: Range = rc.liveFields; + // now, iterate winnowing down the fields + while (specs != null && ff.length != 0) { + var spec = specs.head; + specs = specs.tail; + rc = norm.ra.getClass(spec.asField().receiver); + raField = norm.ra.makeField2(rc, spec.asField()); + ff = raField.normOf(ff); + } + // now should have a single field that we can do Pointer.atField + if (ff.length > 0) { + var op_out = if(VarBinding.ComponentField.?(bind), + V3Op.newPtrAtComponentField(ff[0], i_old.op.sig.returnType()), + V3Op.newPtrAtObjectField(ff[0], i_old.op.sig.returnType())); + var new_inputs = if(ai_new.length == 0, Ssa.NO_INSTRS, [ai_new[0]]); + var i_new = curBlock.addApply(curBlock.source, op_out, new_inputs); + return map1(i_old, i_new); + } else { + // Not sure what to do if no fields remain ... + } + var result = SsaInstr.!(newGraph.intConst(0)); + return map1(i_old, result); + } def normClassSetField(i_old: SsaApplyOp, field: IrField, op: Operator, init: bool) { // XXX: propagate O_NO_NULL_CHECK var raField = extractFieldRef(i_old, field); @@ -1407,6 +1445,21 @@ class SsaRaNormalizer extends SsaRebuilder { } return vals; } + def getVariantUnboxedField(rc: RaClass, raField: RaField, vn: VariantNorm, ninputs: Array) -> Array { + var nf = raField.liveFields(norm.ra); + var flds = Array.new(nf.length); + var field = rc.variantNorm.fields[raField.orig.index]; + + for (i < flds.length) { + var idx = field.indexes[i]; + if (IntRepType.?(vn.at(idx)) && field.intervals != null && field.intervals[i].start > 0) { + // punt on bitfield hackery + } else { + flds[i] = ninputs[idx]; + } + } + return flds; + } def normNullCheck(oldApp: SsaApplyOp, op: Operator) { var newArgs = genRefs(oldApp.inputs); if (newArgs.length >= 1) addNullCheck(oldApp, newArgs[0]); diff --git a/aeneas/src/ir/VstIr.v3 b/aeneas/src/ir/VstIr.v3 index 1b5237fbf..479ebb3a6 100644 --- a/aeneas/src/ir/VstIr.v3 +++ b/aeneas/src/ir/VstIr.v3 @@ -61,10 +61,11 @@ class IrBuilder(ctype: Type, parent: IrClass) { } } } + var unboxed = (boxing == Boxing.UNBOXED); for (list = decl.members; list != null; list = list.tail) { var m = list.head; - if (VstField.?(m)) addVstField(VstField.!(m), isVariant); - else if (VstNew.?(m)) addVstNew(VstNew.!(m), isVariant); + if (VstField.?(m)) addVstField(VstField.!(m), isVariant, unboxed); + else if (VstNew.?(m)) addVstNew(VstNew.!(m), isVariant, unboxed); else if (VstMethod.?(m)) addVstMethod(VstMethod.!(m)); } var ic = build(); @@ -72,16 +73,18 @@ class IrBuilder(ctype: Type, parent: IrClass) { ic.packed = packed; return ic; } - def addVstField(f: VstField, isVariant: bool) { + def addVstField(f: VstField, isVariant: bool, unboxed: bool) { var ir = IrField.new(ctype, f.getType()); ir.source = f; + if (f.writability == Writability.READ_ONLY && !(isVariant && unboxed)) + ir.setFact(Fact.F_VALUE | Fact.O_FOLDABLE); + if (isVariant && !unboxed) + ir.setFact(Fact.O_PURE); if (f.pointedAt) ir.setFact(Fact.F_POINTED_AT); - if (f.writability == Writability.READ_ONLY) ir.setFact(Fact.F_VALUE | Fact.O_FOLDABLE); - if (isVariant) ir.setFact(Fact.F_VALUE | Fact.O_FOLDABLE | Fact.O_PURE); addIrField(ir); f.index = ir.index; } - def addVstNew(m: VstNew, isVariant: bool) { + def addVstNew(m: VstNew, isVariant: bool, unboxed: bool) { // constructors always occupy slot 0 m.index = 0; if (m.nontrivial()) { @@ -90,7 +93,13 @@ class IrBuilder(ctype: Type, parent: IrClass) { var ir = IrMethod.new(ctype, null, sig); ir.source = m; ir.facts |= Fact.M_NEW; - if (isVariant) ir.setFact(Fact.O_PURE | Fact.M_INLINE | Fact.V_NON_ZERO); + if (isVariant) { + if (unboxed) { + ir.setFact(Fact.M_INLINE | Fact.V_NON_ZERO); + } else { + ir.setFact(Fact.O_PURE | Fact.M_INLINE | Fact.V_NON_ZERO); + } + } setIrMethod(0, ir); } } diff --git a/aeneas/src/mach/MachLowering.v3 b/aeneas/src/mach/MachLowering.v3 index 53863fd08..3a8f00708 100644 --- a/aeneas/src/mach/MachLowering.v3 +++ b/aeneas/src/mach/MachLowering.v3 @@ -517,7 +517,14 @@ class MachLowering(mach: MachProgram, compiler: Compiler, config: MachLoweringCo ClassSetField(field) => return genClassSetField(i_old, field, false); ClassGetMethod(method) => i_new = genClassGetMethod(i_old, method); ClassGetSelector(selector) => i_new = genClassGetSelector(i_old, selector); - VariantGetField(field) => return genClassGetField(true, i_old, field); + VariantGetField(field) => { + var rcvrType = i_old.input0().getType(); + var treatAsVariant = ( + !ClassType.?(rcvrType) || + !ClassType.!(rcvrType).classDecl.isUnboxed() + ); + return genClassGetField(treatAsVariant, i_old, field); + } VariantGetMethod(method) => i_new = genVariantGetMethod(i_old, method); VariantGetSelector(selector) => i_new = genVariantGetSelector(i_old, selector); ComponentGetField(field) => return genComponentGetField(i_old, field); diff --git a/aeneas/src/mach/MachProgram.v3 b/aeneas/src/mach/MachProgram.v3 index 0311d6327..1c59bb44a 100644 --- a/aeneas/src/mach/MachProgram.v3 +++ b/aeneas/src/mach/MachProgram.v3 @@ -65,9 +65,24 @@ class MachProgram extends TargetProgram { mlayout.addType(tagType); } var b = ic.fields; + var show = (CLOptions.PRINT_MACH.get() != VstMatcher.None); + var buf: TerminalBuffer; + if (show) { + buf = TerminalBuffer.new().puts("Fields for "); + buf = TerminalBuffer.!(ic.ctype.render(buf)).puts(":").outln(); + } + for (i = start; i < b.length; i++) { var f = b[i]; f.machOffset = mlayout.addType(f.fieldType); + if (show) { + if (f.source == null) { + buf.puts("??"); + } else { + buf.put2("%q{%d}", f.source.render(_), f.index); + } + buf.put1(" at %d", f.machOffset).outln(); + } } ic.machSize = mlayout.size; // create GC maps for object if necessary diff --git a/aeneas/src/main/Error.v3 b/aeneas/src/main/Error.v3 index 54861509e..508a17d2f 100644 --- a/aeneas/src/main/Error.v3 +++ b/aeneas/src/main/Error.v3 @@ -212,6 +212,9 @@ class SourceCodeError extends Error { def PtrAtFieldError() { set("TypeError", "operator \"Pointer.atField\" must be applied to a class or component field reference"); } + def PtrAtUnboxedFieldError(msg: string) { + set("TypeError", msg); + } def MemberNotInitialized(msg: string) { set("MemberNotInitialized", msg); } diff --git a/aeneas/src/ssa/SsaOptimizer.v3 b/aeneas/src/ssa/SsaOptimizer.v3 index e44563281..8118c424e 100644 --- a/aeneas/src/ssa/SsaOptimizer.v3 +++ b/aeneas/src/ssa/SsaOptimizer.v3 @@ -835,6 +835,21 @@ class SsaInstrReducer(context: SsaContext) extends SsaInstrMatcher { return replaceOp(i, V3Op.newClassGetMethod(dv)); // ClassGetVirtual[m](K) => ClassGetMethod[m](K) } } + VariantGetField(field) => { + var receiver = i.input0(); + if (state.end) return receiver; + var unboxed = ClassType.!(receiver.getType()).classDecl.isUnboxed(); + if (SsaConst.?(receiver)) { + // ClassGetField(#K) => #K + return graph.valConst(getFieldType(i.op, field), + asRecord(receiver).values[field.index]); + } + i.setFactIf(Fact.O_NO_NULL_CHECK, Fact.O_PURE); + if (!unboxed) { + i.facts |= Fact.F_VALUE; + } + if (optimize_loads) return state.load(receiver, field, i); + } VariantGetMethod(method) => { var meth = V3Op.extractIrSpec(i.op, method); var receiver = i.input0(); diff --git a/aeneas/src/ssa/VstSsaGen.v3 b/aeneas/src/ssa/VstSsaGen.v3 index 48a97256e..481763d87 100644 --- a/aeneas/src/ssa/VstSsaGen.v3 +++ b/aeneas/src/ssa/VstSsaGen.v3 @@ -778,6 +778,7 @@ class VstSsaGen extends VstVisitor { LayoutRepeatedNested, LayoutRepeatedField, PtrAtField, + PtrAtUnboxedField, LayoutDecl, LayoutFieldDecl, Type => { @@ -828,6 +829,19 @@ class VstSsaGen extends VstVisitor { } return val; } + private def getPointedAtIrFieldInfo(rcvr: Type, fld: VstField) -> (IrSpec, IrField) { + if (rcvr == null) rcvr = fld.receiver.getDeclaredType(); + var spec = specOf(rcvr, fld, null); + var irFld = spec.asField(); + irFld.facts |= Fact.F_POINTED_AT; + return (spec, irFld); + } + private def getPointedAtIrSpec(rcvr: Type, fld: VstField) -> IrSpec { + return getPointedAtIrFieldInfo(rcvr, fld).0; + } + private def getPointedAtIrField(rcvr: Type, fld: VstField) -> IrField { + return getPointedAtIrFieldInfo(rcvr, fld).1; + } def genAppRead(appbind: AppBinding, target: SsaInstr, args: Array, env: VstSsaEnv) -> SsaInstr { match (appbind) { None => { @@ -879,19 +893,23 @@ class VstSsaGen extends VstVisitor { return env.addClosureCreate(V3Op.newCallClosure(target.getType()), args, boundMap); } PtrAtComponentField(receiver, field, ptrType) => { - var spec = specOf(receiver, field, null); - var irFld = spec.asField(); + var spec = getPointedAtIrSpec(receiver, field); return env.addApply(env.source, V3Op.newPtrAtComponentField(spec, ptrType), Ssa.NO_INSTRS); } PtrAtObjectField(receiver, field, ptrType) => { - var spec = specOf(receiver, field, null); - var irFld = spec.asField(); + var spec = getPointedAtIrSpec(receiver, field); return env.addApply(env.source, V3Op.newPtrAtObjectField(spec, ptrType), [target]); } PtrAtRefLayoutField(receiver, field, ptrType) => { var op = V3Op.newPtrAtRefLayoutField(receiver, field.byteOffset, ptrType); return env.addApply(env.source, op, [target]); } + PtrAtUnboxedField(receiver, fields, ptrType, bind) => { + var irSpecs: List = Lists.map<(Type, VstField), IrSpec>(fields, getPointedAtIrSpec(_, _)); + var op = V3Op.newPtrAtUnboxedField(irSpecs, ptrType, bind); + var tgt = if(VarBinding.ComponentField.?(bind), Ssa.NO_INSTRS, [target]); + return env.addApply(env.source, op, tgt); + } } } def genReadModifyWrite(source: Source, lval: Expr, modify: (SsaInstr, VstSsaEnv) -> SsaInstr, env: VstSsaEnv) -> (SsaInstr, SsaInstr) { diff --git a/aeneas/src/vst/Verifier.v3 b/aeneas/src/vst/Verifier.v3 index ea6e196ef..837b278ec 100644 --- a/aeneas/src/vst/Verifier.v3 +++ b/aeneas/src/vst/Verifier.v3 @@ -1549,6 +1549,51 @@ class TypeChecker(ERROR: ErrorGen, file: VstFile) extends VstVisitor expr.func.exactType = elim(funcType); return elim(resultType); } + // Tries to create Vst that creates a Pointer to an unboxed subfield of a (boxed) + // class or component. The operator's target is the boxed class instance (null + // for a component) and the operator itself includes a list of fields, the path + // to the subfield to which the pointer should refer. The subfield must not + // itself be an unboxed variant or a tuple, i.e., it needs to be a scalar or a + // reference. + def getPtrAtUnboxedField(ve: VarExpr, rest: List<(Type, VstField)>) -> (Expr, List<(Type, VstField)>, VarBinding) { + var x = ve.varbind; + match (x) { + ComponentField(member) => { + member.pointedAt = true; + return (null, List<(Type, VstField)>.new((null, member), rest), x); + } + ObjectField(receiver, member) => { + member.pointedAt = true; + return (ve.receiver, List<(Type, VstField)>.new((receiver, member), rest), x); + } + VariantField(receiver, member) => { + // check that the type is unboxed and not packed and the ve receiver is a VarExpr or IndexExpr + if (!ClassType.?(receiver)) { + errAtExpr(ve).PtrAtFieldError(); + return (null, null, x); + } else if (!ClassType.!(receiver).classDecl.isUnboxed() || + ClassType.!(receiver).classDecl.isPacked()) { + errAtExpr(ve).PtrAtUnboxedFieldError("operator \"Pointer.atField\" must be applied to a fully unboxed and not packed variant"); + return (null, null, x); + } else if (VarExpr.?(ve.receiver)) { + member.pointedAt = true; + return getPtrAtUnboxedField(VarExpr.!(ve.receiver), List<(Type, VstField)>.new((receiver, member), rest)); + } else if (IndexExpr.?(ve.receiver) && false) { + // need to convert to something like Pointer.atElem on the array/range, and do Pointer.atField on that ... + // maybe a new operator, PtrAtUnboxedElemField? + member.pointedAt = true; + return (ve.receiver, List<(Type, VstField)>.new((receiver, member), rest), x); + } else { + errAtExpr(ve).PtrAtUnboxedFieldError("operator \"Pointer.atField\" must be applied to a receiver.field form"); + return (null, null, x); + } + } + _ => { + errAtExpr(ve).PtrAtFieldError(); + return (null, null, x); + } + } + } def visitPtrAtField(expr: AppExpr, ptrType: Type, outer: Type) -> Type { if (expr.args.exprs.length() != 1) { errAtRange(expr.args.range()).PtrAtFieldError(); @@ -1579,6 +1624,17 @@ class TypeChecker(ERROR: ErrorGen, file: VstFile) extends VstVisitor expr.target = x.receiver; return expr.exactType = ptrType; } + VariantField(receiver, member) => { + member.pointedAt = true; + var ubinfo = getPtrAtUnboxedField(x, null); + var rcvr = ubinfo.0; + var fields = ubinfo.1; + var vbind = ubinfo.2; + expr.appbind = AppBinding.PtrAtUnboxedField(receiver, fields, ptrType, vbind); + expr.args = TupleExpr.new(VstList.new(expr.args.range(), null)); + expr.target = rcvr; + return expr.exactType = ptrType; + } _ => { errAtExpr(arg).PtrAtFieldError(); } diff --git a/aeneas/src/vst/Vst.v3 b/aeneas/src/vst/Vst.v3 index 3c4b8aba9..f5987457b 100644 --- a/aeneas/src/vst/Vst.v3 +++ b/aeneas/src/vst/Vst.v3 @@ -140,6 +140,28 @@ class VstCompound extends Decl { def isEnum() -> bool { return VstEnum.?(this); } def isVariant() -> bool { return variantTag > NON_VARIANT; } def isVariantCase() -> bool { return variantTag >= 0; } + + def isUnboxed() -> bool { + var hints = repHints; + while (hints != null) { + if (hints.head == VstRepHint.Unboxed) { + return true; + } + hints = hints.tail; + } + return false; + } + def isPacked() -> bool { + var hints = repHints; + while (hints != null) { + match (hints.head) { + Packed, Packing => return true; + _ => ; + } + hints = hints.tail; + } + return false; + } } // Parsed "class X { ... }" class VstClass extends VstCompound { @@ -299,6 +321,11 @@ class VstMember extends Decl { if (receiver != null && !receiver.isFileScope) buf.puts(receiver.fullName).putc('.'); return buf.puts(token.image); } + def render(buf: StringBuilder) -> StringBuilder { + buf.put1("%s", token.image); + if (index >= 0) buf.put1(".%d", index); + return buf; + } } // Desugared case member from a variant. class VstCaseMember extends VstMember { @@ -912,12 +939,13 @@ type VarBinding { case ClassNew(receiver: Type, member: VstNew, funcType: Type); case ComponentMethod(member: VstMethod, typeArgs: TypeArgs); case VariantCase(receiver: Type, member: VstCaseMember); - case VariantField(receiver: Type, member: VstField); + case VariantField(receiver: Type, member: VstField); // writable if unboxed case EnumConst(member: VstEnumCase); case Inst(op: Operator, facts: Fact.set); // instantiate {op} case Apply(op: Operator, facts: Fact.set); // apply {op} to receiver case Partial(op: Operator, facts: Fact.set); // partially apply {op} to receiver case PtrAtField(ptrType: Type); + case PtrAtUnboxedField(ptrType: Type); case LayoutDecl(decl: VstLayout); case LayoutFieldDecl(decl: VstLayoutField); @@ -940,6 +968,7 @@ type AppBinding { case PtrAtComponentField(receiver: Type, field: VstField, ptrType: Type); case PtrAtObjectField(receiver: Type, field: VstField, ptrType: Type); case PtrAtRefLayoutField(receiver: Type, field: VstLayoutField, ptrType: Type); + case PtrAtUnboxedField(receiver: Type, fields: List<(Type, VstField)>, ptrType: Type, bind: VarBinding); def nullCheck() -> bool { match (this) { diff --git a/aeneas/src/vst/VstPrinter.v3 b/aeneas/src/vst/VstPrinter.v3 index 7c5313ae5..b16496300 100644 --- a/aeneas/src/vst/VstPrinter.v3 +++ b/aeneas/src/vst/VstPrinter.v3 @@ -197,6 +197,9 @@ class VstPrinter extends VstVisitor { Terminal.sp(); p.printCommaList(fdecl.repHints, p.printRepHint); } + if (fdecl.pointedAt) { + Terminal.put(" pointedAt"); + } Terminal.put(";\n"); } def printProgram(prog: Program) { @@ -321,7 +324,28 @@ class VstPrinter extends VstVisitor { param("StringExpr", expr.token.image, null, expr.exactType, expr.implicitType, indent); } def visitApp(expr: AppExpr, indent: int) { - param("AppExpr", null, List.new(expr.func, expr.args.exprs.list), expr.exactType, expr.implicitType, indent); + var args = expr.args.exprs.list; + if (expr.target != null) args = List.new(expr.target, args); + var extra: string = null; + match (expr.appbind) { + CallComponentMethod(x_rcvr, member, x_ta) => { extra = member.name(); } + CallObjectMethod(x_rcvr, member, x_ta) => { extra = member.name(); } + CallClassMethod(x_rcvr, member, x_ta) => { extra = member.name(); } + PtrAtComponentField(x_rcvr, field, x_ta) => { extra = field.name(); } + PtrAtObjectField(x_rcvr, field, x_ta) => { extra = field.name(); } + PtrAtRefLayoutField(x_rcvr, field, x_ta) => { extra = field.name(); } + PtrAtUnboxedField(x_rcvr, fields, x_ta, x_bind) => { + var buf = StringBuilder.new(); + var flds = fields; + while (flds != null) { + buf.putc('.').puts(flds.head.1.name()); + flds = flds.tail; + } + extra = buf.toString(); + } + _ => ; + } + param("AppExpr", extra, List.new(expr.func, args), expr.exactType, expr.implicitType, indent); } def visitIndex(expr: IndexExpr, indent: int) { var buf = StringBuilder.new(); diff --git a/test/pointer/Pointer_atUnboxedField00.v3 b/test/pointer/Pointer_atUnboxedField00.v3 new file mode 100644 index 000000000..2432ad337 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField00.v3 @@ -0,0 +1,70 @@ +//@execute 0=0; 6=6 +class C { } +type V1(v1int: int, v1void: void, v1tuple: (int, int), v1class: C) #unboxed { } + +var v1global = V1(1, void, (2, 3), C.new()); + +type V2(v21: int, v22: V1, v23: int) #unboxed { } + +var v2global = V2(100, V1(101, void, (102, 103), C.new()), 104); + +component Comp { + var v1comp = V1(4, void, (5, 6), C.new()); + var v2comp = V2(300, V1(301, void, (302, 302), C.new()), 305); +} + +class Cls { + var v1cls = V1(7, void, (8, 9), C.new()); + var v2cls = V2(400, V1(402, void, (402, 403), C.new()), 406); +} + +var clsglobal = Cls.new(); + +def main(arg: int) -> int { + + var clslocal = Cls.new(); + + // tests of cases that should be legal, across types and container locations + + var pg1v1int = Pointer.atField(v1global.v1int); + var pg1v1void = Pointer.atField(v1global.v1void); + var pg1v1tuple = Pointer.atField(v1global.v1tuple); + var pg1v1class = Pointer.atField(v1global.v1class); + + var pv1compv1int = Pointer.atField(Comp.v1comp.v1int); + var pv1compv1void = Pointer.atField(Comp.v1comp.v1void); + var pv1compv1tuple = Pointer.atField(Comp.v1comp.v1tuple); + var pv1compv1class = Pointer.atField(Comp.v1comp.v1class); + + var pv1clsgv1int = Pointer.atField(clsglobal.v1cls.v1int); + var pv1clsgv1void = Pointer.atField(clsglobal.v1cls.v1void); + var pv1clsgv1tuple = Pointer.atField(clsglobal.v1cls.v1tuple); + var pv1clsgv1class = Pointer.atField(clsglobal.v1cls.v1class); + + var pv1clslv1int = Pointer.atField(clslocal.v1cls.v1int); + var pv1clslv1void = Pointer.atField(clslocal.v1cls.v1void); + var pv1clslv1tuple = Pointer.atField(clslocal.v1cls.v1tuple); + var pv1clslv1class = Pointer.atField(clslocal.v1cls.v1class); + + var pv2gv22v1int = Pointer.atField(v2global.v22.v1int); + var pv2gv22v1void = Pointer.atField(v2global.v22.v1void); + var pv2gv22v1tuple = Pointer.atField(v2global.v22.v1tuple); + var pv2gv22v1class = Pointer.atField(v2global.v22.v1class); + + var pv2compv22v1int = Pointer.atField(Comp.v2comp.v22.v1int); + var pv2compv22v1void = Pointer.atField(Comp.v2comp.v22.v1void); + var pv2compv22v1tuple = Pointer.atField(Comp.v2comp.v22.v1tuple); + var pv2compv22v1class = Pointer.atField(Comp.v2comp.v22.v1class); + + var pv2clsgv22v1int = Pointer.atField(clsglobal.v2cls.v22.v1int); + var pv2clsgv22v1void = Pointer.atField(clsglobal.v2cls.v22.v1void); + var pv2clsgv22v1tuple = Pointer.atField(clsglobal.v2cls.v22.v1tuple); + var pv2clsgv22v1class = Pointer.atField(clsglobal.v2cls.v22.v1class); + + var pv2clslv22v1int = Pointer.atField(clslocal.v2cls.v22.v1int); + var pv2clslv22v1void = Pointer.atField(clslocal.v2cls.v22.v1void); + var pv2clslv22v1tuple = Pointer.atField(clslocal.v2cls.v22.v1tuple); + var pv2clslv22v1class = Pointer.atField(clslocal.v2cls.v22.v1class); + + return arg; +} diff --git a/test/pointer/Pointer_atUnboxedField01.v3 b/test/pointer/Pointer_atUnboxedField01.v3 new file mode 100644 index 000000000..23a023aee --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField01.v3 @@ -0,0 +1,29 @@ +//@execute 0=0; 6=6 +type V2(v21: int, v22: int, v23: int) #unboxed { } +type V3(v31: int, v32: int, v33: int) #unboxed { } + +var v2 = V2(200, 201, 202); +var v3 = V3(300, 301, 302); + +def main(arg: int) -> int { + + // check that field is optimized away: + // A Pointer.atField is given to v22, but is not used so is elided, + // then the field can be elided. + var pv21 = Pointer.atField(v2.v21); + var pv22 = Pointer.atField(v2.v22); + var pv23 = Pointer.atField(v2.v23); + + var diff2 = int.view(pv23 - pv21); + + // check that field is *not* optimized away: + // The Pointer.atField is used by a load and the load result is also used. + var pv31 = Pointer.atField(v3.v31); + var pv32 = Pointer.atField(v3.v32); + var pv33 = Pointer.atField(v3.v33); + var i32 = pv32.load(); + + var diff3 = int.view(pv33 - pv31); + + return arg + (diff2 - 4) + (diff3 - 8) + (i32 - 301); +} diff --git a/test/pointer/Pointer_atUnboxedField02.v3 b/test/pointer/Pointer_atUnboxedField02.v3 new file mode 100644 index 000000000..5ea67f192 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField02.v3 @@ -0,0 +1,195 @@ +//@execute 0=0 +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +var gv = V(1, 2, 3, 4); + +class C { + var clsv: V; + new(v8: u8, v16: u16, v32: u32, v64: u64) { + clsv = V(v8, v16, v32, v64); + } +} + +var gclsv = C.new(5, 6, 7, 8); + +component Comp { + var compv = V(9, 10, 11, 12); + var compclsv = C.new(13, 14, 15, 16); +} + +def main(arg: int) -> u64 { + + var lclsv = C.new(17, 18, 19, 20); + + // check values before doing anything, using regular accesses + + var startingRegularDiff = + + (u64.view(gv.v8 ) ^ 1) + + (u64.view(gv.v16) ^ 2) + + (u64.view(gv.v32) ^ 3) + + (u64.view(gv.v64) ^ 4) + + + (u64.view(gclsv.clsv.v8 ) ^ 5) + + (u64.view(gclsv.clsv.v16) ^ 6) + + (u64.view(gclsv.clsv.v32) ^ 7) + + (u64.view(gclsv.clsv.v64) ^ 8) + + + (u64.view(Comp.compv.v8 ) ^ 9) + + (u64.view(Comp.compv.v16) ^ 10) + + (u64.view(Comp.compv.v32) ^ 11) + + (u64.view(Comp.compv.v64) ^ 12) + + + (u64.view(Comp.compclsv.clsv.v8 ) ^ 13) + + (u64.view(Comp.compclsv.clsv.v16) ^ 14) + + (u64.view(Comp.compclsv.clsv.v32) ^ 15) + + (u64.view(Comp.compclsv.clsv.v64) ^ 16) + + + (u64.view(lclsv.clsv.v8 ) ^ 17) + + (u64.view(lclsv.clsv.v16) ^ 18) + + (u64.view(lclsv.clsv.v32) ^ 19) + + (u64.view(lclsv.clsv.v64) ^ 20); + + // check values before doing anything, using pointer loads + + var startingLoadDiff = + + (u64.view(Pointer.atField(gv.v8 ).load()) ^ 1) + + (u64.view(Pointer.atField(gv.v16).load()) ^ 2) + + (u64.view(Pointer.atField(gv.v32).load()) ^ 3) + + (u64.view(Pointer.atField(gv.v64).load()) ^ 4) + + + (u64.view(Pointer.atField(gclsv.clsv.v8 ).load()) ^ 5) + + (u64.view(Pointer.atField(gclsv.clsv.v16).load()) ^ 6) + + (u64.view(Pointer.atField(gclsv.clsv.v32).load()) ^ 7) + + (u64.view(Pointer.atField(gclsv.clsv.v64).load()) ^ 8) + + + (u64.view(Pointer.atField(Comp.compv.v8 ).load()) ^ 9) + + (u64.view(Pointer.atField(Comp.compv.v16).load()) ^ 10) + + (u64.view(Pointer.atField(Comp.compv.v32).load()) ^ 11) + + (u64.view(Pointer.atField(Comp.compv.v64).load()) ^ 12) + + + (u64.view(Pointer.atField(Comp.compclsv.clsv.v8 ).load()) ^ 13) + + (u64.view(Pointer.atField(Comp.compclsv.clsv.v16).load()) ^ 14) + + (u64.view(Pointer.atField(Comp.compclsv.clsv.v32).load()) ^ 15) + + (u64.view(Pointer.atField(Comp.compclsv.clsv.v64).load()) ^ 16) + + + (u64.view(Pointer.atField(lclsv.clsv.v8 ).load()) ^ 17) + + (u64.view(Pointer.atField(lclsv.clsv.v16).load()) ^ 18) + + (u64.view(Pointer.atField(lclsv.clsv.v32).load()) ^ 19) + + (u64.view(Pointer.atField(lclsv.clsv.v64).load()) ^ 20); + + // change values with cmpswp + + Pointer.atField(gv.v8 ).cmpswp(1, 101); + Pointer.atField(gv.v16).cmpswp(2, 102); + Pointer.atField(gv.v32).cmpswp(3, 103); + Pointer.atField(gv.v64).cmpswp(4, 104); + + Pointer.atField(gclsv.clsv.v8 ).cmpswp(5, 105); + Pointer.atField(gclsv.clsv.v16).cmpswp(6, 106); + Pointer.atField(gclsv.clsv.v32).cmpswp(7, 107); + Pointer.atField(gclsv.clsv.v64).cmpswp(8, 108); + + Pointer.atField(Comp.compv.v8 ).cmpswp(9, 109); + Pointer.atField(Comp.compv.v16).cmpswp(10, 110); + Pointer.atField(Comp.compv.v32).cmpswp(11, 111); + Pointer.atField(Comp.compv.v64).cmpswp(12, 112); + + Pointer.atField(Comp.compclsv.clsv.v8 ).cmpswp(13, 113); + Pointer.atField(Comp.compclsv.clsv.v16).cmpswp(14, 114); + Pointer.atField(Comp.compclsv.clsv.v32).cmpswp(15, 115); + Pointer.atField(Comp.compclsv.clsv.v64).cmpswp(16, 116); + + Pointer.atField(lclsv.clsv.v8 ).cmpswp(17, 117); + Pointer.atField(lclsv.clsv.v16).cmpswp(18, 118); + Pointer.atField(lclsv.clsv.v32).cmpswp(19, 119); + Pointer.atField(lclsv.clsv.v64).cmpswp(20, 120); + + // check values after cmpswp, using regular accesses + + var afterCmpswpDiff = + + (u64.view(gv.v8 ) ^ 101) + + (u64.view(gv.v16) ^ 102) + + (u64.view(gv.v32) ^ 103) + + (u64.view(gv.v64) ^ 104) + + + (u64.view(gclsv.clsv.v8 ) ^ 105) + + (u64.view(gclsv.clsv.v16) ^ 106) + + (u64.view(gclsv.clsv.v32) ^ 107) + + (u64.view(gclsv.clsv.v64) ^ 108) + + + (u64.view(Comp.compv.v8 ) ^ 109) + + (u64.view(Comp.compv.v16) ^ 110) + + (u64.view(Comp.compv.v32) ^ 111) + + (u64.view(Comp.compv.v64) ^ 112) + + + (u64.view(Comp.compclsv.clsv.v8 ) ^ 113) + + (u64.view(Comp.compclsv.clsv.v16) ^ 114) + + (u64.view(Comp.compclsv.clsv.v32) ^ 115) + + (u64.view(Comp.compclsv.clsv.v64) ^ 116) + + + (u64.view(lclsv.clsv.v8 ) ^ 117) + + (u64.view(lclsv.clsv.v16) ^ 118) + + (u64.view(lclsv.clsv.v32) ^ 119) + + (u64.view(lclsv.clsv.v64) ^ 120); + + // store original values back, using pointer stores + + Pointer.atField(gv.v8 ).store(1); + Pointer.atField(gv.v16).store(2); + Pointer.atField(gv.v32).store(3); + Pointer.atField(gv.v64).store(4); + + Pointer.atField(gclsv.clsv.v8 ).store(5); + Pointer.atField(gclsv.clsv.v16).store(6); + Pointer.atField(gclsv.clsv.v32).store(7); + Pointer.atField(gclsv.clsv.v64).store(8); + + Pointer.atField(Comp.compv.v8 ).store(9); + Pointer.atField(Comp.compv.v16).store(10); + Pointer.atField(Comp.compv.v32).store(11); + Pointer.atField(Comp.compv.v64).store(12); + + Pointer.atField(Comp.compclsv.clsv.v8 ).store(13); + Pointer.atField(Comp.compclsv.clsv.v16).store(14); + Pointer.atField(Comp.compclsv.clsv.v32).store(15); + Pointer.atField(Comp.compclsv.clsv.v64).store(16); + + Pointer.atField(lclsv.clsv.v8 ).store(17); + Pointer.atField(lclsv.clsv.v16).store(18); + Pointer.atField(lclsv.clsv.v32).store(19); + Pointer.atField(lclsv.clsv.v64).store(20); + + // check the restored values, using regular accesses + + var afterStoreDiff = + + (u64.view(gv.v8 ) ^ 1) + + (u64.view(gv.v16) ^ 2) + + (u64.view(gv.v32) ^ 3) + + (u64.view(gv.v64) ^ 4) + + + (u64.view(gclsv.clsv.v8 ) ^ 5) + + (u64.view(gclsv.clsv.v16) ^ 6) + + (u64.view(gclsv.clsv.v32) ^ 7) + + (u64.view(gclsv.clsv.v64) ^ 8) + + + (u64.view(Comp.compv.v8 ) ^ 9) + + (u64.view(Comp.compv.v16) ^ 10) + + (u64.view(Comp.compv.v32) ^ 11) + + (u64.view(Comp.compv.v64) ^ 12) + + + (u64.view(Comp.compclsv.clsv.v8 ) ^ 13) + + (u64.view(Comp.compclsv.clsv.v16) ^ 14) + + (u64.view(Comp.compclsv.clsv.v32) ^ 15) + + (u64.view(Comp.compclsv.clsv.v64) ^ 16) + + + (u64.view(lclsv.clsv.v8 ) ^ 17) + + (u64.view(lclsv.clsv.v16) ^ 18) + + (u64.view(lclsv.clsv.v32) ^ 19) + + (u64.view(lclsv.clsv.v64) ^ 20); + + return startingRegularDiff + startingLoadDiff + afterCmpswpDiff + afterStoreDiff; +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad00.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad00.v3 new file mode 100644 index 000000000..50d0c5953 --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad00.v3 @@ -0,0 +1,10 @@ +//@seman = TypeError @ 7:33 +// Disallow pointer to *boxed* field of a global variable +type V(x: int); + +def main() -> int { + var v = V(13); + var p = Pointer.atField(v.x); + p.store(17); + return v.x; +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad01.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad01.v3 new file mode 100644 index 000000000..e914e2726 --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad01.v3 @@ -0,0 +1,13 @@ +//@seman = TypeError @ 10:33 +// Disallow pointer to *boxed* field of a component + +type V(x: int); +component C { + var v = V(13); +} + +def main() -> int { + var p = Pointer.atField(C.v.x); + p.store(17); + return C.v.x; +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad02.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad02.v3 new file mode 100644 index 000000000..a4e4aee6c --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad02.v3 @@ -0,0 +1,17 @@ +//@seman = TypeError @ 14:33 +// Disallow pointer to *boxed* field of a class + +type V(x: int); +class C { + var v: V; + new(i: int) { + v = V(i); + } +} + +def main() -> int { + var c = C.new(13); + var p = Pointer.atField(c.v.x); + p.store(17); + return c.v.x; +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad03.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad03.v3 new file mode 100644 index 000000000..bed477626 --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad03.v3 @@ -0,0 +1,10 @@ +//@seman = TypeError @ 7:33 +// Disallow pointer to *packed* field of a local variable +type V(x: u4, y: u4) #packing 0b_xxxxyyyy; + +def main() -> int { + var v = V(13, 7); + var p = Pointer.atField(v.x); + p.store(11); + return v.x; +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad04.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad04.v3 new file mode 100644 index 000000000..f97329861 --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad04.v3 @@ -0,0 +1,13 @@ +//@seman = TypeError @ 10:33 +// Disallow pointer to *packed* field of a global variable +type V(x: u4, y: u4) #packing 0b_xxxxyyyy; + +component C { + var v = V(13, 7); +} + +def main() -> int { + var p = Pointer.atField(C.v.x); + p.store(17); + return C.v.x; +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad05.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad05.v3 new file mode 100644 index 000000000..f446c2fe9 --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad05.v3 @@ -0,0 +1,18 @@ +//@seman = TypeError @ 15:33 +// Disallow pointer to *packed* field of a local variable +type V(x: u4, y: u4) #packing 0b_xxxxyyyy; + +type V(x: int); +class C { + var v: V; + new(i: int) { + v = V(i, 3); + } +} + +def main() -> int { + var c = C.new(13); + var p = Pointer.atField(c.v.x); + p.store(17); + return c.v.x; +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad06.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad06.v3 new file mode 100644 index 000000000..724a159d5 --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad06.v3 @@ -0,0 +1,9 @@ +//@seman = TypeError @ 6:33 +// Disallow pointer to global tuple +var tup = (1, 13); + +def main() -> int { + var p = Pointer.atField(tup.1); + p.store(17); + return tup.1; +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad07.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad07.v3 new file mode 100644 index 000000000..95e0c60ca --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad07.v3 @@ -0,0 +1,11 @@ +//@seman = TypeError @ 8:33 +// Disallow pointer to component tuple +component C { + var tup = (1, 13); +} + +def main() -> int { + var p = Pointer.atField(C.tup.1); + p.store(17); + return C.tup.1; +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad08.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad08.v3 new file mode 100644 index 000000000..8ea488a48 --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad08.v3 @@ -0,0 +1,12 @@ +//@seman = TypeError @ 9:33 +// Disallow pointer to tuple in class +class C { + var tup = (1, 13); +} + +def main() -> int { + var c = C.new(); + var p = Pointer.atField(c.tup.1); + p.store(17); + return c.tup.1; +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad09.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad09.v3 new file mode 100644 index 000000000..82ac60377 --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad09.v3 @@ -0,0 +1,10 @@ +//@seman = TypeError @ 7:33 +// Disallow pointer to unboxed field of a local variable +type V(x: int) #unboxed; + +def main() -> int { + var v = V(13); + var p = Pointer.atField(v.x); + p.store(17); + return v.x; +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad10.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad10.v3 new file mode 100644 index 000000000..f61d4884e --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad10.v3 @@ -0,0 +1,11 @@ +//@seman = TypeError @ 8:33 +// Disallow pointer to unboxed field of boxed variant +type V(x: int) #unboxed; +type V2(a: int, v: V); + +def main() -> int { + var v2 = V2(13, V(14)); + var p = Pointer.atField(v2.v.x); + p.store(17); + return v2.v.x; +} From 5aff2a58aeb14bf8ba1028b6e313c27b51782212 Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Thu, 5 Dec 2024 19:50:31 -0500 Subject: [PATCH 03/12] Refactor pointer-to-unboxed-field code not to use a VarBinding as part of an operator, but to use two separate operators. Split tests to be more minimal. Conditionalize 64-bit ops in tests so that 32-bit machines won't try to emit those ops. Fix script to run semantic tests. --- aeneas/src/core/Opcode.v3 | 6 +- aeneas/src/core/Operator.v3 | 11 +- aeneas/src/ir/Reachability.v3 | 3 +- aeneas/src/ir/SsaNormalizer.v3 | 24 ++- aeneas/src/ssa/VstSsaGen.v3 | 12 +- aeneas/src/vst/Verifier.v3 | 9 +- aeneas/src/vst/Vst.v3 | 3 +- aeneas/src/vst/VstPrinter.v3 | 11 +- test/fail/Pointer_atUnboxedField03.v3 | 195 ++++++++++++++++++++++ test/pointer/Pointer_atUnboxedField01.v3 | 13 +- test/pointer/Pointer_atUnboxedField02.v3 | 201 ++--------------------- test/pointer/Pointer_atUnboxedField03.v3 | 165 +++++++++++++++++++ test/pointer/Pointer_atUnboxedField04.v3 | 21 +++ test/pointer/Pointer_atUnboxedField05.v3 | 23 +++ test/pointer/Pointer_atUnboxedField06.v3 | 28 ++++ test/pointer/Pointer_atUnboxedField07.v3 | 30 ++++ test/pointer/Pointer_atUnboxedField08.v3 | 28 ++++ test/pointer/Pointer_atUnboxedField09.v3 | 22 +++ test/pointer/Pointer_atUnboxedField10.v3 | 24 +++ test/pointer/Pointer_atUnboxedField11.v3 | 29 ++++ test/pointer/Pointer_atUnboxedField12.v3 | 31 ++++ test/pointer/Pointer_atUnboxedField13.v3 | 29 ++++ test/pointer/Pointer_atUnboxedField14.v3 | 32 ++++ test/pointer/Pointer_atUnboxedField15.v3 | 34 ++++ test/pointer/Pointer_atUnboxedField16.v3 | 39 +++++ test/pointer/Pointer_atUnboxedField17.v3 | 41 +++++ test/pointer/Pointer_atUnboxedField18.v3 | 39 +++++ test/pointer/Pointer_atUnboxedField19.v3 | 31 ++++ test/pointer/Pointer_atUnboxedField20.v3 | 33 ++++ test/pointer/Pointer_atUnboxedField21.v3 | 38 +++++ test/pointer/Pointer_atUnboxedField22.v3 | 40 +++++ test/pointer/Pointer_atUnboxedField23.v3 | 38 +++++ test/pointer/test.bash | 2 + 33 files changed, 1065 insertions(+), 220 deletions(-) create mode 100644 test/fail/Pointer_atUnboxedField03.v3 create mode 100644 test/pointer/Pointer_atUnboxedField03.v3 create mode 100644 test/pointer/Pointer_atUnboxedField04.v3 create mode 100644 test/pointer/Pointer_atUnboxedField05.v3 create mode 100644 test/pointer/Pointer_atUnboxedField06.v3 create mode 100644 test/pointer/Pointer_atUnboxedField07.v3 create mode 100644 test/pointer/Pointer_atUnboxedField08.v3 create mode 100644 test/pointer/Pointer_atUnboxedField09.v3 create mode 100644 test/pointer/Pointer_atUnboxedField10.v3 create mode 100644 test/pointer/Pointer_atUnboxedField11.v3 create mode 100644 test/pointer/Pointer_atUnboxedField12.v3 create mode 100644 test/pointer/Pointer_atUnboxedField13.v3 create mode 100644 test/pointer/Pointer_atUnboxedField14.v3 create mode 100644 test/pointer/Pointer_atUnboxedField15.v3 create mode 100644 test/pointer/Pointer_atUnboxedField16.v3 create mode 100644 test/pointer/Pointer_atUnboxedField17.v3 create mode 100644 test/pointer/Pointer_atUnboxedField18.v3 create mode 100644 test/pointer/Pointer_atUnboxedField19.v3 create mode 100644 test/pointer/Pointer_atUnboxedField20.v3 create mode 100644 test/pointer/Pointer_atUnboxedField21.v3 create mode 100644 test/pointer/Pointer_atUnboxedField22.v3 create mode 100644 test/pointer/Pointer_atUnboxedField23.v3 diff --git a/aeneas/src/core/Opcode.v3 b/aeneas/src/core/Opcode.v3 index 1e7fc6499..9ce59dc85 100644 --- a/aeneas/src/core/Opcode.v3 +++ b/aeneas/src/core/Opcode.v3 @@ -161,7 +161,8 @@ type Opcode { case PtrAtComponentField(field: IrField); case PtrAtObjectField(field: IrField); case PtrAtRefLayoutField(offset: int); - case PtrAtUnboxedField(fields: List, bind: VarBinding); + case PtrAtUnboxedObjectField(fields: List); + case PtrAtUnboxedComponentField(fields: List); case PtrCmpSwp; case PtrLoad; case PtrStore; @@ -326,7 +327,8 @@ component Opcodes { t[Opcode.PtrAtArrayElem.tag] = F; t[Opcode.PtrAtComponentField.tag] = P; t[Opcode.PtrAtObjectField.tag] = P; // EBM: was F - t[Opcode.PtrAtUnboxedField.tag] = P; + t[Opcode.PtrAtUnboxedObjectField.tag] = P; + t[Opcode.PtrAtUnboxedComponentField.tag] = P; t[Opcode.PtrLoad.tag] = NONE; t[Opcode.PtrStore.tag] = NONE; diff --git a/aeneas/src/core/Operator.v3 b/aeneas/src/core/Operator.v3 index 1294ad51e..3c751e8d8 100644 --- a/aeneas/src/core/Operator.v3 +++ b/aeneas/src/core/Operator.v3 @@ -477,9 +477,13 @@ component V3Op { var ta = [refType]; return newOp0(Opcode.PtrAtRefLayoutField(offset), ta, ta, ptrType); } - def newPtrAtUnboxedField(specs: List, ptrType: Type, bind: VarBinding) -> Operator { + def newPtrAtUnboxedObjectField(specs: List, ptrType: Type) -> Operator { var ta = [specs.head.receiver]; - return newOp0(Opcode.PtrAtUnboxedField(specs, bind), ta, ta, ptrType); + return newOp0(Opcode.PtrAtUnboxedObjectField(specs), ta, ta, ptrType); + } + def newPtrAtUnboxedComponentField(specs: List, ptrType: Type) -> Operator { + var ta = [specs.head.receiver]; + return newOp0(Opcode.PtrAtUnboxedComponentField(specs), ta, ta, ptrType); } def newPtrCmpSwp(ptrType: Type, valueType: Type) -> Operator { return newOp0(Opcode.PtrCmpSwp, [ptrType, valueType], [ptrType, valueType, valueType], type_z); @@ -629,7 +633,8 @@ def renderOp(op: Operator, buf: StringBuilder) -> StringBuilder { PtrAtComponentField(field) => rfunc = field.render; PtrAtObjectField(field) => rfunc = field.render; PtrAtRefLayoutField(field) => rfunc = StringBuilder.put1(_, "%d", field); - PtrAtUnboxedField(specs, bind) => rfunc = renderList(_, specs, IrSpec.render, ","); + PtrAtUnboxedObjectField(specs) => rfunc = renderList(_, specs, IrSpec.render, ","); + PtrAtUnboxedComponentField(specs) => rfunc = renderList(_, specs, IrSpec.render, ","); ClassAlloc(method) => if(method != null) rfunc = method.render; ClassGetMethod(method) => rfunc = method.render; ClassGetVirtual(method) => rfunc = method.render; diff --git a/aeneas/src/ir/Reachability.v3 b/aeneas/src/ir/Reachability.v3 index b871fbefb..ae5d323ea 100644 --- a/aeneas/src/ir/Reachability.v3 +++ b/aeneas/src/ir/Reachability.v3 @@ -331,7 +331,8 @@ class ReachabilityAnalyzer(compilation: Compilation) { CallVariantVirtual(method) => getVirtual(opMethod(op, method, context)); PtrAtObjectField(field) => if (op.useList != null) getAndSetField(makeField(op, field, context)); PtrAtComponentField(field) => if (op.useList != null) getAndSetField(makeField(op, field, context)); - PtrAtUnboxedField(specs, bind) => if (op.useList != null) getAndSetUnboxedField(op, specs, context); + PtrAtUnboxedObjectField(specs) => if (op.useList != null) getAndSetUnboxedField(op, specs, context); + PtrAtUnboxedComponentField(specs) => if (op.useList != null) getAndSetUnboxedField(op, specs, context); _ => ; } } diff --git a/aeneas/src/ir/SsaNormalizer.v3 b/aeneas/src/ir/SsaNormalizer.v3 index ce6c8cd56..bc84759e7 100644 --- a/aeneas/src/ir/SsaNormalizer.v3 +++ b/aeneas/src/ir/SsaNormalizer.v3 @@ -559,8 +559,11 @@ class SsaRaNormalizer extends SsaRebuilder { normDefault(i_old, op); } } - PtrAtUnboxedField(specs, bind) => { - normPtrAtUnboxedField(i_old, specs, op, bind); + PtrAtUnboxedObjectField(specs) => { + normPtrAtUnboxedField(i_old, specs, op, BoxKind.OBJECT); + } + PtrAtUnboxedComponentField(specs) => { + normPtrAtUnboxedField(i_old, specs, op, BoxKind.COMPONENT); } SystemCall(syscall) => { // normalize a syscall operator @@ -1320,7 +1323,7 @@ class SsaRaNormalizer extends SsaRebuilder { } return mapN(i_old, vals); } - def normPtrAtUnboxedField(i_old: SsaApplyOp, specs: List, op: Operator, bind: VarBinding) { + def normPtrAtUnboxedField(i_old: SsaApplyOp, specs: List, op: Operator, boxKind: BoxKind) { // XXX: propagate O_NO_NULL_CHECK and O_PURE var ai_new = genRefs(i_old.inputs); if (i_old.useList == null) { @@ -1344,9 +1347,13 @@ class SsaRaNormalizer extends SsaRebuilder { } // now should have a single field that we can do Pointer.atField if (ff.length > 0) { - var op_out = if(VarBinding.ComponentField.?(bind), - V3Op.newPtrAtComponentField(ff[0], i_old.op.sig.returnType()), - V3Op.newPtrAtObjectField(ff[0], i_old.op.sig.returnType())); + var op_out: Operator; + match (boxKind) { + OBJECT => + op_out = V3Op.newPtrAtObjectField(ff[0], i_old.op.sig.returnType()); + COMPONENT => + op_out = V3Op.newPtrAtComponentField(ff[0], i_old.op.sig.returnType()); + } var new_inputs = if(ai_new.length == 0, Ssa.NO_INSTRS, [ai_new[0]]); var i_new = curBlock.addApply(curBlock.source, op_out, new_inputs); return map1(i_old, i_new); @@ -1550,3 +1557,8 @@ class SsaRaNormalizer extends SsaRebuilder { if (!i_old.facts.O_NO_NULL_CHECK) curBlock.opNullCheck(obj.getType(), obj); } } + +enum BoxKind { + OBJECT, + COMPONENT +} diff --git a/aeneas/src/ssa/VstSsaGen.v3 b/aeneas/src/ssa/VstSsaGen.v3 index 481763d87..da39ada4e 100644 --- a/aeneas/src/ssa/VstSsaGen.v3 +++ b/aeneas/src/ssa/VstSsaGen.v3 @@ -904,10 +904,16 @@ class VstSsaGen extends VstVisitor { var op = V3Op.newPtrAtRefLayoutField(receiver, field.byteOffset, ptrType); return env.addApply(env.source, op, [target]); } - PtrAtUnboxedField(receiver, fields, ptrType, bind) => { + PtrAtUnboxedObjectField(receiver, fields, ptrType) => { var irSpecs: List = Lists.map<(Type, VstField), IrSpec>(fields, getPointedAtIrSpec(_, _)); - var op = V3Op.newPtrAtUnboxedField(irSpecs, ptrType, bind); - var tgt = if(VarBinding.ComponentField.?(bind), Ssa.NO_INSTRS, [target]); + var op = V3Op.newPtrAtUnboxedObjectField(irSpecs, ptrType); + var tgt = [target]; + return env.addApply(env.source, op, tgt); + } + PtrAtUnboxedComponentField(receiver, fields, ptrType) => { + var irSpecs: List = Lists.map<(Type, VstField), IrSpec>(fields, getPointedAtIrSpec(_, _)); + var op = V3Op.newPtrAtUnboxedComponentField(irSpecs, ptrType); + var tgt = Ssa.NO_INSTRS; return env.addApply(env.source, op, tgt); } } diff --git a/aeneas/src/vst/Verifier.v3 b/aeneas/src/vst/Verifier.v3 index 837b278ec..a1cbbe9f9 100644 --- a/aeneas/src/vst/Verifier.v3 +++ b/aeneas/src/vst/Verifier.v3 @@ -1629,8 +1629,13 @@ class TypeChecker(ERROR: ErrorGen, file: VstFile) extends VstVisitor var ubinfo = getPtrAtUnboxedField(x, null); var rcvr = ubinfo.0; var fields = ubinfo.1; - var vbind = ubinfo.2; - expr.appbind = AppBinding.PtrAtUnboxedField(receiver, fields, ptrType, vbind); + match (ubinfo.2) { + ObjectField => + expr.appbind = AppBinding.PtrAtUnboxedObjectField(receiver, fields, ptrType); + ComponentField => + expr.appbind = AppBinding.PtrAtUnboxedComponentField(receiver, fields, ptrType); + _ => ; + } expr.args = TupleExpr.new(VstList.new(expr.args.range(), null)); expr.target = rcvr; return expr.exactType = ptrType; diff --git a/aeneas/src/vst/Vst.v3 b/aeneas/src/vst/Vst.v3 index f5987457b..8c9cafd6f 100644 --- a/aeneas/src/vst/Vst.v3 +++ b/aeneas/src/vst/Vst.v3 @@ -968,7 +968,8 @@ type AppBinding { case PtrAtComponentField(receiver: Type, field: VstField, ptrType: Type); case PtrAtObjectField(receiver: Type, field: VstField, ptrType: Type); case PtrAtRefLayoutField(receiver: Type, field: VstLayoutField, ptrType: Type); - case PtrAtUnboxedField(receiver: Type, fields: List<(Type, VstField)>, ptrType: Type, bind: VarBinding); + case PtrAtUnboxedObjectField(receiver: Type, fields: List<(Type, VstField)>, ptrType: Type); + case PtrAtUnboxedComponentField(receiver: Type, fields: List<(Type, VstField)>, ptrType: Type); def nullCheck() -> bool { match (this) { diff --git a/aeneas/src/vst/VstPrinter.v3 b/aeneas/src/vst/VstPrinter.v3 index b16496300..e4e359007 100644 --- a/aeneas/src/vst/VstPrinter.v3 +++ b/aeneas/src/vst/VstPrinter.v3 @@ -334,7 +334,16 @@ class VstPrinter extends VstVisitor { PtrAtComponentField(x_rcvr, field, x_ta) => { extra = field.name(); } PtrAtObjectField(x_rcvr, field, x_ta) => { extra = field.name(); } PtrAtRefLayoutField(x_rcvr, field, x_ta) => { extra = field.name(); } - PtrAtUnboxedField(x_rcvr, fields, x_ta, x_bind) => { + PtrAtUnboxedObjectField(x_rcvr, fields, x_ta) => { + var buf = StringBuilder.new(); + var flds = fields; + while (flds != null) { + buf.putc('.').puts(flds.head.1.name()); + flds = flds.tail; + } + extra = buf.toString(); + } + PtrAtUnboxedComponentField(x_rcvr, fields, x_ta) => { var buf = StringBuilder.new(); var flds = fields; while (flds != null) { diff --git a/test/fail/Pointer_atUnboxedField03.v3 b/test/fail/Pointer_atUnboxedField03.v3 new file mode 100644 index 000000000..6c59af268 --- /dev/null +++ b/test/fail/Pointer_atUnboxedField03.v3 @@ -0,0 +1,195 @@ +//@execute 0=0 +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +var gv = V(1, 2, 3, 4); + +class C { + var clsv: V; + new(v8: u8, v16: u16, v32: u32, v64: u64) { + clsv = V(v8, v16, v32, v64); + } +} + +var gclsv = C.new(5, 6, 7, 8); + +component Comp { + var compv = V(9, 10, 11, 12); + var compclsv = C.new(13, 14, 15, 16); +} + +def main(arg: int) -> u64 { + + var lclsv = C.new(17, 18, 19, 20); + + // check values before doing anything, using regular accesses + + var startingRegularDiff = + + (u64.view(gv.v8 ) ^ 1) + + (u64.view(gv.v16) ^ 2) + + (u64.view(gv.v32) ^ 3) + + (u64.view(gv.v64) ^ 4) + + + (u64.view(gclsv.clsv.v8 ) ^ 5) + + (u64.view(gclsv.clsv.v16) ^ 6) + + (u64.view(gclsv.clsv.v32) ^ 7) + + (u64.view(gclsv.clsv.v64) ^ 8) + + + (u64.view(Comp.compv.v8 ) ^ 9) + + (u64.view(Comp.compv.v16) ^ 10) + + (u64.view(Comp.compv.v32) ^ 11) + + (u64.view(Comp.compv.v64) ^ 12) + + + (u64.view(Comp.compclsv.clsv.v8 ) ^ 13) + + (u64.view(Comp.compclsv.clsv.v16) ^ 14) + + (u64.view(Comp.compclsv.clsv.v32) ^ 15) + + (u64.view(Comp.compclsv.clsv.v64) ^ 16) + + + (u64.view(lclsv.clsv.v8 ) ^ 17) + + (u64.view(lclsv.clsv.v16) ^ 18) + + (u64.view(lclsv.clsv.v32) ^ 19) + + (u64.view(lclsv.clsv.v64) ^ 20); + + // check values before doing anything, using pointer loads + + var startingLoadDiff = + + (u64.view(Pointer.atField(gv.v8 ).load()) ^ 1) + + (u64.view(Pointer.atField(gv.v16).load()) ^ 2) + + (u64.view(Pointer.atField(gv.v32).load()) ^ 3) + + (u64.view(Pointer.atField(gv.v64).load()) ^ 4) + + + (u64.view(Pointer.atField(gclsv.clsv.v8 ).load()) ^ 5) + + (u64.view(Pointer.atField(gclsv.clsv.v16).load()) ^ 6) + + (u64.view(Pointer.atField(gclsv.clsv.v32).load()) ^ 7) + + (u64.view(Pointer.atField(gclsv.clsv.v64).load()) ^ 8) + + + (u64.view(Pointer.atField(Comp.compv.v8 ).load()) ^ 9) + + (u64.view(Pointer.atField(Comp.compv.v16).load()) ^ 10) + + (u64.view(Pointer.atField(Comp.compv.v32).load()) ^ 11) + + (u64.view(Pointer.atField(Comp.compv.v64).load()) ^ 12) + + + (u64.view(Pointer.atField(Comp.compclsv.clsv.v8 ).load()) ^ 13) + + (u64.view(Pointer.atField(Comp.compclsv.clsv.v16).load()) ^ 14) + + (u64.view(Pointer.atField(Comp.compclsv.clsv.v32).load()) ^ 15) + + (u64.view(Pointer.atField(Comp.compclsv.clsv.v64).load()) ^ 16) + + + (u64.view(Pointer.atField(lclsv.clsv.v8 ).load()) ^ 17) + + (u64.view(Pointer.atField(lclsv.clsv.v16).load()) ^ 18) + + (u64.view(Pointer.atField(lclsv.clsv.v32).load()) ^ 19) + + (u64.view(Pointer.atField(lclsv.clsv.v64).load()) ^ 20); + + // change values with cmpswp + + Pointer.atField(gv.v8 ).cmpswp(1, 101); + Pointer.atField(gv.v16).cmpswp(2, 102); + Pointer.atField(gv.v32).cmpswp(3, 103); +// Pointer.atField(gv.v64).cmpswp(4, 104); + + Pointer.atField(gclsv.clsv.v8 ).cmpswp(5, 105); + Pointer.atField(gclsv.clsv.v16).cmpswp(6, 106); + Pointer.atField(gclsv.clsv.v32).cmpswp(7, 107); +// Pointer.atField(gclsv.clsv.v64).cmpswp(8, 108); + + Pointer.atField(Comp.compv.v8 ).cmpswp(9, 109); + Pointer.atField(Comp.compv.v16).cmpswp(10, 110); + Pointer.atField(Comp.compv.v32).cmpswp(11, 111); +// Pointer.atField(Comp.compv.v64).cmpswp(12, 112); + + Pointer.atField(Comp.compclsv.clsv.v8 ).cmpswp(13, 113); + Pointer.atField(Comp.compclsv.clsv.v16).cmpswp(14, 114); + Pointer.atField(Comp.compclsv.clsv.v32).cmpswp(15, 115); +// Pointer.atField(Comp.compclsv.clsv.v64).cmpswp(16, 116); + + Pointer.atField(lclsv.clsv.v8 ).cmpswp(17, 117); + Pointer.atField(lclsv.clsv.v16).cmpswp(18, 118); + Pointer.atField(lclsv.clsv.v32).cmpswp(19, 119); +// Pointer.atField(lclsv.clsv.v64).cmpswp(20, 120); + + // check values after cmpswp, using regular accesses + + var afterCmpswpDiff = + + (u64.view(gv.v8 ) ^ 101) + + (u64.view(gv.v16) ^ 102) + + (u64.view(gv.v32) ^ 103) + + (u64.view(gv.v64) ^ 104) + + + (u64.view(gclsv.clsv.v8 ) ^ 105) + + (u64.view(gclsv.clsv.v16) ^ 106) + + (u64.view(gclsv.clsv.v32) ^ 107) + + (u64.view(gclsv.clsv.v64) ^ 108) + + + (u64.view(Comp.compv.v8 ) ^ 109) + + (u64.view(Comp.compv.v16) ^ 110) + + (u64.view(Comp.compv.v32) ^ 111) + + (u64.view(Comp.compv.v64) ^ 112) + + + (u64.view(Comp.compclsv.clsv.v8 ) ^ 113) + + (u64.view(Comp.compclsv.clsv.v16) ^ 114) + + (u64.view(Comp.compclsv.clsv.v32) ^ 115) + + (u64.view(Comp.compclsv.clsv.v64) ^ 116) + + + (u64.view(lclsv.clsv.v8 ) ^ 117) + + (u64.view(lclsv.clsv.v16) ^ 118) + + (u64.view(lclsv.clsv.v32) ^ 119) + + (u64.view(lclsv.clsv.v64) ^ 120); + + // store original values back, using pointer stores + + Pointer.atField(gv.v8 ).store(1); + Pointer.atField(gv.v16).store(2); + Pointer.atField(gv.v32).store(3); + Pointer.atField(gv.v64).store(4); + + Pointer.atField(gclsv.clsv.v8 ).store(5); + Pointer.atField(gclsv.clsv.v16).store(6); + Pointer.atField(gclsv.clsv.v32).store(7); + Pointer.atField(gclsv.clsv.v64).store(8); + + Pointer.atField(Comp.compv.v8 ).store(9); + Pointer.atField(Comp.compv.v16).store(10); + Pointer.atField(Comp.compv.v32).store(11); + Pointer.atField(Comp.compv.v64).store(12); + + Pointer.atField(Comp.compclsv.clsv.v8 ).store(13); + Pointer.atField(Comp.compclsv.clsv.v16).store(14); + Pointer.atField(Comp.compclsv.clsv.v32).store(15); + Pointer.atField(Comp.compclsv.clsv.v64).store(16); + + Pointer.atField(lclsv.clsv.v8 ).store(17); + Pointer.atField(lclsv.clsv.v16).store(18); + Pointer.atField(lclsv.clsv.v32).store(19); + Pointer.atField(lclsv.clsv.v64).store(20); + + // check the restored values, using regular accesses + + var afterStoreDiff = + + (u64.view(gv.v8 ) ^ 1) + + (u64.view(gv.v16) ^ 2) + + (u64.view(gv.v32) ^ 3) + + (u64.view(gv.v64) ^ 4) + + + (u64.view(gclsv.clsv.v8 ) ^ 5) + + (u64.view(gclsv.clsv.v16) ^ 6) + + (u64.view(gclsv.clsv.v32) ^ 7) + + (u64.view(gclsv.clsv.v64) ^ 8) + + + (u64.view(Comp.compv.v8 ) ^ 9) + + (u64.view(Comp.compv.v16) ^ 10) + + (u64.view(Comp.compv.v32) ^ 11) + + (u64.view(Comp.compv.v64) ^ 12) + + + (u64.view(Comp.compclsv.clsv.v8 ) ^ 13) + + (u64.view(Comp.compclsv.clsv.v16) ^ 14) + + (u64.view(Comp.compclsv.clsv.v32) ^ 15) + + (u64.view(Comp.compclsv.clsv.v64) ^ 16) + + + (u64.view(lclsv.clsv.v8 ) ^ 17) + + (u64.view(lclsv.clsv.v16) ^ 18) + + (u64.view(lclsv.clsv.v32) ^ 19) + + (u64.view(lclsv.clsv.v64) ^ 20); + + return startingRegularDiff + startingLoadDiff + afterCmpswpDiff + afterStoreDiff; +} diff --git a/test/pointer/Pointer_atUnboxedField01.v3 b/test/pointer/Pointer_atUnboxedField01.v3 index 23a023aee..fe8222993 100644 --- a/test/pointer/Pointer_atUnboxedField01.v3 +++ b/test/pointer/Pointer_atUnboxedField01.v3 @@ -1,9 +1,7 @@ //@execute 0=0; 6=6 type V2(v21: int, v22: int, v23: int) #unboxed { } -type V3(v31: int, v32: int, v33: int) #unboxed { } var v2 = V2(200, 201, 202); -var v3 = V3(300, 301, 302); def main(arg: int) -> int { @@ -16,14 +14,5 @@ def main(arg: int) -> int { var diff2 = int.view(pv23 - pv21); - // check that field is *not* optimized away: - // The Pointer.atField is used by a load and the load result is also used. - var pv31 = Pointer.atField(v3.v31); - var pv32 = Pointer.atField(v3.v32); - var pv33 = Pointer.atField(v3.v33); - var i32 = pv32.load(); - - var diff3 = int.view(pv33 - pv31); - - return arg + (diff2 - 4) + (diff3 - 8) + (i32 - 301); + return arg + (diff2 - 4); } diff --git a/test/pointer/Pointer_atUnboxedField02.v3 b/test/pointer/Pointer_atUnboxedField02.v3 index 5ea67f192..36b7da51e 100644 --- a/test/pointer/Pointer_atUnboxedField02.v3 +++ b/test/pointer/Pointer_atUnboxedField02.v3 @@ -1,195 +1,18 @@ -//@execute 0=0 -type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } +//@execute 0=0; 6=6 +type V3(v31: int, v32: int, v33: int) #unboxed { } -var gv = V(1, 2, 3, 4); +var v3 = V3(300, 301, 302); -class C { - var clsv: V; - new(v8: u8, v16: u16, v32: u32, v64: u64) { - clsv = V(v8, v16, v32, v64); - } -} - -var gclsv = C.new(5, 6, 7, 8); - -component Comp { - var compv = V(9, 10, 11, 12); - var compclsv = C.new(13, 14, 15, 16); -} - -def main(arg: int) -> u64 { - - var lclsv = C.new(17, 18, 19, 20); - - // check values before doing anything, using regular accesses - - var startingRegularDiff = - - (u64.view(gv.v8 ) ^ 1) + - (u64.view(gv.v16) ^ 2) + - (u64.view(gv.v32) ^ 3) + - (u64.view(gv.v64) ^ 4) + - - (u64.view(gclsv.clsv.v8 ) ^ 5) + - (u64.view(gclsv.clsv.v16) ^ 6) + - (u64.view(gclsv.clsv.v32) ^ 7) + - (u64.view(gclsv.clsv.v64) ^ 8) + - - (u64.view(Comp.compv.v8 ) ^ 9) + - (u64.view(Comp.compv.v16) ^ 10) + - (u64.view(Comp.compv.v32) ^ 11) + - (u64.view(Comp.compv.v64) ^ 12) + - - (u64.view(Comp.compclsv.clsv.v8 ) ^ 13) + - (u64.view(Comp.compclsv.clsv.v16) ^ 14) + - (u64.view(Comp.compclsv.clsv.v32) ^ 15) + - (u64.view(Comp.compclsv.clsv.v64) ^ 16) + - - (u64.view(lclsv.clsv.v8 ) ^ 17) + - (u64.view(lclsv.clsv.v16) ^ 18) + - (u64.view(lclsv.clsv.v32) ^ 19) + - (u64.view(lclsv.clsv.v64) ^ 20); - - // check values before doing anything, using pointer loads - - var startingLoadDiff = - - (u64.view(Pointer.atField(gv.v8 ).load()) ^ 1) + - (u64.view(Pointer.atField(gv.v16).load()) ^ 2) + - (u64.view(Pointer.atField(gv.v32).load()) ^ 3) + - (u64.view(Pointer.atField(gv.v64).load()) ^ 4) + - - (u64.view(Pointer.atField(gclsv.clsv.v8 ).load()) ^ 5) + - (u64.view(Pointer.atField(gclsv.clsv.v16).load()) ^ 6) + - (u64.view(Pointer.atField(gclsv.clsv.v32).load()) ^ 7) + - (u64.view(Pointer.atField(gclsv.clsv.v64).load()) ^ 8) + - - (u64.view(Pointer.atField(Comp.compv.v8 ).load()) ^ 9) + - (u64.view(Pointer.atField(Comp.compv.v16).load()) ^ 10) + - (u64.view(Pointer.atField(Comp.compv.v32).load()) ^ 11) + - (u64.view(Pointer.atField(Comp.compv.v64).load()) ^ 12) + - - (u64.view(Pointer.atField(Comp.compclsv.clsv.v8 ).load()) ^ 13) + - (u64.view(Pointer.atField(Comp.compclsv.clsv.v16).load()) ^ 14) + - (u64.view(Pointer.atField(Comp.compclsv.clsv.v32).load()) ^ 15) + - (u64.view(Pointer.atField(Comp.compclsv.clsv.v64).load()) ^ 16) + - - (u64.view(Pointer.atField(lclsv.clsv.v8 ).load()) ^ 17) + - (u64.view(Pointer.atField(lclsv.clsv.v16).load()) ^ 18) + - (u64.view(Pointer.atField(lclsv.clsv.v32).load()) ^ 19) + - (u64.view(Pointer.atField(lclsv.clsv.v64).load()) ^ 20); - - // change values with cmpswp - - Pointer.atField(gv.v8 ).cmpswp(1, 101); - Pointer.atField(gv.v16).cmpswp(2, 102); - Pointer.atField(gv.v32).cmpswp(3, 103); - Pointer.atField(gv.v64).cmpswp(4, 104); - - Pointer.atField(gclsv.clsv.v8 ).cmpswp(5, 105); - Pointer.atField(gclsv.clsv.v16).cmpswp(6, 106); - Pointer.atField(gclsv.clsv.v32).cmpswp(7, 107); - Pointer.atField(gclsv.clsv.v64).cmpswp(8, 108); - - Pointer.atField(Comp.compv.v8 ).cmpswp(9, 109); - Pointer.atField(Comp.compv.v16).cmpswp(10, 110); - Pointer.atField(Comp.compv.v32).cmpswp(11, 111); - Pointer.atField(Comp.compv.v64).cmpswp(12, 112); - - Pointer.atField(Comp.compclsv.clsv.v8 ).cmpswp(13, 113); - Pointer.atField(Comp.compclsv.clsv.v16).cmpswp(14, 114); - Pointer.atField(Comp.compclsv.clsv.v32).cmpswp(15, 115); - Pointer.atField(Comp.compclsv.clsv.v64).cmpswp(16, 116); - - Pointer.atField(lclsv.clsv.v8 ).cmpswp(17, 117); - Pointer.atField(lclsv.clsv.v16).cmpswp(18, 118); - Pointer.atField(lclsv.clsv.v32).cmpswp(19, 119); - Pointer.atField(lclsv.clsv.v64).cmpswp(20, 120); - - // check values after cmpswp, using regular accesses - - var afterCmpswpDiff = - - (u64.view(gv.v8 ) ^ 101) + - (u64.view(gv.v16) ^ 102) + - (u64.view(gv.v32) ^ 103) + - (u64.view(gv.v64) ^ 104) + - - (u64.view(gclsv.clsv.v8 ) ^ 105) + - (u64.view(gclsv.clsv.v16) ^ 106) + - (u64.view(gclsv.clsv.v32) ^ 107) + - (u64.view(gclsv.clsv.v64) ^ 108) + - - (u64.view(Comp.compv.v8 ) ^ 109) + - (u64.view(Comp.compv.v16) ^ 110) + - (u64.view(Comp.compv.v32) ^ 111) + - (u64.view(Comp.compv.v64) ^ 112) + - - (u64.view(Comp.compclsv.clsv.v8 ) ^ 113) + - (u64.view(Comp.compclsv.clsv.v16) ^ 114) + - (u64.view(Comp.compclsv.clsv.v32) ^ 115) + - (u64.view(Comp.compclsv.clsv.v64) ^ 116) + - - (u64.view(lclsv.clsv.v8 ) ^ 117) + - (u64.view(lclsv.clsv.v16) ^ 118) + - (u64.view(lclsv.clsv.v32) ^ 119) + - (u64.view(lclsv.clsv.v64) ^ 120); - - // store original values back, using pointer stores - - Pointer.atField(gv.v8 ).store(1); - Pointer.atField(gv.v16).store(2); - Pointer.atField(gv.v32).store(3); - Pointer.atField(gv.v64).store(4); - - Pointer.atField(gclsv.clsv.v8 ).store(5); - Pointer.atField(gclsv.clsv.v16).store(6); - Pointer.atField(gclsv.clsv.v32).store(7); - Pointer.atField(gclsv.clsv.v64).store(8); - - Pointer.atField(Comp.compv.v8 ).store(9); - Pointer.atField(Comp.compv.v16).store(10); - Pointer.atField(Comp.compv.v32).store(11); - Pointer.atField(Comp.compv.v64).store(12); - - Pointer.atField(Comp.compclsv.clsv.v8 ).store(13); - Pointer.atField(Comp.compclsv.clsv.v16).store(14); - Pointer.atField(Comp.compclsv.clsv.v32).store(15); - Pointer.atField(Comp.compclsv.clsv.v64).store(16); - - Pointer.atField(lclsv.clsv.v8 ).store(17); - Pointer.atField(lclsv.clsv.v16).store(18); - Pointer.atField(lclsv.clsv.v32).store(19); - Pointer.atField(lclsv.clsv.v64).store(20); - - // check the restored values, using regular accesses - - var afterStoreDiff = - - (u64.view(gv.v8 ) ^ 1) + - (u64.view(gv.v16) ^ 2) + - (u64.view(gv.v32) ^ 3) + - (u64.view(gv.v64) ^ 4) + - - (u64.view(gclsv.clsv.v8 ) ^ 5) + - (u64.view(gclsv.clsv.v16) ^ 6) + - (u64.view(gclsv.clsv.v32) ^ 7) + - (u64.view(gclsv.clsv.v64) ^ 8) + - - (u64.view(Comp.compv.v8 ) ^ 9) + - (u64.view(Comp.compv.v16) ^ 10) + - (u64.view(Comp.compv.v32) ^ 11) + - (u64.view(Comp.compv.v64) ^ 12) + +def main(arg: int) -> int { - (u64.view(Comp.compclsv.clsv.v8 ) ^ 13) + - (u64.view(Comp.compclsv.clsv.v16) ^ 14) + - (u64.view(Comp.compclsv.clsv.v32) ^ 15) + - (u64.view(Comp.compclsv.clsv.v64) ^ 16) + + // check that field is not optimized away: + // The Pointer.atField is used by a load and the load result is also used. + var pv31 = Pointer.atField(v3.v31); + var pv32 = Pointer.atField(v3.v32); + var pv33 = Pointer.atField(v3.v33); + var i32 = pv32.load(); - (u64.view(lclsv.clsv.v8 ) ^ 17) + - (u64.view(lclsv.clsv.v16) ^ 18) + - (u64.view(lclsv.clsv.v32) ^ 19) + - (u64.view(lclsv.clsv.v64) ^ 20); + var diff3 = int.view(pv33 - pv31); - return startingRegularDiff + startingLoadDiff + afterCmpswpDiff + afterStoreDiff; + return arg + (diff3 - 8) + (i32 - 301); } diff --git a/test/pointer/Pointer_atUnboxedField03.v3 b/test/pointer/Pointer_atUnboxedField03.v3 new file mode 100644 index 000000000..43b9ca098 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField03.v3 @@ -0,0 +1,165 @@ +//@execute 0=0 +type V(v8: u8, v16: u16, v32: u32) #unboxed { } + +var gv = V(1, 2, 3); + +class C { + var clsv: V; + new(v8: u8, v16: u16, v32: u32) { + clsv = V(v8, v16, v32); + } +} + +var gclsv = C.new(5, 6, 7); + +component Comp { + var compv = V(9, 10, 11); + var compclsv = C.new(13, 14, 15); +} + +def main(arg: int) -> u64 { + + var lclsv = C.new(17, 18, 19); + + // check values before doing anything, using regular accesses + + var startingRegularDiff = + + (u32.view(gv.v8 ) ^ 1) + + (u32.view(gv.v16) ^ 2) + + (u32.view(gv.v32) ^ 3) + + + (u32.view(gclsv.clsv.v8 ) ^ 5) + + (u32.view(gclsv.clsv.v16) ^ 6) + + (u32.view(gclsv.clsv.v32) ^ 7) + + + (u32.view(Comp.compv.v8 ) ^ 9) + + (u32.view(Comp.compv.v16) ^ 10) + + (u32.view(Comp.compv.v32) ^ 11) + + + (u32.view(Comp.compclsv.clsv.v8 ) ^ 13) + + (u32.view(Comp.compclsv.clsv.v16) ^ 14) + + (u32.view(Comp.compclsv.clsv.v32) ^ 15) + + + (u32.view(lclsv.clsv.v8 ) ^ 17) + + (u32.view(lclsv.clsv.v16) ^ 18) + + (u32.view(lclsv.clsv.v32) ^ 19); + + // check values before doing anything, using pointer loads + + var startingLoadDiff = + + (u32.view(Pointer.atField(gv.v8 ).load()) ^ 1) + + (u32.view(Pointer.atField(gv.v16).load()) ^ 2) + + (u32.view(Pointer.atField(gv.v32).load()) ^ 3) + + + (u32.view(Pointer.atField(gclsv.clsv.v8 ).load()) ^ 5) + + (u32.view(Pointer.atField(gclsv.clsv.v16).load()) ^ 6) + + (u32.view(Pointer.atField(gclsv.clsv.v32).load()) ^ 7) + + + (u32.view(Pointer.atField(Comp.compv.v8 ).load()) ^ 9) + + (u32.view(Pointer.atField(Comp.compv.v16).load()) ^ 10) + + (u32.view(Pointer.atField(Comp.compv.v32).load()) ^ 11) + + + (u32.view(Pointer.atField(Comp.compclsv.clsv.v8 ).load()) ^ 13) + + (u32.view(Pointer.atField(Comp.compclsv.clsv.v16).load()) ^ 14) + + (u32.view(Pointer.atField(Comp.compclsv.clsv.v32).load()) ^ 15) + + + (u32.view(Pointer.atField(lclsv.clsv.v8 ).load()) ^ 17) + + (u32.view(Pointer.atField(lclsv.clsv.v16).load()) ^ 18) + + (u32.view(Pointer.atField(lclsv.clsv.v32).load()) ^ 19); + + // change values with cmpswp + + Pointer.atField(gv.v8 ).cmpswp(1, 101); + Pointer.atField(gv.v16).cmpswp(2, 102); + Pointer.atField(gv.v32).cmpswp(3, 103); + + Pointer.atField(gclsv.clsv.v8 ).cmpswp(5, 105); + Pointer.atField(gclsv.clsv.v16).cmpswp(6, 106); + Pointer.atField(gclsv.clsv.v32).cmpswp(7, 107); + + Pointer.atField(Comp.compv.v8 ).cmpswp(9, 109); + Pointer.atField(Comp.compv.v16).cmpswp(10, 110); + Pointer.atField(Comp.compv.v32).cmpswp(11, 111); + + Pointer.atField(Comp.compclsv.clsv.v8 ).cmpswp(13, 113); + Pointer.atField(Comp.compclsv.clsv.v16).cmpswp(14, 114); + Pointer.atField(Comp.compclsv.clsv.v32).cmpswp(15, 115); + + Pointer.atField(lclsv.clsv.v8 ).cmpswp(17, 117); + Pointer.atField(lclsv.clsv.v16).cmpswp(18, 118); + Pointer.atField(lclsv.clsv.v32).cmpswp(19, 119); + + // check values after cmpswp, using regular accesses + + var afterCmpswpDiff = + + (u32.view(gv.v8 ) ^ 101) + + (u32.view(gv.v16) ^ 102) + + (u32.view(gv.v32) ^ 103) + + + (u32.view(gclsv.clsv.v8 ) ^ 105) + + (u32.view(gclsv.clsv.v16) ^ 106) + + (u32.view(gclsv.clsv.v32) ^ 107) + + + (u32.view(Comp.compv.v8 ) ^ 109) + + (u32.view(Comp.compv.v16) ^ 110) + + (u32.view(Comp.compv.v32) ^ 111) + + + (u32.view(Comp.compclsv.clsv.v8 ) ^ 113) + + (u32.view(Comp.compclsv.clsv.v16) ^ 114) + + (u32.view(Comp.compclsv.clsv.v32) ^ 115) + + + (u32.view(lclsv.clsv.v8 ) ^ 117) + + (u32.view(lclsv.clsv.v16) ^ 118) + + (u32.view(lclsv.clsv.v32) ^ 119); + + // store original values back, using pointer stores + + Pointer.atField(gv.v8 ).store(1); + Pointer.atField(gv.v16).store(2); + Pointer.atField(gv.v32).store(3); + + Pointer.atField(gclsv.clsv.v8 ).store(5); + Pointer.atField(gclsv.clsv.v16).store(6); + Pointer.atField(gclsv.clsv.v32).store(7); + + Pointer.atField(Comp.compv.v8 ).store(9); + Pointer.atField(Comp.compv.v16).store(10); + Pointer.atField(Comp.compv.v32).store(11); + + Pointer.atField(Comp.compclsv.clsv.v8 ).store(13); + Pointer.atField(Comp.compclsv.clsv.v16).store(14); + Pointer.atField(Comp.compclsv.clsv.v32).store(15); + + Pointer.atField(lclsv.clsv.v8 ).store(17); + Pointer.atField(lclsv.clsv.v16).store(18); + Pointer.atField(lclsv.clsv.v32).store(19); + + // check the restored values, using regular accesses + + var afterStoreDiff = + + (u32.view(gv.v8 ) ^ 1) + + (u32.view(gv.v16) ^ 2) + + (u32.view(gv.v32) ^ 3) + + + (u32.view(gclsv.clsv.v8 ) ^ 5) + + (u32.view(gclsv.clsv.v16) ^ 6) + + (u32.view(gclsv.clsv.v32) ^ 7) + + + (u32.view(Comp.compv.v8 ) ^ 9) + + (u32.view(Comp.compv.v16) ^ 10) + + (u32.view(Comp.compv.v32) ^ 11) + + + (u32.view(Comp.compclsv.clsv.v8 ) ^ 13) + + (u32.view(Comp.compclsv.clsv.v16) ^ 14) + + (u32.view(Comp.compclsv.clsv.v32) ^ 15) + + + (u32.view(lclsv.clsv.v8 ) ^ 17) + + (u32.view(lclsv.clsv.v16) ^ 18) + + (u32.view(lclsv.clsv.v32) ^ 19); + + return startingRegularDiff + startingLoadDiff + afterCmpswpDiff + afterStoreDiff; +} diff --git a/test/pointer/Pointer_atUnboxedField04.v3 b/test/pointer/Pointer_atUnboxedField04.v3 new file mode 100644 index 000000000..f4910b4e2 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField04.v3 @@ -0,0 +1,21 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +var gv = V(1, 2, 3, 4); + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + // check values before doing anything, using regular accesses + + diff += + (u32.view(gv.v8 ) ^ 1) + + (u32.view(gv.v16) ^ 2) + + (u32.view(gv.v32) ^ 3) + + (u32.view(gv.v64) ^ 4); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField05.v3 b/test/pointer/Pointer_atUnboxedField05.v3 new file mode 100644 index 000000000..4a1f9d3b0 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField05.v3 @@ -0,0 +1,23 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +component Comp { + var compv = V(1, 2, 3, 4); +} + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + // check values before doing anything, using regular accesses + + diff += + (u32.view(Comp.compv.v8 ) ^ 1) + + (u32.view(Comp.compv.v16) ^ 2) + + (u32.view(Comp.compv.v32) ^ 3) + + (u32.view(Comp.compv.v64) ^ 4); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField06.v3 b/test/pointer/Pointer_atUnboxedField06.v3 new file mode 100644 index 000000000..f7ae147f0 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField06.v3 @@ -0,0 +1,28 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +class C { + var clsv: V; + new(v8: u8, v16: u16, v32: u32, v64: u64) { + clsv = V(v8, v16, v32, v64); + } +} + +var gclsv = C.new(1, 2, 3, 4); + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + // check values before doing anything, using regular accesses + + diff += + (u32.view(gclsv.clsv.v8 ) ^ 1) + + (u32.view(gclsv.clsv.v16) ^ 2) + + (u32.view(gclsv.clsv.v32) ^ 3) + + (u32.view(gclsv.clsv.v64) ^ 4); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField07.v3 b/test/pointer/Pointer_atUnboxedField07.v3 new file mode 100644 index 000000000..29a83544a --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField07.v3 @@ -0,0 +1,30 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +class C { + var clsv: V; + new(v8: u8, v16: u16, v32: u32, v64: u64) { + clsv = V(v8, v16, v32, v64); + } +} + +component Comp { + var compclsv = C.new(1, 2, 3, 4); +} + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + // check values before doing anything, using regular accesses + + diff += + (u32.view(Comp.compclsv.clsv.v8 ) ^ 1) + + (u32.view(Comp.compclsv.clsv.v16) ^ 2) + + (u32.view(Comp.compclsv.clsv.v32) ^ 3) + + (u32.view(Comp.compclsv.clsv.v64) ^ 4); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField08.v3 b/test/pointer/Pointer_atUnboxedField08.v3 new file mode 100644 index 000000000..ef269675c --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField08.v3 @@ -0,0 +1,28 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +class C { + var clsv: V; + new(v8: u8, v16: u16, v32: u32, v64: u64) { + clsv = V(v8, v16, v32, v64); + } +} + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + var lclsv = C.new(1, 2, 3, 4); + + // check values before doing anything, using regular accesses + + diff += + (u32.view(lclsv.clsv.v8 ) ^ 1) + + (u32.view(lclsv.clsv.v16) ^ 2) + + (u32.view(lclsv.clsv.v32) ^ 3) + + (u32.view(lclsv.clsv.v64) ^ 4); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField09.v3 b/test/pointer/Pointer_atUnboxedField09.v3 new file mode 100644 index 000000000..8909ce1c4 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField09.v3 @@ -0,0 +1,22 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +var gv = V(1, 2, 3, 4); + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + // check values before doing anything, using pointer loads + + diff += + (u32.view(Pointer.atField(gv.v8 ).load< u8>()) ^ 1) + + (u32.view(Pointer.atField(gv.v16).load()) ^ 2) + + (u32.view(Pointer.atField(gv.v32).load()) ^ 3); + if (Pointer.SIZE == 8) + diff += (u32.view(Pointer.atField(gv.v64).load()) ^ 4); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField10.v3 b/test/pointer/Pointer_atUnboxedField10.v3 new file mode 100644 index 000000000..d16bd6c69 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField10.v3 @@ -0,0 +1,24 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +component Comp { + var compv = V(1, 2, 3, 4); +} + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + // check values before doing anything, using pointer loads + + diff += + (u32.view(Pointer.atField(Comp.compv.v8 ).load< u8>()) ^ 1) + + (u32.view(Pointer.atField(Comp.compv.v16).load()) ^ 2) + + (u32.view(Pointer.atField(Comp.compv.v32).load()) ^ 3); + if (Pointer.SIZE == 8) + diff += (u32.view(Pointer.atField(Comp.compv.v64).load()) ^ 4); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField11.v3 b/test/pointer/Pointer_atUnboxedField11.v3 new file mode 100644 index 000000000..8008b8528 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField11.v3 @@ -0,0 +1,29 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +class C { + var clsv: V; + new(v8: u8, v16: u16, v32: u32, v64: u64) { + clsv = V(v8, v16, v32, v64); + } +} + +var gclsv = C.new(1, 2, 3, 4); + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + // check values before doing anything, using pointer loads + + diff += + (u32.view(Pointer.atField(gclsv.clsv.v8 ).load< u8>()) ^ 1) + + (u32.view(Pointer.atField(gclsv.clsv.v16).load()) ^ 2) + + (u32.view(Pointer.atField(gclsv.clsv.v32).load()) ^ 3); + if (Pointer.SIZE == 8) + diff += (u32.view(Pointer.atField(gclsv.clsv.v64).load()) ^ 4); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField12.v3 b/test/pointer/Pointer_atUnboxedField12.v3 new file mode 100644 index 000000000..858e66d12 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField12.v3 @@ -0,0 +1,31 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +class C { + var clsv: V; + new(v8: u8, v16: u16, v32: u32, v64: u64) { + clsv = V(v8, v16, v32, v64); + } +} + +component Comp { + var compclsv = C.new(1, 2, 3, 4); +} + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + // check values before doing anything, using pointer loads + + diff += + (u32.view(Pointer.atField(Comp.compclsv.clsv.v8 ).load< u8>()) ^ 1) + + (u32.view(Pointer.atField(Comp.compclsv.clsv.v16).load()) ^ 2) + + (u32.view(Pointer.atField(Comp.compclsv.clsv.v32).load()) ^ 3); + if (Pointer.SIZE == 8) + diff += (u32.view(Pointer.atField(Comp.compclsv.clsv.v64).load()) ^ 4); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField13.v3 b/test/pointer/Pointer_atUnboxedField13.v3 new file mode 100644 index 000000000..dd8dc5ee4 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField13.v3 @@ -0,0 +1,29 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +class C { + var clsv: V; + new(v8: u8, v16: u16, v32: u32, v64: u64) { + clsv = V(v8, v16, v32, v64); + } +} + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + var lclsv = C.new(1, 2, 3, 4); + + // check values before doing anything, using pointer loads + + diff += + (u32.view(Pointer.atField(lclsv.clsv.v8 ).load< u8>()) ^ 1) + + (u32.view(Pointer.atField(lclsv.clsv.v16).load()) ^ 2) + + (u32.view(Pointer.atField(lclsv.clsv.v32).load()) ^ 3); + if (Pointer.SIZE == 8) + diff += (u32.view(Pointer.atField(lclsv.clsv.v64).load()) ^ 4); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField14.v3 b/test/pointer/Pointer_atUnboxedField14.v3 new file mode 100644 index 000000000..c88abdea0 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField14.v3 @@ -0,0 +1,32 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +var gv = V(1, 2, 3, 4); + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + // change values with cmpswp + + Pointer.atField(gv.v8 ).cmpswp< u8>(1, u8 .!(101 + arg)); + Pointer.atField(gv.v16).cmpswp(2, u16.!(102 + arg)); + Pointer.atField(gv.v32).cmpswp(3, u32.!(103 + arg)); + + if (Pointer.SIZE == 8) + Pointer.atField(gv.v64).cmpswp(4, u64.!(104 + arg)); + + // check values after cmpswp, using regular accesses + + diff += + (u32.view(gv.v8 ) ^ u32.!(101 + arg)) + + (u32.view(gv.v16) ^ u32.!(102 + arg)) + + (u32.view(gv.v32) ^ u32.!(103 + arg)); + + if (Pointer.SIZE == 8) + diff += (u32.view(gv.v64) ^ u32.!(104 + arg)); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField15.v3 b/test/pointer/Pointer_atUnboxedField15.v3 new file mode 100644 index 000000000..6a1044768 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField15.v3 @@ -0,0 +1,34 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +component Comp { + var compv = V(1, 2, 3, 4); +} + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + // change values with cmpswp + + Pointer.atField(Comp.compv.v8 ).cmpswp< u8>(1, u8 .!(101 + arg)); + Pointer.atField(Comp.compv.v16).cmpswp(2, u16.!(102 + arg)); + Pointer.atField(Comp.compv.v32).cmpswp(3, u32.!(103 + arg)); + + if (Pointer.SIZE == 8) + Pointer.atField(Comp.compv.v64).cmpswp(4, u64.!(104 + arg)); + + // check values after cmpswp, using regular accesses + + diff += + (u32.view(Comp.compv.v8 ) ^ u32.!(101 + arg)) + + (u32.view(Comp.compv.v16) ^ u32.!(102 + arg)) + + (u32.view(Comp.compv.v32) ^ u32.!(103 + arg)); + + if (Pointer.SIZE == 8) + diff += (u32.view(Comp.compv.v64) ^ u32.!(104 + arg)); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField16.v3 b/test/pointer/Pointer_atUnboxedField16.v3 new file mode 100644 index 000000000..b59534539 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField16.v3 @@ -0,0 +1,39 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +class C { + var clsv: V; + new(v8: u8, v16: u16, v32: u32, v64: u64) { + clsv = V(v8, v16, v32, v64); + } +} + +var gclsv = C.new(1, 2, 3, 4); + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + // change values with cmpswp + + Pointer.atField(gclsv.clsv.v8 ).cmpswp< u8>(1, u8 .!(101 + arg)); + Pointer.atField(gclsv.clsv.v16).cmpswp(2, u16.!(102 + arg)); + Pointer.atField(gclsv.clsv.v32).cmpswp(3, u32.!(103 + arg)); + + if (Pointer.SIZE == 8) + Pointer.atField(gclsv.clsv.v64).cmpswp(4, u64.!(104 + arg)); + + // check values after cmpswp, using regular accesses + + diff += + (u32.view(gclsv.clsv.v8 ) ^ u32.!(101 + arg)) + + (u32.view(gclsv.clsv.v16) ^ u32.!(102 + arg)) + + (u32.view(gclsv.clsv.v32) ^ u32.!(103 + arg)); + + if (Pointer.SIZE == 8) + diff += (u32.view(gclsv.clsv.v64) ^ u32.!(104 + arg)); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField17.v3 b/test/pointer/Pointer_atUnboxedField17.v3 new file mode 100644 index 000000000..3d5c25520 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField17.v3 @@ -0,0 +1,41 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +class C { + var clsv: V; + new(v8: u8, v16: u16, v32: u32, v64: u64) { + clsv = V(v8, v16, v32, v64); + } +} + +component Comp { + var compclsv = C.new(1, 2, 3, 4); +} + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + // change values with cmpswp + + Pointer.atField(Comp.compclsv.clsv.v8 ).cmpswp< u8>(1, u8 .!(101 + arg)); + Pointer.atField(Comp.compclsv.clsv.v16).cmpswp(2, u16.!(102 + arg)); + Pointer.atField(Comp.compclsv.clsv.v32).cmpswp(3, u32.!(103 + arg)); + + if (Pointer.SIZE == 8) + Pointer.atField(Comp.compclsv.clsv.v64).cmpswp(4, u64.!(104 + arg)); + + // check values after cmpswp, using regular accesses + + diff += + (u32.view(Comp.compclsv.clsv.v8 ) ^ u32.!(101 + arg)) + + (u32.view(Comp.compclsv.clsv.v16) ^ u32.!(102 + arg)) + + (u32.view(Comp.compclsv.clsv.v32) ^ u32.!(103 + arg)); + + if (Pointer.SIZE == 8) + diff += (u32.view(Comp.compclsv.clsv.v64) ^ u32.!(104 + arg)); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField18.v3 b/test/pointer/Pointer_atUnboxedField18.v3 new file mode 100644 index 000000000..622384bf7 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField18.v3 @@ -0,0 +1,39 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +class C { + var clsv: V; + new(v8: u8, v16: u16, v32: u32, v64: u64) { + clsv = V(v8, v16, v32, v64); + } +} + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + var lclsv = C.new(1, 2, 3, 4); + + // change values with cmpswp + + Pointer.atField(lclsv.clsv.v8 ).cmpswp< u8>(1, u8 .!(101 + arg)); + Pointer.atField(lclsv.clsv.v16).cmpswp(2, u16.!(102 + arg)); + Pointer.atField(lclsv.clsv.v32).cmpswp(3, u32.!(103 + arg)); + + if (Pointer.SIZE == 8) + Pointer.atField(lclsv.clsv.v64).cmpswp(4, u64.!(104 + arg)); + + // check values after cmpswp, using regular accesses + + diff += + (u32.view(lclsv.clsv.v8 ) ^ u32.!(101 + arg)) + + (u32.view(lclsv.clsv.v16) ^ u32.!(102 + arg)) + + (u32.view(lclsv.clsv.v32) ^ u32.!(103 + arg)); + + if (Pointer.SIZE == 8) + diff += (u32.view(lclsv.clsv.v64) ^ u32.!(104 + arg)); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField19.v3 b/test/pointer/Pointer_atUnboxedField19.v3 new file mode 100644 index 000000000..c24b57542 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField19.v3 @@ -0,0 +1,31 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +var gv = V(1, 2, 3, 4); + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + // store new values, using pointer stores + + Pointer.atField(gv.v8 ).store< u8>(u8 .!(201 + arg)); + Pointer.atField(gv.v16).store(u16.!(202 + arg)); + Pointer.atField(gv.v32).store(u32.!(203 + arg)); + if (Pointer.SIZE == 8) + Pointer.atField(gv.v64).store(u64.!(204 + arg)); + + // check the new values, using regular accesses + + diff += + (u32.view(gv.v8 ) ^ u32.!(201 + arg)) + + (u32.view(gv.v16) ^ u32.!(202 + arg)) + + (u32.view(gv.v32) ^ u32.!(203 + arg)); + + if (Pointer.SIZE == 8) + diff += (u32.view(gv.v64) ^ u32.!(204 + arg)); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField20.v3 b/test/pointer/Pointer_atUnboxedField20.v3 new file mode 100644 index 000000000..8de3d9337 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField20.v3 @@ -0,0 +1,33 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +component Comp { + var compv = V(1, 2, 3, 4); +} + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + // store new values, using pointer stores + + Pointer.atField(Comp.compv.v8 ).store< u8>(u8 .!(201 + arg)); + Pointer.atField(Comp.compv.v16).store(u16.!(202 + arg)); + Pointer.atField(Comp.compv.v32).store(u32.!(203 + arg)); + if (Pointer.SIZE == 8) + Pointer.atField(Comp.compv.v64).store(u64.!(204 + arg)); + + // check the new values, using regular accesses + + diff += + (u32.view(Comp.compv.v8 ) ^ u32.!(201 + arg)) + + (u32.view(Comp.compv.v16) ^ u32.!(202 + arg)) + + (u32.view(Comp.compv.v32) ^ u32.!(203 + arg)); + + if (Pointer.SIZE == 8) + diff += (u32.view(Comp.compv.v64) ^ u32.!(204 + arg)); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField21.v3 b/test/pointer/Pointer_atUnboxedField21.v3 new file mode 100644 index 000000000..d46f010fe --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField21.v3 @@ -0,0 +1,38 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +class C { + var clsv: V; + new(v8: u8, v16: u16, v32: u32, v64: u64) { + clsv = V(v8, v16, v32, v64); + } +} + +var gclsv = C.new(1, 2, 3, 4); + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + // store new values, using pointer stores + + Pointer.atField(gclsv.clsv.v8 ).store< u8>(u8 .!(201 + arg)); + Pointer.atField(gclsv.clsv.v16).store(u16.!(202 + arg)); + Pointer.atField(gclsv.clsv.v32).store(u32.!(203 + arg)); + if (Pointer.SIZE == 8) + Pointer.atField(gclsv.clsv.v64).store(u64.!(204 + arg)); + + // check the new values, using regular accesses + + diff += + (u32.view(gclsv.clsv.v8 ) ^ u32.!(201 + arg)) + + (u32.view(gclsv.clsv.v16) ^ u32.!(202 + arg)) + + (u32.view(gclsv.clsv.v32) ^ u32.!(203 + arg)); + + if (Pointer.SIZE == 8) + diff += (u32.view(gclsv.clsv.v64) ^ u32.!(204 + arg)); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField22.v3 b/test/pointer/Pointer_atUnboxedField22.v3 new file mode 100644 index 000000000..1366cbfaf --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField22.v3 @@ -0,0 +1,40 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +class C { + var clsv: V; + new(v8: u8, v16: u16, v32: u32, v64: u64) { + clsv = V(v8, v16, v32, v64); + } +} + +component Comp { + var compclsv = C.new(1, 2, 3, 4); +} + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + // store new values, using pointer stores + + Pointer.atField(Comp.compclsv.clsv.v8 ).store< u8>(u8 .!(201 + arg)); + Pointer.atField(Comp.compclsv.clsv.v16).store(u16.!(202 + arg)); + Pointer.atField(Comp.compclsv.clsv.v32).store(u32.!(203 + arg)); + if (Pointer.SIZE == 8) + Pointer.atField(Comp.compclsv.clsv.v64).store(u64.!(204 + arg)); + + // check the new values, using regular accesses + + diff += + (u32.view(Comp.compclsv.clsv.v8 ) ^ u32.!(201 + arg)) + + (u32.view(Comp.compclsv.clsv.v16) ^ u32.!(202 + arg)) + + (u32.view(Comp.compclsv.clsv.v32) ^ u32.!(203 + arg)); + + if (Pointer.SIZE == 8) + diff += (u32.view(Comp.compclsv.clsv.v64) ^ u32.!(204 + arg)); + + return diff; +} diff --git a/test/pointer/Pointer_atUnboxedField23.v3 b/test/pointer/Pointer_atUnboxedField23.v3 new file mode 100644 index 000000000..caa59a6af --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField23.v3 @@ -0,0 +1,38 @@ +//@execute 1=0; 6=0 + +// define a variant type that has a few different sizes of unboxed fields +type V(v8: u8, v16: u16, v32: u32, v64: u64) #unboxed { } + +class C { + var clsv: V; + new(v8: u8, v16: u16, v32: u32, v64: u64) { + clsv = V(v8, v16, v32, v64); + } +} + +def main(arg: int) -> u64 { + + var diff: u32 = 0; + + var lclsv = C.new(1, 2, 3, 4); + + // store new values, using pointer stores + + Pointer.atField(lclsv.clsv.v8 ).store< u8>(u8 .!(201 + arg)); + Pointer.atField(lclsv.clsv.v16).store(u16.!(202 + arg)); + Pointer.atField(lclsv.clsv.v32).store(u32.!(203 + arg)); + if (Pointer.SIZE == 8) + Pointer.atField(lclsv.clsv.v64).store(u64.!(204 + arg)); + + // check the new values, using regular accesses + + diff += + (u32.view(lclsv.clsv.v8 ) ^ u32.!(201 + arg)) + + (u32.view(lclsv.clsv.v16) ^ u32.!(202 + arg)) + + (u32.view(lclsv.clsv.v32) ^ u32.!(203 + arg)); + + if (Pointer.SIZE == 8) + diff += (u32.view(lclsv.clsv.v64) ^ u32.!(204 + arg)); + + return diff; +} diff --git a/test/pointer/test.bash b/test/pointer/test.bash index 61f61d0f2..9fca62d83 100755 --- a/test/pointer/test.bash +++ b/test/pointer/test.bash @@ -5,6 +5,8 @@ if [ $# -gt 0 ]; then TEST_LIST="$@" else + do_seman_tests + TEST_LIST=*.v3 fi From 7ef7e65d94084b7e21c9f4f8da37f302cbca475c Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Sat, 7 Dec 2024 14:02:19 -0500 Subject: [PATCH 04/12] Revise tests in a way that should fix wasm failures --- test/pointer/Pointer_atUnboxedField00.v3 | 22 +++++++++++++--------- test/pointer/Pointer_atUnboxedField14.v3 | 8 ++++++++ test/pointer/Pointer_atUnboxedField15.v3 | 8 ++++++++ test/pointer/Pointer_atUnboxedField16.v3 | 8 ++++++++ test/pointer/Pointer_atUnboxedField17.v3 | 8 ++++++++ test/pointer/Pointer_atUnboxedField18.v3 | 8 ++++++++ 6 files changed, 53 insertions(+), 9 deletions(-) diff --git a/test/pointer/Pointer_atUnboxedField00.v3 b/test/pointer/Pointer_atUnboxedField00.v3 index 2432ad337..bf90b6463 100644 --- a/test/pointer/Pointer_atUnboxedField00.v3 +++ b/test/pointer/Pointer_atUnboxedField00.v3 @@ -16,13 +16,17 @@ component Comp { class Cls { var v1cls = V1(7, void, (8, 9), C.new()); var v2cls = V2(400, V1(402, void, (402, 403), C.new()), 406); + new(a: int, b: int, c: int) { + v1cls = V1(a, void, (b, c), null); + v2cls = V2(a+400, V1(a+401, void, (b+401, c+401), null), 405); + } } -var clsglobal = Cls.new(); +var clsglobal = Cls.new(4, 5, 6); def main(arg: int) -> int { - var clslocal = Cls.new(); + var clslocal = Cls.new(10, 11, 12); // tests of cases that should be legal, across types and container locations @@ -32,37 +36,37 @@ def main(arg: int) -> int { var pg1v1class = Pointer.atField(v1global.v1class); var pv1compv1int = Pointer.atField(Comp.v1comp.v1int); - var pv1compv1void = Pointer.atField(Comp.v1comp.v1void); +// var pv1compv1void = Pointer.atField(Comp.v1comp.v1void); var pv1compv1tuple = Pointer.atField(Comp.v1comp.v1tuple); var pv1compv1class = Pointer.atField(Comp.v1comp.v1class); var pv1clsgv1int = Pointer.atField(clsglobal.v1cls.v1int); - var pv1clsgv1void = Pointer.atField(clsglobal.v1cls.v1void); +// var pv1clsgv1void = Pointer.atField(clsglobal.v1cls.v1void); var pv1clsgv1tuple = Pointer.atField(clsglobal.v1cls.v1tuple); var pv1clsgv1class = Pointer.atField(clsglobal.v1cls.v1class); var pv1clslv1int = Pointer.atField(clslocal.v1cls.v1int); - var pv1clslv1void = Pointer.atField(clslocal.v1cls.v1void); +// var pv1clslv1void = Pointer.atField(clslocal.v1cls.v1void); var pv1clslv1tuple = Pointer.atField(clslocal.v1cls.v1tuple); var pv1clslv1class = Pointer.atField(clslocal.v1cls.v1class); var pv2gv22v1int = Pointer.atField(v2global.v22.v1int); - var pv2gv22v1void = Pointer.atField(v2global.v22.v1void); +// var pv2gv22v1void = Pointer.atField(v2global.v22.v1void); var pv2gv22v1tuple = Pointer.atField(v2global.v22.v1tuple); var pv2gv22v1class = Pointer.atField(v2global.v22.v1class); var pv2compv22v1int = Pointer.atField(Comp.v2comp.v22.v1int); - var pv2compv22v1void = Pointer.atField(Comp.v2comp.v22.v1void); +// var pv2compv22v1void = Pointer.atField(Comp.v2comp.v22.v1void); var pv2compv22v1tuple = Pointer.atField(Comp.v2comp.v22.v1tuple); var pv2compv22v1class = Pointer.atField(Comp.v2comp.v22.v1class); var pv2clsgv22v1int = Pointer.atField(clsglobal.v2cls.v22.v1int); - var pv2clsgv22v1void = Pointer.atField(clsglobal.v2cls.v22.v1void); +// var pv2clsgv22v1void = Pointer.atField(clsglobal.v2cls.v22.v1void); var pv2clsgv22v1tuple = Pointer.atField(clsglobal.v2cls.v22.v1tuple); var pv2clsgv22v1class = Pointer.atField(clsglobal.v2cls.v22.v1class); var pv2clslv22v1int = Pointer.atField(clslocal.v2cls.v22.v1int); - var pv2clslv22v1void = Pointer.atField(clslocal.v2cls.v22.v1void); +// var pv2clslv22v1void = Pointer.atField(clslocal.v2cls.v22.v1void); var pv2clslv22v1tuple = Pointer.atField(clslocal.v2cls.v22.v1tuple); var pv2clslv22v1class = Pointer.atField(clslocal.v2cls.v22.v1class); diff --git a/test/pointer/Pointer_atUnboxedField14.v3 b/test/pointer/Pointer_atUnboxedField14.v3 index c88abdea0..a999dacb6 100644 --- a/test/pointer/Pointer_atUnboxedField14.v3 +++ b/test/pointer/Pointer_atUnboxedField14.v3 @@ -28,5 +28,13 @@ def main(arg: int) -> u64 { if (Pointer.SIZE == 8) diff += (u32.view(gv.v64) ^ u32.!(104 + arg)); + // reset values for succeeding runs + Pointer.atField(gv.v8 ).store< u8>(1); + Pointer.atField(gv.v16).store(2); + Pointer.atField(gv.v32).store(3); + + if (Pointer.SIZE == 8) + Pointer.atField(gv.v64).store(4); + return diff; } diff --git a/test/pointer/Pointer_atUnboxedField15.v3 b/test/pointer/Pointer_atUnboxedField15.v3 index 6a1044768..d1aaf0db0 100644 --- a/test/pointer/Pointer_atUnboxedField15.v3 +++ b/test/pointer/Pointer_atUnboxedField15.v3 @@ -30,5 +30,13 @@ def main(arg: int) -> u64 { if (Pointer.SIZE == 8) diff += (u32.view(Comp.compv.v64) ^ u32.!(104 + arg)); + // reset values for succeeding runs + Pointer.atField(Comp.compv.v8 ).store< u8>(1); + Pointer.atField(Comp.compv.v16).store(2); + Pointer.atField(Comp.compv.v32).store(3); + + if (Pointer.SIZE == 8) + Pointer.atField(Comp.compv.v64).store(4); + return diff; } diff --git a/test/pointer/Pointer_atUnboxedField16.v3 b/test/pointer/Pointer_atUnboxedField16.v3 index b59534539..ec91c3bde 100644 --- a/test/pointer/Pointer_atUnboxedField16.v3 +++ b/test/pointer/Pointer_atUnboxedField16.v3 @@ -35,5 +35,13 @@ def main(arg: int) -> u64 { if (Pointer.SIZE == 8) diff += (u32.view(gclsv.clsv.v64) ^ u32.!(104 + arg)); + // reset values for succeeding runs + Pointer.atField(gclsv.clsv.v8 ).store< u8>(1); + Pointer.atField(gclsv.clsv.v16).store(2); + Pointer.atField(gclsv.clsv.v32).store(3); + + if (Pointer.SIZE == 8) + Pointer.atField(gclsv.clsv.v64).store(4); + return diff; } diff --git a/test/pointer/Pointer_atUnboxedField17.v3 b/test/pointer/Pointer_atUnboxedField17.v3 index 3d5c25520..94710b1bf 100644 --- a/test/pointer/Pointer_atUnboxedField17.v3 +++ b/test/pointer/Pointer_atUnboxedField17.v3 @@ -37,5 +37,13 @@ def main(arg: int) -> u64 { if (Pointer.SIZE == 8) diff += (u32.view(Comp.compclsv.clsv.v64) ^ u32.!(104 + arg)); + // reset values for succeeding runs + Pointer.atField(Comp.compclsv.clsv.v8 ).store< u8>(1); + Pointer.atField(Comp.compclsv.clsv.v16).store(2); + Pointer.atField(Comp.compclsv.clsv.v32).store(3); + + if (Pointer.SIZE == 8) + Pointer.atField(Comp.compclsv.clsv.v64).store(4); + return diff; } diff --git a/test/pointer/Pointer_atUnboxedField18.v3 b/test/pointer/Pointer_atUnboxedField18.v3 index 622384bf7..0f1237fdb 100644 --- a/test/pointer/Pointer_atUnboxedField18.v3 +++ b/test/pointer/Pointer_atUnboxedField18.v3 @@ -35,5 +35,13 @@ def main(arg: int) -> u64 { if (Pointer.SIZE == 8) diff += (u32.view(lclsv.clsv.v64) ^ u32.!(104 + arg)); + // reset values for succeeding runs + Pointer.atField(lclsv.clsv.v8 ).store< u8>(1); + Pointer.atField(lclsv.clsv.v16).store(2); + Pointer.atField(lclsv.clsv.v32).store(3); + + if (Pointer.SIZE == 8) + Pointer.atField(lclsv.clsv.v64).store(4); + return diff; } From 1c0026005d8e3b38b9b2f278654fc28bfb5fef9d Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Mon, 9 Dec 2024 16:44:17 -0500 Subject: [PATCH 05/12] Remove ovverride of render method - other thigns depended on the original output format --- aeneas/src/vst/Vst.v3 | 5 ----- 1 file changed, 5 deletions(-) diff --git a/aeneas/src/vst/Vst.v3 b/aeneas/src/vst/Vst.v3 index 8c9cafd6f..998a5933d 100644 --- a/aeneas/src/vst/Vst.v3 +++ b/aeneas/src/vst/Vst.v3 @@ -321,11 +321,6 @@ class VstMember extends Decl { if (receiver != null && !receiver.isFileScope) buf.puts(receiver.fullName).putc('.'); return buf.puts(token.image); } - def render(buf: StringBuilder) -> StringBuilder { - buf.put1("%s", token.image); - if (index >= 0) buf.put1(".%d", index); - return buf; - } } // Desugared case member from a variant. class VstCaseMember extends VstMember { From ad92b89dfaa742250e1cc743699f7befe61f1805 Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Mon, 9 Dec 2024 16:44:17 -0500 Subject: [PATCH 06/12] Remove override of render method - other things depended on the original output format --- aeneas/src/vst/Vst.v3 | 5 ----- 1 file changed, 5 deletions(-) diff --git a/aeneas/src/vst/Vst.v3 b/aeneas/src/vst/Vst.v3 index 8c9cafd6f..998a5933d 100644 --- a/aeneas/src/vst/Vst.v3 +++ b/aeneas/src/vst/Vst.v3 @@ -321,11 +321,6 @@ class VstMember extends Decl { if (receiver != null && !receiver.isFileScope) buf.puts(receiver.fullName).putc('.'); return buf.puts(token.image); } - def render(buf: StringBuilder) -> StringBuilder { - buf.put1("%s", token.image); - if (index >= 0) buf.put1(".%d", index); - return buf; - } } // Desugared case member from a variant. class VstCaseMember extends VstMember { From 005ae9016072c2738c4b0d11a652346aa2553a84 Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Tue, 28 Jan 2025 20:29:23 +1100 Subject: [PATCH 07/12] This patch fixes a flaw that showed up when handling a pointer to and unboxed field whose type is a type parameter that has been instantiated with an unboxed variant type. --- aeneas/src/ir/Reachability.v3 | 9 +++----- aeneas/src/ir/SsaNormalizer.v3 | 19 +++++++++-------- test/pointer/Pointer_atUnboxedField24.v3 | 26 ++++++++++++++++++++++++ test/pointer/Pointer_atUnboxedField25.v3 | 25 +++++++++++++++++++++++ 4 files changed, 64 insertions(+), 15 deletions(-) create mode 100644 test/pointer/Pointer_atUnboxedField24.v3 create mode 100644 test/pointer/Pointer_atUnboxedField25.v3 diff --git a/aeneas/src/ir/Reachability.v3 b/aeneas/src/ir/Reachability.v3 index ae5d323ea..a3162ca5e 100644 --- a/aeneas/src/ir/Reachability.v3 +++ b/aeneas/src/ir/Reachability.v3 @@ -423,12 +423,9 @@ class ReachabilityAnalyzer(compilation: Compilation) { rf.setFact(RaFact.RF_VAL_MANY); } def getAndSetUnboxedField(op: SsaApplyOp, specs: List, context: IrSpec) { - var rf = makeField(op, specs.head.asField(), context); - getAndSetField(rf); - while (specs.tail != null) { - specs = specs.tail; - var spec = specs.head; - rf = makeField2(makeClass(rf.fieldType), spec.asField()); + for (l = specs; l != null; l = l.tail) { + var spec = l.head; + var rf = makeField(op, spec.asField(), context); getAndSetField(rf); } } diff --git a/aeneas/src/ir/SsaNormalizer.v3 b/aeneas/src/ir/SsaNormalizer.v3 index bc84759e7..39d61202c 100644 --- a/aeneas/src/ir/SsaNormalizer.v3 +++ b/aeneas/src/ir/SsaNormalizer.v3 @@ -1336,23 +1336,24 @@ class SsaRaNormalizer extends SsaRebuilder { // Need to start with outermost field access to get initial set of fields var raField = extractFieldRef(i_old, specs.head.asField()); var rc = norm.ra.getClass(raField.receiver); - var ff: Range = rc.liveFields; + var nf = raField.normOf(rc.liveFields); + normType(raField.receiver); // XXX: normType() side-effect of flattening // now, iterate winnowing down the fields - while (specs != null && ff.length != 0) { - var spec = specs.head; - specs = specs.tail; - rc = norm.ra.getClass(spec.asField().receiver); + for (elt = specs.tail; elt != null && nf.length != 0; elt = elt.tail) { + var spec = elt.head; + var rcvr = spec.asField().receiver; + var rc = norm.ra.getClass(rcvr); raField = norm.ra.makeField2(rc, spec.asField()); - ff = raField.normOf(ff); + nf = raField.normOf(nf); } // now should have a single field that we can do Pointer.atField - if (ff.length > 0) { + if (nf.length > 0) { var op_out: Operator; match (boxKind) { OBJECT => - op_out = V3Op.newPtrAtObjectField(ff[0], i_old.op.sig.returnType()); + op_out = V3Op.newPtrAtObjectField(nf[0], i_old.op.sig.returnType()); COMPONENT => - op_out = V3Op.newPtrAtComponentField(ff[0], i_old.op.sig.returnType()); + op_out = V3Op.newPtrAtComponentField(nf[0], i_old.op.sig.returnType()); } var new_inputs = if(ai_new.length == 0, Ssa.NO_INSTRS, [ai_new[0]]); var i_new = curBlock.addApply(curBlock.source, op_out, new_inputs); diff --git a/test/pointer/Pointer_atUnboxedField24.v3 b/test/pointer/Pointer_atUnboxedField24.v3 new file mode 100644 index 000000000..e90213902 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField24.v3 @@ -0,0 +1,26 @@ +//@execute 0=0; 6=6 +// tests possible issues related to unboxed field and parameterized types + +type A(i: int) #unboxed { } +type B(a: A, b: u32) #unboxed { } + +class P { } +class Q extends P { } +class R extends Q { + var x: T; + def y: int; + new(x, y) { } +} +class S extends R { + def getPtr() -> Pointer { + return Pointer.atField(x); + } + new(x: B, y: int) super(x, y) { } +} + +def main(n: int) -> int { + var s = S.new(B(A(n), 17), 5); + var p = s.getPtr(); + // make sure field is not optimized away - though that should be another test + return p.load() + s.x.a.i - n; +} diff --git a/test/pointer/Pointer_atUnboxedField25.v3 b/test/pointer/Pointer_atUnboxedField25.v3 new file mode 100644 index 000000000..a7391c201 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField25.v3 @@ -0,0 +1,25 @@ +//@execute 0=0; 6=6 +// tests possible issues related to unboxed field and parameterized types + +type A(i: int) #unboxed { } +type B(a: A, b: u32) #unboxed { } + +class P { } +class Q extends P { } +class R extends Q { + var x: T; + def y: int; + new(x, y) { } +} +class S extends R { + def getPtr() -> Pointer { + return Pointer.atField(x.a.i); + } + new(x: B, y: int) super(x, y) { } +} + +def main(n: int) -> int { + var s = S.new(B(A(n), 17), 5); + var p = s.getPtr(); + return p.load(); +} From 3c155bef13ee14ef1cf5259173c873dccbb92eca Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Thu, 30 Jan 2025 11:27:06 +1100 Subject: [PATCH 08/12] Follow Ben's suggested improvements --- aeneas/src/core/Opcode.v3 | 2 +- aeneas/src/core/Operator.v3 | 9 +++------ aeneas/src/ir/SsaNormalizer.v3 | 2 -- aeneas/src/ir/VstIr.v3 | 10 ++-------- aeneas/src/ssa/SsaOptimizer.v3 | 5 +---- 5 files changed, 7 insertions(+), 21 deletions(-) diff --git a/aeneas/src/core/Opcode.v3 b/aeneas/src/core/Opcode.v3 index 9ce59dc85..fbfd2534b 100644 --- a/aeneas/src/core/Opcode.v3 +++ b/aeneas/src/core/Opcode.v3 @@ -326,7 +326,7 @@ component Opcodes { t[Opcode.PtrAtRangeElem.tag] = F; t[Opcode.PtrAtArrayElem.tag] = F; t[Opcode.PtrAtComponentField.tag] = P; - t[Opcode.PtrAtObjectField.tag] = P; // EBM: was F + t[Opcode.PtrAtObjectField.tag] = P; t[Opcode.PtrAtUnboxedObjectField.tag] = P; t[Opcode.PtrAtUnboxedComponentField.tag] = P; t[Opcode.PtrLoad.tag] = NONE; diff --git a/aeneas/src/core/Operator.v3 b/aeneas/src/core/Operator.v3 index 3c751e8d8..29a4c6a14 100644 --- a/aeneas/src/core/Operator.v3 +++ b/aeneas/src/core/Operator.v3 @@ -593,13 +593,10 @@ component V3Op { def renderList(sb: StringBuilder, lst: List, rfunc: (T, StringBuilder) -> StringBuilder, sep: string) -> StringBuilder { - while (true) { - sb = rfunc(lst.head, sb); - lst = lst.tail; - if (lst == null) return sb; - sb = sb.puts(sep); + for (node = lst; node != null; node = node.tail) { + sb = rfunc(node.head, sb).puts(if(node.tail == null, "", sep)); } - return sb; // keep compiler happy + return sb; } def renderOp(op: Operator, buf: StringBuilder) -> StringBuilder { diff --git a/aeneas/src/ir/SsaNormalizer.v3 b/aeneas/src/ir/SsaNormalizer.v3 index 39d61202c..e1627e1a9 100644 --- a/aeneas/src/ir/SsaNormalizer.v3 +++ b/aeneas/src/ir/SsaNormalizer.v3 @@ -1328,8 +1328,6 @@ class SsaRaNormalizer extends SsaRebuilder { var ai_new = genRefs(i_old.inputs); if (i_old.useList == null) { // OPT: remove unused pointer construction - // TODO: EBM determine if addNullCheck is a good idea - // if (!isVariant) addNullCheck(i_old, ai_new[0]); var result = SsaInstr.!(newGraph.intConst(0)); return map1(i_old, result); } diff --git a/aeneas/src/ir/VstIr.v3 b/aeneas/src/ir/VstIr.v3 index 479ebb3a6..57caea395 100644 --- a/aeneas/src/ir/VstIr.v3 +++ b/aeneas/src/ir/VstIr.v3 @@ -76,7 +76,7 @@ class IrBuilder(ctype: Type, parent: IrClass) { def addVstField(f: VstField, isVariant: bool, unboxed: bool) { var ir = IrField.new(ctype, f.getType()); ir.source = f; - if (f.writability == Writability.READ_ONLY && !(isVariant && unboxed)) + if (f.writability == Writability.READ_ONLY && !f.pointedAt) ir.setFact(Fact.F_VALUE | Fact.O_FOLDABLE); if (isVariant && !unboxed) ir.setFact(Fact.O_PURE); @@ -93,13 +93,7 @@ class IrBuilder(ctype: Type, parent: IrClass) { var ir = IrMethod.new(ctype, null, sig); ir.source = m; ir.facts |= Fact.M_NEW; - if (isVariant) { - if (unboxed) { - ir.setFact(Fact.M_INLINE | Fact.V_NON_ZERO); - } else { - ir.setFact(Fact.O_PURE | Fact.M_INLINE | Fact.V_NON_ZERO); - } - } + if (isVariant) ir.setFact(Fact.O_PURE | Fact.M_INLINE | Fact.V_NON_ZERO); setIrMethod(0, ir); } } diff --git a/aeneas/src/ssa/SsaOptimizer.v3 b/aeneas/src/ssa/SsaOptimizer.v3 index 8118c424e..552654689 100644 --- a/aeneas/src/ssa/SsaOptimizer.v3 +++ b/aeneas/src/ssa/SsaOptimizer.v3 @@ -844,10 +844,7 @@ class SsaInstrReducer(context: SsaContext) extends SsaInstrMatcher { return graph.valConst(getFieldType(i.op, field), asRecord(receiver).values[field.index]); } - i.setFactIf(Fact.O_NO_NULL_CHECK, Fact.O_PURE); - if (!unboxed) { - i.facts |= Fact.F_VALUE; - } + if (!i.facts.F_POINTED_AT) i.facts |= Fact.F_VALUE; if (optimize_loads) return state.load(receiver, field, i); } VariantGetMethod(method) => { From b868f654b90c0df2defb45a295062d68d866382a Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Wed, 5 Feb 2025 16:51:28 +1100 Subject: [PATCH 09/12] Refined and cleaned up Pointer.atField to unboxed fields, tests and test description files --- aeneas/src/ir/Reachability.v3 | 53 ++++++++++++++++--- aeneas/src/ir/SsaNormalizer.v3 | 52 +++++++++++------- test/pointer/Pointer_atUnboxedField24.v3 | 5 +- test/pointer/Pointer_atUnboxedField25.v3 | 19 +++---- test/pointer/Pointer_atUnboxedField26.v3 | 17 ++++++ test/pointer/Pointer_atUnboxedField27.v3 | 17 ++++++ .../seman/Pointer_atFieldUnboxedBad00.v3 | 2 +- .../seman/Pointer_atFieldUnboxedBad05.v3 | 3 +- .../seman/Pointer_atFieldUnboxedBad11.v3 | 26 +++++++++ .../seman/Pointer_atFieldUnboxedBad12.v3 | 29 ++++++++++ .../seman/Pointer_atFieldUnboxedBad13.v3 | 35 ++++++++++++ .../seman/Pointer_atFieldUnboxedBad14.v3 | 35 ++++++++++++ .../seman/Pointer_atFieldUnboxedBad15.v3 | 40 ++++++++++++++ .../seman/Pointer_atFieldUnboxedBad16.v3 | 40 ++++++++++++++ .../seman/Pointer_atFieldUnboxedBad17.v3 | 23 ++++++++ .../seman/Pointer_atFieldUnboxedBad18.v3 | 22 ++++++++ .../seman/Pointer_atFieldUnboxedBad19.v3 | 17 ++++++ .../seman/Pointer_atFieldUnboxedBad20.v3 | 13 +++++ .../seman/Pointer_atFieldUnboxedBad21.v3 | 13 +++++ .../seman/Pointer_atFieldUnboxedBad22.v3 | 17 ++++++ test/pointer/seman/descs.txt | 23 ++++++++ test/pointer/unboxed-pointer-at-descs.txt | 30 +++++++++++ 22 files changed, 488 insertions(+), 43 deletions(-) create mode 100644 test/pointer/Pointer_atUnboxedField26.v3 create mode 100644 test/pointer/Pointer_atUnboxedField27.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad11.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad12.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad13.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad14.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad15.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad16.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad17.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad18.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad19.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad20.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad21.v3 create mode 100644 test/pointer/seman/Pointer_atFieldUnboxedBad22.v3 create mode 100644 test/pointer/seman/descs.txt create mode 100644 test/pointer/unboxed-pointer-at-descs.txt diff --git a/aeneas/src/ir/Reachability.v3 b/aeneas/src/ir/Reachability.v3 index a3162ca5e..d5001f2c3 100644 --- a/aeneas/src/ir/Reachability.v3 +++ b/aeneas/src/ir/Reachability.v3 @@ -329,10 +329,10 @@ class ReachabilityAnalyzer(compilation: Compilation) { CallClassMethod(method) => getMethod(op, opMethod(op, method, context)); CallClassVirtual(method) => getVirtual(opMethod(op, method, context)); CallVariantVirtual(method) => getVirtual(opMethod(op, method, context)); - PtrAtObjectField(field) => if (op.useList != null) getAndSetField(makeField(op, field, context)); - PtrAtComponentField(field) => if (op.useList != null) getAndSetField(makeField(op, field, context)); - PtrAtUnboxedObjectField(specs) => if (op.useList != null) getAndSetUnboxedField(op, specs, context); - PtrAtUnboxedComponentField(specs) => if (op.useList != null) getAndSetUnboxedField(op, specs, context); + PtrAtObjectField(field) => if (op.useList != null) getPointerAtField(makeField(op, field, context), op, true); + PtrAtComponentField(field) => if (op.useList != null) getPointerAtField(makeField(op, field, context), op, true); + PtrAtUnboxedObjectField(specs) => if (op.useList != null) getPointerAtUnboxedField(op, specs, context); + PtrAtUnboxedComponentField(specs) => if (op.useList != null) getPointerAtUnboxedField(op, specs, context); _ => ; } } @@ -415,18 +415,42 @@ class ReachabilityAnalyzer(compilation: Compilation) { getEquality(if(rf.fieldType == null, f.fieldType, rf.fieldType), null); } } - def getAndSetField(rf: RaField) { + def pointedAtTypeLegal(t: Type) -> bool { + if (PrimType.?(t) && !VoidType.?(t)) return true; + if (ClassType.?(t) && !ClassType.!(t).classDecl.isUnboxed()) return true; + if (PointerType.?(t)) return true; + // add other types here, e.g., Tuple (eventually), and fix error message in getPointerAtField + return false; + } + def getPointerAtField(rf: RaField, op: SsaApplyOp, checkType: bool) { + if (checkType) { + var ft = rf.fieldType; + if (ft == null) ft = rf.orig.fieldType; + if (!pointedAtTypeLegal(ft)) { + var ERROR = compilation.prog.ERROR; + var msg = Strings.format1("field must be of non-void primitive type or boxed class type (type is %q)", ft.render); + ERROR.addError(op.source.range, null, "TypeError", msg); + } + } + // Treat the field as both read and written, with unknown values getField(rf); rf.raFacts |= RaFact.RF_WRITTEN; var none: Fact.set; rf.writeFacts = none; rf.setFact(RaFact.RF_VAL_MANY); } - def getAndSetUnboxedField(op: SsaApplyOp, specs: List, context: IrSpec) { + def getPointerAtUnboxedField(op: SsaApplyOp, specs: List, context: IrSpec) { + var prevType: Type; + prevType = if(specs != null, specs.head.receiver); for (l = specs; l != null; l = l.tail) { var spec = l.head; - var rf = makeField(op, spec.asField(), context); - getAndSetField(rf); + var fld = spec.asField(); + var rf = makeField3(prevType, fld, context); + prevType = rf.fieldType; + if (prevType == null) prevType = rf.orig.fieldType; + // check only the last type in the range, since the intermediates + // are not pointed at + getPointerAtField(rf, op, l.tail == null); } } def setField(op: SsaApplyOp, rf: RaField) { @@ -476,6 +500,16 @@ class ReachabilityAnalyzer(compilation: Compilation) { // make a polymorphic field return makePolyField(raType, f); } + def makeField3(rcvrType: Type, f: IrField, context: IrSpec) -> RaField { + var rf = f.raField; + if (rf != null) return rf; + // try to make a simple field first + var receiver = f.receiver; + if (!receiver.open()) return makeSimpleField(makeClass(receiver), f); + // get polymorphic receiver type from operator + receiver = mono(rcvrType, context); + return makePolyField(makeClass(receiver), f); + } def makePolyField(raType: RaClass, f: IrField) -> RaField { var rf = raType.fields[f.index]; if (rf == null) { @@ -727,6 +761,9 @@ class RaField(receiver: Type, orig: IrField, fieldType: Type) extends RaItem { var rc = ra.getClass(receiver); return normOf(rc.liveFields); } + def getClosedType() -> Type { + return if(fieldType == null, orig.fieldType, fieldType); + } } // Information about a method, including any specialization, whether it is reusable // across normalization, etc. diff --git a/aeneas/src/ir/SsaNormalizer.v3 b/aeneas/src/ir/SsaNormalizer.v3 index e1627e1a9..5f2541cc1 100644 --- a/aeneas/src/ir/SsaNormalizer.v3 +++ b/aeneas/src/ir/SsaNormalizer.v3 @@ -534,6 +534,13 @@ class SsaRaNormalizer extends SsaRebuilder { var receiver = genRef1(i_old.inputs[0]); var raField = extractFieldRef(i_old, field); var nf = raField.liveFields(norm.ra); + if (nf.length == 0) { + // can happen because of user's code, so not a compiler failure; + // checked in Reachability + var result = SsaInstr.!(newGraph.intConst(0)); + return map1(i_old, result); + } + var ft = nf[0].asField().fieldType; var i_new = curBlock.addApply(curBlock.source, V3Op.newPtrAtObjectField(nf[0], i_old.op.sig.returnType()), [receiver]); map1(i_old, i_new); @@ -541,6 +548,13 @@ class SsaRaNormalizer extends SsaRebuilder { PtrAtComponentField(field) => { var raField = extractFieldRef(i_old, field); var nf = raField.liveFields(norm.ra); + if (nf.length == 0) { + // can happen because of user's code, so not a compiler failure; + // checked in Reachability + var result = SsaInstr.!(newGraph.intConst(0)); + return map1(i_old, result); + } + var ft = nf[0].asField().fieldType; var i_new = curBlock.addApply(curBlock.source, V3Op.newPtrAtComponentField(nf[0], i_old.op.sig.returnType()), Ssa.NO_INSTRS); map1(i_old, i_new); @@ -1336,31 +1350,33 @@ class SsaRaNormalizer extends SsaRebuilder { var rc = norm.ra.getClass(raField.receiver); var nf = raField.normOf(rc.liveFields); normType(raField.receiver); // XXX: normType() side-effect of flattening + var prevType = raField.getClosedType(); // now, iterate winnowing down the fields for (elt = specs.tail; elt != null && nf.length != 0; elt = elt.tail) { var spec = elt.head; - var rcvr = spec.asField().receiver; - var rc = norm.ra.getClass(rcvr); - raField = norm.ra.makeField2(rc, spec.asField()); + var ctxt = if (specSet != null, specSet.first(), context.spec); + raField = norm.ra.makeField3(prevType, spec.asField(), ctxt); + prevType = raField.getClosedType();; nf = raField.normOf(nf); } // now should have a single field that we can do Pointer.atField - if (nf.length > 0) { - var op_out: Operator; - match (boxKind) { - OBJECT => - op_out = V3Op.newPtrAtObjectField(nf[0], i_old.op.sig.returnType()); - COMPONENT => - op_out = V3Op.newPtrAtComponentField(nf[0], i_old.op.sig.returnType()); - } - var new_inputs = if(ai_new.length == 0, Ssa.NO_INSTRS, [ai_new[0]]); - var i_new = curBlock.addApply(curBlock.source, op_out, new_inputs); - return map1(i_old, i_new); - } else { - // Not sure what to do if no fields remain ... + if (nf.length == 0) { + // can happen because of user's code, so not a compiler failure; + // checked in Reachability + var result = SsaInstr.!(newGraph.intConst(0)); + return map1(i_old, result); } - var result = SsaInstr.!(newGraph.intConst(0)); - return map1(i_old, result); + var ft = nf[0].asField().fieldType; + var op_out: Operator; + match (boxKind) { + OBJECT => + op_out = V3Op.newPtrAtObjectField(nf[0], i_old.op.sig.returnType()); + COMPONENT => + op_out = V3Op.newPtrAtComponentField(nf[0], i_old.op.sig.returnType()); + } + var new_inputs = if(ai_new.length == 0, Ssa.NO_INSTRS, [ai_new[0]]); + var i_new = curBlock.addApply(curBlock.source, op_out, new_inputs); + return map1(i_old, i_new); } def normClassSetField(i_old: SsaApplyOp, field: IrField, op: Operator, init: bool) { // XXX: propagate O_NO_NULL_CHECK diff --git a/test/pointer/Pointer_atUnboxedField24.v3 b/test/pointer/Pointer_atUnboxedField24.v3 index e90213902..a7391c201 100644 --- a/test/pointer/Pointer_atUnboxedField24.v3 +++ b/test/pointer/Pointer_atUnboxedField24.v3 @@ -13,7 +13,7 @@ class R extends Q { } class S extends R { def getPtr() -> Pointer { - return Pointer.atField(x); + return Pointer.atField(x.a.i); } new(x: B, y: int) super(x, y) { } } @@ -21,6 +21,5 @@ class S extends R { def main(n: int) -> int { var s = S.new(B(A(n), 17), 5); var p = s.getPtr(); - // make sure field is not optimized away - though that should be another test - return p.load() + s.x.a.i - n; + return p.load(); } diff --git a/test/pointer/Pointer_atUnboxedField25.v3 b/test/pointer/Pointer_atUnboxedField25.v3 index a7391c201..c4d2dfbc1 100644 --- a/test/pointer/Pointer_atUnboxedField25.v3 +++ b/test/pointer/Pointer_atUnboxedField25.v3 @@ -1,25 +1,22 @@ -//@execute 0=0; 6=6 +//@execute 0=0; 12=12 // tests possible issues related to unboxed field and parameterized types -type A(i: int) #unboxed { } -type B(a: A, b: u32) #unboxed { } +type A(i: T) #unboxed { } +type B(a: A, b: u32) #unboxed { } class P { } class Q extends P { } class R extends Q { - var x: T; + var x: B; def y: int; new(x, y) { } -} -class S extends R { - def getPtr() -> Pointer { + def getPtrx() -> Pointer { + // legality depends on what T is return Pointer.atField(x.a.i); } - new(x: B, y: int) super(x, y) { } } - def main(n: int) -> int { - var s = S.new(B(A(n), 17), 5); - var p = s.getPtr(); + var r = R.new(B(A(n), 13), 15); + var p = r.getPtrx(); return p.load(); } diff --git a/test/pointer/Pointer_atUnboxedField26.v3 b/test/pointer/Pointer_atUnboxedField26.v3 new file mode 100644 index 000000000..00f12f262 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField26.v3 @@ -0,0 +1,17 @@ +//@execute 6=6; 10=10 +// tests possible issues related to unboxed field and parameterized types + +class C #unboxed { + var x: int; + new(x) { } +} +class D { + var c: C; + new(x: int) { c = C.new(x); } +} + +def main(n: int) -> int { + var d = D.new(n); + var p = Pointer.atField(d.c.x); + return p.load() + d.c.x - n; +} diff --git a/test/pointer/Pointer_atUnboxedField27.v3 b/test/pointer/Pointer_atUnboxedField27.v3 new file mode 100644 index 000000000..e0cab99b5 --- /dev/null +++ b/test/pointer/Pointer_atUnboxedField27.v3 @@ -0,0 +1,17 @@ +//@execute 6=6; 10=10 +// tests possible issues related to unboxed field and parameterized types + +class C #unboxed { + var x: int; + new(x) { } +} + +component CC { + var c: C; +} + +def main(n: int) -> int { + CC.c = C.new(n); + var p = Pointer.atField(CC.c.x); + return p.load() + CC.c.x - n; +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad00.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad00.v3 index 50d0c5953..db50a83d5 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad00.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad00.v3 @@ -1,5 +1,5 @@ //@seman = TypeError @ 7:33 -// Disallow pointer to *boxed* field of a global variable +// Disallow pointer to *boxed* field of a local variable type V(x: int); def main() -> int { diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad05.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad05.v3 index f446c2fe9..9d8bb3ac9 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad05.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad05.v3 @@ -2,11 +2,10 @@ // Disallow pointer to *packed* field of a local variable type V(x: u4, y: u4) #packing 0b_xxxxyyyy; -type V(x: int); class C { var v: V; new(i: int) { - v = V(i, 3); + v = V(u4.view(i), 3); } } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad11.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad11.v3 new file mode 100644 index 000000000..8680009e8 --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad11.v3 @@ -0,0 +1,26 @@ +//@seman = TypeError @ 16:24 +// tests possible issues related to unboxed field and parameterized types + +type A(i: int) #unboxed { } +type B(a: A, b: u32) #unboxed { } + +class P { } +class Q extends P { } +class R extends Q { + var x: T; + def y: int; + new(x, y) { } +} +class S extends R { + def getPtr() -> Pointer { + return Pointer.atField(x); + } + new(x: B, y: int) super(x, y) { } +} + +def main(n: int) -> int { + var s = S.new(B(A(n), 17), 5); + var p = s.getPtr(); + // make sure field is not optimized away - though that should be another test + return p.load() + s.x.a.i - n; +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad12.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad12.v3 new file mode 100644 index 000000000..fd8ca6400 --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad12.v3 @@ -0,0 +1,29 @@ +//@seman=TypeError @ 15:24 +// tests possible issues related to unboxed field and parameterized types + +type A(i: int) #unboxed { } +type B(a: A, b: u32) #unboxed { } + +class P { } +class Q extends P { } +class R extends Q { + var x: T; + def y: int; + new(x, y) { } + def getPtrx() -> Pointer { + // legality depends on what T is + return Pointer.atField(x); + } +} +class S extends R { + def getPtr() -> Pointer { + return Pointer.atField(x.a.i); + } + new(x: B, y: int) super(x, y) { } +} + +def main(n: int) -> int { + var r = R.new(A(n), 15); + var p = r.getPtrx(); + return p.load(); +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad13.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad13.v3 new file mode 100644 index 000000000..ac4e190bc --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad13.v3 @@ -0,0 +1,35 @@ +//@seman=TypeError @ 33:17 +// tests possible issues related to unboxed field and parameterized types + +type A(i: int, j: int) #unboxed { } +type B(a: A, b: u32) #unboxed { } + +class P { } +class Q extends P { } +class R extends Q { + var x: T; + var y: int; + new(x, y) { } + def getPtrx() -> Pointer { + // legality depends on what T is + return Pointer.atField(x); + } +} +class S extends R { + def getPtr() -> Pointer { + return Pointer.atField(x.a.i); + } + new(x: B, y: int) super(x, y) { } +} + +def main(n: int) -> int { + var r = R.new(B(A(n, n+1), u32.view(n+2)), n+3); + // keep all the fields around + var pi = Pointer.atField(r.x.a.i); + var pj = Pointer.atField(r.x.a.j); + var pb = Pointer.atField(r.x.b); + var py = Pointer.atField(r.y); + // case actually being tested + var p = Pointer.atField(r.x); + return p.load() + pi.load() + pj.load() + pb.load() + py.load() - (4 * n + 6); +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad14.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad14.v3 new file mode 100644 index 000000000..b0bc9b119 --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad14.v3 @@ -0,0 +1,35 @@ +//@seman=TypeError @ 33:17 +// tests possible issues related to unboxed field and parameterized types + +type A(i: int, j: int) #unboxed { } +type B(a: A, b: u32) #unboxed { } + +class P { } +class Q extends P { } +class R extends Q { + var x: T; + var y: int; + new(x, y) { } + def getPtrx() -> Pointer { + // legality depends on what T is + return Pointer.atField(x); + } +} +class S extends R { + def getPtr() -> Pointer { + return Pointer.atField(x.a.i); + } + new(x: B, y: int) super(x, y) { } +} + +def main(n: int) -> int { + var r = R.new(B(A(n, n+1), u32.view(n+2)), n+3); + // keep all the fields around + var pi = Pointer.atField(r.x.a.i); + var pj = Pointer.atField(r.x.a.j); + var pb = Pointer.atField(r.x.b); + var py = Pointer.atField(r.y); + // case actually being tested + var p = Pointer.atField(r.x.a); + return p.load() + pi.load() + pj.load() + pb.load() + py.load() - (4 * n + 6); +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad15.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad15.v3 new file mode 100644 index 000000000..5c063ae6d --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad15.v3 @@ -0,0 +1,40 @@ +//@seman=TypeError @ 38:17 +// tests possible issues related to unboxed field and parameterized types + +type A(i: int, j: int) #unboxed { } +type B(a: A, b: u32) #unboxed { } +type C(c: int) #unboxed { } +type U(d: int) { } + +class P { } +class Q extends P { } +class R extends Q { + var x: T; + var y: int; + new(x, y) { } + def getPtrx() -> Pointer { + // legality depends on what T is + return Pointer.atField(x); + } +} +class S extends R { + def getPtr() -> Pointer { + return Pointer.atField(x.a.i); + } + new(x: B, y: int) super(x, y) { } +} + +component Comp { + var cb: B; +} + +def main(n: int) -> int { + Comp.cb = B(A(n, n+1), u32.view(n+2)); + // keep all the fields around + var pi = Pointer.atField(Comp.cb.a.i); + var pj = Pointer.atField(Comp.cb.a.j); + var pb = Pointer.atField(Comp.cb.b); + // case actually being tested + var p = Pointer.atField(Comp.cb); + return p.load() + pi.load() + pj.load() + pb.load() - (3 * n + 3); +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad16.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad16.v3 new file mode 100644 index 000000000..8bdcd0e0f --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad16.v3 @@ -0,0 +1,40 @@ +//@seman=TypeError @ 38:17 +// tests possible issues related to unboxed field and parameterized types + +type A(i: int, j: int) #unboxed { } +type B(a: A, b: u32) #unboxed { } +type C(c: int) #unboxed { } +type U(d: int) { } + +class P { } +class Q extends P { } +class R extends Q { + var x: T; + var y: int; + new(x, y) { } + def getPtrx() -> Pointer { + // legality depends on what T is + return Pointer.atField(x); + } +} +class S extends R { + def getPtr() -> Pointer { + return Pointer.atField(x.a.i); + } + new(x: B, y: int) super(x, y) { } +} + +component Comp { + var cb: B; +} + +def main(n: int) -> int { + Comp.cb = B(A(n, n+1), u32.view(n+2)); + // keep all the fields around + var pi = Pointer.atField(Comp.cb.a.i); + var pj = Pointer.atField(Comp.cb.a.j); + var pb = Pointer.atField(Comp.cb.b); + // case actually being tested + var p = Pointer.atField(Comp.cb.a); + return p.load() + pi.load() + pj.load() + pb.load() - (3 * n + 3); +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad17.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad17.v3 new file mode 100644 index 000000000..cd353c35c --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad17.v3 @@ -0,0 +1,23 @@ +//@seman=TypeError @ 15:24 +// tests possible issues related to unboxed field and parameterized types + +type A(i: T) #unboxed { } +type B(a: A, b: u32) #unboxed { } + +class P { } +class Q extends P { } +class R extends Q { + var x: B; + def y: int; + new(x, y) { } + def getPtrx() -> Pointer { + // legality depends on what T is + return Pointer.atField(x.a.i); + } +} +def main(n: int) -> int { + var r: R<(bool,int)>; + r = R.new(B(A((true, n)), 13), 15); + var p = r.getPtrx(); + return p.load(); +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad18.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad18.v3 new file mode 100644 index 000000000..72160767d --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad18.v3 @@ -0,0 +1,22 @@ +//@seman=TypeError @ 15:24 +// tests possible issues related to unboxed field and parameterized types + +type A(i: T) #unboxed { } +type B(a: A, b: u32) #unboxed { } + +class P { } +class Q extends P { } +class R extends Q { + var x: B; + def y: int; + new(x, y) { } + def getPtrx() -> Pointer { + // legality depends on what T is + return Pointer.atField(x.a.i); + } +} +def main(n: int) -> int { + var r = R.new(B(A(void), 13), 15); + var p = r.getPtrx(); + return p.load(); +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad19.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad19.v3 new file mode 100644 index 000000000..ac7bc1ee3 --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad19.v3 @@ -0,0 +1,17 @@ +//@seman = TypeError @ 15:17 +// tests possible issues related to unboxed field and parameterized types + +class C #unboxed { + var x: int; + new(x) { } +} +class D { + var c: C; + new(x: int) { c = C.new(x); } +} + +def main(n: int) -> int { + var d = D.new(n); + var p = Pointer.atField(d.c); + return p.load() + d.c.x - n; +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad20.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad20.v3 new file mode 100644 index 000000000..8334bb1c4 --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad20.v3 @@ -0,0 +1,13 @@ +//@seman=TypeError @ 11:17 +// tests possible issues related to unboxed field and parameterized types + +component Comp { + var f: void; +} + +def main(n: int) -> int { + Comp.f = void; + // case actually being tested + var p = Pointer.atField(Comp.f); + return p.load() + n; +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad21.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad21.v3 new file mode 100644 index 000000000..59c7fec70 --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad21.v3 @@ -0,0 +1,13 @@ +//@seman=TypeError @ 11:17 +// tests possible issues related to unboxed field and parameterized types + +component Comp { + var f: (int, bool); +} + +def main(n: int) -> int { + Comp.f = (13, true); + // case actually being tested + var p = Pointer.atField(Comp.f); + return p.load() + n; +} diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad22.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad22.v3 new file mode 100644 index 000000000..4d7b1e86f --- /dev/null +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad22.v3 @@ -0,0 +1,17 @@ +//@seman = TypeError @ 15:17 +// tests possible issues related to unboxed field and parameterized types + +class C #unboxed { + var x: int; + new(x) { } +} + +component CC { + var c: C; +} + +def main(n: int) -> int { + CC.c = C.new(n); + var p = Pointer.atField(CC.c); + return p.load() + CC.c.x - n; +} diff --git a/test/pointer/seman/descs.txt b/test/pointer/seman/descs.txt new file mode 100644 index 000000000..7ba590356 --- /dev/null +++ b/test/pointer/seman/descs.txt @@ -0,0 +1,23 @@ +00 p field of boxed variant, local variable ref +01 p field of boxed variant in component +02 p field of boxed variant in class +03 p field of packed variant, local variable ref +04 p field of packed variant in component +05 p field of packed variant in class +06 p field of tuple in anonymous component +07 p field of tuple in component +08 p field of tuple in class +09 p field of ub variant, local variable ref +10 p field of ub variant in boxed variant +11 class field T, T is ub variant (multiple fields) +12 class field T, T is ub variant (single field) +13 class field T, T is ub variant, p variant, multiple fields +14 class field T, T is ub variant, p variant, single field +15 comp field ub variant, p variant, multiple fields +16 comp field ub variant, p variant, single field +17 class field B, T is tuple, p T +18 class field B, T is void, p T +19 class field is unboxed class type, p to class +20 component field is void +21 component field is tuple +22 component field is unboxed class type, p to class diff --git a/test/pointer/unboxed-pointer-at-descs.txt b/test/pointer/unboxed-pointer-at-descs.txt new file mode 100644 index 000000000..d66e733ad --- /dev/null +++ b/test/pointer/unboxed-pointer-at-descs.txt @@ -0,0 +1,30 @@ +00 Many cases, but Pointer.atField ops are optimizaed away, which means + type constraints on the pointed-at field are not fully checked, since + those checks are done only on *reachable* code. +01 field is optimized away if atField is optimized away and is only ref +02 field is *not* optimized away if atField is not optimized away +03 many tests of load, store, and cmpswp +04 checks values in a global from an initializing assignment +05 checks values in a named component from an initializing assignment +06 checks values in a class field from an initializing assignment +07 checks values in a class field from an initializing assignment, class in a component +08 checks values in a class field from an initializing assignment, class in a local +09 checks loads from a global +10 checks loads from a component +11 checks loads from a global class instance +12 checks loads from a class instance in a component +13 checks loads from a class instance in a local +14 checks cmpswps and stores into a global +15 checks cmpswps and stores into a component +16 checks cmpswps and stores into a global class instance +17 checks cmpswps and stores into a class instance, class in a component +18 checks cmpswps and stores into a class instance, class in a local +19 checks stores into a global +20 checks stores into a component +21 checks stores into a global class instance +22 checks stores into a class instance, class in a component +23 checks stores into a class instance, class in a local +24 field of type T, type legal (int) +25 field of type T, type legal (int) (different parameterization scheme) +26 type check of field in an unboxed class (ok case) +27 type check of field in an unboxed class (another ok case) From 2b9a5ed2a56ed78ff59e494a5bc53772f6468ecc Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Thu, 6 Feb 2025 14:37:53 +1100 Subject: [PATCH 10/12] Update pointerdAtTypeLegal to use a match on the type, simplify semantic tests --- aeneas/src/ir/Reachability.v3 | 11 +++++++---- .../seman/Pointer_atFieldUnboxedBad00.v3 | 4 +--- .../seman/Pointer_atFieldUnboxedBad01.v3 | 9 ++------- .../seman/Pointer_atFieldUnboxedBad02.v3 | 7 ++----- .../seman/Pointer_atFieldUnboxedBad03.v3 | 4 +--- .../seman/Pointer_atFieldUnboxedBad04.v3 | 9 ++------- .../seman/Pointer_atFieldUnboxedBad05.v3 | 6 ++---- .../seman/Pointer_atFieldUnboxedBad06.v3 | 4 +--- .../seman/Pointer_atFieldUnboxedBad07.v3 | 9 ++------- .../seman/Pointer_atFieldUnboxedBad08.v3 | 4 +--- .../seman/Pointer_atFieldUnboxedBad10.v3 | 4 +--- .../seman/Pointer_atFieldUnboxedBad11.v3 | 3 +-- .../seman/Pointer_atFieldUnboxedBad12.v3 | 7 ------- .../seman/Pointer_atFieldUnboxedBad13.v3 | 18 +++--------------- .../seman/Pointer_atFieldUnboxedBad14.v3 | 16 ++-------------- .../seman/Pointer_atFieldUnboxedBad15.v3 | 15 ++------------- .../seman/Pointer_atFieldUnboxedBad16.v3 | 9 ++------- .../seman/Pointer_atFieldUnboxedBad17.v3 | 3 +-- .../seman/Pointer_atFieldUnboxedBad19.v3 | 2 +- .../seman/Pointer_atFieldUnboxedBad20.v3 | 3 +-- .../seman/Pointer_atFieldUnboxedBad21.v3 | 3 +-- .../seman/Pointer_atFieldUnboxedBad22.v3 | 2 +- 22 files changed, 37 insertions(+), 115 deletions(-) diff --git a/aeneas/src/ir/Reachability.v3 b/aeneas/src/ir/Reachability.v3 index 5d32e418f..daf7e90cd 100644 --- a/aeneas/src/ir/Reachability.v3 +++ b/aeneas/src/ir/Reachability.v3 @@ -416,10 +416,13 @@ class ReachabilityAnalyzer(compilation: Compilation) { } } def pointedAtTypeLegal(t: Type) -> bool { - if (PrimType.?(t) && !VoidType.?(t)) return true; - if (ClassType.?(t) && !ClassType.!(t).classDecl.isUnboxed()) return true; - if (PointerType.?(t)) return true; - // add other types here, e.g., Tuple (eventually), and fix error message in getPointerAtField + match (t) { + x: VoidType => return false; // must come before PrimType + x: PrimType => return true; + x: ClassType => return !x.classDecl.isUnboxed(); + x: PointerType => return true; + // x: TupleType => return true; // add when handled + } return false; } def getPointerAtField(rf: RaField, op: SsaApplyOp, checkType: bool) { diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad00.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad00.v3 index db50a83d5..1b2d3179f 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad00.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad00.v3 @@ -2,9 +2,7 @@ // Disallow pointer to *boxed* field of a local variable type V(x: int); -def main() -> int { +component C { var v = V(13); var p = Pointer.atField(v.x); - p.store(17); - return v.x; } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad01.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad01.v3 index e914e2726..914613dbc 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad01.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad01.v3 @@ -1,13 +1,8 @@ -//@seman = TypeError @ 10:33 +//@seman = TypeError @ 7:33 // Disallow pointer to *boxed* field of a component type V(x: int); component C { var v = V(13); -} - -def main() -> int { - var p = Pointer.atField(C.v.x); - p.store(17); - return C.v.x; + var p = Pointer.atField(v.x); } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad02.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad02.v3 index a4e4aee6c..1e54edd0c 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad02.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad02.v3 @@ -1,4 +1,4 @@ -//@seman = TypeError @ 14:33 +//@seman = TypeError @ 13:33 // Disallow pointer to *boxed* field of a class type V(x: int); @@ -8,10 +8,7 @@ class C { v = V(i); } } - -def main() -> int { +component Comp { var c = C.new(13); var p = Pointer.atField(c.v.x); - p.store(17); - return c.v.x; } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad03.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad03.v3 index bed477626..3f66228e4 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad03.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad03.v3 @@ -2,9 +2,7 @@ // Disallow pointer to *packed* field of a local variable type V(x: u4, y: u4) #packing 0b_xxxxyyyy; -def main() -> int { +component C { var v = V(13, 7); var p = Pointer.atField(v.x); - p.store(11); - return v.x; } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad04.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad04.v3 index f97329861..9fe720872 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad04.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad04.v3 @@ -1,13 +1,8 @@ -//@seman = TypeError @ 10:33 +//@seman = TypeError @ 7:33 // Disallow pointer to *packed* field of a global variable type V(x: u4, y: u4) #packing 0b_xxxxyyyy; component C { var v = V(13, 7); -} - -def main() -> int { - var p = Pointer.atField(C.v.x); - p.store(17); - return C.v.x; + var p = Pointer.atField(v.x); } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad05.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad05.v3 index 9d8bb3ac9..89d03e5ff 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad05.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad05.v3 @@ -1,4 +1,4 @@ -//@seman = TypeError @ 15:33 +//@seman = TypeError @ 14:33 // Disallow pointer to *packed* field of a local variable type V(x: u4, y: u4) #packing 0b_xxxxyyyy; @@ -9,9 +9,7 @@ class C { } } -def main() -> int { +component Comp { var c = C.new(13); var p = Pointer.atField(c.v.x); - p.store(17); - return c.v.x; } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad06.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad06.v3 index 724a159d5..e77e4833b 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad06.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad06.v3 @@ -2,8 +2,6 @@ // Disallow pointer to global tuple var tup = (1, 13); -def main() -> int { +component C { var p = Pointer.atField(tup.1); - p.store(17); - return tup.1; } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad07.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad07.v3 index 95e0c60ca..76ae3b2ba 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad07.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad07.v3 @@ -1,11 +1,6 @@ -//@seman = TypeError @ 8:33 +//@seman = TypeError @ 5:33 // Disallow pointer to component tuple component C { var tup = (1, 13); -} - -def main() -> int { - var p = Pointer.atField(C.tup.1); - p.store(17); - return C.tup.1; + var p = Pointer.atField(tup.1); } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad08.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad08.v3 index 8ea488a48..55084d876 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad08.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad08.v3 @@ -4,9 +4,7 @@ class C { var tup = (1, 13); } -def main() -> int { +component Comp { var c = C.new(); var p = Pointer.atField(c.tup.1); - p.store(17); - return c.tup.1; } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad10.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad10.v3 index f61d4884e..ad03e6290 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad10.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad10.v3 @@ -3,9 +3,7 @@ type V(x: int) #unboxed; type V2(a: int, v: V); -def main() -> int { +component C { var v2 = V2(13, V(14)); var p = Pointer.atField(v2.v.x); - p.store(17); - return v2.v.x; } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad11.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad11.v3 index 8680009e8..e08b4cb18 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad11.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad11.v3 @@ -21,6 +21,5 @@ class S extends R { def main(n: int) -> int { var s = S.new(B(A(n), 17), 5); var p = s.getPtr(); - // make sure field is not optimized away - though that should be another test - return p.load() + s.x.a.i - n; + return 0; } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad12.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad12.v3 index fd8ca6400..197bec5fc 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad12.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad12.v3 @@ -15,13 +15,6 @@ class R extends Q { return Pointer.atField(x); } } -class S extends R { - def getPtr() -> Pointer { - return Pointer.atField(x.a.i); - } - new(x: B, y: int) super(x, y) { } -} - def main(n: int) -> int { var r = R.new(A(n), 15); var p = r.getPtrx(); diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad13.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad13.v3 index ac4e190bc..5120485fa 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad13.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad13.v3 @@ -1,4 +1,4 @@ -//@seman=TypeError @ 33:17 +//@seman=TypeError @ 21:17 // tests possible issues related to unboxed field and parameterized types type A(i: int, j: int) #unboxed { } @@ -15,21 +15,9 @@ class R extends Q { return Pointer.atField(x); } } -class S extends R { - def getPtr() -> Pointer { - return Pointer.atField(x.a.i); - } - new(x: B, y: int) super(x, y) { } -} def main(n: int) -> int { - var r = R.new(B(A(n, n+1), u32.view(n+2)), n+3); - // keep all the fields around - var pi = Pointer.atField(r.x.a.i); - var pj = Pointer.atField(r.x.a.j); - var pb = Pointer.atField(r.x.b); - var py = Pointer.atField(r.y); - // case actually being tested + var r = R.new(B(A(n, n), u32.view(n)), n); var p = Pointer.atField(r.x); - return p.load() + pi.load() + pj.load() + pb.load() + py.load() - (4 * n + 6); + return p.load(); } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad14.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad14.v3 index b0bc9b119..e77c77004 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad14.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad14.v3 @@ -1,4 +1,4 @@ -//@seman=TypeError @ 33:17 +//@seman=TypeError @ 21:17 // tests possible issues related to unboxed field and parameterized types type A(i: int, j: int) #unboxed { } @@ -15,21 +15,9 @@ class R extends Q { return Pointer.atField(x); } } -class S extends R { - def getPtr() -> Pointer { - return Pointer.atField(x.a.i); - } - new(x: B, y: int) super(x, y) { } -} def main(n: int) -> int { var r = R.new(B(A(n, n+1), u32.view(n+2)), n+3); - // keep all the fields around - var pi = Pointer.atField(r.x.a.i); - var pj = Pointer.atField(r.x.a.j); - var pb = Pointer.atField(r.x.b); - var py = Pointer.atField(r.y); - // case actually being tested var p = Pointer.atField(r.x.a); - return p.load() + pi.load() + pj.load() + pb.load() + py.load() - (4 * n + 6); + return p.load(); } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad15.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad15.v3 index 5c063ae6d..62466b3a5 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad15.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad15.v3 @@ -1,4 +1,4 @@ -//@seman=TypeError @ 38:17 +//@seman=TypeError @ 27:17 // tests possible issues related to unboxed field and parameterized types type A(i: int, j: int) #unboxed { } @@ -17,12 +17,6 @@ class R extends Q { return Pointer.atField(x); } } -class S extends R { - def getPtr() -> Pointer { - return Pointer.atField(x.a.i); - } - new(x: B, y: int) super(x, y) { } -} component Comp { var cb: B; @@ -30,11 +24,6 @@ component Comp { def main(n: int) -> int { Comp.cb = B(A(n, n+1), u32.view(n+2)); - // keep all the fields around - var pi = Pointer.atField(Comp.cb.a.i); - var pj = Pointer.atField(Comp.cb.a.j); - var pb = Pointer.atField(Comp.cb.b); - // case actually being tested var p = Pointer.atField(Comp.cb); - return p.load() + pi.load() + pj.load() + pb.load() - (3 * n + 3); + return p.load(); } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad16.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad16.v3 index 8bdcd0e0f..70ba3c2f0 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad16.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad16.v3 @@ -1,4 +1,4 @@ -//@seman=TypeError @ 38:17 +//@seman=TypeError @ 33:17 // tests possible issues related to unboxed field and parameterized types type A(i: int, j: int) #unboxed { } @@ -30,11 +30,6 @@ component Comp { def main(n: int) -> int { Comp.cb = B(A(n, n+1), u32.view(n+2)); - // keep all the fields around - var pi = Pointer.atField(Comp.cb.a.i); - var pj = Pointer.atField(Comp.cb.a.j); - var pb = Pointer.atField(Comp.cb.b); - // case actually being tested var p = Pointer.atField(Comp.cb.a); - return p.load() + pi.load() + pj.load() + pb.load() - (3 * n + 3); + return p.load(); } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad17.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad17.v3 index cd353c35c..8d1a62cc7 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad17.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad17.v3 @@ -16,8 +16,7 @@ class R extends Q { } } def main(n: int) -> int { - var r: R<(bool,int)>; - r = R.new(B(A((true, n)), 13), 15); + var r: R<(bool,int)> = R.new(B(A((true, n)), 13), 15); var p = r.getPtrx(); return p.load(); } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad19.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad19.v3 index ac7bc1ee3..4cbb71b2f 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad19.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad19.v3 @@ -13,5 +13,5 @@ class D { def main(n: int) -> int { var d = D.new(n); var p = Pointer.atField(d.c); - return p.load() + d.c.x - n; + return p.load(); } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad20.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad20.v3 index 8334bb1c4..d158faf42 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad20.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad20.v3 @@ -1,4 +1,4 @@ -//@seman=TypeError @ 11:17 +//@seman=TypeError @ 10:17 // tests possible issues related to unboxed field and parameterized types component Comp { @@ -7,7 +7,6 @@ component Comp { def main(n: int) -> int { Comp.f = void; - // case actually being tested var p = Pointer.atField(Comp.f); return p.load() + n; } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad21.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad21.v3 index 59c7fec70..e25c7e846 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad21.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad21.v3 @@ -1,4 +1,4 @@ -//@seman=TypeError @ 11:17 +//@seman=TypeError @ 10:17 // tests possible issues related to unboxed field and parameterized types component Comp { @@ -7,7 +7,6 @@ component Comp { def main(n: int) -> int { Comp.f = (13, true); - // case actually being tested var p = Pointer.atField(Comp.f); return p.load() + n; } diff --git a/test/pointer/seman/Pointer_atFieldUnboxedBad22.v3 b/test/pointer/seman/Pointer_atFieldUnboxedBad22.v3 index 4d7b1e86f..4148a423f 100644 --- a/test/pointer/seman/Pointer_atFieldUnboxedBad22.v3 +++ b/test/pointer/seman/Pointer_atFieldUnboxedBad22.v3 @@ -13,5 +13,5 @@ component CC { def main(n: int) -> int { CC.c = C.new(n); var p = Pointer.atField(CC.c); - return p.load() + CC.c.x - n; + return p.load(); } From 069c89615dc33c1482fba07d06cf592c0ed4716a Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Thu, 6 Feb 2025 16:13:34 +1100 Subject: [PATCH 11/12] Need also to allow pointers to fields of array type (they're never unboxed (so far)) --- aeneas/src/ir/Reachability.v3 | 1 + 1 file changed, 1 insertion(+) diff --git a/aeneas/src/ir/Reachability.v3 b/aeneas/src/ir/Reachability.v3 index daf7e90cd..8e444b5bf 100644 --- a/aeneas/src/ir/Reachability.v3 +++ b/aeneas/src/ir/Reachability.v3 @@ -421,6 +421,7 @@ class ReachabilityAnalyzer(compilation: Compilation) { x: PrimType => return true; x: ClassType => return !x.classDecl.isUnboxed(); x: PointerType => return true; + x: ArrayType => return true; // x: TupleType => return true; // add when handled } return false; From 7d4ebc9d107c8ef6f82b3500dffd488a57735752 Mon Sep 17 00:00:00 2001 From: Eliot Moss Date: Thu, 6 Feb 2025 16:34:13 +1100 Subject: [PATCH 12/12] Adding a test that Pointer.atField on a field of array type is ok --- test/pointer/Pointer_atField18.v3 | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/pointer/Pointer_atField18.v3 diff --git a/test/pointer/Pointer_atField18.v3 b/test/pointer/Pointer_atField18.v3 new file mode 100644 index 000000000..82e6f5ead --- /dev/null +++ b/test/pointer/Pointer_atField18.v3 @@ -0,0 +1,12 @@ +//@execute 11=11; -55=-55 +component C { + var a: Array; +} + +def main(n: int) -> int { + C.a = [14]; + var p = Pointer.atField(C.a); + var aa: Array = [n]; + p.cmpswp(C.a, aa); + return C.a[0]; +}