class BoxUnbox[BT <: BTypes] extends AnyRef
- Alphabetic
- By Inheritance
- BoxUnbox
- AnyRef
- Any
- by any2stringadd
- by StringFormat
- by Ensuring
- by ArrowAssoc
- Hide All
- Show All
- Public
- All
Instance Constructors
- new BoxUnbox(btypes: BT)
Type Members
- sealed trait BoxConsumer extends AnyRef
- sealed trait BoxCreation extends AnyRef
- trait BoxKind extends AnyRef
-
class
CopyOpsIterator
extends Iterator[AbstractInsnNode]
For a set of box creation operations and a corresponding set of box consumer operations, this iterator returns all copy operations (load, store, dup) that are in between.
-
case class
EscapingConsumer
(consumer: AbstractInsnNode) extends BoxConsumer with Product with Serializable
An unknown box consumer
- case class InstanceCreation (newOp: TypeInsnNode, dupOp: InsnNode, initCall: MethodInsnNode) extends BoxCreation with Product with Serializable
- case class ModuleFactory (moduleLoad: AbstractInsnNode, producer: MethodInsnNode) extends BoxCreation with Product with Serializable
-
case class
ModuleGetter
(moduleLoad: AbstractInsnNode, consumer: MethodInsnNode) extends BoxConsumer with Product with Serializable
An extractor method in a Scala module, e.g.,
Predef.Integer2int
- case class PrimitiveBox (boxedType: Type, boxClass: InternalName) extends BoxKind with Product with Serializable
-
case class
PrimitiveBoxingGetter
(consumer: MethodInsnNode) extends BoxConsumer with Product with Serializable
A getter that boxes the returned value, e.g.,
Tuple2$mcII$sp._1
-
case class
PrimitiveUnboxingGetter
(consumer: MethodInsnNode, unboxedPrimitive: Type) extends BoxConsumer with Product with Serializable
A getter that unboxes the returned value, e.g.,
Tuple2._1$mcI$sp
- case class Ref (boxedType: Type, refClass: InternalName) extends BoxKind with Product with Serializable
- case class StaticFactory (producer: MethodInsnNode, loadInitialValues: Option[List[AbstractInsnNode]]) extends BoxCreation with Product with Serializable
-
case class
StaticGetterOrInstanceRead
(consumer: AbstractInsnNode) extends BoxConsumer with Product with Serializable
Static extractor (BoxesRunTime.unboxToInt) or GETFIELD or getter invocation
-
case class
StaticSetterOrInstanceWrite
(consumer: AbstractInsnNode) extends BoxConsumer with Product with Serializable
PUTFIELD or setter invocation
- case class Tuple (boxedTypes: List[Type], tupleClass: InternalName) extends BoxKind with Product with Serializable
Value Members
-
final
def
!=(arg0: Any): Boolean
Test two objects for inequality.
Test two objects for inequality.
- returns
true
if !(this == that), false otherwise.
- Definition Classes
- AnyRef → Any
-
final
def
##(): Int
Equivalent to
x.hashCode
except for boxed numeric types andnull
.Equivalent to
x.hashCode
except for boxed numeric types andnull
. For numerics, it returns a hash value which is consistent with value equality: if two value type instances compare as true, then ## will produce the same hash value for each of them. Fornull
returns a hashcode wherenull.hashCode
throws aNullPointerException
.- returns
a hash value consistent with ==
- Definition Classes
- AnyRef → Any
- def +(other: String): String
- def ->[B](y: B): (BoxUnbox[BT], B)
-
final
def
==(arg0: Any): Boolean
The expression
x == that
is equivalent toif (x eq null) that eq null else x.equals(that)
.The expression
x == that
is equivalent toif (x eq null) that eq null else x.equals(that)
.- returns
true
if the receiver object is equivalent to the argument;false
otherwise.
- Definition Classes
- AnyRef → Any
-
def
allCreationsConsumers(initialCreation: BoxCreation, boxKind: BoxKind, prodCons: ProdConsAnalyzer): Option[(Set[BoxCreation], Set[BoxConsumer])]
Given a box creations operation
Given a box creations operation
- find all ultimate consumers for the produced value. then:
- for all consumed values, find all producer operations. check that all are box creations
- recurse until reaching a fixpoint
- for all consumed values, find all producer operations. check that all are box creations
Returns a set of box creations and a set of box consumers. Note that the box consumers may contain EscapingConsumers, even if there are multiple box creation operations. The callee will handle this case (and not attempt to eliminate the box).
- find all ultimate consumers for the produced value. then:
-
final
def
asInstanceOf[T0]: T0
Cast the receiver object to be of type
T0
.Cast the receiver object to be of type
T0
.Note that the success of a cast at runtime is modulo Scala's erasure semantics. Therefore the expression
1.asInstanceOf[String]
will throw aClassCastException
at runtime, while the expressionList(1).asInstanceOf[List[String]]
will not. In the latter example, because the type argument is erased as part of compilation it is not possible to check whether the contents of the list are of the requested type.- returns
the receiver object.
- Definition Classes
- Any
- Exceptions thrown
ClassCastException
if the receiver object is not an instance of the erasure of typeT0
.
-
def
boxUnboxElimination(method: MethodNode, owner: InternalName): Boolean
Eliminate box-unbox pairs within
method
.Eliminate box-unbox pairs within
method
. Such appear commonly after closure elimination:def t2 = { val f = (b: Byte, i: Int) => i + b // no specialized variant for this function type f(1, 2) // invokes the generic
apply
}The closure optimizer re-writes the
apply
call toanonfun$adapted
method, which takes boxed arguments. After inlining this method, we getdef t2 = { val a = boxByte(1) val b = boxInteger(2) val r = boxInteger(anonfun$(unboxByte(a), unboxInt(b))) unboxInt(r) }
All these box/unbox operations are eliminated here.
Implementation: for every box operation, find all consumers of the boxed value, then all producers of these consumers, repeat until reaching a fixpoint. If this results in a set of boxing and unboxing operations, the box can be eliminated.
There are two methods for eliminating boxes: M1: If there is a single boxing operation, the boxed value(s) are stored into new local variable(s) at the allocation site. Accesses to the boxed value are re-written to reads / writes of these locals. Advantages:
- supports mutable boxes (IntRef and friends)
- supports eliminating unbox operations even if the box object needs to be created
because it escapes (see E4)
- works by keeping the unboxed value(s) in locals AND the box in its original form
- only for immutable boxes: modifications to the escaped box cannot be applied to the local variable(s) holding the boxed value(s). Restriction:
- does not work if there are multiple boxing operations (see E1)
M2: If there are multiple boxing operations, the boxing operations are simply eliminated, leaving the unboxed value(s) on the stack. Store / load operations that previously acted on the box are adapted to handle the boxed type(s). If the box contains multiple values (or a size-2 value, which doesn't fit into locals that were used for the box), new local slots are used for store / load operations. Restrictions:
- does not support re-writing writes to (mutable) boxes (see E2)
- does not support re-writing reads of boxes that also escape (see E3)
E1: M1 only works if there's a single boxing operation. def e1(b: Boolean) = { val i: Integer = box(10) // 10 is stored into a new local, box operation and i removed val j: Integer = box(20) // 20 is stored into a new local, box operation adn j removed val r = if (b) i else j // loads and stores of the box are eliminated, r no longer exists unbox(r) // cannot rewrite: we don't know which local to load } Note: the example has no write and the box does not escape, so M2 works here.
E2: mutable boxes with multiple boxing operations cannot be eliminated. M1: see E1 M2: cannot replace an
IntRef
on the stack by anInt
value on the stack, an Int on the stack cannot be modified.def e2(b: Boolean) = { val r1 = new IntRef(0) val r2 = new IntRef(1) val modRef = if (b) r1 else r2 modRef.elem += 10 // M1: cannot rewrite: which local to write? same as E1. (if (b) r1 else r2).elem += 10 // M2: cannot change an Int on the stack (r1.elem, r2.elem) }
E3: escaping boxes with multiple boxing operations cannot be rewritten. M1: see E1. M2: at *, instead of an Integer, an Int is on the stack, but the escape method expects an Integer. We cannot just create a box at this point: if there are multiple escapes (or an escape is executed more than once), the difference could be observed (reference equality).
def e3(b: Boolean) = { val i: Integer = box(1) val j: Integer = box(2) escape(if (b) i else j) // * unbox(if (b) i else j) }
E4: M1 supports rewriting unbox operations of immutable boxes that escape def e4 = { val i: Integer = box(10) // 10 is stored into a new local, loaded as argument for the box call escape(i) // not changed, still loads the local i holding the box unbox(i) // rewritten to a pop (of the box) and a load of the local variable }
E4 seems to be a bit of a corner case, but it's necessary to unblock box eliminations with mutual dependencies. Example:
val ((a, b), c) = ((1, 2), 3) a + b + c
generates (after a few cleanups) the following (pseudo-bytecode, ignoring primitive boxing, specialization):
load 1, load 2, new Tuple2 // stack: Tuple2 load 3 // stack: Tuple2; Int val local1 = new Tuple2 val local2 = local1._1.asInstanceOf[Tuple2] val c = local1._2.asInstanceOf[Int] if (local2 == null) throw new MatchError(local1) val a = local2._1 val b = local2._2 a + b + c
In order to eliminate the tuples, we first need to eliminate the outer tuple (stored in local1)
- single box operation, so we use M1
- there are three consumers of the outer tuple:
local1._1
,local1._2
andnew MatchError(local1)
. in the last one, the tuple escapes. - note that the MatchError creation is dead code: local2 is never null. However, our nullness analysis cannot identify this: it does not track nullness through tuple stores and loads.
- if we re-write the non-escaping consumers of the outer tuple, but keep the tuple allocation and the escaping consumer, we get the follwoing:
load 1, load 2 val newLocal1 = new Tuple2; load newLocal1 // stack: Tuple2 val newLocal2 = 3; load newLocal2 // stack: Tuple2; Int val local1 = new Tuple2 val local2 = newLocal1 val c = newLocal2 if (local2 == null) throw new MatchError(local1) val a = local2._1 val b = local2._2 a + b + c
At this point, the nullness analysis sees that
local2 == null
is false, dead code elimination removes thethrow new MatchError(local1)
. After eliminating the allocation of the outer tuple, the inner tuple (stored in newLocal1) can also be eliminated.Special case for tuples wrt specialization: a tuple getter may box or unbox the value stored in the tuple: calling
_1
on aTuple2$mcII$sp
boxes the primitive Int stored in the tuple. Similarly, calling_1$mcI$sp
on a non-specializedTuple2
unboxes the Integer in the tuple. When eliminating such getters, we have to introduce appropriate box / unbox calls.TODO: add new calls (box / unbox) to the call graph (not urgent) TODO: update the call graph because stack heights change (not urgent). this may also affect other optimizations, we ignored the issue so far. check how stack heights stored in the call graph are used. Note: these tasks are not urgent because the call graph is not currently used during / after method-local optimizations, only before to perform inlining and closure rewriting.
- val btypes: BT
-
def
checkCopyOpReplacements(initialProds: Set[BoxCreation], finalCons: Set[BoxConsumer], valueTypes: List[Type], nextLocal: Int, prodCons: ProdConsAnalyzer): Option[(Map[AbstractInsnNode, List[AbstractInsnNode]], Int, Map[Int, Type])]
Takes two sets
initialProds
andfinalCons
such that all boxes produced by the first set are only consumed by an operation in the second set.Takes two sets
initialProds
andfinalCons
such that all boxes produced by the first set are only consumed by an operation in the second set.Returns a map that replaces copy operations (ALOAD / ASTORE) between the producers and consumers with corresponding copy operations for the values stored in the box. The returned
Int
value returns the next free local variable slot.Examples:
- for an Integer box, an ASTORE x is simply replaced by ISTORE x
- for a pair of two references, an ASTORE x is replaced by
ASTORE x1; ASTORE x2
where x1 and x2 are fresh locals
Not all copy operations can be supported: DUP only works for single-value boxes, the more exotic copy operations (DUP2_X2) are not supported (note that Scalac never emits them). If a copy operation cannot be replaced, this method returns
None
. -
def
clone(): AnyRef
Create a copy of the receiver object.
Create a copy of the receiver object.
The default implementation of the
clone
method is platform dependent.- returns
a copy of the receiver object.
- Attributes
- protected[java.lang]
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
- Note
not specified by SLS as a member of AnyRef
- def ensuring(cond: (BoxUnbox[BT]) ⇒ Boolean, msg: ⇒ Any): BoxUnbox[BT]
- def ensuring(cond: (BoxUnbox[BT]) ⇒ Boolean): BoxUnbox[BT]
- def ensuring(cond: Boolean, msg: ⇒ Any): BoxUnbox[BT]
- def ensuring(cond: Boolean): BoxUnbox[BT]
-
final
def
eq(arg0: AnyRef): Boolean
Tests whether the argument (
that
) is a reference to the receiver object (this
).Tests whether the argument (
that
) is a reference to the receiver object (this
).The
eq
method implements an equivalence relation on non-null instances ofAnyRef
, and has three additional properties:- It is consistent: for any non-null instances
x
andy
of typeAnyRef
, multiple invocations ofx.eq(y)
consistently returnstrue
or consistently returnsfalse
. - For any non-null instance
x
of typeAnyRef
,x.eq(null)
andnull.eq(x)
returnsfalse
. null.eq(null)
returnstrue
.
When overriding the
equals
orhashCode
methods, it is important to ensure that their behavior is consistent with reference equality. Therefore, if two objects are references to each other (o1 eq o2
), they should be equal to each other (o1 == o2
) and they should hash to the same value (o1.hashCode == o2.hashCode
).- returns
true
if the argument is a reference to the receiver object;false
otherwise.
- Definition Classes
- AnyRef
- It is consistent: for any non-null instances
-
def
equals(arg0: Any): Boolean
The equality method for reference types.
-
def
finalize(): Unit
Called by the garbage collector on the receiver object when there are no more references to the object.
Called by the garbage collector on the receiver object when there are no more references to the object.
The details of when and if the
finalize
method is invoked, as well as the interaction betweenfinalize
and non-local returns and exceptions, are all platform dependent.- Attributes
- protected[java.lang]
- Definition Classes
- AnyRef
- Annotations
- @throws( classOf[java.lang.Throwable] )
- Note
not specified by SLS as a member of AnyRef
- def formatted(fmtstr: String): String
-
final
def
getClass(): Class[_]
Returns the runtime class representation of the object.
Returns the runtime class representation of the object.
- returns
a class object corresponding to the runtime type of the receiver.
- Definition Classes
- AnyRef → Any
-
def
hashCode(): Int
The hashCode method for reference types.
-
final
def
isInstanceOf[T0]: Boolean
Test whether the dynamic type of the receiver object is
T0
.Test whether the dynamic type of the receiver object is
T0
.Note that the result of the test is modulo Scala's erasure semantics. Therefore the expression
1.isInstanceOf[String]
will returnfalse
, while the expressionList(1).isInstanceOf[List[String]]
will returntrue
. In the latter example, because the type argument is erased as part of compilation it is not possible to check whether the contents of the list are of the specified type.- returns
true
if the receiver object is an instance of erasure of typeT0
;false
otherwise.
- Definition Classes
- Any
-
final
def
ne(arg0: AnyRef): Boolean
Equivalent to
!(this eq that)
.Equivalent to
!(this eq that)
.- returns
true
if the argument is not a reference to the receiver object;false
otherwise.
- Definition Classes
- AnyRef
-
final
def
notify(): Unit
Wakes up a single thread that is waiting on the receiver object's monitor.
Wakes up a single thread that is waiting on the receiver object's monitor.
- Definition Classes
- AnyRef
- Note
not specified by SLS as a member of AnyRef
-
final
def
notifyAll(): Unit
Wakes up all threads that are waiting on the receiver object's monitor.
Wakes up all threads that are waiting on the receiver object's monitor.
- Definition Classes
- AnyRef
- Note
not specified by SLS as a member of AnyRef
-
final
def
synchronized[T0](arg0: ⇒ T0): T0
- Definition Classes
- AnyRef
-
def
toString(): String
Creates a String representation of this object.
Creates a String representation of this object. The default representation is platform dependent. On the java platform it is the concatenation of the class name, "@", and the object's hashcode in hexadecimal.
- returns
a String representation of the object.
- Definition Classes
- AnyRef → Any
-
final
def
wait(): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
-
final
def
wait(arg0: Long, arg1: Int): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
-
final
def
wait(arg0: Long): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
- def →[B](y: B): (BoxUnbox[BT], B)
- object BoxKind
- object PrimitiveBox extends Serializable
- object Ref extends Serializable
- object Tuple extends Serializable
The Scala compiler API.
The following resources are useful for Scala plugin/compiler development: