first pass at minimal invokedynamic support for Java 8 lambdas

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>
This commit is contained in:
joshuawarner32@gmail.com 2015-05-03 20:57:38 -06:00 committed by Joel Dice
parent 4093036e6f
commit 792684b935
20 changed files with 442 additions and 87 deletions

View File

@ -0,0 +1,21 @@
/* Copyright (c) 2008-2015, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
package java.lang;
public class InternalError extends VirtualMachineError {
public InternalError(String message) {
super(message);
}
public InternalError() {
this(null);
}
}

View File

@ -0,0 +1,21 @@
/* Copyright (c) 2008-2015, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
package java.lang;
public class TypeNotPresentException extends Exception {
public TypeNotPresentException(String message) {
super(message);
}
public TypeNotPresentException() {
this(null);
}
}

View File

@ -0,0 +1,4 @@
package java.lang.invoke;
public abstract class CallSite {
}

View File

@ -0,0 +1,9 @@
package java.lang.invoke;
public class LambdaConversionException extends java.lang.Exception {
public LambdaConversionException() { throw new RuntimeException(); }
public LambdaConversionException(String s) { throw new RuntimeException(); }
public LambdaConversionException(String s, Throwable th) { throw new RuntimeException(); }
public LambdaConversionException(Throwable th) { throw new RuntimeException(); }
public LambdaConversionException(String s, Throwable th, boolean b, boolean b2) { throw new RuntimeException(); }
}

View File

@ -0,0 +1,5 @@
package java.lang.invoke;
public class LambdaMetafactory {
public static CallSite metafactory(MethodHandles.Lookup l, String s, MethodType mt, MethodType mt2, MethodHandle mh, MethodType mt3) throws LambdaConversionException { throw new RuntimeException(); }
}

View File

@ -0,0 +1,3 @@
package java.lang.invoke;
public abstract class MethodHandle {
}

View File

@ -0,0 +1,5 @@
package java.lang.invoke;
public class MethodHandles {
public static class Lookup {
}
}

View File

@ -0,0 +1,3 @@
package java.lang.invoke;
public final class MethodType implements java.io.Serializable {
}

43
invoke.sh Normal file
View File

@ -0,0 +1,43 @@
#!/bin/bash
unzip -l /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/lib/rt.jar | \
grep java/lang/invoke | \
grep -E '\$' | \
awk '{print $4}' | \
sed -E -e 's/\.class//g' | \
while read path; do
echo $path
name=$(echo $path | sed -e 's|/|.|g')
if javap -public $name | grep -q "public.*interface"; then
javap -public $name | grep -v "Compiled from" | \
sed -E \
-e 's/java.lang.invoke.//g' \
-e 's/java.lang.Object/Object o/g' \
-e 's/java.lang.reflect.Method/Method o/g' \
-e 's/java.lang.String/String s/g' \
-e 's/java.lang.Throwable/Throwable/g' \
-e 's/int/int i/g' \
-e 's/byte/byte b/g' \
-e 's/boolean/boolean b/g' \
-e 's/MethodType/MethodType mt/g' \
-e 's/MethodHandle/MethodHandle mh/g' \
-e 's/java.lang.Class\<\?\>/Class\<\?\> c/g' \
-e 's/;/ { throw new RuntimeException(); }/g' \
-e 's/public String s/public String/g' \
-e 's/public static String s/public static String/g' \
-e 's/public abstract MethodHandle mh/public abstract MethodHandle/g' \
-e 's/public abstract MethodHandle mh/public abstract MethodHandle/g' \
-e 's/public boolean b/public boolean/g' \
-e 's/public int i/public int/g' \
-e 's/public static final int i/public static final int/g' \
-e 's/public byte b/public byte/g' \
-e 's/public Object o/public Object/g' \
-e 's/public MethodType mt/public MethodType/g' \
-e 's/public MethodHandle mh/public MethodHandle/g' \
-e 's/Object o\.\.\./Object... o/g' \
-e 's/public final native Object o/public final native Object/g' \
-e 's/public Class<?> c/public Class<?>/g' \
> classpath/${path}.java
fi
done

View File

@ -1689,7 +1689,7 @@ $(classpath-dep): $(classpath-sources) $(classpath-jar-dep)
classes="$(shell $(MAKE) -s --no-print-directory build=$(build) \
$(classpath-classes) arch=$(build-arch) platform=$(bootimage-platform))"; \
if [ -n "$${classes}" ]; then \
$(javac) -source 1.6 -target 1.6 \
$(javac) -source 1.8 -target 1.8 \
-d $(classpath-build) -bootclasspath $(boot-classpath) \
$${classes}; fi
@touch $(@)
@ -1765,7 +1765,7 @@ $(test-dep): $(test-sources) $(test-library)
@mkdir -p $(test-build)
files="$(shell $(MAKE) -s --no-print-directory build=$(build) $(test-classes))"; \
if test -n "$${files}"; then \
$(javac) -source 1.6 -target 1.6 \
$(javac) -source 1.8 -target 1.8 \
-classpath $(test-build) -d $(test-build) -bootclasspath $(boot-classpath) $${files}; \
fi
$(javac) -source 1.2 -target 1.1 -XDjsrlimit=0 -d $(test-build) \
@ -1777,7 +1777,7 @@ $(test-extra-dep): $(test-extra-sources)
@mkdir -p $(test-build)
files="$(shell $(MAKE) -s --no-print-directory build=$(build) $(test-extra-classes))"; \
if test -n "$${files}"; then \
$(javac) -source 1.6 -target 1.6 \
$(javac) -source 1.8 -target 1.8 \
-d $(test-build) -bootclasspath $(boot-classpath) $${files}; \
fi
@touch $(@)

View File

@ -49,6 +49,7 @@ openjdk-sources = \
$(openjdk-src)/share/native/java/util/zip/zip_util.c \
$(openjdk-src)/share/native/sun/management/VMManagementImpl.c \
$(openjdk-src)/share/native/sun/misc/GC.c \
$(openjdk-src)/share/native/sun/misc/URLClassPath.c \
$(openjdk-src)/share/native/sun/misc/MessageUtils.c \
$(openjdk-src)/share/native/sun/misc/NativeSignalHandler.c \
$(openjdk-src)/share/native/sun/misc/Signal.c \
@ -121,6 +122,7 @@ openjdk-headers-classes = \
sun.misc.VM \
sun.misc.VMSupport \
sun.misc.Version \
sun.misc.URLClassPath \
sun.net.spi.DefaultProxySelector \
sun.nio.ch.FileKey \
sun.nio.ch.FileChannelImpl \

View File

@ -146,6 +146,7 @@ enum OpCode {
imul = 0x68,
ineg = 0x74,
instanceof = 0xc1,
invokedynamic = 0xba,
invokeinterface = 0xb9,
invokespecial = 0xb7,
invokestatic = 0xb8,

View File

@ -30,7 +30,8 @@
#define TARGET_THREAD_HEAPIMAGE 2312
#define TARGET_THREAD_CODEIMAGE 2320
#define TARGET_THREAD_THUNKTABLE 2328
#define TARGET_THREAD_STACKLIMIT 2376
#define TARGET_THREAD_DYNAMICTABLE 2336
#define TARGET_THREAD_STACKLIMIT 2384
#elif(TARGET_BYTES_PER_WORD == 4)
@ -50,7 +51,8 @@
#define TARGET_THREAD_HEAPIMAGE 2192
#define TARGET_THREAD_CODEIMAGE 2196
#define TARGET_THREAD_THUNKTABLE 2200
#define TARGET_THREAD_STACKLIMIT 2224
#define TARGET_THREAD_DYNAMICTABLE 2204
#define TARGET_THREAD_STACKLIMIT 2228
#else
#error

View File

@ -34,6 +34,7 @@ FIELD(virtualThunks)
THUNK_FIELD(default_);
THUNK_FIELD(defaultVirtual);
THUNK_FIELD(defaultDynamic);
THUNK_FIELD(native);
THUNK_FIELD(aioob);
THUNK_FIELD(stackOverflow);

View File

@ -2091,12 +2091,21 @@ void interceptFileOperations(Thread* t, bool updateRuntimeData)
if (fileInputStreamFdField) {
cp->fileInputStreamFdField = fileInputStreamFdField->offset();
intercept(t,
fileInputStreamClass,
"open",
"(Ljava/lang/String;)V",
voidPointer(openFile),
updateRuntimeData);
if (findMethodOrNull(t, fileInputStreamClass, "open0", "(Ljava/lang/String;)V") != 0) {
intercept(t,
fileInputStreamClass,
"open0",
"(Ljava/lang/String;)V",
voidPointer(openFile),
updateRuntimeData);
} else {
intercept(t,
fileInputStreamClass,
"open",
"(Ljava/lang/String;)V",
voidPointer(openFile),
updateRuntimeData);
}
if (findMethodOrNull(t, fileInputStreamClass, "read0", "()I") != 0) {
intercept(t,

View File

@ -58,7 +58,7 @@ namespace {
namespace local {
const bool DebugCompile = false;
const bool DebugCompile = true;
const bool DebugNatives = false;
const bool DebugCallTable = false;
const bool DebugMethodTree = false;
@ -83,6 +83,7 @@ const unsigned InitialZoneCapacityInBytes = 64 * 1024;
enum ThunkIndex {
compileMethodIndex,
compileVirtualMethodIndex,
linkDynamicMethodIndex,
invokeNativeIndex,
throwArrayIndexOutOfBoundsIndex,
throwStackOverflowIndex,
@ -295,6 +296,7 @@ class MyThread : public Thread {
uintptr_t* heapImage;
uint8_t* codeImage;
void** thunkTable;
void** dynamicTable;
CallTrace* trace;
Reference* reference;
avian::codegen::Architecture* arch;
@ -1278,6 +1280,93 @@ class Context {
Slice<ir::Value*> argumentBuffer;
};
unsigned& dynamicIndex(MyThread* t);
void**& dynamicTable(MyThread* t);
void updateDynamicTable(MyThread* t, MyThread* o)
{
o->dynamicTable = dynamicTable(t);
if (t->peer)
updateDynamicTable(static_cast<MyThread*>(t->peer), o);
if (t->child)
updateDynamicTable(static_cast<MyThread*>(t->child), o);
}
uintptr_t defaultDynamicThunk(MyThread* t);
uintptr_t compileVirtualThunk(MyThread* t,
unsigned index,
unsigned* size,
uintptr_t thunk,
const char* baseName);
unsigned addDynamic(MyThread* t, GcInvocation* invocation)
{
ACQUIRE(t, t->m->classLock);
int index = invocation->index();
if (index == -1) {
index = dynamicIndex(t)++;
invocation->index() = index;
unsigned oldCapacity = roots(t)->invocations()
? roots(t)->invocations()->length()
: 0;
if (static_cast<unsigned>(index) >= oldCapacity) {
unsigned newCapacity = oldCapacity ? 2 * oldCapacity : 4096;
void** newTable = static_cast<void**>(
t->m->heap->allocate(newCapacity * BytesPerWord));
GcArray* newData = makeArray(t, newCapacity);
PROTECT(t, newData);
GcWordArray* newThunks = makeWordArray(t, newCapacity * 2);
PROTECT(t, newThunks);
if (dynamicTable(t)) {
memcpy(newTable, dynamicTable(t), oldCapacity * BytesPerWord);
for(size_t i = 0; i < oldCapacity; i++) {
newData->setBodyElement(t, i,
roots(t)->invocations()->body()[i]);
}
mark(t, newData, ArrayBody, oldCapacity);
memcpy(newThunks->body().begin(),
compileRoots(t)->dynamicThunks()->body().begin(),
compileRoots(t)->dynamicThunks()->length() * BytesPerWord);
}
ENTER(t, Thread::ExclusiveState);
dynamicTable(t) = newTable;
roots(t)->setInvocations(t, newData);
updateDynamicTable(static_cast<MyThread*>(t->m->rootThread), t);
compileRoots(t)->setDynamicThunks(t, newThunks);
}
unsigned size;
uintptr_t thunk = compileVirtualThunk(
t, index, &size, defaultDynamicThunk(t), "dynamicThunk");
compileRoots(t)->dynamicThunks()->body()[index * 2] = thunk;
compileRoots(t)->dynamicThunks()->body()[(index * 2) + 1] = size;
t->dynamicTable[index] = reinterpret_cast<void*>(thunk);
roots(t)->invocations()->setBodyElement(t, index, invocation);
}
return index;
}
unsigned translateLocalIndex(Context* context,
unsigned footprint,
unsigned index)
@ -4944,6 +5033,53 @@ loop:
args(c->threadRegister(), frame->append(argument), instance)));
} break;
case invokedynamic: {
context->leaf = false;
uint16_t poolIndex = codeReadInt16(t, code, ip);
ip += 2;
GcInvocation* invocation = cast<GcInvocation>(
t,
singletonObject(t, context->method->code()->pool(), poolIndex - 1));
PROTECT(t, invocation);
invocation->setClass(t, context->method->class_());
unsigned index = addDynamic(t, invocation);
GcMethod* template_ = invocation->template_();
unsigned returnCode = template_->returnCode();
unsigned rSize = resultSize(t, returnCode);
unsigned parameterFootprint = template_->parameterFootprint();
// TODO: can we allow tailCalls in general?
// e.g. what happens if the call site is later bound to a method that can't be tail called?
// NOTE: calling isTailCall right now would cause an segfault, since
// invocation->template_()->class_() will be null.
// bool tailCall
// = isTailCall(t, code, ip, context->method, invocation->template_());
bool tailCall = false;
// todo: do we need to tell the compiler to add a load barrier
// here for VolatileCallSite instances?
ir::Value* result = c->stackCall(
c->memory(c->memory(c->threadRegister(), ir::Type::object(),
TARGET_THREAD_DYNAMICTABLE),
ir::Type::object(), index * TargetBytesPerWord),
tailCall ? Compiler::TailJump : 0, frame->trace(0, 0),
operandTypeForFieldCode(t, returnCode),
frame->peekMethodArguments(parameterFootprint));
frame->popFootprint(parameterFootprint);
if (rSize) {
frame->pushReturnValue(returnCode, result);
}
} break;
case invokeinterface: {
context->leaf = false;
@ -6914,13 +7050,13 @@ void finish(MyThread* t, FixedAllocator* allocator, Context* context)
reinterpret_cast<const char*>(context->method->spec()->body().begin()));
// for debugging:
if (false
if (true
and ::strcmp(reinterpret_cast<const char*>(
context->method->class_()->name()->body().begin()),
"java/lang/System") == 0
"InvokeDynamic") == 0
and ::strcmp(reinterpret_cast<const char*>(
context->method->name()->body().begin()),
"<clinit>") == 0) {
"main") == 0) {
trap();
}
syncInstructionCache(start, codeSize);
@ -8317,6 +8453,7 @@ class MyProcessor : public Processor {
public:
Thunk default_;
Thunk defaultVirtual;
Thunk defaultDynamic;
Thunk native;
Thunk aioob;
Thunk stackOverflow;
@ -8342,8 +8479,10 @@ class MyProcessor : public Processor {
GcArithmeticException::FixedSize),
codeAllocator(s, Slice<uint8_t>(0, 0)),
callTableSize(0),
dynamicIndex(0),
useNativeFeatures(useNativeFeatures),
compilationHandlers(0)
compilationHandlers(0),
dynamicTable(0)
{
thunkTable[compileMethodIndex] = voidPointer(local::compileMethod);
thunkTable[compileVirtualMethodIndex] = voidPointer(compileVirtualMethod);
@ -8429,6 +8568,10 @@ class MyProcessor : public Processor {
TARGET_THREAD_THUNKTABLE,
&MyThread::thunkTable,
"TARGET_THREAD_THUNKTABLE")
+ checkConstant(t,
TARGET_THREAD_DYNAMICTABLE,
&MyThread::dynamicTable,
"TARGET_THREAD_DYNAMICTABLE")
+ checkConstant(t,
TARGET_THREAD_STACKLIMIT,
&MyThread::stackLimit,
@ -8993,7 +9136,7 @@ class MyProcessor : public Processor {
if (image and code) {
local::boot(static_cast<MyThread*>(t), image, code);
} else {
roots = makeCompileRoots(t, 0, 0, 0, 0, 0, 0, 0, 0, 0);
roots = makeCompileRoots(t, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
{
GcArray* ct = makeArray(t, 128);
@ -9096,11 +9239,23 @@ class MyProcessor : public Processor {
ThunkCollection thunks;
ThunkCollection bootThunks;
unsigned callTableSize;
unsigned dynamicIndex;
bool useNativeFeatures;
void* thunkTable[dummyIndex + 1];
CompilationHandlerList* compilationHandlers;
void** dynamicTable;
};
unsigned& dynamicIndex(MyThread* t)
{
return static_cast<MyProcessor*>(t->m->processor)->dynamicIndex;
}
void**& dynamicTable(MyThread* t)
{
return static_cast<MyProcessor*>(t->m->processor)->dynamicTable;
}
const char* stringOrNull(const char* str)
{
if (str) {
@ -9240,15 +9395,16 @@ bool isThunkUnsafeStack(MyProcessor::Thunk* thunk, void* ip)
bool isThunkUnsafeStack(MyProcessor::ThunkCollection* thunks, void* ip)
{
const unsigned NamedThunkCount = 5;
const unsigned NamedThunkCount = 6;
MyProcessor::Thunk table[NamedThunkCount + ThunkCount];
table[0] = thunks->default_;
table[1] = thunks->defaultVirtual;
table[2] = thunks->native;
table[3] = thunks->aioob;
table[4] = thunks->stackOverflow;
table[2] = thunks->defaultDynamic;
table[3] = thunks->native;
table[4] = thunks->aioob;
table[5] = thunks->stackOverflow;
for (unsigned i = 0; i < ThunkCount; ++i) {
new (table + NamedThunkCount + i)
@ -9598,8 +9754,8 @@ void findThunks(MyThread* t, BootImage* image, uint8_t* code)
MyProcessor* p = processor(t);
p->bootThunks.default_ = thunkToThunk(image->thunks.default_, code);
p->bootThunks.defaultVirtual
= thunkToThunk(image->thunks.defaultVirtual, code);
p->bootThunks.defaultVirtual = thunkToThunk(image->thunks.defaultVirtual, code);
p->bootThunks.defaultDynamic = thunkToThunk(image->thunks.defaultDynamic, code);
p->bootThunks.native = thunkToThunk(image->thunks.native, code);
p->bootThunks.aioob = thunkToThunk(image->thunks.aioob, code);
p->bootThunks.stackOverflow = thunkToThunk(image->thunks.stackOverflow, code);
@ -9807,6 +9963,68 @@ void compileCall(MyThread* t, Context* c, ThunkIndex index, bool call = true)
}
}
void compileDefaultThunk(MyThread* t,
FixedAllocator* allocator,
MyProcessor::Thunk* thunk,
const char* name,
ThunkIndex thunkIndex,
bool hasTarget)
{
Context context(t);
avian::codegen::Assembler* a = context.assembler;
if(hasTarget) {
lir::RegisterPair class_(t->arch->virtualCallTarget());
lir::Memory virtualCallTargetSrc(
t->arch->stack(),
(t->arch->frameFooterSize() + t->arch->frameReturnAddressSize())
* TargetBytesPerWord);
a->apply(lir::Move,
OperandInfo(
TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallTargetSrc),
OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &class_));
lir::Memory virtualCallTargetDst(t->arch->thread(),
TARGET_THREAD_VIRTUALCALLTARGET);
a->apply(
lir::Move,
OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &class_),
OperandInfo(
TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallTargetDst));
}
lir::RegisterPair index(t->arch->virtualCallIndex());
lir::Memory virtualCallIndex(t->arch->thread(),
TARGET_THREAD_VIRTUALCALLINDEX);
a->apply(
lir::Move,
OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &index),
OperandInfo(TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallIndex));
a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP);
thunk->frameSavedOffset = a->length();
lir::RegisterPair thread(t->arch->thread());
a->pushFrame(1, TargetBytesPerWord, lir::Operand::Type::RegisterPair, &thread);
compileCall(t, &context, thunkIndex);
a->popFrame(t->arch->alignFrameSize(1));
lir::RegisterPair result(t->arch->returnLow());
a->apply(lir::Jump,
OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &result));
thunk->length = a->endBlock(false)->resolve(0, 0);
thunk->start = finish(
t, allocator, a, name, thunk->length);
}
void compileThunks(MyThread* t, FixedAllocator* allocator)
{
MyProcessor* p = processor(t);
@ -9836,59 +10054,13 @@ void compileThunks(MyThread* t, FixedAllocator* allocator)
= finish(t, allocator, a, "default", p->thunks.default_.length);
}
{
Context context(t);
avian::codegen::Assembler* a = context.assembler;
compileDefaultThunk
(t, allocator, &(p->thunks.defaultVirtual), "defaultVirtual",
compileVirtualMethodIndex, true);
lir::RegisterPair class_(t->arch->virtualCallTarget());
lir::Memory virtualCallTargetSrc(
t->arch->stack(),
(t->arch->frameFooterSize() + t->arch->frameReturnAddressSize())
* TargetBytesPerWord);
a->apply(lir::Move,
OperandInfo(
TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallTargetSrc),
OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &class_));
lir::Memory virtualCallTargetDst(t->arch->thread(),
TARGET_THREAD_VIRTUALCALLTARGET);
a->apply(
lir::Move,
OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &class_),
OperandInfo(
TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallTargetDst));
lir::RegisterPair index(t->arch->virtualCallIndex());
lir::Memory virtualCallIndex(t->arch->thread(),
TARGET_THREAD_VIRTUALCALLINDEX);
a->apply(
lir::Move,
OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &index),
OperandInfo(TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallIndex));
a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP);
p->thunks.defaultVirtual.frameSavedOffset = a->length();
lir::RegisterPair thread(t->arch->thread());
a->pushFrame(1, TargetBytesPerWord, lir::Operand::Type::RegisterPair, &thread);
compileCall(t, &context, compileVirtualMethodIndex);
a->popFrame(t->arch->alignFrameSize(1));
lir::RegisterPair result(t->arch->returnLow());
a->apply(lir::Jump,
OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &result));
p->thunks.defaultVirtual.length = a->endBlock(false)->resolve(0, 0);
p->thunks.defaultVirtual.start = finish(
t, allocator, a, "defaultVirtual", p->thunks.defaultVirtual.length);
}
compileDefaultThunk
(t, allocator, &(p->thunks.defaultDynamic), "defaultDynamic",
linkDynamicMethodIndex, false);
{
Context context(t);
@ -10040,6 +10212,11 @@ uintptr_t defaultVirtualThunk(MyThread* t)
return reinterpret_cast<uintptr_t>(processor(t)->thunks.defaultVirtual.start);
}
uintptr_t defaultDynamicThunk(MyThread* t)
{
return reinterpret_cast<uintptr_t>(processor(t)->thunks.defaultDynamic.start);
}
uintptr_t nativeThunk(MyThread* t)
{
return reinterpret_cast<uintptr_t>(processor(t)->thunks.native.start);
@ -10056,7 +10233,11 @@ bool unresolved(MyThread* t, uintptr_t methodAddress)
or methodAddress == bootDefaultThunk(t);
}
uintptr_t compileVirtualThunk(MyThread* t, unsigned index, unsigned* size)
uintptr_t compileVirtualThunk(MyThread* t,
unsigned index,
unsigned* size,
uintptr_t thunk,
const char* baseName)
{
Context context(t);
avian::codegen::Assembler* a = context.assembler;
@ -10069,11 +10250,10 @@ uintptr_t compileVirtualThunk(MyThread* t, unsigned index, unsigned* size)
OperandInfo(TargetBytesPerWord, lir::Operand::Type::Constant, &indexConstant),
OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &indexRegister));
avian::codegen::ResolvedPromise defaultVirtualThunkPromise(
defaultVirtualThunk(t));
lir::Constant thunk(&defaultVirtualThunkPromise);
avian::codegen::ResolvedPromise promise(thunk);
lir::Constant target(&promise);
a->apply(lir::Jump,
OperandInfo(TargetBytesPerWord, lir::Operand::Type::Constant, &thunk));
OperandInfo(TargetBytesPerWord, lir::Operand::Type::Constant, &target));
*size = a->endBlock(false)->resolve(0, 0);
@ -10083,8 +10263,7 @@ uintptr_t compileVirtualThunk(MyThread* t, unsigned index, unsigned* size)
a->setDestination(start);
a->write();
const char* const virtualThunkBaseName = "virtualThunk";
const size_t virtualThunkBaseNameLength = strlen(virtualThunkBaseName);
const size_t virtualThunkBaseNameLength = strlen(baseName);
const size_t maxIntStringLength = 10;
THREAD_RUNTIME_ARRAY(t,
@ -10094,7 +10273,7 @@ uintptr_t compileVirtualThunk(MyThread* t, unsigned index, unsigned* size)
sprintf(RUNTIME_ARRAY_BODY(virtualThunkName),
"%s%d",
virtualThunkBaseName,
baseName,
index);
logCompile(t, start, *size, 0, RUNTIME_ARRAY_BODY(virtualThunkName), 0);
@ -10120,7 +10299,7 @@ uintptr_t virtualThunk(MyThread* t, unsigned index)
if (oldArray->body()[index * 2] == 0) {
unsigned size;
uintptr_t thunk = compileVirtualThunk(t, index, &size);
uintptr_t thunk = compileVirtualThunk(t, index, &size, defaultVirtualThunk(t), "virtualThunk");
oldArray->body()[index * 2] = thunk;
oldArray->body()[(index * 2) + 1] = size;
}

View File

@ -3784,3 +3784,22 @@ extern "C" AVIAN_EXPORT jint JNICALL
return run(*t, local::boot, 0) ? 0 : -1;
}
extern "C" AVIAN_EXPORT jstring JNICALL JVM_GetTemporaryDirectory(JNIEnv* e UNUSED)
{
// Unimplemented
// This is used in newer builds of openjdk8, as a place to store statistics or something...
abort();
}
extern "C" AVIAN_EXPORT jboolean JNICALL JVM_KnownToNotExist(JNIEnv* e UNUSED, jobject loader UNUSED, jstring classname UNUSED)
{
// Unimplemented
abort();
}
extern "C" AVIAN_EXPORT jintArray JNICALL JVM_GetResourceLookupCache(JNIEnv* e UNUSED, jobject loader UNUSED, jstring resourcename UNUSED)
{
// Unimplemented
abort();
}

View File

@ -398,7 +398,8 @@ GcTriple* makeCodeImage(Thread* t,
RUNTIME_ARRAY_BODY(types)[1] = Type_intptr_t;
for (unsigned i = 2; i < count + 2; ++i) {
switch (s.read1()) {
unsigned constType = s.read1();
switch (constType) {
case CONSTANT_Class:
case CONSTANT_String:
RUNTIME_ARRAY_BODY(types)[i] = Type_object;
@ -436,7 +437,25 @@ GcTriple* makeCodeImage(Thread* t,
s.skip(s.read2());
break;
case CONSTANT_MethodHandle:
RUNTIME_ARRAY_BODY(types)[i] = Type_object;
s.skip(3);
break;
case CONSTANT_MethodType:
RUNTIME_ARRAY_BODY(types)[i] = Type_object;
s.skip(2);
break;
case CONSTANT_InvokeDynamic:
RUNTIME_ARRAY_BODY(types)[i] = Type_object;
s.skip(4);
break;
default:
fprintf(stderr, "unknown class constant: %d\n", constType);
abort(t);
}
}

View File

@ -78,7 +78,7 @@
(int32_t index)
(object class)
(object pool)
(object template)
(method template)
(object site))
(type triple
@ -406,7 +406,8 @@
(finder virtualFileFinders)
(field array virtualFiles)
(field array arrayInterfaceTable)
(object threadTerminated))
(object threadTerminated)
(field array invocations))
(type compileRoots
(field array callTable)
@ -415,6 +416,7 @@
(object objectPools)
(object staticTableArray)
(wordArray virtualThunks)
(wordArray dynamicThunks)
(method receiveMethod)
(method windMethod)
(method rewindMethod))

7
test/InvokeDynamic.java Normal file
View File

@ -0,0 +1,7 @@
public class InvokeDynamic {
public static void main(String[] args) {
Runnable r = () -> System.out.println("success");
r.run();
}
}