more JNI work

This commit is contained in:
Joel Dice 2007-06-28 20:58:48 -06:00
parent f0bc4dbe76
commit 2057e72956
9 changed files with 401 additions and 62 deletions

View File

@ -4,6 +4,14 @@ bld = build
src = src src = src
classpath = classpath classpath = classpath
arch = $(shell uname -m)
ifeq ($(arch),i586)
arch = i386
endif
ifeq ($(arch),i686)
arch = i386
endif
cxx = g++ cxx = g++
cc = gcc cc = gcc
vg = nice valgrind --leak-check=full --num-callers=32 --db-attach=yes \ vg = nice valgrind --leak-check=full --num-callers=32 --db-attach=yes \
@ -27,6 +35,7 @@ test-cflags = -DDEBUG_MEMORY
stress-cflags = -DDEBUG_MEMORY -DDEBUG_MEMORY_MAJOR stress-cflags = -DDEBUG_MEMORY -DDEBUG_MEMORY_MAJOR
cpp-objects = $(foreach x,$(1),$(patsubst $(2)/%.cpp,$(bld)/%.o,$(x))) cpp-objects = $(foreach x,$(1),$(patsubst $(2)/%.cpp,$(bld)/%.o,$(x)))
assembly-objects = $(foreach x,$(1),$(patsubst $(2)/%.S,$(bld)/%.o,$(x)))
stdcpp-sources = $(src)/stdc++.cpp stdcpp-sources = $(src)/stdc++.cpp
stdcpp-objects = $(call cpp-objects,$(stdcpp-sources),$(src)) stdcpp-objects = $(call cpp-objects,$(stdcpp-sources),$(src))
@ -58,7 +67,21 @@ interpreter-sources = \
$(src)/vm.cpp \ $(src)/vm.cpp \
$(src)/heap.cpp \ $(src)/heap.cpp \
$(src)/main.cpp $(src)/main.cpp
interpreter-objects = $(call cpp-objects,$(interpreter-sources),$(src))
ifeq ($(arch),i386)
interpreter-assembly-sources = $(src)/cdecl.S
endif
ifeq ($(arch),x86_64)
interpreter-assembly-sources = $(src)/amd64.S
endif
interpreter-cpp-objects = \
$(call cpp-objects,$(interpreter-sources),$(src))
interpreter-assembly-objects = \
$(call assembly-objects,$(interpreter-assembly-sources),$(src))
interpreter-objects = \
$(interpreter-cpp-objects) \
$(interpreter-assembly-objects)
interpreter-cflags = $(slow) $(cflags) interpreter-cflags = $(slow) $(cflags)
generator-headers = \ generator-headers = \
@ -138,7 +161,7 @@ stress-all: $(stress-executable)
.PHONY: clean .PHONY: clean
clean: clean:
@echo "removing $(bld)" @echo "removing $(bld)"
rm -r $(bld) rm -rf $(bld)
gen-arg = $(shell echo $(1) | sed -e 's:$(bld)/type-\(.*\)\.cpp:\1:') gen-arg = $(shell echo $(1) | sed -e 's:$(bld)/type-\(.*\)\.cpp:\1:')
$(generated-code): %.cpp: $(src)/types.def $(generator-executable) $(generated-code): %.cpp: $(src)/types.def $(generator-executable)
@ -159,7 +182,12 @@ $(stdcpp-objects): $(bld)/%.o: $(src)/%.cpp
@mkdir -p $(dir $(@)) @mkdir -p $(dir $(@))
$(cxx) $(stdcpp-cflags) -c $(<) -o $(@) $(cxx) $(stdcpp-cflags) -c $(<) -o $(@)
$(interpreter-objects): $(bld)/%.o: $(src)/%.cpp $(interpreter-depends) $(interpreter-cpp-objects): $(bld)/%.o: $(src)/%.cpp $(interpreter-depends)
@echo "compiling $(@)"
@mkdir -p $(dir $(@))
$(cxx) $(interpreter-cflags) -c $(<) -o $(@)
$(interpreter-assembly-objects): $(bld)/%.o: $(src)/%.S
@echo "compiling $(@)" @echo "compiling $(@)"
@mkdir -p $(dir $(@)) @mkdir -p $(dir $(@))
$(cxx) $(interpreter-cflags) -c $(<) -o $(@) $(cxx) $(interpreter-cflags) -c $(<) -o $(@)

107
src/amd64.S Normal file
View File

@ -0,0 +1,107 @@
#include "types.h"
.text
.globl amd64Call
.type amd64Call, @function
amd64Call:
pushq %rbp
// %rdi aka 0(%rbp): function
// %rsi aka 8(%rbp): stack
// %rdx aka 16(%rbp): stackSize
// %rcx aka 24(%rbp): gprTable
// %r8 aka 32(%rbp): sseTable
// %r9 aka 40(%rbp): returnType
// save our argument registers so we can clobber them
pushq %r9
pushq %r8
pushq %rcx
pushq %rdx
pushq %rsi
pushq %rdi
movq %rsp,%rbp
// reserve space for arguments passed via memory
subq %rdx,%rsp
// copy memory arguments into place
movq $0,%rcx
jmp test
loop:
movq %rcx,%rax
movq %rcx,%rdx
addq %rsp,%rdx
addq 8(%rbp),%rax
movq (%rax),%rax
movq %rax,(%rdx)
addq $8,%rcx
test:
cmpq 16(%rbp),%rcx
jb loop
// do we need to load the general-purpose registers?
cmpq $0,24(%rbp)
je sse
// yes, we do
movq 24(%rbp),%rax
movq 0(%rax),%rdi
movq 8(%rax),%rsi
movq 16(%rax),%rcx
movq 24(%rax),%rdx
movq 32(%rax),%r8
movq 40(%rax),%r9
sse:
// do we need to load the SSE registers?
cmpq $0,32(%rbp)
je call
// yes, we do
movq 32(%rbp),%rax
movq 0(%rax),%xmm0
movq 8(%rax),%xmm1
movq 16(%rax),%xmm2
movq 24(%rax),%xmm3
movq 32(%rax),%xmm4
movq 40(%rax),%xmm5
movq 48(%rax),%xmm6
movq 64(%rax),%xmm7
call:
call *0(%rbp)
// clear space reserved for memory arguments
movq 16(%rbp),%rcx
addq %rcx,%rsp
// handle return value based on expected type
movq 40(%rbp),%rcx
void:
cmpq $VOID_TYPE,%rcx
jne float
jmp exit
float:
cmpq $FLOAT_TYPE,%rcx
je copy
cmpq $DOUBLE_TYPE,%rcx
jne exit
copy:
movq %xmm0,%rax
exit:
movq %rbp,%rsp
// pop our argument registers
addq $48,%rsp
popq %rbp
ret

78
src/cdecl.S Normal file
View File

@ -0,0 +1,78 @@
#include "types.h"
.text
.globl cdeclCall
.type cdeclCall, @function
cdeclCall:
pushl %ebp
movl %esp,%ebp
// 8(%ebp): function
// 12(%ebp): stack
// 16(%ebp): stackSize
// 20(%ebp): returnType
// reserve space for arguments
movl 16(%ebp),%ecx
subl %ecx,%esp
// copy arguments into place
movl $0,%ecx
jmp test
loop:
movl %ecx,%eax
movl %ecx,%edx
addl %esp,%edx
addl 12(%ebp),%eax
movl (%eax),%eax
movl %eax,(%edx)
addl $4,%ecx
test:
cmpl 16(%ebp),%ecx
jb loop
// call function
call *8(%ebp)
// clear space reserved for arguments
movl 16(%ebp),%ecx
addl %ecx,%esp
// handle return value based on expected type
movl 20(%ebp),%ecx
void:
cmpl $VOID_TYPE,%ecx
jne int64
jmp exit
int64:
cmpl $INT64_TYPE,%ecx
jne float
jmp exit
float:
cmpl $FLOAT_TYPE,%ecx
jne double
fstps 8(%ebp)
movl 8(%ebp),%eax
jmp int32
double:
cmpl $DOUBLE_TYPE,%ecx
jne int32
fstpl 8(%ebp)
movl 8(%ebp),%eax
movl 12(%ebp),%edx
jmp exit
int32:
movl $0,%edx
exit:
movl %ebp,%esp
popl %ebp
ret

View File

@ -6,6 +6,7 @@
#include "stdarg.h" #include "stdarg.h"
#include "string.h" #include "string.h"
#include "stdio.h" #include "stdio.h"
#include "types.h"
#define NO_RETURN __attribute__((noreturn)) #define NO_RETURN __attribute__((noreturn))

View File

@ -11,6 +11,77 @@
using namespace vm; using namespace vm;
#ifdef __i386__
extern "C" uint64_t
cdeclCall(void* function, void* stack, unsigned stackSize,
unsigned returnType);
namespace {
inline uint64_t
dynamicCall(void* function, uint32_t* arguments, uint8_t*,
unsigned, unsigned argumentsSize, unsigned returnType)
{
return cdeclCall(function, arguments, argumentsSize, returnType);
}
} // namespace
#elif defined __x86_64__
extern "C" uint64_t
amd64Call(void* function, void* stack, unsigned stackSize,
void* gprTable, void* sseTable, unsigned returnType);
namespace {
uint64_t
dynamicCall(void* function, uint64_t* arguments, uint8_t* argumentTypes,
unsigned argumentCount, unsigned, unsigned returnType)
{
const unsigned GprCount = 6;
uint64_t gprTable[GprCount];
unsigned gprIndex = 0;
const unsigned SseCount = 8;
uint64_t sseTable[SseCount];
unsigned sseIndex = 0;
uint64_t stack[argumentCount];
unsigned stackIndex = 0;
for (unsigned i = 0; i < argumentCount; ++i) {
switch (argumentTypes[i]) {
case FLOAT_TYPE:
case DOUBLE_TYPE: {
if (sseIndex < SseCount) {
sseTable[sseIndex++] = arguments[i];
} else {
stack[stackIndex++] = arguments[i];
}
} break;
default: {
if (gprIndex < GprCount) {
gprTable[gprIndex++] = arguments[i];
} else {
stack[stackIndex++] = arguments[i];
}
} break;
}
}
return amd64Call(function, stack, stackIndex * 8, (gprIndex ? gprTable : 0),
(sseIndex ? sseTable : 0), returnType);
}
} // namespace
#else
# error unsupported platform
#endif
namespace { namespace {
const bool Verbose = false; const bool Verbose = false;
@ -115,13 +186,10 @@ class System: public vm::System {
return 0; return 0;
} }
virtual uint64_t call(void* ,//function, virtual uint64_t call(void* function, uintptr_t* arguments, uint8_t* types,
unsigned ,//argumentCount, unsigned count, unsigned size, unsigned returnType)
uint32_t* ,//argumentTable,
uint8_t* ,//argumentSizeTable,
unsigned )//returnSize)
{ {
::abort(); return dynamicCall(function, arguments, types, count, size, returnType);
} }
virtual Status load(vm::System::Library** lib, virtual Status load(vm::System::Library** lib,

View File

@ -42,9 +42,9 @@ class System {
virtual void free(const void*) = 0; virtual void free(const void*) = 0;
virtual Status start(Thread*) = 0; virtual Status start(Thread*) = 0;
virtual Status make(Monitor**) = 0; virtual Status make(Monitor**) = 0;
virtual uint64_t call(void* function, unsigned argumentCount, virtual uint64_t call(void* function, uintptr_t* arguments, uint8_t* types,
uint32_t* argumentTable, uint8_t* argumentSizeTable, unsigned count, unsigned size,
unsigned returnSize) = 0; unsigned returnType) = 0;
virtual Status load(Library**, const char* name, Library* next) = 0; virtual Status load(Library**, const char* name, Library* next) = 0;
virtual void abort() = 0; virtual void abort() = 0;

View File

@ -37,7 +37,7 @@
(uint16_t argumentTableSize) (uint16_t argumentTableSize)
(uint8_t returnCode) (uint8_t returnCode)
(uint8_t builtin) (uint8_t builtin)
(array uint8_t parameterCodes)) (array uint8_t parameterTypes))
(type pointer (type pointer
(void* value)) (void* value))

13
src/types.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef TYPES_H
#define TYPES_H
#define VOID_TYPE 0
#define INT8_TYPE 1
#define INT16_TYPE 2
#define INT32_TYPE 3
#define INT64_TYPE 4
#define FLOAT_TYPE 5
#define DOUBLE_TYPE 6
#define POINTER_TYPE 7
#endif//TYPES_H

View File

@ -17,7 +17,8 @@ using namespace vm;
namespace { namespace {
static const bool Debug = true; static const bool Verbose = false;
static const bool Debug = false;
class Thread; class Thread;
@ -827,7 +828,7 @@ makeRuntimeException(Thread* t, object message)
{ {
PROTECT(t, message); PROTECT(t, message);
object trace = makeTrace(t); object trace = makeTrace(t);
return makeClassCastException(t, message, trace, 0); return makeRuntimeException(t, message, trace, 0);
} }
object object
@ -935,6 +936,33 @@ fieldCode(Thread* t, unsigned javaCode)
} }
} }
unsigned
fieldType(Thread* t, unsigned code)
{
switch (code) {
case VoidField:
return VOID_TYPE;
case ByteField:
case BooleanField:
return INT8_TYPE;
case CharField:
case ShortField:
return INT16_TYPE;
case DoubleField:
return DOUBLE_TYPE;
case FloatField:
return FLOAT_TYPE;
case IntField:
return INT32_TYPE;
case LongField:
return INT64_TYPE;
case ObjectField:
return POINTER_TYPE;
default: abort(t);
}
}
uint64_t uint64_t
primitiveValue(Thread* t, unsigned code, object o) primitiveValue(Thread* t, unsigned code, object o)
{ {
@ -1828,15 +1856,19 @@ resolveClass(Thread* t, object spec)
(reinterpret_cast<const char*>(&byteArrayBody(t, spec, 0))); (reinterpret_cast<const char*>(&byteArrayBody(t, spec, 0)));
if (data) { if (data) {
fprintf(stderr, "parsing %s\n", &byteArrayBody if (Verbose) {
(t, spec, 0)); fprintf(stderr, "parsing %s\n", &byteArrayBody
(t, spec, 0));
}
// parse class file // parse class file
class_ = parseClass(t, data->start(), data->length()); class_ = parseClass(t, data->start(), data->length());
data->dispose(); data->dispose();
fprintf(stderr, "done parsing %s\n", &byteArrayBody if (Verbose) {
(t, className(t, class_), 0)); fprintf(stderr, "done parsing %s\n", &byteArrayBody
(t, className(t, class_), 0));
}
PROTECT(t, class_); PROTECT(t, class_);
@ -1929,15 +1961,17 @@ makeNativeMethodData(Thread* t, object method, void* function, bool builtin)
0, // argument table size 0, // argument table size
0, // return code, 0, // return code,
builtin, builtin,
methodParameterCount(t, method), methodParameterCount(t, method) + 1,
false); false);
unsigned argumentTableSize = BytesPerWord / 4; unsigned argumentTableSize = BytesPerWord;
unsigned index = 0; unsigned index = 0;
nativeMethodDataParameterTypes(t, data, index++) = POINTER_TYPE;
if ((methodFlags(t, method) & ACC_STATIC) == 0) { if ((methodFlags(t, method) & ACC_STATIC) == 0) {
nativeMethodDataParameterCodes(t, data, index++) = ObjectField; nativeMethodDataParameterTypes(t, data, index++) = POINTER_TYPE;
argumentTableSize += BytesPerWord / 4; argumentTableSize += BytesPerWord;
} }
const char* s = reinterpret_cast<const char*> const char* s = reinterpret_cast<const char*>
@ -1945,22 +1979,22 @@ makeNativeMethodData(Thread* t, object method, void* function, bool builtin)
++ s; // skip '(' ++ s; // skip '('
while (*s and *s != ')') { while (*s and *s != ')') {
unsigned code = fieldCode(t, *s); unsigned code = fieldCode(t, *s);
nativeMethodDataParameterCodes(t, data, index++) = code; nativeMethodDataParameterTypes(t, data, index++) = fieldType(t, code);
switch (*s) { switch (*s) {
case 'L': case 'L':
argumentTableSize += BytesPerWord / 4; argumentTableSize += BytesPerWord;
while (*s and *s != ';') ++ s; while (*s and *s != ';') ++ s;
++ s; ++ s;
break; break;
case '[': case '[':
argumentTableSize += BytesPerWord / 4; argumentTableSize += BytesPerWord;
while (*s == '[') ++ s; while (*s == '[') ++ s;
break; break;
default: default:
argumentTableSize += divide(primitiveSize(t, code), 4); argumentTableSize += pad(primitiveSize(t, code));
++ s; ++ s;
break; break;
} }
@ -2020,44 +2054,50 @@ invokeNative(Thread* t, object method)
return 0; return 0;
} }
unsigned parameterCount = methodParameterCount(t, method); unsigned count = methodParameterCount(t, method);
uint32_t args[nativeMethodDataArgumentTableSize(t, data)]; unsigned size = nativeMethodDataArgumentTableSize(t, data);
uint8_t sizes[parameterCount + 1]; uintptr_t args[size / BytesPerWord];
unsigned offset = 0; unsigned offset = 0;
sizes[0] = BytesPerWord; args[offset++] = reinterpret_cast<uintptr_t>(t);
memcpy(args + offset, &t, BytesPerWord);
offset += BytesPerWord / 4;
for (unsigned i = 0; i < parameterCount; ++i) { for (unsigned i = 0; i < count; ++i) {
unsigned code = nativeMethodDataParameterCodes(t, data, i); unsigned type = nativeMethodDataParameterTypes(t, data, i);
unsigned position = (t->sp - count) + i;
object o = t->stack[position];
if (code == ObjectField) { switch (type) {
sizes[i + 1] = BytesPerWord; case INT8_TYPE:
unsigned position = (t->sp - parameterCount) + i; args[offset++] = cast<uint8_t>(o, BytesPerWord);
if (t->stack[position]) { break;
memcpy(args + offset, t->stack + position, BytesPerWord);
} else { case INT16_TYPE:
memset(args + offset, 0, BytesPerWord); args[offset++] = cast<uint16_t>(o, BytesPerWord);
} break;
offset += BytesPerWord / 4;
} else { case INT32_TYPE:
sizes[i + 1] = primitiveSize(t, code); case FLOAT_TYPE:
uint64_t v = primitiveValue(t, code, t->stack[t->sp + i]); args[offset++] = cast<uint32_t>(o, BytesPerWord);
if (sizes[i + 1] == 8) { break;
memcpy(args + offset, &v, 8);
offset += 2; case INT64_TYPE:
} else { case DOUBLE_TYPE: {
args[offset++] = v; uint64_t v = cast<uint64_t>(o, BytesPerWord);
} memcpy(args + offset, &v, 8);
offset += (8 / BytesPerWord);
} break;
case POINTER_TYPE:
args[offset++] = reinterpret_cast<uintptr_t>(t->stack + position);
break;
default: abort(t);
} }
} }
unsigned returnCode = nativeMethodDataReturnCode(t, data); unsigned returnCode = nativeMethodDataReturnCode(t, data);
unsigned returnSize unsigned returnType = fieldType(t, returnCode);
= (returnCode == ObjectField ? 4 : primitiveSize(t, returnCode));
void* function = nativeMethodDataFunction(t, data); void* function = nativeMethodDataFunction(t, data);
bool builtin = nativeMethodDataBuiltin(t, data); bool builtin = nativeMethodDataBuiltin(t, data);
@ -2065,11 +2105,13 @@ invokeNative(Thread* t, object method)
enter(t, Thread::IdleState); enter(t, Thread::IdleState);
} }
uint64_t rv = t->vm->system->call(function, uint64_t rv = t->vm->system->call
parameterCount, (function,
args, args,
sizes, &nativeMethodDataParameterTypes(t, data, 0),
returnSize); count + 1,
size,
returnType);
if (not builtin) { if (not builtin) {
enter(t, Thread::ActiveState); enter(t, Thread::ActiveState);
@ -3656,6 +3698,8 @@ run(Thread* t)
if (nativeMethodDataReturnCode(t, methodCode(t, code)) != VoidField) { if (nativeMethodDataReturnCode(t, methodCode(t, code)) != VoidField) {
push(t, r); push(t, r);
} }
code = methodCode(t, frameMethod(t, frame));
} else { } else {
if (UNLIKELY(codeMaxStack(t, methodCode(t, code)) + base if (UNLIKELY(codeMaxStack(t, methodCode(t, code)) + base
> Thread::StackSizeInWords)) > Thread::StackSizeInWords))
@ -3734,7 +3778,7 @@ run(Thread* t)
} }
for (; p; p = traceNext(t, p)) { for (; p; p = traceNext(t, p)) {
fprintf(stderr, " at %s:%s\n", fprintf(stderr, " at %s.%s\n",
&byteArrayBody &byteArrayBody
(t, className(t, methodClass(t, traceMethod(t, p))), 0), (t, className(t, methodClass(t, traceMethod(t, p))), 0),
&byteArrayBody &byteArrayBody