diff --git a/makefile b/makefile index 41261378fe..9729ca0246 100644 --- a/makefile +++ b/makefile @@ -4,6 +4,14 @@ bld = build src = src classpath = classpath +arch = $(shell uname -m) +ifeq ($(arch),i586) + arch = i386 +endif +ifeq ($(arch),i686) + arch = i386 +endif + cxx = g++ cc = gcc 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 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-objects = $(call cpp-objects,$(stdcpp-sources),$(src)) @@ -58,7 +67,21 @@ interpreter-sources = \ $(src)/vm.cpp \ $(src)/heap.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) generator-headers = \ @@ -138,7 +161,7 @@ stress-all: $(stress-executable) .PHONY: clean clean: @echo "removing $(bld)" - rm -r $(bld) + rm -rf $(bld) gen-arg = $(shell echo $(1) | sed -e 's:$(bld)/type-\(.*\)\.cpp:\1:') $(generated-code): %.cpp: $(src)/types.def $(generator-executable) @@ -159,7 +182,12 @@ $(stdcpp-objects): $(bld)/%.o: $(src)/%.cpp @mkdir -p $(dir $(@)) $(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 $(@)" @mkdir -p $(dir $(@)) $(cxx) $(interpreter-cflags) -c $(<) -o $(@) diff --git a/src/amd64.S b/src/amd64.S new file mode 100644 index 0000000000..b97bc8c15d --- /dev/null +++ b/src/amd64.S @@ -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 diff --git a/src/cdecl.S b/src/cdecl.S new file mode 100644 index 0000000000..4abdc9aa09 --- /dev/null +++ b/src/cdecl.S @@ -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 diff --git a/src/common.h b/src/common.h index 4b05fbbcfe..8e94fe7a61 100644 --- a/src/common.h +++ b/src/common.h @@ -6,6 +6,7 @@ #include "stdarg.h" #include "string.h" #include "stdio.h" +#include "types.h" #define NO_RETURN __attribute__((noreturn)) diff --git a/src/main.cpp b/src/main.cpp index ffb14b3bd4..19294850b3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,77 @@ 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 { const bool Verbose = false; @@ -115,13 +186,10 @@ class System: public vm::System { return 0; } - virtual uint64_t call(void* ,//function, - unsigned ,//argumentCount, - uint32_t* ,//argumentTable, - uint8_t* ,//argumentSizeTable, - unsigned )//returnSize) + virtual uint64_t call(void* function, uintptr_t* arguments, uint8_t* types, + unsigned count, unsigned size, unsigned returnType) { - ::abort(); + return dynamicCall(function, arguments, types, count, size, returnType); } virtual Status load(vm::System::Library** lib, diff --git a/src/system.h b/src/system.h index cee0f8a285..edd1d48176 100644 --- a/src/system.h +++ b/src/system.h @@ -42,9 +42,9 @@ class System { virtual void free(const void*) = 0; virtual Status start(Thread*) = 0; virtual Status make(Monitor**) = 0; - virtual uint64_t call(void* function, unsigned argumentCount, - uint32_t* argumentTable, uint8_t* argumentSizeTable, - unsigned returnSize) = 0; + virtual uint64_t call(void* function, uintptr_t* arguments, uint8_t* types, + unsigned count, unsigned size, + unsigned returnType) = 0; virtual Status load(Library**, const char* name, Library* next) = 0; virtual void abort() = 0; diff --git a/src/types.def b/src/types.def index cb2ef259a2..645a984e58 100644 --- a/src/types.def +++ b/src/types.def @@ -37,7 +37,7 @@ (uint16_t argumentTableSize) (uint8_t returnCode) (uint8_t builtin) - (array uint8_t parameterCodes)) + (array uint8_t parameterTypes)) (type pointer (void* value)) diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000000..dd3e09f8a2 --- /dev/null +++ b/src/types.h @@ -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 diff --git a/src/vm.cpp b/src/vm.cpp index 6ac710d5ec..b53bd7cac7 100644 --- a/src/vm.cpp +++ b/src/vm.cpp @@ -17,7 +17,8 @@ using namespace vm; namespace { -static const bool Debug = true; +static const bool Verbose = false; +static const bool Debug = false; class Thread; @@ -827,7 +828,7 @@ makeRuntimeException(Thread* t, object message) { PROTECT(t, message); object trace = makeTrace(t); - return makeClassCastException(t, message, trace, 0); + return makeRuntimeException(t, message, trace, 0); } 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 primitiveValue(Thread* t, unsigned code, object o) { @@ -1828,15 +1856,19 @@ resolveClass(Thread* t, object spec) (reinterpret_cast(&byteArrayBody(t, spec, 0))); if (data) { - fprintf(stderr, "parsing %s\n", &byteArrayBody - (t, spec, 0)); + if (Verbose) { + fprintf(stderr, "parsing %s\n", &byteArrayBody + (t, spec, 0)); + } // parse class file class_ = parseClass(t, data->start(), data->length()); data->dispose(); - fprintf(stderr, "done parsing %s\n", &byteArrayBody - (t, className(t, class_), 0)); + if (Verbose) { + fprintf(stderr, "done parsing %s\n", &byteArrayBody + (t, className(t, class_), 0)); + } PROTECT(t, class_); @@ -1929,15 +1961,17 @@ makeNativeMethodData(Thread* t, object method, void* function, bool builtin) 0, // argument table size 0, // return code, builtin, - methodParameterCount(t, method), + methodParameterCount(t, method) + 1, false); - unsigned argumentTableSize = BytesPerWord / 4; + unsigned argumentTableSize = BytesPerWord; unsigned index = 0; + nativeMethodDataParameterTypes(t, data, index++) = POINTER_TYPE; + if ((methodFlags(t, method) & ACC_STATIC) == 0) { - nativeMethodDataParameterCodes(t, data, index++) = ObjectField; - argumentTableSize += BytesPerWord / 4; + nativeMethodDataParameterTypes(t, data, index++) = POINTER_TYPE; + argumentTableSize += BytesPerWord; } const char* s = reinterpret_cast @@ -1945,22 +1979,22 @@ makeNativeMethodData(Thread* t, object method, void* function, bool builtin) ++ s; // skip '(' while (*s and *s != ')') { unsigned code = fieldCode(t, *s); - nativeMethodDataParameterCodes(t, data, index++) = code; + nativeMethodDataParameterTypes(t, data, index++) = fieldType(t, code); switch (*s) { case 'L': - argumentTableSize += BytesPerWord / 4; + argumentTableSize += BytesPerWord; while (*s and *s != ';') ++ s; ++ s; break; case '[': - argumentTableSize += BytesPerWord / 4; + argumentTableSize += BytesPerWord; while (*s == '[') ++ s; break; default: - argumentTableSize += divide(primitiveSize(t, code), 4); + argumentTableSize += pad(primitiveSize(t, code)); ++ s; break; } @@ -2020,44 +2054,50 @@ invokeNative(Thread* t, object method) return 0; } - unsigned parameterCount = methodParameterCount(t, method); + unsigned count = methodParameterCount(t, method); - uint32_t args[nativeMethodDataArgumentTableSize(t, data)]; - uint8_t sizes[parameterCount + 1]; + unsigned size = nativeMethodDataArgumentTableSize(t, data); + uintptr_t args[size / BytesPerWord]; unsigned offset = 0; - sizes[0] = BytesPerWord; - memcpy(args + offset, &t, BytesPerWord); - offset += BytesPerWord / 4; + args[offset++] = reinterpret_cast(t); - for (unsigned i = 0; i < parameterCount; ++i) { - unsigned code = nativeMethodDataParameterCodes(t, data, i); + for (unsigned i = 0; i < count; ++i) { + unsigned type = nativeMethodDataParameterTypes(t, data, i); + unsigned position = (t->sp - count) + i; + object o = t->stack[position]; - if (code == ObjectField) { - sizes[i + 1] = BytesPerWord; - unsigned position = (t->sp - parameterCount) + i; - if (t->stack[position]) { - memcpy(args + offset, t->stack + position, BytesPerWord); - } else { - memset(args + offset, 0, BytesPerWord); - } - offset += BytesPerWord / 4; - } else { - sizes[i + 1] = primitiveSize(t, code); - uint64_t v = primitiveValue(t, code, t->stack[t->sp + i]); - if (sizes[i + 1] == 8) { - memcpy(args + offset, &v, 8); - offset += 2; - } else { - args[offset++] = v; - } + switch (type) { + case INT8_TYPE: + args[offset++] = cast(o, BytesPerWord); + break; + + case INT16_TYPE: + args[offset++] = cast(o, BytesPerWord); + break; + + case INT32_TYPE: + case FLOAT_TYPE: + args[offset++] = cast(o, BytesPerWord); + break; + + case INT64_TYPE: + case DOUBLE_TYPE: { + uint64_t v = cast(o, BytesPerWord); + memcpy(args + offset, &v, 8); + offset += (8 / BytesPerWord); + } break; + + case POINTER_TYPE: + args[offset++] = reinterpret_cast(t->stack + position); + break; + + default: abort(t); } } unsigned returnCode = nativeMethodDataReturnCode(t, data); - unsigned returnSize - = (returnCode == ObjectField ? 4 : primitiveSize(t, returnCode)); - + unsigned returnType = fieldType(t, returnCode); void* function = nativeMethodDataFunction(t, data); bool builtin = nativeMethodDataBuiltin(t, data); @@ -2065,11 +2105,13 @@ invokeNative(Thread* t, object method) enter(t, Thread::IdleState); } - uint64_t rv = t->vm->system->call(function, - parameterCount, - args, - sizes, - returnSize); + uint64_t rv = t->vm->system->call + (function, + args, + &nativeMethodDataParameterTypes(t, data, 0), + count + 1, + size, + returnType); if (not builtin) { enter(t, Thread::ActiveState); @@ -3656,6 +3698,8 @@ run(Thread* t) if (nativeMethodDataReturnCode(t, methodCode(t, code)) != VoidField) { push(t, r); } + + code = methodCode(t, frameMethod(t, frame)); } else { if (UNLIKELY(codeMaxStack(t, methodCode(t, code)) + base > Thread::StackSizeInWords)) @@ -3734,7 +3778,7 @@ run(Thread* t) } for (; p; p = traceNext(t, p)) { - fprintf(stderr, " at %s:%s\n", + fprintf(stderr, " at %s.%s\n", &byteArrayBody (t, className(t, methodClass(t, traceMethod(t, p))), 0), &byteArrayBody