Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for pointers to unboxed variant fields (that are not in arrays) #307

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
6 changes: 5 additions & 1 deletion aeneas/src/core/Opcode.v3
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,8 @@ type Opcode {
case PtrAtComponentField(field: IrField);
case PtrAtObjectField(field: IrField);
case PtrAtRefLayoutField(offset: int);
case PtrAtUnboxedObjectField(fields: List<IrSpec>);
case PtrAtUnboxedComponentField(fields: List<IrSpec>);
case PtrCmpSwp;
case PtrLoad;
case PtrStore;
Expand Down Expand Up @@ -324,7 +326,9 @@ 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.PtrAtUnboxedObjectField.tag] = P;
t[Opcode.PtrAtUnboxedComponentField.tag] = P;
t[Opcode.PtrLoad.tag] = NONE;
t[Opcode.PtrStore.tag] = NONE;

Expand Down
24 changes: 24 additions & 0 deletions aeneas/src/core/Operator.v3
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,14 @@ component V3Op {
var ta = [refType];
return newOp0(Opcode.PtrAtRefLayoutField(offset), ta, ta, ptrType);
}
def newPtrAtUnboxedObjectField(specs: List<IrSpec>, ptrType: Type) -> Operator {
var ta = [specs.head.receiver];
return newOp0(Opcode.PtrAtUnboxedObjectField(specs), ta, ta, ptrType);
}
def newPtrAtUnboxedComponentField(specs: List<IrSpec>, 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);
}
Expand Down Expand Up @@ -583,6 +591,17 @@ component V3Op {
}
}

def renderList<T>(sb: StringBuilder, lst: List<T>,
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();
Expand Down Expand Up @@ -611,6 +630,11 @@ 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);
PtrAtUnboxedObjectField(specs) => rfunc = renderList<IrSpec>(_, specs, IrSpec.render, ",");
PtrAtUnboxedComponentField(specs) => rfunc = renderList<IrSpec>(_, specs, IrSpec.render, ",");
ClassAlloc(method) => if(method != null) rfunc = method.render;
ClassGetMethod(method) => rfunc = method.render;
ClassGetVirtual(method) => rfunc = method.render;
Expand Down
12 changes: 12 additions & 0 deletions aeneas/src/ir/Reachability.v3
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +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));
PtrAtUnboxedObjectField(specs) => if (op.useList != null) getAndSetUnboxedField(op, specs, context);
PtrAtUnboxedComponentField(specs) => if (op.useList != null) getAndSetUnboxedField(op, specs, context);
_ => ;
}
}
Expand Down Expand Up @@ -420,6 +422,16 @@ class ReachabilityAnalyzer(compilation: Compilation) {
rf.writeFacts = none;
rf.setFact(RaFact.RF_VAL_MANY);
}
def getAndSetUnboxedField(op: SsaApplyOp, specs: List<IrSpec>, 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();
Expand Down
69 changes: 67 additions & 2 deletions aeneas/src/ir/SsaNormalizer.v3
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,12 @@ class SsaRaNormalizer extends SsaRebuilder {
normDefault(i_old, op);
}
}
PtrAtUnboxedObjectField(specs) => {
normPtrAtUnboxedField(i_old, specs, op, BoxKind.OBJECT);
}
PtrAtUnboxedComponentField(specs) => {
normPtrAtUnboxedField(i_old, specs, op, BoxKind.COMPONENT);
}
SystemCall(syscall) => {
// normalize a syscall operator
var ptn = normType(Tuple.fromTypeArray(op.sig.paramTypes));
Expand Down Expand Up @@ -1295,11 +1301,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);
Expand All @@ -1318,6 +1323,46 @@ class SsaRaNormalizer extends SsaRebuilder {
}
return mapN(i_old, vals);
}
def normPtrAtUnboxedField(i_old: SsaApplyOp, specs: List<IrSpec>, 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) {
// 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<IrSpec> = 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: 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);
} 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);
Expand Down Expand Up @@ -1407,6 +1452,21 @@ class SsaRaNormalizer extends SsaRebuilder {
}
return vals;
}
def getVariantUnboxedField(rc: RaClass, raField: RaField, vn: VariantNorm, ninputs: Array<IrField>) -> Array<IrField> {
var nf = raField.liveFields(norm.ra);
var flds = Array<IrField>.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]);
Expand Down Expand Up @@ -1497,3 +1557,8 @@ class SsaRaNormalizer extends SsaRebuilder {
if (!i_old.facts.O_NO_NULL_CHECK) curBlock.opNullCheck(obj.getType(), obj);
}
}

enum BoxKind {
OBJECT,
COMPONENT
}
23 changes: 16 additions & 7 deletions aeneas/src/ir/VstIr.v3
Original file line number Diff line number Diff line change
Expand Up @@ -61,27 +61,30 @@ 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();
ic.boxing = boxing;
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()) {
Expand All @@ -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);
}
}
Expand Down
9 changes: 8 additions & 1 deletion aeneas/src/mach/MachLowering.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
15 changes: 15 additions & 0 deletions aeneas/src/mach/MachProgram.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions aeneas/src/main/Error.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
15 changes: 15 additions & 0 deletions aeneas/src/ssa/SsaOptimizer.v3
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
32 changes: 28 additions & 4 deletions aeneas/src/ssa/VstSsaGen.v3
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,7 @@ class VstSsaGen extends VstVisitor<VstSsaEnv, SsaInstr> {
LayoutRepeatedNested,
LayoutRepeatedField,
PtrAtField,
PtrAtUnboxedField,
LayoutDecl,
LayoutFieldDecl,
Type => {
Expand Down Expand Up @@ -828,6 +829,19 @@ class VstSsaGen extends VstVisitor<VstSsaEnv, SsaInstr> {
}
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<SsaInstr>, env: VstSsaEnv) -> SsaInstr {
match (appbind) {
None => {
Expand Down Expand Up @@ -879,19 +893,29 @@ class VstSsaGen extends VstVisitor<VstSsaEnv, SsaInstr> {
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]);
}
PtrAtUnboxedObjectField(receiver, fields, ptrType) => {
var irSpecs: List<IrSpec> = Lists.map<(Type, VstField), IrSpec>(fields, getPointedAtIrSpec(_, _));
var op = V3Op.newPtrAtUnboxedObjectField(irSpecs, ptrType);
var tgt = [target];
return env.addApply(env.source, op, tgt);
}
PtrAtUnboxedComponentField(receiver, fields, ptrType) => {
var irSpecs: List<IrSpec> = 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);
}
}
}
def genReadModifyWrite(source: Source, lval: Expr, modify: (SsaInstr, VstSsaEnv) -> SsaInstr, env: VstSsaEnv) -> (SsaInstr, SsaInstr) {
Expand Down
Loading
Loading