This stopped working recently, apparently due to obsolete jfrog.org
credentials. Since no-one is actually using these artifacts as far as
we are aware, I'm just disabling the task so the build doesn't
continue to fail.
These expressions are tricky because they rely on invokedynamic, which
normally implies runtime code generation. However, since lambdas
don't actually use the "dynamicness" of invokedynamic, we can convert
them into static calls to synthetic classes at compile time.
Since I had already written code to synthesize such classes in Java
and I didn't want to rewrite it in C++, I needed to add support for
running Java code to the bootimage generator. And since the primary
VM used by the generator is purpose-built to generate AOT-compiled
code for a specific target architecture and is not capable of
generating or running JIT-compiled code for the host architecture, I
added support for loading a second, independent, host-specific VM for
running Java code.
The rest of the patch handles the fact that each method compilation
might cause new, synthetic classes to be created, so we need to make
sure those classes and their methods are included in the final heap
and code images. This required breaking some giant code blocks out of
makeCodeImage into their own methods, which makes the diff look
scarier than it really is.
This allows multiple Avian VMs to share the same process space,
provided they don't try to use functionality that involves global
shared resources (e.g. signal handling).
The main goal here is to avoid making JNI calls from code that really
shouldn't need JNI (e.g. before this patch, ArrayList.add called
Math.max, which called Math.<clinit>, which called Random.<init>,
which called System.currentTimeMillis).
Besides following the "pay for only what you need" principle, this
change ensures we can call LambdaMetaFactory methods during AOT
compilation with a minimal VM (i.e. without compiling in JNI methods
we don't need).
The two big pieces here are basic invokedynamic support and a working
version of LambdaMetaFactory.metafactory. The latter works by
dynamically building a synthetic class with three methods: a static
factory method, a constructor for the factory method to call, and a
method to satisfy the requested interface which defers to the
specified MethodHandle.
This work relies heavily on Avian's specific MethodType and
MethodHandle implementations, which provide extra, non-standard
features to make code generation easier. That means we'll probably
need to use Avian's versions of java.lang.invoke.* even when building
with the OpenJDK or Android class libraries.
This is a bunch of commits squashed into one per Josh's request.
add dynamicTable field
add invokedynamic instruction
add defaultDynamic bootimage field
add dummy invokedynamic support in bootimage-generator
add defaultDynamic thunk
check dynamicTable offset
comment defaultDynamicThunk to fix unused function
comment defaultDynamicThunk to fix unused function
add dynamicTable / dynamicIndex stuff
comment dynamicIndex and dynamicTable
add invokedynamic instruction impl
stub out addDynamic
unstub addDynamic
don't allow tail calls in invokedynamic
implement stub JVM_GetTemporaryDirectory method
(build broken) begin add InvokeDynamicTest
Revert "(build broken) begin add InvokeDynamicTest"
This reverts commit 77f9c54e32ac66d0803eeab93e4a10d3541987a8.
add InternalError
add URLClassPath.c for openjdk-src builds
implement stub JVM_KnownToNotExist and JVM_GetResourceLookupCache methods
intercept open0 / open for openjdk
add basic java/lang/invoke stubs
remove non-public java/lang/invoke classes
fix invokedynamic example building
<wip debugging>
Someone on the Google Groups site asked how to set up an Eclipse
project with Avian classpath. This patch creates the descriptor
that you can you use to do that at `$(build)/eclipse/jdk/avian.ee`.
The descriptor includes the Avian version, platform, architecture,
and build options to allow for multiple versions to exist side by
side. Users can import the descriptor into Eclipse via:
Window >> Preferences >> Java >> Installed JREs >> Add >> Execution
Environment Description
Once the descriptor is imported, Avian can be used just like any other
JVM installation for Eclipse projects. Personally I use this in
conjunction with Eclim to gain code completion for Avian in vim.
The new targets also create symlinks to loosely mimic OpenJDK's
filenames and folder layout:
build/linux-x86_64-tails-continuations/eclipse/jdk/
├── avian.ee
├── bin
│ └── java -> ../../../avian
├── jre
│ └── lib
│ └── rt.jar -> ../../../../classpath.jar
└── src -> ../../classpath
Annoyingly, Eclipse for some reason expects this layout to exist
even though the descriptor format has required parameters for
specifying these locations. I suppose that other software may
look for this "standard" layout in a JVM installation so it may be
generally useful.
These artifacts are only built if the platform is one of `windows`,
`linux`, or `macosx`. The symlinks might not actually work at all on
Windows, I'm not sure how things like cygwin/msys handle that and I
do not have the means to test it. If they do not work a fallback
for windows might be to actually copy the files instead of symlinking.
I realize this can be done outside of the makefile but it seemed
useful to put it here to gain access to the information about the
build location, platform, architecture, and other build options.
For the record, this contribution is my original work and is released
under the same license that Avian uses, found in the license.txt
file in this repository.
In a bootimage=true build, we create allocate certain objects as
"immortal fixies", which means they will never been deallocated at
runtime and should only be visited if/when they point to objects which
might move during garbage collection. However, there was a bug in the
following case:
1. immortal fixie F is updated to point to a movable object M and
thus F is added to the list of fixies to visit during the next minor
collection (but not the next major one, since all reachable objects
are visited during a major collection, and there's no point in
visiting an unreachable object, whereas during a minor collection we
have to visit F because we don't know if it's reachable or not)
2. a major collection occurs, but F is not reachable and thus is not
visited, whereas M is moved
3. a minor collection occurs, and since F is still in the list, it is
visited, but since it contains a stale pointer to M's old location,
we crash
The solution is to ensure unreachable immortal fixies are removed from
the above list after each major collection, thus guaranteeing they
won't be visited on any subsequent collection.
GCC is a lot more sensitive about -Werror=unused-variable, to the
point that stuff declared in header files but unused in a given
compilation unit is flagged. This may be due to the way we're
here's the fix.
At first, it might look like the atomicIncrement operations here,
since they resolve to OSAtomicCompareAndSwap32Barrier, ought to
provide all the memory barrier guarantees we need; however, it turns
out it's not quite sufficient.
Here's why: Apple's OSAtomic*Barrier operations guarantee that memory
operations *before* the atomic op won't be reordered with memory
operations *after* the atomic op - but makes no guarantee that the
atomic op itself won't be reordered with other operations before or
after it. The key is that the atomic operation is not really atomic,
but rather consists of separate read, check and write steps - in a
loop, no less. Here, presumably, the read of t->m->exclusive is
hoisted by the out-of-order processor to between the read and write
steps of the "atomic" increment of t->m->activeCount.
Longer term, it probably makes sense to replace this with the c11
<stdatomic.h> operations or the c++11 <atomic> types. We ought to
actually use the atomic increment operations provided there. As it
is, our atomicIncrement looks substantially less efficient than those,
since it's actually implemented on arm64 as two nested loops (one in
atomicIncrement and one in the compare-and-swap) instead of one. We
should also evaluate whether the various instances of atomic
operations actually need as strong of barriers as we're giving them.
FWIW, the gcc __sync_* builtins seem to have the same problem, at
least on arm64.