diff options
author | Adriaan Moors <adriaan@lightbend.com> | 2016-08-19 11:07:00 -0700 |
---|---|---|
committer | Adriaan Moors <adriaan@lightbend.com> | 2016-08-29 09:54:08 +0200 |
commit | b2e0911ded57f33e9600c6337a307c0fe1877eff (patch) | |
tree | f7b25cd3240a85cd2c16b68ca5d62fdba1c84ab7 /test/files/neg/t6446-show-phases.check | |
parent | c3b5f1bab3fc65296fcc596f3378ff940b4fb92c (diff) | |
download | scala-b2e0911ded57f33e9600c6337a307c0fe1877eff.tar.gz scala-b2e0911ded57f33e9600c6337a307c0fe1877eff.tar.bz2 scala-b2e0911ded57f33e9600c6337a307c0fe1877eff.zip |
Fields does bitmaps & synch for lazy vals & modules
Essentially, we fuse mixin and lazyvals into the fields phase.
With fields mixing in trait members into subclasses, we
have all info needed to compute bitmaps, and thus we can
synthesize the synchronisation logic as well.
By doing this before erasure we get better signatures,
and before specialized means specialized lazy vals work now.
Mixins is now almost reduced to its essence: implementing
super accessors and forwarders. It still synthesizes
accessors for param accessors and early init trait vals.
Concretely, trait lazy vals are mixed into subclasses
with the needed synchronization logic in place, as do
lazy vals in classes and methods. Similarly, modules
are initialized using double checked locking.
Since the code to initialize a module is short,
we do not emit compute methods for modules (anymore).
For simplicity, local lazy vals do not get a compute method either.
The strange corner case of constant-typed final lazy vals
is resolved in favor of laziness, by no longer assigning
a constant type to a lazy val (see widenIfNecessary in namers).
If you explicitly ask for something lazy, you get laziness;
with the constant-typedness implicit, it yields to the
conflicting `lazy` modifier because it is explicit.
Co-Authored-By: Lukas Rytz <lukas@lightbend.com>
Fixes scala/scala-dev#133
Inspired by dotc, desugar a local `lazy val x = rhs` into
```
val x$lzy = new scala.runtime.LazyInt()
def x(): Int = {
x$lzy.synchronized {
if (!x$lzy.initialized) {
x$lzy.initialized = true
x$lzy.value = rhs
}
x$lzy.value
}
}
```
Note that the 2.11 decoding (into a local variable and a bitmap) also
creates boxes for local lazy vals, in fact two for each lazy val:
```
def f = {
lazy val x = 0
x
}
```
desugars to
```
public int f() {
IntRef x$lzy = IntRef.zero();
VolatileByteRef bitmap$0 = VolatileByteRef.create((byte)0);
return this.x$1(x$lzy, bitmap$0);
}
private final int x$lzycompute$1(IntRef x$lzy$1, VolatileByteRef bitmap$0$1) {
C c = this;
synchronized (c) {
if ((byte)(bitmap$0$1.elem & 1) == 0) {
x$lzy$1.elem = 0;
bitmap$0$1.elem = (byte)(bitmap$0$1.elem | 1);
}
return x$lzy$1.elem;
}
}
private final int x$1(IntRef x$lzy$1, VolatileByteRef bitmap$0$1) {
return (byte)(bitmap$0$1.elem & 1) == 0 ?
this.x$lzycompute$1(x$lzy$1, bitmap$0$1) : x$lzy$1.elem;
}
```
An additional problem with the above encoding is that the `lzycompute`
method synchronizes on `this`. In connection with the new lambda
encoding that no longer generates anonymous classes, captured lazy vals
no longer synchronize on the lambda object.
The new encoding solves this problem (scala/scala-dev#133)
by synchronizing on the lazy holder.
Currently, we don't exploit the fact that the initialized field
is `@volatile`, because it's not clear the performance is needed
for local lazy vals (as they are not contended, and as soon as
the VM warms up, biased locking should deal with that)
Note, be very very careful when moving to double-checked locking,
as this needs a different variation than the one we use for
class-member lazy vals. A read of a volatile field of a class
does not necessarily impart any knowledge about a "subsequent" read
of another non-volatile field of the same object. A pair of
volatile reads and write can be used to implement a lock, but it's
not clear if the complexity is worth an unproven performance gain.
(Once the performance gain is proven, let's change the encoding.)
- don't explicitly init bitmap in bytecode
- must apply method to () explicitly after uncurry
Diffstat (limited to 'test/files/neg/t6446-show-phases.check')
-rw-r--r-- | test/files/neg/t6446-show-phases.check | 19 |
1 files changed, 9 insertions, 10 deletions
diff --git a/test/files/neg/t6446-show-phases.check b/test/files/neg/t6446-show-phases.check index 03f8273c17..cf8595db5d 100644 --- a/test/files/neg/t6446-show-phases.check +++ b/test/files/neg/t6446-show-phases.check @@ -10,18 +10,17 @@ superaccessors 6 add super accessors in traits and nested classes pickler 8 serialize symbol tables refchecks 9 reference/override checking, translate nested objects uncurry 10 uncurry, translate function values to anonymous classes - fields 11 synthesize accessors and fields + fields 11 synthesize accessors and fields, including bitmaps for la... tailcalls 12 replace tail calls by jumps specialize 13 @specialized-driven class and method specialization explicitouter 14 this refs to outer pointers erasure 15 erase types, add interfaces for traits posterasure 16 clean up erased inline classes - lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs - lambdalift 18 move nested functions to top level - constructors 19 move field definitions into constructors - flatten 20 eliminate inner classes - mixin 21 mixin composition - cleanup 22 platform-specific cleanups, generate reflective calls - delambdafy 23 remove lambdas - jvm 24 generate JVM bytecode - terminal 25 the last phase during a compilation run + lambdalift 17 move nested functions to top level + constructors 18 move field definitions into constructors + flatten 19 eliminate inner classes + mixin 20 mixin composition + cleanup 21 platform-specific cleanups, generate reflective calls + delambdafy 22 remove lambdas + jvm 23 generate JVM bytecode + terminal 24 the last phase during a compilation run |