# HG changeset patch
# User duke
# Date 1499269489 -7200
# Node ID 0dc08749a6ccbd1a5cfb982413d4fcce0e7bc108
# Parent 9fcad86579e64fda3a9c8bde52677baff9caf529# Parent 08a4f9ea29b6687ed0dffc9e2c729361d04b8684
Merge
diff -r 9fcad86579e6 -r 0dc08749a6cc .hgtags-top-repo
--- a/.hgtags-top-repo Sat May 14 10:24:02 2011 -0700
+++ b/.hgtags-top-repo Wed Jul 05 17:44:49 2017 +0200
@@ -116,3 +116,4 @@
7ed6d0b9aaa12320832a7ddadb88d6d8d0dda4c1 jdk7-b139
dcfe74f1c6553c556e7d361c30b0b614eb5e40f6 jdk7-b140
c6569c5585851dfd39b8de8e021c3c312f51af12 jdk7-b141
+cfbbdb77eac0397b03eb99ee2e07ea00e0a7b81e jdk7-b142
diff -r 9fcad86579e6 -r 0dc08749a6cc Makefile
--- a/Makefile Sat May 14 10:24:02 2011 -0700
+++ b/Makefile Wed Jul 05 17:44:49 2017 +0200
@@ -25,6 +25,34 @@
BUILD_PARENT_DIRECTORY=.
+# Basename of any originally supplied ALT_OUTPUTDIR directory
+ifndef ORIG_OUTPUTDIR_BASENAME
+ ifdef ALT_OUTPUTDIR
+ ORIG_OUTPUTDIR_BASENAME := $(shell basename $(ALT_OUTPUTDIR))
+ else
+ ORIG_OUTPUTDIR_BASENAME = $(PLATFORM)-$(ARCH)
+ endif
+endif
+export ORIG_OUTPUTDIR_BASENAME
+
+# The three possible directories created for output (3 build flavors)
+OUTPUTDIR_BASENAME- = $(ORIG_OUTPUTDIR_BASENAME)
+OUTPUTDIR_BASENAME-debug = $(ORIG_OUTPUTDIR_BASENAME)-debug
+OUTPUTDIR_BASENAME-fastdebug = $(ORIG_OUTPUTDIR_BASENAME)-fastdebug
+
+# Relative path to a debug output area
+REL_JDK_OUTPUTDIR = ../$(OUTPUTDIR_BASENAME-$(DEBUG_NAME))
+
+# The created jdk image directory
+JDK_IMAGE_DIRNAME = j2sdk-image
+JDK_IMAGE_DIR = $(OUTPUTDIR)/$(JDK_IMAGE_DIRNAME)
+ABS_JDK_IMAGE_DIR = $(ABS_OUTPUTDIR)/$(JDK_IMAGE_DIRNAME)
+
+# Relative path from an output directory to the image directory
+REL_JDK_IMAGE_DIR = ../$(OUTPUTDIR_BASENAME-$(DEBUG_NAME))/$(JDK_IMAGE_DIRNAME)
+REL_JDK_DEBUG_IMAGE_DIR = ../$(OUTPUTDIR_BASENAME-debug)/$(JDK_IMAGE_DIRNAME)
+REL_JDK_FASTDEBUG_IMAGE_DIR = ../$(OUTPUTDIR_BASENAME-fastdebug)/$(JDK_IMAGE_DIRNAME)
+
ifndef TOPDIR
TOPDIR:=.
endif
@@ -98,8 +126,7 @@
# Generic build of basic repo series
generic_build_repo_series:: $(SOURCE_TIPS)
- $(MKDIR) -p $(OUTPUTDIR)
- $(MKDIR) -p $(OUTPUTDIR)/j2sdk-image
+ $(MKDIR) -p $(JDK_IMAGE_DIR)
@$(call StartTimer)
ifeq ($(BUILD_LANGTOOLS), true)
@@ -146,8 +173,8 @@
#
# DEBUG_NAME is fastdebug or debug
# ALT_OUTPUTDIR is changed to have -debug or -fastdebug suffix
-# The resulting j2sdk-image is used by the install makefiles to create a
-# debug install bundle jdk-*-debug-** bundle (tar or zip)
+# The resulting image directory (j2sdk-image) is used by the install makefiles
+# to create a debug install bundle jdk-*-debug-** bundle (tar or zip)
# which will install in the debug or fastdebug subdirectory of the
# normal product install area.
# The install process needs to know what the DEBUG_NAME is, so
@@ -160,8 +187,8 @@
# Location of fresh bootdir output
ABS_BOOTDIR_OUTPUTDIR=$(ABS_OUTPUTDIR)/bootjdk
-FRESH_BOOTDIR=$(ABS_BOOTDIR_OUTPUTDIR)/j2sdk-image
-FRESH_DEBUG_BOOTDIR=$(ABS_BOOTDIR_OUTPUTDIR)/../$(PLATFORM)-$(ARCH)-$(DEBUG_NAME)/j2sdk-image
+FRESH_BOOTDIR=$(ABS_BOOTDIR_OUTPUTDIR)/$(JDK_IMAGE_DIRNAME)
+FRESH_DEBUG_BOOTDIR=$(ABS_BOOTDIR_OUTPUTDIR)/$(REL_JDK_IMAGE_DIR)
create_fresh_product_bootdir: FRC
$(MAKE) ALT_OUTPUTDIR=$(ABS_BOOTDIR_OUTPUTDIR) \
@@ -226,7 +253,7 @@
generic_debug_build:
$(MAKE) \
- ALT_OUTPUTDIR=$(ABS_OUTPUTDIR)/../$(PLATFORM)-$(ARCH)-$(DEBUG_NAME) \
+ ALT_OUTPUTDIR=$(ABS_OUTPUTDIR)/$(REL_JDK_OUTPUTDIR) \
DEBUG_NAME=$(DEBUG_NAME) \
GENERATE_DOCS=false \
$(BOOT_CYCLE_DEBUG_SETTINGS) \
@@ -254,8 +281,6 @@
clobber:: REPORT_BUILD_TIMES=
clobber::
$(RM) -r $(OUTPUTDIR)/*
- $(RM) -r $(OUTPUTDIR)/../$(PLATFORM)-$(ARCH)-debug/*
- $(RM) -r $(OUTPUTDIR)/../$(PLATFORM)-$(ARCH)-fastdebug/*
-($(RMDIR) -p $(OUTPUTDIR) > $(DEV_NULL) 2>&1; $(TRUE))
clean: clobber
@@ -489,8 +514,8 @@
# Get log file of all tests run
JDK_TO_TEST := $(shell \
- if [ -d "$(ABS_OUTPUTDIR)/j2sdk-image" ] ; then \
- $(ECHO) "$(ABS_OUTPUTDIR)/j2sdk-image"; \
+ if [ -d "$(ABS_JDK_IMAGE_DIR)" ] ; then \
+ $(ECHO) "$(ABS_JDK_IMAGE_DIR)"; \
elif [ -d "$(ABS_OUTPUTDIR)/bin" ] ; then \
$(ECHO) "$(ABS_OUTPUTDIR)"; \
elif [ "$(PRODUCT_HOME)" != "" -a -d "$(PRODUCT_HOME)/bin" ] ; then \
diff -r 9fcad86579e6 -r 0dc08749a6cc corba/.hgtags
--- a/corba/.hgtags Sat May 14 10:24:02 2011 -0700
+++ b/corba/.hgtags Wed Jul 05 17:44:49 2017 +0200
@@ -116,3 +116,4 @@
60b074ec6fcf5cdf9efce22fdfb02326ed8fa2d3 jdk7-b139
cdf5d19ec142424489549025e9c42e51f32cf688 jdk7-b140
a58635cdd921bafef353f4864184a0481353197b jdk7-b141
+a2f340a048c88d10cbedc0504f5cf03d39925a40 jdk7-b142
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/.hgtags
--- a/hotspot/.hgtags Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/.hgtags Wed Jul 05 17:44:49 2017 +0200
@@ -170,3 +170,5 @@
d283b82966712b353fa307845a1316da42a355f4 hs21-b10
5d07913abd59261c77f24cc04a759cb75d804099 jdk7-b141
3aea9e9feb073f5500e031be6186666bcae89aa2 hs21-b11
+9ad1548c6b63d596c411afc35147ffd5254426d9 jdk7-b142
+9ad1548c6b63d596c411afc35147ffd5254426d9 hs21-b12
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/make/hotspot_version
--- a/hotspot/make/hotspot_version Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/make/hotspot_version Wed Jul 05 17:44:49 2017 +0200
@@ -35,7 +35,7 @@
HS_MAJOR_VER=21
HS_MINOR_VER=0
-HS_BUILD_NUMBER=12
+HS_BUILD_NUMBER=13
JDK_MAJOR_VER=1
JDK_MINOR_VER=7
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/make/solaris/makefiles/saproc.make
--- a/hotspot/make/solaris/makefiles/saproc.make Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/make/solaris/makefiles/saproc.make Wed Jul 05 17:44:49 2017 +0200
@@ -57,24 +57,27 @@
endif
# The libproc Pstack_iter() interface changed in Nevada-B159.
-# This logic needs to match
+# Use 'uname -r -v' to determine the Solaris version as per
+# Solaris Nevada team request. This logic needs to match:
# agent/src/os/solaris/proc/saproc.cpp: set_has_newer_Pstack_iter():
# - skip SunOS 4 or older
# - skip Solaris 10 or older
-# - skip two digit Nevada builds
-# - skip three digit Nevada builds thru 149
-# - skip Nevada builds 150-158
+# - skip two digit internal Nevada builds
+# - skip three digit internal Nevada builds thru 149
+# - skip internal Nevada builds 150-158
+# - if not skipped, print define for Nevada-B159 or later
SOLARIS_11_B159_OR_LATER := \
$(shell uname -r -v \
- | sed -n ' \
- /^[0-3]\. /b \
- /^5\.[0-9] /b \
- /^5\.10 /b \
- / snv_[0-9][0-9]$/b \
- / snv_[01][0-4][0-9]$/b \
- / snv_15[0-8]$/b \
- s/.*/-DSOLARIS_11_B159_OR_LATER/p \
- ')
+ | sed -n \
+ -e '/^[0-4]\. /b' \
+ -e '/^5\.[0-9] /b' \
+ -e '/^5\.10 /b' \
+ -e '/ snv_[0-9][0-9]$/b' \
+ -e '/ snv_[01][0-4][0-9]$/b' \
+ -e '/ snv_15[0-8]$/b' \
+ -e 's/.*/-DSOLARIS_11_B159_OR_LATER/' \
+ -e 'p' \
+ )
# Uncomment the following to simulate building on Nevada-B159 or later
# when actually building on Nevada-B158 or earlier:
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp
--- a/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -2176,6 +2176,7 @@
int tempcount, // Number of slots on java expression stack in use
int popframe_extra_args,
int moncount, // Number of active monitors
+ int caller_actual_parameters,
int callee_param_size,
int callee_locals_size,
frame* caller,
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/sparc/vm/frame_sparc.cpp
--- a/hotspot/src/cpu/sparc/vm/frame_sparc.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/sparc/vm/frame_sparc.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -811,7 +811,7 @@
#ifdef ASSERT
#define DESCRIBE_FP_OFFSET(name) \
- values.describe(-1, fp() + frame::name##_offset, #name)
+ values.describe(frame_no, fp() + frame::name##_offset, #name)
void frame::describe_pd(FrameValues& values, int frame_no) {
for (int w = 0; w < frame::register_save_words; w++) {
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp
--- a/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -423,25 +423,6 @@
return true;
}
-// This method tells the deoptimizer how big an interpreted frame must be:
-int AbstractInterpreter::size_activation(methodOop method,
- int tempcount,
- int popframe_extra_args,
- int moncount,
- int callee_param_count,
- int callee_locals,
- bool is_top_frame) {
- return layout_activation(method,
- tempcount,
- popframe_extra_args,
- moncount,
- callee_param_count,
- callee_locals,
- (frame*)NULL,
- (frame*)NULL,
- is_top_frame);
-}
-
void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) {
// This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp
--- a/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -142,18 +142,8 @@
Register O2_form = O2_scratch;
Register O3_adapter = O3_scratch;
__ load_heap_oop(Address(O0_mtype, __ delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, O1_scratch)), O2_form);
- // load_heap_oop(Address(O2_form, __ delayed_value(java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes, O1_scratch)), O3_adapter);
- // deal with old JDK versions:
- __ add( Address(O2_form, __ delayed_value(java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes, O1_scratch)), O3_adapter);
- __ cmp(O3_adapter, O2_form);
- Label sorry_no_invoke_generic;
- __ brx(Assembler::lessUnsigned, false, Assembler::pn, sorry_no_invoke_generic);
- __ delayed()->nop();
-
- __ load_heap_oop(Address(O3_adapter, 0), O3_adapter);
- __ tst(O3_adapter);
- __ brx(Assembler::zero, false, Assembler::pn, sorry_no_invoke_generic);
- __ delayed()->nop();
+ __ load_heap_oop(Address(O2_form, __ delayed_value(java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes, O1_scratch)), O3_adapter);
+ __ verify_oop(O3_adapter);
__ st_ptr(O3_adapter, Address(O4_argbase, 1 * Interpreter::stackElementSize));
// As a trusted first argument, pass the type being called, so the adapter knows
// the actual types of the arguments and return values.
@@ -164,12 +154,6 @@
trace_method_handle(_masm, "invokeGeneric");
__ jump_to_method_handle_entry(G3_method_handle, O1_scratch);
- __ bind(sorry_no_invoke_generic); // no invokeGeneric implementation available!
- __ mov(O0_mtype, G5_method_type); // required by throw_WrongMethodType
- // mov(G3_method_handle, G3_method_handle); // already in this register
- __ jump_to(AddressLiteral(Interpreter::throw_WrongMethodType_entry()), O1_scratch);
- __ delayed()->nop();
-
return entry_point;
}
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp
--- a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -1623,6 +1623,7 @@
int tempcount,
int popframe_extra_args,
int moncount,
+ int caller_actual_parameters,
int callee_param_count,
int callee_local_count,
frame* caller,
@@ -1698,21 +1699,35 @@
popframe_extra_args;
int local_words = method->max_locals() * Interpreter::stackElementWords;
+ NEEDS_CLEANUP;
intptr_t* locals;
- if (caller->is_compiled_frame()) {
- // Compiled frames do not allocate a varargs area so place them
- // next to the register save area.
- locals = fp + frame::register_save_words + local_words - 1;
- // Caller wants his own SP back
- int caller_frame_size = caller->cb()->frame_size();
- *interpreter_frame->register_addr(I5_savedSP) = (intptr_t)(caller->fp() - caller_frame_size) - STACK_BIAS;
+ if (caller->is_interpreted_frame()) {
+ // Can force the locals area to end up properly overlapping the top of the expression stack.
+ intptr_t* Lesp_ptr = caller->interpreter_frame_tos_address() - 1;
+ // Note that this computation means we replace size_of_parameters() values from the caller
+ // interpreter frame's expression stack with our argument locals
+ int parm_words = caller_actual_parameters * Interpreter::stackElementWords;
+ locals = Lesp_ptr + parm_words;
+ int delta = local_words - parm_words;
+ int computed_sp_adjustment = (delta > 0) ? round_to(delta, WordsPerLong) : 0;
+ *interpreter_frame->register_addr(I5_savedSP) = (intptr_t) (fp + computed_sp_adjustment) - STACK_BIAS;
} else {
- assert(caller->is_interpreted_frame() || caller->is_entry_frame(), "only possible cases");
- // The entry and interpreter frames are laid out like normal C
- // frames so place the locals adjacent to the varargs area.
- locals = fp + frame::memory_parameter_word_sp_offset + local_words - 1;
- if (caller->is_interpreted_frame()) {
- *interpreter_frame->register_addr(I5_savedSP) = (intptr_t) (fp + rounded_cls) - STACK_BIAS;
+ assert(caller->is_compiled_frame() || caller->is_entry_frame(), "only possible cases");
+ // Don't have Lesp available; lay out locals block in the caller
+ // adjacent to the register window save area.
+ //
+ // Compiled frames do not allocate a varargs area which is why this if
+ // statement is needed.
+ //
+ if (caller->is_compiled_frame()) {
+ locals = fp + frame::register_save_words + local_words - 1;
+ } else {
+ locals = fp + frame::memory_parameter_word_sp_offset + local_words - 1;
+ }
+ if (!caller->is_entry_frame()) {
+ // Caller wants his own SP back
+ int caller_frame_size = caller->cb()->frame_size();
+ *interpreter_frame->register_addr(I5_savedSP) = (intptr_t)(caller->fp() - caller_frame_size) - STACK_BIAS;
}
}
if (TraceDeoptimization) {
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/x86/vm/assembler_x86.hpp
--- a/hotspot/src/cpu/x86/vm/assembler_x86.hpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/assembler_x86.hpp Wed Jul 05 17:44:49 2017 +0200
@@ -234,6 +234,20 @@
a._disp += disp;
return a;
}
+ Address plus_disp(RegisterOrConstant disp, ScaleFactor scale = times_1) const {
+ Address a = (*this);
+ a._disp += disp.constant_or_zero() * scale_size(scale);
+ if (disp.is_register()) {
+ assert(!a.index()->is_valid(), "competing indexes");
+ a._index = disp.as_register();
+ a._scale = scale;
+ }
+ return a;
+ }
+ bool is_same_address(Address a) const {
+ // disregard _rspec
+ return _base == a._base && _disp == a._disp && _index == a._index && _scale == a._scale;
+ }
// The following two overloads are used in connection with the
// ByteSize type (see sizes.hpp). They simplify the use of
@@ -2029,6 +2043,10 @@
void addptr(Register dst, Address src) { LP64_ONLY(addq(dst, src)) NOT_LP64(addl(dst, src)); }
void addptr(Register dst, int32_t src);
void addptr(Register dst, Register src);
+ void addptr(Register dst, RegisterOrConstant src) {
+ if (src.is_constant()) addptr(dst, (int) src.as_constant());
+ else addptr(dst, src.as_register());
+ }
void andptr(Register dst, int32_t src);
void andptr(Register src1, Register src2) { LP64_ONLY(andq(src1, src2)) NOT_LP64(andl(src1, src2)) ; }
@@ -2090,7 +2108,10 @@
void subptr(Register dst, Address src) { LP64_ONLY(subq(dst, src)) NOT_LP64(subl(dst, src)); }
void subptr(Register dst, int32_t src);
void subptr(Register dst, Register src);
-
+ void subptr(Register dst, RegisterOrConstant src) {
+ if (src.is_constant()) subptr(dst, (int) src.as_constant());
+ else subptr(dst, src.as_register());
+ }
void sbbptr(Address dst, int32_t src) { LP64_ONLY(sbbq(dst, src)) NOT_LP64(sbbl(dst, src)); }
void sbbptr(Register dst, int32_t src) { LP64_ONLY(sbbq(dst, src)) NOT_LP64(sbbl(dst, src)); }
@@ -2288,6 +2309,11 @@
void movptr(Address dst, Register src);
+ void movptr(Register dst, RegisterOrConstant src) {
+ if (src.is_constant()) movptr(dst, src.as_constant());
+ else movptr(dst, src.as_register());
+ }
+
#ifdef _LP64
// Generally the next two are only used for moving NULL
// Although there are situations in initializing the mark word where
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp
--- a/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -2339,14 +2339,15 @@
}
int AbstractInterpreter::layout_activation(methodOop method,
- int tempcount, //
- int popframe_extra_args,
- int moncount,
- int callee_param_count,
- int callee_locals,
- frame* caller,
- frame* interpreter_frame,
- bool is_top_frame) {
+ int tempcount, //
+ int popframe_extra_args,
+ int moncount,
+ int caller_actual_parameters,
+ int callee_param_count,
+ int callee_locals,
+ frame* caller,
+ frame* interpreter_frame,
+ bool is_top_frame) {
assert(popframe_extra_args == 0, "FIX ME");
// NOTE this code must exactly mimic what InterpreterGenerator::generate_compute_interpreter_state()
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/x86/vm/frame_x86.cpp
--- a/hotspot/src/cpu/x86/vm/frame_x86.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/frame_x86.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -339,7 +339,6 @@
return fr;
}
-
//------------------------------------------------------------------------------
// frame::verify_deopt_original_pc
//
@@ -361,6 +360,55 @@
}
#endif
+//------------------------------------------------------------------------------
+// frame::adjust_unextended_sp
+void frame::adjust_unextended_sp() {
+ // If we are returning to a compiled MethodHandle call site, the
+ // saved_fp will in fact be a saved value of the unextended SP. The
+ // simplest way to tell whether we are returning to such a call site
+ // is as follows:
+
+ nmethod* sender_nm = (_cb == NULL) ? NULL : _cb->as_nmethod_or_null();
+ if (sender_nm != NULL) {
+ // If the sender PC is a deoptimization point, get the original
+ // PC. For MethodHandle call site the unextended_sp is stored in
+ // saved_fp.
+ if (sender_nm->is_deopt_mh_entry(_pc)) {
+ DEBUG_ONLY(verify_deopt_mh_original_pc(sender_nm, _fp));
+ _unextended_sp = _fp;
+ }
+ else if (sender_nm->is_deopt_entry(_pc)) {
+ DEBUG_ONLY(verify_deopt_original_pc(sender_nm, _unextended_sp));
+ }
+ else if (sender_nm->is_method_handle_return(_pc)) {
+ _unextended_sp = _fp;
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+// frame::update_map_with_saved_link
+void frame::update_map_with_saved_link(RegisterMap* map, intptr_t** link_addr) {
+ // The interpreter and compiler(s) always save EBP/RBP in a known
+ // location on entry. We must record where that location is
+ // so this if EBP/RBP was live on callout from c2 we can find
+ // the saved copy no matter what it called.
+
+ // Since the interpreter always saves EBP/RBP if we record where it is then
+ // we don't have to always save EBP/RBP on entry and exit to c2 compiled
+ // code, on entry will be enough.
+ map->set_location(rbp->as_VMReg(), (address) link_addr);
+#ifdef AMD64
+ // this is weird "H" ought to be at a higher address however the
+ // oopMaps seems to have the "H" regs at the same address and the
+ // vanilla register.
+ // XXXX make this go away
+ if (true) {
+ map->set_location(rbp->as_VMReg()->next(), (address) link_addr);
+ }
+#endif // AMD64
+}
+
//------------------------------------------------------------------------------
// frame::sender_for_interpreter_frame
@@ -372,54 +420,13 @@
// This is the sp before any possible extension (adapter/locals).
intptr_t* unextended_sp = interpreter_frame_sender_sp();
- // Stored FP.
- intptr_t* saved_fp = link();
-
- address sender_pc = this->sender_pc();
- CodeBlob* sender_cb = CodeCache::find_blob_unsafe(sender_pc);
- assert(sender_cb, "sanity");
- nmethod* sender_nm = sender_cb->as_nmethod_or_null();
-
- if (sender_nm != NULL) {
- // If the sender PC is a deoptimization point, get the original
- // PC. For MethodHandle call site the unextended_sp is stored in
- // saved_fp.
- if (sender_nm->is_deopt_mh_entry(sender_pc)) {
- DEBUG_ONLY(verify_deopt_mh_original_pc(sender_nm, saved_fp));
- unextended_sp = saved_fp;
- }
- else if (sender_nm->is_deopt_entry(sender_pc)) {
- DEBUG_ONLY(verify_deopt_original_pc(sender_nm, unextended_sp));
- }
- else if (sender_nm->is_method_handle_return(sender_pc)) {
- unextended_sp = saved_fp;
- }
- }
-
- // The interpreter and compiler(s) always save EBP/RBP in a known
- // location on entry. We must record where that location is
- // so this if EBP/RBP was live on callout from c2 we can find
- // the saved copy no matter what it called.
-
- // Since the interpreter always saves EBP/RBP if we record where it is then
- // we don't have to always save EBP/RBP on entry and exit to c2 compiled
- // code, on entry will be enough.
#ifdef COMPILER2
if (map->update_map()) {
- map->set_location(rbp->as_VMReg(), (address) addr_at(link_offset));
-#ifdef AMD64
- // this is weird "H" ought to be at a higher address however the
- // oopMaps seems to have the "H" regs at the same address and the
- // vanilla register.
- // XXXX make this go away
- if (true) {
- map->set_location(rbp->as_VMReg()->next(), (address)addr_at(link_offset));
- }
-#endif // AMD64
+ update_map_with_saved_link(map, (intptr_t**) addr_at(link_offset));
}
#endif // COMPILER2
- return frame(sender_sp, unextended_sp, saved_fp, sender_pc);
+ return frame(sender_sp, unextended_sp, link(), sender_pc());
}
@@ -427,6 +434,7 @@
// frame::sender_for_compiled_frame
frame frame::sender_for_compiled_frame(RegisterMap* map) const {
assert(map != NULL, "map must be set");
+ assert(!is_ricochet_frame(), "caller must handle this");
// frame owned by optimizing compiler
assert(_cb->frame_size() >= 0, "must have non-zero frame size");
@@ -438,31 +446,7 @@
// This is the saved value of EBP which may or may not really be an FP.
// It is only an FP if the sender is an interpreter frame (or C1?).
- intptr_t* saved_fp = (intptr_t*) *(sender_sp - frame::sender_sp_offset);
-
- // If we are returning to a compiled MethodHandle call site, the
- // saved_fp will in fact be a saved value of the unextended SP. The
- // simplest way to tell whether we are returning to such a call site
- // is as follows:
- CodeBlob* sender_cb = CodeCache::find_blob_unsafe(sender_pc);
- assert(sender_cb, "sanity");
- nmethod* sender_nm = sender_cb->as_nmethod_or_null();
-
- if (sender_nm != NULL) {
- // If the sender PC is a deoptimization point, get the original
- // PC. For MethodHandle call site the unextended_sp is stored in
- // saved_fp.
- if (sender_nm->is_deopt_mh_entry(sender_pc)) {
- DEBUG_ONLY(verify_deopt_mh_original_pc(sender_nm, saved_fp));
- unextended_sp = saved_fp;
- }
- else if (sender_nm->is_deopt_entry(sender_pc)) {
- DEBUG_ONLY(verify_deopt_original_pc(sender_nm, unextended_sp));
- }
- else if (sender_nm->is_method_handle_return(sender_pc)) {
- unextended_sp = saved_fp;
- }
- }
+ intptr_t** saved_fp_addr = (intptr_t**) (sender_sp - frame::sender_sp_offset);
if (map->update_map()) {
// Tell GC to use argument oopmaps for some runtime stubs that need it.
@@ -472,23 +456,15 @@
if (_cb->oop_maps() != NULL) {
OopMapSet::update_register_map(this, map);
}
+
// Since the prolog does the save and restore of EBP there is no oopmap
// for it so we must fill in its location as if there was an oopmap entry
// since if our caller was compiled code there could be live jvm state in it.
- map->set_location(rbp->as_VMReg(), (address) (sender_sp - frame::sender_sp_offset));
-#ifdef AMD64
- // this is weird "H" ought to be at a higher address however the
- // oopMaps seems to have the "H" regs at the same address and the
- // vanilla register.
- // XXXX make this go away
- if (true) {
- map->set_location(rbp->as_VMReg()->next(), (address) (sender_sp - frame::sender_sp_offset));
- }
-#endif // AMD64
+ update_map_with_saved_link(map, saved_fp_addr);
}
assert(sender_sp != sp(), "must have changed");
- return frame(sender_sp, unextended_sp, saved_fp, sender_pc);
+ return frame(sender_sp, unextended_sp, *saved_fp_addr, sender_pc);
}
@@ -502,6 +478,7 @@
if (is_entry_frame()) return sender_for_entry_frame(map);
if (is_interpreted_frame()) return sender_for_interpreter_frame(map);
assert(_cb == CodeCache::find_blob(pc()),"Must be the same");
+ if (is_ricochet_frame()) return sender_for_ricochet_frame(map);
if (_cb != NULL) {
return sender_for_compiled_frame(map);
@@ -673,7 +650,7 @@
#ifdef ASSERT
#define DESCRIBE_FP_OFFSET(name) \
- values.describe(-1, fp() + frame::name##_offset, #name)
+ values.describe(frame_no, fp() + frame::name##_offset, #name)
void frame::describe_pd(FrameValues& values, int frame_no) {
if (is_interpreted_frame()) {
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/x86/vm/frame_x86.hpp
--- a/hotspot/src/cpu/x86/vm/frame_x86.hpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/frame_x86.hpp Wed Jul 05 17:44:49 2017 +0200
@@ -164,6 +164,7 @@
// original sp we use that convention.
intptr_t* _unextended_sp;
+ void adjust_unextended_sp();
intptr_t* ptr_at_addr(int offset) const {
return (intptr_t*) addr_at(offset);
@@ -197,6 +198,9 @@
// expression stack tos if we are nested in a java call
intptr_t* interpreter_frame_last_sp() const;
+ // helper to update a map with callee-saved RBP
+ static void update_map_with_saved_link(RegisterMap* map, intptr_t** link_addr);
+
#ifndef CC_INTERP
// deoptimization support
void interpreter_frame_set_last_sp(intptr_t* sp);
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/x86/vm/frame_x86.inline.hpp
--- a/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp Wed Jul 05 17:44:49 2017 +0200
@@ -62,6 +62,7 @@
_pc = pc;
assert(pc != NULL, "no pc?");
_cb = CodeCache::find_blob(pc);
+ adjust_unextended_sp();
address original_pc = nmethod::get_deopt_original_pc(this);
if (original_pc != NULL) {
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/x86/vm/interpreter_x86.hpp
--- a/hotspot/src/cpu/x86/vm/interpreter_x86.hpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/interpreter_x86.hpp Wed Jul 05 17:44:49 2017 +0200
@@ -26,7 +26,9 @@
#define CPU_X86_VM_INTERPRETER_X86_HPP
public:
- static Address::ScaleFactor stackElementScale() { return Address::times_4; }
+ static Address::ScaleFactor stackElementScale() {
+ return NOT_LP64(Address::times_4) LP64_ONLY(Address::times_8);
+ }
// Offset from rsp (which points to the last stack element)
static int expr_offset_in_bytes(int i) { return stackElementSize * i; }
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp
--- a/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -242,26 +242,6 @@
return entry_point;
}
-
-// This method tells the deoptimizer how big an interpreted frame must be:
-int AbstractInterpreter::size_activation(methodOop method,
- int tempcount,
- int popframe_extra_args,
- int moncount,
- int callee_param_count,
- int callee_locals,
- bool is_top_frame) {
- return layout_activation(method,
- tempcount,
- popframe_extra_args,
- moncount,
- callee_param_count,
- callee_locals,
- (frame*) NULL,
- (frame*) NULL,
- is_top_frame);
-}
-
void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) {
// This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp
--- a/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -362,20 +362,6 @@
}
-// This method tells the deoptimizer how big an interpreted frame must be:
-int AbstractInterpreter::size_activation(methodOop method,
- int tempcount,
- int popframe_extra_args,
- int moncount,
- int callee_param_count,
- int callee_locals,
- bool is_top_frame) {
- return layout_activation(method,
- tempcount, popframe_extra_args, moncount,
- callee_param_count, callee_locals,
- (frame*) NULL, (frame*) NULL, is_top_frame);
-}
-
void Deoptimization::unwind_callee_save_values(frame* f, vframeArray* vframe_array) {
// This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/x86/vm/methodHandles_x86.cpp
--- a/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -69,23 +69,475 @@
return me;
}
+// stack walking support
+
+frame MethodHandles::ricochet_frame_sender(const frame& fr, RegisterMap *map) {
+ RicochetFrame* f = RicochetFrame::from_frame(fr);
+ if (map->update_map())
+ frame::update_map_with_saved_link(map, &f->_sender_link);
+ return frame(f->extended_sender_sp(), f->exact_sender_sp(), f->sender_link(), f->sender_pc());
+}
+
+void MethodHandles::ricochet_frame_oops_do(const frame& fr, OopClosure* blk, const RegisterMap* reg_map) {
+ RicochetFrame* f = RicochetFrame::from_frame(fr);
+
+ // pick up the argument type descriptor:
+ Thread* thread = Thread::current();
+ Handle cookie(thread, f->compute_saved_args_layout(true, true));
+
+ // process fixed part
+ blk->do_oop((oop*)f->saved_target_addr());
+ blk->do_oop((oop*)f->saved_args_layout_addr());
+
+ // process variable arguments:
+ if (cookie.is_null()) return; // no arguments to describe
+
+ // the cookie is actually the invokeExact method for my target
+ // his argument signature is what I'm interested in
+ assert(cookie->is_method(), "");
+ methodHandle invoker(thread, methodOop(cookie()));
+ assert(invoker->name() == vmSymbols::invokeExact_name(), "must be this kind of method");
+ assert(!invoker->is_static(), "must have MH argument");
+ int slot_count = invoker->size_of_parameters();
+ assert(slot_count >= 1, "must include 'this'");
+ intptr_t* base = f->saved_args_base();
+ intptr_t* retval = NULL;
+ if (f->has_return_value_slot())
+ retval = f->return_value_slot_addr();
+ int slot_num = slot_count;
+ intptr_t* loc = &base[slot_num -= 1];
+ //blk->do_oop((oop*) loc); // original target, which is irrelevant
+ int arg_num = 0;
+ for (SignatureStream ss(invoker->signature()); !ss.is_done(); ss.next()) {
+ if (ss.at_return_type()) continue;
+ BasicType ptype = ss.type();
+ if (ptype == T_ARRAY) ptype = T_OBJECT; // fold all refs to T_OBJECT
+ assert(ptype >= T_BOOLEAN && ptype <= T_OBJECT, "not array or void");
+ loc = &base[slot_num -= type2size[ptype]];
+ bool is_oop = (ptype == T_OBJECT && loc != retval);
+ if (is_oop) blk->do_oop((oop*)loc);
+ arg_num += 1;
+ }
+ assert(slot_num == 0, "must have processed all the arguments");
+}
+
+oop MethodHandles::RicochetFrame::compute_saved_args_layout(bool read_cache, bool write_cache) {
+ oop cookie = NULL;
+ if (read_cache) {
+ cookie = saved_args_layout();
+ if (cookie != NULL) return cookie;
+ }
+ oop target = saved_target();
+ oop mtype = java_lang_invoke_MethodHandle::type(target);
+ oop mtform = java_lang_invoke_MethodType::form(mtype);
+ cookie = java_lang_invoke_MethodTypeForm::vmlayout(mtform);
+ if (write_cache) {
+ (*saved_args_layout_addr()) = cookie;
+ }
+ return cookie;
+}
+
+void MethodHandles::RicochetFrame::generate_ricochet_blob(MacroAssembler* _masm,
+ // output params:
+ int* frame_size_in_words,
+ int* bounce_offset,
+ int* exception_offset) {
+ (*frame_size_in_words) = RicochetFrame::frame_size_in_bytes() / wordSize;
+
+ address start = __ pc();
+
#ifdef ASSERT
-static void verify_argslot(MacroAssembler* _masm, Register argslot_reg,
- const char* error_message) {
+ __ hlt(); __ hlt(); __ hlt();
+ // here's a hint of something special:
+ __ push(MAGIC_NUMBER_1);
+ __ push(MAGIC_NUMBER_2);
+#endif //ASSERT
+ __ hlt(); // not reached
+
+ // A return PC has just been popped from the stack.
+ // Return values are in registers.
+ // The ebp points into the RicochetFrame, which contains
+ // a cleanup continuation we must return to.
+
+ (*bounce_offset) = __ pc() - start;
+ BLOCK_COMMENT("ricochet_blob.bounce");
+
+ if (VerifyMethodHandles) RicochetFrame::verify_clean(_masm);
+ trace_method_handle(_masm, "return/ricochet_blob.bounce");
+
+ __ jmp(frame_address(continuation_offset_in_bytes()));
+ __ hlt();
+ DEBUG_ONLY(__ push(MAGIC_NUMBER_2));
+
+ (*exception_offset) = __ pc() - start;
+ BLOCK_COMMENT("ricochet_blob.exception");
+
+ // compare this to Interpreter::rethrow_exception_entry, which is parallel code
+ // for example, see TemplateInterpreterGenerator::generate_throw_exception
+ // Live registers in:
+ // rax: exception
+ // rdx: return address/pc that threw exception (ignored, always equal to bounce addr)
+ __ verify_oop(rax);
+
+ // no need to empty_FPU_stack or reinit_heapbase, since caller frame will do the same if needed
+
+ // Take down the frame.
+
+ // Cf. InterpreterMacroAssembler::remove_activation.
+ leave_ricochet_frame(_masm, /*rcx_recv=*/ noreg,
+ saved_last_sp_register(),
+ /*sender_pc_reg=*/ rdx);
+
+ // In between activations - previous activation type unknown yet
+ // compute continuation point - the continuation point expects the
+ // following registers set up:
+ //
+ // rax: exception
+ // rdx: return address/pc that threw exception
+ // rsp: expression stack of caller
+ // rbp: ebp of caller
+ __ push(rax); // save exception
+ __ push(rdx); // save return address
+ Register thread_reg = LP64_ONLY(r15_thread) NOT_LP64(rdi);
+ NOT_LP64(__ get_thread(thread_reg));
+ __ call_VM_leaf(CAST_FROM_FN_PTR(address,
+ SharedRuntime::exception_handler_for_return_address),
+ thread_reg, rdx);
+ __ mov(rbx, rax); // save exception handler
+ __ pop(rdx); // restore return address
+ __ pop(rax); // restore exception
+ __ jmp(rbx); // jump to exception
+ // handler of caller
+}
+
+void MethodHandles::RicochetFrame::enter_ricochet_frame(MacroAssembler* _masm,
+ Register rcx_recv,
+ Register rax_argv,
+ address return_handler,
+ Register rbx_temp) {
+ const Register saved_last_sp = saved_last_sp_register();
+ Address rcx_mh_vmtarget( rcx_recv, java_lang_invoke_MethodHandle::vmtarget_offset_in_bytes() );
+ Address rcx_amh_conversion( rcx_recv, java_lang_invoke_AdapterMethodHandle::conversion_offset_in_bytes() );
+
+ // Push the RicochetFrame a word at a time.
+ // This creates something similar to an interpreter frame.
+ // Cf. TemplateInterpreterGenerator::generate_fixed_frame.
+ BLOCK_COMMENT("push RicochetFrame {");
+ DEBUG_ONLY(int rfo = (int) sizeof(RicochetFrame));
+ assert((rfo -= wordSize) == RicochetFrame::sender_pc_offset_in_bytes(), "");
+#define RF_FIELD(push_value, name) \
+ { push_value; \
+ assert((rfo -= wordSize) == RicochetFrame::name##_offset_in_bytes(), ""); }
+ RF_FIELD(__ push(rbp), sender_link);
+ RF_FIELD(__ push(saved_last_sp), exact_sender_sp); // rsi/r13
+ RF_FIELD(__ pushptr(rcx_amh_conversion), conversion);
+ RF_FIELD(__ push(rax_argv), saved_args_base); // can be updated if args are shifted
+ RF_FIELD(__ push((int32_t) NULL_WORD), saved_args_layout); // cache for GC layout cookie
+ if (UseCompressedOops) {
+ __ load_heap_oop(rbx_temp, rcx_mh_vmtarget);
+ RF_FIELD(__ push(rbx_temp), saved_target);
+ } else {
+ RF_FIELD(__ pushptr(rcx_mh_vmtarget), saved_target);
+ }
+ __ lea(rbx_temp, ExternalAddress(return_handler));
+ RF_FIELD(__ push(rbx_temp), continuation);
+#undef RF_FIELD
+ assert(rfo == 0, "fully initialized the RicochetFrame");
+ // compute new frame pointer:
+ __ lea(rbp, Address(rsp, RicochetFrame::sender_link_offset_in_bytes()));
+ // Push guard word #1 in debug mode.
+ DEBUG_ONLY(__ push((int32_t) RicochetFrame::MAGIC_NUMBER_1));
+ // For debugging, leave behind an indication of which stub built this frame.
+ DEBUG_ONLY({ Label L; __ call(L, relocInfo::none); __ bind(L); });
+ BLOCK_COMMENT("} RicochetFrame");
+}
+
+void MethodHandles::RicochetFrame::leave_ricochet_frame(MacroAssembler* _masm,
+ Register rcx_recv,
+ Register new_sp_reg,
+ Register sender_pc_reg) {
+ assert_different_registers(rcx_recv, new_sp_reg, sender_pc_reg);
+ const Register saved_last_sp = saved_last_sp_register();
+ // Take down the frame.
+ // Cf. InterpreterMacroAssembler::remove_activation.
+ BLOCK_COMMENT("end_ricochet_frame {");
+ // TO DO: If (exact_sender_sp - extended_sender_sp) > THRESH, compact the frame down.
+ // This will keep stack in bounds even with unlimited tailcalls, each with an adapter.
+ if (rcx_recv->is_valid())
+ __ movptr(rcx_recv, RicochetFrame::frame_address(RicochetFrame::saved_target_offset_in_bytes()));
+ __ movptr(sender_pc_reg, RicochetFrame::frame_address(RicochetFrame::sender_pc_offset_in_bytes()));
+ __ movptr(saved_last_sp, RicochetFrame::frame_address(RicochetFrame::exact_sender_sp_offset_in_bytes()));
+ __ movptr(rbp, RicochetFrame::frame_address(RicochetFrame::sender_link_offset_in_bytes()));
+ __ mov(rsp, new_sp_reg);
+ BLOCK_COMMENT("} end_ricochet_frame");
+}
+
+// Emit code to verify that RBP is pointing at a valid ricochet frame.
+#ifdef ASSERT
+enum {
+ ARG_LIMIT = 255, SLOP = 4,
+ // use this parameter for checking for garbage stack movements:
+ UNREASONABLE_STACK_MOVE = (ARG_LIMIT + SLOP)
+ // the slop defends against false alarms due to fencepost errors
+};
+
+void MethodHandles::RicochetFrame::verify_clean(MacroAssembler* _masm) {
+ // The stack should look like this:
+ // ... keep1 | dest=42 | keep2 | RF | magic | handler | magic | recursive args |
+ // Check various invariants.
+ verify_offsets();
+
+ Register rdi_temp = rdi;
+ Register rcx_temp = rcx;
+ { __ push(rdi_temp); __ push(rcx_temp); }
+#define UNPUSH_TEMPS \
+ { __ pop(rcx_temp); __ pop(rdi_temp); }
+
+ Address magic_number_1_addr = RicochetFrame::frame_address(RicochetFrame::magic_number_1_offset_in_bytes());
+ Address magic_number_2_addr = RicochetFrame::frame_address(RicochetFrame::magic_number_2_offset_in_bytes());
+ Address continuation_addr = RicochetFrame::frame_address(RicochetFrame::continuation_offset_in_bytes());
+ Address conversion_addr = RicochetFrame::frame_address(RicochetFrame::conversion_offset_in_bytes());
+ Address saved_args_base_addr = RicochetFrame::frame_address(RicochetFrame::saved_args_base_offset_in_bytes());
+
+ Label L_bad, L_ok;
+ BLOCK_COMMENT("verify_clean {");
+ // Magic numbers must check out:
+ __ cmpptr(magic_number_1_addr, (int32_t) MAGIC_NUMBER_1);
+ __ jcc(Assembler::notEqual, L_bad);
+ __ cmpptr(magic_number_2_addr, (int32_t) MAGIC_NUMBER_2);
+ __ jcc(Assembler::notEqual, L_bad);
+
+ // Arguments pointer must look reasonable:
+ __ movptr(rcx_temp, saved_args_base_addr);
+ __ cmpptr(rcx_temp, rbp);
+ __ jcc(Assembler::below, L_bad);
+ __ subptr(rcx_temp, UNREASONABLE_STACK_MOVE * Interpreter::stackElementSize);
+ __ cmpptr(rcx_temp, rbp);
+ __ jcc(Assembler::above, L_bad);
+
+ load_conversion_dest_type(_masm, rdi_temp, conversion_addr);
+ __ cmpl(rdi_temp, T_VOID);
+ __ jcc(Assembler::equal, L_ok);
+ __ movptr(rcx_temp, saved_args_base_addr);
+ load_conversion_vminfo(_masm, rdi_temp, conversion_addr);
+ __ cmpptr(Address(rcx_temp, rdi_temp, Interpreter::stackElementScale()),
+ (int32_t) RETURN_VALUE_PLACEHOLDER);
+ __ jcc(Assembler::equal, L_ok);
+ __ BIND(L_bad);
+ UNPUSH_TEMPS;
+ __ stop("damaged ricochet frame");
+ __ BIND(L_ok);
+ UNPUSH_TEMPS;
+ BLOCK_COMMENT("} verify_clean");
+
+#undef UNPUSH_TEMPS
+
+}
+#endif //ASSERT
+
+void MethodHandles::load_klass_from_Class(MacroAssembler* _masm, Register klass_reg) {
+ if (VerifyMethodHandles)
+ verify_klass(_masm, klass_reg, SystemDictionaryHandles::Class_klass(),
+ "AMH argument is a Class");
+ __ load_heap_oop(klass_reg, Address(klass_reg, java_lang_Class::klass_offset_in_bytes()));
+}
+
+void MethodHandles::load_conversion_vminfo(MacroAssembler* _masm, Register reg, Address conversion_field_addr) {
+ int bits = BitsPerByte;
+ int offset = (CONV_VMINFO_SHIFT / bits);
+ int shift = (CONV_VMINFO_SHIFT % bits);
+ __ load_unsigned_byte(reg, conversion_field_addr.plus_disp(offset));
+ assert(CONV_VMINFO_MASK == right_n_bits(bits - shift), "else change type of previous load");
+ assert(shift == 0, "no shift needed");
+}
+
+void MethodHandles::load_conversion_dest_type(MacroAssembler* _masm, Register reg, Address conversion_field_addr) {
+ int bits = BitsPerByte;
+ int offset = (CONV_DEST_TYPE_SHIFT / bits);
+ int shift = (CONV_DEST_TYPE_SHIFT % bits);
+ __ load_unsigned_byte(reg, conversion_field_addr.plus_disp(offset));
+ assert(CONV_TYPE_MASK == right_n_bits(bits - shift), "else change type of previous load");
+ __ shrl(reg, shift);
+ DEBUG_ONLY(int conv_type_bits = (int) exact_log2(CONV_TYPE_MASK+1));
+ assert((shift + conv_type_bits) == bits, "left justified in byte");
+}
+
+void MethodHandles::load_stack_move(MacroAssembler* _masm,
+ Register rdi_stack_move,
+ Register rcx_amh,
+ bool might_be_negative) {
+ BLOCK_COMMENT("load_stack_move");
+ Address rcx_amh_conversion(rcx_amh, java_lang_invoke_AdapterMethodHandle::conversion_offset_in_bytes());
+ __ movl(rdi_stack_move, rcx_amh_conversion);
+ __ sarl(rdi_stack_move, CONV_STACK_MOVE_SHIFT);
+#ifdef _LP64
+ if (might_be_negative) {
+ // clean high bits of stack motion register (was loaded as an int)
+ __ movslq(rdi_stack_move, rdi_stack_move);
+ }
+#endif //_LP64
+ if (VerifyMethodHandles) {
+ Label L_ok, L_bad;
+ int32_t stack_move_limit = 0x4000; // extra-large
+ __ cmpptr(rdi_stack_move, stack_move_limit);
+ __ jcc(Assembler::greaterEqual, L_bad);
+ __ cmpptr(rdi_stack_move, -stack_move_limit);
+ __ jcc(Assembler::greater, L_ok);
+ __ bind(L_bad);
+ __ stop("load_stack_move of garbage value");
+ __ BIND(L_ok);
+ }
+}
+
+#ifndef PRODUCT
+void MethodHandles::RicochetFrame::verify_offsets() {
+ // Check compatibility of this struct with the more generally used offsets of class frame:
+ int ebp_off = sender_link_offset_in_bytes(); // offset from struct base to local rbp value
+ assert(ebp_off + wordSize*frame::interpreter_frame_method_offset == saved_args_base_offset_in_bytes(), "");
+ assert(ebp_off + wordSize*frame::interpreter_frame_last_sp_offset == conversion_offset_in_bytes(), "");
+ assert(ebp_off + wordSize*frame::interpreter_frame_sender_sp_offset == exact_sender_sp_offset_in_bytes(), "");
+ // These last two have to be exact:
+ assert(ebp_off + wordSize*frame::link_offset == sender_link_offset_in_bytes(), "");
+ assert(ebp_off + wordSize*frame::return_addr_offset == sender_pc_offset_in_bytes(), "");
+}
+
+void MethodHandles::RicochetFrame::verify() const {
+ verify_offsets();
+ assert(magic_number_1() == MAGIC_NUMBER_1, "");
+ assert(magic_number_2() == MAGIC_NUMBER_2, "");
+ if (!Universe::heap()->is_gc_active()) {
+ if (saved_args_layout() != NULL) {
+ assert(saved_args_layout()->is_method(), "must be valid oop");
+ }
+ if (saved_target() != NULL) {
+ assert(java_lang_invoke_MethodHandle::is_instance(saved_target()), "checking frame value");
+ }
+ }
+ int conv_op = adapter_conversion_op(conversion());
+ assert(conv_op == java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS ||
+ conv_op == java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS ||
+ conv_op == java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_REF,
+ "must be a sane conversion");
+ if (has_return_value_slot()) {
+ assert(*return_value_slot_addr() == RETURN_VALUE_PLACEHOLDER, "");
+ }
+}
+#endif //PRODUCT
+
+#ifdef ASSERT
+void MethodHandles::verify_argslot(MacroAssembler* _masm,
+ Register argslot_reg,
+ const char* error_message) {
// Verify that argslot lies within (rsp, rbp].
Label L_ok, L_bad;
- BLOCK_COMMENT("{ verify_argslot");
+ BLOCK_COMMENT("verify_argslot {");
__ cmpptr(argslot_reg, rbp);
__ jccb(Assembler::above, L_bad);
__ cmpptr(rsp, argslot_reg);
__ jccb(Assembler::below, L_ok);
__ bind(L_bad);
__ stop(error_message);
- __ bind(L_ok);
+ __ BIND(L_ok);
BLOCK_COMMENT("} verify_argslot");
}
-#endif
+
+void MethodHandles::verify_argslots(MacroAssembler* _masm,
+ RegisterOrConstant arg_slots,
+ Register arg_slot_base_reg,
+ bool negate_argslots,
+ const char* error_message) {
+ // Verify that [argslot..argslot+size) lies within (rsp, rbp).
+ Label L_ok, L_bad;
+ Register rdi_temp = rdi;
+ BLOCK_COMMENT("verify_argslots {");
+ __ push(rdi_temp);
+ if (negate_argslots) {
+ if (arg_slots.is_constant()) {
+ arg_slots = -1 * arg_slots.as_constant();
+ } else {
+ __ movptr(rdi_temp, arg_slots);
+ __ negptr(rdi_temp);
+ arg_slots = rdi_temp;
+ }
+ }
+ __ lea(rdi_temp, Address(arg_slot_base_reg, arg_slots, Interpreter::stackElementScale()));
+ __ cmpptr(rdi_temp, rbp);
+ __ pop(rdi_temp);
+ __ jcc(Assembler::above, L_bad);
+ __ cmpptr(rsp, arg_slot_base_reg);
+ __ jcc(Assembler::below, L_ok);
+ __ bind(L_bad);
+ __ stop(error_message);
+ __ BIND(L_ok);
+ BLOCK_COMMENT("} verify_argslots");
+}
+// Make sure that arg_slots has the same sign as the given direction.
+// If (and only if) arg_slots is a assembly-time constant, also allow it to be zero.
+void MethodHandles::verify_stack_move(MacroAssembler* _masm,
+ RegisterOrConstant arg_slots, int direction) {
+ bool allow_zero = arg_slots.is_constant();
+ if (direction == 0) { direction = +1; allow_zero = true; }
+ assert(stack_move_unit() == -1, "else add extra checks here");
+ if (arg_slots.is_register()) {
+ Label L_ok, L_bad;
+ BLOCK_COMMENT("verify_stack_move {");
+ // testl(arg_slots.as_register(), -stack_move_unit() - 1); // no need
+ // jcc(Assembler::notZero, L_bad);
+ __ cmpptr(arg_slots.as_register(), (int32_t) NULL_WORD);
+ if (direction > 0) {
+ __ jcc(allow_zero ? Assembler::less : Assembler::lessEqual, L_bad);
+ __ cmpptr(arg_slots.as_register(), (int32_t) UNREASONABLE_STACK_MOVE);
+ __ jcc(Assembler::less, L_ok);
+ } else {
+ __ jcc(allow_zero ? Assembler::greater : Assembler::greaterEqual, L_bad);
+ __ cmpptr(arg_slots.as_register(), (int32_t) -UNREASONABLE_STACK_MOVE);
+ __ jcc(Assembler::greater, L_ok);
+ }
+ __ bind(L_bad);
+ if (direction > 0)
+ __ stop("assert arg_slots > 0");
+ else
+ __ stop("assert arg_slots < 0");
+ __ BIND(L_ok);
+ BLOCK_COMMENT("} verify_stack_move");
+ } else {
+ intptr_t size = arg_slots.as_constant();
+ if (direction < 0) size = -size;
+ assert(size >= 0, "correct direction of constant move");
+ assert(size < UNREASONABLE_STACK_MOVE, "reasonable size of constant move");
+ }
+}
+
+void MethodHandles::verify_klass(MacroAssembler* _masm,
+ Register obj, KlassHandle klass,
+ const char* error_message) {
+ oop* klass_addr = klass.raw_value();
+ assert(klass_addr >= SystemDictionaryHandles::Object_klass().raw_value() &&
+ klass_addr <= SystemDictionaryHandles::Long_klass().raw_value(),
+ "must be one of the SystemDictionaryHandles");
+ Register temp = rdi;
+ Label L_ok, L_bad;
+ BLOCK_COMMENT("verify_klass {");
+ __ verify_oop(obj);
+ __ testptr(obj, obj);
+ __ jcc(Assembler::zero, L_bad);
+ __ push(temp);
+ __ load_klass(temp, obj);
+ __ cmpptr(temp, ExternalAddress((address) klass_addr));
+ __ jcc(Assembler::equal, L_ok);
+ intptr_t super_check_offset = klass->super_check_offset();
+ __ movptr(temp, Address(temp, super_check_offset));
+ __ cmpptr(temp, ExternalAddress((address) klass_addr));
+ __ jcc(Assembler::equal, L_ok);
+ __ pop(temp);
+ __ bind(L_bad);
+ __ stop(error_message);
+ __ BIND(L_ok);
+ __ pop(temp);
+ BLOCK_COMMENT("} verify_klass");
+}
+#endif //ASSERT
// Code generation
address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* _masm) {
@@ -116,6 +568,9 @@
address entry_point = __ pc();
// fetch the MethodType from the method handle into rax (the 'check' register)
+ // FIXME: Interpreter should transmit pre-popped stack pointer, to locate base of arg list.
+ // This would simplify several touchy bits of code.
+ // See 6984712: JSR 292 method handle calls need a clean argument base pointer
{
Register tem = rbx_method;
for (jint* pchase = methodOopDesc::method_type_offsets_chain(); (*pchase) != -1; pchase++) {
@@ -128,17 +583,23 @@
__ load_heap_oop(rdx_temp, Address(rax_mtype, __ delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, rdi_temp)));
Register rdx_vmslots = rdx_temp;
__ movl(rdx_vmslots, Address(rdx_temp, __ delayed_value(java_lang_invoke_MethodTypeForm::vmslots_offset_in_bytes, rdi_temp)));
- __ movptr(rcx_recv, __ argument_address(rdx_vmslots));
+ Address mh_receiver_slot_addr = __ argument_address(rdx_vmslots);
+ __ movptr(rcx_recv, mh_receiver_slot_addr);
trace_method_handle(_masm, "invokeExact");
__ check_method_handle_type(rax_mtype, rcx_recv, rdi_temp, wrong_method_type);
+
+ // Nobody uses the MH receiver slot after this. Make sure.
+ DEBUG_ONLY(__ movptr(mh_receiver_slot_addr, (int32_t)0x999999));
+
__ jump_to_method_handle_entry(rcx_recv, rdi_temp);
// for invokeGeneric (only), apply argument and result conversions on the fly
__ bind(invoke_generic_slow_path);
#ifdef ASSERT
- { Label L;
+ if (VerifyMethodHandles) {
+ Label L;
__ cmpb(Address(rbx_method, methodOopDesc::intrinsic_id_offset_in_bytes()), (int) vmIntrinsics::_invokeGeneric);
__ jcc(Assembler::equal, L);
__ stop("bad methodOop::intrinsic_id");
@@ -150,22 +611,14 @@
// make room on the stack for another pointer:
Register rcx_argslot = rcx_recv;
__ lea(rcx_argslot, __ argument_address(rdx_vmslots, 1));
- insert_arg_slots(_masm, 2 * stack_move_unit(), _INSERT_REF_MASK,
+ insert_arg_slots(_masm, 2 * stack_move_unit(),
rcx_argslot, rbx_temp, rdx_temp);
// load up an adapter from the calling type (Java weaves this)
- __ load_heap_oop(rdx_temp, Address(rax_mtype, __ delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, rdi_temp)));
Register rdx_adapter = rdx_temp;
- // __ load_heap_oop(rdx_adapter, Address(rdx_temp, java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes()));
- // deal with old JDK versions:
- __ lea(rdi_temp, Address(rdx_temp, __ delayed_value(java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes, rdi_temp)));
- __ cmpptr(rdi_temp, rdx_temp);
- Label sorry_no_invoke_generic;
- __ jcc(Assembler::below, sorry_no_invoke_generic);
-
- __ load_heap_oop(rdx_adapter, Address(rdi_temp, 0));
- __ testptr(rdx_adapter, rdx_adapter);
- __ jcc(Assembler::zero, sorry_no_invoke_generic);
+ __ load_heap_oop(rdx_temp, Address(rax_mtype, __ delayed_value(java_lang_invoke_MethodType::form_offset_in_bytes, rdi_temp)));
+ __ load_heap_oop(rdx_adapter, Address(rdx_temp, __ delayed_value(java_lang_invoke_MethodTypeForm::genericInvoker_offset_in_bytes, rdi_temp)));
+ __ verify_oop(rdx_adapter);
__ movptr(Address(rcx_argslot, 1 * Interpreter::stackElementSize), rdx_adapter);
// As a trusted first argument, pass the type being called, so the adapter knows
// the actual types of the arguments and return values.
@@ -176,49 +629,31 @@
trace_method_handle(_masm, "invokeGeneric");
__ jump_to_method_handle_entry(rcx, rdi_temp);
- __ bind(sorry_no_invoke_generic); // no invokeGeneric implementation available!
- __ movptr(rcx_recv, Address(rcx_argslot, -1 * Interpreter::stackElementSize)); // recover original MH
- __ push(rax_mtype); // required mtype
- __ push(rcx_recv); // bad mh (1st stacked argument)
- __ jump(ExternalAddress(Interpreter::throw_WrongMethodType_entry()));
+ return entry_point;
+}
- return entry_point;
+// Workaround for C++ overloading nastiness on '0' for RegisterOrConstant.
+static RegisterOrConstant constant(int value) {
+ return RegisterOrConstant(value);
}
// Helper to insert argument slots into the stack.
-// arg_slots must be a multiple of stack_move_unit() and <= 0
+// arg_slots must be a multiple of stack_move_unit() and < 0
+// rax_argslot is decremented to point to the new (shifted) location of the argslot
+// But, rdx_temp ends up holding the original value of rax_argslot.
void MethodHandles::insert_arg_slots(MacroAssembler* _masm,
RegisterOrConstant arg_slots,
- int arg_mask,
Register rax_argslot,
- Register rbx_temp, Register rdx_temp, Register temp3_reg) {
- assert(temp3_reg == noreg, "temp3 not required");
+ Register rbx_temp, Register rdx_temp) {
+ // allow constant zero
+ if (arg_slots.is_constant() && arg_slots.as_constant() == 0)
+ return;
assert_different_registers(rax_argslot, rbx_temp, rdx_temp,
(!arg_slots.is_register() ? rsp : arg_slots.as_register()));
-
-#ifdef ASSERT
- verify_argslot(_masm, rax_argslot, "insertion point must fall within current frame");
- if (arg_slots.is_register()) {
- Label L_ok, L_bad;
- __ cmpptr(arg_slots.as_register(), (int32_t) NULL_WORD);
- __ jccb(Assembler::greater, L_bad);
- __ testl(arg_slots.as_register(), -stack_move_unit() - 1);
- __ jccb(Assembler::zero, L_ok);
- __ bind(L_bad);
- __ stop("assert arg_slots <= 0 and clear low bits");
- __ bind(L_ok);
- } else {
- assert(arg_slots.as_constant() <= 0, "");
- assert(arg_slots.as_constant() % -stack_move_unit() == 0, "");
- }
-#endif //ASSERT
-
-#ifdef _LP64
- if (arg_slots.is_register()) {
- // clean high bits of stack motion register (was loaded as an int)
- __ movslq(arg_slots.as_register(), arg_slots.as_register());
- }
-#endif
+ if (VerifyMethodHandles)
+ verify_argslot(_masm, rax_argslot, "insertion point must fall within current frame");
+ if (VerifyMethodHandles)
+ verify_stack_move(_masm, arg_slots, -1);
// Make space on the stack for the inserted argument(s).
// Then pull down everything shallower than rax_argslot.
@@ -230,59 +665,39 @@
// argslot -= size;
BLOCK_COMMENT("insert_arg_slots {");
__ mov(rdx_temp, rsp); // source pointer for copy
- __ lea(rsp, Address(rsp, arg_slots, Address::times_ptr));
+ __ lea(rsp, Address(rsp, arg_slots, Interpreter::stackElementScale()));
{
Label loop;
__ BIND(loop);
// pull one word down each time through the loop
__ movptr(rbx_temp, Address(rdx_temp, 0));
- __ movptr(Address(rdx_temp, arg_slots, Address::times_ptr), rbx_temp);
+ __ movptr(Address(rdx_temp, arg_slots, Interpreter::stackElementScale()), rbx_temp);
__ addptr(rdx_temp, wordSize);
__ cmpptr(rdx_temp, rax_argslot);
- __ jccb(Assembler::less, loop);
+ __ jcc(Assembler::below, loop);
}
// Now move the argslot down, to point to the opened-up space.
- __ lea(rax_argslot, Address(rax_argslot, arg_slots, Address::times_ptr));
+ __ lea(rax_argslot, Address(rax_argslot, arg_slots, Interpreter::stackElementScale()));
BLOCK_COMMENT("} insert_arg_slots");
}
// Helper to remove argument slots from the stack.
-// arg_slots must be a multiple of stack_move_unit() and >= 0
+// arg_slots must be a multiple of stack_move_unit() and > 0
void MethodHandles::remove_arg_slots(MacroAssembler* _masm,
- RegisterOrConstant arg_slots,
- Register rax_argslot,
- Register rbx_temp, Register rdx_temp, Register temp3_reg) {
- assert(temp3_reg == noreg, "temp3 not required");
+ RegisterOrConstant arg_slots,
+ Register rax_argslot,
+ Register rbx_temp, Register rdx_temp) {
+ // allow constant zero
+ if (arg_slots.is_constant() && arg_slots.as_constant() == 0)
+ return;
assert_different_registers(rax_argslot, rbx_temp, rdx_temp,
(!arg_slots.is_register() ? rsp : arg_slots.as_register()));
-
-#ifdef ASSERT
- // Verify that [argslot..argslot+size) lies within (rsp, rbp).
- __ lea(rbx_temp, Address(rax_argslot, arg_slots, Address::times_ptr));
- verify_argslot(_masm, rbx_temp, "deleted argument(s) must fall within current frame");
- if (arg_slots.is_register()) {
- Label L_ok, L_bad;
- __ cmpptr(arg_slots.as_register(), (int32_t) NULL_WORD);
- __ jccb(Assembler::less, L_bad);
- __ testl(arg_slots.as_register(), -stack_move_unit() - 1);
- __ jccb(Assembler::zero, L_ok);
- __ bind(L_bad);
- __ stop("assert arg_slots >= 0 and clear low bits");
- __ bind(L_ok);
- } else {
- assert(arg_slots.as_constant() >= 0, "");
- assert(arg_slots.as_constant() % -stack_move_unit() == 0, "");
- }
-#endif //ASSERT
-
-#ifdef _LP64
- if (false) { // not needed, since register is positive
- // clean high bits of stack motion register (was loaded as an int)
- if (arg_slots.is_register())
- __ movslq(arg_slots.as_register(), arg_slots.as_register());
- }
-#endif
+ if (VerifyMethodHandles)
+ verify_argslots(_masm, arg_slots, rax_argslot, false,
+ "deleted argument(s) must fall within current frame");
+ if (VerifyMethodHandles)
+ verify_stack_move(_masm, arg_slots, +1);
BLOCK_COMMENT("remove_arg_slots {");
// Pull up everything shallower than rax_argslot.
@@ -299,54 +714,344 @@
__ BIND(loop);
// pull one word up each time through the loop
__ movptr(rbx_temp, Address(rdx_temp, 0));
- __ movptr(Address(rdx_temp, arg_slots, Address::times_ptr), rbx_temp);
+ __ movptr(Address(rdx_temp, arg_slots, Interpreter::stackElementScale()), rbx_temp);
__ addptr(rdx_temp, -wordSize);
__ cmpptr(rdx_temp, rsp);
- __ jccb(Assembler::greaterEqual, loop);
+ __ jcc(Assembler::aboveEqual, loop);
}
// Now move the argslot up, to point to the just-copied block.
- __ lea(rsp, Address(rsp, arg_slots, Address::times_ptr));
+ __ lea(rsp, Address(rsp, arg_slots, Interpreter::stackElementScale()));
// And adjust the argslot address to point at the deletion point.
- __ lea(rax_argslot, Address(rax_argslot, arg_slots, Address::times_ptr));
+ __ lea(rax_argslot, Address(rax_argslot, arg_slots, Interpreter::stackElementScale()));
BLOCK_COMMENT("} remove_arg_slots");
}
+// Helper to copy argument slots to the top of the stack.
+// The sequence starts with rax_argslot and is counted by slot_count
+// slot_count must be a multiple of stack_move_unit() and >= 0
+// This function blows the temps but does not change rax_argslot.
+void MethodHandles::push_arg_slots(MacroAssembler* _masm,
+ Register rax_argslot,
+ RegisterOrConstant slot_count,
+ int skip_words_count,
+ Register rbx_temp, Register rdx_temp) {
+ assert_different_registers(rax_argslot, rbx_temp, rdx_temp,
+ (!slot_count.is_register() ? rbp : slot_count.as_register()),
+ rsp);
+ assert(Interpreter::stackElementSize == wordSize, "else change this code");
+
+ if (VerifyMethodHandles)
+ verify_stack_move(_masm, slot_count, 0);
+
+ // allow constant zero
+ if (slot_count.is_constant() && slot_count.as_constant() == 0)
+ return;
+
+ BLOCK_COMMENT("push_arg_slots {");
+
+ Register rbx_top = rbx_temp;
+
+ // There is at most 1 word to carry down with the TOS.
+ switch (skip_words_count) {
+ case 1: __ pop(rdx_temp); break;
+ case 0: break;
+ default: ShouldNotReachHere();
+ }
+
+ if (slot_count.is_constant()) {
+ for (int i = slot_count.as_constant() - 1; i >= 0; i--) {
+ __ pushptr(Address(rax_argslot, i * wordSize));
+ }
+ } else {
+ Label L_plural, L_loop, L_break;
+ // Emit code to dynamically check for the common cases, zero and one slot.
+ __ cmpl(slot_count.as_register(), (int32_t) 1);
+ __ jccb(Assembler::greater, L_plural);
+ __ jccb(Assembler::less, L_break);
+ __ pushptr(Address(rax_argslot, 0));
+ __ jmpb(L_break);
+ __ BIND(L_plural);
+
+ // Loop for 2 or more:
+ // rbx = &rax[slot_count]
+ // while (rbx > rax) *(--rsp) = *(--rbx)
+ __ lea(rbx_top, Address(rax_argslot, slot_count, Address::times_ptr));
+ __ BIND(L_loop);
+ __ subptr(rbx_top, wordSize);
+ __ pushptr(Address(rbx_top, 0));
+ __ cmpptr(rbx_top, rax_argslot);
+ __ jcc(Assembler::above, L_loop);
+ __ bind(L_break);
+ }
+ switch (skip_words_count) {
+ case 1: __ push(rdx_temp); break;
+ case 0: break;
+ default: ShouldNotReachHere();
+ }
+ BLOCK_COMMENT("} push_arg_slots");
+}
+
+// in-place movement; no change to rsp
+// blows rax_temp, rdx_temp
+void MethodHandles::move_arg_slots_up(MacroAssembler* _masm,
+ Register rbx_bottom, // invariant
+ Address top_addr, // can use rax_temp
+ RegisterOrConstant positive_distance_in_slots,
+ Register rax_temp, Register rdx_temp) {
+ BLOCK_COMMENT("move_arg_slots_up {");
+ assert_different_registers(rbx_bottom,
+ rax_temp, rdx_temp,
+ positive_distance_in_slots.register_or_noreg());
+ Label L_loop, L_break;
+ Register rax_top = rax_temp;
+ if (!top_addr.is_same_address(Address(rax_top, 0)))
+ __ lea(rax_top, top_addr);
+ // Detect empty (or broken) loop:
+#ifdef ASSERT
+ if (VerifyMethodHandles) {
+ // Verify that &bottom < &top (non-empty interval)
+ Label L_ok, L_bad;
+ if (positive_distance_in_slots.is_register()) {
+ __ cmpptr(positive_distance_in_slots.as_register(), (int32_t) 0);
+ __ jcc(Assembler::lessEqual, L_bad);
+ }
+ __ cmpptr(rbx_bottom, rax_top);
+ __ jcc(Assembler::below, L_ok);
+ __ bind(L_bad);
+ __ stop("valid bounds (copy up)");
+ __ BIND(L_ok);
+ }
+#endif
+ __ cmpptr(rbx_bottom, rax_top);
+ __ jccb(Assembler::aboveEqual, L_break);
+ // work rax down to rbx, copying contiguous data upwards
+ // In pseudo-code:
+ // [rbx, rax) = &[bottom, top)
+ // while (--rax >= rbx) *(rax + distance) = *(rax + 0), rax--;
+ __ BIND(L_loop);
+ __ subptr(rax_top, wordSize);
+ __ movptr(rdx_temp, Address(rax_top, 0));
+ __ movptr( Address(rax_top, positive_distance_in_slots, Address::times_ptr), rdx_temp);
+ __ cmpptr(rax_top, rbx_bottom);
+ __ jcc(Assembler::above, L_loop);
+ assert(Interpreter::stackElementSize == wordSize, "else change loop");
+ __ bind(L_break);
+ BLOCK_COMMENT("} move_arg_slots_up");
+}
+
+// in-place movement; no change to rsp
+// blows rax_temp, rdx_temp
+void MethodHandles::move_arg_slots_down(MacroAssembler* _masm,
+ Address bottom_addr, // can use rax_temp
+ Register rbx_top, // invariant
+ RegisterOrConstant negative_distance_in_slots,
+ Register rax_temp, Register rdx_temp) {
+ BLOCK_COMMENT("move_arg_slots_down {");
+ assert_different_registers(rbx_top,
+ negative_distance_in_slots.register_or_noreg(),
+ rax_temp, rdx_temp);
+ Label L_loop, L_break;
+ Register rax_bottom = rax_temp;
+ if (!bottom_addr.is_same_address(Address(rax_bottom, 0)))
+ __ lea(rax_bottom, bottom_addr);
+ // Detect empty (or broken) loop:
+#ifdef ASSERT
+ assert(!negative_distance_in_slots.is_constant() || negative_distance_in_slots.as_constant() < 0, "");
+ if (VerifyMethodHandles) {
+ // Verify that &bottom < &top (non-empty interval)
+ Label L_ok, L_bad;
+ if (negative_distance_in_slots.is_register()) {
+ __ cmpptr(negative_distance_in_slots.as_register(), (int32_t) 0);
+ __ jcc(Assembler::greaterEqual, L_bad);
+ }
+ __ cmpptr(rax_bottom, rbx_top);
+ __ jcc(Assembler::below, L_ok);
+ __ bind(L_bad);
+ __ stop("valid bounds (copy down)");
+ __ BIND(L_ok);
+ }
+#endif
+ __ cmpptr(rax_bottom, rbx_top);
+ __ jccb(Assembler::aboveEqual, L_break);
+ // work rax up to rbx, copying contiguous data downwards
+ // In pseudo-code:
+ // [rax, rbx) = &[bottom, top)
+ // while (rax < rbx) *(rax - distance) = *(rax + 0), rax++;
+ __ BIND(L_loop);
+ __ movptr(rdx_temp, Address(rax_bottom, 0));
+ __ movptr( Address(rax_bottom, negative_distance_in_slots, Address::times_ptr), rdx_temp);
+ __ addptr(rax_bottom, wordSize);
+ __ cmpptr(rax_bottom, rbx_top);
+ __ jcc(Assembler::below, L_loop);
+ assert(Interpreter::stackElementSize == wordSize, "else change loop");
+ __ bind(L_break);
+ BLOCK_COMMENT("} move_arg_slots_down");
+}
+
+// Copy from a field or array element to a stacked argument slot.
+// is_element (ignored) says whether caller is loading an array element instead of an instance field.
+void MethodHandles::move_typed_arg(MacroAssembler* _masm,
+ BasicType type, bool is_element,
+ Address slot_dest, Address value_src,
+ Register rbx_temp, Register rdx_temp) {
+ BLOCK_COMMENT(!is_element ? "move_typed_arg {" : "move_typed_arg { (array element)");
+ if (type == T_OBJECT || type == T_ARRAY) {
+ __ load_heap_oop(rbx_temp, value_src);
+ __ movptr(slot_dest, rbx_temp);
+ } else if (type != T_VOID) {
+ int arg_size = type2aelembytes(type);
+ bool arg_is_signed = is_signed_subword_type(type);
+ int slot_size = (arg_size > wordSize) ? arg_size : wordSize;
+ __ load_sized_value( rdx_temp, value_src, arg_size, arg_is_signed, rbx_temp);
+ __ store_sized_value( slot_dest, rdx_temp, slot_size, rbx_temp);
+ }
+ BLOCK_COMMENT("} move_typed_arg");
+}
+
+void MethodHandles::move_return_value(MacroAssembler* _masm, BasicType type,
+ Address return_slot) {
+ BLOCK_COMMENT("move_return_value {");
+ // Old versions of the JVM must clean the FPU stack after every return.
+#ifndef _LP64
+#ifdef COMPILER2
+ // The FPU stack is clean if UseSSE >= 2 but must be cleaned in other cases
+ if ((type == T_FLOAT && UseSSE < 1) || (type == T_DOUBLE && UseSSE < 2)) {
+ for (int i = 1; i < 8; i++) {
+ __ ffree(i);
+ }
+ } else if (UseSSE < 2) {
+ __ empty_FPU_stack();
+ }
+#endif //COMPILER2
+#endif //!_LP64
+
+ // Look at the type and pull the value out of the corresponding register.
+ if (type == T_VOID) {
+ // nothing to do
+ } else if (type == T_OBJECT) {
+ __ movptr(return_slot, rax);
+ } else if (type == T_INT || is_subword_type(type)) {
+ // write the whole word, even if only 32 bits is significant
+ __ movptr(return_slot, rax);
+ } else if (type == T_LONG) {
+ // store the value by parts
+ // Note: We assume longs are continguous (if misaligned) on the interpreter stack.
+ __ store_sized_value(return_slot, rax, BytesPerLong, rdx);
+ } else if (NOT_LP64((type == T_FLOAT && UseSSE < 1) ||
+ (type == T_DOUBLE && UseSSE < 2) ||)
+ false) {
+ // Use old x86 FPU registers:
+ if (type == T_FLOAT)
+ __ fstp_s(return_slot);
+ else
+ __ fstp_d(return_slot);
+ } else if (type == T_FLOAT) {
+ __ movflt(return_slot, xmm0);
+ } else if (type == T_DOUBLE) {
+ __ movdbl(return_slot, xmm0);
+ } else {
+ ShouldNotReachHere();
+ }
+ BLOCK_COMMENT("} move_return_value");
+}
+
+
#ifndef PRODUCT
extern "C" void print_method_handle(oop mh);
void trace_method_handle_stub(const char* adaptername,
+ oop mh,
+ intptr_t* saved_regs,
+ intptr_t* entry_sp,
intptr_t* saved_sp,
- oop mh,
- intptr_t* sp) {
+ intptr_t* saved_bp) {
// called as a leaf from native code: do not block the JVM!
- intptr_t* entry_sp = sp + LP64_ONLY(16) NOT_LP64(8);
- tty->print_cr("MH %s mh="INTPTR_FORMAT" sp="INTPTR_FORMAT" saved_sp="INTPTR_FORMAT")",
- adaptername, (intptr_t)mh, (intptr_t)entry_sp, saved_sp);
+ bool has_mh = (strstr(adaptername, "return/") == NULL); // return adapters don't have rcx_mh
+ intptr_t* last_sp = (intptr_t*) saved_bp[frame::interpreter_frame_last_sp_offset];
+ intptr_t* base_sp = last_sp;
+ typedef MethodHandles::RicochetFrame RicochetFrame;
+ RicochetFrame* rfp = (RicochetFrame*)((address)saved_bp - RicochetFrame::sender_link_offset_in_bytes());
+ if (!UseRicochetFrames || Universe::heap()->is_in((address) rfp->saved_args_base())) {
+ // Probably an interpreter frame.
+ base_sp = (intptr_t*) saved_bp[frame::interpreter_frame_monitor_block_top_offset];
+ }
+ intptr_t mh_reg = (intptr_t)mh;
+ const char* mh_reg_name = "rcx_mh";
+ if (!has_mh) mh_reg_name = "rcx";
+ tty->print_cr("MH %s %s="PTR_FORMAT" sp=("PTR_FORMAT"+"INTX_FORMAT") stack_size="INTX_FORMAT" bp="PTR_FORMAT,
+ adaptername, mh_reg_name, mh_reg,
+ (intptr_t)entry_sp, (intptr_t)(saved_sp - entry_sp), (intptr_t)(base_sp - last_sp), (intptr_t)saved_bp);
if (Verbose) {
- print_method_handle(mh);
+ tty->print(" reg dump: ");
+ int saved_regs_count = (entry_sp-1) - saved_regs;
+ // 32 bit: rdi rsi rbp rsp; rbx rdx rcx (*) rax
+ int i;
+ for (i = 0; i <= saved_regs_count; i++) {
+ if (i > 0 && i % 4 == 0 && i != saved_regs_count) {
+ tty->cr();
+ tty->print(" + dump: ");
+ }
+ tty->print(" %d: "PTR_FORMAT, i, saved_regs[i]);
+ }
+ tty->cr();
+ if (last_sp != saved_sp && last_sp != NULL)
+ tty->print_cr("*** last_sp="PTR_FORMAT, (intptr_t)last_sp);
+ int stack_dump_count = 16;
+ if (stack_dump_count < (int)(saved_bp + 2 - saved_sp))
+ stack_dump_count = (int)(saved_bp + 2 - saved_sp);
+ if (stack_dump_count > 64) stack_dump_count = 48;
+ for (i = 0; i < stack_dump_count; i += 4) {
+ tty->print_cr(" dump at SP[%d] "PTR_FORMAT": "PTR_FORMAT" "PTR_FORMAT" "PTR_FORMAT" "PTR_FORMAT,
+ i, (intptr_t) &entry_sp[i+0], entry_sp[i+0], entry_sp[i+1], entry_sp[i+2], entry_sp[i+3]);
+ }
+ if (has_mh)
+ print_method_handle(mh);
}
}
+
+// The stub wraps the arguments in a struct on the stack to avoid
+// dealing with the different calling conventions for passing 6
+// arguments.
+struct MethodHandleStubArguments {
+ const char* adaptername;
+ oopDesc* mh;
+ intptr_t* saved_regs;
+ intptr_t* entry_sp;
+ intptr_t* saved_sp;
+ intptr_t* saved_bp;
+};
+void trace_method_handle_stub_wrapper(MethodHandleStubArguments* args) {
+ trace_method_handle_stub(args->adaptername,
+ args->mh,
+ args->saved_regs,
+ args->entry_sp,
+ args->saved_sp,
+ args->saved_bp);
+}
+
void MethodHandles::trace_method_handle(MacroAssembler* _masm, const char* adaptername) {
if (!TraceMethodHandles) return;
BLOCK_COMMENT("trace_method_handle {");
+ __ push(rax);
+ __ lea(rax, Address(rsp, wordSize * NOT_LP64(6) LP64_ONLY(14))); // entry_sp __ pusha();
__ pusha();
-#ifdef _LP64
- // Pass arguments carefully since the registers overlap with the calling convention.
+ __ mov(rbx, rsp);
+ __ enter();
+ // incoming state:
// rcx: method handle
- // r13: saved sp
- __ mov(c_rarg2, rcx); // mh
- __ mov(c_rarg1, r13); // saved sp
- __ mov(c_rarg3, rsp); // sp
- __ movptr(c_rarg0, (intptr_t) adaptername);
- __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, trace_method_handle_stub), c_rarg0, c_rarg1, c_rarg2, c_rarg3);
-#else
- // arguments:
- // rcx: method handle
- // rsi: saved sp
- __ movptr(rbx, (intptr_t) adaptername);
- __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, trace_method_handle_stub), rbx, rsi, rcx, rsp);
-#endif
+ // r13 or rsi: saved sp
+ // To avoid calling convention issues, build a record on the stack and pass the pointer to that instead.
+ __ push(rbp); // saved_bp
+ __ push(rsi); // saved_sp
+ __ push(rax); // entry_sp
+ __ push(rbx); // pusha saved_regs
+ __ push(rcx); // mh
+ __ push(rcx); // adaptername
+ __ movptr(Address(rsp, 0), (intptr_t) adaptername);
+ __ super_call_VM_leaf(CAST_FROM_FN_PTR(address, trace_method_handle_stub_wrapper), rsp);
+ __ leave();
__ popa();
+ __ pop(rax);
BLOCK_COMMENT("} trace_method_handle");
}
#endif //PRODUCT
@@ -358,13 +1063,20 @@
|(1<= _bound_ref_direct_mh);
- BasicType arg_type = T_ILLEGAL;
- int arg_mask = _INSERT_NO_MASK;
- int arg_slots = -1;
- get_ek_bound_mh_info(ek, arg_type, arg_mask, arg_slots);
+ BasicType arg_type = ek_bound_mh_arg_type(ek);
+ int arg_slots = type2size[arg_type];
// make room for the new argument:
__ movl(rax_argslot, rcx_bmh_vmargslot);
__ lea(rax_argslot, __ argument_address(rax_argslot));
- insert_arg_slots(_masm, arg_slots * stack_move_unit(), arg_mask, rax_argslot, rbx_temp, rdx_temp);
+ insert_arg_slots(_masm, arg_slots * stack_move_unit(), rax_argslot, rbx_temp, rdx_temp);
// store bound argument into the new stack slot:
__ load_heap_oop(rbx_temp, rcx_bmh_argument);
@@ -589,9 +1308,10 @@
__ movptr(Address(rax_argslot, 0), rbx_temp);
} else {
Address prim_value_addr(rbx_temp, java_lang_boxing_object::value_offset_in_bytes(arg_type));
- const int arg_size = type2aelembytes(arg_type);
- __ load_sized_value(rdx_temp, prim_value_addr, arg_size, is_signed_subword_type(arg_type), rbx_temp);
- __ store_sized_value(Address(rax_argslot, 0), rdx_temp, arg_size, rbx_temp);
+ move_typed_arg(_masm, arg_type, false,
+ Address(rax_argslot, 0),
+ prim_value_addr,
+ rbx_temp, rdx_temp);
}
if (direct_to_method) {
@@ -628,7 +1348,7 @@
// What class are we casting to?
__ load_heap_oop(rbx_klass, rcx_amh_argument); // this is a Class object!
- __ load_heap_oop(rbx_klass, Address(rbx_klass, java_lang_Class::klass_offset_in_bytes()));
+ load_klass_from_Class(_masm, rbx_klass);
Label done;
__ movptr(rdx_temp, vmarg);
@@ -663,6 +1383,7 @@
case _adapter_prim_to_prim:
case _adapter_ref_to_prim:
+ case _adapter_prim_to_ref:
// handled completely by optimized cases
__ stop("init_AdapterMethodHandle should not issue this");
break;
@@ -714,8 +1435,7 @@
// Do the requested conversion and store the value.
Register rbx_vminfo = rbx_temp;
- __ movl(rbx_vminfo, rcx_amh_conversion);
- assert(CONV_VMINFO_SHIFT == 0, "preshifted");
+ load_conversion_vminfo(_masm, rbx_vminfo, rcx_amh_conversion);
// get the new MH:
__ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
@@ -753,7 +1473,7 @@
// on a little-endian machine we keep the first slot and add another after
__ lea(rax_argslot, __ argument_address(rax_argslot, 1));
- insert_arg_slots(_masm, stack_move_unit(), _INSERT_INT_MASK,
+ insert_arg_slots(_masm, stack_move_unit(),
rax_argslot, rbx_temp, rdx_temp);
Address vmarg1(rax_argslot, -Interpreter::stackElementSize);
Address vmarg2 = vmarg1.plus_disp(Interpreter::stackElementSize);
@@ -805,7 +1525,7 @@
__ movl(rax_argslot, rcx_amh_vmargslot);
__ lea(rax_argslot, __ argument_address(rax_argslot, 1));
if (ek == _adapter_opt_f2d) {
- insert_arg_slots(_masm, stack_move_unit(), _INSERT_INT_MASK,
+ insert_arg_slots(_masm, stack_move_unit(),
rax_argslot, rbx_temp, rdx_temp);
}
Address vmarg(rax_argslot, -Interpreter::stackElementSize);
@@ -823,7 +1543,7 @@
#else //_LP64
if (ek == _adapter_opt_f2d) {
__ fld_s(vmarg); // load float to ST0
- __ fstp_s(vmarg); // store single
+ __ fstp_d(vmarg); // store double
} else {
__ fld_d(vmarg); // load double to ST0
__ fstp_s(vmarg); // store single
@@ -840,10 +1560,6 @@
}
break;
- case _adapter_prim_to_ref:
- __ unimplemented(entry_name(ek)); // %%% FIXME: NYI
- break;
-
case _adapter_swap_args:
case _adapter_rot_args:
// handled completely by optimized cases
@@ -857,8 +1573,8 @@
case _adapter_opt_rot_2_up:
case _adapter_opt_rot_2_down:
{
- int swap_bytes = 0, rotate = 0;
- get_ek_adapter_opt_swap_rot_info(ek, swap_bytes, rotate);
+ int swap_slots = ek_adapter_opt_swap_slots(ek);
+ int rotate = ek_adapter_opt_swap_mode(ek);
// 'argslot' is the position of the first argument to swap
__ movl(rax_argslot, rcx_amh_vmargslot);
@@ -866,83 +1582,69 @@
// 'vminfo' is the second
Register rbx_destslot = rbx_temp;
- __ movl(rbx_destslot, rcx_amh_conversion);
- assert(CONV_VMINFO_SHIFT == 0, "preshifted");
- __ andl(rbx_destslot, CONV_VMINFO_MASK);
+ load_conversion_vminfo(_masm, rbx_destslot, rcx_amh_conversion);
__ lea(rbx_destslot, __ argument_address(rbx_destslot));
- DEBUG_ONLY(verify_argslot(_masm, rbx_destslot, "swap point must fall within current frame"));
+ if (VerifyMethodHandles)
+ verify_argslot(_masm, rbx_destslot, "swap point must fall within current frame");
+ assert(Interpreter::stackElementSize == wordSize, "else rethink use of wordSize here");
if (!rotate) {
- for (int i = 0; i < swap_bytes; i += wordSize) {
- __ movptr(rdx_temp, Address(rax_argslot , i));
- __ push(rdx_temp);
- __ movptr(rdx_temp, Address(rbx_destslot, i));
- __ movptr(Address(rax_argslot, i), rdx_temp);
- __ pop(rdx_temp);
- __ movptr(Address(rbx_destslot, i), rdx_temp);
+ // simple swap
+ for (int i = 0; i < swap_slots; i++) {
+ __ movptr(rdi_temp, Address(rax_argslot, i * wordSize));
+ __ movptr(rdx_temp, Address(rbx_destslot, i * wordSize));
+ __ movptr(Address(rax_argslot, i * wordSize), rdx_temp);
+ __ movptr(Address(rbx_destslot, i * wordSize), rdi_temp);
}
} else {
- // push the first chunk, which is going to get overwritten
- for (int i = swap_bytes; (i -= wordSize) >= 0; ) {
- __ movptr(rdx_temp, Address(rax_argslot, i));
- __ push(rdx_temp);
+ // A rotate is actually pair of moves, with an "odd slot" (or pair)
+ // changing place with a series of other slots.
+ // First, push the "odd slot", which is going to get overwritten
+ for (int i = swap_slots - 1; i >= 0; i--) {
+ // handle one with rdi_temp instead of a push:
+ if (i == 0) __ movptr(rdi_temp, Address(rax_argslot, i * wordSize));
+ else __ pushptr( Address(rax_argslot, i * wordSize));
}
-
if (rotate > 0) {
- // rotate upward
- __ subptr(rax_argslot, swap_bytes);
-#ifdef ASSERT
- {
- // Verify that argslot > destslot, by at least swap_bytes.
- Label L_ok;
- __ cmpptr(rax_argslot, rbx_destslot);
- __ jccb(Assembler::aboveEqual, L_ok);
- __ stop("source must be above destination (upward rotation)");
- __ bind(L_ok);
- }
-#endif
+ // Here is rotate > 0:
+ // (low mem) (high mem)
+ // | dest: more_slots... | arg: odd_slot :arg+1 |
+ // =>
+ // | dest: odd_slot | dest+1: more_slots... :arg+1 |
// work argslot down to destslot, copying contiguous data upwards
// pseudo-code:
// rax = src_addr - swap_bytes
// rbx = dest_addr
// while (rax >= rbx) *(rax + swap_bytes) = *(rax + 0), rax--;
- Label loop;
- __ bind(loop);
- __ movptr(rdx_temp, Address(rax_argslot, 0));
- __ movptr(Address(rax_argslot, swap_bytes), rdx_temp);
- __ addptr(rax_argslot, -wordSize);
- __ cmpptr(rax_argslot, rbx_destslot);
- __ jccb(Assembler::aboveEqual, loop);
+ move_arg_slots_up(_masm,
+ rbx_destslot,
+ Address(rax_argslot, 0),
+ swap_slots,
+ rax_argslot, rdx_temp);
} else {
- __ addptr(rax_argslot, swap_bytes);
-#ifdef ASSERT
- {
- // Verify that argslot < destslot, by at least swap_bytes.
- Label L_ok;
- __ cmpptr(rax_argslot, rbx_destslot);
- __ jccb(Assembler::belowEqual, L_ok);
- __ stop("source must be below destination (downward rotation)");
- __ bind(L_ok);
- }
-#endif
+ // Here is the other direction, rotate < 0:
+ // (low mem) (high mem)
+ // | arg: odd_slot | arg+1: more_slots... :dest+1 |
+ // =>
+ // | arg: more_slots... | dest: odd_slot :dest+1 |
// work argslot up to destslot, copying contiguous data downwards
// pseudo-code:
// rax = src_addr + swap_bytes
// rbx = dest_addr
// while (rax <= rbx) *(rax - swap_bytes) = *(rax + 0), rax++;
- Label loop;
- __ bind(loop);
- __ movptr(rdx_temp, Address(rax_argslot, 0));
- __ movptr(Address(rax_argslot, -swap_bytes), rdx_temp);
- __ addptr(rax_argslot, wordSize);
- __ cmpptr(rax_argslot, rbx_destslot);
- __ jccb(Assembler::belowEqual, loop);
+ __ addptr(rbx_destslot, wordSize);
+ move_arg_slots_down(_masm,
+ Address(rax_argslot, swap_slots * wordSize),
+ rbx_destslot,
+ -swap_slots,
+ rax_argslot, rdx_temp);
+
+ __ subptr(rbx_destslot, wordSize);
}
-
// pop the original first chunk into the destination slot, now free
- for (int i = 0; i < swap_bytes; i += wordSize) {
- __ pop(rdx_temp);
- __ movptr(Address(rbx_destslot, i), rdx_temp);
+ for (int i = 0; i < swap_slots; i++) {
+ if (i == 0) __ movptr(Address(rbx_destslot, i * wordSize), rdi_temp);
+ else __ popptr(Address(rbx_destslot, i * wordSize));
}
}
@@ -958,53 +1660,22 @@
__ lea(rax_argslot, __ argument_address(rax_argslot));
// 'stack_move' is negative number of words to duplicate
- Register rdx_stack_move = rdx_temp;
- __ movl2ptr(rdx_stack_move, rcx_amh_conversion);
- __ sarptr(rdx_stack_move, CONV_STACK_MOVE_SHIFT);
-
- int argslot0_num = 0;
- Address argslot0 = __ argument_address(RegisterOrConstant(argslot0_num));
- assert(argslot0.base() == rsp, "");
- int pre_arg_size = argslot0.disp();
- assert(pre_arg_size % wordSize == 0, "");
- assert(pre_arg_size > 0, "must include PC");
-
- // remember the old rsp+1 (argslot[0])
- Register rbx_oldarg = rbx_temp;
- __ lea(rbx_oldarg, argslot0);
+ Register rdi_stack_move = rdi_temp;
+ load_stack_move(_masm, rdi_stack_move, rcx_recv, true);
- // move rsp down to make room for dups
- __ lea(rsp, Address(rsp, rdx_stack_move, Address::times_ptr));
-
- // compute the new rsp+1 (argslot[0])
- Register rdx_newarg = rdx_temp;
- __ lea(rdx_newarg, argslot0);
-
- __ push(rdi); // need a temp
- // (preceding push must be done after arg addresses are taken!)
-
- // pull down the pre_arg_size data (PC)
- for (int i = -pre_arg_size; i < 0; i += wordSize) {
- __ movptr(rdi, Address(rbx_oldarg, i));
- __ movptr(Address(rdx_newarg, i), rdi);
+ if (VerifyMethodHandles) {
+ verify_argslots(_masm, rdi_stack_move, rax_argslot, true,
+ "copied argument(s) must fall within current frame");
}
- // copy from rax_argslot[0...] down to new_rsp[1...]
- // pseudo-code:
- // rbx = old_rsp+1
- // rdx = new_rsp+1
- // rax = argslot
- // while (rdx < rbx) *rdx++ = *rax++
- Label loop;
- __ bind(loop);
- __ movptr(rdi, Address(rax_argslot, 0));
- __ movptr(Address(rdx_newarg, 0), rdi);
- __ addptr(rax_argslot, wordSize);
- __ addptr(rdx_newarg, wordSize);
- __ cmpptr(rdx_newarg, rbx_oldarg);
- __ jccb(Assembler::less, loop);
+ // insert location is always the bottom of the argument list:
+ Address insert_location = __ argument_address(constant(0));
+ int pre_arg_words = insert_location.disp() / wordSize; // return PC is pushed
+ assert(insert_location.base() == rsp, "");
- __ pop(rdi); // restore temp
+ __ negl(rdi_stack_move);
+ push_arg_slots(_masm, rax_argslot, rdi_stack_move,
+ pre_arg_words, rbx_temp, rdx_temp);
__ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
__ jump_to_method_handle_entry(rcx_recv, rdx_temp);
@@ -1017,63 +1688,583 @@
__ movl(rax_argslot, rcx_amh_vmargslot);
__ lea(rax_argslot, __ argument_address(rax_argslot));
- __ push(rdi); // need a temp
// (must do previous push after argslot address is taken)
// 'stack_move' is number of words to drop
- Register rdi_stack_move = rdi;
- __ movl2ptr(rdi_stack_move, rcx_amh_conversion);
- __ sarptr(rdi_stack_move, CONV_STACK_MOVE_SHIFT);
+ Register rdi_stack_move = rdi_temp;
+ load_stack_move(_masm, rdi_stack_move, rcx_recv, false);
remove_arg_slots(_masm, rdi_stack_move,
rax_argslot, rbx_temp, rdx_temp);
- __ pop(rdi); // restore temp
-
__ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
__ jump_to_method_handle_entry(rcx_recv, rdx_temp);
}
break;
case _adapter_collect_args:
- __ unimplemented(entry_name(ek)); // %%% FIXME: NYI
- break;
-
+ case _adapter_fold_args:
case _adapter_spread_args:
// handled completely by optimized cases
__ stop("init_AdapterMethodHandle should not issue this");
break;
+ case _adapter_opt_collect_ref:
+ case _adapter_opt_collect_int:
+ case _adapter_opt_collect_long:
+ case _adapter_opt_collect_float:
+ case _adapter_opt_collect_double:
+ case _adapter_opt_collect_void:
+ case _adapter_opt_collect_0_ref:
+ case _adapter_opt_collect_1_ref:
+ case _adapter_opt_collect_2_ref:
+ case _adapter_opt_collect_3_ref:
+ case _adapter_opt_collect_4_ref:
+ case _adapter_opt_collect_5_ref:
+ case _adapter_opt_filter_S0_ref:
+ case _adapter_opt_filter_S1_ref:
+ case _adapter_opt_filter_S2_ref:
+ case _adapter_opt_filter_S3_ref:
+ case _adapter_opt_filter_S4_ref:
+ case _adapter_opt_filter_S5_ref:
+ case _adapter_opt_collect_2_S0_ref:
+ case _adapter_opt_collect_2_S1_ref:
+ case _adapter_opt_collect_2_S2_ref:
+ case _adapter_opt_collect_2_S3_ref:
+ case _adapter_opt_collect_2_S4_ref:
+ case _adapter_opt_collect_2_S5_ref:
+ case _adapter_opt_fold_ref:
+ case _adapter_opt_fold_int:
+ case _adapter_opt_fold_long:
+ case _adapter_opt_fold_float:
+ case _adapter_opt_fold_double:
+ case _adapter_opt_fold_void:
+ case _adapter_opt_fold_1_ref:
+ case _adapter_opt_fold_2_ref:
+ case _adapter_opt_fold_3_ref:
+ case _adapter_opt_fold_4_ref:
+ case _adapter_opt_fold_5_ref:
+ {
+ // Given a fresh incoming stack frame, build a new ricochet frame.
+ // On entry, TOS points at a return PC, and RBP is the callers frame ptr.
+ // RSI/R13 has the caller's exact stack pointer, which we must also preserve.
+ // RCX contains an AdapterMethodHandle of the indicated kind.
+
+ // Relevant AMH fields:
+ // amh.vmargslot:
+ // points to the trailing edge of the arguments
+ // to filter, collect, or fold. For a boxing operation,
+ // it points just after the single primitive value.
+ // amh.argument:
+ // recursively called MH, on |collect| arguments
+ // amh.vmtarget:
+ // final destination MH, on return value, etc.
+ // amh.conversion.dest:
+ // tells what is the type of the return value
+ // (not needed here, since dest is also derived from ek)
+ // amh.conversion.vminfo:
+ // points to the trailing edge of the return value
+ // when the vmtarget is to be called; this is
+ // equal to vmargslot + (retained ? |collect| : 0)
+
+ // Pass 0 or more argument slots to the recursive target.
+ int collect_count_constant = ek_adapter_opt_collect_count(ek);
+
+ // The collected arguments are copied from the saved argument list:
+ int collect_slot_constant = ek_adapter_opt_collect_slot(ek);
+
+ assert(ek_orig == _adapter_collect_args ||
+ ek_orig == _adapter_fold_args, "");
+ bool retain_original_args = (ek_orig == _adapter_fold_args);
+
+ // The return value is replaced (or inserted) at the 'vminfo' argslot.
+ // Sometimes we can compute this statically.
+ int dest_slot_constant = -1;
+ if (!retain_original_args)
+ dest_slot_constant = collect_slot_constant;
+ else if (collect_slot_constant >= 0 && collect_count_constant >= 0)
+ // We are preserving all the arguments, and the return value is prepended,
+ // so the return slot is to the left (above) the |collect| sequence.
+ dest_slot_constant = collect_slot_constant + collect_count_constant;
+
+ // Replace all those slots by the result of the recursive call.
+ // The result type can be one of ref, int, long, float, double, void.
+ // In the case of void, nothing is pushed on the stack after return.
+ BasicType dest = ek_adapter_opt_collect_type(ek);
+ assert(dest == type2wfield[dest], "dest is a stack slot type");
+ int dest_count = type2size[dest];
+ assert(dest_count == 1 || dest_count == 2 || (dest_count == 0 && dest == T_VOID), "dest has a size");
+
+ // Choose a return continuation.
+ EntryKind ek_ret = _adapter_opt_return_any;
+ if (dest != T_CONFLICT && OptimizeMethodHandles) {
+ switch (dest) {
+ case T_INT : ek_ret = _adapter_opt_return_int; break;
+ case T_LONG : ek_ret = _adapter_opt_return_long; break;
+ case T_FLOAT : ek_ret = _adapter_opt_return_float; break;
+ case T_DOUBLE : ek_ret = _adapter_opt_return_double; break;
+ case T_OBJECT : ek_ret = _adapter_opt_return_ref; break;
+ case T_VOID : ek_ret = _adapter_opt_return_void; break;
+ default : ShouldNotReachHere();
+ }
+ if (dest == T_OBJECT && dest_slot_constant >= 0) {
+ EntryKind ek_try = EntryKind(_adapter_opt_return_S0_ref + dest_slot_constant);
+ if (ek_try <= _adapter_opt_return_LAST &&
+ ek_adapter_opt_return_slot(ek_try) == dest_slot_constant) {
+ ek_ret = ek_try;
+ }
+ }
+ assert(ek_adapter_opt_return_type(ek_ret) == dest, "");
+ }
+
+ // Already pushed: ... keep1 | collect | keep2 | sender_pc |
+ // push(sender_pc);
+
+ // Compute argument base:
+ Register rax_argv = rax_argslot;
+ __ lea(rax_argv, __ argument_address(constant(0)));
+
+ // Push a few extra argument words, if we need them to store the return value.
+ {
+ int extra_slots = 0;
+ if (retain_original_args) {
+ extra_slots = dest_count;
+ } else if (collect_count_constant == -1) {
+ extra_slots = dest_count; // collect_count might be zero; be generous
+ } else if (dest_count > collect_count_constant) {
+ extra_slots = (dest_count - collect_count_constant);
+ } else {
+ // else we know we have enough dead space in |collect| to repurpose for return values
+ }
+ DEBUG_ONLY(extra_slots += 1);
+ if (extra_slots > 0) {
+ __ pop(rbx_temp); // return value
+ __ subptr(rsp, (extra_slots * Interpreter::stackElementSize));
+ // Push guard word #2 in debug mode.
+ DEBUG_ONLY(__ movptr(Address(rsp, 0), (int32_t) RicochetFrame::MAGIC_NUMBER_2));
+ __ push(rbx_temp);
+ }
+ }
+
+ RicochetFrame::enter_ricochet_frame(_masm, rcx_recv, rax_argv,
+ entry(ek_ret)->from_interpreted_entry(), rbx_temp);
+
+ // Now pushed: ... keep1 | collect | keep2 | RF |
+ // some handy frame slots:
+ Address exact_sender_sp_addr = RicochetFrame::frame_address(RicochetFrame::exact_sender_sp_offset_in_bytes());
+ Address conversion_addr = RicochetFrame::frame_address(RicochetFrame::conversion_offset_in_bytes());
+ Address saved_args_base_addr = RicochetFrame::frame_address(RicochetFrame::saved_args_base_offset_in_bytes());
+
+#ifdef ASSERT
+ if (VerifyMethodHandles && dest != T_CONFLICT) {
+ BLOCK_COMMENT("verify AMH.conv.dest");
+ load_conversion_dest_type(_masm, rbx_temp, conversion_addr);
+ Label L_dest_ok;
+ __ cmpl(rbx_temp, (int) dest);
+ __ jcc(Assembler::equal, L_dest_ok);
+ if (dest == T_INT) {
+ for (int bt = T_BOOLEAN; bt < T_INT; bt++) {
+ if (is_subword_type(BasicType(bt))) {
+ __ cmpl(rbx_temp, (int) bt);
+ __ jcc(Assembler::equal, L_dest_ok);
+ }
+ }
+ }
+ __ stop("bad dest in AMH.conv");
+ __ BIND(L_dest_ok);
+ }
+#endif //ASSERT
+
+ // Find out where the original copy of the recursive argument sequence begins.
+ Register rax_coll = rax_argv;
+ {
+ RegisterOrConstant collect_slot = collect_slot_constant;
+ if (collect_slot_constant == -1) {
+ __ movl(rdi_temp, rcx_amh_vmargslot);
+ collect_slot = rdi_temp;
+ }
+ if (collect_slot_constant != 0)
+ __ lea(rax_coll, Address(rax_argv, collect_slot, Interpreter::stackElementScale()));
+ // rax_coll now points at the trailing edge of |collect| and leading edge of |keep2|
+ }
+
+ // Replace the old AMH with the recursive MH. (No going back now.)
+ // In the case of a boxing call, the recursive call is to a 'boxer' method,
+ // such as Integer.valueOf or Long.valueOf. In the case of a filter
+ // or collect call, it will take one or more arguments, transform them,
+ // and return some result, to store back into argument_base[vminfo].
+ __ load_heap_oop(rcx_recv, rcx_amh_argument);
+ if (VerifyMethodHandles) verify_method_handle(_masm, rcx_recv);
+
+ // Push a space for the recursively called MH first:
+ __ push((int32_t)NULL_WORD);
+
+ // Calculate |collect|, the number of arguments we are collecting.
+ Register rdi_collect_count = rdi_temp;
+ RegisterOrConstant collect_count;
+ if (collect_count_constant >= 0) {
+ collect_count = collect_count_constant;
+ } else {
+ __ load_method_handle_vmslots(rdi_collect_count, rcx_recv, rdx_temp);
+ collect_count = rdi_collect_count;
+ }
+#ifdef ASSERT
+ if (VerifyMethodHandles && collect_count_constant >= 0) {
+ __ load_method_handle_vmslots(rbx_temp, rcx_recv, rdx_temp);
+ Label L_count_ok;
+ __ cmpl(rbx_temp, collect_count_constant);
+ __ jcc(Assembler::equal, L_count_ok);
+ __ stop("bad vminfo in AMH.conv");
+ __ BIND(L_count_ok);
+ }
+#endif //ASSERT
+
+ // copy |collect| slots directly to TOS:
+ push_arg_slots(_masm, rax_coll, collect_count, 0, rbx_temp, rdx_temp);
+ // Now pushed: ... keep1 | collect | keep2 | RF... | collect |
+ // rax_coll still points at the trailing edge of |collect| and leading edge of |keep2|
+
+ // If necessary, adjust the saved arguments to make room for the eventual return value.
+ // Normal adjustment: ... keep1 | +dest+ | -collect- | keep2 | RF... | collect |
+ // If retaining args: ... keep1 | +dest+ | collect | keep2 | RF... | collect |
+ // In the non-retaining case, this might move keep2 either up or down.
+ // We don't have to copy the whole | RF... collect | complex,
+ // but we must adjust RF.saved_args_base.
+ // Also, from now on, we will forget about the origial copy of |collect|.
+ // If we are retaining it, we will treat it as part of |keep2|.
+ // For clarity we will define |keep3| = |collect|keep2| or |keep2|.
+
+ BLOCK_COMMENT("adjust trailing arguments {");
+ // Compare the sizes of |+dest+| and |-collect-|, which are opposed opening and closing movements.
+ int open_count = dest_count;
+ RegisterOrConstant close_count = collect_count_constant;
+ Register rdi_close_count = rdi_collect_count;
+ if (retain_original_args) {
+ close_count = constant(0);
+ } else if (collect_count_constant == -1) {
+ close_count = rdi_collect_count;
+ }
+
+ // How many slots need moving? This is simply dest_slot (0 => no |keep3|).
+ RegisterOrConstant keep3_count;
+ Register rsi_keep3_count = rsi; // can repair from RF.exact_sender_sp
+ if (dest_slot_constant >= 0) {
+ keep3_count = dest_slot_constant;
+ } else {
+ load_conversion_vminfo(_masm, rsi_keep3_count, conversion_addr);
+ keep3_count = rsi_keep3_count;
+ }
+#ifdef ASSERT
+ if (VerifyMethodHandles && dest_slot_constant >= 0) {
+ load_conversion_vminfo(_masm, rbx_temp, conversion_addr);
+ Label L_vminfo_ok;
+ __ cmpl(rbx_temp, dest_slot_constant);
+ __ jcc(Assembler::equal, L_vminfo_ok);
+ __ stop("bad vminfo in AMH.conv");
+ __ BIND(L_vminfo_ok);
+ }
+#endif //ASSERT
+
+ // tasks remaining:
+ bool move_keep3 = (!keep3_count.is_constant() || keep3_count.as_constant() != 0);
+ bool stomp_dest = (NOT_DEBUG(dest == T_OBJECT) DEBUG_ONLY(dest_count != 0));
+ bool fix_arg_base = (!close_count.is_constant() || open_count != close_count.as_constant());
+
+ if (stomp_dest | fix_arg_base) {
+ // we will probably need an updated rax_argv value
+ if (collect_slot_constant >= 0) {
+ // rax_coll already holds the leading edge of |keep2|, so tweak it
+ assert(rax_coll == rax_argv, "elided a move");
+ if (collect_slot_constant != 0)
+ __ subptr(rax_argv, collect_slot_constant * Interpreter::stackElementSize);
+ } else {
+ // Just reload from RF.saved_args_base.
+ __ movptr(rax_argv, saved_args_base_addr);
+ }
+ }
+
+ // Old and new argument locations (based at slot 0).
+ // Net shift (&new_argv - &old_argv) is (close_count - open_count).
+ bool zero_open_count = (open_count == 0); // remember this bit of info
+ if (move_keep3 && fix_arg_base) {
+ // It will be easier t have everything in one register:
+ if (close_count.is_register()) {
+ // Deduct open_count from close_count register to get a clean +/- value.
+ __ subptr(close_count.as_register(), open_count);
+ } else {
+ close_count = close_count.as_constant() - open_count;
+ }
+ open_count = 0;
+ }
+ Address old_argv(rax_argv, 0);
+ Address new_argv(rax_argv, close_count, Interpreter::stackElementScale(),
+ - open_count * Interpreter::stackElementSize);
+
+ // First decide if any actual data are to be moved.
+ // We can skip if (a) |keep3| is empty, or (b) the argument list size didn't change.
+ // (As it happens, all movements involve an argument list size change.)
+
+ // If there are variable parameters, use dynamic checks to skip around the whole mess.
+ Label L_done;
+ if (!keep3_count.is_constant()) {
+ __ testl(keep3_count.as_register(), keep3_count.as_register());
+ __ jcc(Assembler::zero, L_done);
+ }
+ if (!close_count.is_constant()) {
+ __ cmpl(close_count.as_register(), open_count);
+ __ jcc(Assembler::equal, L_done);
+ }
+
+ if (move_keep3 && fix_arg_base) {
+ bool emit_move_down = false, emit_move_up = false, emit_guard = false;
+ if (!close_count.is_constant()) {
+ emit_move_down = emit_guard = !zero_open_count;
+ emit_move_up = true;
+ } else if (open_count != close_count.as_constant()) {
+ emit_move_down = (open_count > close_count.as_constant());
+ emit_move_up = !emit_move_down;
+ }
+ Label L_move_up;
+ if (emit_guard) {
+ __ cmpl(close_count.as_register(), open_count);
+ __ jcc(Assembler::greater, L_move_up);
+ }
+
+ if (emit_move_down) {
+ // Move arguments down if |+dest+| > |-collect-|
+ // (This is rare, except when arguments are retained.)
+ // This opens space for the return value.
+ if (keep3_count.is_constant()) {
+ for (int i = 0; i < keep3_count.as_constant(); i++) {
+ __ movptr(rdx_temp, old_argv.plus_disp(i * Interpreter::stackElementSize));
+ __ movptr( new_argv.plus_disp(i * Interpreter::stackElementSize), rdx_temp);
+ }
+ } else {
+ Register rbx_argv_top = rbx_temp;
+ __ lea(rbx_argv_top, old_argv.plus_disp(keep3_count, Interpreter::stackElementScale()));
+ move_arg_slots_down(_masm,
+ old_argv, // beginning of old argv
+ rbx_argv_top, // end of old argv
+ close_count, // distance to move down (must be negative)
+ rax_argv, rdx_temp);
+ // Used argv as an iteration variable; reload from RF.saved_args_base.
+ __ movptr(rax_argv, saved_args_base_addr);
+ }
+ }
+
+ if (emit_guard) {
+ __ jmp(L_done); // assumes emit_move_up is true also
+ __ BIND(L_move_up);
+ }
+
+ if (emit_move_up) {
+
+ // Move arguments up if |+dest+| < |-collect-|
+ // (This is usual, except when |keep3| is empty.)
+ // This closes up the space occupied by the now-deleted collect values.
+ if (keep3_count.is_constant()) {
+ for (int i = keep3_count.as_constant() - 1; i >= 0; i--) {
+ __ movptr(rdx_temp, old_argv.plus_disp(i * Interpreter::stackElementSize));
+ __ movptr( new_argv.plus_disp(i * Interpreter::stackElementSize), rdx_temp);
+ }
+ } else {
+ Address argv_top = old_argv.plus_disp(keep3_count, Interpreter::stackElementScale());
+ move_arg_slots_up(_masm,
+ rax_argv, // beginning of old argv
+ argv_top, // end of old argv
+ close_count, // distance to move up (must be positive)
+ rbx_temp, rdx_temp);
+ }
+ }
+ }
+ __ BIND(L_done);
+
+ if (fix_arg_base) {
+ // adjust RF.saved_args_base by adding (close_count - open_count)
+ if (!new_argv.is_same_address(Address(rax_argv, 0)))
+ __ lea(rax_argv, new_argv);
+ __ movptr(saved_args_base_addr, rax_argv);
+ }
+
+ if (stomp_dest) {
+ // Stomp the return slot, so it doesn't hold garbage.
+ // This isn't strictly necessary, but it may help detect bugs.
+ int forty_two = RicochetFrame::RETURN_VALUE_PLACEHOLDER;
+ __ movptr(Address(rax_argv, keep3_count, Address::times_ptr),
+ (int32_t) forty_two);
+ // uses rsi_keep3_count
+ }
+ BLOCK_COMMENT("} adjust trailing arguments");
+
+ BLOCK_COMMENT("do_recursive_call");
+ __ mov(saved_last_sp, rsp); // set rsi/r13 for callee
+ __ pushptr(ExternalAddress(SharedRuntime::ricochet_blob()->bounce_addr()).addr());
+ // The globally unique bounce address has two purposes:
+ // 1. It helps the JVM recognize this frame (frame::is_ricochet_frame).
+ // 2. When returned to, it cuts back the stack and redirects control flow
+ // to the return handler.
+ // The return handler will further cut back the stack when it takes
+ // down the RF. Perhaps there is a way to streamline this further.
+
+ // State during recursive call:
+ // ... keep1 | dest | dest=42 | keep3 | RF... | collect | bounce_pc |
+ __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
+
+ break;
+ }
+
+ case _adapter_opt_return_ref:
+ case _adapter_opt_return_int:
+ case _adapter_opt_return_long:
+ case _adapter_opt_return_float:
+ case _adapter_opt_return_double:
+ case _adapter_opt_return_void:
+ case _adapter_opt_return_S0_ref:
+ case _adapter_opt_return_S1_ref:
+ case _adapter_opt_return_S2_ref:
+ case _adapter_opt_return_S3_ref:
+ case _adapter_opt_return_S4_ref:
+ case _adapter_opt_return_S5_ref:
+ {
+ BasicType dest_type_constant = ek_adapter_opt_return_type(ek);
+ int dest_slot_constant = ek_adapter_opt_return_slot(ek);
+
+ if (VerifyMethodHandles) RicochetFrame::verify_clean(_masm);
+
+ if (dest_slot_constant == -1) {
+ // The current stub is a general handler for this dest_type.
+ // It can be called from _adapter_opt_return_any below.
+ // Stash the address in a little table.
+ assert((dest_type_constant & CONV_TYPE_MASK) == dest_type_constant, "oob");
+ address return_handler = __ pc();
+ _adapter_return_handlers[dest_type_constant] = return_handler;
+ if (dest_type_constant == T_INT) {
+ // do the subword types too
+ for (int bt = T_BOOLEAN; bt < T_INT; bt++) {
+ if (is_subword_type(BasicType(bt)) &&
+ _adapter_return_handlers[bt] == NULL) {
+ _adapter_return_handlers[bt] = return_handler;
+ }
+ }
+ }
+ }
+
+ Register rbx_arg_base = rbx_temp;
+ assert_different_registers(rax, rdx, // possibly live return value registers
+ rdi_temp, rbx_arg_base);
+
+ Address conversion_addr = RicochetFrame::frame_address(RicochetFrame::conversion_offset_in_bytes());
+ Address saved_args_base_addr = RicochetFrame::frame_address(RicochetFrame::saved_args_base_offset_in_bytes());
+
+ __ movptr(rbx_arg_base, saved_args_base_addr);
+ RegisterOrConstant dest_slot = dest_slot_constant;
+ if (dest_slot_constant == -1) {
+ load_conversion_vminfo(_masm, rdi_temp, conversion_addr);
+ dest_slot = rdi_temp;
+ }
+ // Store the result back into the argslot.
+ // This code uses the interpreter calling sequence, in which the return value
+ // is usually left in the TOS register, as defined by InterpreterMacroAssembler::pop.
+ // There are certain irregularities with floating point values, which can be seen
+ // in TemplateInterpreterGenerator::generate_return_entry_for.
+ move_return_value(_masm, dest_type_constant, Address(rbx_arg_base, dest_slot, Interpreter::stackElementScale()));
+
+ RicochetFrame::leave_ricochet_frame(_masm, rcx_recv, rbx_arg_base, rdx_temp);
+ __ push(rdx_temp); // repush the return PC
+
+ // Load the final target and go.
+ if (VerifyMethodHandles) verify_method_handle(_masm, rcx_recv);
+ __ jump_to_method_handle_entry(rcx_recv, rdx_temp);
+ __ hlt(); // --------------------
+ break;
+ }
+
+ case _adapter_opt_return_any:
+ {
+ if (VerifyMethodHandles) RicochetFrame::verify_clean(_masm);
+ Register rdi_conv = rdi_temp;
+ assert_different_registers(rax, rdx, // possibly live return value registers
+ rdi_conv, rbx_temp);
+
+ Address conversion_addr = RicochetFrame::frame_address(RicochetFrame::conversion_offset_in_bytes());
+ load_conversion_dest_type(_masm, rdi_conv, conversion_addr);
+ __ lea(rbx_temp, ExternalAddress((address) &_adapter_return_handlers[0]));
+ __ movptr(rbx_temp, Address(rbx_temp, rdi_conv, Address::times_ptr));
+
+#ifdef ASSERT
+ { Label L_badconv;
+ __ testptr(rbx_temp, rbx_temp);
+ __ jccb(Assembler::zero, L_badconv);
+ __ jmp(rbx_temp);
+ __ bind(L_badconv);
+ __ stop("bad method handle return");
+ }
+#else //ASSERT
+ __ jmp(rbx_temp);
+#endif //ASSERT
+ break;
+ }
+
case _adapter_opt_spread_0:
- case _adapter_opt_spread_1:
- case _adapter_opt_spread_more:
+ case _adapter_opt_spread_1_ref:
+ case _adapter_opt_spread_2_ref:
+ case _adapter_opt_spread_3_ref:
+ case _adapter_opt_spread_4_ref:
+ case _adapter_opt_spread_5_ref:
+ case _adapter_opt_spread_ref:
+ case _adapter_opt_spread_byte:
+ case _adapter_opt_spread_char:
+ case _adapter_opt_spread_short:
+ case _adapter_opt_spread_int:
+ case _adapter_opt_spread_long:
+ case _adapter_opt_spread_float:
+ case _adapter_opt_spread_double:
{
// spread an array out into a group of arguments
- int length_constant = get_ek_adapter_opt_spread_info(ek);
+ int length_constant = ek_adapter_opt_spread_count(ek);
+ bool length_can_be_zero = (length_constant == 0);
+ if (length_constant < 0) {
+ // some adapters with variable length must handle the zero case
+ if (!OptimizeMethodHandles ||
+ ek_adapter_opt_spread_type(ek) != T_OBJECT)
+ length_can_be_zero = true;
+ }
// find the address of the array argument
__ movl(rax_argslot, rcx_amh_vmargslot);
__ lea(rax_argslot, __ argument_address(rax_argslot));
- // grab some temps
- { __ push(rsi); __ push(rdi); }
- // (preceding pushes must be done after argslot address is taken!)
-#define UNPUSH_RSI_RDI \
- { __ pop(rdi); __ pop(rsi); }
+ // grab another temp
+ Register rsi_temp = rsi;
+ { if (rsi_temp == saved_last_sp) __ push(saved_last_sp); }
+ // (preceding push must be done after argslot address is taken!)
+#define UNPUSH_RSI \
+ { if (rsi_temp == saved_last_sp) __ pop(saved_last_sp); }
// arx_argslot points both to the array and to the first output arg
vmarg = Address(rax_argslot, 0);
// Get the array value.
- Register rsi_array = rsi;
+ Register rsi_array = rsi_temp;
Register rdx_array_klass = rdx_temp;
- BasicType elem_type = T_OBJECT;
+ BasicType elem_type = ek_adapter_opt_spread_type(ek);
+ int elem_slots = type2size[elem_type]; // 1 or 2
+ int array_slots = 1; // array is always a T_OBJECT
int length_offset = arrayOopDesc::length_offset_in_bytes();
int elem0_offset = arrayOopDesc::base_offset_in_bytes(elem_type);
__ movptr(rsi_array, vmarg);
- Label skip_array_check;
- if (length_constant == 0) {
+
+ Label L_array_is_empty, L_insert_arg_space, L_copy_args, L_args_done;
+ if (length_can_be_zero) {
+ // handle the null pointer case, if zero is allowed
+ Label L_skip;
+ if (length_constant < 0) {
+ load_conversion_vminfo(_masm, rbx_temp, rcx_amh_conversion);
+ __ testl(rbx_temp, rbx_temp);
+ __ jcc(Assembler::notZero, L_skip);
+ }
__ testptr(rsi_array, rsi_array);
- __ jcc(Assembler::zero, skip_array_check);
+ __ jcc(Assembler::zero, L_array_is_empty);
+ __ bind(L_skip);
}
__ null_check(rsi_array, oopDesc::klass_offset_in_bytes());
__ load_klass(rdx_array_klass, rsi_array);
@@ -1081,22 +2272,20 @@
// Check the array type.
Register rbx_klass = rbx_temp;
__ load_heap_oop(rbx_klass, rcx_amh_argument); // this is a Class object!
- __ load_heap_oop(rbx_klass, Address(rbx_klass, java_lang_Class::klass_offset_in_bytes()));
+ load_klass_from_Class(_masm, rbx_klass);
Label ok_array_klass, bad_array_klass, bad_array_length;
- __ check_klass_subtype(rdx_array_klass, rbx_klass, rdi, ok_array_klass);
+ __ check_klass_subtype(rdx_array_klass, rbx_klass, rdi_temp, ok_array_klass);
// If we get here, the type check failed!
__ jmp(bad_array_klass);
- __ bind(ok_array_klass);
+ __ BIND(ok_array_klass);
// Check length.
if (length_constant >= 0) {
__ cmpl(Address(rsi_array, length_offset), length_constant);
} else {
Register rbx_vminfo = rbx_temp;
- __ movl(rbx_vminfo, rcx_amh_conversion);
- assert(CONV_VMINFO_SHIFT == 0, "preshifted");
- __ andl(rbx_vminfo, CONV_VMINFO_MASK);
+ load_conversion_vminfo(_masm, rbx_vminfo, rcx_amh_conversion);
__ cmpl(rbx_vminfo, Address(rsi_array, length_offset));
}
__ jcc(Assembler::notEqual, bad_array_length);
@@ -1108,90 +2297,104 @@
// Form a pointer to the end of the affected region.
__ lea(rdx_argslot_limit, Address(rax_argslot, Interpreter::stackElementSize));
// 'stack_move' is negative number of words to insert
- Register rdi_stack_move = rdi;
- __ movl2ptr(rdi_stack_move, rcx_amh_conversion);
- __ sarptr(rdi_stack_move, CONV_STACK_MOVE_SHIFT);
+ // This number already accounts for elem_slots.
+ Register rdi_stack_move = rdi_temp;
+ load_stack_move(_masm, rdi_stack_move, rcx_recv, true);
+ __ cmpptr(rdi_stack_move, 0);
+ assert(stack_move_unit() < 0, "else change this comparison");
+ __ jcc(Assembler::less, L_insert_arg_space);
+ __ jcc(Assembler::equal, L_copy_args);
+ // single argument case, with no array movement
+ __ BIND(L_array_is_empty);
+ remove_arg_slots(_masm, -stack_move_unit() * array_slots,
+ rax_argslot, rbx_temp, rdx_temp);
+ __ jmp(L_args_done); // no spreading to do
+ __ BIND(L_insert_arg_space);
+ // come here in the usual case, stack_move < 0 (2 or more spread arguments)
Register rsi_temp = rsi_array; // spill this
- insert_arg_slots(_masm, rdi_stack_move, -1,
+ insert_arg_slots(_masm, rdi_stack_move,
rax_argslot, rbx_temp, rsi_temp);
- // reload the array (since rsi was killed)
- __ movptr(rsi_array, vmarg);
- } else if (length_constant > 1) {
- int arg_mask = 0;
- int new_slots = (length_constant - 1);
- for (int i = 0; i < new_slots; i++) {
- arg_mask <<= 1;
- arg_mask |= _INSERT_REF_MASK;
- }
- insert_arg_slots(_masm, new_slots * stack_move_unit(), arg_mask,
+ // reload the array since rsi was killed
+ // reload from rdx_argslot_limit since rax_argslot is now decremented
+ __ movptr(rsi_array, Address(rdx_argslot_limit, -Interpreter::stackElementSize));
+ } else if (length_constant >= 1) {
+ int new_slots = (length_constant * elem_slots) - array_slots;
+ insert_arg_slots(_masm, new_slots * stack_move_unit(),
rax_argslot, rbx_temp, rdx_temp);
- } else if (length_constant == 1) {
- // no stack resizing required
} else if (length_constant == 0) {
- remove_arg_slots(_masm, -stack_move_unit(),
+ __ BIND(L_array_is_empty);
+ remove_arg_slots(_masm, -stack_move_unit() * array_slots,
rax_argslot, rbx_temp, rdx_temp);
+ } else {
+ ShouldNotReachHere();
}
// Copy from the array to the new slots.
// Note: Stack change code preserves integrity of rax_argslot pointer.
// So even after slot insertions, rax_argslot still points to first argument.
+ // Beware: Arguments that are shallow on the stack are deep in the array,
+ // and vice versa. So a downward-growing stack (the usual) has to be copied
+ // elementwise in reverse order from the source array.
+ __ BIND(L_copy_args);
if (length_constant == -1) {
// [rax_argslot, rdx_argslot_limit) is the area we are inserting into.
+ // Array element [0] goes at rdx_argslot_limit[-wordSize].
Register rsi_source = rsi_array;
__ lea(rsi_source, Address(rsi_array, elem0_offset));
+ Register rdx_fill_ptr = rdx_argslot_limit;
Label loop;
- __ bind(loop);
- __ movptr(rbx_temp, Address(rsi_source, 0));
- __ movptr(Address(rax_argslot, 0), rbx_temp);
+ __ BIND(loop);
+ __ addptr(rdx_fill_ptr, -Interpreter::stackElementSize * elem_slots);
+ move_typed_arg(_masm, elem_type, true,
+ Address(rdx_fill_ptr, 0), Address(rsi_source, 0),
+ rbx_temp, rdi_temp);
__ addptr(rsi_source, type2aelembytes(elem_type));
- __ addptr(rax_argslot, Interpreter::stackElementSize);
- __ cmpptr(rax_argslot, rdx_argslot_limit);
- __ jccb(Assembler::less, loop);
+ __ cmpptr(rdx_fill_ptr, rax_argslot);
+ __ jcc(Assembler::above, loop);
} else if (length_constant == 0) {
- __ bind(skip_array_check);
// nothing to copy
} else {
int elem_offset = elem0_offset;
- int slot_offset = 0;
+ int slot_offset = length_constant * Interpreter::stackElementSize;
for (int index = 0; index < length_constant; index++) {
- __ movptr(rbx_temp, Address(rsi_array, elem_offset));
- __ movptr(Address(rax_argslot, slot_offset), rbx_temp);
+ slot_offset -= Interpreter::stackElementSize * elem_slots; // fill backward
+ move_typed_arg(_masm, elem_type, true,
+ Address(rax_argslot, slot_offset), Address(rsi_array, elem_offset),
+ rbx_temp, rdi_temp);
elem_offset += type2aelembytes(elem_type);
- slot_offset += Interpreter::stackElementSize;
}
}
+ __ BIND(L_args_done);
// Arguments are spread. Move to next method handle.
- UNPUSH_RSI_RDI;
+ UNPUSH_RSI;
__ load_heap_oop(rcx_recv, rcx_mh_vmtarget);
__ jump_to_method_handle_entry(rcx_recv, rdx_temp);
__ bind(bad_array_klass);
- UNPUSH_RSI_RDI;
+ UNPUSH_RSI;
assert(!vmarg.uses(rarg2_required), "must be different registers");
- __ movptr(rarg2_required, Address(rdx_array_klass, java_mirror_offset)); // required type
- __ movptr(rarg1_actual, vmarg); // bad array
- __ movl( rarg0_code, (int) Bytecodes::_aaload); // who is complaining?
+ __ load_heap_oop( rarg2_required, Address(rdx_array_klass, java_mirror_offset)); // required type
+ __ movptr( rarg1_actual, vmarg); // bad array
+ __ movl( rarg0_code, (int) Bytecodes::_aaload); // who is complaining?
__ jump(ExternalAddress(from_interpreted_entry(_raise_exception)));
__ bind(bad_array_length);
- UNPUSH_RSI_RDI;
+ UNPUSH_RSI;
assert(!vmarg.uses(rarg2_required), "must be different registers");
- __ mov (rarg2_required, rcx_recv); // AMH requiring a certain length
- __ movptr(rarg1_actual, vmarg); // bad array
- __ movl( rarg0_code, (int) Bytecodes::_arraylength); // who is complaining?
+ __ mov( rarg2_required, rcx_recv); // AMH requiring a certain length
+ __ movptr( rarg1_actual, vmarg); // bad array
+ __ movl( rarg0_code, (int) Bytecodes::_arraylength); // who is complaining?
__ jump(ExternalAddress(from_interpreted_entry(_raise_exception)));
+#undef UNPUSH_RSI
-#undef UNPUSH_RSI_RDI
+ break;
}
- break;
- case _adapter_flyby:
- case _adapter_ricochet:
- __ unimplemented(entry_name(ek)); // %%% FIXME: NYI
- break;
-
- default: ShouldNotReachHere();
+ default:
+ // do not require all platforms to recognize all adapter types
+ __ nop();
+ return;
}
__ hlt();
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/x86/vm/methodHandles_x86.hpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/cpu/x86/vm/methodHandles_x86.hpp Wed Jul 05 17:44:49 2017 +0200
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2010, 2011, 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.
+ *
+ */
+
+// Platform-specific definitions for method handles.
+// These definitions are inlined into class MethodHandles.
+
+public:
+
+// The stack just after the recursive call from a ricochet frame
+// looks something like this. Offsets are marked in words, not bytes.
+// rsi (r13 on LP64) is part of the interpreter calling sequence
+// which tells the callee where is my real rsp (for frame walking).
+// (...lower memory addresses)
+// rsp: [ return pc ] always the global RicochetBlob::bounce_addr
+// rsp+1: [ recursive arg N ]
+// rsp+2: [ recursive arg N-1 ]
+// ...
+// rsp+N: [ recursive arg 1 ]
+// rsp+N+1: [ recursive method handle ]
+// ...
+// rbp-6: [ cleanup continuation pc ] <-- (struct RicochetFrame)
+// rbp-5: [ saved target MH ] the MH we will call on the saved args
+// rbp-4: [ saved args layout oop ] an int[] array which describes argument layout
+// rbp-3: [ saved args pointer ] address of transformed adapter arg M (slot 0)
+// rbp-2: [ conversion ] information about how the return value is used
+// rbp-1: [ exact sender sp ] exact TOS (rsi/r13) of original sender frame
+// rbp+0: [ saved sender fp ] (for original sender of AMH)
+// rbp+1: [ saved sender pc ] (back to original sender of AMH)
+// rbp+2: [ transformed adapter arg M ] <-- (extended TOS of original sender)
+// rbp+3: [ transformed adapter arg M-1]
+// ...
+// rbp+M+1: [ transformed adapter arg 1 ]
+// rbp+M+2: [ padding ] <-- (rbp + saved args base offset)
+// ... [ optional padding]
+// (higher memory addresses...)
+//
+// The arguments originally passed by the original sender
+// are lost, and arbitrary amounts of stack motion might have
+// happened due to argument transformation.
+// (This is done by C2I/I2C adapters and non-direct method handles.)
+// This is why there is an unpredictable amount of memory between
+// the extended and exact TOS of the sender.
+// The ricochet adapter itself will also (in general) perform
+// transformations before the recursive call.
+//
+// The transformed and saved arguments, immediately above the saved
+// return PC, are a well-formed method handle invocation ready to execute.
+// When the GC needs to walk the stack, these arguments are described
+// via the saved arg types oop, an int[] array with a private format.
+// This array is derived from the type of the transformed adapter
+// method handle, which also sits at the base of the saved argument
+// bundle. Since the GC may not be able to fish out the int[]
+// array, so it is pushed explicitly on the stack. This may be
+// an unnecessary expense.
+//
+// The following register conventions are significant at this point:
+// rsp the thread stack, as always; preserved by caller
+// rsi/r13 exact TOS of recursive frame (contents of [rbp-2])
+// rcx recursive method handle (contents of [rsp+N+1])
+// rbp preserved by caller (not used by caller)
+// Unless otherwise specified, all registers can be blown by the call.
+//
+// If this frame must be walked, the transformed adapter arguments
+// will be found with the help of the saved arguments descriptor.
+//
+// Therefore, the descriptor must match the referenced arguments.
+// The arguments must be followed by at least one word of padding,
+// which will be necessary to complete the final method handle call.
+// That word is not treated as holding an oop. Neither is the word
+//
+// The word pointed to by the return argument pointer is not
+// treated as an oop, even if points to a saved argument.
+// This allows the saved argument list to have a "hole" in it
+// to receive an oop from the recursive call.
+// (The hole might temporarily contain RETURN_VALUE_PLACEHOLDER.)
+//
+// When the recursive callee returns, RicochetBlob::bounce_addr will
+// immediately jump to the continuation stored in the RF.
+// This continuation will merge the recursive return value
+// into the saved argument list. At that point, the original
+// rsi, rbp, and rsp will be reloaded, the ricochet frame will
+// disappear, and the final target of the adapter method handle
+// will be invoked on the transformed argument list.
+
+class RicochetFrame {
+ friend class MethodHandles;
+
+ private:
+ intptr_t* _continuation; // what to do when control gets back here
+ oopDesc* _saved_target; // target method handle to invoke on saved_args
+ oopDesc* _saved_args_layout; // caching point for MethodTypeForm.vmlayout cookie
+ intptr_t* _saved_args_base; // base of pushed arguments (slot 0, arg N) (-3)
+ intptr_t _conversion; // misc. information from original AdapterMethodHandle (-2)
+ intptr_t* _exact_sender_sp; // parallel to interpreter_frame_sender_sp (-1)
+ intptr_t* _sender_link; // *must* coincide with frame::link_offset (0)
+ address _sender_pc; // *must* coincide with frame::return_addr_offset (1)
+
+ public:
+ intptr_t* continuation() const { return _continuation; }
+ oop saved_target() const { return _saved_target; }
+ oop saved_args_layout() const { return _saved_args_layout; }
+ intptr_t* saved_args_base() const { return _saved_args_base; }
+ intptr_t conversion() const { return _conversion; }
+ intptr_t* exact_sender_sp() const { return _exact_sender_sp; }
+ intptr_t* sender_link() const { return _sender_link; }
+ address sender_pc() const { return _sender_pc; }
+
+ intptr_t* extended_sender_sp() const { return saved_args_base(); }
+
+ intptr_t return_value_slot_number() const {
+ return adapter_conversion_vminfo(conversion());
+ }
+ BasicType return_value_type() const {
+ return adapter_conversion_dest_type(conversion());
+ }
+ bool has_return_value_slot() const {
+ return return_value_type() != T_VOID;
+ }
+ intptr_t* return_value_slot_addr() const {
+ assert(has_return_value_slot(), "");
+ return saved_arg_slot_addr(return_value_slot_number());
+ }
+ intptr_t* saved_target_slot_addr() const {
+ return saved_arg_slot_addr(saved_args_length());
+ }
+ intptr_t* saved_arg_slot_addr(int slot) const {
+ assert(slot >= 0, "");
+ return (intptr_t*)( (address)saved_args_base() + (slot * Interpreter::stackElementSize) );
+ }
+
+ jint saved_args_length() const;
+ jint saved_arg_offset(int arg) const;
+
+ // GC interface
+ oop* saved_target_addr() { return (oop*)&_saved_target; }
+ oop* saved_args_layout_addr() { return (oop*)&_saved_args_layout; }
+
+ oop compute_saved_args_layout(bool read_cache, bool write_cache);
+
+ // Compiler/assembler interface.
+ static int continuation_offset_in_bytes() { return offset_of(RicochetFrame, _continuation); }
+ static int saved_target_offset_in_bytes() { return offset_of(RicochetFrame, _saved_target); }
+ static int saved_args_layout_offset_in_bytes(){ return offset_of(RicochetFrame, _saved_args_layout); }
+ static int saved_args_base_offset_in_bytes() { return offset_of(RicochetFrame, _saved_args_base); }
+ static int conversion_offset_in_bytes() { return offset_of(RicochetFrame, _conversion); }
+ static int exact_sender_sp_offset_in_bytes() { return offset_of(RicochetFrame, _exact_sender_sp); }
+ static int sender_link_offset_in_bytes() { return offset_of(RicochetFrame, _sender_link); }
+ static int sender_pc_offset_in_bytes() { return offset_of(RicochetFrame, _sender_pc); }
+
+ // This value is not used for much, but it apparently must be nonzero.
+ static int frame_size_in_bytes() { return sender_link_offset_in_bytes(); }
+
+#ifdef ASSERT
+ // The magic number is supposed to help find ricochet frames within the bytes of stack dumps.
+ enum { MAGIC_NUMBER_1 = 0xFEED03E, MAGIC_NUMBER_2 = 0xBEEF03E };
+ static int magic_number_1_offset_in_bytes() { return -wordSize; }
+ static int magic_number_2_offset_in_bytes() { return sizeof(RicochetFrame); }
+ intptr_t magic_number_1() const { return *(intptr_t*)((address)this + magic_number_1_offset_in_bytes()); };
+ intptr_t magic_number_2() const { return *(intptr_t*)((address)this + magic_number_2_offset_in_bytes()); };
+#endif //ASSERT
+
+ enum { RETURN_VALUE_PLACEHOLDER = (NOT_DEBUG(0) DEBUG_ONLY(42)) };
+
+ static void verify_offsets() NOT_DEBUG_RETURN;
+ void verify() const NOT_DEBUG_RETURN; // check for MAGIC_NUMBER, etc.
+ void zap_arguments() NOT_DEBUG_RETURN;
+
+ static void generate_ricochet_blob(MacroAssembler* _masm,
+ // output params:
+ int* frame_size_in_words, int* bounce_offset, int* exception_offset);
+
+ static void enter_ricochet_frame(MacroAssembler* _masm,
+ Register rcx_recv,
+ Register rax_argv,
+ address return_handler,
+ Register rbx_temp);
+ static void leave_ricochet_frame(MacroAssembler* _masm,
+ Register rcx_recv,
+ Register new_sp_reg,
+ Register sender_pc_reg);
+
+ static Address frame_address(int offset = 0) {
+ // The RicochetFrame is found by subtracting a constant offset from rbp.
+ return Address(rbp, - sender_link_offset_in_bytes() + offset);
+ }
+
+ static RicochetFrame* from_frame(const frame& fr) {
+ address bp = (address) fr.fp();
+ RicochetFrame* rf = (RicochetFrame*)(bp - sender_link_offset_in_bytes());
+ rf->verify();
+ return rf;
+ }
+
+ static void verify_clean(MacroAssembler* _masm) NOT_DEBUG_RETURN;
+};
+
+// Additional helper methods for MethodHandles code generation:
+public:
+ static void load_klass_from_Class(MacroAssembler* _masm, Register klass_reg);
+ static void load_conversion_vminfo(MacroAssembler* _masm, Register reg, Address conversion_field_addr);
+ static void load_conversion_dest_type(MacroAssembler* _masm, Register reg, Address conversion_field_addr);
+
+ static void load_stack_move(MacroAssembler* _masm,
+ Register rdi_stack_move,
+ Register rcx_amh,
+ bool might_be_negative);
+
+ static void insert_arg_slots(MacroAssembler* _masm,
+ RegisterOrConstant arg_slots,
+ Register rax_argslot,
+ Register rbx_temp, Register rdx_temp);
+
+ static void remove_arg_slots(MacroAssembler* _masm,
+ RegisterOrConstant arg_slots,
+ Register rax_argslot,
+ Register rbx_temp, Register rdx_temp);
+
+ static void push_arg_slots(MacroAssembler* _masm,
+ Register rax_argslot,
+ RegisterOrConstant slot_count,
+ int skip_words_count,
+ Register rbx_temp, Register rdx_temp);
+
+ static void move_arg_slots_up(MacroAssembler* _masm,
+ Register rbx_bottom, // invariant
+ Address top_addr, // can use rax_temp
+ RegisterOrConstant positive_distance_in_slots,
+ Register rax_temp, Register rdx_temp);
+
+ static void move_arg_slots_down(MacroAssembler* _masm,
+ Address bottom_addr, // can use rax_temp
+ Register rbx_top, // invariant
+ RegisterOrConstant negative_distance_in_slots,
+ Register rax_temp, Register rdx_temp);
+
+ static void move_typed_arg(MacroAssembler* _masm,
+ BasicType type, bool is_element,
+ Address slot_dest, Address value_src,
+ Register rbx_temp, Register rdx_temp);
+
+ static void move_return_value(MacroAssembler* _masm, BasicType type,
+ Address return_slot);
+
+ static void verify_argslot(MacroAssembler* _masm, Register argslot_reg,
+ const char* error_message) NOT_DEBUG_RETURN;
+
+ static void verify_argslots(MacroAssembler* _masm,
+ RegisterOrConstant argslot_count,
+ Register argslot_reg,
+ bool negate_argslot,
+ const char* error_message) NOT_DEBUG_RETURN;
+
+ static void verify_stack_move(MacroAssembler* _masm,
+ RegisterOrConstant arg_slots,
+ int direction) NOT_DEBUG_RETURN;
+
+ static void verify_klass(MacroAssembler* _masm,
+ Register obj, KlassHandle klass,
+ const char* error_message = "wrong klass") NOT_DEBUG_RETURN;
+
+ static void verify_method_handle(MacroAssembler* _masm, Register mh_reg) {
+ verify_klass(_masm, mh_reg, SystemDictionaryHandles::MethodHandle_klass(),
+ "reference is a MH");
+ }
+
+ static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN;
+
+ static Register saved_last_sp_register() {
+ // Should be in sharedRuntime, not here.
+ return LP64_ONLY(r13) NOT_LP64(rsi);
+ }
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp
--- a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -2253,6 +2253,31 @@
return 0;
}
+//----------------------------generate_ricochet_blob---------------------------
+void SharedRuntime::generate_ricochet_blob() {
+ if (!EnableInvokeDynamic) return; // leave it as a null
+
+ // allocate space for the code
+ ResourceMark rm;
+ // setup code generation tools
+ CodeBuffer buffer("ricochet_blob", 256, 256);
+ MacroAssembler* masm = new MacroAssembler(&buffer);
+
+ int frame_size_in_words = -1, bounce_offset = -1, exception_offset = -1;
+ MethodHandles::RicochetFrame::generate_ricochet_blob(masm, &frame_size_in_words, &bounce_offset, &exception_offset);
+
+ // -------------
+ // make sure all code is generated
+ masm->flush();
+
+ // failed to generate?
+ if (frame_size_in_words < 0 || bounce_offset < 0 || exception_offset < 0) {
+ assert(false, "bad ricochet blob");
+ return;
+ }
+
+ _ricochet_blob = RicochetBlob::create(&buffer, bounce_offset, exception_offset, frame_size_in_words);
+}
//------------------------------generate_deopt_blob----------------------------
void SharedRuntime::generate_deopt_blob() {
@@ -2996,6 +3021,8 @@
generate_handler_blob(CAST_FROM_FN_PTR(address,
SafepointSynchronize::handle_polling_page_exception), true);
+ generate_ricochet_blob();
+
generate_deopt_blob();
#ifdef COMPILER2
generate_uncommon_trap_blob();
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp
--- a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -2530,6 +2530,32 @@
}
+//----------------------------generate_ricochet_blob---------------------------
+void SharedRuntime::generate_ricochet_blob() {
+ if (!EnableInvokeDynamic) return; // leave it as a null
+
+ // allocate space for the code
+ ResourceMark rm;
+ // setup code generation tools
+ CodeBuffer buffer("ricochet_blob", 512, 512);
+ MacroAssembler* masm = new MacroAssembler(&buffer);
+
+ int frame_size_in_words = -1, bounce_offset = -1, exception_offset = -1;
+ MethodHandles::RicochetFrame::generate_ricochet_blob(masm, &frame_size_in_words, &bounce_offset, &exception_offset);
+
+ // -------------
+ // make sure all code is generated
+ masm->flush();
+
+ // failed to generate?
+ if (frame_size_in_words < 0 || bounce_offset < 0 || exception_offset < 0) {
+ assert(false, "bad ricochet blob");
+ return;
+ }
+
+ _ricochet_blob = RicochetBlob::create(&buffer, bounce_offset, exception_offset, frame_size_in_words);
+}
+
//------------------------------generate_deopt_blob----------------------------
void SharedRuntime::generate_deopt_blob() {
// Allocate space for the code
@@ -3205,6 +3231,8 @@
generate_handler_blob(CAST_FROM_FN_PTR(address,
SafepointSynchronize::handle_polling_page_exception), true);
+ generate_ricochet_blob();
+
generate_deopt_blob();
#ifdef COMPILER2
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp
--- a/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp Wed Jul 05 17:44:49 2017 +0200
@@ -36,7 +36,7 @@
// MethodHandles adapters
enum method_handles_platform_dependent_constants {
- method_handles_adapters_code_size = 10000
+ method_handles_adapters_code_size = 30000 DEBUG_ONLY(+ 10000)
};
class x86 {
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp
--- a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp Wed Jul 05 17:44:49 2017 +0200
@@ -38,7 +38,7 @@
// MethodHandles adapters
enum method_handles_platform_dependent_constants {
- method_handles_adapters_code_size = 40000
+ method_handles_adapters_code_size = 80000 DEBUG_ONLY(+ 120000)
};
class x86 {
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp
--- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -1589,6 +1589,7 @@
int tempcount,
int popframe_extra_args,
int moncount,
+ int caller_actual_parameters,
int callee_param_count,
int callee_locals,
frame* caller,
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp
--- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -1603,6 +1603,7 @@
int tempcount,
int popframe_extra_args,
int moncount,
+ int caller_actual_parameters,
int callee_param_count,
int callee_locals,
frame* caller,
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp
--- a/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -1427,6 +1427,7 @@
int tempcount,
int popframe_extra_args,
int moncount,
+ int caller_actual_parameters,
int callee_param_count,
int callee_locals,
frame* caller,
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/cpu/zero/vm/interpreter_zero.cpp
--- a/hotspot/src/cpu/zero/vm/interpreter_zero.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/cpu/zero/vm/interpreter_zero.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -82,24 +82,6 @@
return true;
}
-int AbstractInterpreter::size_activation(methodOop method,
- int tempcount,
- int popframe_extra_args,
- int moncount,
- int callee_param_count,
- int callee_locals,
- bool is_top_frame) {
- return layout_activation(method,
- tempcount,
- popframe_extra_args,
- moncount,
- callee_param_count,
- callee_locals,
- (frame*) NULL,
- (frame*) NULL,
- is_top_frame);
-}
-
void Deoptimization::unwind_callee_save_values(frame* f,
vframeArray* vframe_array) {
}
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/os/linux/vm/os_linux.cpp
--- a/hotspot/src/os/linux/vm/os_linux.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/os/linux/vm/os_linux.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -2850,7 +2850,7 @@
char chars[257];
long x = 0;
if (fgets(chars, sizeof(chars), fp)) {
- if (sscanf(chars, "%lx-%*lx", &x) == 1
+ if (sscanf(chars, "%lx-%*x", &x) == 1
&& x == (long)p) {
if (strstr (chars, "hugepage")) {
result = true;
diff -r 9fcad86579e6 -r 0dc08749a6cc hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp
--- a/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp Sat May 14 10:24:02 2011 -0700
+++ b/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp Wed Jul 05 17:44:49 2017 +0200
@@ -132,17 +132,22 @@
if (value->is_null_object()) {
output()->print("null");
} else if (!value->is_loaded()) {
- output()->print("", value);
+ output()->print("", value);
} else if (value->is_method()) {
ciMethod* m = (ciMethod*)value;
output()->print("", m->holder()->name()->as_utf8(), m->name()->as_utf8());
} else {
- output()->print("
*
- *
The {@link StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} option should be
+ *
The {@link StandardSocketOptions#SO_REUSEADDR SO_REUSEADDR} option should be
* enabled prior to {@link NetworkChannel#bind binding} the socket. This is
* required to allow multiple members of the group to bind to the same
* address.
*
diff -r 9fcad86579e6 -r 0dc08749a6cc jdk/src/share/classes/java/nio/charset/Charset.java
--- a/jdk/src/share/classes/java/nio/charset/Charset.java Sat May 14 10:24:02 2011 -0700
+++ b/jdk/src/share/classes/java/nio/charset/Charset.java Wed Jul 05 17:44:49 2017 +0200
@@ -215,7 +215,7 @@
* determined during virtual-machine startup and typically depends upon the
* locale and charset being used by the underlying operating system.
*
- *
The {@link StandardCharset} class defines constants for each of the
+ *
The {@link StandardCharsets} class defines constants for each of the
* standard charsets.
*
*
Terminology
diff -r 9fcad86579e6 -r 0dc08749a6cc jdk/src/share/classes/java/nio/charset/StandardCharset.java
--- a/jdk/src/share/classes/java/nio/charset/StandardCharset.java Sat May 14 10:24:02 2011 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2011, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.
- */
-package java.nio.charset;
-
-/**
- * Constant definitions for the standard {@link Charset Charsets}. These
- * charsets are guaranteed to be available on every implementation of the Java
- * platform.
- *
- * @see Standard Charsets
- * @since 1.7
- */
-public final class StandardCharset {
-
- private StandardCharset() {
- throw new AssertionError("No java.nio.charset.StandardCharset instances for you!");
- }
- /**
- * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the
- * Unicode character set
- */
- public static final Charset US_ASCII = Charset.forName("US-ASCII");
- /**
- * ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1
- */
- public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
- /**
- * Eight-bit UCS Transformation Format
- */
- public static final Charset UTF_8 = Charset.forName("UTF-8");
- /**
- * Sixteen-bit UCS Transformation Format, big-endian byte order
- */
- public static final Charset UTF_16BE = Charset.forName("UTF-16BE");
- /**
- * Sixteen-bit UCS Transformation Format, little-endian byte order
- */
- public static final Charset UTF_16LE = Charset.forName("UTF-16LE");
- /**
- * Sixteen-bit UCS Transformation Format, byte order identified by an
- * optional byte-order mark
- */
- public static final Charset UTF_16 = Charset.forName("UTF-16");
-}
diff -r 9fcad86579e6 -r 0dc08749a6cc jdk/src/share/classes/java/nio/charset/StandardCharsets.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/nio/charset/StandardCharsets.java Wed Jul 05 17:44:49 2017 +0200
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2011, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+package java.nio.charset;
+
+/**
+ * Constant definitions for the standard {@link Charset Charsets}. These
+ * charsets are guaranteed to be available on every implementation of the Java
+ * platform.
+ *
+ * @see Standard Charsets
+ * @since 1.7
+ */
+public final class StandardCharsets {
+
+ private StandardCharsets() {
+ throw new AssertionError("No java.nio.charset.StandardCharsets instances for you!");
+ }
+ /**
+ * Seven-bit ASCII, a.k.a. ISO646-US, a.k.a. the Basic Latin block of the
+ * Unicode character set
+ */
+ public static final Charset US_ASCII = Charset.forName("US-ASCII");
+ /**
+ * ISO Latin Alphabet No. 1, a.k.a. ISO-LATIN-1
+ */
+ public static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
+ /**
+ * Eight-bit UCS Transformation Format
+ */
+ public static final Charset UTF_8 = Charset.forName("UTF-8");
+ /**
+ * Sixteen-bit UCS Transformation Format, big-endian byte order
+ */
+ public static final Charset UTF_16BE = Charset.forName("UTF-16BE");
+ /**
+ * Sixteen-bit UCS Transformation Format, little-endian byte order
+ */
+ public static final Charset UTF_16LE = Charset.forName("UTF-16LE");
+ /**
+ * Sixteen-bit UCS Transformation Format, byte order identified by an
+ * optional byte-order mark
+ */
+ public static final Charset UTF_16 = Charset.forName("UTF-16");
+}
diff -r 9fcad86579e6 -r 0dc08749a6cc jdk/src/share/classes/java/nio/file/Path.java
--- a/jdk/src/share/classes/java/nio/file/Path.java Sat May 14 10:24:02 2011 -0700
+++ b/jdk/src/share/classes/java/nio/file/Path.java Wed Jul 05 17:44:49 2017 +0200
@@ -72,7 +72,7 @@
* directory and is UTF-8 encoded.
*
{@link StandardWatchEventKinds#ENTRY_MODIFY ENTRY_MODIFY} -
* entry in directory was modified
*
*
@@ -622,7 +622,7 @@
* that locates the directory entry that is created, deleted, or modified.
*
*
The set of events may include additional implementation specific
- * event that are not defined by the enum {@link StandardWatchEventKind}
+ * event that are not defined by the enum {@link StandardWatchEventKinds}
*
*
The {@code modifiers} parameter specifies modifiers that
* qualify how the directory is registered. This release does not define any
diff -r 9fcad86579e6 -r 0dc08749a6cc jdk/src/share/classes/java/nio/file/StandardWatchEventKind.java
--- a/jdk/src/share/classes/java/nio/file/StandardWatchEventKind.java Sat May 14 10:24:02 2011 -0700
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +0,0 @@
-/*
- * Copyright (c) 2007, 2009, 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. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.
- */
-
-package java.nio.file;
-
-/**
- * Defines the standard event kinds.
- *
- * @since 1.7
- */
-
-public final class StandardWatchEventKind {
- private StandardWatchEventKind() { }
-
- /**
- * A special event to indicate that events may have been lost or
- * discarded.
- *
- *
The {@link WatchEvent#context context} for this event is
- * implementation specific and may be {@code null}. The event {@link
- * WatchEvent#count count} may be greater than {@code 1}.
- *
- * @see WatchService
- */
- public static final WatchEvent.Kind OVERFLOW =
- new StdWatchEventKind("OVERFLOW", Void.class);
-
- /**
- * Directory entry created.
- *
- *
When a directory is registered for this event then the {@link WatchKey}
- * is queued when it is observed that an entry is created in the directory
- * or renamed into the directory. The event {@link WatchEvent#count count}
- * for this event is always {@code 1}.
- */
- public static final WatchEvent.Kind ENTRY_CREATE =
- new StdWatchEventKind("ENTRY_CREATE", Path.class);
-
- /**
- * Directory entry deleted.
- *
- *
When a directory is registered for this event then the {@link WatchKey}
- * is queued when it is observed that an entry is deleted or renamed out of
- * the directory. The event {@link WatchEvent#count count} for this event
- * is always {@code 1}.
- */
- public static final WatchEvent.Kind ENTRY_DELETE =
- new StdWatchEventKind("ENTRY_DELETE", Path.class);
-
- /**
- * Directory entry modified.
- *
- *
When a directory is registered for this event then the {@link WatchKey}
- * is queued when it is observed that an entry in the directory has been
- * modified. The event {@link WatchEvent#count count} for this event is
- * {@code 1} or greater.
- */
- public static final WatchEvent.Kind ENTRY_MODIFY =
- new StdWatchEventKind("ENTRY_MODIFY", Path.class);
-
- private static class StdWatchEventKind implements WatchEvent.Kind {
- private final String name;
- private final Class type;
- StdWatchEventKind(String name, Class type) {
- this.name = name;
- this.type = type;
- }
- @Override public String name() { return name; }
- @Override public Class type() { return type; }
- @Override public String toString() { return name; }
- }
-}
diff -r 9fcad86579e6 -r 0dc08749a6cc jdk/src/share/classes/java/nio/file/StandardWatchEventKinds.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/nio/file/StandardWatchEventKinds.java Wed Jul 05 17:44:49 2017 +0200
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2007, 2009, 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. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.
+ */
+
+package java.nio.file;
+
+/**
+ * Defines the standard event kinds.
+ *
+ * @since 1.7
+ */
+
+public final class StandardWatchEventKinds {
+ private StandardWatchEventKinds() { }
+
+ /**
+ * A special event to indicate that events may have been lost or
+ * discarded.
+ *
+ *
The {@link WatchEvent#context context} for this event is
+ * implementation specific and may be {@code null}. The event {@link
+ * WatchEvent#count count} may be greater than {@code 1}.
+ *
+ * @see WatchService
+ */
+ public static final WatchEvent.Kind OVERFLOW =
+ new StdWatchEventKind("OVERFLOW", Object.class);
+
+ /**
+ * Directory entry created.
+ *
+ *
When a directory is registered for this event then the {@link WatchKey}
+ * is queued when it is observed that an entry is created in the directory
+ * or renamed into the directory. The event {@link WatchEvent#count count}
+ * for this event is always {@code 1}.
+ */
+ public static final WatchEvent.Kind ENTRY_CREATE =
+ new StdWatchEventKind("ENTRY_CREATE", Path.class);
+
+ /**
+ * Directory entry deleted.
+ *
+ *
When a directory is registered for this event then the {@link WatchKey}
+ * is queued when it is observed that an entry is deleted or renamed out of
+ * the directory. The event {@link WatchEvent#count count} for this event
+ * is always {@code 1}.
+ */
+ public static final WatchEvent.Kind ENTRY_DELETE =
+ new StdWatchEventKind("ENTRY_DELETE", Path.class);
+
+ /**
+ * Directory entry modified.
+ *
+ *
When a directory is registered for this event then the {@link WatchKey}
+ * is queued when it is observed that an entry in the directory has been
+ * modified. The event {@link WatchEvent#count count} for this event is
+ * {@code 1} or greater.
+ */
+ public static final WatchEvent.Kind ENTRY_MODIFY =
+ new StdWatchEventKind("ENTRY_MODIFY", Path.class);
+
+ private static class StdWatchEventKind implements WatchEvent.Kind {
+ private final String name;
+ private final Class type;
+ StdWatchEventKind(String name, Class type) {
+ this.name = name;
+ this.type = type;
+ }
+ @Override public String name() { return name; }
+ @Override public Class type() { return type; }
+ @Override public String toString() { return name; }
+ }
+}
diff -r 9fcad86579e6 -r 0dc08749a6cc jdk/src/share/classes/java/nio/file/WatchEvent.java
--- a/jdk/src/share/classes/java/nio/file/WatchEvent.java Sat May 14 10:24:02 2011 -0700
+++ b/jdk/src/share/classes/java/nio/file/WatchEvent.java Wed Jul 05 17:44:49 2017 +0200
@@ -50,7 +50,7 @@
* An event kind, for the purposes of identification.
*
* @since 1.7
- * @see StandardWatchEventKind
+ * @see StandardWatchEventKinds
*/
public static interface Kind {
/**
@@ -98,9 +98,9 @@
/**
* Returns the context for the event.
*
- *
In the case of {@link StandardWatchEventKind#ENTRY_CREATE ENTRY_CREATE},
- * {@link StandardWatchEventKind#ENTRY_DELETE ENTRY_DELETE}, and {@link
- * StandardWatchEventKind#ENTRY_MODIFY ENTRY_MODIFY} events the context is
+ *
In the case of {@link StandardWatchEventKinds#ENTRY_CREATE ENTRY_CREATE},
+ * {@link StandardWatchEventKinds#ENTRY_DELETE ENTRY_DELETE}, and {@link
+ * StandardWatchEventKinds#ENTRY_MODIFY ENTRY_MODIFY} events the context is
* a {@code Path} that is the {@link Path#relativize relative} path between
* the directory registered with the watch service, and the entry that is
* created, deleted, or modified.
diff -r 9fcad86579e6 -r 0dc08749a6cc jdk/src/share/classes/java/nio/file/WatchService.java
--- a/jdk/src/share/classes/java/nio/file/WatchService.java Sat May 14 10:24:02 2011 -0700
+++ b/jdk/src/share/classes/java/nio/file/WatchService.java Wed Jul 05 17:44:49 2017 +0200
@@ -68,7 +68,7 @@
* of events that it may accumulate. Where an implementation knowingly
* discards events then it arranges for the key's {@link WatchKey#pollEvents
* pollEvents} method to return an element with an event type of {@link
- * StandardWatchEventKind#OVERFLOW OVERFLOW}. This event can be used by the
+ * StandardWatchEventKinds#OVERFLOW OVERFLOW}. This event can be used by the
* consumer as a trigger to re-examine the state of the object.
*
*
When an event is reported to indicate that a file in a watched directory
@@ -87,7 +87,7 @@
* are detected, their timeliness, and whether their ordering is preserved are
* highly implementation specific. For example, when a file in a watched
* directory is modified then it may result in a single {@link
- * StandardWatchEventKind#ENTRY_MODIFY ENTRY_MODIFY} event in some
+ * StandardWatchEventKinds#ENTRY_MODIFY ENTRY_MODIFY} event in some
* implementations but several events in other implementations. Short-lived
* files (meaning files that are deleted very quickly after they are created)
* may not be detected by primitive implementations that periodically poll the
diff -r 9fcad86579e6 -r 0dc08749a6cc jdk/src/share/classes/java/nio/file/Watchable.java
--- a/jdk/src/share/classes/java/nio/file/Watchable.java Sat May 14 10:24:02 2011 -0700
+++ b/jdk/src/share/classes/java/nio/file/Watchable.java Wed Jul 05 17:44:49 2017 +0200
@@ -53,7 +53,7 @@
* those specified by the {@code events} and {@code modifiers} parameters.
* Changing the event set does not cause pending events for the object to be
* discarded. Objects are automatically registered for the {@link
- * StandardWatchEventKind#OVERFLOW OVERFLOW} event. This event is not
+ * StandardWatchEventKinds#OVERFLOW OVERFLOW} event. This event is not
* required to be present in the array of events.
*
*
Otherwise the file system object has not yet been registered with the
diff -r 9fcad86579e6 -r 0dc08749a6cc jdk/src/share/classes/java/sql/BatchUpdateException.java
--- a/jdk/src/share/classes/java/sql/BatchUpdateException.java Sat May 14 10:24:02 2011 -0700
+++ b/jdk/src/share/classes/java/sql/BatchUpdateException.java Wed Jul 05 17:44:49 2017 +0200
@@ -89,7 +89,7 @@
* The cause is not initialized, and may subsequently be
* initialized by a call to the
* {@link Throwable#initCause(java.lang.Throwable)} method. The vendor code
- * is intialized to 0.
+ * is initialized to 0.
*
BigDecimal
*
*
*
@@ -1362,7 +1362,7 @@
* precision is not provided, then all of the digits as returned by {@link
* Double#toHexString(double)} will be output.
*
- *
The formatting of the magnitude m depends upon its value.
@@ -1427,11 +1427,11 @@
*
*
If m is greater than or equal to 10-4 but less
* than 10precision then it is represented in decimal format.
+ * href="#bdecimal">decimal format.
*
*
If m is less than 10-4 or greater than or equal to
* 10precision, then it is represented in computerized scientific notation.
+ * href="#bscientific">computerized scientific notation.
*
*
The total number of significant digits in m is equal to the
* precision. If the precision is not specified, then the default value is
@@ -1447,7 +1447,7 @@
*
*
{@code 'f'}
*
'\u0066'
- *
Requires the output to be formatted using decimal
+ *
Requires the output to be formatted using decimal
* format. The localization algorithm is
* applied.
*
diff -r 9fcad86579e6 -r 0dc08749a6cc jdk/src/share/classes/java/util/concurrent/Phaser.java
--- a/jdk/src/share/classes/java/util/concurrent/Phaser.java Sat May 14 10:24:02 2011 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/Phaser.java Wed Jul 05 17:44:49 2017 +0200
@@ -159,7 +159,7 @@
* void runTasks(List tasks) {
* final Phaser phaser = new Phaser(1); // "1" to register self
* // create and start threads
- * for (Runnable task : tasks) {
+ * for (final Runnable task : tasks) {
* phaser.register();
* new Thread() {
* public void run() {
diff -r 9fcad86579e6 -r 0dc08749a6cc jdk/src/share/classes/java/util/concurrent/locks/LockSupport.java
--- a/jdk/src/share/classes/java/util/concurrent/locks/LockSupport.java Sat May 14 10:24:02 2011 -0700
+++ b/jdk/src/share/classes/java/util/concurrent/locks/LockSupport.java Wed Jul 05 17:44:49 2017 +0200
@@ -275,10 +275,14 @@
* snapshot -- the thread may have since unblocked or blocked on a
* different blocker object.
*
+ * @param t the thread
* @return the blocker
+ * @throws NullPointerException if argument is null
* @since 1.6
*/
public static Object getBlocker(Thread t) {
+ if (t == null)
+ throw new NullPointerException();
return unsafe.getObjectVolatile(t, parkBlockerOffset);
}
diff -r 9fcad86579e6 -r 0dc08749a6cc jdk/src/share/classes/java/util/logging/LogManager.java
--- a/jdk/src/share/classes/java/util/logging/LogManager.java Sat May 14 10:24:02 2011 -0700
+++ b/jdk/src/share/classes/java/util/logging/LogManager.java Wed Jul 05 17:44:49 2017 +0200
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2011, 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
@@ -342,12 +342,35 @@
// already been created with the given name it is returned.
// Otherwise a new logger instance is created and registered
// in the LogManager global namespace.
+
+ // This method will always return a non-null Logger object.
+ // Synchronization is not required here. All synchronization for
+ // adding a new Logger object is handled by addLogger().
Logger demandLogger(String name) {
Logger result = getLogger(name);
if (result == null) {
- result = new Logger(name, null);
- addLogger(result);
- result = getLogger(name);
+ // only allocate the new logger once
+ Logger newLogger = new Logger(name, null);
+ do {
+ if (addLogger(newLogger)) {
+ // We successfully added the new Logger that we
+ // created above so return it without refetching.
+ return newLogger;
+ }
+
+ // We didn't add the new Logger that we created above
+ // because another thread added a Logger with the same
+ // name after our null check above and before our call
+ // to addLogger(). We have to refetch the Logger because
+ // addLogger() returns a boolean instead of the Logger
+ // reference itself. However, if the thread that created
+ // the other Logger is not holding a strong reference to
+ // the other Logger, then it is possible for the other
+ // Logger to be GC'ed after we saw it in addLogger() and
+ // before we can refetch it. If it has been GC'ed then
+ // we'll just loop around and try again.
+ result = getLogger(name);
+ } while (result == null);
}
return result;
}
diff -r 9fcad86579e6 -r 0dc08749a6cc jdk/src/share/classes/java/util/logging/Logger.java
--- a/jdk/src/share/classes/java/util/logging/Logger.java Sat May 14 10:24:02 2011 -0700
+++ b/jdk/src/share/classes/java/util/logging/Logger.java Wed Jul 05 17:44:49 2017 +0200
@@ -310,7 +310,20 @@
* @return a suitable Logger
* @throws NullPointerException if the name is null.
*/
- public static synchronized Logger getLogger(String name) {
+
+ // Synchronization is not required here. All synchronization for
+ // adding a new Logger object is handled by LogManager.addLogger().
+ public static Logger getLogger(String name) {
+ // This method is intentionally not a wrapper around a call
+ // to getLogger(name, resourceBundleName). If it were then
+ // this sequence:
+ //
+ // getLogger("Foo", "resourceBundleForFoo");
+ // getLogger("Foo");
+ //
+ // would throw an IllegalArgumentException in the second call
+ // because the wrapper would result in an attempt to replace
+ // the existing "resourceBundleForFoo" with null.
LogManager manager = LogManager.getLogManager();
return manager.demandLogger(name);
}
@@ -355,7 +368,10 @@
* a different resource bundle name.
* @throws NullPointerException if the name is null.
*/
- public static synchronized Logger getLogger(String name, String resourceBundleName) {
+
+ // Synchronization is not required here. All synchronization for
+ // adding a new Logger object is handled by LogManager.addLogger().
+ public static Logger getLogger(String name, String resourceBundleName) {
LogManager manager = LogManager.getLogManager();
Logger result = manager.demandLogger(name);
if (result.resourceBundleName == null) {
@@ -417,7 +433,10 @@
* @throws MissingResourceException if the resourceBundleName is non-null and
* no corresponding resource can be found.
*/
- public static synchronized Logger getAnonymousLogger(String resourceBundleName) {
+
+ // Synchronization is not required here. All synchronization for
+ // adding a new anonymous Logger object is handled by doSetParent().
+ public static Logger getAnonymousLogger(String resourceBundleName) {
LogManager manager = LogManager.getLogManager();
// cleanup some Loggers that have been GC'ed
manager.drainLoggerRefQueueBounded();
diff -r 9fcad86579e6 -r 0dc08749a6cc jdk/src/share/classes/java/util/regex/Pattern.java
--- a/jdk/src/share/classes/java/util/regex/Pattern.java Sat May 14 10:24:02 2011 -0700
+++ b/jdk/src/share/classes/java/util/regex/Pattern.java Wed Jul 05 17:44:49 2017 +0200
@@ -213,7 +213,7 @@
*
+Optional ColorSpace support:
+Handling of PhotoYCC (YCC), PhotoYCCA (YCCA), RGBA and YCbCrA color spaces
+by the standard plugin, as described below, is dependent on capabilities
+of the libraries used to interpret the JPEG data. Thus all consequential
+behaviors are optional. If the support is not available when decoding,
+the color space will be treated as unrecognized and the appropriate
+default color space for the specified number of component channels
+may be used.
+When writing, an Exception may be thrown if no suitable conversion
+can be applied before encoding.
+But where the support for these color spaces is available, the behavior
+must be as documented.
+
+
When reading, the contents of the stream are interpreted by the usual
JPEG conventions, as follows:
@@ -241,8 +255,11 @@
2-channel images are assumed to be grayscale with an alpha channel.
For 3- and 4-channel images, the component ids are consulted. If these
values are 1-3 for a 3-channel image, then the image is assumed to be
- YCbCr. If these values are 1-4 for a 4-channel image, then the image
- is assumed to be YCbCrA. If these values are > 4, they are checked
+ YCbCr. Subject to the availability of the
+ optional color space support
+ described above, if these values are 1-4 for a 4-channel image,
+ then the image is assumed to be YCbCrA.
+ If these values are > 4, they are checked
against the ASCII codes for 'R', 'G', 'B', 'A', 'C', 'c'. These can
encode the following colorspaces:
@@ -346,12 +363,16 @@
component ids in the frame and scan headers are set to 1, 2, and 3.
-
RGBA images are converted to YCbCrA, subsampled in the
+
Subject to the optional library support
+ described above,
+ RGBA images are converted to YCbCrA, subsampled in the
chrominance channels by half both vertically and horizontally, and
written without any special marker segments. The component ids
in the frame and scan headers are set to 1, 2, 3, and 4.
-
PhotoYCC and YCCAimages are subsampled by half in the chrominance
+
Subject to the optional library support
+ described above,
+ PhotoYCC and YCCAimages are subsampled by half in the chrominance
channels both vertically and horizontally and written with an
Adobe APP14 marker segment and 'Y','C', and 'c' (and
'A' if an alpha channel is present) as component ids in the frame
@@ -433,6 +454,8 @@
If an app0JFIF node is present in the metadata object,
it is ignored and a warning is sent to listeners, as JFIF does not
diff -r 9fcad86579e6 -r 0dc08749a6cc jdk/src/share/classes/javax/management/loading/package.html
--- a/jdk/src/share/classes/javax/management/loading/package.html Sat May 14 10:24:02 2011 -0700
+++ b/jdk/src/share/classes/javax/management/loading/package.html Wed Jul 05 17:44:49 2017 +0200
@@ -2,7 +2,7 @@
javax.management.loading package