From e33fbdd125da2879d6778a224858528f3aa522be Mon Sep 17 00:00:00 2001 From: Bradley Teo Date: Mon, 12 Feb 2024 18:00:39 -0500 Subject: [PATCH] Minor changes --- aeneas/src/ir/Normalization.v3 | 39 +++++++++++++++++---------------- aeneas/src/ir/Reachability.v3 | 1 + aeneas/src/ir/SsaNormalizer.v3 | 12 +++++----- aeneas/src/ir/VariantSolver.v3 | 40 ++++++++++++++++++++++------------ aeneas/src/main/CLOptions.v3 | 4 ++-- 5 files changed, 55 insertions(+), 41 deletions(-) diff --git a/aeneas/src/ir/Normalization.v3 b/aeneas/src/ir/Normalization.v3 index c435f76e0..dbb62969f 100644 --- a/aeneas/src/ir/Normalization.v3 +++ b/aeneas/src/ir/Normalization.v3 @@ -86,7 +86,7 @@ class ReachabilityNormalizer(config: NormalizerConfig, ra: ReachabilityAnalyzer) ra.queue.add(normClassRecord, (rc, oldRecord, newRecord.values)); } ra.prog.setComponentRecord(comp, newRecord); - } else if (rc.variantNorm == null) { + } else if (!rc.isUnboxed()) { // create and map new records to be normalized for (l = rc.instances; l != null; l = l.tail) { var oldRecord = l.head, newRecord = ra.prog.newRecord(tn.newType, rc.normFields.length); @@ -108,7 +108,7 @@ class ReachabilityNormalizer(config: NormalizerConfig, ra: ReachabilityAnalyzer) var rm = l.head, m = rm.orig; if (rm.norm != null) continue; // already done var ftype = if(rm.spec == null, m.sig.funcType(), rm.spec.getMethodType()); - if (rc.variantNorm != null) { + if (rc.isUnboxed()) { // move flattened data type receiver to function sig ftype = Function.prependParamTypes(rc.variantNorm.sub, ftype); } @@ -348,21 +348,22 @@ class ReachabilityNormalizer(config: NormalizerConfig, ra: ReachabilityAnalyzer) return; } - if (!vn.isEnum()) { - var rfs = vrc.fields; - for (i < vrc.fields.length) { - var rf = vrc.fields[i]; - if (rf != null && rf.normIndex >= 0) { - var v = oldRecord.values[i]; - var indexes = vn.fields[i].indexes; - var fieldArray = Array.new(indexes.length); - if (rf.fieldType == null) fieldArray[0] = normSimpleVal(null, v); - else normValIntoArray(v, rf.typeNorm, fieldArray, 0); - for (j < indexes.length) array[indexes[j]] = fieldArray[j]; - } + if (!vn.isTagless()) array[vn.tagIndex()] = Int.box(vn.tagValue); + if (vn.isEnum()) return; + + var rfs = vrc.fields; + for (i < vrc.fields.length) { + var rf = vrc.fields[i]; + if (rf != null && rf.normIndex >= 0) { + var v = oldRecord.values[i]; + var indexes = vn.fields[i].indexes; + // XXX: get rid of fieldArray or reuse + var fieldArray = Array.new(indexes.length); + if (rf.fieldType == null) fieldArray[0] = normSimpleVal(null, v); + else normValIntoArray(v, rf.typeNorm, fieldArray, 0); + for (j < indexes.length) array[indexes[j]] = fieldArray[j]; } } - if (!vn.isTagless()) array[vn.tagIndex()] = Int.box(vn.tagValue); } // normalize the live instances of a simple (i.e. size-1 element) array type def normSimpleArrayRecord(record: Record) { @@ -391,7 +392,7 @@ class ReachabilityNormalizer(config: NormalizerConfig, ra: ReachabilityAnalyzer) def normSimpleVal(tn: TypeNorm, v: Val) -> Val { match (v) { x: Record => { - if (tn != null && VariantNorm.?(tn) && VariantNorm.!(tn).isEnum()) return normVariantVal(VariantNorm.!(tn), x)[0]; + if (VariantNorm.?(tn) && VariantNorm.!(tn).isEnum()) return normVariantVal(VariantNorm.!(tn), x)[0]; var r = recordMap[x]; return if(r == null, x, r); } @@ -450,7 +451,7 @@ class ReachabilityNormalizer(config: NormalizerConfig, ra: ReachabilityAnalyzer) var table = Array.new(size), mtable = IrMtable.new(rm.norm, rc.minClassId, table); rv.mtable = mtable; - if (rc.variantNorm != null) { + if (rc.isUnboxed()) { var ft = Function.funcRefType(rm.norm.getMethodType()); mtable.record = ra.prog.newRecord(V3Array.newType(ft), size); } @@ -550,7 +551,7 @@ class ReachabilityNormalizer(config: NormalizerConfig, ra: ReachabilityAnalyzer) } def createIrClass(rc: RaClass) { var sc = if(rc.parent != null, rc.parent.normClass); - var normFields = if(rc.variantNorm != null, NO_FIELDS, rc.normFields); + var normFields = if(rc.isUnboxed(), NO_FIELDS, rc.normFields); var ic = IrClass.new(rc.newIrType, null, sc, normFields, rc.normMethods); ic.minClassId = rc.minClassId; ic.maxClassId = rc.maxClassId; @@ -574,7 +575,7 @@ class ReachabilityNormalizer(config: NormalizerConfig, ra: ReachabilityAnalyzer) var receiver = ra.oldIr.getIrClass(ctype); var root = ra.oldIr.getIrClass(V3.getRootType(ctype)); var rc = ra.getClass(rm.receiver); - if (false && rc.variantNorm != null) { // TODO: disable generation of compare method bodies + if (false && rc.isUnboxed()) { // TODO: disable generation of compare method bodies // flattened data types will have inlined compare rm.orig.ssa = SsaGraph.new([], Bool.TYPE); rm.orig.ssa.startBlock.append(SsaReturn.new([rm.orig.ssa.falseConst()])); diff --git a/aeneas/src/ir/Reachability.v3 b/aeneas/src/ir/Reachability.v3 index 160087c95..772c95d5b 100644 --- a/aeneas/src/ir/Reachability.v3 +++ b/aeneas/src/ir/Reachability.v3 @@ -634,6 +634,7 @@ class RaClass extends RaType { } return true; } + def isUnboxed() -> bool { return variantNorm != null; } } // Tracks instances of an array type. class RaArray extends RaType { diff --git a/aeneas/src/ir/SsaNormalizer.v3 b/aeneas/src/ir/SsaNormalizer.v3 index e1cade440..a5940a91e 100644 --- a/aeneas/src/ir/SsaNormalizer.v3 +++ b/aeneas/src/ir/SsaNormalizer.v3 @@ -239,7 +239,7 @@ class SsaRaNormalizer extends SsaRebuilder { var newOp = V3Op.newCallMethod(m); var newArgs = normArgs(funcNorm, genRefs(app.inputs)); if (V3.isVariant(rc.oldType)) { - if (rc.variantNorm != null) { + if (rc.isUnboxed()) { // flattened data type becomes component call and needs new receiver newArgs = Arrays.prepend(newGraph.nullReceiver(), newArgs); } else { @@ -281,7 +281,7 @@ class SsaRaNormalizer extends SsaRebuilder { var t = extractVirtualRef(orig, method), funcNorm = t.0, m = t.1; var newArgs = normArgs(funcNorm, genRefs(app.inputs)); if (t.2) { // still a virtual dispatch - if (rc.variantNorm != null) { + if (rc.isUnboxed()) { // use the variant tag as an index into a table of functions var tag = newArgs[0]; var record = IrSelector.!(m.member).mtable.record; @@ -293,7 +293,7 @@ class SsaRaNormalizer extends SsaRebuilder { normCall(app, funcNorm, V3Op.newCallVariantSelector(m), newArgs); } } else { - if (rc.variantNorm != null) { + if (rc.isUnboxed()) { // flattened data type becomes component call and needs new receiver newArgs = Arrays.prepend(newGraph.nullReceiver(), newArgs); } else { @@ -1218,9 +1218,9 @@ class SsaRaNormalizer extends SsaRebuilder { if (!isVariant) addNullCheck(oldApp, ninputs[0]); return map0(oldApp); } - normType(raField.receiver); // XXX: normType() side-effect of flattening var rc = norm.ra.getClass(raField.receiver); - if (isVariant && raField != null && rc.variantNorm != null) { + normType(raField.receiver); // XXX: normType() side-effect of flattening + if (isVariant && raField != null && rc.isUnboxed()) { // field of unboxed data type var vals = Array.new(nf.length); var field = rc.variantNorm.fields[raField.orig.index]; @@ -1253,7 +1253,7 @@ class SsaRaNormalizer extends SsaRebuilder { // OPT: remove write of zero-width field // OPT: remove write of write-only field return addNullCheck(oldApp, receiver); - } else if (rc.variantNorm != null) { + } else if (rc.isUnboxed()) { // init/set of field of flattened data type return map0(oldApp); } else if (nf.length == 1) { diff --git a/aeneas/src/ir/VariantSolver.v3 b/aeneas/src/ir/VariantSolver.v3 index a14bcaf04..e851ba4be 100644 --- a/aeneas/src/ir/VariantSolver.v3 +++ b/aeneas/src/ir/VariantSolver.v3 @@ -65,6 +65,10 @@ class VariantField { def ON_STACK = -1; class VariantSolver(nc: NormalizerConfig, rn: ReachabilityNormalizer, verbose: bool) { + def variantUnboxMatcher = GlobMatcher.new(CLOptions.UNBOX_VARIANTS.get()); + + // Normalizes a non-recursive variant, returns either a VariantNorm for an unboxed variant, + // or a simple TypeNorm for a boxed variant def normVariant(t: Type, rc: RaClass) -> TypeNorm { if (rc.variantNorm != null) return rc.variantNorm; if (rc.orig.boxing == Boxing.BOXED) rn.mapSimple(t); @@ -83,17 +87,10 @@ class VariantSolver(nc: NormalizerConfig, rn: ReachabilityNormalizer, verbose: b } return rn.mapSimple(t); } - def setVariantNormForChildren(rc: RaClass, tagType: IntType, tagField: VariantField) { - var vn = VariantNorm.new(rc.oldType, tagType, [tagType], [], tagField); - vn.tagValue = V3.getVariantTag(rc.oldType); - rc.raFacts |= RaFact.RC_ENUM; - rc.variantNorm = vn; - - for (l = rc.children; l != null; l = l.tail) { - setVariantNormForChildren(l.head, tagType, tagField); - } - } - def tryUnboxing(rc: RaClass) -> bool { + // Try to unbox a variant in one of two ways: + // 1. If a (non-closure) variant has all empty fields in all cases, represent it as a single uN tag (enum representation). + // 2. If a variant has only one case, represent it as a tagless tuple of scalars (data representation). + private def tryUnboxing(rc: RaClass) -> bool { if (rc.variantNorm != null) return true; // already done while (rc.parent != null) rc = rc.parent; @@ -114,7 +111,7 @@ class VariantSolver(nc: NormalizerConfig, rn: ReachabilityNormalizer, verbose: b var tagType = V3.getVariantTagType(rc.oldType); var tagTypeNorm = rn.norm(tagType); var tagField = VariantField.new(tagTypeNorm, [0]); - setVariantNormForChildren(rc, tagType, tagField); + unboxUsingEnumVariantNorm(rc, tagType, tagField); return true; } if (rc.children != null) { @@ -123,14 +120,19 @@ class VariantSolver(nc: NormalizerConfig, rn: ReachabilityNormalizer, verbose: b } match (rc.orig.boxing) { BOXED => return false; - AUTO => if (rc.normFields.length > nc.MaxFlatDataValues && CLOptions.UNBOX_ALL.get()) return false; + AUTO => { + if (rc.normFields.length > nc.MaxFlatDataValues) { + var classDecl = ClassType.!(rc.orig.ctype).classDecl; + if (!variantUnboxMatcher.matches(classDecl.token.image)) return false; + } + } UNBOXED => ; // program specified unboxed; TODO: recursion or closure should be an error } if (rc.recursive > 1 || closure) return false; // recursive or closed over unboxUsingTaglessVariantNorm(rc); return true; } - def unboxUsingTaglessVariantNorm(rc: RaClass) { + private def unboxUsingTaglessVariantNorm(rc: RaClass) { var ofs = rc.orig.fields; var fields = Array.new(ofs.length); var fieldRanges = Array<(int, int)>.new(ofs.length); @@ -168,4 +170,14 @@ class VariantSolver(nc: NormalizerConfig, rn: ReachabilityNormalizer, verbose: b if (verbose) Terminal.put1("variant norm %q\n", rc.variantNorm.render); } + private def unboxUsingEnumVariantNorm(rc: RaClass, tagType: IntType, tagField: VariantField) { + var vn = VariantNorm.new(rc.oldType, tagType, [tagType], [], tagField); + vn.tagValue = V3.getVariantTag(rc.oldType); + rc.raFacts |= RaFact.RC_ENUM; + rc.variantNorm = vn; + + for (l = rc.children; l != null; l = l.tail) { + unboxUsingEnumVariantNorm(l.head, tagType, tagField); + } + } } diff --git a/aeneas/src/main/CLOptions.v3 b/aeneas/src/main/CLOptions.v3 index 8d288a666..80898b14b 100644 --- a/aeneas/src/main/CLOptions.v3 +++ b/aeneas/src/main/CLOptions.v3 @@ -151,9 +151,9 @@ component CLOptions { "Automatically set execute permission for compiled binaries."); def USE_GLOBALREGALLOC = compileOpt.newMatcherOption("global-regalloc", "Enable global register allocator."); - def UNBOX_VARIANTS = compileOpt.newBoolOption("unbox-variants", false, + def UNBOX_VARIANTS = compileOpt.newStringOption("unbox-variants", "*", "Enable variant unboxing features."); - def UNBOX_ALL = compileOpt.newBoolOption("unbox-all", false, + def UNBOX_VARIANT_CASES = compileOpt.newStringOption("unbox-variant-cases", "", "Unbox all non-recursive variants."); // JVM target options def JVM_RT_PATH = jvmOpt.newStringOption("jvm.rt-path", null,