--- /dev/null 2018-04-20 00:47:31.000000000 +0100 +++ new/src/hotspot/share/runtime/continuation.cpp 2018-04-20 00:47:30.000000000 +0100 @@ -0,0 +1,973 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "classfile/javaClasses.inline.hpp" +#include "classfile/vmSymbols.hpp" +#include "interpreter/interpreter.hpp" +#include "logging/log.hpp" +#include "logging/logStream.hpp" +#include "oops/access.inline.hpp" +#include "oops/objArrayOop.inline.hpp" +#include "runtime/continuation.hpp" +#include "runtime/interfaceSupport.inline.hpp" +#include "runtime/javaCalls.hpp" +#include "utilities/macros.hpp" + +// TODO +// +// !!! Keep an eye out for deopt, and patch_pc +// +// Add: +// 0. Exceptions +// 1. stack walking (+ exceptions) +// 2. compiled methods +// 3. special native methods: Method.invoke, doPrivileged +// 4. compiled->intrepreted for serialization (look at scopeDesc) +// 5. caching in thread stacks + + +JVM_ENTRY(void, CONT_Foo(JNIEnv* env, jobject c)) { + tty->print_cr("Hello, World!"); +} +JVM_END + +#define CC (char*) /*cast a literal from (const char*)*/ +#define FN_PTR(f) CAST_FROM_FN_PTR(void*, &f) + +static JNINativeMethod CONT_methods[] = { + {CC"foo", CC"()V", FN_PTR(CONT_Foo)}, +}; + +void CONT_RegisterNativeMethods(JNIEnv *env, jclass cls) { + int status = env->RegisterNatives(cls, CONT_methods, sizeof(CONT_methods)/sizeof(JNINativeMethod)); + guarantee(status == JNI_OK && !env->ExceptionOccurred(), "register java.lang.Continuation natives"); +} + +static oop get_continuation(JavaThread* thread) { + return java_lang_Thread::continuation(thread->threadObj()); +} + +static void set_continuation(JavaThread* thread, oop cont) { + java_lang_Thread::set_continuation(thread->threadObj(), cont); +} + +static intptr_t* frame_bottom(frame &f, RegisterMap* map) { // exclusive; this is not copied for the frame f + frame sender = f.sender(map); + return sender.is_interpreted_frame() // TODO reuse an old computed value + ? *(intptr_t**)sender.addr_at(frame::interpreter_frame_initial_sp_offset) // *(intptr_t**)f.addr_at(frame::interpreter_frame_locals_offset) // + : sender.unextended_sp(); +} + +static intptr_t* frame_top(frame &f) { // inclusive this will be copied with the frame + return f.is_interpreted_frame() // TODO reuse an old computed value + ? *(intptr_t**)f.addr_at(frame::interpreter_frame_initial_sp_offset) + : f.unextended_sp(); +} + +struct HFrameMetadata { + int num_oops; + int frame_size; +}; + +#define METADATA_SIZE sizeof(HFrameMetadata) // bytes + +static inline int to_index(size_t x) { + return x >> 2; // stack is int[] +} + +static inline int to_bytes(int x) { + return x << 2; // stack is int[] +} + +static inline HFrameMetadata* metadata(intptr_t* hsp) { + return (HFrameMetadata*)((address)hsp - METADATA_SIZE); +} + +static inline intptr_t* to_haddress(const void* base, const int index) { + return (intptr_t*)((address)base + to_bytes(index)); +} + +static inline int to_index(void* base, void* ptr) { + return to_index((char*)ptr - (char*)base); +} + +static inline void relativize(intptr_t* const fp, intptr_t* const hfp, int offset) { + *(long*)(hfp + offset) = to_index((address)*(hfp + offset) - (address)fp + METADATA_SIZE); +} + +static inline void derelativize(intptr_t* const fp, int offset) { + *(fp + offset) = (intptr_t)((address)fp + to_bytes(*(long*)(fp + offset)) - METADATA_SIZE); +} + +static void print_oop(void *p, oop obj, outputStream* st = tty) { + if (!log_is_enabled(Trace, jvmcont)) return; + + st->print_cr(INTPTR_FORMAT ": ", p2i(p)); + if (obj == NULL) { + st->print_cr("*NULL*"); + } else { + if (oopDesc::is_oop_or_null(obj)) { + if (obj->is_objArray()) { + st->print_cr("valid objArray: " INTPTR_FORMAT, p2i(obj)); + } else { + obj->print_value_on(st); + // obj->print(); + } + } else { + st->print_cr("invalid oop: " INTPTR_FORMAT, p2i(obj)); + } + st->cr(); + } +} + +static void print_hframe(intptr_t* fp, void *pc, outputStream* st = tty) { + if (!log_is_enabled(Trace, jvmcont)) return; + + st->print_cr("\tfp: %p", fp); + + if (Interpreter::contains((address)pc)) { + Method* method = *(Method**)(fp + frame::interpreter_frame_method_offset); + st->print_cr("\tmethod: %p", method); + st->print("\tmethod: "); method->print_short_name(st); st->cr(); + } else { + CodeBlob* cb = CodeCache::find_blob(pc); + st->print_cr("\tcb: %p", cb); + if (cb != NULL) { + st->print("\tcb: "); os::print_location(st, (intptr_t)cb); + st->print_cr("\tcb.frame_size: %d", cb->frame_size()); + } + } + st->print_cr("\tlink: %ld", *(long*) (fp + frame::link_offset)); + st->print_cr("\treturn_pc: %p", *(void**)(fp + frame::return_addr_offset)); + if (Interpreter::contains((address)pc)) { + st->print_cr("\tissp: %ld", *(long*) (fp + frame::interpreter_frame_sender_sp_offset)); + st->print_cr("\tlast_sp: %ld", *(long*) (fp + frame::interpreter_frame_last_sp_offset)); + st->print_cr("\tinitial_sp: %ld", *(long*) (fp + frame::interpreter_frame_initial_sp_offset)); + // st->print_cr("\tmon_block_top: %ld", *(long*) (fp + frame::interpreter_frame_monitor_block_top_offset)); + // st->print_cr("\tmon_block_bottom: %ld", *(long*) (fp + frame::interpreter_frame_monitor_block_bottom_offset)); + st->print_cr("\tlocals: %ld", *(long*) (fp + frame::interpreter_frame_locals_offset)); + st->print_cr("\tcache: %p", *(void**)(fp + frame::interpreter_frame_cache_offset)); + st->print_cr("\tbcp: %p", *(void**)(fp + frame::interpreter_frame_bcp_offset)); + st->print_cr("\tmirror: %p", *(void**)(fp + frame::interpreter_frame_mirror_offset)); + // st->print("\tmirror: "); os::print_location(st, *(intptr_t*)(fp + frame::interpreter_frame_mirror_offset), true); + } + st->print("\treturn_pc: "); os::print_location(st, *(intptr_t*)(fp + frame::return_addr_offset)); +} + +static void print_hframe(oop cont, int fp, int sp, void* pc, outputStream* st = tty) { + if (!log_is_enabled(Trace, jvmcont)) return; + + int length = java_lang_Continuation::stack(cont)->length(); + int* hstack = (int*)java_lang_Continuation::stack_base(cont); + st->print_cr("\tfp: %d sp: %d", fp, sp); + intptr_t* hsp = to_haddress(hstack, sp); + intptr_t* hfp = to_haddress(hstack, fp); + HFrameMetadata* md = metadata(hsp); + st->print_cr("\tMetadata size: %d num_oops: %d", md->frame_size, md->num_oops); + + print_hframe(hfp, pc, st); + + if (false) { + st->print_cr("--data--"); + for(int i=0; iframe_size; i++) + st->print_cr("%p: %x", ((address)hsp + i), *((address)hsp + i)); + st->print_cr("--end data--"); + } +} + +static void print_hframe(oop cont, intptr_t* hfp, intptr_t *hsp, void* pc, outputStream* st = tty) { + print_hframe(cont, to_index(java_lang_Continuation::stack_base(cont), hfp), + to_index(java_lang_Continuation::stack_base(cont), hsp), + pc, + st); +} + +static void print_vframe(intptr_t* fp, outputStream* st = tty) { + if (!log_is_enabled(Trace, jvmcont)) return; + + st->print_cr("\tfp: %p", fp); + Method* method = *(Method**)(fp + frame::interpreter_frame_method_offset); + st->print("\tmethod: "); method->print_short_name(st); st->cr(); + st->print_cr("\tlink: %p", *(void**)(fp + frame::link_offset)); + st->print_cr("\treturn_pc: %p", *(void**)(fp + frame::return_addr_offset)); + st->print_cr("\tssp: %p", (void*) (fp + frame::sender_sp_offset)); + st->print_cr("\tissp: %p", *(void**)(fp + frame::interpreter_frame_sender_sp_offset)); + st->print_cr("\tlast_sp: %p", *(void**)(fp + frame::interpreter_frame_last_sp_offset)); + st->print_cr("\tinitial_sp: %p", *(void**)(fp + frame::interpreter_frame_initial_sp_offset)); + // st->print_cr("\tmon_block_top: %p", *(void**)(fp + frame::interpreter_frame_monitor_block_top_offset)); + // st->print_cr("\tmon_block_bottom: %p", *(void**)(fp + frame::interpreter_frame_monitor_block_bottom_offset)); + st->print_cr("\tlocals: %p", *(void**)(fp + frame::interpreter_frame_locals_offset)); + st->print_cr("\tcache: %p", *(void**)(fp + frame::interpreter_frame_cache_offset)); + st->print_cr("\tbcp: %p", *(void**)(fp + frame::interpreter_frame_bcp_offset)); + st->print_cr("\tmirror: %p", *(void**)(fp + frame::interpreter_frame_mirror_offset)); + // st->print("\tmirror: "); os::print_location(st, *(intptr_t*)(fp + frame::interpreter_frame_mirror_offset), true); + st->print("\treturn_pc: "); os::print_location(st, *(intptr_t*)(fp + frame::return_addr_offset)); +} + +static void print_vframe(frame f, RegisterMap* map = NULL, outputStream* st = tty) { + if (!log_is_enabled(Trace, jvmcont)) return; + + st->print_cr("\tfp: %p real_fp: %p, sp: %p pc: %p usp: %p top: %p", f.fp(), f.real_fp(), f.sp(), f.pc(), f.unextended_sp(), frame_top(f)); + + f.print_value_on(st, NULL); + + // st->print("\tpc: "); os::print_location(st, *(intptr_t*)f.pc()); + intptr_t* fp = f.fp(); + intptr_t* usp = frame_top(f); + if (f.is_interpreted_frame()) { + Method* method = f.interpreter_frame_method(); + st->print_cr("\tinterpreted"); + st->print("\tMethod: "); method->print_short_name(st); st->cr(); + // st->print_cr("base: %p end: %p", method->constMethod()->code_base(), method->constMethod()->code_end()); + print_vframe(fp); + } else if (f.is_compiled_frame()) { + st->print_cr("\tcompiled"); + st->print_cr("\tlink: %p", (void*)f.at(frame::link_offset)); + st->print_cr("\treturn_pc: %p", *(void**)(fp + frame::return_addr_offset)); + st->print_cr("\tssp: %p", *(void**)(fp + frame::sender_sp_offset)); + st->print_cr("\tcb.size: %d", f.cb()->frame_size()); + st->print("\treturn_pc: "); os::print_location(st, *(intptr_t*)(fp + frame::return_addr_offset)); + st->print_cr("\t'real' return_pc: %p", *(void**)(f.real_fp() - 1)); + st->print("\t`real` return_pc: "); os::print_location(st, *(intptr_t*)(f.real_fp() - 1)); + } + if (map != NULL) { + frame sender = f.sender(map); + long fsize = (address)frame_bottom(f, map) - (address)usp; + st->print_cr("\tsize: %ld", fsize); + st->print_cr("\tbounds: %p - %p", usp, frame_bottom(f, map)); + + if (false) { + st->print_cr("--data--"); + for(int i=0; iprint_cr("%p: %x", ((address)usp + i), *((address)usp + i)); + st->print_cr("--end data--"); + } + } +} + +// static void describe_frame(JavaThread* thread, frame& f, int i) { +// ResourceMark rm; +// FrameValues values; +// f.describe(values, i); +// values.print(thread); +// } + +static void print_frames(JavaThread* thread, outputStream* st = tty) { + if (!log_is_enabled(Trace, jvmcont)) return; + + st->print_cr("------- frames ---------"); + RegisterMap map(thread, false); + +#ifndef PRODUCT + ResourceMark rm; + FrameValues values; +#endif + + int i = 0; + for (frame f = thread->last_frame(); !f.is_entry_frame(); f = f.sender(&map)) { + print_vframe(f, &map, st); + #ifndef PRODUCT + f.describe(values, i); + #endif + i++; + } + // tty->print_cr("::::"); +#ifndef PRODUCT + values.print(thread); +#endif + st->print_cr("======= end frames ========="); +} + +static typeArrayOop getStack(JavaThread* thread, oop cont, int size, typeArrayOop stack) { + if (stack != NULL) { // fast path + if (to_index(size) <= java_lang_Continuation::sp(cont) - to_index(METADATA_SIZE)) + return stack; + } + HandleMark hm(thread); + Handle conth(thread, cont); + JavaCallArguments args; + args.push_oop(conth); + args.push_int(size); + JavaValue result(T_VOID); + JavaCalls::call_virtual(&result, SystemDictionary::Continuation_klass(), vmSymbols::getStack_name(), vmSymbols::continuationGetStack_signature(), &args, thread); + return java_lang_Continuation::stack(cont); +} + +static objArrayOop getRefStack(JavaThread* thread, oop &cont, int size, objArrayOop ref_stack) { + // assert (size == 1, "size != 1"); // the code below handles only the size == 1 case + if (ref_stack != NULL && size <= java_lang_Continuation::refSP(cont)) + return ref_stack; + + HandleMark hm(thread); + Handle conth(thread, cont); + JavaCallArguments args; + args.push_oop(conth); + args.push_int(size); + JavaValue result(T_VOID); + JavaCalls::call_virtual(&result, SystemDictionary::Continuation_klass(), vmSymbols::getRefStack_name(), vmSymbols::continuationGetRefStack_signature(), &args, thread); + cont = get_continuation(thread); // may have changed at safepoint + return java_lang_Continuation::refStack(cont); +} + +// freeze result +typedef enum { + freeze_ok = 0, + freeze_pinned_native, + freeze_pinned_monitor = 2 +} res_freeze; + +static void call_pinned(JavaThread* thread, oop cont, res_freeze res, frame& f) { + HandleMark hm(thread); + Handle conth(thread, cont); + JavaCallArguments args; + args.push_oop(conth); + args.push_int(res); + JavaValue result(T_VOID); + JavaCalls::call_virtual(&result, SystemDictionary::Continuation_klass(), vmSymbols::onPinned_name(), vmSymbols::continuationOnPinned_signature(), &args, thread); +} + +class FreezeOopClosure: public OopClosure, public CodeBlobClosure { + private: + JavaThread* _thread; + oop* _oops; + int* _oop_ind; + + intptr_t* _vsp; + intptr_t* _hsp; +#ifndef PRODUCT + oop _cont; +#endif + + + public: + int _count; + + protected: + template inline void do_oop_work(T* p) { + oop obj = RootAccess<>::oop_load(p); + assert (oopDesc::is_oop_or_null(obj), "invalid oop"); + + log_trace(jvmcont)("i: %d", *_oop_ind); + print_oop(p, obj); + + #ifndef PRODUCT + int offset = (address)p - (address)_vsp; + address hloc = (address)_hsp + offset; + if (hloc >= java_lang_Continuation::stack_base(_cont)) // callee-saved registers of the yield frame may be stored in the doYield frame, which would result in an underflow here + memset(hloc, 0xba, sizeof(T)); // mark oops + else + assert (*_oop_ind == 0, "oop_ind: %d", *_oop_ind); // ASSERTION A + #endif + + _oops[*_oop_ind] = obj; + (*_oop_ind)++; + _count++; + } + public: + FreezeOopClosure(JavaThread* thread, oop* oops, int* oop_ind, intptr_t* const vsp, intptr_t* const hsp, oop cont) + : _thread(thread), _oops(oops), _oop_ind(oop_ind), _vsp(vsp), _hsp(hsp) { + _count = 0; + #ifndef PRODUCT + _cont = cont; + assert (_hsp >= java_lang_Continuation::stack_base(cont), ""); + #endif + } + virtual void do_oop(oop* p) { do_oop_work(p); } + virtual void do_oop(narrowOop* p) { do_oop_work(p); } + virtual void do_code_blob(CodeBlob* cb) { + // code copied from g1CodeBlobClojure.cpp + nmethod* nm = cb->as_nmethod_or_null(); + if (nm != NULL) { + log_trace(jvmcont)("nmethod oops_do"); + nm->oops_do(this); + } + } +}; + +class ThawOopClosure: public OopClosure, public CodeBlobClosure { + private: + JavaThread* _thread; + oop _cont; + int _i; + public: + int _count; + protected: + template inline void do_oop_work(T* p) { + oop obj = java_lang_Continuation::refStack(_cont)->obj_at(_i); // does a HeapAccess load barrier + + log_trace(jvmcont)("_i: %d", _i); + print_oop(p, obj); + + RootAccess<>::oop_store(p, obj); + _i++; + _count++; + } + public: + ThawOopClosure(JavaThread* thread, oop cont, int num_oops) + : _thread(thread), _cont(cont) { + _i = java_lang_Continuation::refSP(cont); + _count = 0; + } + virtual void do_oop(oop* p) { do_oop_work(p); } + virtual void do_oop(narrowOop* p) { do_oop_work(p); } + virtual void do_code_blob(CodeBlob* cb) { + // code copied from g1CodeBlobClojure.cpp + nmethod* nm = cb->as_nmethod_or_null(); + if (nm != NULL) { + log_trace(jvmcont)("nmethod oops_do"); + nm->oops_do(this); + } + } +}; + +// see Continuation.refStackSize +static int num_oops(oop cont) { + objArrayOop refStack = java_lang_Continuation::refStack(cont); + return refStack == NULL ? 0 : refStack->length() - java_lang_Continuation::refSP(cont); +} + +static void pop_oops(oop cont, int num) { + if (num == 0) return; + log_trace(jvmcont)("popping %d oops", num); + + java_lang_Continuation::set_refSP(cont, java_lang_Continuation::refSP(cont) + num); +} + +static void set_anchor(JavaThread* thread, FrameInfo* fi) { + JavaFrameAnchor* anchor = thread->frame_anchor(); + anchor->set_last_Java_fp((intptr_t*)fi->fp); + anchor->set_last_Java_sp((intptr_t*)fi->sp); + anchor->set_last_Java_pc(fi->pc); + + log_trace(jvmcont)("set_anchor:"); + print_vframe(thread->last_frame()); +} + +// freezes a single frame +static res_freeze freeze_frame(JavaThread* thread, oop cont, frame &f, address &target, RegisterMap &map, int* ifp, bool first, oop* oops, int& oop_ind) { + log_trace(jvmcont)("============================="); + RegisterMap dmap(NULL, false); + + print_vframe(f, &dmap); + + const bool is_interpreted = f.is_interpreted_frame(); + const bool is_compiled = f.is_compiled_frame(); + + if (!is_interpreted && !is_compiled) { + tty->print_cr("not Java: %p", f.pc()); + os::print_location(tty, (intptr_t)f.pc()); + + // TODO: support reflection, doPrivileged + return freeze_pinned_native; + } + + if (is_interpreted) { + assert(f.is_interpreted_frame_valid(thread), "invalid frame"); + if (f.interpreter_frame_monitor_end() < f.interpreter_frame_monitor_begin()) { + return freeze_pinned_monitor; + } + } + + intptr_t* const usp = first ? f.sp() : frame_top(f); + intptr_t* bottom = frame_bottom(f, &dmap); + assert (bottom > usp, "bottom: %p usp: %p", bottom, usp); + intptr_t* const entrySP = (intptr_t*)java_lang_Continuation::entrySP(cont); + if (bottom > entrySP) + bottom = entrySP; + const int fsize = (bottom - usp)*sizeof(intptr_t); + + HFrameMetadata* md = (HFrameMetadata*)target; + intptr_t* hsp = (intptr_t*)(target + METADATA_SIZE); + + log_trace(jvmcont)("Copying from v: %p - %p (%d bytes)", usp, (address)usp + fsize, fsize); + log_trace(jvmcont)("Copying to h: %p - %p (%d - %d)", hsp, (address)hsp + fsize, + to_index(java_lang_Continuation::stack_base(cont), hsp), to_index(java_lang_Continuation::stack_base(cont), (address)hsp + fsize)); + + Copy::conjoint_memory_atomic(usp, hsp, fsize); + + log_trace(jvmcont)("hsp index: %d", to_index(java_lang_Continuation::stack_base(cont), hsp)); + log_trace(jvmcont)("hstack length: %d", java_lang_Continuation::stack(cont)->length()); + + // TODO: Check whether FP is at all necessary for compiled frames. + intptr_t* const fp = f.real_fp(); + const int fpoffset = fp - usp; + intptr_t* hfp = hsp + fpoffset; + *ifp = to_index(java_lang_Continuation::stack_base(cont), hfp); + log_trace(jvmcont)("hfp index: %d", *ifp); + + // patch + if (is_interpreted) { + relativize(fp, hfp, frame::link_offset); + relativize(fp, hfp, frame::interpreter_frame_sender_sp_offset); + relativize(fp, hfp, frame::interpreter_frame_last_sp_offset); + relativize(fp, hfp, frame::interpreter_frame_initial_sp_offset); // == block_top == block_bottom + relativize(fp, hfp, frame::interpreter_frame_locals_offset); + } else { + // TODO + } + + log_trace(jvmcont)("Walking oops"); + + FreezeOopClosure oopClosure(thread, oops, &oop_ind, usp, hsp, cont); + f.oops_do(&oopClosure, &oopClosure, &map); + int num_oops = oopClosure._count; + log_trace(jvmcont)("Done walking oops"); + + md->num_oops = num_oops; + md->frame_size = fsize; + + log_trace(jvmcont)("num_oops: %d", num_oops); + log_trace(jvmcont)("size: %d", fsize); + log_trace(jvmcont)("hframe:"); + print_hframe(cont, hfp, hsp, NULL); + + target += fsize + METADATA_SIZE; + f = f.sender(&map); + + return freeze_ok; +} + +static void copy_oops(JavaThread* thread, oop cont, oop* oops, int oop_ind) { + objArrayOop ref_stack = getRefStack(thread, cont, oop_ind, java_lang_Continuation::refStack(cont)); // TODO: may not be safe! May trigger a safepoint. Is the handle in the entry point enough to protect us? + + log_trace(jvmcont)("Copying %d oops", oop_ind); + int refSP = java_lang_Continuation::refSP(cont); + for (int i=0; iobj_at_put(refSP - oop_ind + i, oops[i]); // does a HeapAccess write barrier + + java_lang_Continuation::set_refSP(cont, refSP - oop_ind); + log_trace(jvmcont)("refSP: %d", java_lang_Continuation::refSP(cont)); +} + +static int count_frames(frame f, intptr_t* bottom) { + RegisterMap map(NULL, false); + int i; + log_trace(jvmcont)("count_frames bottom: %p", bottom); + while (f.unextended_sp() < bottom) { + f = f.sender(&map); + i++; + } + log_trace(jvmcont)("count_frames #frames: %d", i-1); + return i-1; +} + +// freezes all frames of a single continuation +static bool freeze_continuation(JavaThread* thread, oop cont, frame& f, RegisterMap& map) { + HandleMark hm(thread); + + assert (cont != NULL, "cont: %p", (void*)cont); + + LogStreamHandle(Trace, jvmcont) st; + + log_trace(jvmcont)("Freeze 0000 sp: %p fp: %p pc: %p, cont: %p", f.sp(), f.fp(), f.pc(), (void*)cont); + log_trace(jvmcont)("Freeze 1111 sp: %d fp: %d pc: %p", java_lang_Continuation::sp(cont), java_lang_Continuation::fp(cont), java_lang_Continuation::pc(cont)); + + intptr_t* bottom = (intptr_t*)java_lang_Continuation::entrySP(cont); // watch for off-by-n (bottom is highest address; stacks grow down) + intptr_t* top = (intptr_t*)f.sp(); + int size = (bottom - top) * sizeof(intptr_t); // in bytes + int num_frames = count_frames(f, bottom); + size += num_frames * METADATA_SIZE; + + assert (num_frames < 1000, "num_frames: %d size: %d", num_frames, size); // just sanity; sometimes get garbage + + typeArrayOop s = getStack(thread, cont, size, java_lang_Continuation::stack(cont)); + assert(java_lang_Continuation::stack_size(cont) >= size, "sanity check"); + + log_trace(jvmcont)("bottom: %p size: %d, count %d", bottom, size, num_frames); + + const int hsp = java_lang_Continuation::sp(cont); + int* hstack = (int*)java_lang_Continuation::stack_base(cont); + const int orig_hfp = java_lang_Continuation::fp(cont); + void* const orig_pc = java_lang_Continuation::pc(cont); + + address limit = (address)(hstack + hsp) - METADATA_SIZE; + assert(limit - (address)hstack >= size, "sanity check"); + address target = limit - size; + const int target_index = to_index(hstack, target); + log_trace(jvmcont)("target_index: %d", target_index); + + ResourceMark rm(thread); + + int oop_ind = 0; + oop *oops = NEW_RESOURCE_ARRAY(oop, size); + + address source = (address)f.sp(); + + intptr_t *sp = NULL, *fp = NULL; + address pc = NULL; + bool first = true; + int ifp; + + while(true) { + if (frame_top(f) >= bottom) { + if (log_is_enabled(Trace, jvmcont)) { + log_trace(jvmcont)("Found entry frame: "); + if (f.is_interpreted_frame()) + f.interpreter_frame_method()->print_short_name(tty); tty->cr(); + f.print_value_on(tty, NULL); + // log_trace(jvmcont)("Found entry frame: %s", f.interpreter_frame_method()->name()->as_C_string()); + } + break; + } + + sp = f.sp(); + fp = f.fp(); + pc = f.pc(); + res_freeze res = freeze_frame(thread, cont, f, target, map, &ifp, first, oops, oop_ind); // changes f, target and oop_ind + if (res != freeze_ok) { // f hasn't changed + call_pinned(thread, cont, res, f); + return false; + } + if (first) { + java_lang_Continuation::set_sp(cont, target_index + to_index(METADATA_SIZE) + to_index(source, sp)); + java_lang_Continuation::set_fp(cont, ifp); + java_lang_Continuation::set_pc(cont, pc); // Interpreter::return_entry(vtos, 0, Bytecodes::_invokestatic, true)); // + + log_trace(jvmcont)("Set cont fields sp: %d fp %d pc: %p", java_lang_Continuation::sp(cont), java_lang_Continuation::fp(cont), java_lang_Continuation::pc(cont)); + first = false; + } + + source = (address)f.sp(); + } + + copy_oops(thread, cont, oops, oop_ind); + + // fix fp link in h-stack for bottom-most frame + hstack[ifp] = orig_hfp > 0 ? orig_hfp - ifp : orig_hfp; + assert (orig_hfp == -1 || ifp + hstack[ifp] == orig_hfp, "ifp: %d hstack[ifp]: %d orig_hfp: %d", ifp, hstack[ifp], orig_hfp); + if (orig_pc != NULL) { + *(void**)(to_haddress(hstack, ifp) + frame::return_addr_offset) = orig_pc; + } + + return true; +} + +// recursively call freeze for all continuations up the chain until appropriate scope +static bool freeze_continuations(JavaThread* thread, oop cont, oop scope, frame& f, RegisterMap &map) { + // save old values to restore in case of freeze failure + const int orig_sp = java_lang_Continuation::sp(cont); + const int orig_fp = java_lang_Continuation::fp(cont); + const void* orig_pc = java_lang_Continuation::pc(cont); + const int orig_num_oops = num_oops(cont); + + if(!(freeze_continuation(thread, cont, f, map) + && (java_lang_Continuation::scope(cont) == scope // end recursion + || freeze_continuations(thread, java_lang_Continuation::parent(cont), scope, f, map)))) { + + // reset cont's fp/sp/pc to old values and pop/null oops + java_lang_Continuation::set_sp(cont, orig_sp); + java_lang_Continuation::set_fp(cont, orig_fp); + java_lang_Continuation::set_pc(cont, orig_pc); + pop_oops(cont, num_oops(cont) - orig_num_oops); + + return false; // propagates failure up the recursive call-chain. + } + + return true; +} + +// returns the continuation yielding (based on context), or NULL for failure (due to pinning) +// it freezes multiple continuations, depending on contex +// it must set Continuation.stackSize +// sets Continuation.fp/sp to relative indices +// +// In: fi->pc, fi->sp, fi->fp all point to the current (topmost) frame to freeze +// Out: fi->pc, fi->sp, fi->fp all point to the entry frame +// unless freezing has failed, in which case fi->pc = 0 +// +JRT_ENTRY(void, Continuation::freeze(JavaThread* thread, FrameInfo* fi, oop scope)) + set_anchor(thread, fi); // DEBUG + print_frames(thread); + + HandleMark hm(thread); + + oop cont = get_continuation(thread); + assert(cont != NULL && oopDesc::is_oop_or_null(cont), "Invalid cont: %p", (void*)cont); + + RegisterMap map(thread, true); + map.set_include_argument_oops(false); + frame f((intptr_t*)fi->sp, (intptr_t*)fi->fp, fi->pc); + frame::update_map_with_saved_link(&map, (intptr_t**)(f.sp() - 2)); // the doYield stub saves rbp here (stub_generator_....cpp generate_cont_doYield, call to enter()) + assert (map.update_map(), "RegisterMap not set to update"); + + if (!freeze_continuations(thread, cont, scope, f, map)) { + fi->fp = NULL; + fi->sp = NULL; + fi->pc = NULL; + return; + } + + set_continuation(thread, cont); // TODO BUG: check what happens in Continuation.run. Does it overwrite the current cont? + set_anchor(thread, fi); + + fi->sp = java_lang_Continuation::entrySP(cont); + fi->fp = java_lang_Continuation::entryFP(cont); + fi->pc = java_lang_Continuation::entryPC(cont); + + log_trace(jvmcont)("ENTRY: sp: %p fp: %p pc: %p", fi->sp, fi->fp, fi->pc); + log_trace(jvmcont)("=== end of freeze"); +JRT_END + +static intptr_t* thaw_frame(JavaThread* thread, oop cont, address &target, int &sp, int &fp, void* &pc, RegisterMap &map) { + log_trace(jvmcont)("============================="); + const int* hstack = (int*)java_lang_Continuation::stack_base(cont); + intptr_t* hsp = to_haddress(hstack, sp); + intptr_t* hfp = to_haddress(hstack, fp); + HFrameMetadata* md = metadata(hsp); + + int fsize = md->frame_size; + + log_trace(jvmcont)("hfp: %d hsp: %d", fp, sp); + log_trace(jvmcont)("fsize: %d", fsize); + + log_trace(jvmcont)("Copying from h: %p - %p (%d - %d)", hsp, (address)hsp + fsize, + to_index(java_lang_Continuation::stack_base(cont), hsp), to_index(java_lang_Continuation::stack_base(cont), (address)hsp + fsize)); + log_trace(jvmcont)("Copying to v: %p - %p (%d bytes)", target, target + fsize, fsize); + + print_hframe(cont, fp, sp, pc); + + assert(target + fsize <= java_lang_Continuation::entrySP(cont), + "Overwriting entry frame entrySP: %p, fsize: %d target: %p, target+fsize: %p", + java_lang_Continuation::entrySP(cont), fsize, target, target+fsize); + + Copy::conjoint_memory_atomic(hsp, target, fsize); + + // patch + int fpoffset = hfp - hsp; + intptr_t* vfp = (intptr_t*)target + fpoffset; + + frame f((intptr_t*)target, vfp, (address)pc); + const bool is_interpreted = f.is_interpreted_frame(); + const bool is_compiled = f.is_compiled_frame(); + + if (is_interpreted) { + derelativize(vfp, frame::link_offset); + derelativize(vfp, frame::interpreter_frame_sender_sp_offset); + derelativize(vfp, frame::interpreter_frame_last_sp_offset); + derelativize(vfp, frame::interpreter_frame_initial_sp_offset); // == block_top == block_bottom + derelativize(vfp, frame::interpreter_frame_locals_offset); + } else if (is_compiled) { + // TODO get nmethod. Call popNmethod if necessary + + // when copying nmethod frames, we need to check for them being made non-reentrant, in which case we need to deopt them + // and turn them into interpreter frames. + } else { + os::print_location(tty, (intptr_t)pc); + assert(false, "Shouldn't get here"); + } + + log_trace(jvmcont)("Walking oops (thaw)"); + ThawOopClosure oopClosure(thread, cont, md->num_oops); + f.oops_do(&oopClosure, &oopClosure, &map); + log_trace(jvmcont)("_count: %d num_oops: %d", oopClosure._count, md->num_oops); + assert(oopClosure._count == md->num_oops, "closure oop count different."); + log_trace(jvmcont)("Done walking oops (thaw)"); + + assert(!f.is_interpreted_frame() || f.is_interpreted_frame_valid(thread), "invalid thawed frame"); + print_vframe(f, &map); + + pc = *(void**)(to_haddress(hstack, fp) + frame::return_addr_offset); + sp += to_index(fsize + METADATA_SIZE); + if (hstack[fp] > 0) + fp += hstack[fp]; // TODO XXXXXXXX + else + fp = -1; + + target += fsize; + + return vfp; +} + +// called after preparations (stack overflow check and making room) +static void thaw1(JavaThread* thread, FrameInfo* fi, const int num_frames) { + address target = fi->sp; // we leave fi->sp as-is + oop cont = get_continuation(thread); + assert(cont != NULL && oopDesc::is_oop_or_null(cont), "Invalid cont: %p", (void*)cont); + + log_trace(jvmcont)("=========== thaw %d", num_frames); + if (num_frames == 1) + log_trace(jvmcont)("== RETURN BARRIER"); + + log_trace(jvmcont)("thaw: TARGET: %p", target); + assert(num_frames > 0, "num_frames <= 0: %d", num_frames); + + const int length = java_lang_Continuation::stack(cont)->length(); + const int* hstack = (int*)java_lang_Continuation::stack_base(cont); + + int sp = java_lang_Continuation::sp(cont); + int fp = java_lang_Continuation::fp(cont); + void* pc = java_lang_Continuation::pc(cont); + + log_trace(jvmcont)("sp: %d fp: %d pc: %p", sp, fp, pc); + + assert(fp < length, "no more frames"); + + ResourceMark rm(thread); + RegisterMap map(thread, false); + map.set_include_argument_oops(false); + + intptr_t* last_vfp; + void* last_pc; + + for (int i = 0; i < num_frames && fp < length; i++) { + assert(fp > 0, ""); + int prev_sp = sp; + last_pc = pc; + address last_sp = target; + last_vfp = thaw_frame(thread, cont, target, sp, fp, pc, map); // changes target, fp, sp, pc + + // pop oops + int num_oops = metadata(to_haddress(hstack, prev_sp))->num_oops; + pop_oops(cont, num_oops); + + // TODO: the following paragraph can and should be moved outside the loop + // we set the continuation frame to the caller of the bottom-most frame thawed + java_lang_Continuation::set_fp(cont, fp); + java_lang_Continuation::set_sp(cont, sp); + java_lang_Continuation::set_pc(cont, pc); + log_trace(jvmcont)("ZZZZ cont sp: %d fp: %d pc: %p", java_lang_Continuation::sp(cont), java_lang_Continuation::fp(cont), java_lang_Continuation::pc(cont)); + + if (i == 0) { + log_trace(jvmcont)("last_pc: %p", last_pc); + fi->pc = (address)last_pc; // we'll jump to the current continuation pc // Interpreter::return_entry(vtos, 0, Bytecodes::_invokestatic, true); // + fi->fp = (address)last_vfp; + assert(fi->sp == last_sp, "last_sp is wrong"); + set_anchor(thread, fi); + } + + // DEBUG + // *(void**)(last_vfp + frame::return_addr_offset) = Interpreter::return_entry(vtos, 0, Bytecodes::_invokestatic, true); + } + + // patch return address on bottom-most frame. vfp points to that frame + // TODO: see frame::patch_pc to handle deopt + log_trace(jvmcont)("last fp: %p", last_vfp); + *(void**) (last_vfp + frame::return_addr_offset) = StubRoutines::cont_returnBarrier(); // TODO: choose return barrier based on return type + *(intptr_t**)(last_vfp + frame::link_offset) = (intptr_t*)java_lang_Continuation::entryFP(cont); + if (Interpreter::contains((address)last_pc)) { // interpreted frame + *(intptr_t**)(last_vfp + frame::interpreter_frame_sender_sp_offset) = (intptr_t*)java_lang_Continuation::entrySP(cont); + } + + log_trace(jvmcont)("fi->pc: %p", fi->pc); + // log_trace(jvmcont)("fi->pc: "); os::print_location(tty, *(intptr_t*)fi->pc); + + print_frames(thread); + log_trace(jvmcont)("cont sp: %d fp: %d", java_lang_Continuation::sp(cont), java_lang_Continuation::fp(cont)); + log_trace(jvmcont)("=== End of thaw"); +} + +static size_t frames_size(oop cont, int frames) { + size_t size = 0; + int length = java_lang_Continuation::stack(cont)->length(); + int* hstack = (int*)java_lang_Continuation::stack_base(cont); + int sp = java_lang_Continuation::sp(cont); + // int fp = java_lang_Continuation::fp(cont); + for (int i=0; i < frames && sp >= 0 && sp < length; i++) { + int fsize = metadata(to_haddress(hstack, sp))->frame_size; // (indices are to 32-bit words) + size += fsize; + sp += to_index(fsize + METADATA_SIZE); + // fp += hstack[fp]; // contains offset to previous fp + } + log_trace(jvmcont)("frames_size: %lu", size); + return size; +} + +static bool stack_overflow_check(JavaThread* thread, int size, address sp) { + const int page_size = os::vm_page_size(); + if (size > page_size) { + if (sp - size < thread->stack_overflow_limit()) { + return false; + } + } + return true; +} + +// In: fi->sp = the sp of the entry frame +// Out: returns the size of frames to thaw or 0 for no more frames or a stack overflow +// On failure: fi->sp - cont's entry SP +// fi->fp - cont's entry FP +// fi->pc - overflow? throw StackOverflowError : cont's entry PC +JRT_LEAF(int, Continuation::prepare_thaw(FrameInfo* fi, int num_frames)) + log_trace(jvmcont)("prepare_thaw %d", num_frames); + + const address bottom = fi->sp; // os::current_stack_pointer(); + log_trace(jvmcont)("bottom: %p", bottom); + + JavaThread* thread = JavaThread::current(); + oop cont = get_continuation(thread); + + int size = frames_size(cont, num_frames); + if (size == 0) { // no more frames + fi->sp = java_lang_Continuation::entrySP(cont); + fi->fp = java_lang_Continuation::entryFP(cont); + fi->pc = java_lang_Continuation::entryPC(cont); + return 0; + } + if (!stack_overflow_check(thread, size + 300, bottom)) { + fi->sp = java_lang_Continuation::entrySP(cont); + fi->fp = java_lang_Continuation::entryFP(cont); + fi->pc = StubRoutines::throw_StackOverflowError_entry(); + return 0; + } + + address target = bottom - size; + log_trace(jvmcont)("target: %p", target); + + return size; +JRT_END + +// IN: fi->sp = the future SP of the topmost thawed frame (where we'll copy the thawed frames) +// Out: fi->sp = the SP of the topmost thawed frame -- the one we will resume at +// fi->fp = the FP " ... +// fi->pc = the PC " ... +// JRT_ENTRY(void, Continuation::thaw(JavaThread* thread, FrameInfo* fi, int num_frames)) +JRT_LEAF(void, Continuation::thaw(FrameInfo* fi, int num_frames)) + thaw1(JavaThread::current(), fi, num_frames); +JRT_END + +// When walking the virtual stack, this method returns true +// iff the frame is a thawed continuation frame whose +// caller is still frozen on the h-stack. +// The continuation object can be extracted from the thread. +bool Continuation::is_cont_bottom_frame(const frame& f) { + return f.sender_pc() == StubRoutines::cont_returnBarrier(); // TODO: account for multiple return barriers (based on return type) +} + +static oop find_continuation_for_frame(JavaThread* thread, intptr_t* const fp) { + oop cont = get_continuation(thread); + while (cont != NULL && java_lang_Continuation::entryFP(cont) < (address)fp) + cont = java_lang_Continuation::parent(cont); + return cont; +} + +frame Continuation::fix_continuation_bottom_sender(const frame& callee, frame f, RegisterMap* map) { + if (map->thread() != NULL && is_cont_bottom_frame(callee)) { + // log_trace(jvmcont)("YEYEYEYEYEYEYEEYEY"); + oop cont = find_continuation_for_frame(map->thread(), f.fp()); + address pc = java_lang_Continuation::entryPC(cont); + f.set_pc(pc); + } + return f; +}