Skip to content

Commit ab780f1

Browse files
committed
added extra boxing/unboxing attachments
1 parent 822aa9f commit ab780f1

File tree

11 files changed

+196
-43
lines changed

11 files changed

+196
-43
lines changed

compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import dotty.tools.dotc.report
2828
import dotty.tools.dotc.ast.Trees.SyntheticUnit
2929
import dotty.tools.dotc.transform.InvokeReturnType
3030
import dotty.tools.dotc.transform.InstructionTypeArguments
31+
import dotty.tools.dotc.transform.NoBoxingNeeded
32+
import dotty.tools.dotc.transform.NoUnboxingNeeded
3133

3234
/*
3335
*
@@ -823,15 +825,21 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
823825
val nativeKind = tpeTK(expr)
824826
genLoad(expr, nativeKind)
825827
val MethodNameAndType(mname, methodType) = asmBoxTo(nativeKind)
826-
bc.invokestatic(srBoxesRuntimeRef.internalName, mname, methodType.descriptor, itf = false)
828+
val hasExtraBoxAttach = app.getAttachment(NoBoxingNeeded).isDefined
829+
if hasExtraBoxAttach then
830+
println(s"Debug: extra box attach found on ${app.show}")
831+
bc.invokestatic(srBoxesRuntimeRef.internalName, mname, methodType.descriptor, itf = false, extraBoxingUnboxing = hasExtraBoxAttach)
827832
generatedType = boxResultType(fun.symbol) // was toTypeKind(fun.symbol.tpe.resultType)
828833

829834
case Apply(fun, List(expr)) if Erasure.Boxing.isUnbox(fun.symbol) && fun.symbol.denot.owner != defn.UnitModuleClass =>
830835
genLoad(expr)
831836
val boxType = unboxResultType(fun.symbol) // was toTypeKind(fun.symbol.owner.linkedClassOfClass.tpe)
832837
generatedType = boxType
833838
val MethodNameAndType(mname, methodType) = asmUnboxTo(boxType)
834-
bc.invokestatic(srBoxesRuntimeRef.internalName, mname, methodType.descriptor, itf = false)
839+
val hasExtraUnboxAttach = app.getAttachment(NoUnboxingNeeded).isDefined
840+
if hasExtraUnboxAttach then
841+
println(s"Debug: extra unbox attach found on ${app.show}")
842+
bc.invokestatic(srBoxesRuntimeRef.internalName, mname, methodType.descriptor, itf = false, extraBoxingUnboxing = hasExtraUnboxAttach)
835843

836844
case app @ Apply(fun, args) =>
837845
val sym = fun.symbol

compiler/src/dotty/tools/backend/jvm/BCodeHelpers.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -775,8 +775,10 @@ trait BCodeHelpers extends BCodeIdiomatic {
775775
* signatures, e.g. `def apply(i: Int): T`. A TyperRef to T is replaced by ObjectReference.
776776
*/
777777
def nonClassTypeRefToBType(sym: Symbol): ClassBType = {
778-
assert(sym.isType && compilingArray, sym)
779-
ObjectRef.asInstanceOf[ct.bTypes.ClassBType]
778+
if (sym == defn.ObjectAnySymbol) ObjectRef
779+
else
780+
assert(sym.isType && compilingArray, sym)
781+
ObjectRef.asInstanceOf[ct.bTypes.ClassBType]
780782
}
781783

782784
tp.widenDealias match {

compiler/src/dotty/tools/backend/jvm/BCodeIdiomatic.scala

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -416,10 +416,12 @@ trait BCodeIdiomatic {
416416
emitInvoke(Opcodes.INVOKESPECIAL, owner, name, desc, itf, invokeReturnType = invokeReturnType, instrTypeArgs = instrTypeArgs)
417417
}
418418
// can-multi-thread
419+
// boxing calls are only invokestatic
419420
final def invokestatic(owner: String, name: String, desc: String, itf: Boolean,
420-
invokeReturnType : Option[dotty.tools.dotc.transform.TypeB] = None, instrTypeArgs : Option[List[dotty.tools.dotc.transform.TypeA]] = None
421+
invokeReturnType : Option[dotty.tools.dotc.transform.TypeB] = None, instrTypeArgs : Option[List[dotty.tools.dotc.transform.TypeA]] = None,
422+
extraBoxingUnboxing : Boolean = false
421423
): Unit = {
422-
emitInvoke(Opcodes.INVOKESTATIC, owner, name, desc, itf, invokeReturnType = invokeReturnType, instrTypeArgs = instrTypeArgs)
424+
emitInvoke(Opcodes.INVOKESTATIC, owner, name, desc, itf, invokeReturnType = invokeReturnType, instrTypeArgs = instrTypeArgs, extraBoxingUnboxing = extraBoxingUnboxing)
423425
}
424426
// can-multi-thread
425427
final def invokeinterface(owner: String, name: String, desc: String,
@@ -459,7 +461,9 @@ trait BCodeIdiomatic {
459461

460462

461463
def emitInvoke(opcode: Int, owner: String, name: String, desc: String, itf: Boolean,
462-
invokeReturnType : Option[dotty.tools.dotc.transform.TypeB] = None, instrTypeArgs : Option[List[dotty.tools.dotc.transform.TypeA]] = None): Unit = {
464+
invokeReturnType : Option[dotty.tools.dotc.transform.TypeB] = None, instrTypeArgs : Option[List[dotty.tools.dotc.transform.TypeA]] = None,
465+
extraBoxingUnboxing : Boolean = false
466+
): Unit = {
463467
// println(s"emitInvoke $opcode $owner $name $desc itf=$itf invokeReturnType=$invokeReturnType instrTypeArgs=$instrTypeArgs")
464468
val node = new MethodInsnNode(opcode, owner, name, desc, itf)
465469
jmethod.instructions.add(node)
@@ -475,6 +479,9 @@ trait BCodeIdiomatic {
475479
val bcTypeAs = lstTypeAs.map(toJTypeA)
476480
jmethod.asInstanceOf[MethodNode1].instructionTypeArgTypeAs.put(node, bcTypeAs.asJava);
477481
}
482+
if (extraBoxingUnboxing){
483+
jmethod.asInstanceOf[MethodNode1].extraBoxUnboxMap.put(node, java.lang.Boolean.TRUE);
484+
}
478485
}
479486

480487

compiler/src/dotty/tools/backend/jvm/MethodNode1.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import dotty.tools.backend.jvm.attributes.InstructionTypeArguments;
1616
import dotty.tools.backend.jvm.attributes.InvokeReturnType;
17+
import dotty.tools.backend.jvm.attributes.ExtraBoxUnbox;
1718
import java.util.Map;
1819

1920
import dotty.tools.backend.jvm.attributes.TypeHints;
@@ -43,12 +44,16 @@ public class MethodNode1 extends MethodNode {
4344

4445
public Map<AbstractInsnNode, List<TypeHints.TypeA>> instructionTypeArgTypeAs = new LinkedHashMap<>();
4546

47+
public Map<AbstractInsnNode, Boolean> extraBoxUnboxMap = new LinkedHashMap<>();
48+
4649
private Map<AbstractInsnNode, Integer> offsetMap = new LinkedHashMap<>();
4750

4851
private InvokeReturnType invokeReturnTypeAttribute;
4952

5053
private InstructionTypeArguments instructionTypeArgumentsAttribute;
5154

55+
private ExtraBoxUnbox extraBoxUnboxAttribute;
56+
5257
public MethodNode1(int api, int access, String name, String descriptor, String signature, String[] exceptions) {
5358
super(api, access, name, descriptor, signature, exceptions);
5459
}
@@ -393,6 +398,7 @@ public void setAttribtues(){
393398
if (!hasTypeHints()) return;
394399
genOffsetMap();
395400
if (DEBUG) printOffsetMap();
401+
// create InvokeReturnType attribute
396402
List<TypeHints.TypeBHint> typeBHintList = new ArrayList<>();
397403
for (Map.Entry<AbstractInsnNode, TypeHints.TypeB> entry : invokeReturnTypeBs.entrySet()){
398404
AbstractInsnNode insn = entry.getKey();
@@ -411,6 +417,7 @@ public void setAttribtues(){
411417
if (DEBUG) System.out.println("invokeReturnTypeAttribute: " + invokeReturnTypeAttribute);
412418
visitAttribute(invokeReturnTypeAttribute);
413419
}
420+
// create InstructionTypeArguments attribute
414421
List<TypeHints.TypeAHint> typeAHintList = new ArrayList<>();
415422
for (Map.Entry<AbstractInsnNode, List<TypeHints.TypeA>> entry : instructionTypeArgTypeAs.entrySet()){
416423
AbstractInsnNode insn = entry.getKey();
@@ -428,6 +435,25 @@ public void setAttribtues(){
428435
if (DEBUG) System.out.println("instructionTypeArgumentsAttribute: " + instructionTypeArgumentsAttribute);
429436
visitAttribute(instructionTypeArgumentsAttribute);
430437
}
438+
// create ExtraBoxUnbox attribute
439+
List<Integer> extraBoxUnboxOffsets = new ArrayList<>();
440+
for (Map.Entry<AbstractInsnNode, Boolean> entry : extraBoxUnboxMap.entrySet()){
441+
AbstractInsnNode insn = entry.getKey();
442+
Boolean hasExtraBoxUnbox = entry.getValue();
443+
if (!hasExtraBoxUnbox) continue;
444+
Integer bcOffset = offsetMap.getOrDefault(insn, -1);
445+
if (bcOffset != -1){
446+
extraBoxUnboxOffsets.add(bcOffset);
447+
} else {
448+
throw new IllegalArgumentException();
449+
}
450+
}
451+
if (extraBoxUnboxOffsets.size() != 0){
452+
int[] offsetsArray = extraBoxUnboxOffsets.stream().mapToInt(i -> i).toArray();
453+
this.extraBoxUnboxAttribute = new ExtraBoxUnbox(offsetsArray);
454+
if (DEBUG) System.out.println("extraBoxUnboxAttribute: " + extraBoxUnboxAttribute);
455+
visitAttribute(extraBoxUnboxAttribute);
456+
}
431457

432458
}
433459

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package dotty.tools.backend.jvm.attributes;
2+
3+
import scala.tools.asm.Attribute;
4+
import scala.tools.asm.ByteVector;
5+
import scala.tools.asm.ClassReader;
6+
import scala.tools.asm.ClassWriter;
7+
import scala.tools.asm.Label;
8+
9+
/*
10+
ExtraBoxUnbox_attribute {
11+
u2 attribute_name_index;
12+
u4 attribute_length;
13+
u2 count;
14+
u2 offsets[count];
15+
}
16+
*/
17+
public class ExtraBoxUnbox extends Attribute {
18+
public final int[] offsets;
19+
20+
public ExtraBoxUnbox(int[] offsets) {
21+
super("ExtraBoxUnbox");
22+
this.offsets = offsets;
23+
}
24+
25+
@Override
26+
public boolean isUnknown() {
27+
return false;
28+
}
29+
30+
@Override
31+
public Attribute read(ClassReader cr, int off, int len, char[] buf, int codeOff, Label[] labels) {
32+
int cur = off;
33+
int count = cr.readUnsignedShort(cur);
34+
cur += 2;
35+
int[] offs = new int[count];
36+
for (int i = 0; i < count; i++) {
37+
offs[i] = cr.readUnsignedShort(cur);
38+
cur += 2;
39+
}
40+
return new ExtraBoxUnbox(offs);
41+
}
42+
43+
@Override
44+
public ByteVector write(ClassWriter cw, byte[] code, int len, int maxStack, int maxLocals) {
45+
ByteVector bv = new ByteVector();
46+
bv.putShort(offsets.length);
47+
for (int offset : offsets) {
48+
bv.putShort(offset);
49+
}
50+
return bv;
51+
}
52+
}

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,10 @@ class Definitions {
328328
}
329329
def ObjectType: TypeRef = ObjectClass.typeRef
330330

331+
@tu lazy val ObjectAnySymbol: TypeSymbol =
332+
newPermanentSymbol(ScalaRuntimePackageClass, "ObjectAny".toTypeName, EmptyFlags, TypeAlias(ObjectType)).entered
333+
def ObjectAnyType : TypeRef = ObjectAnySymbol.typeRef
334+
331335
/** A type alias of Object used to represent any reference to Object in a Java
332336
* signature, the secret sauce is that subtype checking treats it specially:
333337
*

compiler/src/dotty/tools/dotc/core/TypeErasure.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -635,10 +635,19 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
635635
tp
636636
case tp: TypeRef =>
637637
val sym = tp.symbol
638-
if !sym.isClass then this(checkedSuperType(tp))
638+
if !sym.isClass then
639+
val erasedUnderlying = this(checkedSuperType(tp))
640+
if isSymbol && sym.isTypeParam && (erasedUnderlying eq defn.ObjectType) then
641+
defn.ObjectAnyType
642+
else
643+
erasedUnderlying
639644
else if semiEraseVCs && sym.isDerivedValueClass then eraseDerivedValueClass(tp)
640645
else if defn.isSyntheticFunctionClass(sym) then defn.functionTypeErasure(sym)
641646
else eraseNormalClassRef(tp)
647+
//if (typeRefTp == defn.ObjectType) then defn.ObjectAnyType else typeRefTp
648+
case tp: TypeParamRef =>
649+
val erasedUnderlying = this(tp.underlying)
650+
if isSymbol && (erasedUnderlying eq defn.ObjectType) then defn.ObjectAnyType else erasedUnderlying
642651
case tp: AppliedType =>
643652
val tycon = tp.tycon
644653
if (tycon.isRef(defn.ArrayClass)) eraseArray(tp)

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ import dotty.tools.dotc.transform.TreeExtractors.BinaryOp
3838
import dotty.tools.dotc.transform.InstructionTypeArguments
3939
import dotty.tools.dotc.transform.InvokeReturnType
4040
import dotty.tools.dotc.transform.MethodParameterReturnType
41+
import dotty.tools.dotc.transform.NoBoxingNeeded
42+
import dotty.tools.dotc.transform.NoUnboxingNeeded
4143

4244
class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
4345

@@ -467,6 +469,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
467469

468470
toTextLocal(expr) ~ "." ~ selectorsText
469471

472+
(if tree.hasAttachment(NoBoxingNeeded) then Str("NoBoxingNeeded ") else Str("")) ~
473+
(if tree.hasAttachment(NoUnboxingNeeded) then Str("NoUnboxingNeeded ") else Str("")) ~
470474
(if tree.hasAttachment(InvokeReturnType) then Str("InvokeReturnType("+tree.attachment(InvokeReturnType).toString + ") ") else Str("")) ~
471475
(if tree.hasAttachment(InstructionTypeArguments) then Str("InstructionTypeArguments("+tree.attachment(InstructionTypeArguments).toString + ") ") else Str("")) ~
472476
(if tree.hasAttachment(MethodParameterReturnType) then Str("MethodParameterReturnType("+tree.attachment(MethodParameterReturnType).toString + ") ") else Str("")) ~

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,9 @@ class Erasure extends Phase with DenotTransformer {
204204
}
205205
}
206206

207+
object NoBoxingNeeded extends util.Property.StickyKey[String]
208+
object NoUnboxingNeeded extends util.Property.StickyKey[String]
209+
207210
object Erasure {
208211
import tpd.*
209212
import TypeTestsCasts.*
@@ -243,6 +246,21 @@ object Erasure {
243246

244247
object Boxing:
245248

249+
def isObjectAny(t: Type)(using Context): Boolean =
250+
t.widenDealias.typeSymbol == defn.ObjectAnySymbol
251+
252+
def maybeMarkBox(tree: Tree, tp: Type)(using Context): Tree =
253+
if (isObjectAny(tp)) then
254+
println(" MarkBoxing: marking box for "+ tp)
255+
tree.withAttachment(NoBoxingNeeded, tp.typeSymbol.name.toString)
256+
else tree
257+
258+
def maybeMarkUnbox(tree: Tree, tp: Type)(using Context): Tree =
259+
if (isObjectAny(tp)) then
260+
println(" MarkBoxing: marking unbox for "+ tp)
261+
tree.withAttachment(NoUnboxingNeeded, tp.typeSymbol.name.toString)
262+
else tree
263+
246264
def isUnbox(sym: Symbol)(using Context): Boolean =
247265
sym.name == nme.unbox && sym.owner.linkedClass.isPrimitiveValueClass
248266

@@ -372,6 +390,10 @@ object Erasure {
372390
case _ =>
373391
if (pt.isPrimitiveValueType)
374392
primitiveConversion(tree, pt.classSymbol)
393+
// cast tree type to pt, if pt is ObjectAny, do not inject a cast
394+
else if isObjectAny(pt) && !tree.tpe.widen.isPrimitiveValueType then
395+
println(" MarkBoxing: skipping cast to ObjectAny for "+ pt + " from tree "+ tree.show)
396+
tree.withType(pt)
375397
else
376398
tree.asInstance(pt)
377399
}
@@ -403,10 +425,15 @@ object Erasure {
403425
adaptToType(box(tree), pt)
404426
else if (pt.isErasedValueType)
405427
adaptToType(unbox(tree, pt), pt)
428+
// pt: target type
406429
else if (tpw.isPrimitiveValueType && !pt.isPrimitiveValueType)
407-
adaptToType(box(tree), pt)
430+
//adaptToType(box(tree), pt)
431+
val boxed = box(tree)
432+
adaptToType(maybeMarkBox(boxed, pt), pt)
408433
else if (pt.isPrimitiveValueType && !tpw.isPrimitiveValueType)
409-
adaptToType(unbox(tree, pt), pt)
434+
//adaptToType(unbox(tree, pt), pt)
435+
val unboxed = unbox(tree, pt)
436+
adaptToType(maybeMarkUnbox(unboxed, tree.tpe), pt)
410437
else
411438
cast(tree, pt)
412439
end adaptToType

test-bytecodegen/bcGen/Classes/Pair.scala

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28,44 +28,43 @@ object testPair {
2828
val v = passGenericPair[Int, String](intRefPair)
2929
println(v)
3030
}
31-
}
3231

33-
// @main def testPair2(): Unit = {
34-
// intRefPair.setKey(2)
35-
// intRefPair.setValue("two")
36-
// println(s"Updated Key: ${intRefPair.getKey}, Updated Value: ${intRefPair.getValue}")
37-
// }
32+
@main def testPair2(): Unit = {
33+
intRefPair.setKey(2)
34+
intRefPair.setValue("two")
35+
println(s"Updated Key: ${intRefPair.getKey}, Updated Value: ${intRefPair.getValue}")
36+
}
3837

39-
// @main def testPair3(): Unit = {
40-
// println(s"Key: ${doubleLongPair.getKey}, Value: ${doubleLongPair.getValue}")
41-
// }
38+
@main def testPair3(): Unit = {
39+
println(s"Key: ${doubleLongPair.getKey}, Value: ${doubleLongPair.getValue}")
40+
}
4241

43-
// @main def testPair4(): Unit = {
44-
// doubleLongPair.setKey(6.28)
45-
// doubleLongPair.setValue(84L)
46-
// println(s"Updated Key: ${doubleLongPair.getKey}, Updated Value: ${doubleLongPair.getValue}")
47-
// }
42+
@main def testPair4(): Unit = {
43+
doubleLongPair.setKey(6.28)
44+
doubleLongPair.setValue(84L)
45+
println(s"Updated Key: ${doubleLongPair.getKey}, Updated Value: ${doubleLongPair.getValue}")
46+
}
4847

4948

5049

51-
// @main def testPair6(): Unit = {
52-
// val v = passGenericPair[Double, Long](doubleLongPair)
53-
// println(v)
54-
// }
50+
@main def testPair6(): Unit = {
51+
val v = passGenericPair[Double, Long](doubleLongPair)
52+
println(v)
53+
}
5554

56-
// @main def testPair7(): Unit = {
57-
// println(s"Key: ${anyAnyPair.getKey}, Value: ${anyAnyPair.getValue}")
58-
// }
55+
@main def testPair7(): Unit = {
56+
println(s"Key: ${anyAnyPair.getKey}, Value: ${anyAnyPair.getValue}")
57+
}
5958

60-
// @main def testPair8(): Unit = {
61-
// anyAnyPair.setKey(123)
62-
// anyAnyPair.setValue("newValue")
63-
// println(s"Updated Key: ${anyAnyPair.getKey}, Updated Value: ${anyAnyPair.getValue}")
64-
// }
59+
@main def testPair8(): Unit = {
60+
anyAnyPair.setKey(123)
61+
anyAnyPair.setValue("newValue")
62+
println(s"Updated Key: ${anyAnyPair.getKey}, Updated Value: ${anyAnyPair.getValue}")
63+
}
6564

66-
// @main def testPair9(): Unit = {
67-
// val v = passGenericPair[Any, Any](anyAnyPair)
68-
// println(v)
69-
// }
65+
@main def testPair9(): Unit = {
66+
val v = passGenericPair[Any, Any](anyAnyPair)
67+
println(v)
68+
}
7069

71-
// }
70+
}

0 commit comments

Comments
 (0)