diff --git a/.hgtags b/.hgtags
index dbc6b22ea68..a8e52f397ec 100644
--- a/.hgtags
+++ b/.hgtags
@@ -116,3 +116,4 @@ d1cf7d4ee16c341f5b8c7e7f1d68a8c412b6c693 jdk7-b137
955488f34ca418f6cdab843d61c20d2c615637d9 jdk7-b139
f4298bc3f4b6baa315643be06966f09684290068 jdk7-b140
5d86d0c7692e8f4a58d430d68c03594e2d3403b3 jdk7-b141
+92bf0655022d4187e9b49c1400f98fb3392a4630 jdk7-b142
diff --git a/.hgtags-top-repo b/.hgtags-top-repo
index 6745af50b29..8c8e07b841c 100644
--- a/.hgtags-top-repo
+++ b/.hgtags-top-repo
@@ -116,3 +116,4 @@ fc47c97bbbd91b1f774d855c48a7e285eb1a351a jdk7-b138
7ed6d0b9aaa12320832a7ddadb88d6d8d0dda4c1 jdk7-b139
dcfe74f1c6553c556e7d361c30b0b614eb5e40f6 jdk7-b140
c6569c5585851dfd39b8de8e021c3c312f51af12 jdk7-b141
+cfbbdb77eac0397b03eb99ee2e07ea00e0a7b81e jdk7-b142
diff --git a/Makefile b/Makefile
index c8598161cec..5d0e08d6d15 100644
--- a/Makefile
+++ b/Makefile
@@ -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 @@ endef
# 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 @@ generic_build_repo_series::
#
# 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 @@ generic_build_repo_series::
# 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 @@ build_product_image:
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 @@ $(SOURCE_TIPS): FRC
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 @@ $(OUTPUTDIR)/test_failures.txt: $(OUTPUTDIR)/test_log.txt
# 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 --git a/corba/.hgtags b/corba/.hgtags
index 9624f1f3878..2f400d34aab 100644
--- a/corba/.hgtags
+++ b/corba/.hgtags
@@ -116,3 +116,4 @@ a66c01d8bf895261715955df0b95545c000ed6a8 jdk7-b137
60b074ec6fcf5cdf9efce22fdfb02326ed8fa2d3 jdk7-b139
cdf5d19ec142424489549025e9c42e51f32cf688 jdk7-b140
a58635cdd921bafef353f4864184a0481353197b jdk7-b141
+a2f340a048c88d10cbedc0504f5cf03d39925a40 jdk7-b142
diff --git a/hotspot/.hgtags b/hotspot/.hgtags
index 5e01fb71812..8b20d73be2a 100644
--- a/hotspot/.hgtags
+++ b/hotspot/.hgtags
@@ -170,3 +170,5 @@ d283b82966712b353fa307845a1316da42a355f4 jdk7-b140
d283b82966712b353fa307845a1316da42a355f4 hs21-b10
5d07913abd59261c77f24cc04a759cb75d804099 jdk7-b141
3aea9e9feb073f5500e031be6186666bcae89aa2 hs21-b11
+9ad1548c6b63d596c411afc35147ffd5254426d9 jdk7-b142
+9ad1548c6b63d596c411afc35147ffd5254426d9 hs21-b12
diff --git a/hotspot/make/hotspot_version b/hotspot/make/hotspot_version
index a8dda3ec3ba..452ad051644 100644
--- a/hotspot/make/hotspot_version
+++ b/hotspot/make/hotspot_version
@@ -35,7 +35,7 @@ HOTSPOT_VM_COPYRIGHT=Copyright 2011
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 --git a/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp
index f3a3f10ea80..46d7d27a259 100644
--- a/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp
+++ b/hotspot/src/cpu/sparc/vm/cppInterpreter_sparc.cpp
@@ -2176,6 +2176,7 @@ int AbstractInterpreter::layout_activation(methodOop method,
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 --git a/hotspot/src/cpu/sparc/vm/frame_sparc.cpp b/hotspot/src/cpu/sparc/vm/frame_sparc.cpp
index d9dd1700ca2..10d4772ab78 100644
--- a/hotspot/src/cpu/sparc/vm/frame_sparc.cpp
+++ b/hotspot/src/cpu/sparc/vm/frame_sparc.cpp
@@ -811,7 +811,7 @@ intptr_t* frame::interpreter_frame_tos_at(jint offset) const {
#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 --git a/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp
index f90f28873e8..774c3b64d45 100644
--- a/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp
+++ b/hotspot/src/cpu/sparc/vm/interpreter_sparc.cpp
@@ -423,25 +423,6 @@ bool AbstractInterpreter::can_be_compiled(methodHandle m) {
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 --git a/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp b/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp
index 4e80e1acdda..5f858bd84ee 100644
--- a/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp
+++ b/hotspot/src/cpu/sparc/vm/methodHandles_sparc.cpp
@@ -142,18 +142,8 @@ address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler*
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 @@ address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler*
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 --git a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp
index ac855dad84a..efc0a46c872 100644
--- a/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp
+++ b/hotspot/src/cpu/sparc/vm/templateInterpreter_sparc.cpp
@@ -1623,6 +1623,7 @@ int AbstractInterpreter::layout_activation(methodOop method,
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 @@ int AbstractInterpreter::layout_activation(methodOop method,
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 --git a/hotspot/src/cpu/x86/vm/assembler_x86.hpp b/hotspot/src/cpu/x86/vm/assembler_x86.hpp
index 34e8ed9c87f..c36709a33df 100644
--- a/hotspot/src/cpu/x86/vm/assembler_x86.hpp
+++ b/hotspot/src/cpu/x86/vm/assembler_x86.hpp
@@ -234,6 +234,20 @@ class Address VALUE_OBJ_CLASS_SPEC {
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 @@ class MacroAssembler: public Assembler {
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 @@ class MacroAssembler: public Assembler {
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 @@ public:
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 --git a/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp b/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp
index edacb282edb..226c6cbc6e6 100644
--- a/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp
+++ b/hotspot/src/cpu/x86/vm/cppInterpreter_x86.cpp
@@ -2339,14 +2339,15 @@ void BytecodeInterpreter::layout_interpreterState(interpreterState to_fill,
}
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 --git a/hotspot/src/cpu/x86/vm/frame_x86.cpp b/hotspot/src/cpu/x86/vm/frame_x86.cpp
index 8709a0e8698..21705957e50 100644
--- a/hotspot/src/cpu/x86/vm/frame_x86.cpp
+++ b/hotspot/src/cpu/x86/vm/frame_x86.cpp
@@ -339,7 +339,6 @@ frame frame::sender_for_entry_frame(RegisterMap* map) const {
return fr;
}
-
//------------------------------------------------------------------------------
// frame::verify_deopt_original_pc
//
@@ -361,6 +360,55 @@ void frame::verify_deopt_original_pc(nmethod* nm, intptr_t* unextended_sp, bool
}
#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 @@ frame frame::sender_for_interpreter_frame(RegisterMap* map) const {
// 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 frame::sender_for_interpreter_frame(RegisterMap* map) const {
// 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 @@ frame frame::sender_for_compiled_frame(RegisterMap* map) const {
// 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 @@ frame frame::sender_for_compiled_frame(RegisterMap* map) const {
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 @@ frame frame::sender(RegisterMap* map) const {
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 @@ intptr_t* frame::interpreter_frame_tos_at(jint offset) const {
#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 --git a/hotspot/src/cpu/x86/vm/frame_x86.hpp b/hotspot/src/cpu/x86/vm/frame_x86.hpp
index ea92dc518cc..a307a340428 100644
--- a/hotspot/src/cpu/x86/vm/frame_x86.hpp
+++ b/hotspot/src/cpu/x86/vm/frame_x86.hpp
@@ -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 --git a/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp b/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp
index bb9ac15cb31..d093facdc15 100644
--- a/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp
+++ b/hotspot/src/cpu/x86/vm/frame_x86.inline.hpp
@@ -62,6 +62,7 @@ inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address
_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 --git a/hotspot/src/cpu/x86/vm/interpreter_x86.hpp b/hotspot/src/cpu/x86/vm/interpreter_x86.hpp
index f3ff867b893..8a6169c0ca4 100644
--- a/hotspot/src/cpu/x86/vm/interpreter_x86.hpp
+++ b/hotspot/src/cpu/x86/vm/interpreter_x86.hpp
@@ -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 --git a/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp b/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp
index cb2345a4183..43a5a18a5b6 100644
--- a/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp
+++ b/hotspot/src/cpu/x86/vm/interpreter_x86_32.cpp
@@ -242,26 +242,6 @@ address InterpreterGenerator::generate_method_handle_entry(void) {
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 --git a/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp b/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp
index 3dbea5754dc..1c124c2f0b7 100644
--- a/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp
+++ b/hotspot/src/cpu/x86/vm/interpreter_x86_64.cpp
@@ -362,20 +362,6 @@ address InterpreterGenerator::generate_empty_entry(void) {
}
-// 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 --git a/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp b/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp
index 42a57c1322d..1427d13c2c4 100644
--- a/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp
+++ b/hotspot/src/cpu/x86/vm/methodHandles_x86.cpp
@@ -69,23 +69,475 @@ MethodHandleEntry* MethodHandleEntry::finish_compiled_entry(MacroAssembler* _mas
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 MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler*
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 @@ address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler*
__ 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 @@ address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler*
// 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 @@ address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler*
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;
}
+// 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 @@ void MethodHandles::insert_arg_slots(MacroAssembler* _masm,
// 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 @@ void MethodHandles::remove_arg_slots(MacroAssembler* _masm,
__ 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,
- intptr_t* saved_sp,
oop mh,
- intptr_t* sp) {
+ intptr_t* saved_regs,
+ intptr_t* entry_sp,
+ intptr_t* saved_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 @@ int MethodHandles::adapter_conversion_ops_supported_mask() {
|(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 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
__ 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 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
// 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 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
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 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
// 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 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
// 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 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
__ 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 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
#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 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
}
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 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
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 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
// '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 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
__ 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);
+ Register rdi_stack_move = rdi_temp;
+ load_stack_move(_masm, rdi_stack_move, rcx_recv, true);
- 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);
-
- // 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 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
__ 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 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
// 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 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
// 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 --git a/hotspot/src/cpu/x86/vm/methodHandles_x86.hpp b/hotspot/src/cpu/x86/vm/methodHandles_x86.hpp
new file mode 100644
index 00000000000..2a93500d7e5
--- /dev/null
+++ b/hotspot/src/cpu/x86/vm/methodHandles_x86.hpp
@@ -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 --git a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp
index 149bc7c9ea4..bb32cf34dab 100644
--- a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp
+++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_32.cpp
@@ -2253,6 +2253,31 @@ uint SharedRuntime::out_preserve_stack_slots() {
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 @@ void SharedRuntime::generate_stubs() {
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 --git a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp
index c017cf86d47..89766d1f3f7 100644
--- a/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp
+++ b/hotspot/src/cpu/x86/vm/sharedRuntime_x86_64.cpp
@@ -2530,6 +2530,32 @@ uint SharedRuntime::out_preserve_stack_slots() {
}
+//----------------------------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 @@ void SharedRuntime::generate_stubs() {
generate_handler_blob(CAST_FROM_FN_PTR(address,
SafepointSynchronize::handle_polling_page_exception), true);
+ generate_ricochet_blob();
+
generate_deopt_blob();
#ifdef COMPILER2
diff --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp
index 283274e3dde..65b131b3757 100644
--- a/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp
+++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_32.hpp
@@ -36,7 +36,7 @@ enum platform_dependent_constants {
// 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 --git a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp
index b1726caa692..ac6f2fcd853 100644
--- a/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp
+++ b/hotspot/src/cpu/x86/vm/stubRoutines_x86_64.hpp
@@ -38,7 +38,7 @@ enum platform_dependent_constants {
// 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 --git a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp
index a7222c265a5..6184bc393b5 100644
--- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp
+++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_32.cpp
@@ -1589,6 +1589,7 @@ int AbstractInterpreter::layout_activation(methodOop method,
int tempcount,
int popframe_extra_args,
int moncount,
+ int caller_actual_parameters,
int callee_param_count,
int callee_locals,
frame* caller,
diff --git a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp
index 8d3740c7049..80f08524b06 100644
--- a/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp
+++ b/hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp
@@ -1603,6 +1603,7 @@ int AbstractInterpreter::layout_activation(methodOop method,
int tempcount,
int popframe_extra_args,
int moncount,
+ int caller_actual_parameters,
int callee_param_count,
int callee_locals,
frame* caller,
diff --git a/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp b/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp
index 13a4710a104..961de9fdd96 100644
--- a/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp
+++ b/hotspot/src/cpu/zero/vm/cppInterpreter_zero.cpp
@@ -1427,6 +1427,7 @@ int AbstractInterpreter::layout_activation(methodOop method,
int tempcount,
int popframe_extra_args,
int moncount,
+ int caller_actual_parameters,
int callee_param_count,
int callee_locals,
frame* caller,
diff --git a/hotspot/src/cpu/zero/vm/interpreter_zero.cpp b/hotspot/src/cpu/zero/vm/interpreter_zero.cpp
index ff5a69be88e..93c3b90fa45 100644
--- a/hotspot/src/cpu/zero/vm/interpreter_zero.cpp
+++ b/hotspot/src/cpu/zero/vm/interpreter_zero.cpp
@@ -82,24 +82,6 @@ bool AbstractInterpreter::can_be_compiled(methodHandle m) {
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 --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp
index f00e426b881..791f62ecb7b 100644
--- a/hotspot/src/os/linux/vm/os_linux.cpp
+++ b/hotspot/src/os/linux/vm/os_linux.cpp
@@ -2850,7 +2850,7 @@ bool os::Linux::hugetlbfs_sanity_check(bool warn, size_t page_size) {
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 --git a/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp b/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp
index e27ce064d94..1db5a4dd1b7 100644
--- a/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp
+++ b/hotspot/src/share/vm/c1/c1_InstructionPrinter.cpp
@@ -132,17 +132,22 @@ void InstructionPrinter::print_object(Value obj) {
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 --git a/jdk/src/share/classes/java/nio/charset/Charset.java b/jdk/src/share/classes/java/nio/charset/Charset.java
index 51dbe1504dc..1a30ba96103 100644
--- a/jdk/src/share/classes/java/nio/charset/Charset.java
+++ b/jdk/src/share/classes/java/nio/charset/Charset.java
@@ -215,7 +215,7 @@ import sun.security.action.GetPropertyAction;
* 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 --git a/jdk/src/share/classes/java/nio/charset/StandardCharset.java b/jdk/src/share/classes/java/nio/charset/StandardCharsets.java
similarity index 96%
rename from jdk/src/share/classes/java/nio/charset/StandardCharset.java
rename to jdk/src/share/classes/java/nio/charset/StandardCharsets.java
index edec058bc45..b2572c361cf 100644
--- a/jdk/src/share/classes/java/nio/charset/StandardCharset.java
+++ b/jdk/src/share/classes/java/nio/charset/StandardCharsets.java
@@ -32,10 +32,10 @@ package java.nio.charset;
* @see Standard Charsets
* @since 1.7
*/
-public final class StandardCharset {
+public final class StandardCharsets {
- private StandardCharset() {
- throw new AssertionError("No java.nio.charset.StandardCharset instances for you!");
+ 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
diff --git a/jdk/src/share/classes/java/nio/file/Path.java b/jdk/src/share/classes/java/nio/file/Path.java
index 2d2e977d49d..370c38dbac2 100644
--- a/jdk/src/share/classes/java/nio/file/Path.java
+++ b/jdk/src/share/classes/java/nio/file/Path.java
@@ -72,7 +72,7 @@ import java.util.Iterator;
* directory and is UTF-8 encoded.
*
@@ -609,11 +609,11 @@ public interface Path
* directory can be watched. The {@code events} parameter is the events to
* register and may contain the following events:
*
{@link StandardWatchEventKinds#ENTRY_MODIFY ENTRY_MODIFY} -
* entry in directory was modified
*
*
@@ -622,7 +622,7 @@ public interface Path
* 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 --git a/jdk/src/share/classes/java/nio/file/StandardWatchEventKind.java b/jdk/src/share/classes/java/nio/file/StandardWatchEventKinds.java
similarity index 94%
rename from jdk/src/share/classes/java/nio/file/StandardWatchEventKind.java
rename to jdk/src/share/classes/java/nio/file/StandardWatchEventKinds.java
index 6064d75a7fa..ef111e2bf7d 100644
--- a/jdk/src/share/classes/java/nio/file/StandardWatchEventKind.java
+++ b/jdk/src/share/classes/java/nio/file/StandardWatchEventKinds.java
@@ -31,8 +31,8 @@ package java.nio.file;
* @since 1.7
*/
-public final class StandardWatchEventKind {
- private StandardWatchEventKind() { }
+public final class StandardWatchEventKinds {
+ private StandardWatchEventKinds() { }
/**
* A special event to indicate that events may have been lost or
@@ -44,8 +44,8 @@ public final class StandardWatchEventKind {
*
* @see WatchService
*/
- public static final WatchEvent.Kind OVERFLOW =
- new StdWatchEventKind("OVERFLOW", Void.class);
+ public static final WatchEvent.Kind OVERFLOW =
+ new StdWatchEventKind("OVERFLOW", Object.class);
/**
* Directory entry created.
diff --git a/jdk/src/share/classes/java/nio/file/WatchEvent.java b/jdk/src/share/classes/java/nio/file/WatchEvent.java
index 4328828fe22..cab199f0b57 100644
--- a/jdk/src/share/classes/java/nio/file/WatchEvent.java
+++ b/jdk/src/share/classes/java/nio/file/WatchEvent.java
@@ -50,7 +50,7 @@ public interface WatchEvent {
* An event kind, for the purposes of identification.
*
* @since 1.7
- * @see StandardWatchEventKind
+ * @see StandardWatchEventKinds
*/
public static interface Kind {
/**
@@ -98,9 +98,9 @@ public interface WatchEvent {
/**
* 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 --git a/jdk/src/share/classes/java/nio/file/WatchService.java b/jdk/src/share/classes/java/nio/file/WatchService.java
index 58e633610c0..5a63fcd8722 100644
--- a/jdk/src/share/classes/java/nio/file/WatchService.java
+++ b/jdk/src/share/classes/java/nio/file/WatchService.java
@@ -68,7 +68,7 @@ import java.util.concurrent.TimeUnit;
* 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 @@ import java.util.concurrent.TimeUnit;
* 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 --git a/jdk/src/share/classes/java/nio/file/Watchable.java b/jdk/src/share/classes/java/nio/file/Watchable.java
index 41ef35200e7..f84437c5c69 100644
--- a/jdk/src/share/classes/java/nio/file/Watchable.java
+++ b/jdk/src/share/classes/java/nio/file/Watchable.java
@@ -53,7 +53,7 @@ public interface Watchable {
* 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 --git a/jdk/src/share/classes/java/sql/BatchUpdateException.java b/jdk/src/share/classes/java/sql/BatchUpdateException.java
index 4fa6241169f..8405da2dac7 100644
--- a/jdk/src/share/classes/java/sql/BatchUpdateException.java
+++ b/jdk/src/share/classes/java/sql/BatchUpdateException.java
@@ -89,7 +89,7 @@ public class BatchUpdateException extends SQLException {
* 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 @@ import sun.misc.FormattedFloatingDecimal;
* 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 @@ import sun.misc.FormattedFloatingDecimal;
*
*
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 @@ import sun.misc.FormattedFloatingDecimal;
*
*
{@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 --git a/jdk/src/share/classes/java/util/concurrent/Phaser.java b/jdk/src/share/classes/java/util/concurrent/Phaser.java
index c4ebe2378cb..bdafbbb7a9d 100644
--- a/jdk/src/share/classes/java/util/concurrent/Phaser.java
+++ b/jdk/src/share/classes/java/util/concurrent/Phaser.java
@@ -159,7 +159,7 @@ import java.util.concurrent.locks.LockSupport;
* 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 --git a/jdk/src/share/classes/java/util/concurrent/locks/LockSupport.java b/jdk/src/share/classes/java/util/concurrent/locks/LockSupport.java
index f0cd3bc0456..7b829f63a06 100644
--- a/jdk/src/share/classes/java/util/concurrent/locks/LockSupport.java
+++ b/jdk/src/share/classes/java/util/concurrent/locks/LockSupport.java
@@ -275,10 +275,14 @@ public class LockSupport {
* 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 --git a/jdk/src/share/classes/java/util/logging/LogManager.java b/jdk/src/share/classes/java/util/logging/LogManager.java
index cf49a0a69b7..a28b0d8a824 100644
--- a/jdk/src/share/classes/java/util/logging/LogManager.java
+++ b/jdk/src/share/classes/java/util/logging/LogManager.java
@@ -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 @@ public class LogManager {
// 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 --git a/jdk/src/share/classes/java/util/logging/Logger.java b/jdk/src/share/classes/java/util/logging/Logger.java
index 87f6867169f..e3ce6bdac60 100644
--- a/jdk/src/share/classes/java/util/logging/Logger.java
+++ b/jdk/src/share/classes/java/util/logging/Logger.java
@@ -310,7 +310,20 @@ public class Logger {
* @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 @@ public class Logger {
* 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 @@ public class Logger {
* @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 --git a/jdk/src/share/classes/java/util/regex/Pattern.java b/jdk/src/share/classes/java/util/regex/Pattern.java
index fa302cfbab7..d89eca9845d 100644
--- a/jdk/src/share/classes/java/util/regex/Pattern.java
+++ b/jdk/src/share/classes/java/util/regex/Pattern.java
@@ -213,7 +213,7 @@ import java.util.Arrays;
*
diff --git a/jdk/src/share/classes/java/util/zip/ZipCoder.java b/jdk/src/share/classes/java/util/zip/ZipCoder.java
index 62844916f3d..12404cb38ac 100644
--- a/jdk/src/share/classes/java/util/zip/ZipCoder.java
+++ b/jdk/src/share/classes/java/util/zip/ZipCoder.java
@@ -28,7 +28,7 @@ package java.util.zip;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
-import java.nio.charset.StandardCharset;
+import java.nio.charset.StandardCharsets;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult;
@@ -107,7 +107,7 @@ final class ZipCoder {
if (isUTF8)
return getBytes(s);
if (utf8 == null)
- utf8 = new ZipCoder(StandardCharset.UTF_8);
+ utf8 = new ZipCoder(StandardCharsets.UTF_8);
return utf8.getBytes(s);
}
@@ -116,7 +116,7 @@ final class ZipCoder {
if (isUTF8)
return toString(ba, len);
if (utf8 == null)
- utf8 = new ZipCoder(StandardCharset.UTF_8);
+ utf8 = new ZipCoder(StandardCharsets.UTF_8);
return utf8.toString(ba, len);
}
@@ -132,7 +132,7 @@ final class ZipCoder {
private ZipCoder(Charset cs) {
this.cs = cs;
- this.isUTF8 = cs.name().equals(StandardCharset.UTF_8.name());
+ this.isUTF8 = cs.name().equals(StandardCharsets.UTF_8.name());
}
static ZipCoder get(Charset charset) {
diff --git a/jdk/src/share/classes/java/util/zip/ZipFile.java b/jdk/src/share/classes/java/util/zip/ZipFile.java
index d1861ad732c..9693809331a 100644
--- a/jdk/src/share/classes/java/util/zip/ZipFile.java
+++ b/jdk/src/share/classes/java/util/zip/ZipFile.java
@@ -31,7 +31,7 @@ import java.io.IOException;
import java.io.EOFException;
import java.io.File;
import java.nio.charset.Charset;
-import java.nio.charset.StandardCharset;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Enumeration;
@@ -141,7 +141,7 @@ class ZipFile implements ZipConstants, Closeable {
* @since 1.3
*/
public ZipFile(File file, int mode) throws IOException {
- this(file, mode, StandardCharset.UTF_8);
+ this(file, mode, StandardCharsets.UTF_8);
}
/**
diff --git a/jdk/src/share/classes/java/util/zip/ZipInputStream.java b/jdk/src/share/classes/java/util/zip/ZipInputStream.java
index ebfcce146cc..a60adb8040e 100644
--- a/jdk/src/share/classes/java/util/zip/ZipInputStream.java
+++ b/jdk/src/share/classes/java/util/zip/ZipInputStream.java
@@ -30,7 +30,7 @@ import java.io.IOException;
import java.io.EOFException;
import java.io.PushbackInputStream;
import java.nio.charset.Charset;
-import java.nio.charset.StandardCharset;
+import java.nio.charset.StandardCharsets;
import static java.util.zip.ZipConstants64.*;
/**
@@ -76,7 +76,7 @@ class ZipInputStream extends InflaterInputStream implements ZipConstants {
* @param in the actual input stream
*/
public ZipInputStream(InputStream in) {
- this(in, StandardCharset.UTF_8);
+ this(in, StandardCharsets.UTF_8);
}
/**
diff --git a/jdk/src/share/classes/java/util/zip/ZipOutputStream.java b/jdk/src/share/classes/java/util/zip/ZipOutputStream.java
index c0b59488002..91f7f75ece2 100644
--- a/jdk/src/share/classes/java/util/zip/ZipOutputStream.java
+++ b/jdk/src/share/classes/java/util/zip/ZipOutputStream.java
@@ -28,7 +28,7 @@ package java.util.zip;
import java.io.OutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
-import java.nio.charset.StandardCharset;
+import java.nio.charset.StandardCharsets;
import java.util.Vector;
import java.util.HashSet;
import static java.util.zip.ZipConstants64.*;
@@ -101,7 +101,7 @@ class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
* @param out the actual output stream
*/
public ZipOutputStream(OutputStream out) {
- this(out, StandardCharset.UTF_8);
+ this(out, StandardCharsets.UTF_8);
}
/**
diff --git a/jdk/src/share/classes/javax/imageio/metadata/doc-files/jpeg_metadata.html b/jdk/src/share/classes/javax/imageio/metadata/doc-files/jpeg_metadata.html
index 721bdb2e702..5d3b0ea5e7d 100644
--- a/jdk/src/share/classes/javax/imageio/metadata/doc-files/jpeg_metadata.html
+++ b/jdk/src/share/classes/javax/imageio/metadata/doc-files/jpeg_metadata.html
@@ -211,6 +211,20 @@ to any listeners.
+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 @@ JPEG conventions, as follows:
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 @@ If no metadata object is specified, then the following defaults apply:
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 @@ in the frame header node of the metadata object, regardless of color space.)
If an app0JFIF node is present in the metadata object,
it is ignored and a warning is sent to listeners, as JFIF does not
@@ -456,6 +479,8 @@ in the frame header node of the metadata object, regardless of color space.)
If an app0JFIF node is present in the metadata object,
the image is converted to sRGB, and then to YCbCr during encoding,
@@ -471,6 +496,8 @@ in the frame header node of the metadata object, regardless of color space.)
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 --git a/jdk/src/share/classes/javax/management/loading/package.html b/jdk/src/share/classes/javax/management/loading/package.html
index 7b7ca68a99f..1cfeef73bfa 100644
--- a/jdk/src/share/classes/javax/management/loading/package.html
+++ b/jdk/src/share/classes/javax/management/loading/package.html
@@ -2,7 +2,7 @@
javax.management.loading package