-
Notifications
You must be signed in to change notification settings - Fork 17
Running | Running the Plugin
Note: This description corresponds to the version of the Miniboxing plugin in the DRT Virtual Machine. In the virtual machine, the minibooxing plugin is installed in ~/Workspace/miniboxing-plugin
.
The following introduction requires some familiarity with erased generics and specialization, which you can get by skimming through this presentation. The full description of the Miniboxing transformation is given in a OOPSLA'13 paper.
To start, please launch the Terminal
form the Links
directory on the desktop. The terminal allows you to access the mb-scalac
and mb-scala
scripts, which are the equivalents of scalac
and scala
with the miniboxing plugin activated.
When you start the Terminal
, you should be able to fire up scalac
(the scala compiler), scala
(the scala interpreter and executor) and their miniboxing-enabled counterparts:
acc@acc-vm:~$ scala # same for mb-scala, you won't notice the difference
Welcome to Scala version 2.11.1 (OpenJDK Client VM, Java 1.7.0_09).
Type in expressions to have them evaluated.
Type :help for more information.
scala>
acc@acc-vm:~$ scalac # same for mb-scala, you won't notice the difference
Usage: scalac <options> <source files>
...
Before we start, please update the project, as there's always something we overlooked or could be improved:
acc@acc-vm:~$ cd Workspace/miniboxing-plugin/
acc@acc-vm:~/Workspace/miniboxing-plugin$ git pull origin wip
...
acc@acc-vm:~/Workspace/miniboxing-plugin$ sbt clean compile package
...
Now, let us create a small program to use miniboxing on:
acc@acc-vm:~$ cd ~/Workspace/miniboxing-plugin/sandbox
acc@acc-vm:~/Workspace/miniboxing-plugin/sandbox$
Now use your favorite editor to input the following code in a file named C.scala
:
class C[@miniboxed T](val t: T)
After you saved the file, return to the prompt and type:
$ mb-scalac -P:minibox:log C.scala
Specializing class C...
// interface:
abstract trait C[T] extends Object {
val t(): T
val t_J(val T_TypeTag: Byte): T @storage[Long]
}
// specialized class:
class C_J[Tsp] extends C[Tsp] {
def <init>(val C_J|T_TypeTag: Byte,t: Tsp @storage[Long]): C_J[Tsp] // is a specialized implementation of constructor C
private[this] val C_J|T_TypeTag: Byte // no info
private[this] val t: Tsp @storage[Long] // is a specialized implementation of value t
val t(): Tsp // is a forwarder to the specialized member
val t_J(val T_TypeTag: Byte): Tsp @storage[Long] // is a setter or getter for value t
}
// specialized class:
class C_L[Tsp] extends C[Tsp] {
def <init>(t: Tsp): C_L[Tsp] // is a specialized implementation of constructor C
private[this] val t: Tsp // is a specialized implementation of value t
val t(): Tsp // is a setter or getter for value t
val t_J(val T_TypeTag: Byte): Tsp @storage[Long] // is a forwarder to the specialized member
}
Here we see the interface C[T]
, which is the top of the hierarchy and its two miniboxed variants, C_J
for primitive types and C_L
for reference types. One can easily spot a difference between the two: in C_J
there are two fields, t
of type Tsp @storage[Long]
and T_TypeTag
of type Byte. These correspond to the miniboxed value. As we will see later, Tsp @storage[Long]
will be further transformed to Long
. On the other hand, C_L
only contains a reference of type Tsp
. In the attached comments, we can see exactly what each member is supposed to do. This comes from the way the specialization and miniboxing transformations work: they first record the metadata for the transformation and only later perform the actual tree rewriting. But making sense of this metadata can be tricky: in each class we have two members called t. To overcome this, we can use the -uniqid
option, which assigns a unique id to each symbol:
$ mb-scalac -P:minibox:log C.scala -uniqid
Specializing class C#7762...
// interface:
abstract trait C#7762[T#7774] extends Object#124 {
val t#7861(): T#7774
val t_J#15701(val T_TypeTag#15702: Byte#2435): T#7774 @storage#15699[Long#2321]
}
// specialized class:
class C_J#15709[Tsp#15710] extends C#7762[Tsp#15710] {
def <init>#15716(val C_J|T_TypeTag#15720: Byte#2435,t#15719: Tsp#15710 @storage#15699[Long#2321]): C_J#15709[Tsp#15710] // is a specialized implementation of constructor C#7863
private[this] val C_J|T_TypeTag#15711: Byte#2435 // no info
private[this] val t#15713: Tsp#15710 @storage#15699[Long#2321] // is a specialized implementation of value t#7862
val t#15712(): Tsp#15710 // is a forwarder to the specialized member
val t_J#15714(val T_TypeTag#15715: Byte#2435): Tsp#15710 @storage#15699[Long#2321] // is a setter or getter for value t#15713
}
// specialized class:
class C_L#15729[Tsp#15730] extends C#7762[Tsp#15730] {
def <init>#15735(t#15737: Tsp#15730): C_L#15729[Tsp#15730] // is a specialized implementation of constructor C#7863
private[this] val t#15732: Tsp#15730 // is a specialized implementation of value t#7862
val t#15731(): Tsp#15730 // is a setter or getter for value t#15732
val t_J#15733(val T_TypeTag#15734: Byte#2435): Tsp#15730 @storage#15699[Long#2321] // is a forwarder to the specialized member
}
With -uniqid
, references became explicit: each symbol in the compiler receives a unique id which is displayed after its name. Now we can see exactly which name refers to which symbol. So far, we have the two classes and we can see both define t
and t_J
. In the primitive type class C_J
, def t_J
is a getter for the miniboxed value and def t
is a forwarder which boxes. In the other class, C_L
we have def t
as a getter while def t_J
does unboxing. We will see this pattern recurring many times in the transformation.
So far we've seen the metadata, which miniboxing displays for us when adding the -P:minibox:log
argument. But we might also want to see the intermediate representation, which is build based on the metadata. To do so, we use -Xprint:minibox-inject
:
$ mb-scalac -Xprint:minibox-inject C.scala
[[syntax trees at end of minibox-inject]] // C.scala
package <empty> {
abstract trait C[@miniboxed T] extends Object {
<stable> <accessor> <paramaccessor> def t(): T;
<stable> <accessor> def t_J(T_TypeTag: Byte): T @storage[Long]
};
class C_J[Tsp] extends Object with C[Tsp] {
def <init>(C_J|T_TypeTag: Byte, t: Tsp @storage[Long]): C_J[Tsp] = {
C_J.super.<init>();
()
};
<paramaccessor> private[this] val C_J|T_TypeTag: Byte = _;
<paramaccessor> private[this] val t: Tsp @storage[Long] = _;
<stable> <accessor> <paramaccessor> def t(): Tsp = C_J.this.t_J(C_J.this.C_J|T_TypeTag);
<stable> <accessor> def t_J(T_TypeTag: Byte): Tsp @storage[Long] = C_J.this.t
};
class C_L[Tsp] extends Object with C[Tsp] {
def <init>(t: Tsp): C_L[Tsp] = {
C_L.super.<init>();
()
};
<paramaccessor> private[this] val t: Tsp = _;
<stable> <accessor> <paramaccessor> def t(): Tsp = C_L.this.t;
<stable> <accessor> def t_J(T_TypeTag: Byte): Tsp @storage[Long] = C_L.this.t()
}
}
As promised, the references to Tsp @storage[Long]
are later transformed to Long
:
$ mb-scalac -Xprint:minibox-commit C.scala
[[syntax trees at end of minibox-commit]] // C.scala
package <empty> {
abstract trait C[@miniboxed T] extends Object {
<stable> <accessor> <paramaccessor> def t(): T;
<stable> <accessor> def t_J(T_TypeTag: Byte): Long
};
class C_J[Tsp] extends Object with C[Tsp] {
def <init>(C_J|T_TypeTag: Byte, t: Long): C_J[Tsp] = {
C_J.super.<init>();
()
};
<paramaccessor> private[this] val C_J|T_TypeTag: Byte = _;
<paramaccessor> private[this] val t: Long = _;
<stable> <accessor> <paramaccessor> def t(): Tsp = MiniboxConversions.this.minibox2box[Tsp](C_J.this.t_J(C_J.this.C_J|T_TypeTag), C_J.this.C_J|T_TypeTag);
<stable> <accessor> def t_J(T_TypeTag: Byte): Long = C_J.this.t
};
class C_L[Tsp] extends Object with C[Tsp] {
def <init>(t: Tsp): C_L[Tsp] = {
C_L.super.<init>();
()
};
<paramaccessor> private[this] val t: Tsp = _;
<stable> <accessor> <paramaccessor> def t(): Tsp = C_L.this.t;
<stable> <accessor> def t_J(T_TypeTag: Byte): Long = MiniboxConversions.this.box2minibox_tt[Tsp](C_L.this.t(), T_TypeTag)
}
}
This will be presented in detail in a more advanced tutorial.
You probably noticed the calls to MiniboxConversions.this.box2minibox
and its reverse, MiniboxConversions.this.minibox2box
. These convert from one representation to the other, meaning from a miniboxed value (stored as a long integer) to its actual boxed value.
The compiler flags you may be interested in are:
-Xshow-phases show the compiler phases (notice how mb-scalac includes minibox while scalac does not)
-Xprint:<phase> print trees at the end of <phase>
-uniqid prepend symbol id to the symbol name, to distinguish between symbols
-P:minibox:log log miniboxing signature transformations
-P:minibox:hijack hijack the @specialized(...) notation for miniboxing
-P:minibox:two-way generate variants for long and double instead of just long
Let us now prepare a program that is transformed using miniboxing and we can execute (in a file called MBme.scala
:
class MBme[@miniboxed U, @miniboxed V] {
def clazz = this.getClass.toString
}
object Test {
def main(args: Array[String]): Unit = {
println(new MBme[Int, Int].clazz)
println(new MBme[Int, String].clazz)
println(new MBme[String, Int].clazz)
println(new MBme[String, String].clazz)
}
}
Compile the file with:
mb-scalac MBme.scala
And run the Test class with:
mb-scala Test
The output is:
class MBme_JJ
class MBme_JL
class MBme_LJ
class MBme_LL
As expected, the miniboxing plugin used the most specific variants of the class for each of the instantiations. A more in-depth tutorial of using miniboxing plugin is available on the scala-miniboxing.org
website, but this tutorial will now steer towards the data representation transformations in miniboxing.
You can continue with the following resources: