diff --git a/classpath/java/io/File.java b/classpath/java/io/File.java index 0f91ebb746..7e961ce138 100644 --- a/classpath/java/io/File.java +++ b/classpath/java/io/File.java @@ -14,6 +14,8 @@ public class File { private static final String FileSeparator = System.getProperty("file.separator"); + public static final String separator = FileSeparator; + // static { // System.loadLibrary("natives"); // } @@ -157,7 +159,7 @@ public class File { } String[] result = new String[count]; - for (int i = count; i >= 0; --i) { + for (int i = count - 1; i >= 0; --i) { result[i] = list.value; list = list.next; } diff --git a/classpath/java/lang/SystemClassLoader.java b/classpath/java/lang/SystemClassLoader.java index 3e936fa040..723651ca10 100644 --- a/classpath/java/lang/SystemClassLoader.java +++ b/classpath/java/lang/SystemClassLoader.java @@ -14,8 +14,6 @@ import java.net.URL; import java.net.MalformedURLException; public class SystemClassLoader extends ClassLoader { - private Object map; - protected native Class findClass(String name) throws ClassNotFoundException; protected native Class findLoadedClass(String name); diff --git a/classpath/java/lang/reflect/Method.java b/classpath/java/lang/reflect/Method.java index b28b70c045..68058aedce 100644 --- a/classpath/java/lang/reflect/Method.java +++ b/classpath/java/lang/reflect/Method.java @@ -22,7 +22,7 @@ public class Method extends AccessibleObject implements Member { private byte[] spec; private Class class_; private Object code; - private Object compiled; + private long compiled; private Method() { } diff --git a/classpath/java/util/Calendar.java b/classpath/java/util/Calendar.java index d39b8a1438..629eaced15 100644 --- a/classpath/java/util/Calendar.java +++ b/classpath/java/util/Calendar.java @@ -126,7 +126,7 @@ public abstract class Calendar { int hour = remainder / MILLIS_PER_HOUR; remainder = remainder % MILLIS_PER_HOUR; int minute = remainder / MILLIS_PER_MINUTE; - remainder = remainder / MILLIS_PER_MINUTE; + remainder = remainder % MILLIS_PER_MINUTE; int second = remainder / MILLIS_PER_SECOND; fields[YEAR] = year; fields[MONTH] = month; diff --git a/makefile b/makefile index 90ea95aecb..980e19d9f3 100644 --- a/makefile +++ b/makefile @@ -62,7 +62,6 @@ warnings = -Wall -Wextra -Werror -Wunused-parameter -Winit-self \ common-cflags = $(warnings) -fno-rtti -fno-exceptions -fno-omit-frame-pointer \ "-I$(JAVA_HOME)/include" -idirafter $(src) -I$(native-build) \ -D__STDC_LIMIT_MACROS -D_JNI_IMPLEMENTATION_ -DAVIAN_VERSION=\"$(version)\" \ - -DBOOT_CLASSPATH=\"[classpathJar]\" build-cflags = $(common-cflags) -fPIC -fvisibility=hidden \ "-I$(JAVA_HOME)/include/linux" -I$(src) -pthread @@ -104,6 +103,9 @@ endif ifeq ($(platform),darwin) build-cflags = $(common-cflags) -fPIC -fvisibility=hidden -I$(src) lflags = $(common-lflags) -ldl -framework CoreFoundation + ifeq ($(bootimage),true) + bootimage-lflags = -Wl,-segprot,__BOOT,rwx,rwx + endif rdynamic = strip-all = -S -x binaryToMacho = $(native-build)/binaryToMacho @@ -132,6 +134,7 @@ ifeq ($(platform),windows) ar = i586-mingw32msvc-ar ranlib = i586-mingw32msvc-ranlib objcopy = i586-mingw32msvc-objcopy + strip = i586-mingw32msvc-strip else build-cflags = $(common-cflags) \ "-I$(JAVA_HOME)/include/win32" -I$(src) -mthreads @@ -159,11 +162,9 @@ ifeq ($(mode),stress-major) endif ifeq ($(mode),fast) cflags += -O3 -g3 -DNDEBUG - strip = strip endif ifeq ($(mode),small) cflags += -Os -g3 -DNDEBUG - strip = strip endif cpp-objects = $(foreach x,$(1),$(patsubst $(2)/%.cpp,$(3)/%.o,$(x))) @@ -198,6 +199,8 @@ vm-depends = \ $(src)/assembler.h \ $(src)/compiler.h \ $(src)/$(asm).h + $(src)/heapwalk.h \ + $(src)/bootimage.h vm-sources = \ $(src)/$(system).cpp \ @@ -211,11 +214,6 @@ vm-sources = \ $(src)/process.cpp \ $(src)/$(asm).cpp -ifeq ($(heapdump),true) - vm-sources += $(src)/heapdump.cpp - cflags += -DAVIAN_HEAPDUMP -endif - vm-asm-sources = $(src)/$(asm).S ifeq ($(process),compile) @@ -232,6 +230,37 @@ vm-cpp-objects = $(call cpp-objects,$(vm-sources),$(src),$(native-build)) vm-asm-objects = $(call asm-objects,$(vm-asm-sources),$(src),$(native-build)) vm-objects = $(vm-cpp-objects) $(vm-asm-objects) +heapwalk-sources = $(src)/heapwalk.cpp +heapwalk-objects = \ + $(call cpp-objects,$(heapwalk-sources),$(src),$(native-build)) + +ifeq ($(heapdump),true) + vm-sources += $(src)/heapdump.cpp + vm-heapwalk-objects = $(heapwalk-objects) + cflags += -DAVIAN_HEAPDUMP +endif + +bootimage-generator-sources = $(src)/bootimage.cpp +bootimage-generator-objects = \ + $(call cpp-objects,$(bootimage-generator-sources),$(src),$(native-build)) +bootimage-generator = \ + $(build)/$(build-platform)-$(build-arch)-compile-fast/bootimage-generator + +bootimage-bin = $(native-build)/bootimage.bin +bootimage-object = $(native-build)/bootimage-bin.o + +ifeq ($(bootimage),true) + ifneq ($(build-arch),$(arch)) + error "can't cross-build a bootimage" + endif + + vm-classpath-object = $(bootimage-object) + cflags += -DBOOT_IMAGE=\"bootimageBin\" +else + vm-classpath-object = $(classpath-object) + cflags += -DBOOT_CLASSPATH=\"[classpathJar]\" +endif + driver-source = $(src)/main.cpp driver-object = $(native-build)/main.o driver-dynamic-object = $(native-build)/main-dynamic.o @@ -358,6 +387,12 @@ $(vm-cpp-objects): $(native-build)/%.o: $(src)/%.cpp $(vm-depends) $(vm-asm-objects): $(native-build)/%-asm.o: $(src)/%.S $(compile-asm-object) +$(bootimage-generator-objects): $(native-build)/%.o: $(src)/%.cpp $(vm-depends) + $(compile-object) + +$(heapwalk-objects): $(native-build)/%.o: $(src)/%.cpp $(vm-depends) + $(compile-object) + $(driver-object): $(driver-source) $(compile-object) @@ -381,7 +416,7 @@ $(binaryToMacho): $(src)/binaryToMacho.cpp $(classpath-object): $(build)/classpath.jar $(binaryToMacho) @echo "creating $(@)" ifeq ($(platform),darwin) - $(binaryToMacho) $(asm) $(build)/classpath.jar \ + $(binaryToMacho) $(asm) $(build)/classpath.jar __TEXT __text \ __binary_classpath_jar_start __binary_classpath_jar_end > $(@) else (wd=$$(pwd); \ @@ -399,15 +434,54 @@ $(generator-objects): $(native-build)/%.o: $(src)/%.cpp $(jni-objects): $(native-build)/%.o: $(classpath)/%.cpp $(compile-object) -$(static-library): $(vm-objects) $(jni-objects) +$(static-library): $(vm-objects) $(jni-objects) $(vm-heapwalk-objects) @echo "creating $(@)" rm -rf $(@) $(ar) cru $(@) $(^) $(ranlib) $(@) +$(bootimage-bin): $(bootimage-generator) + $(<) $(classpath-build) > $(@) + +$(bootimage-object): $(bootimage-bin) $(binaryToMacho) + @echo "creating $(@)" +ifeq ($(platform),darwin) + $(binaryToMacho) $(<) __BOOT __boot \ + __binary_bootimage_bin_start __binary_bootimage_bin_end > $(@) +else + (wd=$$(pwd); \ + cd $(native-build); \ + $(objcopy) --rename-section=.data=.boot -I binary bootimage.bin \ + -O $(object-format) -B $(object-arch) "$${wd}/$(@).tmp"; \ + $(objcopy) --set-section-flags .boot=alloc,load,code "$${wd}/$(@).tmp" \ + "$${wd}/$(@)") +endif + $(executable): \ - $(vm-objects) $(classpath-object) $(jni-objects) $(driver-object) \ - $(boot-object) + $(vm-objects) $(jni-objects) $(driver-object) $(vm-heapwalk-objects) \ + $(boot-object) $(vm-classpath-object) + @echo "linking $(@)" +ifeq ($(platform),windows) + $(dlltool) -z $(@).def $(^) + $(dlltool) -d $(@).def -e $(@).exp + $(cc) $(@).exp $(^) $(lflags) -o $(@) +else + $(cc) $(^) $(rdynamic) $(lflags) $(bootimage-lflags) -o $(@) +endif + $(strip) $(strip-all) $(@) + +$(bootimage-generator): + (unset MAKEFLAGS && \ + make mode=fast process=compile \ + arch=$(build-arch) \ + platform=$(build-platform) \ + bootimage-generator= \ + build-bootimage-generator=$(bootimage-generator) \ + $(bootimage-generator)) + +$(build-bootimage-generator): \ + $(vm-objects) $(classpath-object) $(jni-objects) $(heapwalk-objects) \ + $(bootimage-generator-objects) @echo "linking $(@)" ifeq ($(platform),windows) $(dlltool) -z $(@).def $(^) @@ -416,13 +490,12 @@ ifeq ($(platform),windows) else $(cc) $(^) $(rdynamic) $(lflags) -o $(@) endif - $(strip) $(strip-all) $(@) $(dynamic-library): \ - $(vm-objects) $(classpath-object) $(dynamic-object) $(jni-objects) \ - $(boot-object) + $(vm-objects) $(dynamic-object) $(jni-objects) $(vm-heapwalk-objects) \ + $(boot-object) $(vm-classpath-object) @echo "linking $(@)" - $(cc) $(^) $(shared) $(lflags) -o $(@) + $(cc) $(^) $(shared) $(lflags) $(bootimage-lflags) -o $(@) $(strip) $(strip-all) $(@) $(executable-dynamic): $(driver-dynamic-object) $(dynamic-library) diff --git a/readme.txt b/readme.txt index c4e80a3cfd..81ebc28d86 100644 --- a/readme.txt +++ b/readme.txt @@ -174,7 +174,7 @@ for darwin-i386: (objcopy is not currently supported on this platform, so we use the binaryToMacho utility instead) $ ../build/darwin-i386-compile-fast/binaryToMacho boot.jar \ - __binary_boot_jar_start __binary_boot_jar_end > boot-jar.o + __TEXT __text __binary_boot_jar_start __binary_boot_jar_end > boot-jar.o Step 4: Write a driver which starts the VM and runs the desired main diff --git a/src/assembler.h b/src/assembler.h index ceac55c18b..5a199d8014 100644 --- a/src/assembler.h +++ b/src/assembler.h @@ -78,8 +78,16 @@ const int NoRegister = -1; class Promise { public: + class Listener { + public: + virtual void* resolve(int64_t value) = 0; + + Listener* next; + }; + virtual int64_t value() = 0; virtual bool resolved() = 0; + virtual Listener* listen(unsigned) { return 0; } }; class ResolvedPromise: public Promise { @@ -134,6 +142,59 @@ class CombinedPromise: public Promise { Promise* high; }; +class ListenPromise: public Promise { + public: + ListenPromise(System* s, Allocator* allocator): + s(s), allocator(allocator), listener(0) + { } + + virtual int64_t value() { + abort(s); + } + + virtual bool resolved() { + return false; + } + + virtual Listener* listen(unsigned sizeInBytes) { + Listener* l = static_cast(allocator->allocate(sizeInBytes)); + l->next = listener; + listener = l; + return l; + } + + System* s; + Allocator* allocator; + Listener* listener; + Promise* promise; +}; + +class DelayedPromise: public ListenPromise { + public: + DelayedPromise(System* s, Allocator* allocator, Promise* basis, + DelayedPromise* next): + ListenPromise(s, allocator), basis(basis), next(next) + { } + + virtual int64_t value() { + abort(s); + } + + virtual bool resolved() { + return false; + } + + virtual Listener* listen(unsigned sizeInBytes) { + Listener* l = static_cast(allocator->allocate(sizeInBytes)); + l->next = listener; + listener = l; + return l; + } + + Promise* basis; + DelayedPromise* next; +}; + class TraceHandler { public: virtual void handleTrace(Promise* address, unsigned padIndex, @@ -209,7 +270,8 @@ class Assembler { virtual unsigned argumentRegisterCount() = 0; virtual int argumentRegister(unsigned index) = 0; - virtual void updateCall(void* returnAddress, void* newTarget) = 0; + virtual void updateCall(UnaryOperation op, bool assertAlignment, + void* returnAddress, void* newTarget) = 0; virtual unsigned alignFrameSize(unsigned sizeInWords) = 0; diff --git a/src/binaryToMacho.cpp b/src/binaryToMacho.cpp index 8f04202e21..c5f55d118a 100644 --- a/src/binaryToMacho.cpp +++ b/src/binaryToMacho.cpp @@ -33,6 +33,7 @@ pad(unsigned n) void writeObject(const char* architecture, FILE* out, const uint8_t* data, unsigned size, + const char* segmentName, const char* sectionName, const char* startName, const char* endName) { unsigned startNameLength = strlen(startName) + 1; @@ -63,7 +64,7 @@ writeObject(const char* architecture, segment_command segment = { LC_SEGMENT, // cmd sizeof(segment_command) + sizeof(section), // cmdsize - "__TEXT", // segname + "", // segname 0, // vmaddr pad(size), // vmsize sizeof(mach_header) @@ -77,9 +78,11 @@ writeObject(const char* architecture, 0 // flags }; + strncpy(segment.segname, segmentName, sizeof(segment.segname)); + section sect = { - "__const", // sectname - "__TEXT", // segname + "", // sectname + "", // segname 0, // addr pad(size), // size sizeof(mach_header) @@ -94,6 +97,9 @@ writeObject(const char* architecture, 0, // reserved2 }; + strncpy(sect.segname, segmentName, sizeof(sect.segname)); + strncpy(sect.sectname, sectionName, sizeof(sect.sectname)); + symtab_command symbolTable = { LC_SYMTAB, // cmd sizeof(symtab_command), // cmdsize @@ -149,10 +155,10 @@ writeObject(const char* architecture, int main(int argc, const char** argv) { - if (argc != 5) { + if (argc != 7) { fprintf(stderr, - "usage: %s " - "\n", + "usage: %s " + "
\n", argv[0]); return -1; } @@ -172,8 +178,11 @@ main(int argc, const char** argv) } if (data) { - writeObject(argv[1], stdout, data, size, argv[3], argv[4]); + writeObject + (argv[1], stdout, data, size, argv[3], argv[4], argv[5], argv[6]); + munmap(data, size); + return 0; } else { perror(argv[0]); diff --git a/src/boot.cpp b/src/boot.cpp index 9f4c96a612..9fca36f7f5 100644 --- a/src/boot.cpp +++ b/src/boot.cpp @@ -15,6 +15,34 @@ // ourselves: extern "C" void __cxa_pure_virtual(void) { abort(); } +#ifdef BOOT_IMAGE + +#ifdef __MINGW32__ +# define EXPORT __declspec(dllexport) +# define SYMBOL(x) binary_bootimage_bin_##x +#else +# define EXPORT __attribute__ ((visibility("default"))) +# define SYMBOL(x) _binary_bootimage_bin_##x +#endif + +extern "C" { + + extern const uint8_t SYMBOL(start)[]; + extern const uint8_t SYMBOL(end)[]; + + EXPORT const uint8_t* + bootimageBin(unsigned* size) + { + *size = SYMBOL(end) - SYMBOL(start); + return SYMBOL(start); + } + +} + +#endif//BOOT_IMAGE + +#ifdef BOOT_CLASSPATH + #ifdef __MINGW32__ # define EXPORT __declspec(dllexport) # define SYMBOL(x) binary_classpath_jar_##x @@ -36,3 +64,5 @@ extern "C" { } } + +#endif//BOOT_CLASSPATH diff --git a/src/bootimage.cpp b/src/bootimage.cpp new file mode 100644 index 0000000000..9291acdfd0 --- /dev/null +++ b/src/bootimage.cpp @@ -0,0 +1,369 @@ +/* Copyright (c) 2008, 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. */ + +#include "bootimage.h" +#include "heapwalk.h" +#include "common.h" +#include "machine.h" +#include "util.h" +#include "assembler.h" + +// since we aren't linking against libstdc++, we must implement this +// ourselves: +extern "C" void __cxa_pure_virtual(void) { abort(); } + +using namespace vm; + +namespace { + +bool +endsWith(const char* suffix, const char* s, unsigned length) +{ + unsigned suffixLength = strlen(suffix); + return length >= suffixLength + and memcmp(suffix, s + (length - suffixLength), suffixLength) == 0; +} + +object +makeCodeImage(Thread* t, Zone* zone, BootImage* image, uint8_t* code, + unsigned capacity, uintptr_t* codeMap) +{ + unsigned size = 0; + t->m->processor->compileThunks(t, image, code, &size, capacity); + + object constants = 0; + PROTECT(t, constants); + + object calls = 0; + PROTECT(t, calls); + + DelayedPromise* addresses = 0; + + for (Finder::Iterator it(t->m->finder); it.hasMore();) { + unsigned nameSize = 0; + const char* name = it.next(&nameSize); + + if (endsWith(".class", name, nameSize)) { + //fprintf(stderr, "%.*s\n", nameSize - 6, name); + object c = resolveClass + (t, makeByteArray(t, "%.*s", nameSize - 6, name)); + PROTECT(t, c); + + if (classMethodTable(t, c)) { + for (unsigned i = 0; i < arrayLength(t, classMethodTable(t, c)); ++i) { + object method = arrayBody(t, classMethodTable(t, c), i); + if (methodCode(t, method) or (methodFlags(t, method) & ACC_NATIVE)) { + t->m->processor->compileMethod + (t, zone, code, &size, capacity, &constants, &calls, &addresses, + method); + } + } + } + } + } + + for (; calls; calls = tripleThird(t, calls)) { + object method = tripleFirst(t, calls); + uintptr_t address; + if (methodFlags(t, method) & ACC_NATIVE) { + address = reinterpret_cast(code + image->nativeThunk); + } else { + address = methodCompiled(t, method); + } + + static_cast(pointerValue(t, tripleSecond(t, calls))) + ->listener->resolve(address); + } + + for (; addresses; addresses = addresses->next) { + uint8_t* value = reinterpret_cast(addresses->basis->value()); + assert(t, value >= code); + + void* dst = addresses->listener->resolve + ((value - code) | (1 << BootShift)); + assert(t, reinterpret_cast(dst) + >= reinterpret_cast(code)); + + markBit(codeMap, reinterpret_cast(dst) + - reinterpret_cast(code)); + } + + image->codeSize = size; + + return constants; +} + +unsigned +objectSize(Thread* t, object o) +{ + assert(t, not objectExtended(t, o)); + + return baseSize(t, o, objectClass(t, o)); +} + +void +visitRoots(Thread* t, BootImage* image, HeapWalker* w, object constants) +{ + Machine* m = t->m; + + for (HashMapIterator it(t, m->classMap); it.hasMore();) { + w->visitRoot(tripleSecond(t, it.next())); + } + + image->loader = w->visitRoot(m->loader); + image->types = w->visitRoot(m->types); + + m->processor->visitRoots(image, w); + + for (; constants; constants = tripleThird(t, constants)) { + w->visitRoot(tripleFirst(t, constants)); + } +} + +HeapWalker* +makeHeapImage(Thread* t, BootImage* image, uintptr_t* heap, uintptr_t* map, + unsigned capacity, object constants) +{ + class Visitor: public HeapVisitor { + public: + Visitor(Thread* t, uintptr_t* heap, uintptr_t* map, unsigned capacity): + t(t), currentObject(0), currentNumber(0), currentOffset(0), heap(heap), + map(map), position(0), capacity(capacity) + { } + + void visit(unsigned number) { + if (currentObject) { + unsigned offset = currentNumber - 1 + currentOffset; + unsigned mark = heap[offset] & (~PointerMask); + unsigned value = number | (mark << BootShift); + + if (value) markBit(map, offset); + + heap[offset] = value; + } + } + + virtual void root() { + currentObject = 0; + } + + virtual unsigned visitNew(object p) { + if (p) { + unsigned size = objectSize(t, p); + + unsigned number; + if (currentObject + and (currentOffset * BytesPerWord) == ClassStaticTable) + { + FixedAllocator allocator + (t, reinterpret_cast(heap + position), + (capacity - position) * BytesPerWord); + + unsigned totalInBytes; + uintptr_t* dst = static_cast + (t->m->heap->allocateImmortalFixed + (&allocator, size, true, &totalInBytes)); + + memcpy(dst, p, size * BytesPerWord); + + dst[0] |= FixedMark; + + number = (dst - heap) + 1; + position += ceiling(totalInBytes, BytesPerWord); + } else { + assert(t, position + size < capacity); + memcpy(heap + position, p, size * BytesPerWord); + + number = position + 1; + position += size; + } + + visit(number); + + return number; + } else { + return 0; + } + } + + virtual void visitOld(object, unsigned number) { + visit(number); + } + + virtual void push(object object, unsigned number, unsigned offset) { + currentObject = object; + currentNumber = number; + currentOffset = offset; + } + + virtual void pop() { + currentObject = 0; + } + + Thread* t; + object currentObject; + unsigned currentNumber; + unsigned currentOffset; + uintptr_t* heap; + uintptr_t* map; + unsigned position; + unsigned capacity; + } visitor(t, heap, map, capacity / BytesPerWord); + + HeapWalker* w = makeHeapWalker(t, &visitor); + visitRoots(t, image, w, constants); + + image->heapSize = visitor.position * BytesPerWord; + + return w; +} + +void +updateConstants(Thread* t, object constants, uint8_t* code, uintptr_t* codeMap, + HeapMap* heapTable) +{ + for (; constants; constants = tripleThird(t, constants)) { + unsigned target = heapTable->find(tripleFirst(t, constants)); + assert(t, target > 0); + + for (Promise::Listener* pl = static_cast + (pointerValue(t, tripleSecond(t, constants)))->listener; + pl; pl = pl->next) + { + void* dst = pl->resolve(target); + + assert(t, reinterpret_cast(dst) + >= reinterpret_cast(code)); + + markBit(codeMap, reinterpret_cast(dst) + - reinterpret_cast(code)); + } + } +} + +unsigned +offset(object a, uintptr_t* b) +{ + return reinterpret_cast(b) - reinterpret_cast(a); +} + +void +writeBootImage(Thread* t, FILE* out) +{ + Zone zone(t->m->system, t->m->heap, 64 * 1024); + BootImage image; + + const unsigned CodeCapacity = 32 * 1024 * 1024; + uint8_t* code = static_cast(t->m->heap->allocate(CodeCapacity)); + uintptr_t* codeMap = static_cast + (t->m->heap->allocate(codeMapSize(CodeCapacity))); + memset(codeMap, 0, codeMapSize(CodeCapacity)); + + object constants = makeCodeImage + (t, &zone, &image, code, CodeCapacity, codeMap); + PROTECT(t, constants); + + const unsigned HeapCapacity = 32 * 1024 * 1024; + uintptr_t* heap = static_cast + (t->m->heap->allocate(HeapCapacity)); + uintptr_t* heapMap = static_cast + (t->m->heap->allocate(heapMapSize(HeapCapacity))); + memset(heapMap, 0, heapMapSize(HeapCapacity)); + + collect(t, Heap::MajorCollection); + + HeapWalker* heapWalker = makeHeapImage + (t, &image, heap, heapMap, HeapCapacity, constants); + + updateConstants(t, constants, code, codeMap, heapWalker->map()); + + image.classCount = hashMapSize(t, t->m->classMap); + unsigned* classTable = static_cast + (t->m->heap->allocate(image.classCount * sizeof(unsigned))); + + { unsigned i = 0; + for (HashMapIterator it(t, t->m->classMap); it.hasMore();) { + classTable[i++] = heapWalker->map()->find(tripleSecond(t, it.next())); + } + } + + image.stringCount = hashMapSize(t, t->m->stringMap); + unsigned* stringTable = static_cast + (t->m->heap->allocate(image.stringCount * sizeof(unsigned))); + + { unsigned i = 0; + for (HashMapIterator it(t, t->m->stringMap); it.hasMore();) { + stringTable[i++] = heapWalker->map()->find + (jreferenceTarget(t, tripleFirst(t, it.next()))); + } + } + + unsigned* callTable = t->m->processor->makeCallTable + (t, &image, heapWalker, code); + + heapWalker->dispose(); + + image.magic = BootImage::Magic; + image.codeBase = reinterpret_cast(code); + + fprintf(stderr, "class count %d string count %d call count %d\n" + "heap size %d code size %d\n", + image.classCount, image.stringCount, image.callCount, image.heapSize, + image.codeSize); + + if (true) { + fwrite(&image, sizeof(BootImage), 1, out); + + fwrite(classTable, image.classCount * sizeof(unsigned), 1, out); + fwrite(stringTable, image.stringCount * sizeof(unsigned), 1, out); + fwrite(callTable, image.callCount * sizeof(unsigned) * 2, 1, out); + + unsigned offset = (image.classCount * sizeof(unsigned)) + + (image.stringCount * sizeof(unsigned)) + + (image.callCount * sizeof(unsigned) * 2); + + while (offset % BytesPerWord) { + uint8_t c = 0; + fwrite(&c, 1, 1, out); + ++ offset; + } + + fwrite(heapMap, pad(heapMapSize(image.heapSize)), 1, out); + fwrite(heap, pad(image.heapSize), 1, out); + + fwrite(codeMap, pad(codeMapSize(image.codeSize)), 1, out); + fwrite(code, pad(image.codeSize), 1, out); + } +} + +} // namespace + +int +main(int ac, const char** av) +{ + if (ac != 2) { + fprintf(stderr, "usage: %s \n", av[0]); + return -1; + } + + System* s = makeSystem(0); + Heap* h = makeHeap(s, 128 * 1024 * 1024); + Finder* f = makeFinder(s, av[1], 0); + Processor* p = makeProcessor(s, h); + Machine* m = new (h->allocate(sizeof(Machine))) Machine(s, h, f, p, 0, 0); + Thread* t = p->makeThread(m, 0, 0); + + enter(t, Thread::ActiveState); + enter(t, Thread::IdleState); + + writeBootImage(t, stdout); + + return 0; +} diff --git a/src/bootimage.h b/src/bootimage.h new file mode 100644 index 0000000000..4b2ee7d458 --- /dev/null +++ b/src/bootimage.h @@ -0,0 +1,82 @@ +/* Copyright (c) 2008, 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. */ + +#ifndef BOOTIMAGE_H +#define BOOTIMAGE_H + +#include "common.h" + +namespace vm { + +const unsigned BootMask = (~static_cast(0)) / BytesPerWord; + +const unsigned BootShift = 32 - log(BytesPerWord); + +class BootImage { + public: + static const unsigned Magic = 0x22377322; + + unsigned magic; + + unsigned heapSize; + unsigned codeSize; + + unsigned classCount; + unsigned stringCount; + unsigned callCount; + + unsigned loader; + unsigned types; + unsigned methodTree; + unsigned methodTreeSentinal; + + uintptr_t codeBase; + + unsigned defaultThunk; + unsigned nativeThunk; + unsigned aioobThunk; + + unsigned thunkTable; + unsigned thunkSize; + + unsigned compileMethodCall; + unsigned invokeNativeCall; + unsigned throwArrayIndexOutOfBoundsCall; + +#define THUNK(s) unsigned s##Call; +#include "thunks.cpp" +#undef THUNK +}; + +inline unsigned +codeMapSize(unsigned codeSize) +{ + return ceiling(codeSize, BitsPerWord) * BytesPerWord; +} + +inline unsigned +heapMapSize(unsigned heapSize) +{ + return ceiling(heapSize, BitsPerWord * BytesPerWord) * BytesPerWord; +} + +inline object +bootObject(uintptr_t* heap, unsigned offset) +{ + if (offset) { + return reinterpret_cast(heap + offset - 1); + } else { + return 0; + } +} + +} // namespace vm + +#endif//BOOTIMAGE_H diff --git a/src/common.h b/src/common.h index bce549694e..0b5fc040a3 100644 --- a/src/common.h +++ b/src/common.h @@ -287,6 +287,15 @@ difference(void* a, void* b) return reinterpret_cast(a) - reinterpret_cast(b); } +template +inline void* +voidPointer(T function) +{ + void* p; + memcpy(&p, &function, sizeof(void*)); + return p; +} + class Machine; class Thread; diff --git a/src/compile.cpp b/src/compile.cpp index 91515a642e..f7a3c754f9 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -122,23 +122,26 @@ methodTree(MyThread* t); object methodTreeSentinal(MyThread* t); +unsigned +compiledSize(intptr_t address) +{ + return reinterpret_cast(address)[-1]; +} + intptr_t compareIpToMethodBounds(Thread* t, intptr_t ip, object method) { - intptr_t start = reinterpret_cast - (&singletonValue(t, methodCompiled(t, method), 0)); + intptr_t start = methodCompiled(t, method); if (DebugMethodTree) { fprintf(stderr, "find 0x%"LX" in (0x%"LX",0x%"LX")\n", ip, start, - start + (singletonCount(t, methodCompiled(t, method)) - * BytesPerWord)); + start + compiledSize(start)); } if (ip < start) { return -1; } else if (ip < start + static_cast - (singletonCount(t, methodCompiled(t, method)) - * BytesPerWord)) + (compiledSize(start) + BytesPerWord)) { return 0; } else { @@ -292,8 +295,7 @@ class MyStackWalker: public Processor::StackWalker { virtual int ip() { switch (state) { case Method: - return reinterpret_cast(ip_) - reinterpret_cast - (&singletonValue(t, methodCompiled(t, method_), 0)); + return reinterpret_cast(ip_) - methodCompiled(t, method_); case NativeMethod: return 0; @@ -383,14 +385,24 @@ localObject(MyThread* t, void* stack, object method, unsigned index) + (t->arch->frameReturnAddressSize() * BytesPerWord)); } -class PoolElement { +class PoolElement: public Promise { public: - PoolElement(object value, Promise* address, PoolElement* next): - value(value), address(address), next(next) + PoolElement(Thread* t, object target, PoolElement* next): + t(t), target(target), address(0), next(next) { } - object value; - Promise* address; + virtual int64_t value() { + assert(t, resolved()); + return address; + } + + virtual bool resolved() { + return address != 0; + } + + Thread* t; + object target; + intptr_t address; PoolElement* next; }; @@ -478,6 +490,34 @@ const unsigned ThunkCount = gcIfNecessaryThunk + 1; intptr_t getThunk(MyThread* t, Thunk thunk); +class BootContext { + public: + class MyProtector: public Thread::Protector { + public: + MyProtector(Thread* t, BootContext* c): Protector(t), c(c) { } + + virtual void visit(Heap::Visitor* v) { + v->visit(&(c->constants)); + v->visit(&(c->calls)); + } + + BootContext* c; + }; + + BootContext(Thread* t, object constants, object calls, + DelayedPromise* addresses, Zone* zone): + protector(t, this), constants(constants), calls(calls), + addresses(addresses), addressSentinal(addresses), zone(zone) + { } + + MyProtector protector; + object constants; + object calls; + DelayedPromise* addresses; + DelayedPromise* addressSentinal; + Zone* zone; +}; + class Context { public: class MyProtector: public Thread::Protector { @@ -488,7 +528,7 @@ class Context { v->visit(&(c->method)); for (PoolElement* p = c->objectPool; p; p = p->next) { - v->visit(&(p->value)); + v->visit(&(p->target)); } for (TraceElement* p = c->traceLog; p; p = p->next) { @@ -530,14 +570,16 @@ class Context { MyThread* t; }; - Context(MyThread* t, object method): + Context(MyThread* t, BootContext* bootContext, object method): thread(t), zone(t->m->system, t->m->heap, InitialZoneCapacityInBytes), assembler(makeAssembler(t->m->system, t->m->heap, &zone, t->arch)), client(t), compiler(makeCompiler(t->m->system, assembler, &zone, &client)), method(method), + bootContext(bootContext), objectPool(0), + objectPoolCount(0), traceLog(0), traceLogCount(0), visitTable(makeVisitTable(t, &zone, method)), @@ -553,7 +595,9 @@ class Context { client(t), compiler(0), method(0), + bootContext(0), objectPool(0), + objectPoolCount(0), traceLog(0), traceLogCount(0), visitTable(0), @@ -573,7 +617,9 @@ class Context { MyClient client; Compiler* compiler; object method; + BootContext* bootContext; PoolElement* objectPool; + unsigned objectPoolCount; TraceElement* traceLog; unsigned traceLogCount; uint16_t* visitTable; @@ -629,11 +675,26 @@ class Frame { } Compiler::Operand* append(object o) { - Promise* p = c->poolAppend(0); - context->objectPool = new - (context->zone.allocate(sizeof(PoolElement))) - PoolElement(o, p, context->objectPool); - return c->address(p); + if (context->bootContext) { + BootContext* bc = context->bootContext; + + Promise* p = new (bc->zone->allocate(sizeof(ListenPromise))) + ListenPromise(t->m->system, bc->zone); + + PROTECT(t, o); + object pointer = makePointer(t, p); + bc->constants = makeTriple(t, o, pointer, bc->constants); + + return c->promiseConstant(p); + } else { + context->objectPool = new + (context->zone.allocate(sizeof(PoolElement))) + PoolElement(t, o, context->objectPool); + + ++ context->objectPoolCount; + + return c->address(context->objectPool); + } } unsigned localSize() { @@ -829,6 +890,21 @@ class Frame { set(sp - 2, saved); } + Promise* addressPromise(Promise* p) { + BootContext* bc = context->bootContext; + if (bc) { + bc->addresses = new (bc->zone->allocate(sizeof(DelayedPromise))) + DelayedPromise(t->m->system, bc->zone, p, bc->addresses); + return bc->addresses; + } else { + return p; + } + } + + Compiler::Operand* addressOperand(Promise* p) { + return c->promiseConstant(addressPromise(p)); + } + Compiler::Operand* machineIp(unsigned logicalIp) { return c->promiseConstant(c->machineIp(logicalIp)); } @@ -1130,8 +1206,7 @@ findExceptionHandler(Thread* t, object method, void* ip) if (table) { object index = arrayBody(t, table, 0); - uint8_t* compiled = reinterpret_cast - (&singletonValue(t, methodCompiled(t, method), 0)); + uint8_t* compiled = reinterpret_cast(methodCompiled(t, method)); for (unsigned i = 0; i < arrayLength(t, table) - 1; ++i) { unsigned start = intArrayBody(t, index, i * 3); @@ -1227,13 +1302,35 @@ tryInitClass(MyThread* t, object class_) if (UNLIKELY(t->exception)) unwind(t); } +object& +objectPools(MyThread* t); + +uintptr_t +defaultThunk(MyThread* t); + +uintptr_t +nativeThunk(MyThread* t); + +uintptr_t +aioobThunk(MyThread* t); + +uintptr_t +methodAddress(Thread* t, object method) +{ + if (methodFlags(t, method) & ACC_NATIVE) { + return nativeThunk(static_cast(t)); + } else { + return methodCompiled(t, method); + } +} + void* findInterfaceMethodFromInstance(MyThread* t, object method, object instance) { if (instance) { - return &singletonValue - (t, methodCompiled - (t, findInterfaceMethod(t, method, objectClass(t, instance))), 0); + return reinterpret_cast + (methodAddress + (t, findInterfaceMethod(t, method, objectClass(t, instance)))); } else { t->exception = makeNullPointerException(t); unwind(t); @@ -1465,10 +1562,46 @@ makeBlankObjectArray(MyThread* t, object class_, int32_t length) } object -makeBlankArray(MyThread* t, object (*constructor)(Thread*, uintptr_t, bool), - int32_t length) +makeBlankArray(MyThread* t, unsigned type, int32_t length) { if (length >= 0) { + object (*constructor)(Thread*, uintptr_t, bool); + switch (type) { + case T_BOOLEAN: + constructor = makeBooleanArray; + break; + + case T_CHAR: + constructor = makeCharArray; + break; + + case T_FLOAT: + constructor = makeFloatArray; + break; + + case T_DOUBLE: + constructor = makeDoubleArray; + break; + + case T_BYTE: + constructor = makeByteArray; + break; + + case T_SHORT: + constructor = makeShortArray; + break; + + case T_INT: + constructor = makeIntArray; + break; + + case T_LONG: + constructor = makeLongArray; + break; + + default: abort(t); + } + return constructor(t, length, true); } else { object message = makeString(t, "%d", length); @@ -1697,15 +1830,6 @@ emptyMethod(MyThread* t, object method) and (codeBody(t, methodCode(t, method), 0) == return_); } -object -defaultThunk(MyThread* t); - -object -nativeThunk(MyThread* t); - -object -aioobThunk(MyThread* t); - void compileDirectInvoke(MyThread* t, Frame* frame, object target) { @@ -1716,31 +1840,52 @@ compileDirectInvoke(MyThread* t, Frame* frame, object target) Compiler::Operand* result = 0; if (not emptyMethod(t, target)) { - if (methodFlags(t, target) & ACC_NATIVE) { + BootContext* bc = frame->context->bootContext; + if (bc) { + if (methodClass(t, target) == methodClass(t, frame->context->method) + or (not classNeedsInit(t, methodClass(t, target)))) + { + Promise* p = new (bc->zone->allocate(sizeof(ListenPromise))) + ListenPromise(t->m->system, bc->zone); + + PROTECT(t, target); + object pointer = makePointer(t, p); + bc->calls = makeTriple(t, target, pointer, bc->calls); + + object traceTarget + = (methodFlags(t, target) & ACC_NATIVE) ? target : 0; + + result = c->stackCall + (c->promiseConstant(p), + 0, + frame->trace(traceTarget, false), + rSize, + methodParameterFootprint(t, target)); + } else { + result = c->stackCall + (c->constant(defaultThunk(t)), + Compiler::Aligned, + frame->trace(target, false), + rSize, + methodParameterFootprint(t, target)); + } + } else if (methodAddress(t, target) == defaultThunk(t) + or classNeedsInit(t, methodClass(t, target))) + { result = c->stackCall - (c->constant - (reinterpret_cast - (&singletonBody(t, nativeThunk(t), 0))), - 0, - frame->trace(target, false), - rSize, - methodParameterFootprint(t, target)); - } else if (methodCompiled(t, target) == defaultThunk(t)) { - result = c->stackCall - (c->constant - (reinterpret_cast - (&singletonBody(t, defaultThunk(t), 0))), + (c->constant(defaultThunk(t)), Compiler::Aligned, frame->trace(target, false), rSize, methodParameterFootprint(t, target)); } else { + object traceTarget + = (methodFlags(t, target) & ACC_NATIVE) ? target : 0; + result = c->stackCall - (c->constant - (reinterpret_cast - (&singletonBody(t, methodCompiled(t, target), 0))), + (c->constant(methodAddress(t, target)), 0, - frame->trace(0, false), + frame->trace(traceTarget, false), rSize, methodParameterFootprint(t, target)); } @@ -1952,8 +2097,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } if (CheckArrayBounds) { - c->checkBounds(array, ArrayLength, index, reinterpret_cast - (&singletonValue(t, aioobThunk(t), 0))); + c->checkBounds(array, ArrayLength, index, aioobThunk(t)); } switch (instruction) { @@ -2018,8 +2162,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } if (CheckArrayBounds) { - c->checkBounds(array, ArrayLength, index, reinterpret_cast - (&singletonValue(t, aioobThunk(t), 0))); + c->checkBounds(array, ArrayLength, index, aioobThunk(t)); } switch (instruction) { @@ -2423,12 +2566,15 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, object field = resolveField(t, codePool(t, code), index - 1); if (UNLIKELY(t->exception)) return; + if (throwIfVolatileField(t, field)) return; Compiler::Operand* table; if (instruction == getstatic) { - if ((classVmFlags(t, fieldClass(t, field)) & NeedInitFlag) - and (classVmFlags(t, fieldClass(t, field)) & InitFlag) == 0) + assert(t, fieldFlags(t, field) & ACC_STATIC); + + if (fieldClass(t, field) != methodClass(t, context->method) + and classNeedsInit(t, fieldClass(t, field))) { c->call (c->constant(getThunk(t, tryInitClassThunk)), @@ -2440,6 +2586,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, table = frame->append(classStaticTable(t, fieldClass(t, field))); } else { + assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0); + table = frame->popObject(); if (inTryBlock(t, code, ip - 3)) { @@ -2961,6 +3109,7 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } frame->pushAddress(frame->machineIp(ip)); + c->jmp(frame->machineIp(newIp)); Compiler::Subroutine* sr = c->startSubroutine(); @@ -3110,8 +3259,8 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, uint32_t defaultIp = base + codeReadInt32(t, code, ip); assert(t, defaultIp < codeLength(t, code)); - Compiler::Operand* default_ = c->address - (c->poolAppendPromise(c->machineIp(defaultIp))); + Compiler::Operand* default_ = frame->addressOperand + (c->machineIp(defaultIp)); int32_t pairCount = codeReadInt32(t, code, ip); @@ -3127,9 +3276,9 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, Promise* p = c->poolAppend(key); if (i == 0) { - start = c->promiseConstant(p); + start = frame->addressOperand(p); } - c->poolAppendPromise(c->machineIp(newIp)); + c->poolAppendPromise(frame->addressPromise(c->machineIp(newIp))); } assert(t, start); @@ -3296,51 +3445,13 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, Compiler::Operand* length = frame->popInt(); - object (*constructor)(Thread*, uintptr_t, bool); - switch (type) { - case T_BOOLEAN: - constructor = makeBooleanArray; - break; - - case T_CHAR: - constructor = makeCharArray; - break; - - case T_FLOAT: - constructor = makeFloatArray; - break; - - case T_DOUBLE: - constructor = makeDoubleArray; - break; - - case T_BYTE: - constructor = makeByteArray; - break; - - case T_SHORT: - constructor = makeShortArray; - break; - - case T_INT: - constructor = makeIntArray; - break; - - case T_LONG: - constructor = makeLongArray; - break; - - default: abort(t); - } - frame->pushObject (c->call (c->constant(getThunk(t, makeBlankArrayThunk)), 0, frame->trace(0, false), BytesPerWord, - 3, c->thread(), c->constant(reinterpret_cast(constructor)), - length)); + 3, c->thread(), c->constant(type), length)); } break; case nop: break; @@ -3359,12 +3470,15 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, object field = resolveField(t, codePool(t, code), index - 1); if (UNLIKELY(t->exception)) return; + if (throwIfVolatileField(t, field)) return; object staticTable = 0; if (instruction == putstatic) { - if ((classVmFlags(t, fieldClass(t, field)) & NeedInitFlag) - and (classVmFlags(t, fieldClass(t, field)) & InitFlag) == 0) + assert(t, fieldFlags(t, field) & ACC_STATIC); + + if (fieldClass(t, field) != methodClass(t, context->method) + and classNeedsInit(t, fieldClass(t, field))) { c->call (c->constant(getThunk(t, tryInitClassThunk)), @@ -3375,8 +3489,12 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, } staticTable = classStaticTable(t, fieldClass(t, field)); - } else if (inTryBlock(t, code, ip - 3)) { - c->saveLocals(); + } else { + assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0); + + if (inTryBlock(t, code, ip - 3)) { + c->saveLocals(); + } } Compiler::Operand* value; @@ -3489,9 +3607,10 @@ compile(MyThread* t, Frame* initialFrame, unsigned ip, ipTable[i] = newIp; - Promise* p = c->poolAppendPromise(c->machineIp(newIp)); + Promise* p = c->poolAppendPromise + (frame->addressPromise(c->machineIp(newIp))); if (i == 0) { - start = c->promiseConstant(p); + start = frame->addressOperand(p); } } assert(t, start); @@ -3840,31 +3959,17 @@ codeSingletonSizeInBytes(MyThread*, unsigned codeSizeInBytes) return pad(SingletonBody + (size * BytesPerWord)); } -object -allocateCode(MyThread* t, unsigned codeSizeInBytes) +uint8_t* +finish(MyThread* t, Allocator* allocator, Assembler* a, const char* name) { - unsigned count = ceiling(codeSizeInBytes, BytesPerWord); - unsigned size = count + singletonMaskSize(count); - object result = allocate3 - (t, codeZone(t), Machine::ImmortalAllocation, - SingletonBody + (size * BytesPerWord), true); - initSingleton(t, result, size, true); - mark(t, result, 0); - singletonMask(t, result)[0] = 1; - return result; -} - -object -finish(MyThread* t, Assembler* a, const char* name) -{ - object result = allocateCode(t, a->length()); - uint8_t* start = reinterpret_cast(&singletonValue(t, result, 0)); + uint8_t* start = static_cast + (allocator->allocate(pad(a->length()))); a->writeTo(start); logCompile(t, start, a->length(), 0, name, 0); - return result; + return start; } void @@ -3885,19 +3990,52 @@ clearBit(MyThread* t, object map, unsigned count, unsigned size, unsigned i, &= ~(static_cast(1) << (index % 32)); } -object -finish(MyThread* t, Context* context) +uint8_t* +finish(MyThread* t, Allocator* allocator, Context* context) { Compiler* c = context->compiler; unsigned codeSize = c->compile(); - object result = allocateCode(t, pad(codeSize) + c->poolSize()); - PROTECT(t, result); + uintptr_t* code = static_cast + (allocator->allocate(pad(codeSize) + pad(c->poolSize()) + BytesPerWord)); + code[0] = codeSize; + uint8_t* start = reinterpret_cast(code + 1); - uint8_t* start = reinterpret_cast(&singletonValue(t, result, 0)); + if (context->objectPool) { + object pool = allocate3 + (t, allocator, Machine::ImmortalAllocation, + FixedSizeOfArray + ((context->objectPoolCount + 1) * BytesPerWord), + true); + + initArray(t, pool, context->objectPoolCount + 1, false); + mark(t, pool, 0); + + set(t, pool, ArrayBody, objectPools(t)); + objectPools(t) = pool; + + unsigned i = 1; + for (PoolElement* p = context->objectPool; p; p = p->next) { + unsigned offset = ArrayBody + ((i++) * BytesPerWord); + + p->address = reinterpret_cast(pool) + offset; + + set(t, pool, offset, p->target); + } + } c->writeTo(start); + BootContext* bc = context->bootContext; + if (bc) { + for (DelayedPromise* p = bc->addresses; + p != bc->addressSentinal; + p = p->next) + { + p->basis = new (bc->zone->allocate(sizeof(ResolvedPromise))) + ResolvedPromise(p->basis->value()); + } + } + translateExceptionHandlerTable(t, c, methodCode(t, context->method), reinterpret_cast(start)); if (UNLIKELY(t->exception)) return 0; @@ -3996,14 +4134,6 @@ finish(MyThread* t, Context* context) set(t, methodCode(t, context->method), CodePool, map); } - for (PoolElement* p = context->objectPool; p; p = p->next) { - intptr_t offset = p->address->value() - reinterpret_cast(start); - - singletonMarkObject(t, result, offset / BytesPerWord); - - set(t, result, SingletonBody + offset, p->value); - } - logCompile (t, start, codeSize, reinterpret_cast @@ -4027,11 +4157,11 @@ finish(MyThread* t, Context* context) asm("int3"); } - return result; + return start; } -object -compile(MyThread* t, Context* context) +uint8_t* +compile(MyThread* t, Allocator* allocator, Context* context) { Compiler* c = context->compiler; @@ -4149,11 +4279,19 @@ compile(MyThread* t, Context* context) calculateFrameMaps(t, context, 0, 0, 0); } - return finish(t, context); + return finish(t, allocator, context); } void -compile(MyThread* t, object method); +updateCall(MyThread* t, UnaryOperation op, bool assertAlignment, + void* returnAddress, void* target) +{ + t->arch->updateCall(op, assertAlignment, returnAddress, target); +} + +void +compile(MyThread* t, Allocator* allocator, BootContext* bootContext, + object method); void* compileMethod2(MyThread* t) @@ -4169,23 +4307,24 @@ compileMethod2(MyThread* t) } if (LIKELY(t->exception == 0)) { - compile(t, target); + compile(t, codeZone(t), 0, target); } if (UNLIKELY(t->exception)) { return 0; } else { + void* address = reinterpret_cast(methodAddress(t, target)); if (callNodeVirtualCall(t, node)) { classVtable (t, objectClass (t, resolveThisPointer(t, t->stack, target)), methodOffset(t, target)) - = &singletonValue(t, methodCompiled(t, target), 0); + = address; } else { - t->arch->updateCall - (reinterpret_cast(callNodeAddress(t, node)), - &singletonValue(t, methodCompiled(t, target), 0)); + updateCall + (t, Call, true, reinterpret_cast(callNodeAddress(t, node)), + address); } - return &singletonValue(t, methodCompiled(t, target), 0); + return address; } } @@ -4211,7 +4350,7 @@ invokeNative2(MyThread* t, object method) initClass(t, methodClass(t, method)); if (UNLIKELY(t->exception)) return 0; - if (methodCode(t, method) == 0) { + if (methodCompiled(t, method) == defaultThunk(t)) { void* function = resolveNativeMethod(t, method); if (UNLIKELY(function == 0)) { object message = makeString @@ -4223,8 +4362,7 @@ invokeNative2(MyThread* t, object method) return 0; } - object p = makePointer(t, function); - set(t, method, MethodCode, p); + methodCompiled(t, method) = reinterpret_cast(function); } object class_ = methodClass(t, method); @@ -4290,7 +4428,7 @@ invokeNative2(MyThread* t, object method) } } - void* function = pointerValue(t, methodCode(t, method)); + void* function = reinterpret_cast(methodCompiled(t, method)); unsigned returnCode = methodReturnCode(t, method); unsigned returnType = fieldType(t, returnCode); uint64_t result; @@ -4451,7 +4589,7 @@ visitStackAndLocals(MyThread* t, Heap::Visitor* v, void* stack, object method, object map = codePool(t, methodCode(t, method)); int index = frameMapIndex (t, method, difference - (ip, &singletonValue(t, methodCompiled(t, method), 0))); + (ip, reinterpret_cast(methodAddress(t, method)))); for (unsigned i = 0; i < count; ++i) { int j = index + i; @@ -4640,8 +4778,8 @@ invoke(Thread* thread, object method, ArgumentList* arguments) } result = vmInvoke - (t, &singletonValue(t, methodCompiled(t, method), 0), arguments->array, - arguments->position, returnType); + (t, reinterpret_cast(methodAddress(t, method)), + arguments->array, arguments->position, returnType); } if (t->exception) { @@ -4724,8 +4862,15 @@ class SegFaultHandler: public System::SignalHandler { Machine* m; }; +void +boot(MyThread* t, BootImage* image); + class MyProcessor; +void +compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p, + BootImage* image, uint8_t* imageBase); + MyProcessor* processor(MyThread* t); @@ -4762,6 +4907,8 @@ class MyProcessor: public Processor { callTableSize(0), methodTree(0), methodTreeSentinal(0), + objectPools(0), + staticTableArray(0), codeAllocator(s), codeZone(s, &codeAllocator, 64 * 1024) { } @@ -4821,8 +4968,8 @@ class MyProcessor: public Processor { virtual void initVtable(Thread* t, object c) { - void* compiled = &singletonBody - (t, ::defaultThunk(static_cast(t)), 0); + void* compiled = reinterpret_cast + (::defaultThunk(static_cast(t))); for (unsigned i = 0; i < classLength(t, c); ++i) { classVtable(t, c, i) = compiled; @@ -4835,9 +4982,7 @@ class MyProcessor: public Processor { PROTECT(t, c); ACQUIRE(t, t->m->classLock); - if (classVmFlags(t, c) & NeedInitFlag - and (classVmFlags(t, c) & InitFlag) == 0) - { + if (classNeedsInit(t, c)) { classVmFlags(t, c) |= InitFlag; invoke(t, classInitializer(t, c), 0); if (t->exception) { @@ -4853,13 +4998,11 @@ class MyProcessor: public Processor { MyThread* t = static_cast(vmt); if (t == t->m->rootThread) { - v->visit(&defaultThunk); - v->visit(&nativeThunk); - v->visit(&aioobThunk); - v->visit(&thunkTable); v->visit(&callTable); v->visit(&methodTree); v->visit(&methodTreeSentinal); + v->visit(&objectPools); + v->visit(&staticTableArray); } for (MyThread::CallTrace* trace = t->trace; trace; trace = trace->next) { @@ -4932,7 +5075,7 @@ class MyProcessor: public Processor { PROTECT(t, method); - compile(static_cast(t), method); + compile(static_cast(t), &codeZone, 0, method); if (LIKELY(t->exception == 0)) { return ::invoke(t, method, &list); @@ -4963,7 +5106,7 @@ class MyProcessor: public Processor { PROTECT(t, method); - compile(static_cast(t), method); + compile(static_cast(t), &codeZone, 0, method); if (LIKELY(t->exception == 0)) { return ::invoke(t, method, &list); @@ -4993,7 +5136,7 @@ class MyProcessor: public Processor { PROTECT(t, method); - compile(static_cast(t), method); + compile(static_cast(t), &codeZone, 0, method); if (LIKELY(t->exception == 0)) { return ::invoke(t, method, &list); @@ -5026,7 +5169,7 @@ class MyProcessor: public Processor { virtual object getStackTrace(Thread* vmt, Thread* vmTarget) { MyThread* t = static_cast(vmt); MyThread* target = static_cast(vmTarget); - MyProcessor* p = processor(t); + MyProcessor* p = this; class Visitor: public System::ThreadVisitor { public: @@ -5044,8 +5187,7 @@ class MyProcessor: public Processor { target->base = base; target->stack = stack; } else { - uint8_t* thunkStart = reinterpret_cast - (&singletonValue(t, p->thunkTable, 0)); + uint8_t* thunkStart = p->thunkTable; uint8_t* thunkEnd = thunkStart + (p->thunkSize * ThunkCount); if (static_cast(ip) >= thunkStart @@ -5084,294 +5226,97 @@ class MyProcessor: public Processor { return visitor.trace; } + + virtual void compileThunks(Thread* vmt, BootImage* image, uint8_t* code, + unsigned* offset, unsigned capacity) + { + MyThread* t = static_cast(vmt); + FixedAllocator allocator(t, code + *offset, capacity); + + ::compileThunks(t, &allocator, this, image, code); + + *offset += allocator.offset; + } + + virtual void compileMethod(Thread* vmt, Zone* zone, uint8_t* code, + unsigned* offset, unsigned capacity, + object* constants, object* calls, + DelayedPromise** addresses, object method) + { + MyThread* t = static_cast(vmt); + FixedAllocator allocator(t, code + *offset, capacity); + BootContext bootContext(t, *constants, *calls, *addresses, zone); + + compile(t, &allocator, &bootContext, method); + + *constants = bootContext.constants; + *calls = bootContext.calls; + *addresses = bootContext.addresses; + *offset += allocator.offset; + } + + virtual void visitRoots(BootImage* image, HeapWalker* w) { + image->methodTree = w->visitRoot(methodTree); + image->methodTreeSentinal = w->visitRoot(methodTreeSentinal); + } + + virtual unsigned* makeCallTable(Thread* t, BootImage* image, HeapWalker* w, + uint8_t* code) + { + image->callCount = callTableSize; + + unsigned* table = static_cast + (t->m->heap->allocate(callTableSize * sizeof(unsigned) * 2)); + + unsigned index = 0; + for (unsigned i = 0; i < arrayLength(t, callTable); ++i) { + for (object p = arrayBody(t, callTable, i); p; p = callNodeNext(t, p)) { + table[index++] = callNodeAddress(t, p) + - reinterpret_cast(code); + table[index++] = w->map()->find(callNodeTarget(t, p)) + | (static_cast(callNodeVirtualCall(t, p)) << BootShift); + } + } + + return table; + } + + virtual void boot(Thread* t, BootImage* image) { + if (image) { + ::boot(static_cast(t), image); + } else { + callTable = makeArray(t, 128, true); + + methodTree = methodTreeSentinal = makeTreeNode(t, 0, 0, 0); + set(t, methodTree, TreeNodeLeft, methodTreeSentinal); + set(t, methodTree, TreeNodeRight, methodTreeSentinal); + + ::compileThunks(static_cast(t), &codeZone, this, 0, 0); + } + + segFaultHandler.m = t->m; + expect(t, t->m->system->success + (t->m->system->handleSegFault(&segFaultHandler))); + } System* s; Allocator* allocator; - object defaultThunk; - object nativeThunk; - object aioobThunk; - object thunkTable; + uint8_t* defaultThunk; + uint8_t* nativeThunk; + uint8_t* aioobThunk; + uint8_t* thunkTable; unsigned thunkSize; object callTable; unsigned callTableSize; object methodTree; object methodTreeSentinal; + object objectPools; + object staticTableArray; SegFaultHandler segFaultHandler; CodeAllocator codeAllocator; Zone codeZone; }; -intptr_t -getThunk(MyThread* t, Thunk thunk) -{ - MyProcessor* p = processor(t); - - return reinterpret_cast - (&singletonValue(t, p->thunkTable, (thunk * p->thunkSize) / BytesPerWord)); -} - -void -compileThunks(MyThread* t, MyProcessor* p) -{ - class ThunkContext { - public: - class MyPromise: public Promise { - public: - MyPromise(): resolved_(false) { } - - virtual int64_t value() { - return value_; - } - - virtual bool resolved() { - return resolved_; - } - - int64_t value_; - bool resolved_; - }; - - ThunkContext(MyThread* t): context(t) { } - - Context context; - MyPromise promise; - }; - - ThunkContext defaultContext(t); - - { Assembler* a = defaultContext.context.assembler; - - a->saveFrame(difference(&(t->stack), t), difference(&(t->base), t)); - - Assembler::Register thread(t->arch->thread()); - a->pushFrame(1, BytesPerWord, RegisterOperand, &thread); - - defaultContext.promise.resolved_ = true; - defaultContext.promise.value_ = reinterpret_cast(compileMethod); - - Assembler::Constant proc(&(defaultContext.promise)); - a->apply(LongCall, BytesPerWord, ConstantOperand, &proc); - - a->popFrame(); - - Assembler::Register result(t->arch->returnLow()); - a->apply(Jump, BytesPerWord, RegisterOperand, &result); - - a->endBlock(false)->resolve(0, 0); - } - - ThunkContext nativeContext(t); - - { Assembler* a = nativeContext.context.assembler; - - a->saveFrame(difference(&(t->stack), t), difference(&(t->base), t)); - - Assembler::Register thread(t->arch->thread()); - a->pushFrame(1, BytesPerWord, RegisterOperand, &thread); - - nativeContext.promise.resolved_ = true; - nativeContext.promise.value_ = reinterpret_cast(invokeNative); - - Assembler::Constant proc(&(nativeContext.promise)); - a->apply(LongCall, BytesPerWord, ConstantOperand, &proc); - - a->popFrame(); - - a->apply(Return); - - a->endBlock(false)->resolve(0, 0); - } - - ThunkContext aioobContext(t); - - { Assembler* a = aioobContext.context.assembler; - - a->saveFrame(difference(&(t->stack), t), difference(&(t->base), t)); - - Assembler::Register thread(t->arch->thread()); - a->pushFrame(1, BytesPerWord, RegisterOperand, &thread); - - aioobContext.promise.resolved_ = true; - aioobContext.promise.value_ = reinterpret_cast - (throwArrayIndexOutOfBounds); - - Assembler::Constant proc(&(aioobContext.promise)); - a->apply(LongCall, BytesPerWord, ConstantOperand, &proc); - - a->endBlock(false)->resolve(0, 0); - } - - ThunkContext tableContext(t); - - { Assembler* a = tableContext.context.assembler; - - a->saveFrame(difference(&(t->stack), t), difference(&(t->base), t)); - - Assembler::Constant proc(&(tableContext.promise)); - a->apply(LongJump, BytesPerWord, ConstantOperand, &proc); - - a->endBlock(false)->resolve(0, 0); - } - - p->thunkSize = pad(tableContext.context.assembler->length()); - - expect(t, codeZone(t)->ensure - (codeSingletonSizeInBytes - (t, defaultContext.context.assembler->length()) - + codeSingletonSizeInBytes - (t, nativeContext.context.assembler->length()) - + codeSingletonSizeInBytes - (t, aioobContext.context.assembler->length()) - + codeSingletonSizeInBytes - (t, p->thunkSize * ThunkCount))); - - p->defaultThunk = finish(t, defaultContext.context.assembler, "default"); - p->nativeThunk = finish(t, nativeContext.context.assembler, "native"); - p->aioobThunk = finish(t, aioobContext.context.assembler, "aioob"); - - p->thunkTable = allocateCode(t, p->thunkSize * ThunkCount); - uint8_t* start = reinterpret_cast - (&singletonValue(t, p->thunkTable, 0)); - - logCompile(t, start, p->thunkSize * ThunkCount, 0, "thunkTable", 0); - - tableContext.promise.resolved_ = true; - -#define THUNK(s) \ - tableContext.promise.value_ = reinterpret_cast(s); \ - tableContext.context.assembler->writeTo(start); \ - start += p->thunkSize; - -#include "thunks.cpp" - -#undef THUNK -} - -MyProcessor* -processor(MyThread* t) -{ - MyProcessor* p = static_cast(t->m->processor); - if (p->callTable == 0) { - ACQUIRE(t, t->m->classLock); - - if (p->callTable == 0) { - p->callTable = makeArray(t, 128, true); - - p->methodTree = p->methodTreeSentinal = makeTreeNode(t, 0, 0, 0); - set(t, p->methodTree, TreeNodeLeft, p->methodTreeSentinal); - set(t, p->methodTree, TreeNodeRight, p->methodTreeSentinal); - - compileThunks(t, p); - - p->segFaultHandler.m = t->m; - expect(t, t->m->system->success - (t->m->system->handleSegFault(&(p->segFaultHandler)))); - } - } - return p; -} - -object -defaultThunk(MyThread* t) -{ - return processor(t)->defaultThunk; -} - -object -nativeThunk(MyThread* t) -{ - return processor(t)->nativeThunk; -} - -object -aioobThunk(MyThread* t) -{ - return processor(t)->aioobThunk; -} - -void -compile(MyThread* t, object method) -{ - MyProcessor* p = processor(t); - - if (methodCompiled(t, method) == p->defaultThunk) { - PROTECT(t, method); - - ACQUIRE(t, t->m->classLock); - - if (methodCompiled(t, method) == p->defaultThunk) { - initClass(t, methodClass(t, method)); - if (UNLIKELY(t->exception)) return; - - if (methodCompiled(t, method) == p->defaultThunk) { - object node; - object compiled; - if (methodFlags(t, method) & ACC_NATIVE) { - node = 0; - compiled = p->nativeThunk; - } else { - Context context(t, method); - compiled = compile(t, &context); - if (UNLIKELY(t->exception)) return; - - PROTECT(t, compiled); - - if (DebugMethodTree) { - fprintf(stderr, "insert method at %p\n", - &singletonValue(t, compiled, 0)); - } - - // We can't set the MethodCompiled field on the original - // method before it is placed into the method tree, since - // another thread might call the method, from which stack - // unwinding would fail (since there is not yet an entry in - // the method tree). However, we can't insert the original - // method into the tree before setting the MethodCompiled - // field on it since we rely on that field to determine its - // position in the tree. Therefore, we insert a clone in - // its place. Later, we'll replace the clone with the - // original to save memory. - - object clone = makeMethod - (t, methodVmFlags(t, method), - methodReturnCode(t, method), - methodParameterCount(t, method), - methodParameterFootprint(t, method), - methodFlags(t, method), - methodOffset(t, method), - methodNativeID(t, method), - methodName(t, method), - methodSpec(t, method), - methodClass(t, method), - methodCode(t, method), - compiled); - - node = makeTreeNode - (t, clone, methodTreeSentinal(t), methodTreeSentinal(t)); - - PROTECT(t, node); - - methodTree(t) = treeInsertNode - (t, &(context.zone), methodTree(t), reinterpret_cast - (&singletonValue(t, compiled, 0)), node, methodTreeSentinal(t), - compareIpToMethodBounds); - } - - set(t, method, MethodCompiled, compiled); - - if (methodVirtual(t, method)) { - classVtable(t, methodClass(t, method), methodOffset(t, method)) - = &singletonValue(t, compiled, 0); - } - - if (node) { - set(t, node, TreeNodeValue, method); - } - } - } - } -} - object findCallNode(MyThread* t, void* address) { @@ -5432,30 +5377,551 @@ resizeTable(MyThread* t, object oldTable, unsigned newLength) return newTable; } -void -insertCallNode(MyThread* t, object node) +object +insertCallNode(MyThread* t, object table, unsigned* size, object node) { if (DebugCallTable) { fprintf(stderr, "insert call node %p\n", reinterpret_cast(callNodeAddress(t, node))); } - MyProcessor* p = processor(t); + PROTECT(t, table); PROTECT(t, node); - ++ p->callTableSize; + ++ (*size); - if (p->callTableSize >= arrayLength(t, p->callTable) * 2) { - p->callTable = resizeTable - (t, p->callTable, arrayLength(t, p->callTable) * 2); + if (*size >= arrayLength(t, table) * 2) { + table = resizeTable(t, table, arrayLength(t, table) * 2); } intptr_t key = callNodeAddress(t, node); - unsigned index = static_cast(key) - & (arrayLength(t, p->callTable) - 1); + unsigned index = static_cast(key) & (arrayLength(t, table) - 1); - set(t, node, CallNodeNext, arrayBody(t, p->callTable, index)); - set(t, p->callTable, ArrayBody + (index * BytesPerWord), node); + set(t, node, CallNodeNext, arrayBody(t, table, index)); + set(t, table, ArrayBody + (index * BytesPerWord), node); + + return table; +} + +void +insertCallNode(MyThread* t, object node) +{ + MyProcessor* p = processor(t); + p->callTable = insertCallNode(t, p->callTable, &(p->callTableSize), node); +} + +object +makeClassMap(Thread* t, unsigned* table, unsigned count, uintptr_t* heap) +{ + object array = makeArray(t, nextPowerOfTwo(count), true); + object map = makeHashMap(t, 0, array); + PROTECT(t, map); + + for (unsigned i = 0; i < count; ++i) { + object c = bootObject(heap, table[i]); + hashMapInsert(t, map, className(t, c), c, byteArrayHash); + } + + return map; +} + +object +makeStaticTableArray(Thread* t, unsigned* table, unsigned count, + uintptr_t* heap) +{ + object array = makeArray(t, count, false); + + for (unsigned i = 0; i < count; ++i) { + set(t, array, ArrayBody + (i * BytesPerWord), + classStaticTable(t, bootObject(heap, table[i]))); + } + + return array; +} + +object +makeStringMap(Thread* t, unsigned* table, unsigned count, uintptr_t* heap) +{ + object array = makeArray(t, nextPowerOfTwo(count), true); + object map = makeWeakHashMap(t, 0, array); + PROTECT(t, map); + + for (unsigned i = 0; i < count; ++i) { + object s = bootObject(heap, table[i]); + hashMapInsert(t, map, s, 0, stringHash); + } + + return map; +} + +object +makeCallTable(MyThread* t, uintptr_t* heap, unsigned* calls, unsigned count, + uintptr_t base) +{ + object table = makeArray(t, nextPowerOfTwo(count), true); + PROTECT(t, table); + + unsigned size = 0; + for (unsigned i = 0; i < count; ++i) { + unsigned address = calls[i * 2]; + unsigned target = calls[(i * 2) + 1]; + + object node = makeCallNode + (t, base + address, bootObject(heap, target & BootMask), + target >> BootShift, 0); + + table = insertCallNode(t, table, &size, node); + } + + return table; +} + +void +fixupHeap(MyThread* t UNUSED, uintptr_t* map, unsigned size, uintptr_t* heap) +{ + for (unsigned word = 0; word < size; ++word) { + uintptr_t w = map[word]; + if (w) { + for (unsigned bit = 0; bit < BitsPerWord; ++bit) { + if (w & (static_cast(1) << bit)) { + unsigned index = indexOf(word, bit); + uintptr_t* p = heap + index; + assert(t, *p); + + uintptr_t number = *p & BootMask; + uintptr_t mark = *p >> BootShift; + + if (number) { + *p = reinterpret_cast(heap + (number - 1)) | mark; + } else { + *p = mark; + } + } + } + } + } +} + +void +fixupCode(Thread*, uintptr_t* map, unsigned size, uint8_t* code, + uintptr_t* heap) +{ + for (unsigned word = 0; word < size; ++word) { + uintptr_t w = map[word]; + if (w) { + for (unsigned bit = 0; bit < BitsPerWord; ++bit) { + if (w & (static_cast(1) << bit)) { + unsigned index = indexOf(word, bit); + uintptr_t v; memcpy(&v, code + index, BytesPerWord); + uintptr_t mark = v >> BootShift; + if (mark) { + v = reinterpret_cast(code + (v & BootMask)); + memcpy(code + index, &v, BytesPerWord); + } else { + v = reinterpret_cast(heap + v - 1); + memcpy(code + index, &v, BytesPerWord); + } + } + } + } + } +} + +void +fixupMethods(Thread* t, BootImage* image, uint8_t* code) +{ + for (HashMapIterator it(t, t->m->classMap); it.hasMore();) { + object c = tripleSecond(t, it.next()); + + if (classMethodTable(t, c)) { + for (unsigned i = 0; i < arrayLength(t, classMethodTable(t, c)); ++i) { + object method = arrayBody(t, classMethodTable(t, c), i); + if (methodCode(t, method) or (methodFlags(t, method) & ACC_NATIVE)) { + assert(t, (methodCompiled(t, method) - image->codeBase) + <= image->codeSize); + + methodCompiled(t, method) + = (methodCompiled(t, method) - image->codeBase) + + reinterpret_cast(code); + + if (DebugCompile and (methodFlags(t, method) & ACC_NATIVE) == 0) { + logCompile + (static_cast(t), + reinterpret_cast(methodCompiled(t, method)), + reinterpret_cast + (methodCompiled(t, method))[-1], + reinterpret_cast + (&byteArrayBody(t, className(t, methodClass(t, method)), 0)), + reinterpret_cast + (&byteArrayBody(t, methodName(t, method), 0)), + reinterpret_cast + (&byteArrayBody(t, methodSpec(t, method), 0))); + } + } + } + } + + t->m->processor->initVtable(t, c); + } +} + +void +fixupThunks(MyThread* t, BootImage* image, uint8_t* code) +{ + MyProcessor* p = processor(t); + + p->defaultThunk = code + image->defaultThunk; + + updateCall(t, LongCall, false, code + image->compileMethodCall, + voidPointer(::compileMethod)); + + p->nativeThunk = code + image->nativeThunk; + + updateCall(t, LongCall, false, code + image->invokeNativeCall, + voidPointer(invokeNative)); + + p->aioobThunk = code + image->aioobThunk; + + updateCall(t, LongCall, false, + code + image->throwArrayIndexOutOfBoundsCall, + voidPointer(throwArrayIndexOutOfBounds)); + + p->thunkTable = code + image->thunkTable; + p->thunkSize = image->thunkSize; + +#define THUNK(s) \ + updateCall(t, LongJump, false, code + image->s##Call, voidPointer(s)); + +#include "thunks.cpp" + +#undef THUNK +} + +void +boot(MyThread* t, BootImage* image) +{ + assert(t, image->magic == BootImage::Magic); + + unsigned* classTable = reinterpret_cast(image + 1); + unsigned* stringTable = classTable + image->classCount; + unsigned* callTable = stringTable + image->stringCount; + + uintptr_t* heapMap = reinterpret_cast + (pad(reinterpret_cast(callTable + (image->callCount * 2)))); + unsigned heapMapSizeInWords = ceiling + (heapMapSize(image->heapSize), BytesPerWord); + uintptr_t* heap = heapMap + heapMapSizeInWords; + +// fprintf(stderr, "heap from %p to %p\n", +// heap, heap + ceiling(image->heapSize, BytesPerWord)); + + uintptr_t* codeMap = heap + ceiling(image->heapSize, BytesPerWord); + unsigned codeMapSizeInWords = ceiling + (codeMapSize(image->codeSize), BytesPerWord); + uint8_t* code = reinterpret_cast(codeMap + codeMapSizeInWords); + +// fprintf(stderr, "code from %p to %p\n", +// code, code + image->codeSize); + + fixupHeap(t, heapMap, heapMapSizeInWords, heap); + + t->m->heap->setImmortalHeap(heap, image->heapSize / BytesPerWord); + + t->m->loader = bootObject(heap, image->loader); + t->m->types = bootObject(heap, image->types); + + MyProcessor* p = static_cast(t->m->processor); + + p->methodTree = bootObject(heap, image->methodTree); + p->methodTreeSentinal = bootObject(heap, image->methodTreeSentinal); + + fixupCode(t, codeMap, codeMapSizeInWords, code, heap); + + t->m->classMap = makeClassMap(t, classTable, image->classCount, heap); + t->m->stringMap = makeStringMap(t, stringTable, image->stringCount, heap); + + p->callTableSize = image->callCount; + p->callTable = makeCallTable + (t, heap, callTable, image->callCount, + reinterpret_cast(code)); + + p->staticTableArray = makeStaticTableArray + (t, classTable, image->classCount, heap); + + fixupThunks(t, image, code); + + fixupMethods(t, image, code); + + t->m->bootstrapClassMap = makeHashMap(t, 0, 0); +} + +intptr_t +getThunk(MyThread* t, Thunk thunk) +{ + MyProcessor* p = processor(t); + + return reinterpret_cast + (p->thunkTable + (thunk * p->thunkSize)); +} + +void +compileThunks(MyThread* t, Allocator* allocator, MyProcessor* p, + BootImage* image, uint8_t* imageBase) +{ + class ThunkContext { + public: + ThunkContext(MyThread* t, Zone* zone): + context(t), promise(t->m->system, zone) + { } + + Context context; + ListenPromise promise; + }; + + Zone zone(t->m->system, t->m->heap, 1024); + ThunkContext defaultContext(t, &zone); + + { Assembler* a = defaultContext.context.assembler; + + a->saveFrame(difference(&(t->stack), t), difference(&(t->base), t)); + + Assembler::Register thread(t->arch->thread()); + a->pushFrame(1, BytesPerWord, RegisterOperand, &thread); + + Assembler::Constant proc(&(defaultContext.promise)); + a->apply(LongCall, BytesPerWord, ConstantOperand, &proc); + + a->popFrame(); + + Assembler::Register result(t->arch->returnLow()); + a->apply(Jump, BytesPerWord, RegisterOperand, &result); + + a->endBlock(false)->resolve(0, 0); + } + + ThunkContext nativeContext(t, &zone); + + { Assembler* a = nativeContext.context.assembler; + + a->saveFrame(difference(&(t->stack), t), difference(&(t->base), t)); + + Assembler::Register thread(t->arch->thread()); + a->pushFrame(1, BytesPerWord, RegisterOperand, &thread); + + nativeContext.promise.resolved_ = true; + nativeContext.promise.value_ = reinterpret_cast(invokeNative); + + Assembler::Constant proc(&(nativeContext.promise)); + a->apply(LongCall, BytesPerWord, ConstantOperand, &proc); + + a->popFrame(); + + a->apply(Return); + + a->endBlock(false)->resolve(0, 0); + } + + ThunkContext aioobContext(t, &zone); + + { Assembler* a = aioobContext.context.assembler; + + a->saveFrame(difference(&(t->stack), t), difference(&(t->base), t)); + + Assembler::Register thread(t->arch->thread()); + a->pushFrame(1, BytesPerWord, RegisterOperand, &thread); + + Assembler::Constant proc(&(aioobContext.promise)); + a->apply(LongCall, BytesPerWord, ConstantOperand, &proc); + + a->endBlock(false)->resolve(0, 0); + } + + ThunkContext tableContext(t, &zone); + + { Assembler* a = tableContext.context.assembler; + + a->saveFrame(difference(&(t->stack), t), difference(&(t->base), t)); + + Assembler::Constant proc(&(tableContext.promise)); + a->apply(LongJump, BytesPerWord, ConstantOperand, &proc); + + a->endBlock(false)->resolve(0, 0); + } + + p->thunkSize = pad(tableContext.context.assembler->length()); + + expect(t, codeZone(t)->ensure + (codeSingletonSizeInBytes + (t, defaultContext.context.assembler->length()) + + codeSingletonSizeInBytes + (t, nativeContext.context.assembler->length()) + + codeSingletonSizeInBytes + (t, aioobContext.context.assembler->length()) + + codeSingletonSizeInBytes + (t, p->thunkSize * ThunkCount))); + + p->defaultThunk = finish + (t, allocator, defaultContext.context.assembler, "default"); + + { uint8_t* call = static_cast + (defaultContext.promise.listener->resolve + (reinterpret_cast(voidPointer(compileMethod)))); + + if (image) { + image->defaultThunk = p->defaultThunk - imageBase; + image->compileMethodCall = call - imageBase; + } + } + + p->nativeThunk = finish + (t, allocator, nativeContext.context.assembler, "native"); + + { uint8_t* call = static_cast + (nativeContext.promise.listener->resolve + (reinterpret_cast(voidPointer(invokeNative)))); + + if (image) { + image->nativeThunk = p->nativeThunk - imageBase; + image->invokeNativeCall = call - imageBase; + } + } + + p->aioobThunk = finish + (t, allocator, aioobContext.context.assembler, "aioob"); + + { uint8_t* call = static_cast + (aioobContext.promise.listener->resolve + (reinterpret_cast(voidPointer(throwArrayIndexOutOfBounds)))); + + if (image) { + image->aioobThunk = p->aioobThunk - imageBase; + image->throwArrayIndexOutOfBoundsCall = call - imageBase; + } + } + + p->thunkTable = static_cast + (allocator->allocate(p->thunkSize * ThunkCount)); + + if (image) { + image->thunkTable = p->thunkTable - imageBase; + image->thunkSize = p->thunkSize; + } + + logCompile(t, p->thunkTable, p->thunkSize * ThunkCount, 0, "thunkTable", 0); + + uint8_t* start = p->thunkTable; + +#define THUNK(s) \ + tableContext.context.assembler->writeTo(start); \ + start += p->thunkSize; \ + { uint8_t* call = static_cast \ + (tableContext.promise.listener->resolve \ + (reinterpret_cast(voidPointer(s)))); \ + if (image) { \ + image->s##Call = call - imageBase; \ + } \ + } + +#include "thunks.cpp" + +#undef THUNK +} + +MyProcessor* +processor(MyThread* t) +{ + return static_cast(t->m->processor); +} + +object& +objectPools(MyThread* t) +{ + return processor(t)->objectPools; +} + +uintptr_t +defaultThunk(MyThread* t) +{ + return reinterpret_cast(processor(t)->defaultThunk); +} + +uintptr_t +nativeThunk(MyThread* t) +{ + return reinterpret_cast(processor(t)->nativeThunk); +} + +uintptr_t +aioobThunk(MyThread* t) +{ + return reinterpret_cast(processor(t)->aioobThunk); +} + +void +compile(MyThread* t, Allocator* allocator, BootContext* bootContext, + object method) +{ + PROTECT(t, method); + + if (bootContext == 0) { + initClass(t, methodClass(t, method)); + if (UNLIKELY(t->exception)) return; + } + + if (methodAddress(t, method) == defaultThunk(t)) { + ACQUIRE(t, t->m->classLock); + + if (methodAddress(t, method) == defaultThunk(t)) { + assert(t, (methodFlags(t, method) & ACC_NATIVE) == 0); + + Context context(t, bootContext, method); + uint8_t* compiled = compile(t, allocator, &context); + if (UNLIKELY(t->exception)) return; + + if (DebugMethodTree) { + fprintf(stderr, "insert method at %p\n", compiled); + } + + // We can't set the MethodCompiled field on the original method + // before it is placed into the method tree, since another + // thread might call the method, from which stack unwinding + // would fail (since there is not yet an entry in the method + // tree). However, we can't insert the original method into the + // tree before setting the MethodCompiled field on it since we + // rely on that field to determine its position in the tree. + // Therefore, we insert a clone in its place. Later, we'll + // replace the clone with the original to save memory. + + object clone = makeMethod + (t, methodVmFlags(t, method), + methodReturnCode(t, method), + methodParameterCount(t, method), + methodParameterFootprint(t, method), + methodFlags(t, method), + methodOffset(t, method), + methodNativeID(t, method), + methodName(t, method), + methodSpec(t, method), + methodClass(t, method), + methodCode(t, method), + reinterpret_cast(compiled)); + + methodTree(t) = treeInsert + (t, &(context.zone), methodTree(t), + reinterpret_cast(compiled), clone, methodTreeSentinal(t), + compareIpToMethodBounds); + + methodCompiled(t, method) = reinterpret_cast(compiled); + + if (methodVirtual(t, method)) { + classVtable(t, methodClass(t, method), methodOffset(t, method)) + = compiled; + } + + treeUpdate(t, methodTree(t), reinterpret_cast(compiled), + method, methodTreeSentinal(t), compareIpToMethodBounds); + } + } } object& diff --git a/src/compiler.cpp b/src/compiler.cpp index 031a80f7d3..8b9a4c1873 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -5044,8 +5044,26 @@ class MyCompiler: public Compiler { int i = 0; for (ConstantPoolNode* n = c.firstConstant; n; n = n->next) { - *reinterpret_cast(dst + pad(c.machineCodeSize) + i) - = n->promise->value(); + intptr_t* target = reinterpret_cast + (dst + pad(c.assembler->length()) + i); + + if (n->promise->resolved()) { + *target = n->promise->value(); + } else { + class Listener: public Promise::Listener { + public: + Listener(intptr_t* target): target(target){ } + + virtual void* resolve(int64_t value) { + *target = value; + return target; + } + + intptr_t* target; + }; + new (n->promise->listen(sizeof(Listener))) Listener(target); + } + i += BytesPerWord; } } diff --git a/src/finder.cpp b/src/finder.cpp index 0f8fa0d793..ae784eb48c 100644 --- a/src/finder.cpp +++ b/src/finder.cpp @@ -51,8 +51,15 @@ equal(const void* a, unsigned al, const void* b, unsigned bl) class Element { public: + class Iterator { + public: + virtual const char* next(unsigned* size) = 0; + virtual void dispose() = 0; + }; + Element(): next(0) { } + virtual Iterator* iterator() = 0; virtual System::Region* find(const char* name) = 0; virtual bool exists(const char* name) = 0; virtual void dispose() = 0; @@ -62,10 +69,71 @@ class Element { class DirectoryElement: public Element { public: + class Iterator: public Element::Iterator { + public: + Iterator(System* s, const char* name, unsigned skip): + s(s), name(name), skip(skip), directory(0), last(0), it(0) + { + if (not s->success(s->open(&directory, name))) { + directory = 0; + } + } + + virtual const char* next(unsigned* size) { + if (it) { + const char* v = it->next(size); + if (v) { + return v; + } else { + it->dispose(); + it = 0; + } + } + + if (last) { + s->free(last); + } + + if (directory) { + for (const char* v = directory->next(); v; v = directory->next()) { + if (v[0] != '.') { + last = append(s, name, "/", v); + if (s->identify(last) == System::TypeDirectory) { + it = new (allocate(s, sizeof(Iterator))) Iterator(s, last, skip); + it->name = last; + } + const char* result = last + skip; + *size = strlen(result); + return result; + } + } + } + + return 0; + } + + virtual void dispose() { + directory->dispose(); + s->free(this); + } + + System* s; + const char* name; + unsigned skip; + System::Directory* directory; + const char* last; + Iterator* it; + }; + DirectoryElement(System* s, const char* name): s(s), name(name) { } + virtual Element::Iterator* iterator() { + return new (allocate(s, sizeof(Iterator))) + Iterator(s, name, strlen(name) + 1); + } + virtual System::Region* find(const char* name) { const char* file = append(s, this->name, "/", name); System::Region* region; @@ -83,7 +151,7 @@ class DirectoryElement: public Element { const char* file = append(s, this->name, "/", name); System::FileType type = s->identify(file); s->free(file); - return type != System::DoesNotExist; + return type != System::TypeDoesNotExist; } virtual void dispose() { @@ -371,10 +439,39 @@ class JarIndex { class JarElement: public Element { public: + class Iterator: public Element::Iterator { + public: + Iterator(System* s, JarIndex* index): s(s), index(index), position(0) { } + + virtual const char* next(unsigned* size) { + if (position < index->position) { + JarIndex::Node* n = index->nodes + (position++); + *size = JarIndex::fileNameLength(n->entry); + return reinterpret_cast(JarIndex::fileName(n->entry)); + } else { + return 0; + } + } + + virtual void dispose() { + s->free(this); + } + + System* s; + JarIndex* index; + unsigned position; + }; + JarElement(System* s, const char* name): s(s), name(name), region(0), index(0) { } + virtual Element::Iterator* iterator() { + init(); + + return new (allocate(s, sizeof(Iterator))) Iterator(s, index); + } + virtual void init() { if (index == 0) { System::Region* r; @@ -504,11 +601,11 @@ parsePath(System* s, const char* path, const char* bootLibrary) name[token.length] = 0; switch (s->identify(name)) { - case System::File: { + case System::TypeFile: { e = new (allocate(s, sizeof(JarElement))) JarElement(s, name); } break; - case System::Directory: { + case System::TypeDirectory: { e = new (allocate(s, sizeof(DirectoryElement))) DirectoryElement(s, name); } break; @@ -533,6 +630,40 @@ parsePath(System* s, const char* path, const char* bootLibrary) return first; } +class MyIterator: public Finder::IteratorImp { + public: + MyIterator(System* s, Element* path): + s(s), e(path ? path->next : 0), it(path ? path->iterator() : 0) + { } + + virtual const char* next(unsigned* size) { + while (it) { + const char* v = it->next(size); + if (v) { + return v; + } else { + it->dispose(); + if (e) { + it = e->iterator(); + e = e->next; + } else { + it = 0; + } + } + } + return 0; + } + + virtual void dispose() { + if (it) it->dispose(); + s->free(this); + } + + System* s; + Element* e; + Element::Iterator* it; +}; + class MyFinder: public Finder { public: MyFinder(System* system, const char* path, const char* bootLibrary): @@ -541,6 +672,11 @@ class MyFinder: public Finder { pathString(copy(system, path)) { } + virtual IteratorImp* iterator() { + return new (allocate(system, sizeof(MyIterator))) + MyIterator(system, path_); + } + virtual System::Region* find(const char* name) { for (Element* e = path_; e; e = e->next) { System::Region* r = e->find(name); diff --git a/src/finder.h b/src/finder.h index 64eb4c287b..4cd1256baf 100644 --- a/src/finder.h +++ b/src/finder.h @@ -19,6 +19,46 @@ namespace vm { class Finder { public: + class IteratorImp { + public: + virtual const char* next(unsigned* size) = 0; + virtual void dispose() = 0; + }; + + class Iterator { + public: + Iterator(Finder* finder): + it(finder->iterator()), + current(it->next(¤tSize)) + { } + + ~Iterator() { + it->dispose(); + } + + bool hasMore() { + if (current) return true; + current = it->next(¤tSize); + return current != 0; + } + + const char* next(unsigned* size) { + if (hasMore()) { + *size = currentSize; + const char* v = current; + current = 0; + return v; + } else { + return 0; + } + } + + IteratorImp* it; + const char* current; + unsigned currentSize; + }; + + virtual IteratorImp* iterator() = 0; virtual System::Region* find(const char* name) = 0; virtual bool exists(const char* name) = 0; virtual const char* path() = 0; diff --git a/src/heap.cpp b/src/heap.cpp index a70f176b3e..f04d5de2c3 100644 --- a/src/heap.cpp +++ b/src/heap.cpp @@ -121,17 +121,17 @@ class Segment { for (; word <= wordLimit and (word < wordLimit or bit < bitLimit); ++word) { - uintptr_t* p = map->data() + word; - if (*p) { + uintptr_t w = map->data[word]; + if (2) { for (; bit < BitsPerWord and (word < wordLimit or bit < bitLimit); ++bit) { - if (map->data()[word] & (static_cast(1) << bit)) { + if (w & (static_cast(1) << bit)) { index = ::indexOf(word, bit); -// printf("hit at index %d\n", index); + // printf("hit at index %d\n", index); return true; } else { -// printf("miss at index %d\n", indexOf(word, bit)); + // printf("miss at index %d\n", indexOf(word, bit)); } } } @@ -153,14 +153,26 @@ class Segment { Segment* segment; Map* child; + uintptr_t* data; unsigned bitsPerRecord; unsigned scale; bool clearNewData; - Map(Segment* segment, unsigned bitsPerRecord, unsigned scale, - Map* child, bool clearNewData): + Map(Segment* segment, uintptr_t* data, unsigned bitsPerRecord, + unsigned scale, Map* child, bool clearNewData): segment(segment), child(child), + data(data), + bitsPerRecord(bitsPerRecord), + scale(scale), + clearNewData(clearNewData) + { } + + Map(Segment* segment, unsigned bitsPerRecord, unsigned scale, Map* child, + bool clearNewData): + segment(segment), + child(child), + data(0), bitsPerRecord(bitsPerRecord), scale(scale), clearNewData(clearNewData) @@ -171,8 +183,13 @@ class Segment { assert(segment->context, scale); assert(segment->context, powerOfTwo(scale)); + if (data == 0) { + data = segment->data + segment->capacity() + + calculateOffset(segment->capacity()); + } + if (clearNewData) { - memset(data(), 0, size() * BytesPerWord); + memset(data, 0, size() * BytesPerWord); } if (child) { @@ -180,40 +197,47 @@ class Segment { } } + unsigned calculateOffset(unsigned capacity) { + unsigned n = 0; + if (child) n += child->calculateFootprint(capacity); + return n; + } + + static unsigned calculateSize(Context* c UNUSED, unsigned capacity, + unsigned scale, unsigned bitsPerRecord) + { + unsigned result + = ceiling(ceiling(capacity, scale) * bitsPerRecord, BitsPerWord); + assert(c, result); + return result; + } + + unsigned calculateSize(unsigned capacity) { + return calculateSize(segment->context, capacity, scale, bitsPerRecord); + } + + unsigned size() { + return calculateSize(segment->capacity()); + } + + unsigned calculateFootprint(unsigned capacity) { + unsigned n = calculateSize(capacity); + if (child) n += child->calculateFootprint(capacity); + return n; + } + void replaceWith(Map* m) { assert(segment->context, bitsPerRecord == m->bitsPerRecord); assert(segment->context, scale == m->scale); + data = m->data; + m->segment = 0; + m->data = 0; if (child) child->replaceWith(m->child); } - unsigned offset(unsigned capacity) { - unsigned n = 0; - if (child) n += child->footprint(capacity); - return n; - } - - unsigned offset() { - return offset(segment->capacity()); - } - - uintptr_t* data() { - return segment->data + segment->capacity() + offset(); - } - - unsigned size(unsigned capacity) { - unsigned result - = ceiling(ceiling(capacity, scale) * bitsPerRecord, BitsPerWord); - assert(segment->context, result); - return result; - } - - unsigned size() { - return size(max(segment->capacity(), 1)); - } - unsigned indexOf(unsigned segmentIndex) { return (segmentIndex / scale) * bitsPerRecord; } @@ -224,33 +248,20 @@ class Segment { return indexOf(segment->indexOf(p)); } - void update(uintptr_t* newData, unsigned capacity) { - assert(segment->context, capacity >= segment->capacity()); - - uintptr_t* p = newData + offset(capacity); - if (segment->position()) { - memcpy(p, data(), size(segment->position()) * BytesPerWord); - } - - if (child) { - child->update(newData, capacity); - } - } - void clearBit(unsigned i) { assert(segment->context, wordOf(i) < size()); - vm::clearBit(data(), i); + vm::clearBit(data, i); } void setBit(unsigned i) { assert(segment->context, wordOf(i) < size()); - vm::markBit(data(), i); + vm::markBit(data, i); } void clearOnlyIndex(unsigned index) { - clearBits(data(), bitsPerRecord, index); + clearBits(data, bitsPerRecord, index); } void clearOnly(unsigned segmentIndex) { @@ -267,7 +278,7 @@ class Segment { } void setOnlyIndex(unsigned index, unsigned v = 1) { - setBits(data(), bitsPerRecord, index, v); + setBits(data, bitsPerRecord, index, v); } void setOnly(unsigned segmentIndex, unsigned v = 1) { @@ -285,13 +296,7 @@ class Segment { } unsigned get(void* p) { - return getBits(data(), bitsPerRecord, indexOf(p)); - } - - unsigned footprint(unsigned capacity) { - unsigned n = size(capacity); - if (child) n += child->footprint(capacity); - return n; + return getBits(data, bitsPerRecord, indexOf(p)); } }; @@ -334,8 +339,22 @@ class Segment { } } + Segment(Context* context, Map* map, uintptr_t* data, unsigned position, + unsigned capacity): + context(context), + data(data), + position_(position), + capacity_(capacity), + map(map) + { + if (map) { + map->init(); + } + } + unsigned footprint(unsigned capacity) { - return capacity + (map and capacity ? map->footprint(capacity) : 0); + return capacity + + (map and capacity ? map->calculateFootprint(capacity) : 0); } unsigned capacity() { @@ -411,15 +430,18 @@ class Segment { class Fixie { public: - Fixie(unsigned size, bool hasMask, Fixie** handle, bool immortal): + Fixie(Context* c, unsigned size, bool hasMask, Fixie** handle, + bool immortal): age(immortal ? FixieTenureThreshold + 1 : 0), hasMask(hasMask), marked(false), dirty(false), - size(size) + size(size), + next(0), + handle(0) { memset(mask(), 0, maskSize(size, hasMask)); - add(handle); + add(c, handle); if (DebugFixies) { fprintf(stderr, "make fixie %p of size %d\n", this, totalSize()); } @@ -429,7 +451,10 @@ class Fixie { return age == FixieTenureThreshold + 1; } - void add(Fixie** handle) { + void add(Context* c UNUSED, Fixie** handle) { + assert(c, this->handle == 0); + assert(c, next == 0); + this->handle = handle; if (handle) { next = *handle; @@ -440,18 +465,25 @@ class Fixie { } } - void remove() { - if (handle) *handle = next; - if (next) next->handle = handle; + void remove(Context* c UNUSED) { + if (handle) { + assert(c, *handle == this); + *handle = next; + } + if (next) { + next->handle = handle; + } + next = 0; + handle = 0; } - void move(Fixie** handle) { + void move(Context* c, Fixie** handle) { if (DebugFixies) { fprintf(stderr, "move fixie %p\n", this); } - remove(); - add(handle); + remove(c); + add(c, handle); } void** body() { @@ -502,6 +534,9 @@ class Context { limit(limit), lowMemoryThreshold(limit / 2), lock(0), + + immortalHeapStart(0), + immortalHeapEnd(0), ageMap(&gen1, max(1, log(TenureThreshold)), 1, 0, false), gen1(this, &ageMap, 0, 0), @@ -571,7 +606,10 @@ class Context { unsigned lowMemoryThreshold; System::Mutex* lock; - + + uintptr_t* immortalHeapStart; + uintptr_t* immortalHeapEnd; + Segment::Map ageMap; Segment gen1; @@ -690,7 +728,7 @@ inline void initNextGen1(Context* c) { new (&(c->nextAgeMap)) Segment::Map - (&(c->nextGen1), max(1, log(TenureThreshold)), 1, 0, false); + (&(c->nextGen1), max(1, log(TenureThreshold)), 1, 0, false); unsigned minimum = minimumNextGen1Capacity(c); unsigned desired = minimum; @@ -775,6 +813,7 @@ free(Context* c, Fixie** fixies) { for (Fixie** p = fixies; *p;) { Fixie* f = *p; + if (f->immortal()) { p = &(f->next); } else { @@ -802,9 +841,9 @@ sweepFixies(Context* c) c->untenuredFixieFootprint = 0; - for (Fixie** p = &(c->visitedFixies); *p;) { - Fixie* f = *p; - *p = f->next; + while (c->visitedFixies) { + Fixie* f = c->visitedFixies; + f->remove(c); if (not f->immortal()) { ++ f->age; @@ -825,14 +864,14 @@ sweepFixies(Context* c) } if (f->dirty) { - f->move(&(c->dirtyTenuredFixies)); + f->add(c, &(c->dirtyTenuredFixies)); } else { - f->move(&(c->tenuredFixies)); + f->add(c, &(c->tenuredFixies)); } } else { c->untenuredFixieFootprint += f->totalSize(); - f->move(&(c->fixies)); + f->add(c, &(c->fixies)); } f->marked = false; @@ -852,6 +891,12 @@ copyTo(Context* c, Segment* s, void* o, unsigned size) return dst; } +bool +immortalHeapContains(Context* c, void* p) +{ + return p < c->immortalHeapEnd and p >= c->immortalHeapStart; +} + void* copy2(Context* c, void* o) { @@ -888,6 +933,7 @@ copy2(Context* c, void* o) } else { assert(c, not c->nextGen1.contains(o)); assert(c, not c->nextGen2.contains(o)); + assert(c, not immortalHeapContains(c, o)); o = copyTo(c, &(c->nextGen1), o, size); @@ -926,10 +972,13 @@ update3(Context* c, void* o, bool* needsVisit) fprintf(stderr, "mark fixie %p\n", f); } f->marked = true; - f->move(&(c->markedFixies)); + f->move(c, &(c->markedFixies)); } *needsVisit = false; return o; + } else if (immortalHeapContains(c, o)) { + *needsVisit = false; + return o; } else if (wasCollected(c, o)) { *needsVisit = false; return follow(c, o); @@ -950,23 +999,12 @@ update2(Context* c, void* o, bool* needsVisit) return update3(c, o, needsVisit); } -void* -update(Context* c, void** p, bool* needsVisit) -{ - if (mask(*p) == 0) { - *needsVisit = false; - return 0; - } - - return update2(c, mask(*p), needsVisit); -} - void markDirty(Context* c, Fixie* f) { if (not f->dirty) { f->dirty = true; - f->move(&(c->dirtyTenuredFixies)); + f->move(c, &(c->dirtyTenuredFixies)); } } @@ -975,7 +1013,11 @@ markClean(Context* c, Fixie* f) { if (f->dirty) { f->dirty = false; - f->move(&(c->tenuredFixies)); + if (f->immortal()) { + f->remove(c); + } else { + f->move(c, &(c->tenuredFixies)); + } } } @@ -993,9 +1035,10 @@ updateHeapMap(Context* c, void* p, void* target, unsigned offset, void* result) map = &(c->nextHeapMap); } - if (not (c->client->isFixed(result) - and fixie(result)->age >= FixieTenureThreshold) - and not seg->contains(result)) + if (not (immortalHeapContains(c, result) + or (c->client->isFixed(result) + and fixie(result)->age >= FixieTenureThreshold) + or seg->contains(result))) { if (target and c->client->isFixed(target)) { Fixie* f = fixie(target); @@ -1003,11 +1046,11 @@ updateHeapMap(Context* c, void* p, void* target, unsigned offset, void* result) if (static_cast(f->age + 1) >= FixieTenureThreshold) { if (DebugFixies) { - fprintf(stderr, "dirty fixie %p at %d (%p)\n", - f, offset, f->body() + offset); + fprintf(stderr, "dirty fixie %p at %d (%p): %p\n", + f, offset, f->body() + offset, result); } - markDirty(c, f); + f->dirty = true; markBit(f->mask(), offset); } } else if (seg->contains(p)) { @@ -1399,7 +1442,6 @@ visitDirtyFixies(Context* c, Fixie** p) assert(c, wasDirty); if (clean) { - *p = f->next; markClean(c, f); } else { p = &(f->next); @@ -1410,9 +1452,9 @@ visitDirtyFixies(Context* c, Fixie** p) void visitMarkedFixies(Context* c) { - for (Fixie** p = &(c->markedFixies); *p;) { - Fixie* f = *p; - *p = f->next; + while (c->markedFixies) { + Fixie* f = c->markedFixies; + f->remove(c); if (DebugFixies) { fprintf(stderr, "visit fixie %p\n", f); @@ -1435,7 +1477,7 @@ visitMarkedFixies(Context* c) c->client->walk(f->body(), &w); - f->move(&(c->visitedFixies)); + f->move(c, &(c->visitedFixies)); } } @@ -1664,6 +1706,11 @@ class MyHeap: public Heap { c.client = client; } + virtual void setImmortalHeap(uintptr_t* start, unsigned sizeInWords) { + c.immortalHeapStart = start; + c.immortalHeapEnd = start + sizeInWords; + } + virtual void* tryAllocate(unsigned size) { return ::tryAllocate(&c, size); } @@ -1690,18 +1737,21 @@ class MyHeap: public Heap { { *totalInBytes = Fixie::totalSize(sizeInWords, objectMask); return (new (allocator->allocate(*totalInBytes)) - Fixie(sizeInWords, objectMask, &(c.fixies), false))->body(); + Fixie(&c, sizeInWords, objectMask, &(c.fixies), false))->body(); } - virtual void* allocateImmortal(Allocator* allocator, unsigned sizeInWords, - bool objectMask, unsigned* totalInBytes) + virtual void* allocateImmortalFixed(Allocator* allocator, + unsigned sizeInWords, bool objectMask, + unsigned* totalInBytes) { *totalInBytes = Fixie::totalSize(sizeInWords, objectMask); return (new (allocator->allocate(*totalInBytes)) - Fixie(sizeInWords, objectMask, &(c.tenuredFixies), true))->body(); + Fixie(&c, sizeInWords, objectMask, 0, true))->body(); } virtual bool needsMark(void* p) { + assert(&c, c.client->isFixed(p) or (not immortalHeapContains(&c, p))); + if (c.client->isFixed(p)) { return fixie(p)->age >= FixieTenureThreshold; } else { @@ -1717,6 +1767,7 @@ class MyHeap: public Heap { bool targetNeedsMark(void* target) { return target and not c.gen2.contains(target) + and not immortalHeapContains(&c, target) and not (c.client->isFixed(target) and fixie(target)->age >= FixieTenureThreshold); } @@ -1731,8 +1782,8 @@ class MyHeap: public Heap { void** target = static_cast(p) + offset + i; if (targetNeedsMark(mask(*target))) { if (DebugFixies) { - fprintf(stderr, "dirty fixie %p at %d (%p)\n", - f, offset, f->body() + offset); + fprintf(stderr, "dirty fixie %p at %d (%p): %p\n", + f, offset, f->body() + offset, mask(*target)); } dirty = true; @@ -1789,6 +1840,7 @@ class MyHeap: public Heap { } else if (c.nextGen1.contains(p)) { return Reachable; } else if (c.nextGen2.contains(p) + or immortalHeapContains(&c, p) or (c.gen2.contains(p) and (c.mode == Heap::MinorCollection or c.gen2.indexOf(p) >= c.gen2Base))) diff --git a/src/heap.h b/src/heap.h index bc8f9a5a1c..28f1533d8f 100644 --- a/src/heap.h +++ b/src/heap.h @@ -52,11 +52,13 @@ class Heap: public Allocator { }; virtual void setClient(Client* client) = 0; + virtual void setImmortalHeap(uintptr_t* start, unsigned sizeInWords) = 0; virtual void collect(CollectionType type, unsigned footprint) = 0; virtual void* allocateFixed(Allocator* allocator, unsigned sizeInWords, bool objectMask, unsigned* totalInBytes) = 0; - virtual void* allocateImmortal(Allocator* allocator, unsigned sizeInWords, - bool objectMask, unsigned* totalInBytes) = 0; + virtual void* allocateImmortalFixed(Allocator* allocator, + unsigned sizeInWords, bool objectMask, + unsigned* totalInBytes) = 0; virtual bool needsMark(void* p) = 0; virtual bool needsMark(void* p, unsigned offset) = 0; virtual void mark(void* p, unsigned offset, unsigned count) = 0; diff --git a/src/heapdump.cpp b/src/heapdump.cpp index 617d1d2e74..f45c225aaf 100644 --- a/src/heapdump.cpp +++ b/src/heapdump.cpp @@ -9,194 +9,12 @@ details. */ #include "machine.h" +#include "heapwalk.h" using namespace vm; namespace { -const uintptr_t PointerShift = log(BytesPerWord); - -class Set { - public: - class Entry { - public: - object value; - uint32_t number; - int next; - }; - - static unsigned footprint(unsigned capacity) { - return sizeof(Set) - + pad(sizeof(int) * capacity) - + pad(sizeof(Set::Entry) * capacity); - } - - Set(unsigned capacity): - size(0), - capacity(capacity), - index(reinterpret_cast - (reinterpret_cast(this) - + sizeof(Set))), - entries(reinterpret_cast - (reinterpret_cast(index) - + pad(sizeof(int) * capacity))) - { } - - unsigned size; - unsigned capacity; - int* index; - Entry* entries; -}; - -class Stack { - public: - class Entry { - public: - object value; - int offset; - }; - - static const unsigned Capacity = 4096; - - Stack(Stack* next): next(next), entryCount(0) { } - - Stack* next; - unsigned entryCount; - Entry entries[Capacity]; -}; - -class Context { - public: - Context(Thread* thread, FILE* out): - thread(thread), out(out), objects(0), stack(0), nextNumber(1) - { } - - ~Context() { - if (objects) { - thread->m->heap->free(objects, Set::footprint(objects->capacity)); - } - while (stack) { - Stack* dead = stack; - stack = dead->next; - thread->m->heap->free(stack, sizeof(Stack)); - } - } - - Thread* thread; - FILE* out; - Set* objects; - Stack* stack; - uint32_t nextNumber; -}; - -void -push(Context* c, object p, int offset) -{ - if (c->stack == 0 or c->stack->entryCount == Stack::Capacity) { - c->stack = new (c->thread->m->heap->allocate(sizeof(Stack))) - Stack(c->stack); - } - Stack::Entry* e = c->stack->entries + (c->stack->entryCount++); - e->value = p; - e->offset = offset; -} - -bool -pop(Context* c, object* p, int* offset) -{ - if (c->stack) { - if (c->stack->entryCount == 0) { - if (c->stack->next) { - Stack* dead = c->stack; - c->stack = dead->next; - c->thread->m->heap->free(dead, sizeof(Stack)); - } else { - return false; - } - } - Stack::Entry* e = c->stack->entries + (--c->stack->entryCount); - *p = e->value; - *offset = e->offset; - return true; - } else { - return false; - } -} - -unsigned -hash(object p, unsigned capacity) -{ - return (reinterpret_cast(p) >> PointerShift) - & (capacity - 1); -} - -Set::Entry* -find(Context* c, object p) -{ - if (c->objects == 0) return 0; - - for (int i = c->objects->index[hash(p, c->objects->capacity)]; i >= 0;) { - Set::Entry* e = c->objects->entries + i; - if (e->value == p) { - return e; - } - i = e->next; - } - - return 0; -} - -Set::Entry* -add(Context* c UNUSED, Set* set, object p, uint32_t number) -{ - assert(c->thread, set->size < set->capacity); - - unsigned index = hash(p, set->capacity); - - int offset = set->size++; - Set::Entry* e = set->entries + offset; - e->value = p; - e->number = number; - e->next = set->index[index]; - set->index[index] = offset; - return e; -} - -Set::Entry* -add(Context* c, object p) -{ - if (c->objects == 0 or c->objects->size == c->objects->capacity) { - unsigned capacity; - if (c->objects) { - capacity = c->objects->capacity * 2; - } else { - capacity = 4096; // must be power of two - } - - Set* set = new (c->thread->m->heap->allocate(Set::footprint(capacity))) - Set(capacity); - - memset(set->index, 0xFF, sizeof(int) * capacity); - - if (c->objects) { - for (unsigned i = 0; i < c->objects->capacity; ++i) { - for (int j = c->objects->index[i]; j >= 0;) { - Set::Entry* e = c->objects->entries + j; - add(c, set, e->value, e->number); - j = e->next; - } - } - - c->thread->m->heap->free - (c->objects, Set::footprint(c->objects->capacity)); - } - - c->objects = set; - } - - return add(c, c->objects, p, 0); -} - enum { Root, Size, @@ -205,100 +23,30 @@ enum { Pop }; -inline object -get(object o, unsigned offsetInWords) +void +write1(FILE* out, uint8_t v) { - return static_cast - (mask(cast(o, offsetInWords * BytesPerWord))); + size_t n UNUSED = fwrite(&v, 1, 1, out); } void -write1(Context* c, uint8_t v) -{ - fwrite(&v, 1, 1, c->out); -} - -void -write4(Context* c, uint32_t v) +write4(FILE* out, uint32_t v) { uint8_t b[] = { v >> 24, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF }; - fwrite(b, 4, 1, c->out); + size_t n UNUSED = fwrite(b, 4, 1, out); } void -writeString(Context* c, int8_t* p, unsigned size) +writeString(FILE* out, int8_t* p, unsigned size) { - write4(c, size); - fwrite(p, size, 1, c->out); + write4(out, size); + size_t n UNUSED = fwrite(p, size, 1, out); } unsigned objectSize(Thread* t, object o) { - unsigned n = baseSize(t, o, objectClass(t, o)); - if (objectExtended(t, o)) { - ++ n; - } - return n; -} - -void -visit(Context* c, object p) -{ - Thread* t = c->thread; - int nextChildOffset; - - write1(c, Root); - - visit: { - Set::Entry* e = find(c, p); - if (e) { - write4(c, e->number); - } else { - e = add(c, p); - e->number = c->nextNumber++; - - write4(c, e->number); - - write1(c, Size); - write4(c, objectSize(t, p)); - - if (objectClass(t, p) == arrayBody(t, t->m->types, Machine::ClassType)) { - object name = className(t, p); - if (name) { - write1(c, ClassName); - writeString(c, &byteArrayBody(t, name, 0), - byteArrayLength(t, name) - 1); - } - } - - nextChildOffset = walkNext(t, p, -1); - if (nextChildOffset != -1) { - goto children; - } - } - } - - goto pop; - - children: { - write1(c, Push); - push(c, p, nextChildOffset); - p = get(p, nextChildOffset); - goto visit; - } - - pop: { - if (pop(c, &p, &nextChildOffset)) { - write1(c, Pop); - nextChildOffset = walkNext(t, p, nextChildOffset); - if (nextChildOffset >= 0) { - goto children; - } else { - goto pop; - } - } - } + return extendedSize(t, o, baseSize(t, o, objectClass(t, o))); } } // namespace @@ -308,22 +56,58 @@ namespace vm { void dumpHeap(Thread* t, FILE* out) { - Context context(t, out); - - class Visitor : public Heap::Visitor { + class Visitor: public HeapVisitor { public: - Visitor(Context* c): c(c) { } + Visitor(Thread* t, FILE* out): t(t), out(out), nextNumber(1) { } - virtual void visit(void* p) { - ::visit(c, static_cast(mask(*static_cast(p)))); + virtual void root() { + write1(out, Root); } - Context* c; - } v(&context); + virtual unsigned visitNew(object p) { + if (p) { + unsigned number = nextNumber++; + write4(out, number); - add(&context, 0)->number = 0; + write1(out, Size); + write4(out, objectSize(t, p)); - visitRoots(t->m, &v); + if (objectClass(t, p) == arrayBody(t, t->m->types, Machine::ClassType)) + { + object name = className(t, p); + if (name) { + write1(out, ClassName); + writeString(out, &byteArrayBody(t, name, 0), + byteArrayLength(t, name) - 1); + } + } + + return number; + } else { + return 0; + } + } + + virtual void visitOld(object, unsigned number) { + write4(out, number); + } + + virtual void push(object, unsigned, unsigned) { + write1(out, Push); + } + + virtual void pop() { + write1(out, Pop); + } + + Thread* t; + FILE* out; + unsigned nextNumber; + } visitor(t, out); + + HeapWalker* w = makeHeapWalker(t, &visitor); + w->visitAllRoots(); + w->dispose(); } } // namespace vm diff --git a/src/heapwalk.cpp b/src/heapwalk.cpp new file mode 100644 index 0000000000..f6e2b746fe --- /dev/null +++ b/src/heapwalk.cpp @@ -0,0 +1,341 @@ +/* Copyright (c) 2008, 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. */ + +#include "machine.h" +#include "heapwalk.h" + +using namespace vm; + +namespace { + +const uintptr_t PointerShift = log(BytesPerWord); + +class Context; + +class Set: public HeapMap { + public: + class Entry { + public: + object value; + uint32_t number; + int next; + }; + + static unsigned footprint(unsigned capacity) { + return sizeof(Set) + + pad(sizeof(int) * capacity) + + pad(sizeof(Set::Entry) * capacity); + } + + Set(Context* context, unsigned capacity): + context(context), + index(reinterpret_cast + (reinterpret_cast(this) + + sizeof(Set))), + entries(reinterpret_cast + (reinterpret_cast(index) + + pad(sizeof(int) * capacity))), + size(0), + capacity(capacity) + { } + + virtual int find(object value); + + virtual void dispose(); + + Context* context; + int* index; + Entry* entries; + unsigned size; + unsigned capacity; +}; + +class Stack { + public: + class Entry { + public: + object value; + int offset; + }; + + static const unsigned Capacity = 4096; + + Stack(Stack* next): next(next), entryCount(0) { } + + Stack* next; + unsigned entryCount; + Entry entries[Capacity]; +}; + +class Context { + public: + Context(Thread* thread): + thread(thread), objects(0), stack(0) + { } + + void dispose() { + if (objects) { + objects->dispose(); + } + + while (stack) { + Stack* dead = stack; + stack = dead->next; + thread->m->heap->free(stack, sizeof(Stack)); + } + } + + Thread* thread; + Set* objects; + Stack* stack; +}; + +void +push(Context* c, object p, int offset) +{ + if (c->stack == 0 or c->stack->entryCount == Stack::Capacity) { + c->stack = new (c->thread->m->heap->allocate(sizeof(Stack))) + Stack(c->stack); + } + Stack::Entry* e = c->stack->entries + (c->stack->entryCount++); + e->value = p; + e->offset = offset; +} + +bool +pop(Context* c, object* p, int* offset) +{ + if (c->stack) { + if (c->stack->entryCount == 0) { + if (c->stack->next) { + Stack* dead = c->stack; + c->stack = dead->next; + c->thread->m->heap->free(dead, sizeof(Stack)); + } else { + return false; + } + } + Stack::Entry* e = c->stack->entries + (--c->stack->entryCount); + *p = e->value; + *offset = e->offset; + return true; + } else { + return false; + } +} + +unsigned +hash(object p, unsigned capacity) +{ + return (reinterpret_cast(p) >> PointerShift) + & (capacity - 1); +} + +Set::Entry* +find(Context* c, object p) +{ + if (c->objects == 0) return 0; + + for (int i = c->objects->index[hash(p, c->objects->capacity)]; i >= 0;) { + Set::Entry* e = c->objects->entries + i; + if (e->value == p) { + return e; + } + i = e->next; + } + + return 0; +} + +int +Set::find(object value) +{ + Set::Entry* e = ::find(context, value); + if (e) { + return e->number; + } else { + return -1; + } +} + +void +Set::dispose() +{ + context->thread->m->heap->free(this, footprint(capacity)); +} + +Set::Entry* +add(Context* c UNUSED, Set* set, object p, uint32_t number) +{ + assert(c->thread, set->size < set->capacity); + + unsigned index = hash(p, set->capacity); + + int offset = set->size++; + Set::Entry* e = set->entries + offset; + e->value = p; + e->number = number; + e->next = set->index[index]; + set->index[index] = offset; + return e; +} + +Set::Entry* +add(Context* c, object p) +{ + if (c->objects == 0 or c->objects->size == c->objects->capacity) { + unsigned capacity; + if (c->objects) { + capacity = c->objects->capacity * 2; + } else { + capacity = 4096; // must be power of two + } + + Set* set = new (c->thread->m->heap->allocate(Set::footprint(capacity))) + Set(c, capacity); + + memset(set->index, 0xFF, sizeof(int) * capacity); + + if (c->objects) { + for (unsigned i = 0; i < c->objects->capacity; ++i) { + for (int j = c->objects->index[i]; j >= 0;) { + Set::Entry* e = c->objects->entries + j; + add(c, set, e->value, e->number); + j = e->next; + } + } + + c->thread->m->heap->free + (c->objects, Set::footprint(c->objects->capacity)); + } + + c->objects = set; + } + + return add(c, c->objects, p, 0); +} + +inline object +get(object o, unsigned offsetInWords) +{ + return static_cast + (mask(cast(o, offsetInWords * BytesPerWord))); +} + +unsigned +objectSize(Thread* t, object o) +{ + unsigned n = baseSize(t, o, objectClass(t, o)); + if (objectExtended(t, o)) { + ++ n; + } + return n; +} + +unsigned +walk(Context* c, HeapVisitor* v, object p) +{ + Thread* t = c->thread; + object root = p; + int nextChildOffset; + + v->root(); + + visit: { + Set::Entry* e = find(c, p); + if (e) { + v->visitOld(p, e->number); + } else { + e = add(c, p); + e->number = v->visitNew(p); + + nextChildOffset = walkNext(t, p, -1); + if (nextChildOffset != -1) { + goto children; + } + } + } + + goto pop; + + children: { + v->push(p, find(c, p)->number, nextChildOffset); + push(c, p, nextChildOffset); + p = get(p, nextChildOffset); + goto visit; + } + + pop: { + if (pop(c, &p, &nextChildOffset)) { + v->pop(); + nextChildOffset = walkNext(t, p, nextChildOffset); + if (nextChildOffset >= 0) { + goto children; + } else { + goto pop; + } + } + } + + return find(c, root)->number; +} + +class MyHeapWalker: public HeapWalker { + public: + MyHeapWalker(Thread* t, HeapVisitor* v): + context(t), visitor(v) + { + add(&context, 0)->number = v->visitNew(0); + } + + virtual unsigned visitRoot(object root) { + return walk(&context, visitor, root); + } + + virtual void visitAllRoots() { + class Visitor: public Heap::Visitor { + public: + Visitor(Context* c, HeapVisitor* v): c(c), v(v) { } + + virtual void visit(void* p) { + walk(c, v, static_cast(mask(*static_cast(p)))); + } + + Context* c; + HeapVisitor* v; + } v(&context, visitor); + + visitRoots(context.thread->m, &v); + } + + virtual HeapMap* map() { + return context.objects; + } + + virtual void dispose() { + context.dispose(); + context.thread->m->heap->free(this, sizeof(MyHeapWalker)); + } + + Context context; + HeapVisitor* visitor; +}; + +} // namespace + +namespace vm { + +HeapWalker* +makeHeapWalker(Thread* t, HeapVisitor* v) +{ + return new (t->m->heap->allocate(sizeof(MyHeapWalker))) MyHeapWalker(t, v); +} + +} // namespace vm diff --git a/src/heapwalk.h b/src/heapwalk.h new file mode 100644 index 0000000000..25c681d6a3 --- /dev/null +++ b/src/heapwalk.h @@ -0,0 +1,49 @@ +/* Copyright (c) 2008, 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. */ + +#ifndef HEAPWALK_H +#define HEAPWALK_H + +#include "common.h" + +namespace vm { + +class Thread; + +class HeapMap { + public: + virtual int find(object value) = 0; + virtual void dispose() = 0; +}; + +class HeapVisitor { + public: + virtual void root() = 0; + virtual unsigned visitNew(object value) = 0; + virtual void visitOld(object value, unsigned number) = 0; + virtual void push(object parent, unsigned parentNumber, + unsigned childOffset) = 0; + virtual void pop() = 0; +}; + +class HeapWalker { + public: + virtual unsigned visitRoot(object root) = 0; + virtual void visitAllRoots() = 0; + virtual HeapMap* map() = 0; + virtual void dispose() = 0; +}; + +HeapWalker* +makeHeapWalker(Thread* t, HeapVisitor* v); + +} // namespace vm + +#endif//HEAPWALK_H diff --git a/src/interpret.cpp b/src/interpret.cpp index 604a97d43f..d3919f7117 100644 --- a/src/interpret.cpp +++ b/src/interpret.cpp @@ -1452,7 +1452,10 @@ interpret(Thread* t) object field = resolveField(t, codePool(t, code), index - 1); if (UNLIKELY(exception)) goto throw_; - + if (throwIfVolatileField(t, field)) goto throw_; + + assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0); + pushField(t, popObject(t), field); } else { exception = makeNullPointerException(t); @@ -1465,6 +1468,10 @@ interpret(Thread* t) object field = resolveField(t, codePool(t, code), index - 1); if (UNLIKELY(exception)) goto throw_; + if (throwIfVolatileField(t, field)) goto throw_; + + assert(t, fieldFlags(t, field) & ACC_STATIC); + PROTECT(t, field); if (UNLIKELY(classInit(t, fieldClass(t, field), 3))) goto invoke; @@ -2396,6 +2403,9 @@ interpret(Thread* t) object field = resolveField(t, codePool(t, code), index - 1); if (UNLIKELY(exception)) goto throw_; + if (throwIfVolatileField(t, field)) goto throw_; + + assert(t, (fieldFlags(t, field) & ACC_STATIC) == 0); switch (fieldCode(t, field)) { case ByteField: @@ -2461,6 +2471,10 @@ interpret(Thread* t) object field = resolveField(t, codePool(t, code), index - 1); if (UNLIKELY(exception)) goto throw_; + if (throwIfVolatileField(t, field)) goto throw_; + + assert(t, fieldFlags(t, field) & ACC_STATIC); + PROTECT(t, field); if (UNLIKELY(classInit(t, fieldClass(t, field), 3))) goto invoke; @@ -2876,7 +2890,7 @@ class MyProcessor: public Processor { { return vm::makeMethod (t, vmFlags, returnCode, parameterCount, parameterFootprint, flags, - offset, name, spec, class_, code, 0); + offset, 0, name, spec, class_, code, 0); } virtual object @@ -3056,6 +3070,32 @@ class MyProcessor: public Processor { return 0; } + virtual void compileThunks(vm::Thread*, BootImage*, uint8_t*, unsigned*, + unsigned) + { + abort(s); + } + + virtual void compileMethod(vm::Thread*, Zone*, uint8_t*, unsigned*, unsigned, + object*, object*, DelayedPromise**, object) + { + abort(s); + } + + virtual void visitRoots(BootImage*, HeapWalker*) { + abort(s); + } + + virtual unsigned* makeCallTable(vm::Thread*, BootImage*, HeapWalker*, + uint8_t*) + { + abort(s); + } + + virtual void boot(vm::Thread*, BootImage* image) { + expect(s, image == 0); + } + virtual void dispose(vm::Thread* t) { t->m->heap->free(t, sizeof(Thread)); } diff --git a/src/machine.cpp b/src/machine.cpp index ab1bff60f0..fa7cae8707 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -248,38 +248,6 @@ walk(Thread*, Heap::Walker* w, uint32_t* mask, unsigned fixedSize, } } -void -walk(Thread* t, Heap::Walker* w, object o, unsigned start) -{ - object class_ = static_cast(t->m->heap->follow(objectClass(t, o))); - object objectMask = static_cast - (t->m->heap->follow(classObjectMask(t, class_))); - - if (objectMask) { - unsigned fixedSize = classFixedSize(t, class_); - unsigned arrayElementSize = classArrayElementSize(t, class_); - unsigned arrayLength - = (arrayElementSize ? - cast(o, fixedSize - BytesPerWord) : 0); - - uint32_t mask[intArrayLength(t, objectMask)]; - memcpy(mask, &intArrayBody(t, objectMask, 0), - intArrayLength(t, objectMask) * 4); - - walk(t, w, mask, fixedSize, arrayElementSize, arrayLength, start); - } else if (classVmFlags(t, class_) & SingletonFlag) { - unsigned length = singletonLength(t, o); - if (length) { - walk(t, w, singletonMask(t, o), - (singletonCount(t, o) + 2) * BytesPerWord, 0, 0, start); - } else if (start == 0) { - w->visit(0); - } - } else if (start == 0) { - w->visit(0); - } -} - void finalizerTargetUnreachable(Thread* t, Heap::Visitor* v, object* p) { @@ -867,10 +835,6 @@ parseFieldTable(Thread* t, Stream& s, object class_, object pool) staticTypes[staticCount++] = code; } else { - if (value) { - abort(t); // todo: handle non-static field initializers - } - unsigned excess = (memberOffset % fieldSize(t, code)) % BytesPerWord; if (excess) { memberOffset += BytesPerWord - excess; @@ -1534,6 +1498,88 @@ bootJavaClass(Thread* t, Machine::Type type, int superType, const char* name, hashMapInsert(t, t->m->bootstrapClassMap, n, class_, byteArrayHash); } +void +boot(Thread* t) +{ + Machine* m = t->m; + + m->unsafe = true; + + m->loader = allocate(t, sizeof(void*) * 3, true); + memset(m->loader, 0, sizeof(void*) * 2); + + m->types = allocate(t, pad((TypeCount + 2) * BytesPerWord), true); + arrayLength(t, m->types) = TypeCount; + memset(&arrayBody(t, m->types, 0), 0, TypeCount * BytesPerWord); + +#include "type-initializations.cpp" + + object arrayClass = arrayBody(t, m->types, Machine::ArrayType); + set(t, m->types, 0, arrayClass); + + object loaderClass = arrayBody + (t, m->types, Machine::SystemClassLoaderType); + set(t, m->loader, 0, loaderClass); + + object objectClass = arrayBody(t, m->types, Machine::JobjectType); + + object classClass = arrayBody(t, m->types, Machine::ClassType); + set(t, classClass, 0, classClass); + set(t, classClass, ClassSuper, objectClass); + + object intArrayClass = arrayBody(t, m->types, Machine::IntArrayType); + set(t, intArrayClass, 0, classClass); + set(t, intArrayClass, ClassSuper, objectClass); + + m->unsafe = false; + + classVmFlags(t, arrayBody(t, m->types, Machine::SingletonType)) + |= SingletonFlag; + + classVmFlags(t, arrayBody(t, m->types, Machine::JreferenceType)) + |= ReferenceFlag; + classVmFlags(t, arrayBody(t, m->types, Machine::WeakReferenceType)) + |= ReferenceFlag | WeakReferenceFlag; + classVmFlags(t, arrayBody(t, m->types, Machine::PhantomReferenceType)) + |= ReferenceFlag | WeakReferenceFlag; + + classVmFlags(t, arrayBody(t, m->types, Machine::JbooleanType)) + |= PrimitiveFlag; + classVmFlags(t, arrayBody(t, m->types, Machine::JbyteType)) + |= PrimitiveFlag; + classVmFlags(t, arrayBody(t, m->types, Machine::JcharType)) + |= PrimitiveFlag; + classVmFlags(t, arrayBody(t, m->types, Machine::JshortType)) + |= PrimitiveFlag; + classVmFlags(t, arrayBody(t, m->types, Machine::JintType)) + |= PrimitiveFlag; + classVmFlags(t, arrayBody(t, m->types, Machine::JlongType)) + |= PrimitiveFlag; + classVmFlags(t, arrayBody(t, m->types, Machine::JfloatType)) + |= PrimitiveFlag; + classVmFlags(t, arrayBody(t, m->types, Machine::JdoubleType)) + |= PrimitiveFlag; + classVmFlags(t, arrayBody(t, m->types, Machine::JvoidType)) + |= PrimitiveFlag; + + m->bootstrapClassMap = makeHashMap(t, 0, 0); + + m->classMap = makeHashMap(t, 0, 0); + + m->stringMap = makeWeakHashMap(t, 0, 0); + + m->processor->boot(t, 0); + + { object bootCode = makeCode(t, 0, 0, 0, 0, 0, 1, false); + codeBody(t, bootCode, 0) = impdep1; + object bootMethod = makeMethod + (t, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, bootCode, 0); + PROTECT(t, bootMethod); + +#include "type-java-initializations.cpp" + } +} + class HeapClient: public Heap::Client { public: HeapClient(Machine* m): m(m) { } @@ -1648,6 +1694,7 @@ Machine::Machine(System* system, Heap* heap, Finder* finder, referenceLock(0), libraries(0), loader(0), + classMap(0), bootstrapClassMap(0), monitorMap(0), stringMap(0), @@ -1748,86 +1795,32 @@ Thread::init() abort(this); } - Thread* t = this; + BootImage* image = 0; + const char* imageFunctionName = findProperty(m, "avian.bootimage"); + if (imageFunctionName) { + void* p = m->libraries->resolve(imageFunctionName); + if (p) { + BootImage* (*function)(unsigned*); + memcpy(&function, &p, BytesPerWord); - t->m->loader = allocate(t, sizeof(void*) * 3, true); - memset(t->m->loader, 0, sizeof(void*) * 2); - - t->m->types = allocate(t, pad((TypeCount + 2) * BytesPerWord), true); - arrayLength(t, t->m->types) = TypeCount; - memset(&arrayBody(t, t->m->types, 0), 0, TypeCount * BytesPerWord); - -#include "type-initializations.cpp" - - object arrayClass = arrayBody(t, t->m->types, Machine::ArrayType); - set(t, t->m->types, 0, arrayClass); - - object loaderClass = arrayBody - (t, t->m->types, Machine::SystemClassLoaderType); - set(t, t->m->loader, 0, loaderClass); - - object objectClass = arrayBody(t, m->types, Machine::JobjectType); - - object classClass = arrayBody(t, m->types, Machine::ClassType); - set(t, classClass, 0, classClass); - set(t, classClass, ClassSuper, objectClass); - - object intArrayClass = arrayBody(t, m->types, Machine::IntArrayType); - set(t, intArrayClass, 0, classClass); - set(t, intArrayClass, ClassSuper, objectClass); + unsigned size; + image = function(&size); + } + } m->unsafe = false; - classVmFlags(t, arrayBody(t, m->types, Machine::SingletonType)) - |= SingletonFlag; - - classVmFlags(t, arrayBody(t, m->types, Machine::JreferenceType)) - |= ReferenceFlag; - classVmFlags(t, arrayBody(t, m->types, Machine::WeakReferenceType)) - |= ReferenceFlag | WeakReferenceFlag; - classVmFlags(t, arrayBody(t, m->types, Machine::PhantomReferenceType)) - |= ReferenceFlag | WeakReferenceFlag; - - classVmFlags(t, arrayBody(t, m->types, Machine::JbooleanType)) - |= PrimitiveFlag; - classVmFlags(t, arrayBody(t, m->types, Machine::JbyteType)) - |= PrimitiveFlag; - classVmFlags(t, arrayBody(t, m->types, Machine::JcharType)) - |= PrimitiveFlag; - classVmFlags(t, arrayBody(t, m->types, Machine::JshortType)) - |= PrimitiveFlag; - classVmFlags(t, arrayBody(t, m->types, Machine::JintType)) - |= PrimitiveFlag; - classVmFlags(t, arrayBody(t, m->types, Machine::JlongType)) - |= PrimitiveFlag; - classVmFlags(t, arrayBody(t, m->types, Machine::JfloatType)) - |= PrimitiveFlag; - classVmFlags(t, arrayBody(t, m->types, Machine::JdoubleType)) - |= PrimitiveFlag; - classVmFlags(t, arrayBody(t, m->types, Machine::JvoidType)) - |= PrimitiveFlag; - - m->bootstrapClassMap = makeHashMap(this, 0, 0); - - { object loaderMap = makeHashMap(this, 0, 0); - set(t, m->loader, SystemClassLoaderMap, loaderMap); + if (image) { + m->processor->boot(this, image); + } else { + boot(this); } m->monitorMap = makeWeakHashMap(this, 0, 0); - m->stringMap = makeWeakHashMap(this, 0, 0); m->jniMethodTable = makeVector(this, 0, 0, false); m->localThread->set(this); - - { object bootCode = makeCode(t, 0, 0, 0, 0, 0, 1, false); - codeBody(t, bootCode, 0) = impdep1; - object bootMethod = makeMethod - (t, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, bootCode, 0); - PROTECT(t, bootMethod); - -#include "type-java-initializations.cpp" - } } else { peer = parent->child; parent->child = this; @@ -2108,7 +2101,7 @@ allocate3(Thread* t, Allocator* allocator, Machine::AllocationType type, case Machine::ImmortalAllocation: { unsigned total; object o = static_cast - (t->m->heap->allocateImmortal + (t->m->heap->allocateImmortalFixed (allocator, ceiling(sizeInBytes, BytesPerWord), objectMask, &total)); cast(o, 0) = FixedMark; @@ -2329,8 +2322,7 @@ findLoadedClass(Thread* t, object spec) PROTECT(t, spec); ACQUIRE(t, t->m->classLock); - return hashMapFind(t, systemClassLoaderMap(t, t->m->loader), - spec, byteArrayHash, byteArrayEqual); + return hashMapFind(t, t->m->classMap, spec, byteArrayHash, byteArrayEqual); } object @@ -2434,8 +2426,9 @@ resolveClass(Thread* t, object spec) PROTECT(t, spec); ACQUIRE(t, t->m->classLock); - object class_ = hashMapFind(t, systemClassLoaderMap(t, t->m->loader), - spec, byteArrayHash, byteArrayEqual); + object class_ = hashMapFind + (t, t->m->classMap, spec, byteArrayHash, byteArrayEqual); + if (class_ == 0) { if (byteArrayBody(t, spec, 0) == '[') { class_ = hashMapFind @@ -2487,8 +2480,7 @@ resolveClass(Thread* t, object spec) if (class_) { PROTECT(t, class_); - hashMapInsert(t, systemClassLoaderMap(t, t->m->loader), - spec, class_, byteArrayHash); + hashMapInsert(t, t->m->classMap, spec, class_, byteArrayHash); } else if (t->exception == 0) { object message = makeString(t, "%s", &byteArrayBody(t, spec, 0)); t->exception = makeClassNotFoundException(t, message); @@ -2770,6 +2762,38 @@ collect(Thread* t, Heap::CollectionType type) #endif } +void +walk(Thread* t, Heap::Walker* w, object o, unsigned start) +{ + object class_ = static_cast(t->m->heap->follow(objectClass(t, o))); + object objectMask = static_cast + (t->m->heap->follow(classObjectMask(t, class_))); + + if (objectMask) { + unsigned fixedSize = classFixedSize(t, class_); + unsigned arrayElementSize = classArrayElementSize(t, class_); + unsigned arrayLength + = (arrayElementSize ? + cast(o, fixedSize - BytesPerWord) : 0); + + uint32_t mask[intArrayLength(t, objectMask)]; + memcpy(mask, &intArrayBody(t, objectMask, 0), + intArrayLength(t, objectMask) * 4); + + ::walk(t, w, mask, fixedSize, arrayElementSize, arrayLength, start); + } else if (classVmFlags(t, class_) & SingletonFlag) { + unsigned length = singletonLength(t, o); + if (length) { + ::walk(t, w, singletonMask(t, o), + (singletonCount(t, o) + 2) * BytesPerWord, 0, 0, start); + } else if (start == 0) { + w->visit(0); + } + } else if (start == 0) { + w->visit(0); + } +} + int walkNext(Thread* t, object o, int previous) { @@ -2793,6 +2817,7 @@ void visitRoots(Machine* m, Heap::Visitor* v) { v->visit(&(m->loader)); + v->visit(&(m->classMap)); v->visit(&(m->bootstrapClassMap)); v->visit(&(m->monitorMap)); v->visit(&(m->stringMap)); diff --git a/src/machine.h b/src/machine.h index 7b1e5a99ed..06e3298fea 100644 --- a/src/machine.h +++ b/src/machine.h @@ -1175,6 +1175,7 @@ class Machine { System::Monitor* referenceLock; System::Library* libraries; object loader; + object classMap; object bootstrapClassMap; object monitorMap; object stringMap; @@ -1449,6 +1450,35 @@ expect(Thread* t, bool v) expect(t->m->system, v); } +class FixedAllocator: public Allocator { + public: + FixedAllocator(Thread* t, uint8_t* base, unsigned capacity): + t(t), base(base), offset(0), capacity(capacity) + { } + + virtual void* tryAllocate(unsigned) { + abort(t); + } + + virtual void* allocate(unsigned size) { + unsigned paddedSize = pad(size); + expect(t, offset + paddedSize < capacity); + + void* p = base + offset; + offset += paddedSize; + return p; + } + + virtual void free(const void*, unsigned) { + abort(t); + } + + Thread* t; + uint8_t* base; + unsigned offset; + unsigned capacity; +}; + inline void ensure(Thread* t, unsigned sizeInBytes) { @@ -2020,10 +2050,19 @@ resolveMethod(Thread* t, const char* className, const char* methodName, object resolveObjectArrayClass(Thread* t, object elementSpec); +inline bool +classNeedsInit(Thread* t, object c) +{ + return classVmFlags(t, c) & NeedInitFlag + and (classVmFlags(t, c) & InitFlag) == 0; +} + inline void initClass(Thread* t, object c) { - t->m->processor->initClass(t, c); + if (classNeedsInit(t, c)) { + t->m->processor->initClass(t, c); + } } object @@ -2225,6 +2264,9 @@ intern(Thread* t, object s); void exit(Thread* t); +void +walk(Thread* t, Heap::Walker* w, object o, unsigned start); + int walkNext(Thread* t, object o, int previous); diff --git a/src/main.cpp b/src/main.cpp index b784314579..5e412c0bae 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -79,11 +79,20 @@ main(int ac, const char** av) ++ vmArgs.nOptions; #endif +#ifdef BOOT_IMAGE + ++ vmArgs.nOptions; +#endif + JavaVMOption options[vmArgs.nOptions]; vmArgs.options = options; unsigned optionIndex = 0; +#ifdef BOOT_IMAGE + options[optionIndex++].optionString + = const_cast("-Davian.bootimage=" BOOT_IMAGE); +#endif + #ifdef BOOT_CLASSPATH options[optionIndex++].optionString = const_cast("-Xbootclasspath:" BOOT_CLASSPATH); diff --git a/src/posix.cpp b/src/posix.cpp index 1291809fa1..18b38cca99 100644 --- a/src/posix.cpp +++ b/src/posix.cpp @@ -26,6 +26,7 @@ #include "signal.h" #include "ucontext.h" #include "stdint.h" +#include "dirent.h" #include "arch.h" #include "system.h" @@ -428,6 +429,31 @@ class MySystem: public System { size_t length_; }; + class Directory: public System::Directory { + public: + Directory(System* s, DIR* directory): s(s), directory(directory) { } + + virtual const char* next() { + if (directory) { + dirent* e = readdir(directory); + if (e) { + return e->d_name; + } + } + return 0; + } + + virtual void dispose() { + if (directory) { + closedir(directory); + } + s->free(this); + } + + System* s; + DIR* directory; + }; + class Library: public System::Library { public: Library(System* s, void* p, const char* name, unsigned nameLength, @@ -623,7 +649,7 @@ class MySystem: public System { virtual Status map(System::Region** region, const char* name) { Status status = 1; - int fd = open(name, O_RDONLY); + int fd = ::open(name, O_RDONLY); if (fd != -1) { struct stat s; int r = fstat(fd, &s); @@ -641,19 +667,31 @@ class MySystem: public System { return status; } + virtual Status open(System::Directory** directory, const char* name) { + Status status = 1; + + DIR* d = opendir(name); + if (d) { + *directory = new (allocate(this, sizeof(Directory))) Directory(this, d); + status = 0; + } + + return status; + } + virtual FileType identify(const char* name) { struct stat s; int r = stat(name, &s); if (r == 0) { if (S_ISREG(s.st_mode)) { - return File; + return TypeFile; } else if (S_ISDIR(s.st_mode)) { - return Directory; + return TypeDirectory; } else { - return Unknown; + return TypeUnknown; } } else { - return DoesNotExist; + return TypeDoesNotExist; } } diff --git a/src/process.h b/src/process.h index 78c231b307..8be25c0bd0 100644 --- a/src/process.h +++ b/src/process.h @@ -188,6 +188,21 @@ populateMultiArray(Thread* t, object array, int32_t* counts, int findLineNumber(Thread* t, object method, unsigned ip); +inline bool +throwIfVolatileField(Thread* t, object field) +{ + if (fieldFlags(t, field) & ACC_VOLATILE) { + object message = makeString + (t, "volatile fields are not yet supported: %s.%s", + &byteArrayBody(t, className(t, fieldClass(t, field)), 0), + &byteArrayBody(t, fieldName(t, field), 0)); + t->exception = makeNoSuchFieldError(t, message); + return true; + } else { + return false; + } +} + } // namespace vm #endif//PROCESS_H diff --git a/src/processor.h b/src/processor.h index a0ef3491b2..94177a9abd 100644 --- a/src/processor.h +++ b/src/processor.h @@ -14,6 +14,10 @@ #include "common.h" #include "system.h" #include "heap.h" +#include "bootimage.h" +#include "heapwalk.h" +#include "zone.h" +#include "assembler.h" namespace vm { @@ -112,6 +116,24 @@ class Processor { virtual object getStackTrace(Thread* t, Thread* target) = 0; + virtual void + compileThunks(Thread* t, BootImage* image, uint8_t* code, unsigned* size, + unsigned capacity) = 0; + + virtual void + compileMethod(Thread* t, Zone* zone, uint8_t* code, unsigned* offset, + unsigned capacity, object* constants, object* calls, + DelayedPromise** addresses, object method) = 0; + + virtual void + visitRoots(BootImage* image, HeapWalker* w) = 0; + + virtual unsigned* + makeCallTable(Thread* t, BootImage* image, HeapWalker* w, uint8_t* code) = 0; + + virtual void + boot(Thread* t, BootImage* image) = 0; + object invoke(Thread* t, object method, object this_, ...) { diff --git a/src/system.h b/src/system.h index 10addecdd4..bb16c9be5d 100644 --- a/src/system.h +++ b/src/system.h @@ -20,10 +20,10 @@ class System { typedef intptr_t Status; enum FileType { - Unknown, - DoesNotExist, - File, - Directory + TypeUnknown, + TypeDoesNotExist, + TypeFile, + TypeDirectory }; class Thread { @@ -79,6 +79,12 @@ class System { virtual void dispose() = 0; }; + class Directory { + public: + virtual const char* next() = 0; + virtual void dispose() = 0; + }; + class Library { public: virtual void* resolve(const char* function) = 0; @@ -128,6 +134,7 @@ class System { unsigned returnType) = 0; virtual Status map(Region**, const char* name) = 0; virtual FileType identify(const char* name) = 0; + virtual Status open(Directory**, const char* name) = 0; virtual Status load(Library**, const char* name, bool mapName) = 0; virtual char pathSeparator() = 0; virtual int64_t now() = 0; diff --git a/src/util.cpp b/src/util.cpp index f149e9c2e2..2fa3e4ae56 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -106,6 +106,25 @@ cloneTreeNode(Thread* t, object n) return newNode; } +object +treeFind(Thread* t, object tree, intptr_t key, object sentinal, + intptr_t (*compare)(Thread* t, intptr_t key, object b)) +{ + object node = tree; + while (node != sentinal) { + intptr_t difference = compare(t, key, getTreeNodeValue(t, node)); + if (difference < 0) { + node = treeNodeLeft(t, node); + } else if (difference > 0) { + node = treeNodeRight(t, node); + } else { + return node; + } + } + + return 0; +} + void treeFind(Thread* t, TreeContext* c, object old, intptr_t key, object node, object sentinal, @@ -531,29 +550,20 @@ object treeQuery(Thread* t, object tree, intptr_t key, object sentinal, intptr_t (*compare)(Thread* t, intptr_t key, object b)) { - object node = tree; - while (node != sentinal) { - intptr_t difference = compare(t, key, getTreeNodeValue(t, node)); - if (difference < 0) { - node = treeNodeLeft(t, node); - } else if (difference > 0) { - node = treeNodeRight(t, node); - } else { - return getTreeNodeValue(t, node); - } - } - - return 0; + object node = treeFind(t, tree, key, sentinal, compare); + return (node ? getTreeNodeValue(t, node) : 0); } object -treeInsertNode(Thread* t, Zone* zone, object tree, intptr_t key, object node, - object sentinal, - intptr_t (*compare)(Thread* t, intptr_t key, object b)) +treeInsert(Thread* t, Zone* zone, object tree, intptr_t key, object value, + object sentinal, + intptr_t (*compare)(Thread* t, intptr_t key, object b)) { PROTECT(t, tree); PROTECT(t, sentinal); + object node = makeTreeNode(t, value, sentinal, sentinal); + TreeContext c(t, zone); treeFind(t, &c, tree, key, node, sentinal, compare); expect(t, c.fresh); @@ -561,4 +571,11 @@ treeInsertNode(Thread* t, Zone* zone, object tree, intptr_t key, object node, return treeAdd(t, &c); } +void +treeUpdate(Thread* t, object tree, intptr_t key, object value, object sentinal, + intptr_t (*compare)(Thread* t, intptr_t key, object b)) +{ + setTreeNodeValue(t, treeFind(t, tree, key, sentinal, compare), value); +} + } // namespace vm diff --git a/src/util.h b/src/util.h index ea8fc8b09c..bf895f6bae 100644 --- a/src/util.h +++ b/src/util.h @@ -89,9 +89,13 @@ treeQuery(Thread* t, object tree, intptr_t key, object sentinal, intptr_t (*compare)(Thread* t, intptr_t key, object b)); object -treeInsertNode(Thread* t, Zone* zone, object tree, intptr_t key, object node, - object sentinal, - intptr_t (*compare)(Thread* t, intptr_t key, object b)); +treeInsert(Thread* t, Zone* zone, object tree, intptr_t key, object value, + object sentinal, + intptr_t (*compare)(Thread* t, intptr_t key, object b)); + +void +treeUpdate(Thread* t, object tree, intptr_t key, object value, object sentinal, + intptr_t (*compare)(Thread* t, intptr_t key, object b)); class HashMapIterator: public Thread::Protector { public: @@ -103,11 +107,13 @@ class HashMapIterator: public Thread::Protector { void find() { object array = hashMapArray(t, map); - for (unsigned i = index; i < arrayLength(t, array); ++i) { - if (arrayBody(t, array, i)) { - node = arrayBody(t, array, i); - index = i + 1; - return; + if (array) { + for (unsigned i = index; i < arrayLength(t, array); ++i) { + if (arrayBody(t, array, i)) { + node = arrayBody(t, array, i); + index = i + 1; + return; + } } } node = 0; diff --git a/src/windows.cpp b/src/windows.cpp index de295d90b0..59c6bbacce 100644 --- a/src/windows.cpp +++ b/src/windows.cpp @@ -11,6 +11,7 @@ #include "sys/stat.h" #include "windows.h" #include "sys/timeb.h" +#include "dirent.h" #undef max #undef min @@ -422,6 +423,31 @@ class MySystem: public System { HANDLE file; }; + class Directory: public System::Directory { + public: + Directory(System* s, DIR* directory): s(s), directory(directory) { } + + virtual const char* next() { + if (directory) { + dirent* e = readdir(directory); + if (e) { + return e->d_name; + } + } + return 0; + } + + virtual void dispose() { + if (directory) { + closedir(directory); + } + s->free(this); + } + + System* s; + DIR* directory; + }; + class Library: public System::Library { public: Library(System* s, HMODULE handle, const char* name, bool mapName): @@ -630,19 +656,31 @@ class MySystem: public System { return status; } + virtual Status open(System::Directory** directory, const char* name) { + Status status = 1; + + DIR* d = opendir(name); + if (d) { + *directory = new (allocate(this, sizeof(Directory))) Directory(this, d); + status = 0; + } + + return status; + } + virtual FileType identify(const char* name) { struct _stat s; int r = _stat(name, &s); if (r == 0) { if (S_ISREG(s.st_mode)) { - return File; + return TypeFile; } else if (S_ISDIR(s.st_mode)) { - return Directory; + return TypeDirectory; } else { - return Unknown; + return TypeUnknown; } } else { - return DoesNotExist; + return TypeDoesNotExist; } } diff --git a/src/x86.cpp b/src/x86.cpp index 267f865749..7a9ada5b61 100644 --- a/src/x86.cpp +++ b/src/x86.cpp @@ -221,6 +221,38 @@ class Task { Task* next; }; +void* +resolveOffset(System* s, uint8_t* instruction, unsigned instructionSize, + int64_t value) +{ + intptr_t v = reinterpret_cast(value) + - instruction - instructionSize; + + expect(s, isInt32(v)); + + int32_t v4 = v; + memcpy(instruction + instructionSize - 4, &v4, 4); + return instruction + instructionSize; +} + +class OffsetListener: public Promise::Listener { + public: + OffsetListener(System* s, uint8_t* instruction, + unsigned instructionSize): + s(s), + instruction(instruction), + instructionSize(instructionSize) + { } + + virtual void* resolve(int64_t value) { + return resolveOffset(s, instruction, instructionSize, value); + } + + System* s; + uint8_t* instruction; + unsigned instructionSize; +}; + class OffsetTask: public Task { public: OffsetTask(Task* next, Promise* promise, Promise* instructionOffset, @@ -232,14 +264,15 @@ class OffsetTask: public Task { { } virtual void run(Context* c) { - uint8_t* instruction = c->result + instructionOffset->value(); - intptr_t v = reinterpret_cast(promise->value()) - - instruction - instructionSize; - - expect(c, isInt32(v)); - - int32_t v4 = v; - memcpy(instruction + instructionSize - 4, &v4, 4); + if (promise->resolved()) { + resolveOffset + (c->s, c->result + instructionOffset->value(), instructionSize, + promise->value()); + } else { + new (promise->listen(sizeof(OffsetListener))) + OffsetListener(c->s, c->result + instructionOffset->value(), + instructionSize); + } } Promise* promise; @@ -255,40 +288,70 @@ appendOffsetTask(Context* c, Promise* promise, Promise* instructionOffset, (c->tasks, promise, instructionOffset, instructionSize); } +void +copy(System* s, void* dst, int64_t src, unsigned size) +{ + switch (size) { + case 4: { + int32_t v = src; + memcpy(dst, &v, 4); + } break; + + case 8: { + int64_t v = src; + memcpy(dst, &v, 8); + } break; + + default: abort(s); + } +} + +class ImmediateListener: public Promise::Listener { + public: + ImmediateListener(System* s, void* dst, unsigned size, unsigned offset): + s(s), dst(dst), size(size), offset(offset) + { } + + virtual void* resolve(int64_t value) { + copy(s, dst, value, size); + return static_cast(dst) + offset; + } + + System* s; + void* dst; + unsigned size; + unsigned offset; +}; + class ImmediateTask: public Task { public: - ImmediateTask(Task* next, Promise* promise, Promise* offset, unsigned size): + ImmediateTask(Task* next, Promise* promise, Promise* offset, unsigned size, + unsigned promiseOffset): Task(next), promise(promise), offset(offset), - size(size) + size(size), + promiseOffset(promiseOffset) { } virtual void run(Context* c) { - switch (size) { - case 4: { - int32_t v = promise->value(); - memcpy(c->result + offset->value(), &v, size); - } break; - - case 8: { - int64_t v = promise->value(); - memcpy(c->result + offset->value(), &v, size); - } break; - - default: - abort(c); + if (promise->resolved()) { + copy(c->s, c->result + offset->value(), promise->value(), size); + } else { + new (promise->listen(sizeof(ImmediateListener))) ImmediateListener + (c->s, c->result + offset->value(), size, promiseOffset); } } Promise* promise; Promise* offset; unsigned size; + unsigned promiseOffset; }; void appendImmediateTask(Context* c, Promise* promise, Promise* offset, - unsigned size) + unsigned size, unsigned promiseOffset = 0) { c->tasks = new (c->zone->allocate(sizeof(ImmediateTask))) ImmediateTask (c->tasks, promise, offset, size); @@ -448,6 +511,39 @@ index(TernaryOperation operation, * OperandTypeCount * operand2); } +void +moveCR(Context* c, unsigned aSize, Assembler::Constant* a, + unsigned bSize, Assembler::Register* b); + +void +moveCR2(Context*, unsigned, Assembler::Constant*, unsigned, + Assembler::Register*, unsigned); + +void +callR(Context*, unsigned, Assembler::Register*); + +void +callC(Context* c, unsigned size UNUSED, Assembler::Constant* a) +{ + assert(c, size == BytesPerWord); + + unconditional(c, 0xe8, a); +} + +void +longCallC(Context* c, unsigned size, Assembler::Constant* a) +{ + assert(c, size == BytesPerWord); + + if (BytesPerWord == 8) { + Assembler::Register r(r10); + moveCR2(c, size, a, size, &r, 11); + callR(c, size, &r); + } else { + callC(c, size, a); + } +} + void jumpR(Context* c, unsigned size UNUSED, Assembler::Register* a) { @@ -466,6 +562,20 @@ jumpC(Context* c, unsigned size UNUSED, Assembler::Constant* a) unconditional(c, 0xe9, a); } +void +longJumpC(Context* c, unsigned size, Assembler::Constant* a) +{ + assert(c, size == BytesPerWord); + + if (BytesPerWord == 8) { + Assembler::Register r(r10); + moveCR2(c, size, a, &r, 11); + jumpR(c, size, &r); + } else { + jumpC(c, size, a); + } +} + void jumpM(Context* c, unsigned size UNUSED, Assembler::Memory* a) { @@ -536,7 +646,32 @@ longJumpC(Context* c, unsigned size, Assembler::Constant* a) moveCR(c, size, a, size, &r); jumpR(c, size, &r); } else { - jumpC(c, size, a); + if (a->value->resolved()) { + int64_t v = a->value->value(); + if (isInt8(v)) { + c->code.append(0x6a); + c->code.append(v); + } else if (isInt32(v)) { + c->code.append(0x68); + c->code.append4(v); + } else { + Assembler::Register tmp(c->client->acquireTemporary()); + moveCR(c, size, a, size, &tmp); + pushR(c, size, &tmp); + c->client->releaseTemporary(tmp.low); + } + } else { + if (BytesPerWord == 4) { + c->code.append(0x68); + appendImmediateTask(c, a->value, c->code.length(), BytesPerWord); + c->code.appendAddress(static_cast(0)); + } else { + Assembler::Register tmp(c->client->acquireTemporary()); + moveCR(c, size, a, size, &tmp); + pushR(c, size, &tmp); + c->client->releaseTemporary(tmp.low); + } + } } } @@ -654,6 +789,43 @@ negateRR(Context* c, unsigned aSize, Assembler::Register* a, negateR(c, aSize, a); } +void +moveCR2(Context* c, unsigned, Assembler::Constant* a, + unsigned bSize, Assembler::Register* b, unsigned promiseOffset) +{ + if (BytesPerWord == 4 and bSize == 8) { + int64_t v = a->value->value(); + + ResolvedPromise high((v >> 32) & 0xFFFFFFFF); + Assembler::Constant ah(&high); + + ResolvedPromise low(v & 0xFFFFFFFF); + Assembler::Constant al(&low); + + Assembler::Register bh(b->high); + + moveCR(c, 4, &al, 4, b); + moveCR(c, 4, &ah, 4, &bh); + } else { + rex(c, 0x48, b->low); + c->code.append(0xb8 | b->low); + if (a->value->resolved()) { + c->code.appendAddress(a->value->value()); + } else { + appendImmediateTask + (c, a->value, c->code.length(), BytesPerWord, promiseOffset); + c->code.appendAddress(static_cast(0)); + } + } +} + +void +moveCR(Context* c, unsigned aSize, Assembler::Constant* a, + unsigned bSize, Assembler::Register* b) +{ + moveCR2(c, aSize, a, bSize, b, 0); +} + void swapRR(Context* c, unsigned aSize UNUSED, Assembler::Register* a, unsigned bSize UNUSED, Assembler::Register* b) @@ -823,34 +995,6 @@ moveRM(Context* c, unsigned aSize, Assembler::Register* a, } } -// void -// moveMM(Context* c, unsigned aSize, Assembler::Memory* a, -// unsigned bSize, Assembler::Memory* b) -// { -// assert(c, aSize == bSize); - -// if (BytesPerWord == 8 or aSize <= 4) { -// uint32_t mask; -// if (BytesPerWord == 4 and aSize == 1) { -// mask = (1 << rax) | (1 << rcx) | (1 << rdx) | (1 << rbx); -// } else { -// mask = ~static_cast(0); -// } - -// Assembler::Register tmp(c->client->acquireTemporary(mask)); -// moveMR(c, aSize, a, aSize, &tmp); -// moveRM(c, aSize, &tmp, bSize, b); -// c->client->releaseTemporary(tmp.low); -// } else { -// Assembler::Register tmp(c->client->acquireTemporary(), -// c->client->acquireTemporary()); -// moveMR(c, aSize, a, aSize, &tmp); -// moveRM(c, aSize, &tmp, bSize, b); -// c->client->releaseTemporary(tmp.low); -// c->client->releaseTemporary(tmp.high); -// } -// } - void moveAR(Context* c, unsigned aSize, Assembler::Address* a, unsigned bSize, Assembler::Register* b) @@ -864,18 +1008,6 @@ moveAR(Context* c, unsigned aSize, Assembler::Address* a, moveMR(c, bSize, &memory, bSize, b); } -// void -// moveAM(Context* c, unsigned aSize, Assembler::Address* a, -// unsigned bSize, Assembler::Memory* b) -// { -// assert(c, BytesPerWord == 8 or (aSize == 4 and bSize == 4)); - -// Assembler::Register tmp(c->client->acquireTemporary()); -// moveAR(c, aSize, a, aSize, &tmp); -// moveRM(c, aSize, &tmp, bSize, b); -// c->client->releaseTemporary(tmp.low); -// } - void moveCR(Context* c, unsigned, Assembler::Constant* a, unsigned bSize, Assembler::Register* b) @@ -932,7 +1064,7 @@ moveCM(Context* c, unsigned aSize UNUSED, Assembler::Constant* a, if (a->value->resolved()) { c->code.append4(a->value->value()); } else { - appendImmediateTask(c, a->value, offset(c), 4); + appendImmediateTask(c, a->value, c->code.length(), 4); c->code.append4(0); } break; @@ -1452,6 +1584,32 @@ multiplyRR(Context* c, unsigned aSize, Assembler::Register* a, } } +void +compareCR(Context* c, unsigned size, Assembler::Constant* a, + Assembler::Register* b) +{ + assert(c, BytesPerWord == 8 or size == 4); + + if (a->value->resolved() and isInt32(a->value->value())) { + int64_t v = a->value->value(); + if (size == 8) rex(c); + if (isInt8(v)) { + c->code.append(0x83); + c->code.append(0xf8 | b->low); + c->code.append(v); + } else { + c->code.append(0x81); + c->code.append(0xf8 | b->low); + c->code.append4(v); + } + } else { + Assembler::Register tmp(c->client->acquireTemporary()); + moveCR(c, size, a, &tmp); + compareRR(c, size, &tmp, b); + c->client->releaseTemporary(tmp.high); + } +} + void multiplyCR(Context* c, unsigned aSize, Assembler::Constant* a, unsigned bSize, Assembler::Register* b) @@ -1465,7 +1623,6 @@ multiplyCR(Context* c, unsigned aSize, Assembler::Constant* a, moveCR(c, aSize, a, aSize, &tmp); multiplyRR(c, aSize, &tmp, bSize, b); - c->client->releaseTemporary(tmp.low); c->client->releaseTemporary(tmp.high); } else { @@ -1492,6 +1649,44 @@ multiplyCR(Context* c, unsigned aSize, Assembler::Constant* a, } } +void +compareRM(Context* c, unsigned aSize, Assembler::Register* a, + unsigned bSize UNUSED, Assembler::Memory* b) +{ + assert(c, aSize == bSize); + assert(c, BytesPerWord == 8 or aSize == 4); + + if (BytesPerWord == 8 and aSize == 4) { + moveRR(c, 4, a, 8, a); + } + encode(c, 0x39, a->low, b, true); +} + +void +compareCM(Context* c, unsigned aSize, Assembler::Constant* a, + unsigned bSize, Assembler::Memory* b) +{ + assert(c, aSize == bSize); + assert(c, BytesPerWord == 8 or aSize == 4); + + if (a->value->resolved()) { + encode(c, isInt8(v) ? 0x83 : 0x81, 7, b, true); + + if (isInt8(v)) { + c->code.append(v); + } else if (isInt32(v)) { + c->code.append4(v); + } else { + abort(c); + } + } else { + Assembler::Register tmp(c->client->acquireTemporary()); + moveCR(c, aSize, a, bSize, &tmp); + compareRM(c, bSize, &tmp, bSize, b); + c->client->releaseTemporary(tmp.low); + } +} + void longCompare(Context* c, Assembler::Operand* al, Assembler::Operand* ah, Assembler::Operand* bl, Assembler::Operand* bh, @@ -1882,15 +2077,11 @@ populateTables(ArchitectureContext* c) bo[index(Move, M, R)] = CAST2(moveMR); bo[index(Move, R, M)] = CAST2(moveRM); bo[index(Move, C, M)] = CAST2(moveCM); -// bo[index(Move, A, M)] = CAST2(moveAM); bo[index(Move, A, R)] = CAST2(moveAR); -// bo[index(Move, M, M)] = CAST2(moveMM); bo[index(MoveZ, R, R)] = CAST2(moveZRR); bo[index(MoveZ, M, R)] = CAST2(moveZMR); - bo[index(Swap, R, R)] = CAST2(swapRR); - bo[index(Compare, R, R)] = CAST2(compareRR); bo[index(Compare, C, R)] = CAST2(compareCR); bo[index(Compare, C, M)] = CAST2(compareCM); @@ -1998,14 +2189,35 @@ class MyArchitecture: public Assembler::Architecture { } } - virtual void updateCall(void* returnAddress, void* newTarget) { - uint8_t* instruction = static_cast(returnAddress) - 5; - assert(&c, *instruction == 0xE8); - assert(&c, reinterpret_cast(instruction + 1) % 4 == 0); + virtual void updateCall(UnaryOperation op UNUSED, + bool assertAlignment UNUSED, void* returnAddress, + void* newTarget) + { + if (BytesPerWord == 4 or op == Call or op == Jump) { + uint8_t* instruction = static_cast(returnAddress) - 5; - int32_t v = static_cast(newTarget) - - static_cast(returnAddress); - memcpy(instruction + 1, &v, 4); + assert(&c, ((op == Call or op == LongCall) and *instruction == 0xE8) + or ((op == Jump or op == LongJump) and *instruction == 0xE9)); + + assert(&c, (not assertAlignment) + or reinterpret_cast(instruction + 1) % 4 == 0); + + int32_t v = static_cast(newTarget) + - static_cast(returnAddress); + memcpy(instruction + 1, &v, 4); + } else { + uint8_t* instruction = static_cast(returnAddress) - 13; + + assert(&c, instruction[0] == 0x49 and instruction[1] == 0xBA); + assert(&c, instruction[10] == 0x41 and instruction[11] == 0xFF); + assert(&c, (op == LongCall and instruction[12] == 0xD2) + or (op == LongJump and instruction[12] == 0xE2)); + + assert(&c, (not assertAlignment) + or reinterpret_cast(instruction + 2) % 8 == 0); + + memcpy(instruction + 2, &newTarget, 8); + } } virtual unsigned alignFrameSize(unsigned sizeInWords) { diff --git a/test/Misc.java b/test/Misc.java index e75b1726cc..d27f96a735 100644 --- a/test/Misc.java +++ b/test/Misc.java @@ -3,6 +3,8 @@ public class Misc { private static int beta; private static byte byte1, byte2, byte3; + private final int NonStaticConstant = 42; + private int gamma; private int pajama; private boolean boolean1; @@ -108,6 +110,8 @@ public class Misc { { Misc m = new Misc(); m.toString(); + expect(m.NonStaticConstant == 42); + expect(m.time == 0xffffffffffffffffL); long t = m.time; expect(t == 0xffffffffffffffffL); diff --git a/vm.pro b/vm.pro index 7e024896ce..ad77a5eb57 100644 --- a/vm.pro +++ b/vm.pro @@ -50,6 +50,7 @@ -keep public class java.lang.ExceptionInInitializerError -keep public class java.lang.OutOfMemoryError -keep public class java.lang.reflect.InvocationTargetException +-keep public class java.io.IOException # ClassLoader.getSystemClassloader() depends on the existence of this class: