mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 11:04:34 +02:00
Merge
This commit is contained in:
commit
0a07595216
271 changed files with 10736 additions and 2670 deletions
1
.hgtags
1
.hgtags
|
@ -116,3 +116,4 @@ d1cf7d4ee16c341f5b8c7e7f1d68a8c412b6c693 jdk7-b137
|
||||||
955488f34ca418f6cdab843d61c20d2c615637d9 jdk7-b139
|
955488f34ca418f6cdab843d61c20d2c615637d9 jdk7-b139
|
||||||
f4298bc3f4b6baa315643be06966f09684290068 jdk7-b140
|
f4298bc3f4b6baa315643be06966f09684290068 jdk7-b140
|
||||||
5d86d0c7692e8f4a58d430d68c03594e2d3403b3 jdk7-b141
|
5d86d0c7692e8f4a58d430d68c03594e2d3403b3 jdk7-b141
|
||||||
|
92bf0655022d4187e9b49c1400f98fb3392a4630 jdk7-b142
|
||||||
|
|
|
@ -116,3 +116,4 @@ fc47c97bbbd91b1f774d855c48a7e285eb1a351a jdk7-b138
|
||||||
7ed6d0b9aaa12320832a7ddadb88d6d8d0dda4c1 jdk7-b139
|
7ed6d0b9aaa12320832a7ddadb88d6d8d0dda4c1 jdk7-b139
|
||||||
dcfe74f1c6553c556e7d361c30b0b614eb5e40f6 jdk7-b140
|
dcfe74f1c6553c556e7d361c30b0b614eb5e40f6 jdk7-b140
|
||||||
c6569c5585851dfd39b8de8e021c3c312f51af12 jdk7-b141
|
c6569c5585851dfd39b8de8e021c3c312f51af12 jdk7-b141
|
||||||
|
cfbbdb77eac0397b03eb99ee2e07ea00e0a7b81e jdk7-b142
|
||||||
|
|
47
Makefile
47
Makefile
|
@ -25,6 +25,34 @@
|
||||||
|
|
||||||
BUILD_PARENT_DIRECTORY=.
|
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
|
ifndef TOPDIR
|
||||||
TOPDIR:=.
|
TOPDIR:=.
|
||||||
endif
|
endif
|
||||||
|
@ -98,8 +126,7 @@ endef
|
||||||
|
|
||||||
# Generic build of basic repo series
|
# Generic build of basic repo series
|
||||||
generic_build_repo_series:: $(SOURCE_TIPS)
|
generic_build_repo_series:: $(SOURCE_TIPS)
|
||||||
$(MKDIR) -p $(OUTPUTDIR)
|
$(MKDIR) -p $(JDK_IMAGE_DIR)
|
||||||
$(MKDIR) -p $(OUTPUTDIR)/j2sdk-image
|
|
||||||
@$(call StartTimer)
|
@$(call StartTimer)
|
||||||
|
|
||||||
ifeq ($(BUILD_LANGTOOLS), true)
|
ifeq ($(BUILD_LANGTOOLS), true)
|
||||||
|
@ -146,8 +173,8 @@ generic_build_repo_series::
|
||||||
#
|
#
|
||||||
# DEBUG_NAME is fastdebug or debug
|
# DEBUG_NAME is fastdebug or debug
|
||||||
# ALT_OUTPUTDIR is changed to have -debug or -fastdebug suffix
|
# ALT_OUTPUTDIR is changed to have -debug or -fastdebug suffix
|
||||||
# The resulting j2sdk-image is used by the install makefiles to create a
|
# The resulting image directory (j2sdk-image) is used by the install makefiles
|
||||||
# debug install bundle jdk-*-debug-** bundle (tar or zip)
|
# to create a debug install bundle jdk-*-debug-** bundle (tar or zip)
|
||||||
# which will install in the debug or fastdebug subdirectory of the
|
# which will install in the debug or fastdebug subdirectory of the
|
||||||
# normal product install area.
|
# normal product install area.
|
||||||
# The install process needs to know what the DEBUG_NAME is, so
|
# 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
|
# Location of fresh bootdir output
|
||||||
ABS_BOOTDIR_OUTPUTDIR=$(ABS_OUTPUTDIR)/bootjdk
|
ABS_BOOTDIR_OUTPUTDIR=$(ABS_OUTPUTDIR)/bootjdk
|
||||||
FRESH_BOOTDIR=$(ABS_BOOTDIR_OUTPUTDIR)/j2sdk-image
|
FRESH_BOOTDIR=$(ABS_BOOTDIR_OUTPUTDIR)/$(JDK_IMAGE_DIRNAME)
|
||||||
FRESH_DEBUG_BOOTDIR=$(ABS_BOOTDIR_OUTPUTDIR)/../$(PLATFORM)-$(ARCH)-$(DEBUG_NAME)/j2sdk-image
|
FRESH_DEBUG_BOOTDIR=$(ABS_BOOTDIR_OUTPUTDIR)/$(REL_JDK_IMAGE_DIR)
|
||||||
|
|
||||||
create_fresh_product_bootdir: FRC
|
create_fresh_product_bootdir: FRC
|
||||||
$(MAKE) ALT_OUTPUTDIR=$(ABS_BOOTDIR_OUTPUTDIR) \
|
$(MAKE) ALT_OUTPUTDIR=$(ABS_BOOTDIR_OUTPUTDIR) \
|
||||||
|
@ -226,7 +253,7 @@ build_product_image:
|
||||||
|
|
||||||
generic_debug_build:
|
generic_debug_build:
|
||||||
$(MAKE) \
|
$(MAKE) \
|
||||||
ALT_OUTPUTDIR=$(ABS_OUTPUTDIR)/../$(PLATFORM)-$(ARCH)-$(DEBUG_NAME) \
|
ALT_OUTPUTDIR=$(ABS_OUTPUTDIR)/$(REL_JDK_OUTPUTDIR) \
|
||||||
DEBUG_NAME=$(DEBUG_NAME) \
|
DEBUG_NAME=$(DEBUG_NAME) \
|
||||||
GENERATE_DOCS=false \
|
GENERATE_DOCS=false \
|
||||||
$(BOOT_CYCLE_DEBUG_SETTINGS) \
|
$(BOOT_CYCLE_DEBUG_SETTINGS) \
|
||||||
|
@ -254,8 +281,6 @@ $(SOURCE_TIPS): FRC
|
||||||
clobber:: REPORT_BUILD_TIMES=
|
clobber:: REPORT_BUILD_TIMES=
|
||||||
clobber::
|
clobber::
|
||||||
$(RM) -r $(OUTPUTDIR)/*
|
$(RM) -r $(OUTPUTDIR)/*
|
||||||
$(RM) -r $(OUTPUTDIR)/../$(PLATFORM)-$(ARCH)-debug/*
|
|
||||||
$(RM) -r $(OUTPUTDIR)/../$(PLATFORM)-$(ARCH)-fastdebug/*
|
|
||||||
-($(RMDIR) -p $(OUTPUTDIR) > $(DEV_NULL) 2>&1; $(TRUE))
|
-($(RMDIR) -p $(OUTPUTDIR) > $(DEV_NULL) 2>&1; $(TRUE))
|
||||||
|
|
||||||
clean: clobber
|
clean: clobber
|
||||||
|
@ -489,8 +514,8 @@ $(OUTPUTDIR)/test_failures.txt: $(OUTPUTDIR)/test_log.txt
|
||||||
|
|
||||||
# Get log file of all tests run
|
# Get log file of all tests run
|
||||||
JDK_TO_TEST := $(shell \
|
JDK_TO_TEST := $(shell \
|
||||||
if [ -d "$(ABS_OUTPUTDIR)/j2sdk-image" ] ; then \
|
if [ -d "$(ABS_JDK_IMAGE_DIR)" ] ; then \
|
||||||
$(ECHO) "$(ABS_OUTPUTDIR)/j2sdk-image"; \
|
$(ECHO) "$(ABS_JDK_IMAGE_DIR)"; \
|
||||||
elif [ -d "$(ABS_OUTPUTDIR)/bin" ] ; then \
|
elif [ -d "$(ABS_OUTPUTDIR)/bin" ] ; then \
|
||||||
$(ECHO) "$(ABS_OUTPUTDIR)"; \
|
$(ECHO) "$(ABS_OUTPUTDIR)"; \
|
||||||
elif [ "$(PRODUCT_HOME)" != "" -a -d "$(PRODUCT_HOME)/bin" ] ; then \
|
elif [ "$(PRODUCT_HOME)" != "" -a -d "$(PRODUCT_HOME)/bin" ] ; then \
|
||||||
|
|
|
@ -116,3 +116,4 @@ a66c01d8bf895261715955df0b95545c000ed6a8 jdk7-b137
|
||||||
60b074ec6fcf5cdf9efce22fdfb02326ed8fa2d3 jdk7-b139
|
60b074ec6fcf5cdf9efce22fdfb02326ed8fa2d3 jdk7-b139
|
||||||
cdf5d19ec142424489549025e9c42e51f32cf688 jdk7-b140
|
cdf5d19ec142424489549025e9c42e51f32cf688 jdk7-b140
|
||||||
a58635cdd921bafef353f4864184a0481353197b jdk7-b141
|
a58635cdd921bafef353f4864184a0481353197b jdk7-b141
|
||||||
|
a2f340a048c88d10cbedc0504f5cf03d39925a40 jdk7-b142
|
||||||
|
|
|
@ -170,3 +170,5 @@ d283b82966712b353fa307845a1316da42a355f4 jdk7-b140
|
||||||
d283b82966712b353fa307845a1316da42a355f4 hs21-b10
|
d283b82966712b353fa307845a1316da42a355f4 hs21-b10
|
||||||
5d07913abd59261c77f24cc04a759cb75d804099 jdk7-b141
|
5d07913abd59261c77f24cc04a759cb75d804099 jdk7-b141
|
||||||
3aea9e9feb073f5500e031be6186666bcae89aa2 hs21-b11
|
3aea9e9feb073f5500e031be6186666bcae89aa2 hs21-b11
|
||||||
|
9ad1548c6b63d596c411afc35147ffd5254426d9 jdk7-b142
|
||||||
|
9ad1548c6b63d596c411afc35147ffd5254426d9 hs21-b12
|
||||||
|
|
|
@ -35,7 +35,7 @@ HOTSPOT_VM_COPYRIGHT=Copyright 2011
|
||||||
|
|
||||||
HS_MAJOR_VER=21
|
HS_MAJOR_VER=21
|
||||||
HS_MINOR_VER=0
|
HS_MINOR_VER=0
|
||||||
HS_BUILD_NUMBER=12
|
HS_BUILD_NUMBER=13
|
||||||
|
|
||||||
JDK_MAJOR_VER=1
|
JDK_MAJOR_VER=1
|
||||||
JDK_MINOR_VER=7
|
JDK_MINOR_VER=7
|
||||||
|
|
|
@ -2176,6 +2176,7 @@ int AbstractInterpreter::layout_activation(methodOop method,
|
||||||
int tempcount, // Number of slots on java expression stack in use
|
int tempcount, // Number of slots on java expression stack in use
|
||||||
int popframe_extra_args,
|
int popframe_extra_args,
|
||||||
int moncount, // Number of active monitors
|
int moncount, // Number of active monitors
|
||||||
|
int caller_actual_parameters,
|
||||||
int callee_param_size,
|
int callee_param_size,
|
||||||
int callee_locals_size,
|
int callee_locals_size,
|
||||||
frame* caller,
|
frame* caller,
|
||||||
|
|
|
@ -811,7 +811,7 @@ intptr_t* frame::interpreter_frame_tos_at(jint offset) const {
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
|
|
||||||
#define DESCRIBE_FP_OFFSET(name) \
|
#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) {
|
void frame::describe_pd(FrameValues& values, int frame_no) {
|
||||||
for (int w = 0; w < frame::register_save_words; w++) {
|
for (int w = 0; w < frame::register_save_words; w++) {
|
||||||
|
|
|
@ -423,25 +423,6 @@ bool AbstractInterpreter::can_be_compiled(methodHandle m) {
|
||||||
return true;
|
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) {
|
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
|
// This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in
|
||||||
|
|
|
@ -142,18 +142,8 @@ address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler*
|
||||||
Register O2_form = O2_scratch;
|
Register O2_form = O2_scratch;
|
||||||
Register O3_adapter = O3_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(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);
|
__ 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:
|
__ verify_oop(O3_adapter);
|
||||||
__ 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();
|
|
||||||
__ st_ptr(O3_adapter, Address(O4_argbase, 1 * Interpreter::stackElementSize));
|
__ st_ptr(O3_adapter, Address(O4_argbase, 1 * Interpreter::stackElementSize));
|
||||||
// As a trusted first argument, pass the type being called, so the adapter knows
|
// As a trusted first argument, pass the type being called, so the adapter knows
|
||||||
// the actual types of the arguments and return values.
|
// 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");
|
trace_method_handle(_masm, "invokeGeneric");
|
||||||
__ jump_to_method_handle_entry(G3_method_handle, O1_scratch);
|
__ 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;
|
return entry_point;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1623,6 +1623,7 @@ int AbstractInterpreter::layout_activation(methodOop method,
|
||||||
int tempcount,
|
int tempcount,
|
||||||
int popframe_extra_args,
|
int popframe_extra_args,
|
||||||
int moncount,
|
int moncount,
|
||||||
|
int caller_actual_parameters,
|
||||||
int callee_param_count,
|
int callee_param_count,
|
||||||
int callee_local_count,
|
int callee_local_count,
|
||||||
frame* caller,
|
frame* caller,
|
||||||
|
@ -1698,21 +1699,35 @@ int AbstractInterpreter::layout_activation(methodOop method,
|
||||||
popframe_extra_args;
|
popframe_extra_args;
|
||||||
|
|
||||||
int local_words = method->max_locals() * Interpreter::stackElementWords;
|
int local_words = method->max_locals() * Interpreter::stackElementWords;
|
||||||
|
NEEDS_CLEANUP;
|
||||||
intptr_t* locals;
|
intptr_t* locals;
|
||||||
|
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_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()) {
|
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;
|
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
|
// Caller wants his own SP back
|
||||||
int caller_frame_size = caller->cb()->frame_size();
|
int caller_frame_size = caller->cb()->frame_size();
|
||||||
*interpreter_frame->register_addr(I5_savedSP) = (intptr_t)(caller->fp() - caller_frame_size) - STACK_BIAS;
|
*interpreter_frame->register_addr(I5_savedSP) = (intptr_t)(caller->fp() - caller_frame_size) - 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (TraceDeoptimization) {
|
if (TraceDeoptimization) {
|
||||||
|
|
|
@ -234,6 +234,20 @@ class Address VALUE_OBJ_CLASS_SPEC {
|
||||||
a._disp += disp;
|
a._disp += disp;
|
||||||
return a;
|
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
|
// The following two overloads are used in connection with the
|
||||||
// ByteSize type (see sizes.hpp). They simplify the use of
|
// 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, Address src) { LP64_ONLY(addq(dst, src)) NOT_LP64(addl(dst, src)); }
|
||||||
void addptr(Register dst, int32_t src);
|
void addptr(Register dst, int32_t src);
|
||||||
void addptr(Register dst, Register 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 dst, int32_t src);
|
||||||
void andptr(Register src1, Register src2) { LP64_ONLY(andq(src1, src2)) NOT_LP64(andl(src1, src2)) ; }
|
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, Address src) { LP64_ONLY(subq(dst, src)) NOT_LP64(subl(dst, src)); }
|
||||||
void subptr(Register dst, int32_t src);
|
void subptr(Register dst, int32_t src);
|
||||||
void subptr(Register dst, Register 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(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)); }
|
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(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
|
#ifdef _LP64
|
||||||
// Generally the next two are only used for moving NULL
|
// Generally the next two are only used for moving NULL
|
||||||
// Although there are situations in initializing the mark word where
|
// Although there are situations in initializing the mark word where
|
||||||
|
|
|
@ -2342,6 +2342,7 @@ int AbstractInterpreter::layout_activation(methodOop method,
|
||||||
int tempcount, //
|
int tempcount, //
|
||||||
int popframe_extra_args,
|
int popframe_extra_args,
|
||||||
int moncount,
|
int moncount,
|
||||||
|
int caller_actual_parameters,
|
||||||
int callee_param_count,
|
int callee_param_count,
|
||||||
int callee_locals,
|
int callee_locals,
|
||||||
frame* caller,
|
frame* caller,
|
||||||
|
|
|
@ -339,7 +339,6 @@ frame frame::sender_for_entry_frame(RegisterMap* map) const {
|
||||||
return fr;
|
return fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// frame::verify_deopt_original_pc
|
// frame::verify_deopt_original_pc
|
||||||
//
|
//
|
||||||
|
@ -361,6 +360,55 @@ void frame::verify_deopt_original_pc(nmethod* nm, intptr_t* unextended_sp, bool
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
// 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).
|
// This is the sp before any possible extension (adapter/locals).
|
||||||
intptr_t* unextended_sp = interpreter_frame_sender_sp();
|
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
|
#ifdef COMPILER2
|
||||||
if (map->update_map()) {
|
if (map->update_map()) {
|
||||||
map->set_location(rbp->as_VMReg(), (address) addr_at(link_offset));
|
update_map_with_saved_link(map, (intptr_t**) 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
|
|
||||||
}
|
}
|
||||||
#endif // COMPILER2
|
#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::sender_for_compiled_frame
|
||||||
frame frame::sender_for_compiled_frame(RegisterMap* map) const {
|
frame frame::sender_for_compiled_frame(RegisterMap* map) const {
|
||||||
assert(map != NULL, "map must be set");
|
assert(map != NULL, "map must be set");
|
||||||
|
assert(!is_ricochet_frame(), "caller must handle this");
|
||||||
|
|
||||||
// frame owned by optimizing compiler
|
// frame owned by optimizing compiler
|
||||||
assert(_cb->frame_size() >= 0, "must have non-zero frame size");
|
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.
|
// 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?).
|
// 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);
|
intptr_t** saved_fp_addr = (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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map->update_map()) {
|
if (map->update_map()) {
|
||||||
// Tell GC to use argument oopmaps for some runtime stubs that need it.
|
// 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) {
|
if (_cb->oop_maps() != NULL) {
|
||||||
OopMapSet::update_register_map(this, map);
|
OopMapSet::update_register_map(this, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since the prolog does the save and restore of EBP there is no oopmap
|
// 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
|
// 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.
|
// 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));
|
update_map_with_saved_link(map, saved_fp_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) (sender_sp - frame::sender_sp_offset));
|
|
||||||
}
|
|
||||||
#endif // AMD64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(sender_sp != sp(), "must have changed");
|
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_entry_frame()) return sender_for_entry_frame(map);
|
||||||
if (is_interpreted_frame()) return sender_for_interpreter_frame(map);
|
if (is_interpreted_frame()) return sender_for_interpreter_frame(map);
|
||||||
assert(_cb == CodeCache::find_blob(pc()),"Must be the same");
|
assert(_cb == CodeCache::find_blob(pc()),"Must be the same");
|
||||||
|
if (is_ricochet_frame()) return sender_for_ricochet_frame(map);
|
||||||
|
|
||||||
if (_cb != NULL) {
|
if (_cb != NULL) {
|
||||||
return sender_for_compiled_frame(map);
|
return sender_for_compiled_frame(map);
|
||||||
|
@ -673,7 +650,7 @@ intptr_t* frame::interpreter_frame_tos_at(jint offset) const {
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
|
|
||||||
#define DESCRIBE_FP_OFFSET(name) \
|
#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) {
|
void frame::describe_pd(FrameValues& values, int frame_no) {
|
||||||
if (is_interpreted_frame()) {
|
if (is_interpreted_frame()) {
|
||||||
|
|
|
@ -164,6 +164,7 @@
|
||||||
// original sp we use that convention.
|
// original sp we use that convention.
|
||||||
|
|
||||||
intptr_t* _unextended_sp;
|
intptr_t* _unextended_sp;
|
||||||
|
void adjust_unextended_sp();
|
||||||
|
|
||||||
intptr_t* ptr_at_addr(int offset) const {
|
intptr_t* ptr_at_addr(int offset) const {
|
||||||
return (intptr_t*) addr_at(offset);
|
return (intptr_t*) addr_at(offset);
|
||||||
|
@ -197,6 +198,9 @@
|
||||||
// expression stack tos if we are nested in a java call
|
// expression stack tos if we are nested in a java call
|
||||||
intptr_t* interpreter_frame_last_sp() const;
|
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
|
#ifndef CC_INTERP
|
||||||
// deoptimization support
|
// deoptimization support
|
||||||
void interpreter_frame_set_last_sp(intptr_t* sp);
|
void interpreter_frame_set_last_sp(intptr_t* sp);
|
||||||
|
|
|
@ -62,6 +62,7 @@ inline frame::frame(intptr_t* sp, intptr_t* unextended_sp, intptr_t* fp, address
|
||||||
_pc = pc;
|
_pc = pc;
|
||||||
assert(pc != NULL, "no pc?");
|
assert(pc != NULL, "no pc?");
|
||||||
_cb = CodeCache::find_blob(pc);
|
_cb = CodeCache::find_blob(pc);
|
||||||
|
adjust_unextended_sp();
|
||||||
|
|
||||||
address original_pc = nmethod::get_deopt_original_pc(this);
|
address original_pc = nmethod::get_deopt_original_pc(this);
|
||||||
if (original_pc != NULL) {
|
if (original_pc != NULL) {
|
||||||
|
|
|
@ -26,7 +26,9 @@
|
||||||
#define CPU_X86_VM_INTERPRETER_X86_HPP
|
#define CPU_X86_VM_INTERPRETER_X86_HPP
|
||||||
|
|
||||||
public:
|
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)
|
// Offset from rsp (which points to the last stack element)
|
||||||
static int expr_offset_in_bytes(int i) { return stackElementSize * i; }
|
static int expr_offset_in_bytes(int i) { return stackElementSize * i; }
|
||||||
|
|
|
@ -242,26 +242,6 @@ address InterpreterGenerator::generate_method_handle_entry(void) {
|
||||||
return entry_point;
|
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) {
|
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
|
// This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in
|
||||||
|
|
|
@ -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) {
|
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
|
// This code is sort of the equivalent of C2IAdapter::setup_stack_frame back in
|
||||||
|
|
File diff suppressed because it is too large
Load diff
292
hotspot/src/cpu/x86/vm/methodHandles_x86.hpp
Normal file
292
hotspot/src/cpu/x86/vm/methodHandles_x86.hpp
Normal file
|
@ -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);
|
||||||
|
}
|
|
@ -2253,6 +2253,31 @@ uint SharedRuntime::out_preserve_stack_slots() {
|
||||||
return 0;
|
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----------------------------
|
//------------------------------generate_deopt_blob----------------------------
|
||||||
void SharedRuntime::generate_deopt_blob() {
|
void SharedRuntime::generate_deopt_blob() {
|
||||||
|
@ -2996,6 +3021,8 @@ void SharedRuntime::generate_stubs() {
|
||||||
generate_handler_blob(CAST_FROM_FN_PTR(address,
|
generate_handler_blob(CAST_FROM_FN_PTR(address,
|
||||||
SafepointSynchronize::handle_polling_page_exception), true);
|
SafepointSynchronize::handle_polling_page_exception), true);
|
||||||
|
|
||||||
|
generate_ricochet_blob();
|
||||||
|
|
||||||
generate_deopt_blob();
|
generate_deopt_blob();
|
||||||
#ifdef COMPILER2
|
#ifdef COMPILER2
|
||||||
generate_uncommon_trap_blob();
|
generate_uncommon_trap_blob();
|
||||||
|
|
|
@ -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----------------------------
|
//------------------------------generate_deopt_blob----------------------------
|
||||||
void SharedRuntime::generate_deopt_blob() {
|
void SharedRuntime::generate_deopt_blob() {
|
||||||
// Allocate space for the code
|
// Allocate space for the code
|
||||||
|
@ -3205,6 +3231,8 @@ void SharedRuntime::generate_stubs() {
|
||||||
generate_handler_blob(CAST_FROM_FN_PTR(address,
|
generate_handler_blob(CAST_FROM_FN_PTR(address,
|
||||||
SafepointSynchronize::handle_polling_page_exception), true);
|
SafepointSynchronize::handle_polling_page_exception), true);
|
||||||
|
|
||||||
|
generate_ricochet_blob();
|
||||||
|
|
||||||
generate_deopt_blob();
|
generate_deopt_blob();
|
||||||
|
|
||||||
#ifdef COMPILER2
|
#ifdef COMPILER2
|
||||||
|
|
|
@ -36,7 +36,7 @@ enum platform_dependent_constants {
|
||||||
|
|
||||||
// MethodHandles adapters
|
// MethodHandles adapters
|
||||||
enum method_handles_platform_dependent_constants {
|
enum method_handles_platform_dependent_constants {
|
||||||
method_handles_adapters_code_size = 10000
|
method_handles_adapters_code_size = 30000 DEBUG_ONLY(+ 10000)
|
||||||
};
|
};
|
||||||
|
|
||||||
class x86 {
|
class x86 {
|
||||||
|
|
|
@ -38,7 +38,7 @@ enum platform_dependent_constants {
|
||||||
|
|
||||||
// MethodHandles adapters
|
// MethodHandles adapters
|
||||||
enum method_handles_platform_dependent_constants {
|
enum method_handles_platform_dependent_constants {
|
||||||
method_handles_adapters_code_size = 40000
|
method_handles_adapters_code_size = 80000 DEBUG_ONLY(+ 120000)
|
||||||
};
|
};
|
||||||
|
|
||||||
class x86 {
|
class x86 {
|
||||||
|
|
|
@ -1589,6 +1589,7 @@ int AbstractInterpreter::layout_activation(methodOop method,
|
||||||
int tempcount,
|
int tempcount,
|
||||||
int popframe_extra_args,
|
int popframe_extra_args,
|
||||||
int moncount,
|
int moncount,
|
||||||
|
int caller_actual_parameters,
|
||||||
int callee_param_count,
|
int callee_param_count,
|
||||||
int callee_locals,
|
int callee_locals,
|
||||||
frame* caller,
|
frame* caller,
|
||||||
|
|
|
@ -1603,6 +1603,7 @@ int AbstractInterpreter::layout_activation(methodOop method,
|
||||||
int tempcount,
|
int tempcount,
|
||||||
int popframe_extra_args,
|
int popframe_extra_args,
|
||||||
int moncount,
|
int moncount,
|
||||||
|
int caller_actual_parameters,
|
||||||
int callee_param_count,
|
int callee_param_count,
|
||||||
int callee_locals,
|
int callee_locals,
|
||||||
frame* caller,
|
frame* caller,
|
||||||
|
|
|
@ -1427,6 +1427,7 @@ int AbstractInterpreter::layout_activation(methodOop method,
|
||||||
int tempcount,
|
int tempcount,
|
||||||
int popframe_extra_args,
|
int popframe_extra_args,
|
||||||
int moncount,
|
int moncount,
|
||||||
|
int caller_actual_parameters,
|
||||||
int callee_param_count,
|
int callee_param_count,
|
||||||
int callee_locals,
|
int callee_locals,
|
||||||
frame* caller,
|
frame* caller,
|
||||||
|
|
|
@ -82,24 +82,6 @@ bool AbstractInterpreter::can_be_compiled(methodHandle m) {
|
||||||
return true;
|
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,
|
void Deoptimization::unwind_callee_save_values(frame* f,
|
||||||
vframeArray* vframe_array) {
|
vframeArray* vframe_array) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -2850,7 +2850,7 @@ bool os::Linux::hugetlbfs_sanity_check(bool warn, size_t page_size) {
|
||||||
char chars[257];
|
char chars[257];
|
||||||
long x = 0;
|
long x = 0;
|
||||||
if (fgets(chars, sizeof(chars), fp)) {
|
if (fgets(chars, sizeof(chars), fp)) {
|
||||||
if (sscanf(chars, "%lx-%*lx", &x) == 1
|
if (sscanf(chars, "%lx-%*x", &x) == 1
|
||||||
&& x == (long)p) {
|
&& x == (long)p) {
|
||||||
if (strstr (chars, "hugepage")) {
|
if (strstr (chars, "hugepage")) {
|
||||||
result = true;
|
result = true;
|
||||||
|
|
|
@ -132,17 +132,22 @@ void InstructionPrinter::print_object(Value obj) {
|
||||||
if (value->is_null_object()) {
|
if (value->is_null_object()) {
|
||||||
output()->print("null");
|
output()->print("null");
|
||||||
} else if (!value->is_loaded()) {
|
} else if (!value->is_loaded()) {
|
||||||
output()->print("<unloaded object 0x%x>", value);
|
output()->print("<unloaded object " PTR_FORMAT ">", value);
|
||||||
} else if (value->is_method()) {
|
} else if (value->is_method()) {
|
||||||
ciMethod* m = (ciMethod*)value;
|
ciMethod* m = (ciMethod*)value;
|
||||||
output()->print("<method %s.%s>", m->holder()->name()->as_utf8(), m->name()->as_utf8());
|
output()->print("<method %s.%s>", m->holder()->name()->as_utf8(), m->name()->as_utf8());
|
||||||
} else {
|
} else {
|
||||||
output()->print("<object 0x%x>", value->constant_encoding());
|
output()->print("<object " PTR_FORMAT ">", value->constant_encoding());
|
||||||
}
|
}
|
||||||
} else if (type->as_InstanceConstant() != NULL) {
|
} else if (type->as_InstanceConstant() != NULL) {
|
||||||
output()->print("<instance 0x%x>", type->as_InstanceConstant()->value()->constant_encoding());
|
ciInstance* value = type->as_InstanceConstant()->value();
|
||||||
|
if (value->is_loaded()) {
|
||||||
|
output()->print("<instance " PTR_FORMAT ">", value->constant_encoding());
|
||||||
|
} else {
|
||||||
|
output()->print("<unloaded instance " PTR_FORMAT ">", value);
|
||||||
|
}
|
||||||
} else if (type->as_ArrayConstant() != NULL) {
|
} else if (type->as_ArrayConstant() != NULL) {
|
||||||
output()->print("<array 0x%x>", type->as_ArrayConstant()->value()->constant_encoding());
|
output()->print("<array " PTR_FORMAT ">", type->as_ArrayConstant()->value()->constant_encoding());
|
||||||
} else if (type->as_ClassConstant() != NULL) {
|
} else if (type->as_ClassConstant() != NULL) {
|
||||||
ciInstanceKlass* klass = type->as_ClassConstant()->value();
|
ciInstanceKlass* klass = type->as_ClassConstant()->value();
|
||||||
if (!klass->is_loaded()) {
|
if (!klass->is_loaded()) {
|
||||||
|
|
|
@ -252,8 +252,8 @@ Value CE_Eliminator::make_ifop(Value x, Instruction::Condition cond, Value y, Va
|
||||||
Constant::CompareResult t_compare_res = x_tval_const->compare(cond, y_const);
|
Constant::CompareResult t_compare_res = x_tval_const->compare(cond, y_const);
|
||||||
Constant::CompareResult f_compare_res = x_fval_const->compare(cond, y_const);
|
Constant::CompareResult f_compare_res = x_fval_const->compare(cond, y_const);
|
||||||
|
|
||||||
guarantee(t_compare_res != Constant::not_comparable && f_compare_res != Constant::not_comparable, "incomparable constants in IfOp");
|
// not_comparable here is a valid return in case we're comparing unloaded oop constants
|
||||||
|
if (t_compare_res != Constant::not_comparable && f_compare_res != Constant::not_comparable) {
|
||||||
Value new_tval = t_compare_res == Constant::cond_true ? tval : fval;
|
Value new_tval = t_compare_res == Constant::cond_true ? tval : fval;
|
||||||
Value new_fval = f_compare_res == Constant::cond_true ? tval : fval;
|
Value new_fval = f_compare_res == Constant::cond_true ? tval : fval;
|
||||||
|
|
||||||
|
@ -264,17 +264,19 @@ Value CE_Eliminator::make_ifop(Value x, Instruction::Condition cond, Value y, Va
|
||||||
return new IfOp(x_ifop->x(), x_ifop_cond, x_ifop->y(), new_tval, new_fval);
|
return new IfOp(x_ifop->x(), x_ifop_cond, x_ifop->y(), new_tval, new_fval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Constant* x_const = x->as_Constant();
|
Constant* x_const = x->as_Constant();
|
||||||
if (x_const != NULL) { // x and y are constants
|
if (x_const != NULL) { // x and y are constants
|
||||||
Constant::CompareResult x_compare_res = x_const->compare(cond, y_const);
|
Constant::CompareResult x_compare_res = x_const->compare(cond, y_const);
|
||||||
guarantee(x_compare_res != Constant::not_comparable, "incomparable constants in IfOp");
|
// not_comparable here is a valid return in case we're comparing unloaded oop constants
|
||||||
|
if (x_compare_res != Constant::not_comparable) {
|
||||||
_ifop_count++;
|
_ifop_count++;
|
||||||
return x_compare_res == Constant::cond_true ? tval : fval;
|
return x_compare_res == Constant::cond_true ? tval : fval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return new IfOp(x, cond, y, tval, fval);
|
return new IfOp(x, cond, y, tval, fval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -233,6 +233,9 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool is_method_data() { return true; }
|
bool is_method_data() { return true; }
|
||||||
|
|
||||||
|
void set_mature() { _state = mature_state; }
|
||||||
|
|
||||||
bool is_empty() { return _state == empty_state; }
|
bool is_empty() { return _state == empty_state; }
|
||||||
bool is_mature() { return _state == mature_state; }
|
bool is_mature() { return _state == mature_state; }
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
#include "ci/ciClassList.hpp"
|
#include "ci/ciClassList.hpp"
|
||||||
#include "ci/ciInstance.hpp"
|
#include "ci/ciInstance.hpp"
|
||||||
|
#include "ci/ciMethodData.hpp"
|
||||||
#include "ci/ciMethodHandle.hpp"
|
#include "ci/ciMethodHandle.hpp"
|
||||||
#include "ci/ciUtilities.hpp"
|
#include "ci/ciUtilities.hpp"
|
||||||
#include "prims/methodHandleWalk.hpp"
|
#include "prims/methodHandleWalk.hpp"
|
||||||
|
@ -36,13 +37,13 @@
|
||||||
// ciMethodHandle::get_adapter
|
// ciMethodHandle::get_adapter
|
||||||
//
|
//
|
||||||
// Return an adapter for this MethodHandle.
|
// Return an adapter for this MethodHandle.
|
||||||
ciMethod* ciMethodHandle::get_adapter(bool is_invokedynamic) const {
|
ciMethod* ciMethodHandle::get_adapter_impl(bool is_invokedynamic) const {
|
||||||
VM_ENTRY_MARK;
|
VM_ENTRY_MARK;
|
||||||
Handle h(get_oop());
|
Handle h(get_oop());
|
||||||
methodHandle callee(_callee->get_methodOop());
|
methodHandle callee(_callee->get_methodOop());
|
||||||
// We catch all exceptions here that could happen in the method
|
// We catch all exceptions here that could happen in the method
|
||||||
// handle compiler and stop the VM.
|
// handle compiler and stop the VM.
|
||||||
MethodHandleCompiler mhc(h, callee, is_invokedynamic, THREAD);
|
MethodHandleCompiler mhc(h, callee, _profile->count(), is_invokedynamic, THREAD);
|
||||||
if (!HAS_PENDING_EXCEPTION) {
|
if (!HAS_PENDING_EXCEPTION) {
|
||||||
methodHandle m = mhc.compile(THREAD);
|
methodHandle m = mhc.compile(THREAD);
|
||||||
if (!HAS_PENDING_EXCEPTION) {
|
if (!HAS_PENDING_EXCEPTION) {
|
||||||
|
@ -58,6 +59,22 @@ ciMethod* ciMethodHandle::get_adapter(bool is_invokedynamic) const {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// ciMethodHandle::get_adapter
|
||||||
|
//
|
||||||
|
// Return an adapter for this MethodHandle.
|
||||||
|
ciMethod* ciMethodHandle::get_adapter(bool is_invokedynamic) const {
|
||||||
|
ciMethod* result = get_adapter_impl(is_invokedynamic);
|
||||||
|
if (result) {
|
||||||
|
// Fake up the MDO maturity.
|
||||||
|
ciMethodData* mdo = result->method_data();
|
||||||
|
if (mdo != NULL && _caller->method_data() != NULL && _caller->method_data()->is_mature()) {
|
||||||
|
mdo->set_mature();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------
|
// ------------------------------------------------------------------
|
||||||
// ciMethodHandle::print_impl
|
// ciMethodHandle::print_impl
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#ifndef SHARE_VM_CI_CIMETHODHANDLE_HPP
|
#ifndef SHARE_VM_CI_CIMETHODHANDLE_HPP
|
||||||
#define SHARE_VM_CI_CIMETHODHANDLE_HPP
|
#define SHARE_VM_CI_CIMETHODHANDLE_HPP
|
||||||
|
|
||||||
|
#include "ci/ciCallProfile.hpp"
|
||||||
#include "ci/ciInstance.hpp"
|
#include "ci/ciInstance.hpp"
|
||||||
#include "prims/methodHandles.hpp"
|
#include "prims/methodHandles.hpp"
|
||||||
|
|
||||||
|
@ -34,31 +35,36 @@
|
||||||
class ciMethodHandle : public ciInstance {
|
class ciMethodHandle : public ciInstance {
|
||||||
private:
|
private:
|
||||||
ciMethod* _callee;
|
ciMethod* _callee;
|
||||||
|
ciMethod* _caller;
|
||||||
|
ciCallProfile* _profile;
|
||||||
|
|
||||||
// Return an adapter for this MethodHandle.
|
// Return an adapter for this MethodHandle.
|
||||||
|
ciMethod* get_adapter_impl(bool is_invokedynamic) const;
|
||||||
ciMethod* get_adapter( bool is_invokedynamic) const;
|
ciMethod* get_adapter( bool is_invokedynamic) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void print_impl(outputStream* st);
|
void print_impl(outputStream* st);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ciMethodHandle(instanceHandle h_i) : ciInstance(h_i) {};
|
ciMethodHandle(instanceHandle h_i) :
|
||||||
|
ciInstance(h_i),
|
||||||
|
_callee(NULL),
|
||||||
|
_caller(NULL),
|
||||||
|
_profile(NULL)
|
||||||
|
{}
|
||||||
|
|
||||||
// What kind of ciObject is this?
|
// What kind of ciObject is this?
|
||||||
bool is_method_handle() const { return true; }
|
bool is_method_handle() const { return true; }
|
||||||
|
|
||||||
ciMethod* callee() const { return _callee; }
|
|
||||||
void set_callee(ciMethod* m) { _callee = m; }
|
void set_callee(ciMethod* m) { _callee = m; }
|
||||||
|
void set_caller(ciMethod* m) { _caller = m; }
|
||||||
|
void set_call_profile(ciCallProfile* profile) { _profile = profile; }
|
||||||
|
|
||||||
// Return an adapter for a MethodHandle call.
|
// Return an adapter for a MethodHandle call.
|
||||||
ciMethod* get_method_handle_adapter() const {
|
ciMethod* get_method_handle_adapter() const { return get_adapter(false); }
|
||||||
return get_adapter(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return an adapter for an invokedynamic call.
|
// Return an adapter for an invokedynamic call.
|
||||||
ciMethod* get_invokedynamic_adapter() const {
|
ciMethod* get_invokedynamic_adapter() const { return get_adapter(true); }
|
||||||
return get_adapter(true);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SHARE_VM_CI_CIMETHODHANDLE_HPP
|
#endif // SHARE_VM_CI_CIMETHODHANDLE_HPP
|
||||||
|
|
|
@ -2602,6 +2602,7 @@ int java_lang_invoke_MethodType::ptype_count(oop mt) {
|
||||||
// Support for java_lang_invoke_MethodTypeForm
|
// Support for java_lang_invoke_MethodTypeForm
|
||||||
|
|
||||||
int java_lang_invoke_MethodTypeForm::_vmslots_offset;
|
int java_lang_invoke_MethodTypeForm::_vmslots_offset;
|
||||||
|
int java_lang_invoke_MethodTypeForm::_vmlayout_offset;
|
||||||
int java_lang_invoke_MethodTypeForm::_erasedType_offset;
|
int java_lang_invoke_MethodTypeForm::_erasedType_offset;
|
||||||
int java_lang_invoke_MethodTypeForm::_genericInvoker_offset;
|
int java_lang_invoke_MethodTypeForm::_genericInvoker_offset;
|
||||||
|
|
||||||
|
@ -2609,6 +2610,7 @@ void java_lang_invoke_MethodTypeForm::compute_offsets() {
|
||||||
klassOop k = SystemDictionary::MethodTypeForm_klass();
|
klassOop k = SystemDictionary::MethodTypeForm_klass();
|
||||||
if (k != NULL) {
|
if (k != NULL) {
|
||||||
compute_optional_offset(_vmslots_offset, k, vmSymbols::vmslots_name(), vmSymbols::int_signature(), true);
|
compute_optional_offset(_vmslots_offset, k, vmSymbols::vmslots_name(), vmSymbols::int_signature(), true);
|
||||||
|
compute_optional_offset(_vmlayout_offset, k, vmSymbols::vmlayout_name(), vmSymbols::object_signature());
|
||||||
compute_optional_offset(_erasedType_offset, k, vmSymbols::erasedType_name(), vmSymbols::java_lang_invoke_MethodType_signature(), true);
|
compute_optional_offset(_erasedType_offset, k, vmSymbols::erasedType_name(), vmSymbols::java_lang_invoke_MethodType_signature(), true);
|
||||||
compute_optional_offset(_genericInvoker_offset, k, vmSymbols::genericInvoker_name(), vmSymbols::java_lang_invoke_MethodHandle_signature(), true);
|
compute_optional_offset(_genericInvoker_offset, k, vmSymbols::genericInvoker_name(), vmSymbols::java_lang_invoke_MethodHandle_signature(), true);
|
||||||
if (_genericInvoker_offset == 0) _genericInvoker_offset = -1; // set to explicit "empty" value
|
if (_genericInvoker_offset == 0) _genericInvoker_offset = -1; // set to explicit "empty" value
|
||||||
|
@ -2617,9 +2619,31 @@ void java_lang_invoke_MethodTypeForm::compute_offsets() {
|
||||||
|
|
||||||
int java_lang_invoke_MethodTypeForm::vmslots(oop mtform) {
|
int java_lang_invoke_MethodTypeForm::vmslots(oop mtform) {
|
||||||
assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only");
|
assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only");
|
||||||
|
assert(_vmslots_offset > 0, "");
|
||||||
return mtform->int_field(_vmslots_offset);
|
return mtform->int_field(_vmslots_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oop java_lang_invoke_MethodTypeForm::vmlayout(oop mtform) {
|
||||||
|
assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only");
|
||||||
|
assert(_vmlayout_offset > 0, "");
|
||||||
|
return mtform->obj_field(_vmlayout_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
oop java_lang_invoke_MethodTypeForm::init_vmlayout(oop mtform, oop cookie) {
|
||||||
|
assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only");
|
||||||
|
oop previous = vmlayout(mtform);
|
||||||
|
if (previous != NULL) {
|
||||||
|
return previous; // someone else beat us to it
|
||||||
|
}
|
||||||
|
HeapWord* cookie_addr = (HeapWord*) mtform->obj_field_addr<oop>(_vmlayout_offset);
|
||||||
|
OrderAccess::storestore(); // make sure our copy is fully committed
|
||||||
|
previous = oopDesc::atomic_compare_exchange_oop(cookie, cookie_addr, previous);
|
||||||
|
if (previous != NULL) {
|
||||||
|
return previous; // someone else beat us to it
|
||||||
|
}
|
||||||
|
return cookie;
|
||||||
|
}
|
||||||
|
|
||||||
oop java_lang_invoke_MethodTypeForm::erasedType(oop mtform) {
|
oop java_lang_invoke_MethodTypeForm::erasedType(oop mtform) {
|
||||||
assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only");
|
assert(mtform->klass() == SystemDictionary::MethodTypeForm_klass(), "MTForm only");
|
||||||
return mtform->obj_field(_erasedType_offset);
|
return mtform->obj_field(_erasedType_offset);
|
||||||
|
|
|
@ -949,18 +949,19 @@ class java_lang_invoke_AdapterMethodHandle: public java_lang_invoke_BoundMethodH
|
||||||
OP_CHECK_CAST = 0x2, // ref-to-ref conversion; requires a Class argument
|
OP_CHECK_CAST = 0x2, // ref-to-ref conversion; requires a Class argument
|
||||||
OP_PRIM_TO_PRIM = 0x3, // converts from one primitive to another
|
OP_PRIM_TO_PRIM = 0x3, // converts from one primitive to another
|
||||||
OP_REF_TO_PRIM = 0x4, // unboxes a wrapper to produce a primitive
|
OP_REF_TO_PRIM = 0x4, // unboxes a wrapper to produce a primitive
|
||||||
OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper (NYI)
|
OP_PRIM_TO_REF = 0x5, // boxes a primitive into a wrapper
|
||||||
OP_SWAP_ARGS = 0x6, // swap arguments (vminfo is 2nd arg)
|
OP_SWAP_ARGS = 0x6, // swap arguments (vminfo is 2nd arg)
|
||||||
OP_ROT_ARGS = 0x7, // rotate arguments (vminfo is displaced arg)
|
OP_ROT_ARGS = 0x7, // rotate arguments (vminfo is displaced arg)
|
||||||
OP_DUP_ARGS = 0x8, // duplicates one or more arguments (at TOS)
|
OP_DUP_ARGS = 0x8, // duplicates one or more arguments (at TOS)
|
||||||
OP_DROP_ARGS = 0x9, // remove one or more argument slots
|
OP_DROP_ARGS = 0x9, // remove one or more argument slots
|
||||||
OP_COLLECT_ARGS = 0xA, // combine one or more arguments into a varargs (NYI)
|
OP_COLLECT_ARGS = 0xA, // combine arguments using an auxiliary function
|
||||||
OP_SPREAD_ARGS = 0xB, // expand in place a varargs array (of known size)
|
OP_SPREAD_ARGS = 0xB, // expand in place a varargs array (of known size)
|
||||||
OP_FLYBY = 0xC, // operate first on reified argument list (NYI)
|
OP_FOLD_ARGS = 0xC, // combine but do not remove arguments; prepend result
|
||||||
OP_RICOCHET = 0xD, // run an adapter chain on the return value (NYI)
|
//OP_UNUSED_13 = 0xD, // unused code, perhaps for reified argument lists
|
||||||
CONV_OP_LIMIT = 0xE, // limit of CONV_OP enumeration
|
CONV_OP_LIMIT = 0xE, // limit of CONV_OP enumeration
|
||||||
|
|
||||||
CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field
|
CONV_OP_MASK = 0xF00, // this nybble contains the conversion op field
|
||||||
|
CONV_TYPE_MASK = 0x0F, // fits T_ADDRESS and below
|
||||||
CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use
|
CONV_VMINFO_MASK = 0x0FF, // LSB is reserved for JVM use
|
||||||
CONV_VMINFO_SHIFT = 0, // position of bits in CONV_VMINFO_MASK
|
CONV_VMINFO_SHIFT = 0, // position of bits in CONV_VMINFO_MASK
|
||||||
CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK
|
CONV_OP_SHIFT = 8, // position of bits in CONV_OP_MASK
|
||||||
|
@ -1089,6 +1090,7 @@ class java_lang_invoke_MethodTypeForm: AllStatic {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int _vmslots_offset; // number of argument slots needed
|
static int _vmslots_offset; // number of argument slots needed
|
||||||
|
static int _vmlayout_offset; // object describing internal calling sequence
|
||||||
static int _erasedType_offset; // erasedType = canonical MethodType
|
static int _erasedType_offset; // erasedType = canonical MethodType
|
||||||
static int _genericInvoker_offset; // genericInvoker = adapter for invokeGeneric
|
static int _genericInvoker_offset; // genericInvoker = adapter for invokeGeneric
|
||||||
|
|
||||||
|
@ -1100,8 +1102,12 @@ class java_lang_invoke_MethodTypeForm: AllStatic {
|
||||||
static oop erasedType(oop mtform);
|
static oop erasedType(oop mtform);
|
||||||
static oop genericInvoker(oop mtform);
|
static oop genericInvoker(oop mtform);
|
||||||
|
|
||||||
|
static oop vmlayout(oop mtform);
|
||||||
|
static oop init_vmlayout(oop mtform, oop cookie);
|
||||||
|
|
||||||
// Accessors for code generation:
|
// Accessors for code generation:
|
||||||
static int vmslots_offset_in_bytes() { return _vmslots_offset; }
|
static int vmslots_offset_in_bytes() { return _vmslots_offset; }
|
||||||
|
static int vmlayout_offset_in_bytes() { return _vmlayout_offset; }
|
||||||
static int erasedType_offset_in_bytes() { return _erasedType_offset; }
|
static int erasedType_offset_in_bytes() { return _erasedType_offset; }
|
||||||
static int genericInvoker_offset_in_bytes() { return _genericInvoker_offset; }
|
static int genericInvoker_offset_in_bytes() { return _genericInvoker_offset; }
|
||||||
};
|
};
|
||||||
|
|
|
@ -2362,8 +2362,15 @@ methodOop SystemDictionary::find_method_handle_invoke(Symbol* name,
|
||||||
spe = invoke_method_table()->find_entry(index, hash, signature, name_id);
|
spe = invoke_method_table()->find_entry(index, hash, signature, name_id);
|
||||||
if (spe == NULL)
|
if (spe == NULL)
|
||||||
spe = invoke_method_table()->add_entry(index, hash, signature, name_id);
|
spe = invoke_method_table()->add_entry(index, hash, signature, name_id);
|
||||||
if (spe->property_oop() == NULL)
|
if (spe->property_oop() == NULL) {
|
||||||
spe->set_property_oop(m());
|
spe->set_property_oop(m());
|
||||||
|
// Link m to his method type, if it is suitably generic.
|
||||||
|
oop mtform = java_lang_invoke_MethodType::form(mt());
|
||||||
|
if (mtform != NULL && mt() == java_lang_invoke_MethodTypeForm::erasedType(mtform)
|
||||||
|
&& java_lang_invoke_MethodTypeForm::vmlayout_offset_in_bytes() > 0) {
|
||||||
|
java_lang_invoke_MethodTypeForm::init_vmlayout(mtform, m());
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
non_cached_result = m;
|
non_cached_result = m;
|
||||||
}
|
}
|
||||||
|
|
|
@ -341,6 +341,7 @@
|
||||||
template(vmtarget_name, "vmtarget") \
|
template(vmtarget_name, "vmtarget") \
|
||||||
template(vmentry_name, "vmentry") \
|
template(vmentry_name, "vmentry") \
|
||||||
template(vmslots_name, "vmslots") \
|
template(vmslots_name, "vmslots") \
|
||||||
|
template(vmlayout_name, "vmlayout") \
|
||||||
template(vmindex_name, "vmindex") \
|
template(vmindex_name, "vmindex") \
|
||||||
template(vmargslot_name, "vmargslot") \
|
template(vmargslot_name, "vmargslot") \
|
||||||
template(flags_name, "flags") \
|
template(flags_name, "flags") \
|
||||||
|
@ -393,6 +394,7 @@
|
||||||
template(void_signature, "V") \
|
template(void_signature, "V") \
|
||||||
template(byte_array_signature, "[B") \
|
template(byte_array_signature, "[B") \
|
||||||
template(char_array_signature, "[C") \
|
template(char_array_signature, "[C") \
|
||||||
|
template(int_array_signature, "[I") \
|
||||||
template(object_void_signature, "(Ljava/lang/Object;)V") \
|
template(object_void_signature, "(Ljava/lang/Object;)V") \
|
||||||
template(object_int_signature, "(Ljava/lang/Object;)I") \
|
template(object_int_signature, "(Ljava/lang/Object;)I") \
|
||||||
template(object_boolean_signature, "(Ljava/lang/Object;)Z") \
|
template(object_boolean_signature, "(Ljava/lang/Object;)Z") \
|
||||||
|
|
|
@ -152,6 +152,32 @@ void CodeBlob::set_oop_maps(OopMapSet* p) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CodeBlob::trace_new_stub(CodeBlob* stub, const char* name1, const char* name2) {
|
||||||
|
// Do not hold the CodeCache lock during name formatting.
|
||||||
|
assert(!CodeCache_lock->owned_by_self(), "release CodeCache before registering the stub");
|
||||||
|
|
||||||
|
if (stub != NULL) {
|
||||||
|
char stub_id[256];
|
||||||
|
assert(strlen(name1) + strlen(name2) < sizeof(stub_id), "");
|
||||||
|
jio_snprintf(stub_id, sizeof(stub_id), "%s%s", name1, name2);
|
||||||
|
if (PrintStubCode) {
|
||||||
|
tty->print_cr("Decoding %s " INTPTR_FORMAT, stub_id, (intptr_t) stub);
|
||||||
|
Disassembler::decode(stub->code_begin(), stub->code_end());
|
||||||
|
}
|
||||||
|
Forte::register_stub(stub_id, stub->code_begin(), stub->code_end());
|
||||||
|
|
||||||
|
if (JvmtiExport::should_post_dynamic_code_generated()) {
|
||||||
|
const char* stub_name = name2;
|
||||||
|
if (name2[0] == '\0') stub_name = name1;
|
||||||
|
JvmtiExport::post_dynamic_code_generated(stub_name, stub->code_begin(), stub->code_end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track memory usage statistic after releasing CodeCache_lock
|
||||||
|
MemoryService::track_code_cache_memory_usage();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void CodeBlob::flush() {
|
void CodeBlob::flush() {
|
||||||
if (_oop_maps) {
|
if (_oop_maps) {
|
||||||
FREE_C_HEAP_ARRAY(unsigned char, _oop_maps);
|
FREE_C_HEAP_ARRAY(unsigned char, _oop_maps);
|
||||||
|
@ -312,23 +338,7 @@ RuntimeStub* RuntimeStub::new_runtime_stub(const char* stub_name,
|
||||||
stub = new (size) RuntimeStub(stub_name, cb, size, frame_complete, frame_size, oop_maps, caller_must_gc_arguments);
|
stub = new (size) RuntimeStub(stub_name, cb, size, frame_complete, frame_size, oop_maps, caller_must_gc_arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not hold the CodeCache lock during name formatting.
|
trace_new_stub(stub, "RuntimeStub - ", stub_name);
|
||||||
if (stub != NULL) {
|
|
||||||
char stub_id[256];
|
|
||||||
jio_snprintf(stub_id, sizeof(stub_id), "RuntimeStub - %s", stub_name);
|
|
||||||
if (PrintStubCode) {
|
|
||||||
tty->print_cr("Decoding %s " INTPTR_FORMAT, stub_id, stub);
|
|
||||||
Disassembler::decode(stub->code_begin(), stub->code_end());
|
|
||||||
}
|
|
||||||
Forte::register_stub(stub_id, stub->code_begin(), stub->code_end());
|
|
||||||
|
|
||||||
if (JvmtiExport::should_post_dynamic_code_generated()) {
|
|
||||||
JvmtiExport::post_dynamic_code_generated(stub_name, stub->code_begin(), stub->code_end());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Track memory usage statistic after releasing CodeCache_lock
|
|
||||||
MemoryService::track_code_cache_memory_usage();
|
|
||||||
|
|
||||||
return stub;
|
return stub;
|
||||||
}
|
}
|
||||||
|
@ -340,6 +350,50 @@ void* RuntimeStub::operator new(size_t s, unsigned size) {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// operator new shared by all singletons:
|
||||||
|
void* SingletonBlob::operator new(size_t s, unsigned size) {
|
||||||
|
void* p = CodeCache::allocate(size);
|
||||||
|
if (!p) fatal("Initial size of CodeCache is too small");
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
// Implementation of RicochetBlob
|
||||||
|
|
||||||
|
RicochetBlob::RicochetBlob(
|
||||||
|
CodeBuffer* cb,
|
||||||
|
int size,
|
||||||
|
int bounce_offset,
|
||||||
|
int exception_offset,
|
||||||
|
int frame_size
|
||||||
|
)
|
||||||
|
: SingletonBlob("RicochetBlob", cb, sizeof(RicochetBlob), size, frame_size, (OopMapSet*) NULL)
|
||||||
|
{
|
||||||
|
_bounce_offset = bounce_offset;
|
||||||
|
_exception_offset = exception_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RicochetBlob* RicochetBlob::create(
|
||||||
|
CodeBuffer* cb,
|
||||||
|
int bounce_offset,
|
||||||
|
int exception_offset,
|
||||||
|
int frame_size)
|
||||||
|
{
|
||||||
|
RicochetBlob* blob = NULL;
|
||||||
|
ThreadInVMfromUnknown __tiv; // get to VM state in case we block on CodeCache_lock
|
||||||
|
{
|
||||||
|
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
||||||
|
unsigned int size = allocation_size(cb, sizeof(RicochetBlob));
|
||||||
|
blob = new (size) RicochetBlob(cb, size, bounce_offset, exception_offset, frame_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_new_stub(blob, "RicochetBlob");
|
||||||
|
|
||||||
|
return blob;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
// Implementation of DeoptimizationBlob
|
// Implementation of DeoptimizationBlob
|
||||||
|
@ -386,34 +440,12 @@ DeoptimizationBlob* DeoptimizationBlob::create(
|
||||||
frame_size);
|
frame_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not hold the CodeCache lock during name formatting.
|
trace_new_stub(blob, "DeoptimizationBlob");
|
||||||
if (blob != NULL) {
|
|
||||||
char blob_id[256];
|
|
||||||
jio_snprintf(blob_id, sizeof(blob_id), "DeoptimizationBlob@" PTR_FORMAT, blob->code_begin());
|
|
||||||
if (PrintStubCode) {
|
|
||||||
tty->print_cr("Decoding %s " INTPTR_FORMAT, blob_id, blob);
|
|
||||||
Disassembler::decode(blob->code_begin(), blob->code_end());
|
|
||||||
}
|
|
||||||
Forte::register_stub(blob_id, blob->code_begin(), blob->code_end());
|
|
||||||
|
|
||||||
if (JvmtiExport::should_post_dynamic_code_generated()) {
|
|
||||||
JvmtiExport::post_dynamic_code_generated("DeoptimizationBlob", blob->code_begin(), blob->code_end());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Track memory usage statistic after releasing CodeCache_lock
|
|
||||||
MemoryService::track_code_cache_memory_usage();
|
|
||||||
|
|
||||||
return blob;
|
return blob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void* DeoptimizationBlob::operator new(size_t s, unsigned size) {
|
|
||||||
void* p = CodeCache::allocate(size);
|
|
||||||
if (!p) fatal("Initial size of CodeCache is too small");
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
// Implementation of UncommonTrapBlob
|
// Implementation of UncommonTrapBlob
|
||||||
|
|
||||||
|
@ -441,33 +473,12 @@ UncommonTrapBlob* UncommonTrapBlob::create(
|
||||||
blob = new (size) UncommonTrapBlob(cb, size, oop_maps, frame_size);
|
blob = new (size) UncommonTrapBlob(cb, size, oop_maps, frame_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not hold the CodeCache lock during name formatting.
|
trace_new_stub(blob, "UncommonTrapBlob");
|
||||||
if (blob != NULL) {
|
|
||||||
char blob_id[256];
|
|
||||||
jio_snprintf(blob_id, sizeof(blob_id), "UncommonTrapBlob@" PTR_FORMAT, blob->code_begin());
|
|
||||||
if (PrintStubCode) {
|
|
||||||
tty->print_cr("Decoding %s " INTPTR_FORMAT, blob_id, blob);
|
|
||||||
Disassembler::decode(blob->code_begin(), blob->code_end());
|
|
||||||
}
|
|
||||||
Forte::register_stub(blob_id, blob->code_begin(), blob->code_end());
|
|
||||||
|
|
||||||
if (JvmtiExport::should_post_dynamic_code_generated()) {
|
|
||||||
JvmtiExport::post_dynamic_code_generated("UncommonTrapBlob", blob->code_begin(), blob->code_end());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Track memory usage statistic after releasing CodeCache_lock
|
|
||||||
MemoryService::track_code_cache_memory_usage();
|
|
||||||
|
|
||||||
return blob;
|
return blob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void* UncommonTrapBlob::operator new(size_t s, unsigned size) {
|
|
||||||
void* p = CodeCache::allocate(size);
|
|
||||||
if (!p) fatal("Initial size of CodeCache is too small");
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
#endif // COMPILER2
|
#endif // COMPILER2
|
||||||
|
|
||||||
|
|
||||||
|
@ -498,33 +509,12 @@ ExceptionBlob* ExceptionBlob::create(
|
||||||
blob = new (size) ExceptionBlob(cb, size, oop_maps, frame_size);
|
blob = new (size) ExceptionBlob(cb, size, oop_maps, frame_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We do not need to hold the CodeCache lock during name formatting
|
trace_new_stub(blob, "ExceptionBlob");
|
||||||
if (blob != NULL) {
|
|
||||||
char blob_id[256];
|
|
||||||
jio_snprintf(blob_id, sizeof(blob_id), "ExceptionBlob@" PTR_FORMAT, blob->code_begin());
|
|
||||||
if (PrintStubCode) {
|
|
||||||
tty->print_cr("Decoding %s " INTPTR_FORMAT, blob_id, blob);
|
|
||||||
Disassembler::decode(blob->code_begin(), blob->code_end());
|
|
||||||
}
|
|
||||||
Forte::register_stub(blob_id, blob->code_begin(), blob->code_end());
|
|
||||||
|
|
||||||
if (JvmtiExport::should_post_dynamic_code_generated()) {
|
|
||||||
JvmtiExport::post_dynamic_code_generated("ExceptionBlob", blob->code_begin(), blob->code_end());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Track memory usage statistic after releasing CodeCache_lock
|
|
||||||
MemoryService::track_code_cache_memory_usage();
|
|
||||||
|
|
||||||
return blob;
|
return blob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void* ExceptionBlob::operator new(size_t s, unsigned size) {
|
|
||||||
void* p = CodeCache::allocate(size);
|
|
||||||
if (!p) fatal("Initial size of CodeCache is too small");
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
#endif // COMPILER2
|
#endif // COMPILER2
|
||||||
|
|
||||||
|
|
||||||
|
@ -554,35 +544,12 @@ SafepointBlob* SafepointBlob::create(
|
||||||
blob = new (size) SafepointBlob(cb, size, oop_maps, frame_size);
|
blob = new (size) SafepointBlob(cb, size, oop_maps, frame_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We do not need to hold the CodeCache lock during name formatting.
|
trace_new_stub(blob, "SafepointBlob");
|
||||||
if (blob != NULL) {
|
|
||||||
char blob_id[256];
|
|
||||||
jio_snprintf(blob_id, sizeof(blob_id), "SafepointBlob@" PTR_FORMAT, blob->code_begin());
|
|
||||||
if (PrintStubCode) {
|
|
||||||
tty->print_cr("Decoding %s " INTPTR_FORMAT, blob_id, blob);
|
|
||||||
Disassembler::decode(blob->code_begin(), blob->code_end());
|
|
||||||
}
|
|
||||||
Forte::register_stub(blob_id, blob->code_begin(), blob->code_end());
|
|
||||||
|
|
||||||
if (JvmtiExport::should_post_dynamic_code_generated()) {
|
|
||||||
JvmtiExport::post_dynamic_code_generated("SafepointBlob", blob->code_begin(), blob->code_end());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Track memory usage statistic after releasing CodeCache_lock
|
|
||||||
MemoryService::track_code_cache_memory_usage();
|
|
||||||
|
|
||||||
return blob;
|
return blob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void* SafepointBlob::operator new(size_t s, unsigned size) {
|
|
||||||
void* p = CodeCache::allocate(size);
|
|
||||||
if (!p) fatal("Initial size of CodeCache is too small");
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
// Verification and printing
|
// Verification and printing
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
// Suptypes are:
|
// Suptypes are:
|
||||||
// nmethod : Compiled Java methods (include method that calls to native code)
|
// nmethod : Compiled Java methods (include method that calls to native code)
|
||||||
// RuntimeStub : Call to VM runtime methods
|
// RuntimeStub : Call to VM runtime methods
|
||||||
|
// RicochetBlob : Used for blocking MethodHandle adapters
|
||||||
// DeoptimizationBlob : Used for deoptimizatation
|
// DeoptimizationBlob : Used for deoptimizatation
|
||||||
// ExceptionBlob : Used for stack unrolling
|
// ExceptionBlob : Used for stack unrolling
|
||||||
// SafepointBlob : Used to handle illegal instruction exceptions
|
// SafepointBlob : Used to handle illegal instruction exceptions
|
||||||
|
@ -98,6 +99,7 @@ class CodeBlob VALUE_OBJ_CLASS_SPEC {
|
||||||
virtual bool is_buffer_blob() const { return false; }
|
virtual bool is_buffer_blob() const { return false; }
|
||||||
virtual bool is_nmethod() const { return false; }
|
virtual bool is_nmethod() const { return false; }
|
||||||
virtual bool is_runtime_stub() const { return false; }
|
virtual bool is_runtime_stub() const { return false; }
|
||||||
|
virtual bool is_ricochet_stub() const { return false; }
|
||||||
virtual bool is_deoptimization_stub() const { return false; }
|
virtual bool is_deoptimization_stub() const { return false; }
|
||||||
virtual bool is_uncommon_trap_stub() const { return false; }
|
virtual bool is_uncommon_trap_stub() const { return false; }
|
||||||
virtual bool is_exception_stub() const { return false; }
|
virtual bool is_exception_stub() const { return false; }
|
||||||
|
@ -182,6 +184,9 @@ class CodeBlob VALUE_OBJ_CLASS_SPEC {
|
||||||
virtual void print_on(outputStream* st) const;
|
virtual void print_on(outputStream* st) const;
|
||||||
virtual void print_value_on(outputStream* st) const;
|
virtual void print_value_on(outputStream* st) const;
|
||||||
|
|
||||||
|
// Deal with Disassembler, VTune, Forte, JvmtiExport, MemoryService.
|
||||||
|
static void trace_new_stub(CodeBlob* blob, const char* name1, const char* name2 = "");
|
||||||
|
|
||||||
// Print the comment associated with offset on stream, if there is one
|
// Print the comment associated with offset on stream, if there is one
|
||||||
virtual void print_block_comment(outputStream* stream, address block_begin) {
|
virtual void print_block_comment(outputStream* stream, address block_begin) {
|
||||||
intptr_t offset = (intptr_t)(block_begin - code_begin());
|
intptr_t offset = (intptr_t)(block_begin - code_begin());
|
||||||
|
@ -318,6 +323,10 @@ class RuntimeStub: public CodeBlob {
|
||||||
|
|
||||||
class SingletonBlob: public CodeBlob {
|
class SingletonBlob: public CodeBlob {
|
||||||
friend class VMStructs;
|
friend class VMStructs;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void* operator new(size_t s, unsigned size);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SingletonBlob(
|
SingletonBlob(
|
||||||
const char* name,
|
const char* name,
|
||||||
|
@ -340,6 +349,50 @@ class SingletonBlob: public CodeBlob {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------------------------------------
|
||||||
|
// RicochetBlob
|
||||||
|
// Holds an arbitrary argument list indefinitely while Java code executes recursively.
|
||||||
|
|
||||||
|
class RicochetBlob: public SingletonBlob {
|
||||||
|
friend class VMStructs;
|
||||||
|
private:
|
||||||
|
|
||||||
|
int _bounce_offset;
|
||||||
|
int _exception_offset;
|
||||||
|
|
||||||
|
// Creation support
|
||||||
|
RicochetBlob(
|
||||||
|
CodeBuffer* cb,
|
||||||
|
int size,
|
||||||
|
int bounce_offset,
|
||||||
|
int exception_offset,
|
||||||
|
int frame_size
|
||||||
|
);
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Creation
|
||||||
|
static RicochetBlob* create(
|
||||||
|
CodeBuffer* cb,
|
||||||
|
int bounce_offset,
|
||||||
|
int exception_offset,
|
||||||
|
int frame_size
|
||||||
|
);
|
||||||
|
|
||||||
|
// Typing
|
||||||
|
bool is_ricochet_stub() const { return true; }
|
||||||
|
|
||||||
|
// GC for args
|
||||||
|
void preserve_callee_argument_oops(frame fr, const RegisterMap *reg_map, OopClosure* f) { /* Nothing to do */ }
|
||||||
|
|
||||||
|
address bounce_addr() const { return code_begin() + _bounce_offset; }
|
||||||
|
address exception_addr() const { return code_begin() + _exception_offset; }
|
||||||
|
bool returns_to_bounce_addr(address pc) const {
|
||||||
|
address bounce_pc = bounce_addr();
|
||||||
|
return (pc == bounce_pc || (pc + frame::pc_return_offset) == bounce_pc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------------------------------
|
//----------------------------------------------------------------------------------------------------
|
||||||
// DeoptimizationBlob
|
// DeoptimizationBlob
|
||||||
|
|
||||||
|
@ -363,8 +416,6 @@ class DeoptimizationBlob: public SingletonBlob {
|
||||||
int frame_size
|
int frame_size
|
||||||
);
|
);
|
||||||
|
|
||||||
void* operator new(size_t s, unsigned size);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Creation
|
// Creation
|
||||||
static DeoptimizationBlob* create(
|
static DeoptimizationBlob* create(
|
||||||
|
@ -378,7 +429,6 @@ class DeoptimizationBlob: public SingletonBlob {
|
||||||
|
|
||||||
// Typing
|
// Typing
|
||||||
bool is_deoptimization_stub() const { return true; }
|
bool is_deoptimization_stub() const { return true; }
|
||||||
const DeoptimizationBlob *as_deoptimization_stub() const { return this; }
|
|
||||||
bool exception_address_is_unpack_entry(address pc) const {
|
bool exception_address_is_unpack_entry(address pc) const {
|
||||||
address unpack_pc = unpack();
|
address unpack_pc = unpack();
|
||||||
return (pc == unpack_pc || (pc + frame::pc_return_offset) == unpack_pc);
|
return (pc == unpack_pc || (pc + frame::pc_return_offset) == unpack_pc);
|
||||||
|
@ -426,8 +476,6 @@ class UncommonTrapBlob: public SingletonBlob {
|
||||||
int frame_size
|
int frame_size
|
||||||
);
|
);
|
||||||
|
|
||||||
void* operator new(size_t s, unsigned size);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Creation
|
// Creation
|
||||||
static UncommonTrapBlob* create(
|
static UncommonTrapBlob* create(
|
||||||
|
@ -458,8 +506,6 @@ class ExceptionBlob: public SingletonBlob {
|
||||||
int frame_size
|
int frame_size
|
||||||
);
|
);
|
||||||
|
|
||||||
void* operator new(size_t s, unsigned size);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Creation
|
// Creation
|
||||||
static ExceptionBlob* create(
|
static ExceptionBlob* create(
|
||||||
|
@ -491,8 +537,6 @@ class SafepointBlob: public SingletonBlob {
|
||||||
int frame_size
|
int frame_size
|
||||||
);
|
);
|
||||||
|
|
||||||
void* operator new(size_t s, unsigned size);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Creation
|
// Creation
|
||||||
static SafepointBlob* create(
|
static SafepointBlob* create(
|
||||||
|
|
|
@ -796,6 +796,7 @@ void CodeCache::print_internals() {
|
||||||
int nmethodCount = 0;
|
int nmethodCount = 0;
|
||||||
int runtimeStubCount = 0;
|
int runtimeStubCount = 0;
|
||||||
int adapterCount = 0;
|
int adapterCount = 0;
|
||||||
|
int ricochetStubCount = 0;
|
||||||
int deoptimizationStubCount = 0;
|
int deoptimizationStubCount = 0;
|
||||||
int uncommonTrapStubCount = 0;
|
int uncommonTrapStubCount = 0;
|
||||||
int bufferBlobCount = 0;
|
int bufferBlobCount = 0;
|
||||||
|
@ -840,6 +841,8 @@ void CodeCache::print_internals() {
|
||||||
}
|
}
|
||||||
} else if (cb->is_runtime_stub()) {
|
} else if (cb->is_runtime_stub()) {
|
||||||
runtimeStubCount++;
|
runtimeStubCount++;
|
||||||
|
} else if (cb->is_ricochet_stub()) {
|
||||||
|
ricochetStubCount++;
|
||||||
} else if (cb->is_deoptimization_stub()) {
|
} else if (cb->is_deoptimization_stub()) {
|
||||||
deoptimizationStubCount++;
|
deoptimizationStubCount++;
|
||||||
} else if (cb->is_uncommon_trap_stub()) {
|
} else if (cb->is_uncommon_trap_stub()) {
|
||||||
|
@ -876,6 +879,7 @@ void CodeCache::print_internals() {
|
||||||
tty->print_cr("runtime_stubs: %d",runtimeStubCount);
|
tty->print_cr("runtime_stubs: %d",runtimeStubCount);
|
||||||
tty->print_cr("adapters: %d",adapterCount);
|
tty->print_cr("adapters: %d",adapterCount);
|
||||||
tty->print_cr("buffer blobs: %d",bufferBlobCount);
|
tty->print_cr("buffer blobs: %d",bufferBlobCount);
|
||||||
|
tty->print_cr("ricochet_stubs: %d",ricochetStubCount);
|
||||||
tty->print_cr("deoptimization_stubs: %d",deoptimizationStubCount);
|
tty->print_cr("deoptimization_stubs: %d",deoptimizationStubCount);
|
||||||
tty->print_cr("uncommon_traps: %d",uncommonTrapStubCount);
|
tty->print_cr("uncommon_traps: %d",uncommonTrapStubCount);
|
||||||
tty->print_cr("\nnmethod size distribution (non-zombie java)");
|
tty->print_cr("\nnmethod size distribution (non-zombie java)");
|
||||||
|
|
|
@ -283,10 +283,10 @@ void decode_env::print_address(address adr) {
|
||||||
st->print("Stub::%s", desc->name());
|
st->print("Stub::%s", desc->name());
|
||||||
if (desc->begin() != adr)
|
if (desc->begin() != adr)
|
||||||
st->print("%+d 0x%p",adr - desc->begin(), adr);
|
st->print("%+d 0x%p",adr - desc->begin(), adr);
|
||||||
else if (WizardMode) st->print(" " INTPTR_FORMAT, adr);
|
else if (WizardMode) st->print(" " PTR_FORMAT, adr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
st->print("Stub::<unknown> " INTPTR_FORMAT, adr);
|
st->print("Stub::<unknown> " PTR_FORMAT, adr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,8 +314,8 @@ void decode_env::print_address(address adr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall through to a simple numeral.
|
// Fall through to a simple (hexadecimal) numeral.
|
||||||
st->print(INTPTR_FORMAT, (intptr_t)adr);
|
st->print(PTR_FORMAT, adr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void decode_env::print_insn_labels() {
|
void decode_env::print_insn_labels() {
|
||||||
|
@ -326,7 +326,7 @@ void decode_env::print_insn_labels() {
|
||||||
cb->print_block_comment(st, p);
|
cb->print_block_comment(st, p);
|
||||||
}
|
}
|
||||||
if (_print_pc) {
|
if (_print_pc) {
|
||||||
st->print(" " INTPTR_FORMAT ": ", (intptr_t) p);
|
st->print(" " PTR_FORMAT ": ", p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -432,7 +432,7 @@ address decode_env::decode_instructions(address start, address end) {
|
||||||
void Disassembler::decode(CodeBlob* cb, outputStream* st) {
|
void Disassembler::decode(CodeBlob* cb, outputStream* st) {
|
||||||
if (!load_library()) return;
|
if (!load_library()) return;
|
||||||
decode_env env(cb, st);
|
decode_env env(cb, st);
|
||||||
env.output()->print_cr("Decoding CodeBlob " INTPTR_FORMAT, cb);
|
env.output()->print_cr("Decoding CodeBlob " PTR_FORMAT, cb);
|
||||||
env.decode_instructions(cb->code_begin(), cb->code_end());
|
env.decode_instructions(cb->code_begin(), cb->code_end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,7 +446,7 @@ void Disassembler::decode(address start, address end, outputStream* st) {
|
||||||
void Disassembler::decode(nmethod* nm, outputStream* st) {
|
void Disassembler::decode(nmethod* nm, outputStream* st) {
|
||||||
if (!load_library()) return;
|
if (!load_library()) return;
|
||||||
decode_env env(nm, st);
|
decode_env env(nm, st);
|
||||||
env.output()->print_cr("Decoding compiled method " INTPTR_FORMAT ":", nm);
|
env.output()->print_cr("Decoding compiled method " PTR_FORMAT ":", nm);
|
||||||
env.output()->print_cr("Code:");
|
env.output()->print_cr("Code:");
|
||||||
|
|
||||||
#ifdef SHARK
|
#ifdef SHARK
|
||||||
|
@ -478,9 +478,9 @@ void Disassembler::decode(nmethod* nm, outputStream* st) {
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
for (address p = nm->consts_begin(); p < nm->consts_end(); p += 4, offset += 4) {
|
for (address p = nm->consts_begin(); p < nm->consts_end(); p += 4, offset += 4) {
|
||||||
if ((offset % 8) == 0) {
|
if ((offset % 8) == 0) {
|
||||||
env.output()->print_cr(" " INTPTR_FORMAT " (offset: %4d): " PTR32_FORMAT " " PTR64_FORMAT, (intptr_t) p, offset, *((int32_t*) p), *((int64_t*) p));
|
env.output()->print_cr(" " PTR_FORMAT " (offset: %4d): " PTR32_FORMAT " " PTR64_FORMAT, p, offset, *((int32_t*) p), *((int64_t*) p));
|
||||||
} else {
|
} else {
|
||||||
env.output()->print_cr(" " INTPTR_FORMAT " (offset: %4d): " PTR32_FORMAT, (intptr_t) p, offset, *((int32_t*) p));
|
env.output()->print_cr(" " PTR_FORMAT " (offset: %4d): " PTR32_FORMAT, p, offset, *((int32_t*) p));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,13 +29,14 @@
|
||||||
#include "memory/sharedHeap.hpp"
|
#include "memory/sharedHeap.hpp"
|
||||||
#include "memory/space.inline.hpp"
|
#include "memory/space.inline.hpp"
|
||||||
#include "memory/universe.hpp"
|
#include "memory/universe.hpp"
|
||||||
|
#include "oops/oop.inline.hpp"
|
||||||
#include "runtime/java.hpp"
|
#include "runtime/java.hpp"
|
||||||
#include "runtime/mutexLocker.hpp"
|
#include "runtime/mutexLocker.hpp"
|
||||||
#include "runtime/virtualspace.hpp"
|
#include "runtime/virtualspace.hpp"
|
||||||
|
|
||||||
void CardTableModRefBS::non_clean_card_iterate_parallel_work(Space* sp, MemRegion mr,
|
void CardTableModRefBS::non_clean_card_iterate_parallel_work(Space* sp, MemRegion mr,
|
||||||
DirtyCardToOopClosure* dcto_cl,
|
OopsInGenClosure* cl,
|
||||||
ClearNoncleanCardWrapper* cl,
|
CardTableRS* ct,
|
||||||
int n_threads) {
|
int n_threads) {
|
||||||
assert(n_threads > 0, "Error: expected n_threads > 0");
|
assert(n_threads > 0, "Error: expected n_threads > 0");
|
||||||
assert((n_threads == 1 && ParallelGCThreads == 0) ||
|
assert((n_threads == 1 && ParallelGCThreads == 0) ||
|
||||||
|
@ -49,14 +50,14 @@ void CardTableModRefBS::non_clean_card_iterate_parallel_work(Space* sp, MemRegio
|
||||||
lowest_non_clean_base_chunk_index,
|
lowest_non_clean_base_chunk_index,
|
||||||
lowest_non_clean_chunk_size);
|
lowest_non_clean_chunk_size);
|
||||||
|
|
||||||
int n_strides = n_threads * StridesPerThread;
|
int n_strides = n_threads * ParGCStridesPerThread;
|
||||||
SequentialSubTasksDone* pst = sp->par_seq_tasks();
|
SequentialSubTasksDone* pst = sp->par_seq_tasks();
|
||||||
pst->set_n_threads(n_threads);
|
pst->set_n_threads(n_threads);
|
||||||
pst->set_n_tasks(n_strides);
|
pst->set_n_tasks(n_strides);
|
||||||
|
|
||||||
int stride = 0;
|
int stride = 0;
|
||||||
while (!pst->is_task_claimed(/* reference */ stride)) {
|
while (!pst->is_task_claimed(/* reference */ stride)) {
|
||||||
process_stride(sp, mr, stride, n_strides, dcto_cl, cl,
|
process_stride(sp, mr, stride, n_strides, cl, ct,
|
||||||
lowest_non_clean,
|
lowest_non_clean,
|
||||||
lowest_non_clean_base_chunk_index,
|
lowest_non_clean_base_chunk_index,
|
||||||
lowest_non_clean_chunk_size);
|
lowest_non_clean_chunk_size);
|
||||||
|
@ -79,13 +80,13 @@ CardTableModRefBS::
|
||||||
process_stride(Space* sp,
|
process_stride(Space* sp,
|
||||||
MemRegion used,
|
MemRegion used,
|
||||||
jint stride, int n_strides,
|
jint stride, int n_strides,
|
||||||
DirtyCardToOopClosure* dcto_cl,
|
OopsInGenClosure* cl,
|
||||||
ClearNoncleanCardWrapper* cl,
|
CardTableRS* ct,
|
||||||
jbyte** lowest_non_clean,
|
jbyte** lowest_non_clean,
|
||||||
uintptr_t lowest_non_clean_base_chunk_index,
|
uintptr_t lowest_non_clean_base_chunk_index,
|
||||||
size_t lowest_non_clean_chunk_size) {
|
size_t lowest_non_clean_chunk_size) {
|
||||||
// We don't have to go downwards here; it wouldn't help anyway,
|
// We go from higher to lower addresses here; it wouldn't help that much
|
||||||
// because of parallelism.
|
// because of the strided parallelism pattern used here.
|
||||||
|
|
||||||
// Find the first card address of the first chunk in the stride that is
|
// Find the first card address of the first chunk in the stride that is
|
||||||
// at least "bottom" of the used region.
|
// at least "bottom" of the used region.
|
||||||
|
@ -98,25 +99,35 @@ process_stride(Space* sp,
|
||||||
if ((uintptr_t)stride >= start_chunk_stride_num) {
|
if ((uintptr_t)stride >= start_chunk_stride_num) {
|
||||||
chunk_card_start = (jbyte*)(start_card +
|
chunk_card_start = (jbyte*)(start_card +
|
||||||
(stride - start_chunk_stride_num) *
|
(stride - start_chunk_stride_num) *
|
||||||
CardsPerStrideChunk);
|
ParGCCardsPerStrideChunk);
|
||||||
} else {
|
} else {
|
||||||
// Go ahead to the next chunk group boundary, then to the requested stride.
|
// Go ahead to the next chunk group boundary, then to the requested stride.
|
||||||
chunk_card_start = (jbyte*)(start_card +
|
chunk_card_start = (jbyte*)(start_card +
|
||||||
(n_strides - start_chunk_stride_num + stride) *
|
(n_strides - start_chunk_stride_num + stride) *
|
||||||
CardsPerStrideChunk);
|
ParGCCardsPerStrideChunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (chunk_card_start < end_card) {
|
while (chunk_card_start < end_card) {
|
||||||
// We don't have to go downwards here; it wouldn't help anyway,
|
// Even though we go from lower to higher addresses below, the
|
||||||
// because of parallelism. (We take care with "min_done"; see below.)
|
// strided parallelism can interleave the actual processing of the
|
||||||
|
// dirty pages in various ways. For a specific chunk within this
|
||||||
|
// stride, we take care to avoid double scanning or missing a card
|
||||||
|
// by suitably initializing the "min_done" field in process_chunk_boundaries()
|
||||||
|
// below, together with the dirty region extension accomplished in
|
||||||
|
// DirtyCardToOopClosure::do_MemRegion().
|
||||||
|
jbyte* chunk_card_end = chunk_card_start + ParGCCardsPerStrideChunk;
|
||||||
// Invariant: chunk_mr should be fully contained within the "used" region.
|
// Invariant: chunk_mr should be fully contained within the "used" region.
|
||||||
jbyte* chunk_card_end = chunk_card_start + CardsPerStrideChunk;
|
|
||||||
MemRegion chunk_mr = MemRegion(addr_for(chunk_card_start),
|
MemRegion chunk_mr = MemRegion(addr_for(chunk_card_start),
|
||||||
chunk_card_end >= end_card ?
|
chunk_card_end >= end_card ?
|
||||||
used.end() : addr_for(chunk_card_end));
|
used.end() : addr_for(chunk_card_end));
|
||||||
assert(chunk_mr.word_size() > 0, "[chunk_card_start > used_end)");
|
assert(chunk_mr.word_size() > 0, "[chunk_card_start > used_end)");
|
||||||
assert(used.contains(chunk_mr), "chunk_mr should be subset of used");
|
assert(used.contains(chunk_mr), "chunk_mr should be subset of used");
|
||||||
|
|
||||||
|
DirtyCardToOopClosure* dcto_cl = sp->new_dcto_cl(cl, precision(),
|
||||||
|
cl->gen_boundary());
|
||||||
|
ClearNoncleanCardWrapper clear_cl(dcto_cl, ct);
|
||||||
|
|
||||||
|
|
||||||
// Process the chunk.
|
// Process the chunk.
|
||||||
process_chunk_boundaries(sp,
|
process_chunk_boundaries(sp,
|
||||||
dcto_cl,
|
dcto_cl,
|
||||||
|
@ -126,17 +137,30 @@ process_stride(Space* sp,
|
||||||
lowest_non_clean_base_chunk_index,
|
lowest_non_clean_base_chunk_index,
|
||||||
lowest_non_clean_chunk_size);
|
lowest_non_clean_chunk_size);
|
||||||
|
|
||||||
|
// We want the LNC array updates above in process_chunk_boundaries
|
||||||
|
// to be visible before any of the card table value changes as a
|
||||||
|
// result of the dirty card iteration below.
|
||||||
|
OrderAccess::storestore();
|
||||||
|
|
||||||
// We do not call the non_clean_card_iterate_serial() version because
|
// We do not call the non_clean_card_iterate_serial() version because
|
||||||
// we want to clear the cards, and the ClearNoncleanCardWrapper closure
|
// we want to clear the cards: clear_cl here does the work of finding
|
||||||
// itself does the work of finding contiguous dirty ranges of cards to
|
// contiguous dirty ranges of cards to process and clear.
|
||||||
// process (and clear).
|
clear_cl.do_MemRegion(chunk_mr);
|
||||||
cl->do_MemRegion(chunk_mr);
|
|
||||||
|
|
||||||
// Find the next chunk of the stride.
|
// Find the next chunk of the stride.
|
||||||
chunk_card_start += CardsPerStrideChunk * n_strides;
|
chunk_card_start += ParGCCardsPerStrideChunk * n_strides;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If you want a talkative process_chunk_boundaries,
|
||||||
|
// then #define NOISY(x) x
|
||||||
|
#ifdef NOISY
|
||||||
|
#error "Encountered a global preprocessor flag, NOISY, which might clash with local definition to follow"
|
||||||
|
#else
|
||||||
|
#define NOISY(x)
|
||||||
|
#endif
|
||||||
|
|
||||||
void
|
void
|
||||||
CardTableModRefBS::
|
CardTableModRefBS::
|
||||||
process_chunk_boundaries(Space* sp,
|
process_chunk_boundaries(Space* sp,
|
||||||
|
@ -147,103 +171,44 @@ process_chunk_boundaries(Space* sp,
|
||||||
uintptr_t lowest_non_clean_base_chunk_index,
|
uintptr_t lowest_non_clean_base_chunk_index,
|
||||||
size_t lowest_non_clean_chunk_size)
|
size_t lowest_non_clean_chunk_size)
|
||||||
{
|
{
|
||||||
// We must worry about the chunk boundaries.
|
// We must worry about non-array objects that cross chunk boundaries,
|
||||||
|
// because such objects are both precisely and imprecisely marked:
|
||||||
|
// .. if the head of such an object is dirty, the entire object
|
||||||
|
// needs to be scanned, under the interpretation that this
|
||||||
|
// was an imprecise mark
|
||||||
|
// .. if the head of such an object is not dirty, we can assume
|
||||||
|
// precise marking and it's efficient to scan just the dirty
|
||||||
|
// cards.
|
||||||
|
// In either case, each scanned reference must be scanned precisely
|
||||||
|
// once so as to avoid cloning of a young referent. For efficiency,
|
||||||
|
// our closures depend on this property and do not protect against
|
||||||
|
// double scans.
|
||||||
|
|
||||||
// First, set our max_to_do:
|
|
||||||
HeapWord* max_to_do = NULL;
|
|
||||||
uintptr_t cur_chunk_index = addr_to_chunk_index(chunk_mr.start());
|
uintptr_t cur_chunk_index = addr_to_chunk_index(chunk_mr.start());
|
||||||
cur_chunk_index = cur_chunk_index - lowest_non_clean_base_chunk_index;
|
cur_chunk_index = cur_chunk_index - lowest_non_clean_base_chunk_index;
|
||||||
|
|
||||||
if (chunk_mr.end() < used.end()) {
|
NOISY(tty->print_cr("===========================================================================");)
|
||||||
// This is not the last chunk in the used region. What is the last
|
NOISY(tty->print_cr(" process_chunk_boundary: Called with [" PTR_FORMAT "," PTR_FORMAT ")",
|
||||||
// object?
|
chunk_mr.start(), chunk_mr.end());)
|
||||||
HeapWord* last_block = sp->block_start(chunk_mr.end());
|
|
||||||
assert(last_block <= chunk_mr.end(), "In case this property changes.");
|
|
||||||
if (last_block == chunk_mr.end()
|
|
||||||
|| !sp->block_is_obj(last_block)) {
|
|
||||||
max_to_do = chunk_mr.end();
|
|
||||||
|
|
||||||
} else {
|
// First, set "our" lowest_non_clean entry, which would be
|
||||||
// It is an object and starts before the end of the current chunk.
|
// used by the thread scanning an adjoining left chunk with
|
||||||
// last_obj_card is the card corresponding to the start of the last object
|
// a non-array object straddling the mutual boundary.
|
||||||
// in the chunk. Note that the last object may not start in
|
|
||||||
// the chunk.
|
|
||||||
jbyte* last_obj_card = byte_for(last_block);
|
|
||||||
if (!card_may_have_been_dirty(*last_obj_card)) {
|
|
||||||
// The card containing the head is not dirty. Any marks in
|
|
||||||
// subsequent cards still in this chunk must have been made
|
|
||||||
// precisely; we can cap processing at the end.
|
|
||||||
max_to_do = chunk_mr.end();
|
|
||||||
} else {
|
|
||||||
// The last object must be considered dirty, and extends onto the
|
|
||||||
// following chunk. Look for a dirty card in that chunk that will
|
|
||||||
// bound our processing.
|
|
||||||
jbyte* limit_card = NULL;
|
|
||||||
size_t last_block_size = sp->block_size(last_block);
|
|
||||||
jbyte* last_card_of_last_obj =
|
|
||||||
byte_for(last_block + last_block_size - 1);
|
|
||||||
jbyte* first_card_of_next_chunk = byte_for(chunk_mr.end());
|
|
||||||
// This search potentially goes a long distance looking
|
|
||||||
// for the next card that will be scanned. For example,
|
|
||||||
// an object that is an array of primitives will not
|
|
||||||
// have any cards covering regions interior to the array
|
|
||||||
// that will need to be scanned. The scan can be terminated
|
|
||||||
// at the last card of the next chunk. That would leave
|
|
||||||
// limit_card as NULL and would result in "max_to_do"
|
|
||||||
// being set with the LNC value or with the end
|
|
||||||
// of the last block.
|
|
||||||
jbyte* last_card_of_next_chunk = first_card_of_next_chunk +
|
|
||||||
CardsPerStrideChunk;
|
|
||||||
assert(byte_for(chunk_mr.end()) - byte_for(chunk_mr.start())
|
|
||||||
== CardsPerStrideChunk, "last card of next chunk may be wrong");
|
|
||||||
jbyte* last_card_to_check = (jbyte*) MIN2(last_card_of_last_obj,
|
|
||||||
last_card_of_next_chunk);
|
|
||||||
for (jbyte* cur = first_card_of_next_chunk;
|
|
||||||
cur <= last_card_to_check; cur++) {
|
|
||||||
if (card_will_be_scanned(*cur)) {
|
|
||||||
limit_card = cur; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(0 <= cur_chunk_index+1 &&
|
|
||||||
cur_chunk_index+1 < lowest_non_clean_chunk_size,
|
|
||||||
"Bounds error.");
|
|
||||||
// LNC for the next chunk
|
|
||||||
jbyte* lnc_card = lowest_non_clean[cur_chunk_index+1];
|
|
||||||
if (limit_card == NULL) {
|
|
||||||
limit_card = lnc_card;
|
|
||||||
}
|
|
||||||
if (limit_card != NULL) {
|
|
||||||
if (lnc_card != NULL) {
|
|
||||||
limit_card = (jbyte*)MIN2((intptr_t)limit_card,
|
|
||||||
(intptr_t)lnc_card);
|
|
||||||
}
|
|
||||||
max_to_do = addr_for(limit_card);
|
|
||||||
} else {
|
|
||||||
max_to_do = last_block + last_block_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(max_to_do != NULL, "OOPS!");
|
|
||||||
} else {
|
|
||||||
max_to_do = used.end();
|
|
||||||
}
|
|
||||||
// Now we can set the closure we're using so it doesn't to beyond
|
|
||||||
// max_to_do.
|
|
||||||
dcto_cl->set_min_done(max_to_do);
|
|
||||||
#ifndef PRODUCT
|
|
||||||
dcto_cl->set_last_bottom(max_to_do);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Now we set *our" lowest_non_clean entry.
|
|
||||||
// Find the object that spans our boundary, if one exists.
|
// Find the object that spans our boundary, if one exists.
|
||||||
// Nothing to do on the first chunk.
|
// first_block is the block possibly straddling our left boundary.
|
||||||
if (chunk_mr.start() > used.start()) {
|
|
||||||
// first_block is the block possibly spanning the chunk start
|
|
||||||
HeapWord* first_block = sp->block_start(chunk_mr.start());
|
HeapWord* first_block = sp->block_start(chunk_mr.start());
|
||||||
// Does the block span the start of the chunk and is it
|
assert((chunk_mr.start() != used.start()) || (first_block == chunk_mr.start()),
|
||||||
// an object?
|
"First chunk should always have a co-initial block");
|
||||||
if (first_block < chunk_mr.start() &&
|
// Does the block straddle the chunk's left boundary, and is it
|
||||||
sp->block_is_obj(first_block)) {
|
// a non-array object?
|
||||||
|
if (first_block < chunk_mr.start() // first block straddles left bdry
|
||||||
|
&& sp->block_is_obj(first_block) // first block is an object
|
||||||
|
&& !(oop(first_block)->is_objArray() // first block is not an array (arrays are precisely dirtied)
|
||||||
|
|| oop(first_block)->is_typeArray())) {
|
||||||
|
// Find our least non-clean card, so that a left neighbour
|
||||||
|
// does not scan an object straddling the mutual boundary
|
||||||
|
// too far to the right, and attempt to scan a portion of
|
||||||
|
// that object twice.
|
||||||
jbyte* first_dirty_card = NULL;
|
jbyte* first_dirty_card = NULL;
|
||||||
jbyte* last_card_of_first_obj =
|
jbyte* last_card_of_first_obj =
|
||||||
byte_for(first_block + sp->block_size(first_block) - 1);
|
byte_for(first_block + sp->block_size(first_block) - 1);
|
||||||
|
@ -252,21 +217,186 @@ process_chunk_boundaries(Space* sp,
|
||||||
jbyte* last_card_to_check =
|
jbyte* last_card_to_check =
|
||||||
(jbyte*) MIN2((intptr_t) last_card_of_cur_chunk,
|
(jbyte*) MIN2((intptr_t) last_card_of_cur_chunk,
|
||||||
(intptr_t) last_card_of_first_obj);
|
(intptr_t) last_card_of_first_obj);
|
||||||
|
// Note that this does not need to go beyond our last card
|
||||||
|
// if our first object completely straddles this chunk.
|
||||||
for (jbyte* cur = first_card_of_cur_chunk;
|
for (jbyte* cur = first_card_of_cur_chunk;
|
||||||
cur <= last_card_to_check; cur++) {
|
cur <= last_card_to_check; cur++) {
|
||||||
if (card_will_be_scanned(*cur)) {
|
jbyte val = *cur;
|
||||||
|
if (card_will_be_scanned(val)) {
|
||||||
first_dirty_card = cur; break;
|
first_dirty_card = cur; break;
|
||||||
|
} else {
|
||||||
|
assert(!card_may_have_been_dirty(val), "Error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (first_dirty_card != NULL) {
|
if (first_dirty_card != NULL) {
|
||||||
assert(0 <= cur_chunk_index &&
|
NOISY(tty->print_cr(" LNC: Found a dirty card at " PTR_FORMAT " in current chunk",
|
||||||
cur_chunk_index < lowest_non_clean_chunk_size,
|
first_dirty_card);)
|
||||||
|
assert(0 <= cur_chunk_index && cur_chunk_index < lowest_non_clean_chunk_size,
|
||||||
"Bounds error.");
|
"Bounds error.");
|
||||||
|
assert(lowest_non_clean[cur_chunk_index] == NULL,
|
||||||
|
"Write exactly once : value should be stable hereafter for this round");
|
||||||
lowest_non_clean[cur_chunk_index] = first_dirty_card;
|
lowest_non_clean[cur_chunk_index] = first_dirty_card;
|
||||||
|
} NOISY(else {
|
||||||
|
tty->print_cr(" LNC: Found no dirty card in current chunk; leaving LNC entry NULL");
|
||||||
|
// In the future, we could have this thread look for a non-NULL value to copy from its
|
||||||
|
// right neighbour (up to the end of the first object).
|
||||||
|
if (last_card_of_cur_chunk < last_card_of_first_obj) {
|
||||||
|
tty->print_cr(" LNC: BEWARE!!! first obj straddles past right end of chunk:\n"
|
||||||
|
" might be efficient to get value from right neighbour?");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// In this case we can help our neighbour by just asking them
|
||||||
|
// to stop at our first card (even though it may not be dirty).
|
||||||
|
NOISY(tty->print_cr(" LNC: first block is not a non-array object; setting LNC to first card of current chunk");)
|
||||||
|
assert(lowest_non_clean[cur_chunk_index] == NULL, "Write once : value should be stable hereafter");
|
||||||
|
jbyte* first_card_of_cur_chunk = byte_for(chunk_mr.start());
|
||||||
|
lowest_non_clean[cur_chunk_index] = first_card_of_cur_chunk;
|
||||||
|
}
|
||||||
|
NOISY(tty->print_cr(" process_chunk_boundary: lowest_non_clean[" INTPTR_FORMAT "] = " PTR_FORMAT
|
||||||
|
" which corresponds to the heap address " PTR_FORMAT,
|
||||||
|
cur_chunk_index, lowest_non_clean[cur_chunk_index],
|
||||||
|
(lowest_non_clean[cur_chunk_index] != NULL)
|
||||||
|
? addr_for(lowest_non_clean[cur_chunk_index])
|
||||||
|
: NULL);)
|
||||||
|
NOISY(tty->print_cr("---------------------------------------------------------------------------");)
|
||||||
|
|
||||||
|
// Next, set our own max_to_do, which will strictly/exclusively bound
|
||||||
|
// the highest address that we will scan past the right end of our chunk.
|
||||||
|
HeapWord* max_to_do = NULL;
|
||||||
|
if (chunk_mr.end() < used.end()) {
|
||||||
|
// This is not the last chunk in the used region.
|
||||||
|
// What is our last block? We check the first block of
|
||||||
|
// the next (right) chunk rather than strictly check our last block
|
||||||
|
// because it's potentially more efficient to do so.
|
||||||
|
HeapWord* const last_block = sp->block_start(chunk_mr.end());
|
||||||
|
assert(last_block <= chunk_mr.end(), "In case this property changes.");
|
||||||
|
if ((last_block == chunk_mr.end()) // our last block does not straddle boundary
|
||||||
|
|| !sp->block_is_obj(last_block) // last_block isn't an object
|
||||||
|
|| oop(last_block)->is_objArray() // last_block is an array (precisely marked)
|
||||||
|
|| oop(last_block)->is_typeArray()) {
|
||||||
|
max_to_do = chunk_mr.end();
|
||||||
|
NOISY(tty->print_cr(" process_chunk_boundary: Last block on this card is not a non-array object;\n"
|
||||||
|
" max_to_do left at " PTR_FORMAT, max_to_do);)
|
||||||
|
} else {
|
||||||
|
assert(last_block < chunk_mr.end(), "Tautology");
|
||||||
|
// It is a non-array object that straddles the right boundary of this chunk.
|
||||||
|
// last_obj_card is the card corresponding to the start of the last object
|
||||||
|
// in the chunk. Note that the last object may not start in
|
||||||
|
// the chunk.
|
||||||
|
jbyte* const last_obj_card = byte_for(last_block);
|
||||||
|
const jbyte val = *last_obj_card;
|
||||||
|
if (!card_will_be_scanned(val)) {
|
||||||
|
assert(!card_may_have_been_dirty(val), "Error");
|
||||||
|
// The card containing the head is not dirty. Any marks on
|
||||||
|
// subsequent cards still in this chunk must have been made
|
||||||
|
// precisely; we can cap processing at the end of our chunk.
|
||||||
|
max_to_do = chunk_mr.end();
|
||||||
|
NOISY(tty->print_cr(" process_chunk_boundary: Head of last object on this card is not dirty;\n"
|
||||||
|
" max_to_do left at " PTR_FORMAT,
|
||||||
|
max_to_do);)
|
||||||
|
} else {
|
||||||
|
// The last object must be considered dirty, and extends onto the
|
||||||
|
// following chunk. Look for a dirty card in that chunk that will
|
||||||
|
// bound our processing.
|
||||||
|
jbyte* limit_card = NULL;
|
||||||
|
const size_t last_block_size = sp->block_size(last_block);
|
||||||
|
jbyte* const last_card_of_last_obj =
|
||||||
|
byte_for(last_block + last_block_size - 1);
|
||||||
|
jbyte* const first_card_of_next_chunk = byte_for(chunk_mr.end());
|
||||||
|
// This search potentially goes a long distance looking
|
||||||
|
// for the next card that will be scanned, terminating
|
||||||
|
// at the end of the last_block, if no earlier dirty card
|
||||||
|
// is found.
|
||||||
|
assert(byte_for(chunk_mr.end()) - byte_for(chunk_mr.start()) == ParGCCardsPerStrideChunk,
|
||||||
|
"last card of next chunk may be wrong");
|
||||||
|
for (jbyte* cur = first_card_of_next_chunk;
|
||||||
|
cur <= last_card_of_last_obj; cur++) {
|
||||||
|
const jbyte val = *cur;
|
||||||
|
if (card_will_be_scanned(val)) {
|
||||||
|
NOISY(tty->print_cr(" Found a non-clean card " PTR_FORMAT " with value 0x%x",
|
||||||
|
cur, (int)val);)
|
||||||
|
limit_card = cur; break;
|
||||||
|
} else {
|
||||||
|
assert(!card_may_have_been_dirty(val), "Error: card can't be skipped");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (limit_card != NULL) {
|
||||||
|
max_to_do = addr_for(limit_card);
|
||||||
|
assert(limit_card != NULL && max_to_do != NULL, "Error");
|
||||||
|
NOISY(tty->print_cr(" process_chunk_boundary: Found a dirty card at " PTR_FORMAT
|
||||||
|
" max_to_do set at " PTR_FORMAT " which is before end of last block in chunk: "
|
||||||
|
PTR_FORMAT " + " PTR_FORMAT " = " PTR_FORMAT,
|
||||||
|
limit_card, max_to_do, last_block, last_block_size, (last_block+last_block_size));)
|
||||||
|
} else {
|
||||||
|
// The following is a pessimistic value, because it's possible
|
||||||
|
// that a dirty card on a subsequent chunk has been cleared by
|
||||||
|
// the time we get to look at it; we'll correct for that further below,
|
||||||
|
// using the LNC array which records the least non-clean card
|
||||||
|
// before cards were cleared in a particular chunk.
|
||||||
|
limit_card = last_card_of_last_obj;
|
||||||
|
max_to_do = last_block + last_block_size;
|
||||||
|
assert(limit_card != NULL && max_to_do != NULL, "Error");
|
||||||
|
NOISY(tty->print_cr(" process_chunk_boundary: Found no dirty card before end of last block in chunk\n"
|
||||||
|
" Setting limit_card to " PTR_FORMAT
|
||||||
|
" and max_to_do " PTR_FORMAT " + " PTR_FORMAT " = " PTR_FORMAT,
|
||||||
|
limit_card, last_block, last_block_size, max_to_do);)
|
||||||
}
|
}
|
||||||
|
assert(0 < cur_chunk_index+1 && cur_chunk_index+1 < lowest_non_clean_chunk_size,
|
||||||
|
"Bounds error.");
|
||||||
|
// It is possible that a dirty card for the last object may have been
|
||||||
|
// cleared before we had a chance to examine it. In that case, the value
|
||||||
|
// will have been logged in the LNC for that chunk.
|
||||||
|
// We need to examine as many chunks to the right as this object
|
||||||
|
// covers.
|
||||||
|
const uintptr_t last_chunk_index_to_check = addr_to_chunk_index(last_block + last_block_size - 1)
|
||||||
|
- lowest_non_clean_base_chunk_index;
|
||||||
|
DEBUG_ONLY(const uintptr_t last_chunk_index = addr_to_chunk_index(used.last())
|
||||||
|
- lowest_non_clean_base_chunk_index;)
|
||||||
|
assert(last_chunk_index_to_check <= last_chunk_index,
|
||||||
|
err_msg("Out of bounds: last_chunk_index_to_check " INTPTR_FORMAT
|
||||||
|
" exceeds last_chunk_index " INTPTR_FORMAT,
|
||||||
|
last_chunk_index_to_check, last_chunk_index));
|
||||||
|
for (uintptr_t lnc_index = cur_chunk_index + 1;
|
||||||
|
lnc_index <= last_chunk_index_to_check;
|
||||||
|
lnc_index++) {
|
||||||
|
jbyte* lnc_card = lowest_non_clean[lnc_index];
|
||||||
|
if (lnc_card != NULL) {
|
||||||
|
// we can stop at the first non-NULL entry we find
|
||||||
|
if (lnc_card <= limit_card) {
|
||||||
|
NOISY(tty->print_cr(" process_chunk_boundary: LNC card " PTR_FORMAT " is lower than limit_card " PTR_FORMAT,
|
||||||
|
" max_to_do will be lowered to " PTR_FORMAT " from " PTR_FORMAT,
|
||||||
|
lnc_card, limit_card, addr_for(lnc_card), max_to_do);)
|
||||||
|
limit_card = lnc_card;
|
||||||
|
max_to_do = addr_for(limit_card);
|
||||||
|
assert(limit_card != NULL && max_to_do != NULL, "Error");
|
||||||
}
|
}
|
||||||
|
// In any case, we break now
|
||||||
|
break;
|
||||||
|
} // else continue to look for a non-NULL entry if any
|
||||||
|
}
|
||||||
|
assert(limit_card != NULL && max_to_do != NULL, "Error");
|
||||||
|
}
|
||||||
|
assert(max_to_do != NULL, "OOPS 1 !");
|
||||||
|
}
|
||||||
|
assert(max_to_do != NULL, "OOPS 2!");
|
||||||
|
} else {
|
||||||
|
max_to_do = used.end();
|
||||||
|
NOISY(tty->print_cr(" process_chunk_boundary: Last chunk of this space;\n"
|
||||||
|
" max_to_do left at " PTR_FORMAT,
|
||||||
|
max_to_do);)
|
||||||
|
}
|
||||||
|
assert(max_to_do != NULL, "OOPS 3!");
|
||||||
|
// Now we can set the closure we're using so it doesn't to beyond
|
||||||
|
// max_to_do.
|
||||||
|
dcto_cl->set_min_done(max_to_do);
|
||||||
|
#ifndef PRODUCT
|
||||||
|
dcto_cl->set_last_bottom(max_to_do);
|
||||||
|
#endif
|
||||||
|
NOISY(tty->print_cr("===========================================================================\n");)
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef NOISY
|
||||||
|
|
||||||
void
|
void
|
||||||
CardTableModRefBS::
|
CardTableModRefBS::
|
||||||
|
@ -283,8 +413,8 @@ get_LNC_array_for_space(Space* sp,
|
||||||
// LNC array for the covered region. Any later expansion can't affect
|
// LNC array for the covered region. Any later expansion can't affect
|
||||||
// the used_at_save_marks region.
|
// the used_at_save_marks region.
|
||||||
// (I observed a bug in which the first thread to execute this would
|
// (I observed a bug in which the first thread to execute this would
|
||||||
// resize, and then it would cause "expand_and_allocates" that would
|
// resize, and then it would cause "expand_and_allocate" that would
|
||||||
// Increase the number of chunks in the covered region. Then a second
|
// increase the number of chunks in the covered region. Then a second
|
||||||
// thread would come and execute this, see that the size didn't match,
|
// thread would come and execute this, see that the size didn't match,
|
||||||
// and free and allocate again. So the first thread would be using a
|
// and free and allocate again. So the first thread would be using a
|
||||||
// freed "_lowest_non_clean" array.)
|
// freed "_lowest_non_clean" array.)
|
||||||
|
|
|
@ -77,7 +77,23 @@ inline void ParScanClosure::do_oop_work(T* p,
|
||||||
if (!oopDesc::is_null(heap_oop)) {
|
if (!oopDesc::is_null(heap_oop)) {
|
||||||
oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
|
oop obj = oopDesc::decode_heap_oop_not_null(heap_oop);
|
||||||
if ((HeapWord*)obj < _boundary) {
|
if ((HeapWord*)obj < _boundary) {
|
||||||
assert(!_g->to()->is_in_reserved(obj), "Scanning field twice?");
|
#ifndef PRODUCT
|
||||||
|
if (_g->to()->is_in_reserved(obj)) {
|
||||||
|
tty->print_cr("Scanning field (" PTR_FORMAT ") twice?", p);
|
||||||
|
GenCollectedHeap* gch = (GenCollectedHeap*)Universe::heap();
|
||||||
|
Space* sp = gch->space_containing(p);
|
||||||
|
oop obj = oop(sp->block_start(p));
|
||||||
|
assert((HeapWord*)obj < (HeapWord*)p, "Error");
|
||||||
|
tty->print_cr("Object: " PTR_FORMAT, obj);
|
||||||
|
tty->print_cr("-------");
|
||||||
|
obj->print();
|
||||||
|
tty->print_cr("-----");
|
||||||
|
tty->print_cr("Heap:");
|
||||||
|
tty->print_cr("-----");
|
||||||
|
gch->print();
|
||||||
|
ShouldNotReachHere();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
// OK, we need to ensure that it is copied.
|
// OK, we need to ensure that it is copied.
|
||||||
// We read the klass and mark in this order, so that we can reliably
|
// We read the klass and mark in this order, so that we can reliably
|
||||||
// get the size of the object: if the mark we read is not a
|
// get the size of the object: if the mark we read is not a
|
||||||
|
|
|
@ -175,14 +175,27 @@ class AbstractInterpreter: AllStatic {
|
||||||
int temps,
|
int temps,
|
||||||
int popframe_args,
|
int popframe_args,
|
||||||
int monitors,
|
int monitors,
|
||||||
|
int caller_actual_parameters,
|
||||||
int callee_params,
|
int callee_params,
|
||||||
int callee_locals,
|
int callee_locals,
|
||||||
bool is_top_frame);
|
bool is_top_frame) {
|
||||||
|
return layout_activation(method,
|
||||||
|
temps,
|
||||||
|
popframe_args,
|
||||||
|
monitors,
|
||||||
|
caller_actual_parameters,
|
||||||
|
callee_params,
|
||||||
|
callee_locals,
|
||||||
|
(frame*)NULL,
|
||||||
|
(frame*)NULL,
|
||||||
|
is_top_frame);
|
||||||
|
}
|
||||||
|
|
||||||
static int layout_activation(methodOop method,
|
static int layout_activation(methodOop method,
|
||||||
int temps,
|
int temps,
|
||||||
int popframe_args,
|
int popframe_args,
|
||||||
int monitors,
|
int monitors,
|
||||||
|
int caller_actual_parameters,
|
||||||
int callee_params,
|
int callee_params,
|
||||||
int callee_locals,
|
int callee_locals,
|
||||||
frame* caller,
|
frame* caller,
|
||||||
|
|
|
@ -541,20 +541,33 @@ HeapWord* BlockOffsetArrayNonContigSpace::block_start_unsafe(
|
||||||
// to go back by.
|
// to go back by.
|
||||||
size_t n_cards_back = entry_to_cards_back(offset);
|
size_t n_cards_back = entry_to_cards_back(offset);
|
||||||
q -= (N_words * n_cards_back);
|
q -= (N_words * n_cards_back);
|
||||||
assert(q >= _sp->bottom(), "Went below bottom!");
|
assert(q >= _sp->bottom(),
|
||||||
|
err_msg("q = " PTR_FORMAT " crossed below bottom = " PTR_FORMAT,
|
||||||
|
q, _sp->bottom()));
|
||||||
|
assert(q < _sp->end(),
|
||||||
|
err_msg("q = " PTR_FORMAT " crossed above end = " PTR_FORMAT,
|
||||||
|
q, _sp->end()));
|
||||||
index -= n_cards_back;
|
index -= n_cards_back;
|
||||||
offset = _array->offset_array(index);
|
offset = _array->offset_array(index);
|
||||||
}
|
}
|
||||||
assert(offset < N_words, "offset too large");
|
assert(offset < N_words, "offset too large");
|
||||||
index--;
|
index--;
|
||||||
q -= offset;
|
q -= offset;
|
||||||
|
assert(q >= _sp->bottom(),
|
||||||
|
err_msg("q = " PTR_FORMAT " crossed below bottom = " PTR_FORMAT,
|
||||||
|
q, _sp->bottom()));
|
||||||
|
assert(q < _sp->end(),
|
||||||
|
err_msg("q = " PTR_FORMAT " crossed above end = " PTR_FORMAT,
|
||||||
|
q, _sp->end()));
|
||||||
HeapWord* n = q;
|
HeapWord* n = q;
|
||||||
|
|
||||||
while (n <= addr) {
|
while (n <= addr) {
|
||||||
debug_only(HeapWord* last = q); // for debugging
|
debug_only(HeapWord* last = q); // for debugging
|
||||||
q = n;
|
q = n;
|
||||||
n += _sp->block_size(n);
|
n += _sp->block_size(n);
|
||||||
assert(n > q, err_msg("Looping at: " INTPTR_FORMAT, n));
|
assert(n > q,
|
||||||
|
err_msg("Looping at n = " PTR_FORMAT " with last = " PTR_FORMAT " _sp = [" PTR_FORMAT "," PTR_FORMAT ")",
|
||||||
|
n, last, _sp->bottom(), _sp->end()));
|
||||||
}
|
}
|
||||||
assert(q <= addr, err_msg("wrong order for current (" INTPTR_FORMAT ") <= arg (" INTPTR_FORMAT ")", q, addr));
|
assert(q <= addr, err_msg("wrong order for current (" INTPTR_FORMAT ") <= arg (" INTPTR_FORMAT ")", q, addr));
|
||||||
assert(addr <= n, err_msg("wrong order for arg (" INTPTR_FORMAT ") <= next (" INTPTR_FORMAT ")", addr, n));
|
assert(addr <= n, err_msg("wrong order for arg (" INTPTR_FORMAT ") <= next (" INTPTR_FORMAT ")", addr, n));
|
||||||
|
|
|
@ -455,25 +455,29 @@ bool CardTableModRefBS::mark_card_deferred(size_t card_index) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CardTableModRefBS::non_clean_card_iterate_possibly_parallel(Space* sp,
|
void CardTableModRefBS::non_clean_card_iterate_possibly_parallel(Space* sp,
|
||||||
MemRegion mr,
|
MemRegion mr,
|
||||||
DirtyCardToOopClosure* dcto_cl,
|
OopsInGenClosure* cl,
|
||||||
ClearNoncleanCardWrapper* cl) {
|
CardTableRS* ct) {
|
||||||
if (!mr.is_empty()) {
|
if (!mr.is_empty()) {
|
||||||
int n_threads = SharedHeap::heap()->n_par_threads();
|
int n_threads = SharedHeap::heap()->n_par_threads();
|
||||||
if (n_threads > 0) {
|
if (n_threads > 0) {
|
||||||
#ifndef SERIALGC
|
#ifndef SERIALGC
|
||||||
non_clean_card_iterate_parallel_work(sp, mr, dcto_cl, cl, n_threads);
|
non_clean_card_iterate_parallel_work(sp, mr, cl, ct, n_threads);
|
||||||
#else // SERIALGC
|
#else // SERIALGC
|
||||||
fatal("Parallel gc not supported here.");
|
fatal("Parallel gc not supported here.");
|
||||||
#endif // SERIALGC
|
#endif // SERIALGC
|
||||||
} else {
|
} else {
|
||||||
// We do not call the non_clean_card_iterate_serial() version below because
|
// We do not call the non_clean_card_iterate_serial() version below because
|
||||||
// we want to clear the cards (which non_clean_card_iterate_serial() does not
|
// we want to clear the cards (which non_clean_card_iterate_serial() does not
|
||||||
// do for us), and the ClearNoncleanCardWrapper closure itself does the work
|
// do for us): clear_cl here does the work of finding contiguous dirty ranges
|
||||||
// of finding contiguous dirty ranges of cards to process (and clear).
|
// of cards to process and clear.
|
||||||
cl->do_MemRegion(mr);
|
|
||||||
|
DirtyCardToOopClosure* dcto_cl = sp->new_dcto_cl(cl, precision(),
|
||||||
|
cl->gen_boundary());
|
||||||
|
ClearNoncleanCardWrapper clear_cl(dcto_cl, ct);
|
||||||
|
|
||||||
|
clear_cl.do_MemRegion(mr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,7 +150,9 @@ class CardTableModRefBS: public ModRefBarrierSet {
|
||||||
// Mapping from address to card marking array entry
|
// Mapping from address to card marking array entry
|
||||||
jbyte* byte_for(const void* p) const {
|
jbyte* byte_for(const void* p) const {
|
||||||
assert(_whole_heap.contains(p),
|
assert(_whole_heap.contains(p),
|
||||||
"out of bounds access to card marking array");
|
err_msg("Attempt to access p = "PTR_FORMAT" out of bounds of "
|
||||||
|
" card marking array's _whole_heap = ["PTR_FORMAT","PTR_FORMAT")",
|
||||||
|
p, _whole_heap.start(), _whole_heap.end()));
|
||||||
jbyte* result = &byte_map_base[uintptr_t(p) >> card_shift];
|
jbyte* result = &byte_map_base[uintptr_t(p) >> card_shift];
|
||||||
assert(result >= _byte_map && result < _byte_map + _byte_map_size,
|
assert(result >= _byte_map && result < _byte_map + _byte_map_size,
|
||||||
"out of bounds accessor for card marking array");
|
"out of bounds accessor for card marking array");
|
||||||
|
@ -173,18 +175,17 @@ class CardTableModRefBS: public ModRefBarrierSet {
|
||||||
// A variant of the above that will operate in a parallel mode if
|
// A variant of the above that will operate in a parallel mode if
|
||||||
// worker threads are available, and clear the dirty cards as it
|
// worker threads are available, and clear the dirty cards as it
|
||||||
// processes them.
|
// processes them.
|
||||||
// ClearNoncleanCardWrapper cl must wrap the DirtyCardToOopClosure dcto_cl,
|
// XXX ??? MemRegionClosure above vs OopsInGenClosure below XXX
|
||||||
// which may itself be modified by the method.
|
// XXX some new_dcto_cl's take OopClosure's, plus as above there are
|
||||||
|
// some MemRegionClosures. Clean this up everywhere. XXX
|
||||||
void non_clean_card_iterate_possibly_parallel(Space* sp, MemRegion mr,
|
void non_clean_card_iterate_possibly_parallel(Space* sp, MemRegion mr,
|
||||||
DirtyCardToOopClosure* dcto_cl,
|
OopsInGenClosure* cl, CardTableRS* ct);
|
||||||
ClearNoncleanCardWrapper* cl);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Work method used to implement non_clean_card_iterate_possibly_parallel()
|
// Work method used to implement non_clean_card_iterate_possibly_parallel()
|
||||||
// above in the parallel case.
|
// above in the parallel case.
|
||||||
void non_clean_card_iterate_parallel_work(Space* sp, MemRegion mr,
|
void non_clean_card_iterate_parallel_work(Space* sp, MemRegion mr,
|
||||||
DirtyCardToOopClosure* dcto_cl,
|
OopsInGenClosure* cl, CardTableRS* ct,
|
||||||
ClearNoncleanCardWrapper* cl,
|
|
||||||
int n_threads);
|
int n_threads);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -198,11 +199,6 @@ class CardTableModRefBS: public ModRefBarrierSet {
|
||||||
|
|
||||||
// *** Support for parallel card scanning.
|
// *** Support for parallel card scanning.
|
||||||
|
|
||||||
enum SomeConstantsForParallelism {
|
|
||||||
StridesPerThread = 2,
|
|
||||||
CardsPerStrideChunk = 256
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is an array, one element per covered region of the card table.
|
// This is an array, one element per covered region of the card table.
|
||||||
// Each entry is itself an array, with one element per chunk in the
|
// Each entry is itself an array, with one element per chunk in the
|
||||||
// covered region. Each entry of these arrays is the lowest non-clean
|
// covered region. Each entry of these arrays is the lowest non-clean
|
||||||
|
@ -235,7 +231,7 @@ class CardTableModRefBS: public ModRefBarrierSet {
|
||||||
// covers the given address.
|
// covers the given address.
|
||||||
uintptr_t addr_to_chunk_index(const void* addr) {
|
uintptr_t addr_to_chunk_index(const void* addr) {
|
||||||
uintptr_t card = (uintptr_t) byte_for(addr);
|
uintptr_t card = (uintptr_t) byte_for(addr);
|
||||||
return card / CardsPerStrideChunk;
|
return card / ParGCCardsPerStrideChunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply cl, which must either itself apply dcto_cl or be dcto_cl,
|
// Apply cl, which must either itself apply dcto_cl or be dcto_cl,
|
||||||
|
@ -243,8 +239,8 @@ class CardTableModRefBS: public ModRefBarrierSet {
|
||||||
void process_stride(Space* sp,
|
void process_stride(Space* sp,
|
||||||
MemRegion used,
|
MemRegion used,
|
||||||
jint stride, int n_strides,
|
jint stride, int n_strides,
|
||||||
DirtyCardToOopClosure* dcto_cl,
|
OopsInGenClosure* cl,
|
||||||
ClearNoncleanCardWrapper* cl,
|
CardTableRS* ct,
|
||||||
jbyte** lowest_non_clean,
|
jbyte** lowest_non_clean,
|
||||||
uintptr_t lowest_non_clean_base_chunk_index,
|
uintptr_t lowest_non_clean_base_chunk_index,
|
||||||
size_t lowest_non_clean_chunk_size);
|
size_t lowest_non_clean_chunk_size);
|
||||||
|
@ -457,14 +453,18 @@ public:
|
||||||
size_t delta = pointer_delta(p, byte_map_base, sizeof(jbyte));
|
size_t delta = pointer_delta(p, byte_map_base, sizeof(jbyte));
|
||||||
HeapWord* result = (HeapWord*) (delta << card_shift);
|
HeapWord* result = (HeapWord*) (delta << card_shift);
|
||||||
assert(_whole_heap.contains(result),
|
assert(_whole_heap.contains(result),
|
||||||
"out of bounds accessor from card marking array");
|
err_msg("Returning result = "PTR_FORMAT" out of bounds of "
|
||||||
|
" card marking array's _whole_heap = ["PTR_FORMAT","PTR_FORMAT")",
|
||||||
|
result, _whole_heap.start(), _whole_heap.end()));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mapping from address to card marking array index.
|
// Mapping from address to card marking array index.
|
||||||
size_t index_for(void* p) {
|
size_t index_for(void* p) {
|
||||||
assert(_whole_heap.contains(p),
|
assert(_whole_heap.contains(p),
|
||||||
"out of bounds access to card marking array");
|
err_msg("Attempt to access p = "PTR_FORMAT" out of bounds of "
|
||||||
|
" card marking array's _whole_heap = ["PTR_FORMAT","PTR_FORMAT")",
|
||||||
|
p, _whole_heap.start(), _whole_heap.end()));
|
||||||
return byte_for(p) - _byte_map;
|
return byte_for(p) - _byte_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,7 +482,7 @@ public:
|
||||||
void verify_dirty_region(MemRegion mr) PRODUCT_RETURN;
|
void verify_dirty_region(MemRegion mr) PRODUCT_RETURN;
|
||||||
|
|
||||||
static size_t par_chunk_heapword_alignment() {
|
static size_t par_chunk_heapword_alignment() {
|
||||||
return CardsPerStrideChunk * card_size_in_words;
|
return ParGCCardsPerStrideChunk * card_size_in_words;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -162,7 +162,7 @@ inline bool ClearNoncleanCardWrapper::clear_card_serial(jbyte* entry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ClearNoncleanCardWrapper::ClearNoncleanCardWrapper(
|
ClearNoncleanCardWrapper::ClearNoncleanCardWrapper(
|
||||||
MemRegionClosure* dirty_card_closure, CardTableRS* ct) :
|
DirtyCardToOopClosure* dirty_card_closure, CardTableRS* ct) :
|
||||||
_dirty_card_closure(dirty_card_closure), _ct(ct) {
|
_dirty_card_closure(dirty_card_closure), _ct(ct) {
|
||||||
_is_par = (SharedHeap::heap()->n_par_threads() > 0);
|
_is_par = (SharedHeap::heap()->n_par_threads() > 0);
|
||||||
}
|
}
|
||||||
|
@ -246,10 +246,6 @@ void CardTableRS::write_ref_field_gc_par(void* field, oop new_val) {
|
||||||
|
|
||||||
void CardTableRS::younger_refs_in_space_iterate(Space* sp,
|
void CardTableRS::younger_refs_in_space_iterate(Space* sp,
|
||||||
OopsInGenClosure* cl) {
|
OopsInGenClosure* cl) {
|
||||||
DirtyCardToOopClosure* dcto_cl = sp->new_dcto_cl(cl, _ct_bs->precision(),
|
|
||||||
cl->gen_boundary());
|
|
||||||
ClearNoncleanCardWrapper clear_cl(dcto_cl, this);
|
|
||||||
|
|
||||||
const MemRegion urasm = sp->used_region_at_save_marks();
|
const MemRegion urasm = sp->used_region_at_save_marks();
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
// Convert the assertion check to a warning if we are running
|
// Convert the assertion check to a warning if we are running
|
||||||
|
@ -275,10 +271,10 @@ void CardTableRS::younger_refs_in_space_iterate(Space* sp,
|
||||||
if (!urasm.equals(urasm2)) {
|
if (!urasm.equals(urasm2)) {
|
||||||
warning("CMS+ParNew: Flickering used_region_at_save_marks()!!");
|
warning("CMS+ParNew: Flickering used_region_at_save_marks()!!");
|
||||||
}
|
}
|
||||||
|
ShouldNotReachHere();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
_ct_bs->non_clean_card_iterate_possibly_parallel(sp, urasm,
|
_ct_bs->non_clean_card_iterate_possibly_parallel(sp, urasm, cl, this);
|
||||||
dcto_cl, &clear_cl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CardTableRS::clear_into_younger(Generation* gen, bool clear_perm) {
|
void CardTableRS::clear_into_younger(Generation* gen, bool clear_perm) {
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
|
|
||||||
class Space;
|
class Space;
|
||||||
class OopsInGenClosure;
|
class OopsInGenClosure;
|
||||||
class DirtyCardToOopClosure;
|
|
||||||
|
|
||||||
// This kind of "GenRemSet" uses a card table both as shared data structure
|
// This kind of "GenRemSet" uses a card table both as shared data structure
|
||||||
// for a mod ref barrier set and for the rem set information.
|
// for a mod ref barrier set and for the rem set information.
|
||||||
|
@ -167,7 +166,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
class ClearNoncleanCardWrapper: public MemRegionClosure {
|
class ClearNoncleanCardWrapper: public MemRegionClosure {
|
||||||
MemRegionClosure* _dirty_card_closure;
|
DirtyCardToOopClosure* _dirty_card_closure;
|
||||||
CardTableRS* _ct;
|
CardTableRS* _ct;
|
||||||
bool _is_par;
|
bool _is_par;
|
||||||
private:
|
private:
|
||||||
|
@ -179,7 +178,7 @@ private:
|
||||||
inline bool clear_card_parallel(jbyte* entry);
|
inline bool clear_card_parallel(jbyte* entry);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ClearNoncleanCardWrapper(MemRegionClosure* dirty_card_closure, CardTableRS* ct);
|
ClearNoncleanCardWrapper(DirtyCardToOopClosure* dirty_card_closure, CardTableRS* ct);
|
||||||
void do_MemRegion(MemRegion mr);
|
void do_MemRegion(MemRegion mr);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,14 @@ void DirtyCardToOopClosure::walk_mem_region(MemRegion mr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We get called with "mr" representing the dirty region
|
||||||
|
// that we want to process. Because of imprecise marking,
|
||||||
|
// we may need to extend the incoming "mr" to the right,
|
||||||
|
// and scan more. However, because we may already have
|
||||||
|
// scanned some of that extended region, we may need to
|
||||||
|
// trim its right-end back some so we do not scan what
|
||||||
|
// we (or another worker thread) may already have scanned
|
||||||
|
// or planning to scan.
|
||||||
void DirtyCardToOopClosure::do_MemRegion(MemRegion mr) {
|
void DirtyCardToOopClosure::do_MemRegion(MemRegion mr) {
|
||||||
|
|
||||||
// Some collectors need to do special things whenever their dirty
|
// Some collectors need to do special things whenever their dirty
|
||||||
|
@ -148,7 +156,7 @@ void DirtyCardToOopClosure::do_MemRegion(MemRegion mr) {
|
||||||
// e.g. the dirty card region is entirely in a now free object
|
// e.g. the dirty card region is entirely in a now free object
|
||||||
// -- something that could happen with a concurrent sweeper.
|
// -- something that could happen with a concurrent sweeper.
|
||||||
bottom = MIN2(bottom, top);
|
bottom = MIN2(bottom, top);
|
||||||
mr = MemRegion(bottom, top);
|
MemRegion extended_mr = MemRegion(bottom, top);
|
||||||
assert(bottom <= top &&
|
assert(bottom <= top &&
|
||||||
(_precision != CardTableModRefBS::ObjHeadPreciseArray ||
|
(_precision != CardTableModRefBS::ObjHeadPreciseArray ||
|
||||||
_min_done == NULL ||
|
_min_done == NULL ||
|
||||||
|
@ -156,8 +164,8 @@ void DirtyCardToOopClosure::do_MemRegion(MemRegion mr) {
|
||||||
"overlap!");
|
"overlap!");
|
||||||
|
|
||||||
// Walk the region if it is not empty; otherwise there is nothing to do.
|
// Walk the region if it is not empty; otherwise there is nothing to do.
|
||||||
if (!mr.is_empty()) {
|
if (!extended_mr.is_empty()) {
|
||||||
walk_mem_region(mr, bottom_obj, top);
|
walk_mem_region(extended_mr, bottom_obj, top);
|
||||||
}
|
}
|
||||||
|
|
||||||
// An idempotent closure might be applied in any order, so we don't
|
// An idempotent closure might be applied in any order, so we don't
|
||||||
|
|
|
@ -285,10 +285,9 @@ int constantPoolKlass::oop_update_pointers(ParCompactionManager* cm, oop obj) {
|
||||||
void constantPoolKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
|
void constantPoolKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
|
||||||
assert(obj->is_constantPool(), "should be constant pool");
|
assert(obj->is_constantPool(), "should be constant pool");
|
||||||
constantPoolOop cp = (constantPoolOop) obj;
|
constantPoolOop cp = (constantPoolOop) obj;
|
||||||
if (cp->tags() != NULL &&
|
if (cp->tags() != NULL) {
|
||||||
(!JavaObjectsInPerm || (EnableInvokeDynamic && cp->has_pseudo_string()))) {
|
|
||||||
for (int i = 1; i < cp->length(); ++i) {
|
for (int i = 1; i < cp->length(); ++i) {
|
||||||
if (cp->tag_at(i).is_string()) {
|
if (cp->is_pointer_entry(i)) {
|
||||||
oop* base = cp->obj_at_addr_raw(i);
|
oop* base = cp->obj_at_addr_raw(i);
|
||||||
if (PSScavenge::should_scavenge(base)) {
|
if (PSScavenge::should_scavenge(base)) {
|
||||||
pm->claim_or_forward_depth(base);
|
pm->claim_or_forward_depth(base);
|
||||||
|
@ -342,6 +341,11 @@ void constantPoolKlass::oop_print_on(oop obj, outputStream* st) {
|
||||||
anObj->print_value_on(st);
|
anObj->print_value_on(st);
|
||||||
st->print(" {0x%lx}", (address)anObj);
|
st->print(" {0x%lx}", (address)anObj);
|
||||||
break;
|
break;
|
||||||
|
case JVM_CONSTANT_Object :
|
||||||
|
anObj = cp->object_at(index);
|
||||||
|
anObj->print_value_on(st);
|
||||||
|
st->print(" {0x%lx}", (address)anObj);
|
||||||
|
break;
|
||||||
case JVM_CONSTANT_Integer :
|
case JVM_CONSTANT_Integer :
|
||||||
st->print("%d", cp->int_at(index));
|
st->print("%d", cp->int_at(index));
|
||||||
break;
|
break;
|
||||||
|
@ -432,23 +436,21 @@ void constantPoolKlass::oop_verify_on(oop obj, outputStream* st) {
|
||||||
guarantee(cp->is_perm(), "should be in permspace");
|
guarantee(cp->is_perm(), "should be in permspace");
|
||||||
if (!cp->partially_loaded()) {
|
if (!cp->partially_loaded()) {
|
||||||
for (int i = 0; i< cp->length(); i++) {
|
for (int i = 0; i< cp->length(); i++) {
|
||||||
|
constantTag tag = cp->tag_at(i);
|
||||||
CPSlot entry = cp->slot_at(i);
|
CPSlot entry = cp->slot_at(i);
|
||||||
if (cp->tag_at(i).is_klass()) {
|
if (tag.is_klass()) {
|
||||||
if (entry.is_oop()) {
|
if (entry.is_oop()) {
|
||||||
guarantee(entry.get_oop()->is_perm(), "should be in permspace");
|
guarantee(entry.get_oop()->is_perm(), "should be in permspace");
|
||||||
guarantee(entry.get_oop()->is_klass(), "should be klass");
|
guarantee(entry.get_oop()->is_klass(), "should be klass");
|
||||||
}
|
}
|
||||||
}
|
} else if (tag.is_unresolved_klass()) {
|
||||||
if (cp->tag_at(i).is_unresolved_klass()) {
|
|
||||||
if (entry.is_oop()) {
|
if (entry.is_oop()) {
|
||||||
guarantee(entry.get_oop()->is_perm(), "should be in permspace");
|
guarantee(entry.get_oop()->is_perm(), "should be in permspace");
|
||||||
guarantee(entry.get_oop()->is_klass(), "should be klass");
|
guarantee(entry.get_oop()->is_klass(), "should be klass");
|
||||||
}
|
}
|
||||||
}
|
} else if (tag.is_symbol()) {
|
||||||
if (cp->tag_at(i).is_symbol()) {
|
|
||||||
guarantee(entry.get_symbol()->refcount() != 0, "should have nonzero reference count");
|
guarantee(entry.get_symbol()->refcount() != 0, "should have nonzero reference count");
|
||||||
}
|
} else if (tag.is_unresolved_string()) {
|
||||||
if (cp->tag_at(i).is_unresolved_string()) {
|
|
||||||
if (entry.is_oop()) {
|
if (entry.is_oop()) {
|
||||||
guarantee(entry.get_oop()->is_perm(), "should be in permspace");
|
guarantee(entry.get_oop()->is_perm(), "should be in permspace");
|
||||||
guarantee(entry.get_oop()->is_instance(), "should be instance");
|
guarantee(entry.get_oop()->is_instance(), "should be instance");
|
||||||
|
@ -456,8 +458,7 @@ void constantPoolKlass::oop_verify_on(oop obj, outputStream* st) {
|
||||||
else {
|
else {
|
||||||
guarantee(entry.get_symbol()->refcount() != 0, "should have nonzero reference count");
|
guarantee(entry.get_symbol()->refcount() != 0, "should have nonzero reference count");
|
||||||
}
|
}
|
||||||
}
|
} else if (tag.is_string()) {
|
||||||
if (cp->tag_at(i).is_string()) {
|
|
||||||
if (!cp->has_pseudo_string()) {
|
if (!cp->has_pseudo_string()) {
|
||||||
if (entry.is_oop()) {
|
if (entry.is_oop()) {
|
||||||
guarantee(!JavaObjectsInPerm || entry.get_oop()->is_perm(),
|
guarantee(!JavaObjectsInPerm || entry.get_oop()->is_perm(),
|
||||||
|
@ -467,8 +468,11 @@ void constantPoolKlass::oop_verify_on(oop obj, outputStream* st) {
|
||||||
} else {
|
} else {
|
||||||
// can be non-perm, can be non-instance (array)
|
// can be non-perm, can be non-instance (array)
|
||||||
}
|
}
|
||||||
|
} else if (tag.is_object()) {
|
||||||
|
assert(entry.get_oop()->is_oop(), "should be some valid oop");
|
||||||
|
} else {
|
||||||
|
assert(!cp->is_pointer_entry(i), "unhandled oop type in constantPoolKlass::verify_on");
|
||||||
}
|
}
|
||||||
// FIXME: verify JSR 292 tags JVM_CONSTANT_MethodHandle, etc.
|
|
||||||
}
|
}
|
||||||
guarantee(cp->tags()->is_perm(), "should be in permspace");
|
guarantee(cp->tags()->is_perm(), "should be in permspace");
|
||||||
guarantee(cp->tags()->is_typeArray(), "should be type array");
|
guarantee(cp->tags()->is_typeArray(), "should be type array");
|
||||||
|
|
|
@ -89,7 +89,7 @@ static bool is_init_with_ea(ciMethod* callee_method,
|
||||||
}
|
}
|
||||||
|
|
||||||
// positive filter: should send be inlined? returns NULL, if yes, or rejection msg
|
// positive filter: should send be inlined? returns NULL, if yes, or rejection msg
|
||||||
const char* InlineTree::shouldInline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result) const {
|
const char* InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result) const {
|
||||||
// Allows targeted inlining
|
// Allows targeted inlining
|
||||||
if(callee_method->should_inline()) {
|
if(callee_method->should_inline()) {
|
||||||
*wci_result = *(WarmCallInfo::always_hot());
|
*wci_result = *(WarmCallInfo::always_hot());
|
||||||
|
@ -102,7 +102,6 @@ const char* InlineTree::shouldInline(ciMethod* callee_method, ciMethod* caller_m
|
||||||
|
|
||||||
// positive filter: should send be inlined? returns NULL (--> yes)
|
// positive filter: should send be inlined? returns NULL (--> yes)
|
||||||
// or rejection msg
|
// or rejection msg
|
||||||
int max_size = C->max_inline_size();
|
|
||||||
int size = callee_method->code_size();
|
int size = callee_method->code_size();
|
||||||
|
|
||||||
// Check for too many throws (and not too huge)
|
// Check for too many throws (and not too huge)
|
||||||
|
@ -120,9 +119,27 @@ const char* InlineTree::shouldInline(ciMethod* callee_method, ciMethod* caller_m
|
||||||
return NULL; // size and frequency are represented in a new way
|
return NULL; // size and frequency are represented in a new way
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int default_max_inline_size = C->max_inline_size();
|
||||||
|
int inline_small_code_size = InlineSmallCode / 4;
|
||||||
|
int max_inline_size = default_max_inline_size;
|
||||||
|
|
||||||
int call_site_count = method()->scale_count(profile.count());
|
int call_site_count = method()->scale_count(profile.count());
|
||||||
int invoke_count = method()->interpreter_invocation_count();
|
int invoke_count = method()->interpreter_invocation_count();
|
||||||
assert( invoke_count != 0, "Require invokation count greater than zero");
|
|
||||||
|
// Bytecoded method handle adapters do not have interpreter
|
||||||
|
// profiling data but only made up MDO data. Get the counter from
|
||||||
|
// there.
|
||||||
|
if (caller_method->is_method_handle_adapter()) {
|
||||||
|
assert(method()->method_data_or_null(), "must have an MDO");
|
||||||
|
ciMethodData* mdo = method()->method_data();
|
||||||
|
ciProfileData* mha_profile = mdo->bci_to_data(caller_bci);
|
||||||
|
assert(mha_profile, "must exist");
|
||||||
|
CounterData* cd = mha_profile->as_CounterData();
|
||||||
|
invoke_count = cd->count();
|
||||||
|
call_site_count = invoke_count; // use the same value
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(invoke_count != 0, "require invocation count greater than zero");
|
||||||
int freq = call_site_count / invoke_count;
|
int freq = call_site_count / invoke_count;
|
||||||
|
|
||||||
// bump the max size if the call is frequent
|
// bump the max size if the call is frequent
|
||||||
|
@ -130,8 +147,8 @@ const char* InlineTree::shouldInline(ciMethod* callee_method, ciMethod* caller_m
|
||||||
(call_site_count >= InlineFrequencyCount) ||
|
(call_site_count >= InlineFrequencyCount) ||
|
||||||
is_init_with_ea(callee_method, caller_method, C)) {
|
is_init_with_ea(callee_method, caller_method, C)) {
|
||||||
|
|
||||||
max_size = C->freq_inline_size();
|
max_inline_size = C->freq_inline_size();
|
||||||
if (size <= max_size && TraceFrequencyInlining) {
|
if (size <= max_inline_size && TraceFrequencyInlining) {
|
||||||
CompileTask::print_inline_indent(inline_depth());
|
CompileTask::print_inline_indent(inline_depth());
|
||||||
tty->print_cr("Inlined frequent method (freq=%d count=%d):", freq, call_site_count);
|
tty->print_cr("Inlined frequent method (freq=%d count=%d):", freq, call_site_count);
|
||||||
CompileTask::print_inline_indent(inline_depth());
|
CompileTask::print_inline_indent(inline_depth());
|
||||||
|
@ -141,11 +158,11 @@ const char* InlineTree::shouldInline(ciMethod* callee_method, ciMethod* caller_m
|
||||||
} else {
|
} else {
|
||||||
// Not hot. Check for medium-sized pre-existing nmethod at cold sites.
|
// Not hot. Check for medium-sized pre-existing nmethod at cold sites.
|
||||||
if (callee_method->has_compiled_code() &&
|
if (callee_method->has_compiled_code() &&
|
||||||
callee_method->instructions_size(CompLevel_full_optimization) > InlineSmallCode/4)
|
callee_method->instructions_size(CompLevel_full_optimization) > inline_small_code_size)
|
||||||
return "already compiled into a medium method";
|
return "already compiled into a medium method";
|
||||||
}
|
}
|
||||||
if (size > max_size) {
|
if (size > max_inline_size) {
|
||||||
if (max_size > C->max_inline_size())
|
if (max_inline_size > default_max_inline_size)
|
||||||
return "hot method too big";
|
return "hot method too big";
|
||||||
return "too big";
|
return "too big";
|
||||||
}
|
}
|
||||||
|
@ -154,7 +171,7 @@ const char* InlineTree::shouldInline(ciMethod* callee_method, ciMethod* caller_m
|
||||||
|
|
||||||
|
|
||||||
// negative filter: should send NOT be inlined? returns NULL, ok to inline, or rejection msg
|
// negative filter: should send NOT be inlined? returns NULL, ok to inline, or rejection msg
|
||||||
const char* InlineTree::shouldNotInline(ciMethod *callee_method, ciMethod* caller_method, WarmCallInfo* wci_result) const {
|
const char* InlineTree::should_not_inline(ciMethod *callee_method, ciMethod* caller_method, WarmCallInfo* wci_result) const {
|
||||||
// negative filter: should send NOT be inlined? returns NULL (--> inline) or rejection msg
|
// negative filter: should send NOT be inlined? returns NULL (--> inline) or rejection msg
|
||||||
if (!UseOldInlining) {
|
if (!UseOldInlining) {
|
||||||
const char* fail = NULL;
|
const char* fail = NULL;
|
||||||
|
@ -269,14 +286,13 @@ const char* InlineTree::try_to_inline(ciMethod* callee_method, ciMethod* caller_
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *msg = NULL;
|
const char *msg = NULL;
|
||||||
if ((msg = shouldInline(callee_method, caller_method, caller_bci,
|
msg = should_inline(callee_method, caller_method, caller_bci, profile, wci_result);
|
||||||
profile, wci_result)) != NULL) {
|
if (msg != NULL)
|
||||||
return msg;
|
return msg;
|
||||||
}
|
|
||||||
if ((msg = shouldNotInline(callee_method, caller_method,
|
msg = should_not_inline(callee_method, caller_method, wci_result);
|
||||||
wci_result)) != NULL) {
|
if (msg != NULL)
|
||||||
return msg;
|
return msg;
|
||||||
}
|
|
||||||
|
|
||||||
if (InlineAccessors && callee_method->is_accessor()) {
|
if (InlineAccessors && callee_method->is_accessor()) {
|
||||||
// accessor methods are not subject to any of the following limits.
|
// accessor methods are not subject to any of the following limits.
|
||||||
|
@ -492,9 +508,8 @@ InlineTree *InlineTree::build_inline_tree_for_callee( ciMethod* callee_method, J
|
||||||
new_depth_adjust -= 1; // don't count method handle calls from java.lang.invoke implem
|
new_depth_adjust -= 1; // don't count method handle calls from java.lang.invoke implem
|
||||||
}
|
}
|
||||||
if (new_depth_adjust != 0 && PrintInlining) {
|
if (new_depth_adjust != 0 && PrintInlining) {
|
||||||
stringStream nm1; caller_jvms->method()->print_name(&nm1);
|
CompileTask::print_inline_indent(inline_depth());
|
||||||
stringStream nm2; callee_method->print_name(&nm2);
|
tty->print_cr(" \\-> discounting inline depth");
|
||||||
tty->print_cr("discounting inlining depth from %s to %s", nm1.base(), nm2.base());
|
|
||||||
}
|
}
|
||||||
if (new_depth_adjust != 0 && C->log()) {
|
if (new_depth_adjust != 0 && C->log()) {
|
||||||
int id1 = C->log()->identify(caller_jvms->method());
|
int id1 = C->log()->identify(caller_jvms->method());
|
||||||
|
|
|
@ -63,6 +63,9 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index,
|
||||||
JVMState* jvms, bool allow_inline,
|
JVMState* jvms, bool allow_inline,
|
||||||
float prof_factor) {
|
float prof_factor) {
|
||||||
CallGenerator* cg;
|
CallGenerator* cg;
|
||||||
|
ciMethod* caller = jvms->method();
|
||||||
|
int bci = jvms->bci();
|
||||||
|
Bytecodes::Code bytecode = caller->java_code_at_bci(bci);
|
||||||
guarantee(call_method != NULL, "failed method resolution");
|
guarantee(call_method != NULL, "failed method resolution");
|
||||||
|
|
||||||
// Dtrace currently doesn't work unless all calls are vanilla
|
// Dtrace currently doesn't work unless all calls are vanilla
|
||||||
|
@ -73,7 +76,7 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index,
|
||||||
// Note: When we get profiling during stage-1 compiles, we want to pull
|
// Note: When we get profiling during stage-1 compiles, we want to pull
|
||||||
// from more specific profile data which pertains to this inlining.
|
// from more specific profile data which pertains to this inlining.
|
||||||
// Right now, ignore the information in jvms->caller(), and do method[bci].
|
// Right now, ignore the information in jvms->caller(), and do method[bci].
|
||||||
ciCallProfile profile = jvms->method()->call_profile_at_bci(jvms->bci());
|
ciCallProfile profile = caller->call_profile_at_bci(bci);
|
||||||
|
|
||||||
// See how many times this site has been invoked.
|
// See how many times this site has been invoked.
|
||||||
int site_count = profile.count();
|
int site_count = profile.count();
|
||||||
|
@ -116,7 +119,7 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index,
|
||||||
// MethodHandle.invoke* are native methods which obviously don't
|
// MethodHandle.invoke* are native methods which obviously don't
|
||||||
// have bytecodes and so normal inlining fails.
|
// have bytecodes and so normal inlining fails.
|
||||||
if (call_method->is_method_handle_invoke()) {
|
if (call_method->is_method_handle_invoke()) {
|
||||||
if (jvms->method()->java_code_at_bci(jvms->bci()) != Bytecodes::_invokedynamic) {
|
if (bytecode != Bytecodes::_invokedynamic) {
|
||||||
GraphKit kit(jvms);
|
GraphKit kit(jvms);
|
||||||
Node* n = kit.argument(0);
|
Node* n = kit.argument(0);
|
||||||
|
|
||||||
|
@ -125,18 +128,20 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index,
|
||||||
ciObject* const_oop = oop_ptr->const_oop();
|
ciObject* const_oop = oop_ptr->const_oop();
|
||||||
ciMethodHandle* method_handle = const_oop->as_method_handle();
|
ciMethodHandle* method_handle = const_oop->as_method_handle();
|
||||||
|
|
||||||
// Set the actually called method to have access to the class
|
// Set the callee to have access to the class and signature in
|
||||||
// and signature in the MethodHandleCompiler.
|
// the MethodHandleCompiler.
|
||||||
method_handle->set_callee(call_method);
|
method_handle->set_callee(call_method);
|
||||||
|
method_handle->set_caller(caller);
|
||||||
|
method_handle->set_call_profile(&profile);
|
||||||
|
|
||||||
// Get an adapter for the MethodHandle.
|
// Get an adapter for the MethodHandle.
|
||||||
ciMethod* target_method = method_handle->get_method_handle_adapter();
|
ciMethod* target_method = method_handle->get_method_handle_adapter();
|
||||||
CallGenerator* hit_cg = NULL;
|
if (target_method != NULL) {
|
||||||
if (target_method != NULL)
|
CallGenerator* hit_cg = this->call_generator(target_method, vtable_index, false, jvms, true, prof_factor);
|
||||||
hit_cg = this->call_generator(target_method, vtable_index, false, jvms, true, prof_factor);
|
|
||||||
if (hit_cg != NULL && hit_cg->is_inline())
|
if (hit_cg != NULL && hit_cg->is_inline())
|
||||||
return hit_cg;
|
return hit_cg;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return CallGenerator::for_direct_call(call_method);
|
return CallGenerator::for_direct_call(call_method);
|
||||||
}
|
}
|
||||||
|
@ -148,19 +153,21 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index,
|
||||||
ciCallSite* call_site = str.get_call_site();
|
ciCallSite* call_site = str.get_call_site();
|
||||||
ciMethodHandle* method_handle = call_site->get_target();
|
ciMethodHandle* method_handle = call_site->get_target();
|
||||||
|
|
||||||
// Set the actually called method to have access to the class
|
// Set the callee to have access to the class and signature in
|
||||||
// and signature in the MethodHandleCompiler.
|
// the MethodHandleCompiler.
|
||||||
method_handle->set_callee(call_method);
|
method_handle->set_callee(call_method);
|
||||||
|
method_handle->set_caller(caller);
|
||||||
|
method_handle->set_call_profile(&profile);
|
||||||
|
|
||||||
// Get an adapter for the MethodHandle.
|
// Get an adapter for the MethodHandle.
|
||||||
ciMethod* target_method = method_handle->get_invokedynamic_adapter();
|
ciMethod* target_method = method_handle->get_invokedynamic_adapter();
|
||||||
CallGenerator* hit_cg = NULL;
|
if (target_method != NULL) {
|
||||||
if (target_method != NULL)
|
CallGenerator* hit_cg = this->call_generator(target_method, vtable_index, false, jvms, true, prof_factor);
|
||||||
hit_cg = this->call_generator(target_method, vtable_index, false, jvms, true, prof_factor);
|
|
||||||
if (hit_cg != NULL && hit_cg->is_inline()) {
|
if (hit_cg != NULL && hit_cg->is_inline()) {
|
||||||
CallGenerator* miss_cg = CallGenerator::for_dynamic_call(call_method);
|
CallGenerator* miss_cg = CallGenerator::for_dynamic_call(call_method);
|
||||||
return CallGenerator::for_predicted_dynamic_call(method_handle, miss_cg, hit_cg, prof_factor);
|
return CallGenerator::for_predicted_dynamic_call(method_handle, miss_cg, hit_cg, prof_factor);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If something failed, generate a normal dynamic call.
|
// If something failed, generate a normal dynamic call.
|
||||||
return CallGenerator::for_dynamic_call(call_method);
|
return CallGenerator::for_dynamic_call(call_method);
|
||||||
|
|
|
@ -1230,7 +1230,7 @@ void PhaseIdealLoop::do_unroll( IdealLoopTree *loop, Node_List &old_new, bool ad
|
||||||
set_ctrl(new_limit, C->root());
|
set_ctrl(new_limit, C->root());
|
||||||
} else {
|
} else {
|
||||||
// Limit is not constant.
|
// Limit is not constant.
|
||||||
{
|
if (loop_head->unrolled_count() == 1) { // only for first unroll
|
||||||
// Separate limit by Opaque node in case it is an incremented
|
// Separate limit by Opaque node in case it is an incremented
|
||||||
// variable from previous loop to avoid using pre-incremented
|
// variable from previous loop to avoid using pre-incremented
|
||||||
// value which could increase register pressure.
|
// value which could increase register pressure.
|
||||||
|
|
|
@ -68,8 +68,8 @@ protected:
|
||||||
JVMState* caller_jvms,
|
JVMState* caller_jvms,
|
||||||
int caller_bci);
|
int caller_bci);
|
||||||
const char* try_to_inline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result);
|
const char* try_to_inline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result);
|
||||||
const char* shouldInline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result) const;
|
const char* should_inline(ciMethod* callee_method, ciMethod* caller_method, int caller_bci, ciCallProfile& profile, WarmCallInfo* wci_result) const;
|
||||||
const char* shouldNotInline(ciMethod* callee_method, ciMethod* caller_method, WarmCallInfo* wci_result) const;
|
const char* should_not_inline(ciMethod* callee_method, ciMethod* caller_method, WarmCallInfo* wci_result) const;
|
||||||
void print_inlining(ciMethod *callee_method, int caller_bci, const char *failure_msg) const;
|
void print_inlining(ciMethod *callee_method, int caller_bci, const char *failure_msg) const;
|
||||||
|
|
||||||
InlineTree *caller_tree() const { return _caller_tree; }
|
InlineTree *caller_tree() const { return _caller_tree; }
|
||||||
|
|
|
@ -3158,6 +3158,9 @@ inline bool VM_HeapWalkOperation::collect_stack_roots(JavaThread* java_thread,
|
||||||
if (fr->is_entry_frame()) {
|
if (fr->is_entry_frame()) {
|
||||||
last_entry_frame = fr;
|
last_entry_frame = fr;
|
||||||
}
|
}
|
||||||
|
if (fr->is_ricochet_frame()) {
|
||||||
|
fr->oops_ricochet_do(blk, vf->register_map());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vf = vf->sender();
|
vf = vf->sender();
|
||||||
|
|
|
@ -31,6 +31,11 @@
|
||||||
* JSR 292 reference implementation: method handle structure analysis
|
* JSR 292 reference implementation: method handle structure analysis
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef PRODUCT
|
||||||
|
#define print_method_handle(mh) {}
|
||||||
|
#else //PRODUCT
|
||||||
|
extern "C" void print_method_handle(oop mh);
|
||||||
|
#endif //PRODUCT
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// MethodHandleChain
|
// MethodHandleChain
|
||||||
|
@ -206,8 +211,10 @@ MethodHandleWalker::walk(TRAPS) {
|
||||||
lose("bad argument index", CHECK_(empty));
|
lose("bad argument index", CHECK_(empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool retain_original_args = false; // used by fold/collect logic
|
||||||
|
|
||||||
// perform the adapter action
|
// perform the adapter action
|
||||||
switch (chain().adapter_conversion_op()) {
|
switch (conv_op) {
|
||||||
case java_lang_invoke_AdapterMethodHandle::OP_RETYPE_ONLY:
|
case java_lang_invoke_AdapterMethodHandle::OP_RETYPE_ONLY:
|
||||||
// No changes to arguments; pass the bits through.
|
// No changes to arguments; pass the bits through.
|
||||||
break;
|
break;
|
||||||
|
@ -216,51 +223,36 @@ MethodHandleWalker::walk(TRAPS) {
|
||||||
// To keep the verifier happy, emit bitwise ("raw") conversions as needed.
|
// To keep the verifier happy, emit bitwise ("raw") conversions as needed.
|
||||||
// See MethodHandles::same_basic_type_for_arguments for allowed conversions.
|
// See MethodHandles::same_basic_type_for_arguments for allowed conversions.
|
||||||
Handle incoming_mtype(THREAD, chain().method_type_oop());
|
Handle incoming_mtype(THREAD, chain().method_type_oop());
|
||||||
|
Handle outgoing_mtype;
|
||||||
|
{
|
||||||
oop outgoing_mh_oop = chain().vmtarget_oop();
|
oop outgoing_mh_oop = chain().vmtarget_oop();
|
||||||
if (!java_lang_invoke_MethodHandle::is_instance(outgoing_mh_oop))
|
if (!java_lang_invoke_MethodHandle::is_instance(outgoing_mh_oop))
|
||||||
lose("outgoing target not a MethodHandle", CHECK_(empty));
|
lose("outgoing target not a MethodHandle", CHECK_(empty));
|
||||||
Handle outgoing_mtype(THREAD, java_lang_invoke_MethodHandle::type(outgoing_mh_oop));
|
outgoing_mtype = Handle(THREAD, java_lang_invoke_MethodHandle::type(outgoing_mh_oop));
|
||||||
outgoing_mh_oop = NULL; // GC safety
|
}
|
||||||
|
|
||||||
int nptypes = java_lang_invoke_MethodType::ptype_count(outgoing_mtype());
|
int nptypes = java_lang_invoke_MethodType::ptype_count(outgoing_mtype());
|
||||||
if (nptypes != java_lang_invoke_MethodType::ptype_count(incoming_mtype()))
|
if (nptypes != java_lang_invoke_MethodType::ptype_count(incoming_mtype()))
|
||||||
lose("incoming and outgoing parameter count do not agree", CHECK_(empty));
|
lose("incoming and outgoing parameter count do not agree", CHECK_(empty));
|
||||||
|
|
||||||
|
// Argument types.
|
||||||
for (int i = 0, slot = _outgoing.length() - 1; slot >= 0; slot--) {
|
for (int i = 0, slot = _outgoing.length() - 1; slot >= 0; slot--) {
|
||||||
SlotState* arg_state = slot_state(slot);
|
SlotState* arg_state = slot_state(slot);
|
||||||
if (arg_state->_type == T_VOID) continue;
|
if (arg_state->_type == T_VOID) continue;
|
||||||
ArgToken arg = _outgoing.at(slot)._arg;
|
|
||||||
|
|
||||||
klassOop in_klass = NULL;
|
|
||||||
klassOop out_klass = NULL;
|
|
||||||
BasicType inpbt = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(incoming_mtype(), i), &in_klass);
|
|
||||||
BasicType outpbt = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(outgoing_mtype(), i), &out_klass);
|
|
||||||
assert(inpbt == arg.basic_type(), "sanity");
|
|
||||||
|
|
||||||
if (inpbt != outpbt) {
|
|
||||||
vmIntrinsics::ID iid = vmIntrinsics::for_raw_conversion(inpbt, outpbt);
|
|
||||||
if (iid == vmIntrinsics::_none) {
|
|
||||||
lose("no raw conversion method", CHECK_(empty));
|
|
||||||
}
|
|
||||||
ArgToken arglist[2];
|
|
||||||
arglist[0] = arg; // outgoing 'this'
|
|
||||||
arglist[1] = ArgToken(); // sentinel
|
|
||||||
arg = make_invoke(NULL, iid, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK_(empty));
|
|
||||||
change_argument(inpbt, slot, outpbt, arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
klassOop src_klass = NULL;
|
||||||
|
klassOop dst_klass = NULL;
|
||||||
|
BasicType src = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(incoming_mtype(), i), &src_klass);
|
||||||
|
BasicType dst = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(outgoing_mtype(), i), &dst_klass);
|
||||||
|
retype_raw_argument_type(src, dst, slot, CHECK_(empty));
|
||||||
i++; // We need to skip void slots at the top of the loop.
|
i++; // We need to skip void slots at the top of the loop.
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicType inrbt = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(incoming_mtype()));
|
// Return type.
|
||||||
BasicType outrbt = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(outgoing_mtype()));
|
{
|
||||||
if (inrbt != outrbt) {
|
BasicType src = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(incoming_mtype()));
|
||||||
if (inrbt == T_INT && outrbt == T_VOID) {
|
BasicType dst = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(outgoing_mtype()));
|
||||||
// See comments in MethodHandles::same_basic_type_for_arguments.
|
retype_raw_return_type(src, dst, CHECK_(empty));
|
||||||
} else {
|
|
||||||
assert(false, "IMPLEMENT ME");
|
|
||||||
lose("no raw conversion method", CHECK_(empty));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -273,7 +265,7 @@ MethodHandleWalker::walk(TRAPS) {
|
||||||
assert(dest == arg_state->_type, "");
|
assert(dest == arg_state->_type, "");
|
||||||
ArgToken arg = arg_state->_arg;
|
ArgToken arg = arg_state->_arg;
|
||||||
ArgToken new_arg = make_conversion(T_OBJECT, dest_klass, Bytecodes::_checkcast, arg, CHECK_(empty));
|
ArgToken new_arg = make_conversion(T_OBJECT, dest_klass, Bytecodes::_checkcast, arg, CHECK_(empty));
|
||||||
assert(arg.index() == new_arg.index(), "should be the same index");
|
assert(arg.token_type() >= tt_symbolic || arg.index() == new_arg.index(), "should be the same index");
|
||||||
debug_only(dest_klass = (klassOop)badOop);
|
debug_only(dest_klass = (klassOop)badOop);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -332,7 +324,7 @@ MethodHandleWalker::walk(TRAPS) {
|
||||||
ArgToken arglist[2];
|
ArgToken arglist[2];
|
||||||
arglist[0] = arg; // outgoing value
|
arglist[0] = arg; // outgoing value
|
||||||
arglist[1] = ArgToken(); // sentinel
|
arglist[1] = ArgToken(); // sentinel
|
||||||
arg = make_invoke(NULL, boxer, Bytecodes::_invokevirtual, false, 1, &arglist[0], CHECK_(empty));
|
arg = make_invoke(NULL, boxer, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK_(empty));
|
||||||
change_argument(src, arg_slot, T_OBJECT, arg);
|
change_argument(src, arg_slot, T_OBJECT, arg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -404,8 +396,54 @@ MethodHandleWalker::walk(TRAPS) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS: { //NYI, may GC
|
case java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS:
|
||||||
lose("unimplemented", CHECK_(empty));
|
retain_original_args = true; // and fall through:
|
||||||
|
case java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS: {
|
||||||
|
// call argument MH recursively
|
||||||
|
//{static int x; if (!x++) print_method_handle(chain().method_handle_oop()); --x;}
|
||||||
|
Handle recursive_mh(THREAD, chain().adapter_arg_oop());
|
||||||
|
if (!java_lang_invoke_MethodHandle::is_instance(recursive_mh())) {
|
||||||
|
lose("recursive target not a MethodHandle", CHECK_(empty));
|
||||||
|
}
|
||||||
|
Handle recursive_mtype(THREAD, java_lang_invoke_MethodHandle::type(recursive_mh()));
|
||||||
|
int argc = java_lang_invoke_MethodType::ptype_count(recursive_mtype());
|
||||||
|
int coll_slots = java_lang_invoke_MethodHandle::vmslots(recursive_mh());
|
||||||
|
BasicType rtype = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(recursive_mtype()));
|
||||||
|
ArgToken* arglist = NEW_RESOURCE_ARRAY(ArgToken, 1 + argc + 1); // 1+: mh, +1: sentinel
|
||||||
|
arglist[0] = make_oop_constant(recursive_mh(), CHECK_(empty));
|
||||||
|
if (arg_slot < 0 || coll_slots < 0 || arg_slot + coll_slots > _outgoing.length()) {
|
||||||
|
lose("bad fold/collect arg slot", CHECK_(empty));
|
||||||
|
}
|
||||||
|
for (int i = 0, slot = arg_slot + coll_slots - 1; slot >= arg_slot; slot--) {
|
||||||
|
SlotState* arg_state = slot_state(slot);
|
||||||
|
BasicType arg_type = arg_state->_type;
|
||||||
|
if (arg_type == T_VOID) continue;
|
||||||
|
ArgToken arg = _outgoing.at(slot)._arg;
|
||||||
|
if (i >= argc) { lose("bad fold/collect arg", CHECK_(empty)); }
|
||||||
|
arglist[1+i] = arg;
|
||||||
|
if (!retain_original_args)
|
||||||
|
change_argument(arg_type, slot, T_VOID, ArgToken(tt_void));
|
||||||
|
}
|
||||||
|
arglist[1+argc] = ArgToken(); // sentinel
|
||||||
|
oop invoker = java_lang_invoke_MethodTypeForm::vmlayout(
|
||||||
|
java_lang_invoke_MethodType::form(recursive_mtype()) );
|
||||||
|
if (invoker == NULL || !invoker->is_method()) {
|
||||||
|
lose("bad vmlayout slot", CHECK_(empty));
|
||||||
|
}
|
||||||
|
// FIXME: consider inlining the invokee at the bytecode level
|
||||||
|
ArgToken ret = make_invoke(methodOop(invoker), vmIntrinsics::_none,
|
||||||
|
Bytecodes::_invokevirtual, false, 1+argc, &arglist[0], CHECK_(empty));
|
||||||
|
DEBUG_ONLY(invoker = NULL);
|
||||||
|
if (rtype == T_OBJECT) {
|
||||||
|
klassOop rklass = java_lang_Class::as_klassOop( java_lang_invoke_MethodType::rtype(recursive_mtype()) );
|
||||||
|
if (rklass != SystemDictionary::Object_klass() &&
|
||||||
|
!Klass::cast(rklass)->is_interface()) {
|
||||||
|
// preserve type safety
|
||||||
|
ret = make_conversion(T_OBJECT, rklass, Bytecodes::_checkcast, ret, CHECK_(empty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int ret_slot = arg_slot + (retain_original_args ? coll_slots : 0);
|
||||||
|
change_argument(T_VOID, ret_slot, rtype, ret);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,9 +490,18 @@ MethodHandleWalker::walk(TRAPS) {
|
||||||
Bytecodes::_invokestatic, false, 3, &arglist[0], CHECK_(empty));
|
Bytecodes::_invokestatic, false, 3, &arglist[0], CHECK_(empty));
|
||||||
|
|
||||||
// Spread out the array elements.
|
// Spread out the array elements.
|
||||||
Bytecodes::Code aload_op = Bytecodes::_aaload;
|
Bytecodes::Code aload_op = Bytecodes::_nop;
|
||||||
if (element_type != T_OBJECT) {
|
switch (element_type) {
|
||||||
lose("primitive array NYI", CHECK_(empty));
|
case T_INT: aload_op = Bytecodes::_iaload; break;
|
||||||
|
case T_LONG: aload_op = Bytecodes::_laload; break;
|
||||||
|
case T_FLOAT: aload_op = Bytecodes::_faload; break;
|
||||||
|
case T_DOUBLE: aload_op = Bytecodes::_daload; break;
|
||||||
|
case T_OBJECT: aload_op = Bytecodes::_aaload; break;
|
||||||
|
case T_BOOLEAN: // fall through:
|
||||||
|
case T_BYTE: aload_op = Bytecodes::_baload; break;
|
||||||
|
case T_CHAR: aload_op = Bytecodes::_caload; break;
|
||||||
|
case T_SHORT: aload_op = Bytecodes::_saload; break;
|
||||||
|
default: lose("primitive array NYI", CHECK_(empty));
|
||||||
}
|
}
|
||||||
int ap = arg_slot;
|
int ap = arg_slot;
|
||||||
for (int i = 0; i < spread_length; i++) {
|
for (int i = 0; i < spread_length; i++) {
|
||||||
|
@ -467,11 +514,6 @@ MethodHandleWalker::walk(TRAPS) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case java_lang_invoke_AdapterMethodHandle::OP_FLYBY: //NYI, runs Java code
|
|
||||||
case java_lang_invoke_AdapterMethodHandle::OP_RICOCHET: //NYI, runs Java code
|
|
||||||
lose("unimplemented", CHECK_(empty));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
lose("bad adapter conversion", CHECK_(empty));
|
lose("bad adapter conversion", CHECK_(empty));
|
||||||
break;
|
break;
|
||||||
|
@ -495,7 +537,7 @@ MethodHandleWalker::walk(TRAPS) {
|
||||||
lose("bad bound value", CHECK_(empty));
|
lose("bad bound value", CHECK_(empty));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug_only(arg_oop = badOop);
|
DEBUG_ONLY(arg_oop = badOop);
|
||||||
change_argument(T_VOID, arg_slot, arg_type, arg);
|
change_argument(T_VOID, arg_slot, arg_type, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -538,11 +580,10 @@ void MethodHandleWalker::walk_incoming_state(TRAPS) {
|
||||||
}
|
}
|
||||||
for (int i = 0; i < nptypes; i++) {
|
for (int i = 0; i < nptypes; i++) {
|
||||||
klassOop arg_type_klass = NULL;
|
klassOop arg_type_klass = NULL;
|
||||||
BasicType arg_type = java_lang_Class::as_BasicType(
|
BasicType arg_type = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::ptype(mtype(), i), &arg_type_klass);
|
||||||
java_lang_invoke_MethodType::ptype(mtype(), i), &arg_type_klass);
|
|
||||||
int index = new_local_index(arg_type);
|
int index = new_local_index(arg_type);
|
||||||
ArgToken arg = make_parameter(arg_type, arg_type_klass, index, CHECK);
|
ArgToken arg = make_parameter(arg_type, arg_type_klass, index, CHECK);
|
||||||
debug_only(arg_type_klass = (klassOop) NULL);
|
DEBUG_ONLY(arg_type_klass = (klassOop) NULL);
|
||||||
_outgoing.at_put(argp, make_state(arg_type, arg));
|
_outgoing.at_put(argp, make_state(arg_type, arg));
|
||||||
if (type2size[arg_type] == 2) {
|
if (type2size[arg_type] == 2) {
|
||||||
// add the extra slot, so we can model the JVM stack
|
// add the extra slot, so we can model the JVM stack
|
||||||
|
@ -552,8 +593,7 @@ void MethodHandleWalker::walk_incoming_state(TRAPS) {
|
||||||
}
|
}
|
||||||
// call make_parameter at the end of the list for the return type
|
// call make_parameter at the end of the list for the return type
|
||||||
klassOop ret_type_klass = NULL;
|
klassOop ret_type_klass = NULL;
|
||||||
BasicType ret_type = java_lang_Class::as_BasicType(
|
BasicType ret_type = java_lang_Class::as_BasicType(java_lang_invoke_MethodType::rtype(mtype()), &ret_type_klass);
|
||||||
java_lang_invoke_MethodType::rtype(mtype()), &ret_type_klass);
|
|
||||||
ArgToken ret = make_parameter(ret_type, ret_type_klass, -1, CHECK);
|
ArgToken ret = make_parameter(ret_type, ret_type_klass, -1, CHECK);
|
||||||
// ignore ret; client can catch it if needed
|
// ignore ret; client can catch it if needed
|
||||||
}
|
}
|
||||||
|
@ -604,12 +644,55 @@ int MethodHandleWalker::argument_count_slow() {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// MethodHandleWalker::retype_raw_conversion
|
||||||
|
//
|
||||||
|
// Do the raw retype conversions for OP_RETYPE_RAW.
|
||||||
|
void MethodHandleWalker::retype_raw_conversion(BasicType src, BasicType dst, bool for_return, int slot, TRAPS) {
|
||||||
|
if (src != dst) {
|
||||||
|
if (MethodHandles::same_basic_type_for_returns(src, dst, /*raw*/ true)) {
|
||||||
|
if (MethodHandles::is_float_fixed_reinterpretation_cast(src, dst)) {
|
||||||
|
if (for_return) Untested("MHW return raw conversion"); // still untested
|
||||||
|
vmIntrinsics::ID iid = vmIntrinsics::for_raw_conversion(src, dst);
|
||||||
|
if (iid == vmIntrinsics::_none) {
|
||||||
|
lose("no raw conversion method", CHECK);
|
||||||
|
}
|
||||||
|
ArgToken arglist[2];
|
||||||
|
if (!for_return) {
|
||||||
|
// argument type conversion
|
||||||
|
ArgToken arg = _outgoing.at(slot)._arg;
|
||||||
|
assert(arg.token_type() >= tt_symbolic || src == arg.basic_type(), "sanity");
|
||||||
|
arglist[0] = arg; // outgoing 'this'
|
||||||
|
arglist[1] = ArgToken(); // sentinel
|
||||||
|
arg = make_invoke(NULL, iid, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK);
|
||||||
|
change_argument(src, slot, dst, arg);
|
||||||
|
} else {
|
||||||
|
// return type conversion
|
||||||
|
klassOop arg_klass = NULL;
|
||||||
|
arglist[0] = make_parameter(src, arg_klass, -1, CHECK); // return value
|
||||||
|
arglist[1] = ArgToken(); // sentinel
|
||||||
|
(void) make_invoke(NULL, iid, Bytecodes::_invokestatic, false, 1, &arglist[0], CHECK);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Nothing to do.
|
||||||
|
}
|
||||||
|
} else if (src == T_OBJECT && is_java_primitive(dst)) {
|
||||||
|
// ref-to-prim: discard ref, push zero
|
||||||
|
lose("requested ref-to-prim conversion not expected", CHECK);
|
||||||
|
} else {
|
||||||
|
lose("requested raw conversion not allowed", CHECK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// MethodHandleCompiler
|
// MethodHandleCompiler
|
||||||
|
|
||||||
MethodHandleCompiler::MethodHandleCompiler(Handle root, methodHandle callee, bool is_invokedynamic, TRAPS)
|
MethodHandleCompiler::MethodHandleCompiler(Handle root, methodHandle callee, int invoke_count, bool is_invokedynamic, TRAPS)
|
||||||
: MethodHandleWalker(root, is_invokedynamic, THREAD),
|
: MethodHandleWalker(root, is_invokedynamic, THREAD),
|
||||||
_callee(callee),
|
_callee(callee),
|
||||||
|
_invoke_count(invoke_count),
|
||||||
_thread(THREAD),
|
_thread(THREAD),
|
||||||
_bytecode(THREAD, 50),
|
_bytecode(THREAD, 50),
|
||||||
_constants(THREAD, 10),
|
_constants(THREAD, 10),
|
||||||
|
@ -709,6 +792,7 @@ void MethodHandleCompiler::emit_bc(Bytecodes::Code op, int index) {
|
||||||
case Bytecodes::_astore_1:
|
case Bytecodes::_astore_1:
|
||||||
case Bytecodes::_astore_2:
|
case Bytecodes::_astore_2:
|
||||||
case Bytecodes::_astore_3:
|
case Bytecodes::_astore_3:
|
||||||
|
case Bytecodes::_iand:
|
||||||
case Bytecodes::_i2l:
|
case Bytecodes::_i2l:
|
||||||
case Bytecodes::_i2f:
|
case Bytecodes::_i2f:
|
||||||
case Bytecodes::_i2d:
|
case Bytecodes::_i2d:
|
||||||
|
@ -935,7 +1019,11 @@ MethodHandleCompiler::make_conversion(BasicType type, klassOop tk, Bytecodes::Co
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ShouldNotReachHere();
|
if (op == Bytecodes::_illegal)
|
||||||
|
lose("no such primitive conversion", THREAD);
|
||||||
|
else
|
||||||
|
lose("bad primitive conversion op", THREAD);
|
||||||
|
return make_prim_constant(type, &zero_jvalue, THREAD);
|
||||||
}
|
}
|
||||||
|
|
||||||
return make_parameter(type, tk, index, THREAD);
|
return make_parameter(type, tk, index, THREAD);
|
||||||
|
@ -946,7 +1034,9 @@ MethodHandleCompiler::make_conversion(BasicType type, klassOop tk, Bytecodes::Co
|
||||||
// MethodHandleCompiler
|
// MethodHandleCompiler
|
||||||
//
|
//
|
||||||
|
|
||||||
static jvalue zero_jvalue;
|
// Values used by the compiler.
|
||||||
|
jvalue MethodHandleCompiler::zero_jvalue = { 0 };
|
||||||
|
jvalue MethodHandleCompiler::one_jvalue = { 1 };
|
||||||
|
|
||||||
// Emit bytecodes for the given invoke instruction.
|
// Emit bytecodes for the given invoke instruction.
|
||||||
MethodHandleWalker::ArgToken
|
MethodHandleWalker::ArgToken
|
||||||
|
@ -954,11 +1044,11 @@ MethodHandleCompiler::make_invoke(methodOop m, vmIntrinsics::ID iid,
|
||||||
Bytecodes::Code op, bool tailcall,
|
Bytecodes::Code op, bool tailcall,
|
||||||
int argc, MethodHandleWalker::ArgToken* argv,
|
int argc, MethodHandleWalker::ArgToken* argv,
|
||||||
TRAPS) {
|
TRAPS) {
|
||||||
|
ArgToken zero;
|
||||||
if (m == NULL) {
|
if (m == NULL) {
|
||||||
// Get the intrinsic methodOop.
|
// Get the intrinsic methodOop.
|
||||||
m = vmIntrinsics::method_for(iid);
|
m = vmIntrinsics::method_for(iid);
|
||||||
if (m == NULL) {
|
if (m == NULL) {
|
||||||
ArgToken zero;
|
|
||||||
lose(vmIntrinsics::name_at(iid), CHECK_(zero));
|
lose(vmIntrinsics::name_at(iid), CHECK_(zero));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1031,7 +1121,6 @@ MethodHandleCompiler::make_invoke(methodOop m, vmIntrinsics::ID iid,
|
||||||
if (rbt != _rtype) {
|
if (rbt != _rtype) {
|
||||||
if (rbt == T_VOID) {
|
if (rbt == T_VOID) {
|
||||||
// push a zero of the right sort
|
// push a zero of the right sort
|
||||||
ArgToken zero;
|
|
||||||
if (_rtype == T_OBJECT) {
|
if (_rtype == T_OBJECT) {
|
||||||
zero = make_oop_constant(NULL, CHECK_(zero));
|
zero = make_oop_constant(NULL, CHECK_(zero));
|
||||||
} else {
|
} else {
|
||||||
|
@ -1041,9 +1130,27 @@ MethodHandleCompiler::make_invoke(methodOop m, vmIntrinsics::ID iid,
|
||||||
} else if (_rtype == T_VOID) {
|
} else if (_rtype == T_VOID) {
|
||||||
// We'll emit a _return with something on the stack.
|
// We'll emit a _return with something on the stack.
|
||||||
// It's OK to ignore what's on the stack.
|
// It's OK to ignore what's on the stack.
|
||||||
|
} else if (rbt == T_INT && is_subword_type(_rtype)) {
|
||||||
|
// Convert value to match return type.
|
||||||
|
switch (_rtype) {
|
||||||
|
case T_BOOLEAN: {
|
||||||
|
// boolean is treated as a one-bit unsigned integer.
|
||||||
|
// Cf. API documentation: java/lang/invoke/MethodHandles.html#explicitCastArguments
|
||||||
|
ArgToken one = make_prim_constant(T_INT, &one_jvalue, CHECK_(zero));
|
||||||
|
emit_load_constant(one);
|
||||||
|
emit_bc(Bytecodes::_iand);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case T_BYTE: emit_bc(Bytecodes::_i2b); break;
|
||||||
|
case T_CHAR: emit_bc(Bytecodes::_i2c); break;
|
||||||
|
case T_SHORT: emit_bc(Bytecodes::_i2s); break;
|
||||||
|
default: ShouldNotReachHere();
|
||||||
|
}
|
||||||
|
} else if (is_subword_type(rbt) && (is_subword_type(_rtype) || (_rtype == T_INT))) {
|
||||||
|
// The subword type was returned as an int and will be passed
|
||||||
|
// on as an int.
|
||||||
} else {
|
} else {
|
||||||
tty->print_cr("*** rbt=%d != rtype=%d", rbt, _rtype);
|
lose("unknown conversion", CHECK_(zero));
|
||||||
assert(false, "IMPLEMENT ME");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch (_rtype) {
|
switch (_rtype) {
|
||||||
|
@ -1173,7 +1280,7 @@ constantPoolHandle MethodHandleCompiler::get_constant_pool(TRAPS) const {
|
||||||
|
|
||||||
|
|
||||||
methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const {
|
methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const {
|
||||||
methodHandle nullHandle;
|
methodHandle empty;
|
||||||
// Create a method that holds the generated bytecode. invokedynamic
|
// Create a method that holds the generated bytecode. invokedynamic
|
||||||
// has no receiver, normal MH calls do.
|
// has no receiver, normal MH calls do.
|
||||||
int flags_bits;
|
int flags_bits;
|
||||||
|
@ -1182,13 +1289,16 @@ methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const {
|
||||||
else
|
else
|
||||||
flags_bits = (/*JVM_MH_INVOKE_BITS |*/ JVM_ACC_PUBLIC | JVM_ACC_FINAL | JVM_ACC_SYNTHETIC);
|
flags_bits = (/*JVM_MH_INVOKE_BITS |*/ JVM_ACC_PUBLIC | JVM_ACC_FINAL | JVM_ACC_SYNTHETIC);
|
||||||
|
|
||||||
|
// Create a new method
|
||||||
|
methodHandle m;
|
||||||
|
{
|
||||||
methodOop m_oop = oopFactory::new_method(bytecode_length(),
|
methodOop m_oop = oopFactory::new_method(bytecode_length(),
|
||||||
accessFlags_from(flags_bits),
|
accessFlags_from(flags_bits),
|
||||||
0, 0, 0, oopDesc::IsSafeConc, CHECK_(nullHandle));
|
0, 0, 0, oopDesc::IsSafeConc, CHECK_(empty));
|
||||||
methodHandle m(THREAD, m_oop);
|
m = methodHandle(THREAD, m_oop);
|
||||||
m_oop = NULL; // oop not GC safe
|
}
|
||||||
|
|
||||||
constantPoolHandle cpool = get_constant_pool(CHECK_(nullHandle));
|
constantPoolHandle cpool = get_constant_pool(CHECK_(empty));
|
||||||
m->set_constants(cpool());
|
m->set_constants(cpool());
|
||||||
|
|
||||||
m->set_name_index(_name_index);
|
m->set_name_index(_name_index);
|
||||||
|
@ -1203,16 +1313,34 @@ methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const {
|
||||||
typeArrayHandle exception_handlers(THREAD, Universe::the_empty_int_array());
|
typeArrayHandle exception_handlers(THREAD, Universe::the_empty_int_array());
|
||||||
m->set_exception_table(exception_handlers());
|
m->set_exception_table(exception_handlers());
|
||||||
|
|
||||||
// Set the carry bit of the invocation counter to force inlining of
|
|
||||||
// the adapter.
|
|
||||||
InvocationCounter* ic = m->invocation_counter();
|
|
||||||
ic->set_carry_flag();
|
|
||||||
|
|
||||||
// Rewrite the method and set up the constant pool cache.
|
// Rewrite the method and set up the constant pool cache.
|
||||||
objArrayOop m_array = oopFactory::new_system_objArray(1, CHECK_(nullHandle));
|
objArrayOop m_array = oopFactory::new_system_objArray(1, CHECK_(empty));
|
||||||
objArrayHandle methods(THREAD, m_array);
|
objArrayHandle methods(THREAD, m_array);
|
||||||
methods->obj_at_put(0, m());
|
methods->obj_at_put(0, m());
|
||||||
Rewriter::rewrite(_target_klass(), cpool, methods, CHECK_(nullHandle)); // Use fake class.
|
Rewriter::rewrite(_target_klass(), cpool, methods, CHECK_(empty)); // Use fake class.
|
||||||
|
|
||||||
|
// Set the invocation counter's count to the invoke count of the
|
||||||
|
// original call site.
|
||||||
|
InvocationCounter* ic = m->invocation_counter();
|
||||||
|
ic->set(InvocationCounter::wait_for_compile, _invoke_count);
|
||||||
|
|
||||||
|
// Create a new MDO
|
||||||
|
{
|
||||||
|
methodDataOop mdo = oopFactory::new_methodData(m, CHECK_(empty));
|
||||||
|
assert(m->method_data() == NULL, "there should not be an MDO yet");
|
||||||
|
m->set_method_data(mdo);
|
||||||
|
|
||||||
|
// Iterate over all profile data and set the count of the counter
|
||||||
|
// data entries to the original call site counter.
|
||||||
|
for (ProfileData* profile_data = mdo->first_data();
|
||||||
|
mdo->is_valid(profile_data);
|
||||||
|
profile_data = mdo->next_data(profile_data)) {
|
||||||
|
if (profile_data->is_CounterData()) {
|
||||||
|
CounterData* counter_data = profile_data->as_CounterData();
|
||||||
|
counter_data->set_count(_invoke_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
if (TraceMethodHandles) {
|
if (TraceMethodHandles) {
|
||||||
|
@ -1228,7 +1356,6 @@ methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const {
|
||||||
|
|
||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
|
|
||||||
#if 0
|
|
||||||
// MH printer for debugging.
|
// MH printer for debugging.
|
||||||
|
|
||||||
class MethodHandlePrinter : public MethodHandleWalker {
|
class MethodHandlePrinter : public MethodHandleWalker {
|
||||||
|
@ -1236,6 +1363,7 @@ private:
|
||||||
outputStream* _out;
|
outputStream* _out;
|
||||||
bool _verbose;
|
bool _verbose;
|
||||||
int _temp_num;
|
int _temp_num;
|
||||||
|
int _param_state;
|
||||||
stringStream _strbuf;
|
stringStream _strbuf;
|
||||||
const char* strbuf() {
|
const char* strbuf() {
|
||||||
const char* s = _strbuf.as_string();
|
const char* s = _strbuf.as_string();
|
||||||
|
@ -1243,14 +1371,21 @@ private:
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
ArgToken token(const char* str) {
|
ArgToken token(const char* str) {
|
||||||
return (ArgToken) str;
|
jvalue string_con;
|
||||||
|
string_con.j = (intptr_t) str;
|
||||||
|
return ArgToken(tt_symbolic, T_LONG, string_con);
|
||||||
|
}
|
||||||
|
const char* string(ArgToken token) {
|
||||||
|
return (const char*) (intptr_t) token.get_jlong();
|
||||||
}
|
}
|
||||||
void start_params() {
|
void start_params() {
|
||||||
|
_param_state <<= 1;
|
||||||
_out->print("(");
|
_out->print("(");
|
||||||
}
|
}
|
||||||
void end_params() {
|
void end_params() {
|
||||||
if (_verbose) _out->print("\n");
|
if (_verbose) _out->print("\n");
|
||||||
_out->print(") => {");
|
_out->print(") => {");
|
||||||
|
_param_state >>= 1;
|
||||||
}
|
}
|
||||||
void put_type_name(BasicType type, klassOop tk, outputStream* s) {
|
void put_type_name(BasicType type, klassOop tk, outputStream* s) {
|
||||||
const char* kname = NULL;
|
const char* kname = NULL;
|
||||||
|
@ -1270,9 +1405,10 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MethodHandlePrinter(Handle root, bool verbose, outputStream* out, TRAPS)
|
MethodHandlePrinter(Handle root, bool verbose, outputStream* out, TRAPS)
|
||||||
: MethodHandleWalker(root, THREAD),
|
: MethodHandleWalker(root, false, THREAD),
|
||||||
_out(out),
|
_out(out),
|
||||||
_verbose(verbose),
|
_verbose(verbose),
|
||||||
|
_param_state(0),
|
||||||
_temp_num(0)
|
_temp_num(0)
|
||||||
{
|
{
|
||||||
start_params();
|
start_params();
|
||||||
|
@ -1280,9 +1416,10 @@ public:
|
||||||
virtual ArgToken make_parameter(BasicType type, klassOop tk, int argnum, TRAPS) {
|
virtual ArgToken make_parameter(BasicType type, klassOop tk, int argnum, TRAPS) {
|
||||||
if (argnum < 0) {
|
if (argnum < 0) {
|
||||||
end_params();
|
end_params();
|
||||||
return NULL;
|
return token("return");
|
||||||
}
|
}
|
||||||
if (argnum == 0) {
|
if ((_param_state & 1) == 0) {
|
||||||
|
_param_state |= 1;
|
||||||
_out->print(_verbose ? "\n " : "");
|
_out->print(_verbose ? "\n " : "");
|
||||||
} else {
|
} else {
|
||||||
_out->print(_verbose ? ",\n " : ", ");
|
_out->print(_verbose ? ",\n " : ", ");
|
||||||
|
@ -1312,8 +1449,15 @@ public:
|
||||||
java_lang_boxing_object::print(type, con, &_strbuf);
|
java_lang_boxing_object::print(type, con, &_strbuf);
|
||||||
return maybe_make_temp("constant", type, "k");
|
return maybe_make_temp("constant", type, "k");
|
||||||
}
|
}
|
||||||
virtual ArgToken make_conversion(BasicType type, klassOop tk, Bytecodes::Code op, ArgToken src, TRAPS) {
|
void print_bytecode_name(Bytecodes::Code op) {
|
||||||
_strbuf.print("%s(%s", Bytecodes::name(op), (const char*)src);
|
if (Bytecodes::is_defined(op))
|
||||||
|
_strbuf.print("%s", Bytecodes::name(op));
|
||||||
|
else
|
||||||
|
_strbuf.print("bytecode_%d", (int) op);
|
||||||
|
}
|
||||||
|
virtual ArgToken make_conversion(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& src, TRAPS) {
|
||||||
|
print_bytecode_name(op);
|
||||||
|
_strbuf.print("(%s", string(src));
|
||||||
if (tk != NULL) {
|
if (tk != NULL) {
|
||||||
_strbuf.print(", ");
|
_strbuf.print(", ");
|
||||||
put_type_name(type, tk, &_strbuf);
|
put_type_name(type, tk, &_strbuf);
|
||||||
|
@ -1321,8 +1465,8 @@ public:
|
||||||
_strbuf.print(")");
|
_strbuf.print(")");
|
||||||
return maybe_make_temp("convert", type, "v");
|
return maybe_make_temp("convert", type, "v");
|
||||||
}
|
}
|
||||||
virtual ArgToken make_fetch(BasicType type, klassOop tk, Bytecodes::Code op, ArgToken base, ArgToken offset, TRAPS) {
|
virtual ArgToken make_fetch(BasicType type, klassOop tk, Bytecodes::Code op, const ArgToken& base, const ArgToken& offset, TRAPS) {
|
||||||
_strbuf.print("%s(%s, %s", Bytecodes::name(op), (const char*)base, (const char*)offset);
|
_strbuf.print("%s(%s, %s", Bytecodes::name(op), string(base), string(offset));
|
||||||
if (tk != NULL) {
|
if (tk != NULL) {
|
||||||
_strbuf.print(", ");
|
_strbuf.print(", ");
|
||||||
put_type_name(type, tk, &_strbuf);
|
put_type_name(type, tk, &_strbuf);
|
||||||
|
@ -1333,7 +1477,8 @@ public:
|
||||||
virtual ArgToken make_invoke(methodOop m, vmIntrinsics::ID iid,
|
virtual ArgToken make_invoke(methodOop m, vmIntrinsics::ID iid,
|
||||||
Bytecodes::Code op, bool tailcall,
|
Bytecodes::Code op, bool tailcall,
|
||||||
int argc, ArgToken* argv, TRAPS) {
|
int argc, ArgToken* argv, TRAPS) {
|
||||||
Symbol* name, sig;
|
Symbol* name;
|
||||||
|
Symbol* sig;
|
||||||
if (m != NULL) {
|
if (m != NULL) {
|
||||||
name = m->name();
|
name = m->name();
|
||||||
sig = m->signature();
|
sig = m->signature();
|
||||||
|
@ -1343,7 +1488,7 @@ public:
|
||||||
}
|
}
|
||||||
_strbuf.print("%s %s%s(", Bytecodes::name(op), name->as_C_string(), sig->as_C_string());
|
_strbuf.print("%s %s%s(", Bytecodes::name(op), name->as_C_string(), sig->as_C_string());
|
||||||
for (int i = 0; i < argc; i++) {
|
for (int i = 0; i < argc; i++) {
|
||||||
_strbuf.print("%s%s", (i > 0 ? ", " : ""), (const char*)argv[i]);
|
_strbuf.print("%s%s", (i > 0 ? ", " : ""), string(argv[i]));
|
||||||
}
|
}
|
||||||
_strbuf.print(")");
|
_strbuf.print(")");
|
||||||
if (!tailcall) {
|
if (!tailcall) {
|
||||||
|
@ -1381,24 +1526,20 @@ public:
|
||||||
if (HAS_PENDING_EXCEPTION) {
|
if (HAS_PENDING_EXCEPTION) {
|
||||||
oop ex = PENDING_EXCEPTION;
|
oop ex = PENDING_EXCEPTION;
|
||||||
CLEAR_PENDING_EXCEPTION;
|
CLEAR_PENDING_EXCEPTION;
|
||||||
out->print("\n*** ");
|
out->print(" *** ");
|
||||||
if (ex != Universe::virtual_machine_error_instance())
|
if (printer.lose_message() != NULL) out->print("%s ", printer.lose_message());
|
||||||
ex->print_on(out);
|
out->print("}");
|
||||||
else
|
|
||||||
out->print("lose: %s", printer.lose_message());
|
|
||||||
out->print("\n}\n");
|
|
||||||
}
|
}
|
||||||
out->print("\n");
|
out->print("\n");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#endif // 0
|
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
void print_method_handle(oop mh) {
|
void print_method_handle(oop mh) {
|
||||||
if (!mh->is_oop()) {
|
if (!mh->is_oop()) {
|
||||||
tty->print_cr("*** not a method handle: "INTPTR_FORMAT, (intptr_t)mh);
|
tty->print_cr("*** not a method handle: "PTR_FORMAT, (intptr_t)mh);
|
||||||
} else if (java_lang_invoke_MethodHandle::is_instance(mh)) {
|
} else if (java_lang_invoke_MethodHandle::is_instance(mh)) {
|
||||||
//MethodHandlePrinter::print(mh);
|
MethodHandlePrinter::print(mh);
|
||||||
} else {
|
} else {
|
||||||
tty->print("*** not a method handle: ");
|
tty->print("*** not a method handle: ");
|
||||||
mh->print();
|
mh->print();
|
||||||
|
|
|
@ -113,6 +113,7 @@ public:
|
||||||
tt_parameter,
|
tt_parameter,
|
||||||
tt_temporary,
|
tt_temporary,
|
||||||
tt_constant,
|
tt_constant,
|
||||||
|
tt_symbolic,
|
||||||
tt_illegal
|
tt_illegal
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -164,6 +165,10 @@ private:
|
||||||
bool _for_invokedynamic;
|
bool _for_invokedynamic;
|
||||||
int _local_index;
|
int _local_index;
|
||||||
|
|
||||||
|
// This array is kept in an unusual order, indexed by low-level "slot number".
|
||||||
|
// TOS is always _outgoing.at(0), so simple pushes and pops shift the whole _outgoing array.
|
||||||
|
// If there is a receiver in the current argument list, it is at _outgoing.at(_outgoing.length()-1).
|
||||||
|
// If a value at _outgoing.at(n) is T_LONG or T_DOUBLE, the value at _outgoing.at(n+1) is T_VOID.
|
||||||
GrowableArray<SlotState> _outgoing; // current outgoing parameter slots
|
GrowableArray<SlotState> _outgoing; // current outgoing parameter slots
|
||||||
int _outgoing_argc; // # non-empty outgoing slots
|
int _outgoing_argc; // # non-empty outgoing slots
|
||||||
|
|
||||||
|
@ -173,6 +178,11 @@ private:
|
||||||
// Insert or delete a second empty slot as needed.
|
// Insert or delete a second empty slot as needed.
|
||||||
void change_argument(BasicType old_type, int slot, BasicType new_type, const ArgToken& new_arg);
|
void change_argument(BasicType old_type, int slot, BasicType new_type, const ArgToken& new_arg);
|
||||||
|
|
||||||
|
// Raw retype conversions for OP_RAW_RETYPE.
|
||||||
|
void retype_raw_conversion(BasicType src, BasicType dst, bool for_return, int slot, TRAPS);
|
||||||
|
void retype_raw_argument_type(BasicType src, BasicType dst, int slot, TRAPS) { retype_raw_conversion(src, dst, false, slot, CHECK); }
|
||||||
|
void retype_raw_return_type( BasicType src, BasicType dst, TRAPS) { retype_raw_conversion(src, dst, true, -1, CHECK); }
|
||||||
|
|
||||||
SlotState* slot_state(int slot) {
|
SlotState* slot_state(int slot) {
|
||||||
if (slot < 0 || slot >= _outgoing.length())
|
if (slot < 0 || slot >= _outgoing.length())
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -247,11 +257,16 @@ public:
|
||||||
class MethodHandleCompiler : public MethodHandleWalker {
|
class MethodHandleCompiler : public MethodHandleWalker {
|
||||||
private:
|
private:
|
||||||
methodHandle _callee;
|
methodHandle _callee;
|
||||||
|
int _invoke_count; // count the original call site has been executed
|
||||||
KlassHandle _rklass; // Return type for casting.
|
KlassHandle _rklass; // Return type for casting.
|
||||||
BasicType _rtype;
|
BasicType _rtype;
|
||||||
KlassHandle _target_klass;
|
KlassHandle _target_klass;
|
||||||
Thread* _thread;
|
Thread* _thread;
|
||||||
|
|
||||||
|
// Values used by the compiler.
|
||||||
|
static jvalue zero_jvalue;
|
||||||
|
static jvalue one_jvalue;
|
||||||
|
|
||||||
// Fake constant pool entry.
|
// Fake constant pool entry.
|
||||||
class ConstantValue {
|
class ConstantValue {
|
||||||
private:
|
private:
|
||||||
|
@ -416,7 +431,7 @@ private:
|
||||||
methodHandle get_method_oop(TRAPS) const;
|
methodHandle get_method_oop(TRAPS) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MethodHandleCompiler(Handle root, methodHandle call_method, bool for_invokedynamic, TRAPS);
|
MethodHandleCompiler(Handle root, methodHandle callee, int invoke_count, bool for_invokedynamic, TRAPS);
|
||||||
|
|
||||||
// Compile the given MH chain into bytecode.
|
// Compile the given MH chain into bytecode.
|
||||||
methodHandle compile(TRAPS);
|
methodHandle compile(TRAPS);
|
||||||
|
|
|
@ -66,8 +66,8 @@ const char* MethodHandles::_entry_names[_EK_LIMIT+1] = {
|
||||||
"adapter_drop_args",
|
"adapter_drop_args",
|
||||||
"adapter_collect_args",
|
"adapter_collect_args",
|
||||||
"adapter_spread_args",
|
"adapter_spread_args",
|
||||||
"adapter_flyby",
|
"adapter_fold_args",
|
||||||
"adapter_ricochet",
|
"adapter_unused_13",
|
||||||
|
|
||||||
// optimized adapter types:
|
// optimized adapter types:
|
||||||
"adapter_swap_args/1",
|
"adapter_swap_args/1",
|
||||||
|
@ -83,9 +83,76 @@ const char* MethodHandles::_entry_names[_EK_LIMIT+1] = {
|
||||||
"adapter_prim_to_prim/f2d",
|
"adapter_prim_to_prim/f2d",
|
||||||
"adapter_ref_to_prim/unboxi",
|
"adapter_ref_to_prim/unboxi",
|
||||||
"adapter_ref_to_prim/unboxl",
|
"adapter_ref_to_prim/unboxl",
|
||||||
"adapter_spread_args/0",
|
|
||||||
"adapter_spread_args/1",
|
// return value handlers for collect/filter/fold adapters:
|
||||||
"adapter_spread_args/more",
|
"return/ref",
|
||||||
|
"return/int",
|
||||||
|
"return/long",
|
||||||
|
"return/float",
|
||||||
|
"return/double",
|
||||||
|
"return/void",
|
||||||
|
"return/S0/ref",
|
||||||
|
"return/S1/ref",
|
||||||
|
"return/S2/ref",
|
||||||
|
"return/S3/ref",
|
||||||
|
"return/S4/ref",
|
||||||
|
"return/S5/ref",
|
||||||
|
"return/any",
|
||||||
|
|
||||||
|
// spreading (array length cases 0, 1, ...)
|
||||||
|
"adapter_spread/0",
|
||||||
|
"adapter_spread/1/ref",
|
||||||
|
"adapter_spread/2/ref",
|
||||||
|
"adapter_spread/3/ref",
|
||||||
|
"adapter_spread/4/ref",
|
||||||
|
"adapter_spread/5/ref",
|
||||||
|
"adapter_spread/ref",
|
||||||
|
"adapter_spread/byte",
|
||||||
|
"adapter_spread/char",
|
||||||
|
"adapter_spread/short",
|
||||||
|
"adapter_spread/int",
|
||||||
|
"adapter_spread/long",
|
||||||
|
"adapter_spread/float",
|
||||||
|
"adapter_spread/double",
|
||||||
|
|
||||||
|
// blocking filter/collect conversions:
|
||||||
|
"adapter_collect/ref",
|
||||||
|
"adapter_collect/int",
|
||||||
|
"adapter_collect/long",
|
||||||
|
"adapter_collect/float",
|
||||||
|
"adapter_collect/double",
|
||||||
|
"adapter_collect/void",
|
||||||
|
"adapter_collect/0/ref",
|
||||||
|
"adapter_collect/1/ref",
|
||||||
|
"adapter_collect/2/ref",
|
||||||
|
"adapter_collect/3/ref",
|
||||||
|
"adapter_collect/4/ref",
|
||||||
|
"adapter_collect/5/ref",
|
||||||
|
"adapter_filter/S0/ref",
|
||||||
|
"adapter_filter/S1/ref",
|
||||||
|
"adapter_filter/S2/ref",
|
||||||
|
"adapter_filter/S3/ref",
|
||||||
|
"adapter_filter/S4/ref",
|
||||||
|
"adapter_filter/S5/ref",
|
||||||
|
"adapter_collect/2/S0/ref",
|
||||||
|
"adapter_collect/2/S1/ref",
|
||||||
|
"adapter_collect/2/S2/ref",
|
||||||
|
"adapter_collect/2/S3/ref",
|
||||||
|
"adapter_collect/2/S4/ref",
|
||||||
|
"adapter_collect/2/S5/ref",
|
||||||
|
|
||||||
|
// blocking fold conversions:
|
||||||
|
"adapter_fold/ref",
|
||||||
|
"adapter_fold/int",
|
||||||
|
"adapter_fold/long",
|
||||||
|
"adapter_fold/float",
|
||||||
|
"adapter_fold/double",
|
||||||
|
"adapter_fold/void",
|
||||||
|
"adapter_fold/1/ref",
|
||||||
|
"adapter_fold/2/ref",
|
||||||
|
"adapter_fold/3/ref",
|
||||||
|
"adapter_fold/4/ref",
|
||||||
|
"adapter_fold/5/ref",
|
||||||
|
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
@ -96,13 +163,23 @@ int MethodHandles::_adapter_code_size = StubRoutines::meth
|
||||||
|
|
||||||
jobject MethodHandles::_raise_exception_method;
|
jobject MethodHandles::_raise_exception_method;
|
||||||
|
|
||||||
|
address MethodHandles::_adapter_return_handlers[CONV_TYPE_MASK+1];
|
||||||
|
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
bool MethodHandles::spot_check_entry_names() {
|
bool MethodHandles::spot_check_entry_names() {
|
||||||
assert(!strcmp(entry_name(_invokestatic_mh), "invokestatic"), "");
|
assert(!strcmp(entry_name(_invokestatic_mh), "invokestatic"), "");
|
||||||
assert(!strcmp(entry_name(_bound_ref_mh), "bound_ref"), "");
|
assert(!strcmp(entry_name(_bound_ref_mh), "bound_ref"), "");
|
||||||
assert(!strcmp(entry_name(_adapter_retype_only), "adapter_retype_only"), "");
|
assert(!strcmp(entry_name(_adapter_retype_only), "adapter_retype_only"), "");
|
||||||
assert(!strcmp(entry_name(_adapter_ricochet), "adapter_ricochet"), "");
|
assert(!strcmp(entry_name(_adapter_fold_args), "adapter_fold_args"), "");
|
||||||
assert(!strcmp(entry_name(_adapter_opt_unboxi), "adapter_ref_to_prim/unboxi"), "");
|
assert(!strcmp(entry_name(_adapter_opt_unboxi), "adapter_ref_to_prim/unboxi"), "");
|
||||||
|
assert(!strcmp(entry_name(_adapter_opt_spread_char), "adapter_spread/char"), "");
|
||||||
|
assert(!strcmp(entry_name(_adapter_opt_spread_double), "adapter_spread/double"), "");
|
||||||
|
assert(!strcmp(entry_name(_adapter_opt_collect_int), "adapter_collect/int"), "");
|
||||||
|
assert(!strcmp(entry_name(_adapter_opt_collect_0_ref), "adapter_collect/0/ref"), "");
|
||||||
|
assert(!strcmp(entry_name(_adapter_opt_collect_2_S3_ref), "adapter_collect/2/S3/ref"), "");
|
||||||
|
assert(!strcmp(entry_name(_adapter_opt_filter_S5_ref), "adapter_filter/S5/ref"), "");
|
||||||
|
assert(!strcmp(entry_name(_adapter_opt_fold_3_ref), "adapter_fold/3/ref"), "");
|
||||||
|
assert(!strcmp(entry_name(_adapter_opt_fold_void), "adapter_fold/void"), "");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -112,6 +189,9 @@ bool MethodHandles::spot_check_entry_names() {
|
||||||
// MethodHandles::generate_adapters
|
// MethodHandles::generate_adapters
|
||||||
//
|
//
|
||||||
void MethodHandles::generate_adapters() {
|
void MethodHandles::generate_adapters() {
|
||||||
|
#ifdef TARGET_ARCH_NYI_6939861
|
||||||
|
if (FLAG_IS_DEFAULT(UseRicochetFrames)) UseRicochetFrames = false;
|
||||||
|
#endif
|
||||||
if (!EnableInvokeDynamic || SystemDictionary::MethodHandle_klass() == NULL) return;
|
if (!EnableInvokeDynamic || SystemDictionary::MethodHandle_klass() == NULL) return;
|
||||||
|
|
||||||
assert(_adapter_code == NULL, "generate only once");
|
assert(_adapter_code == NULL, "generate only once");
|
||||||
|
@ -126,7 +206,6 @@ void MethodHandles::generate_adapters() {
|
||||||
g.generate();
|
g.generate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// MethodHandlesAdapterGenerator::generate
|
// MethodHandlesAdapterGenerator::generate
|
||||||
//
|
//
|
||||||
|
@ -135,10 +214,60 @@ void MethodHandlesAdapterGenerator::generate() {
|
||||||
for (MethodHandles::EntryKind ek = MethodHandles::_EK_FIRST;
|
for (MethodHandles::EntryKind ek = MethodHandles::_EK_FIRST;
|
||||||
ek < MethodHandles::_EK_LIMIT;
|
ek < MethodHandles::_EK_LIMIT;
|
||||||
ek = MethodHandles::EntryKind(1 + (int)ek)) {
|
ek = MethodHandles::EntryKind(1 + (int)ek)) {
|
||||||
|
if (MethodHandles::ek_supported(ek)) {
|
||||||
StubCodeMark mark(this, "MethodHandle", MethodHandles::entry_name(ek));
|
StubCodeMark mark(this, "MethodHandle", MethodHandles::entry_name(ek));
|
||||||
MethodHandles::generate_method_handle_stub(_masm, ek);
|
MethodHandles::generate_method_handle_stub(_masm, ek);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef TARGET_ARCH_NYI_6939861
|
||||||
|
// these defs belong in methodHandles_<arch>.cpp
|
||||||
|
frame MethodHandles::ricochet_frame_sender(const frame& fr, RegisterMap *map) {
|
||||||
|
ShouldNotCallThis();
|
||||||
|
return fr;
|
||||||
|
}
|
||||||
|
void MethodHandles::ricochet_frame_oops_do(const frame& fr, OopClosure* f, const RegisterMap* reg_map) {
|
||||||
|
ShouldNotCallThis();
|
||||||
|
}
|
||||||
|
#endif //TARGET_ARCH_NYI_6939861
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// MethodHandles::ek_supported
|
||||||
|
//
|
||||||
|
bool MethodHandles::ek_supported(MethodHandles::EntryKind ek) {
|
||||||
|
MethodHandles::EntryKind ek_orig = MethodHandles::ek_original_kind(ek);
|
||||||
|
switch (ek_orig) {
|
||||||
|
case _adapter_unused_13:
|
||||||
|
return false; // not defined yet
|
||||||
|
case _adapter_prim_to_ref:
|
||||||
|
return UseRicochetFrames && conv_op_supported(java_lang_invoke_AdapterMethodHandle::OP_PRIM_TO_REF);
|
||||||
|
case _adapter_collect_args:
|
||||||
|
return UseRicochetFrames && conv_op_supported(java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS);
|
||||||
|
case _adapter_fold_args:
|
||||||
|
return UseRicochetFrames && conv_op_supported(java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS);
|
||||||
|
case _adapter_opt_return_any:
|
||||||
|
return UseRicochetFrames;
|
||||||
|
#ifdef TARGET_ARCH_NYI_6939861
|
||||||
|
// ports before 6939861 supported only three kinds of spread ops
|
||||||
|
case _adapter_spread_args:
|
||||||
|
// restrict spreads to three kinds:
|
||||||
|
switch (ek) {
|
||||||
|
case _adapter_opt_spread_0:
|
||||||
|
case _adapter_opt_spread_1:
|
||||||
|
case _adapter_opt_spread_more:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif //TARGET_ARCH_NYI_6939861
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void MethodHandles::set_enabled(bool z) {
|
void MethodHandles::set_enabled(bool z) {
|
||||||
|
@ -970,6 +1099,14 @@ static oop object_java_mirror() {
|
||||||
return Klass::cast(SystemDictionary::Object_klass())->java_mirror();
|
return Klass::cast(SystemDictionary::Object_klass())->java_mirror();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MethodHandles::is_float_fixed_reinterpretation_cast(BasicType src, BasicType dst) {
|
||||||
|
if (src == T_FLOAT) return dst == T_INT;
|
||||||
|
if (src == T_INT) return dst == T_FLOAT;
|
||||||
|
if (src == T_DOUBLE) return dst == T_LONG;
|
||||||
|
if (src == T_LONG) return dst == T_DOUBLE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool MethodHandles::same_basic_type_for_arguments(BasicType src,
|
bool MethodHandles::same_basic_type_for_arguments(BasicType src,
|
||||||
BasicType dst,
|
BasicType dst,
|
||||||
bool raw,
|
bool raw,
|
||||||
|
@ -996,10 +1133,8 @@ bool MethodHandles::same_basic_type_for_arguments(BasicType src,
|
||||||
return true; // remaining case: byte fits in short
|
return true; // remaining case: byte fits in short
|
||||||
}
|
}
|
||||||
// allow float/fixed reinterpretation casts
|
// allow float/fixed reinterpretation casts
|
||||||
if (src == T_FLOAT) return dst == T_INT;
|
if (is_float_fixed_reinterpretation_cast(src, dst))
|
||||||
if (src == T_INT) return dst == T_FLOAT;
|
return true;
|
||||||
if (src == T_DOUBLE) return dst == T_LONG;
|
|
||||||
if (src == T_LONG) return dst == T_DOUBLE;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1270,7 +1405,7 @@ const char* MethodHandles::check_argument_type_change(BasicType src_type,
|
||||||
int argnum,
|
int argnum,
|
||||||
bool raw) {
|
bool raw) {
|
||||||
const char* err = NULL;
|
const char* err = NULL;
|
||||||
bool for_return = (argnum < 0);
|
const bool for_return = (argnum < 0);
|
||||||
|
|
||||||
// just in case:
|
// just in case:
|
||||||
if (src_type == T_ARRAY) src_type = T_OBJECT;
|
if (src_type == T_ARRAY) src_type = T_OBJECT;
|
||||||
|
@ -1279,17 +1414,17 @@ const char* MethodHandles::check_argument_type_change(BasicType src_type,
|
||||||
// Produce some nice messages if VerifyMethodHandles is turned on:
|
// Produce some nice messages if VerifyMethodHandles is turned on:
|
||||||
if (!same_basic_type_for_arguments(src_type, dst_type, raw, for_return)) {
|
if (!same_basic_type_for_arguments(src_type, dst_type, raw, for_return)) {
|
||||||
if (src_type == T_OBJECT) {
|
if (src_type == T_OBJECT) {
|
||||||
if (raw && dst_type == T_INT && is_always_null_type(src_klass))
|
if (raw && is_java_primitive(dst_type))
|
||||||
return NULL; // OK to convert a null pointer to a garbage int
|
return NULL; // ref-to-prim discards ref and returns zero
|
||||||
err = ((argnum >= 0)
|
err = (!for_return
|
||||||
? "type mismatch: passing a %s for method argument #%d, which expects primitive %s"
|
? "type mismatch: passing a %s for method argument #%d, which expects primitive %s"
|
||||||
: "type mismatch: returning a %s, but caller expects primitive %s");
|
: "type mismatch: returning a %s, but caller expects primitive %s");
|
||||||
} else if (dst_type == T_OBJECT) {
|
} else if (dst_type == T_OBJECT) {
|
||||||
err = ((argnum >= 0)
|
err = (!for_return
|
||||||
? "type mismatch: passing a primitive %s for method argument #%d, which expects %s"
|
? "type mismatch: passing a primitive %s for method argument #%d, which expects %s"
|
||||||
: "type mismatch: returning a primitive %s, but caller expects %s");
|
: "type mismatch: returning a primitive %s, but caller expects %s");
|
||||||
} else {
|
} else {
|
||||||
err = ((argnum >= 0)
|
err = (!for_return
|
||||||
? "type mismatch: passing a %s for method argument #%d, which expects %s"
|
? "type mismatch: passing a %s for method argument #%d, which expects %s"
|
||||||
: "type mismatch: returning a %s, but caller expects %s");
|
: "type mismatch: returning a %s, but caller expects %s");
|
||||||
}
|
}
|
||||||
|
@ -1298,11 +1433,11 @@ const char* MethodHandles::check_argument_type_change(BasicType src_type,
|
||||||
if (!class_cast_needed(dst_klass, src_klass)) {
|
if (!class_cast_needed(dst_klass, src_klass)) {
|
||||||
if (raw)
|
if (raw)
|
||||||
return NULL; // reverse cast is OK; the MH target is trusted to enforce it
|
return NULL; // reverse cast is OK; the MH target is trusted to enforce it
|
||||||
err = ((argnum >= 0)
|
err = (!for_return
|
||||||
? "cast required: passing a %s for method argument #%d, which expects %s"
|
? "cast required: passing a %s for method argument #%d, which expects %s"
|
||||||
: "cast required: returning a %s, but caller expects %s");
|
: "cast required: returning a %s, but caller expects %s");
|
||||||
} else {
|
} else {
|
||||||
err = ((argnum >= 0)
|
err = (!for_return
|
||||||
? "reference mismatch: passing a %s for method argument #%d, which expects %s"
|
? "reference mismatch: passing a %s for method argument #%d, which expects %s"
|
||||||
: "reference mismatch: returning a %s, but caller expects %s");
|
: "reference mismatch: returning a %s, but caller expects %s");
|
||||||
}
|
}
|
||||||
|
@ -1323,7 +1458,7 @@ const char* MethodHandles::check_argument_type_change(BasicType src_type,
|
||||||
|
|
||||||
size_t msglen = strlen(err) + strlen(src_name) + strlen(dst_name) + (argnum < 10 ? 1 : 11);
|
size_t msglen = strlen(err) + strlen(src_name) + strlen(dst_name) + (argnum < 10 ? 1 : 11);
|
||||||
char* msg = NEW_RESOURCE_ARRAY(char, msglen + 1);
|
char* msg = NEW_RESOURCE_ARRAY(char, msglen + 1);
|
||||||
if (argnum >= 0) {
|
if (!for_return) {
|
||||||
assert(strstr(err, "%d") != NULL, "");
|
assert(strstr(err, "%d") != NULL, "");
|
||||||
jio_snprintf(msg, msglen, err, src_name, argnum, dst_name);
|
jio_snprintf(msg, msglen, err, src_name, argnum, dst_name);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1564,6 +1699,8 @@ void MethodHandles::init_BoundMethodHandle_with_receiver(Handle mh,
|
||||||
if (m->is_abstract()) { THROW(vmSymbols::java_lang_AbstractMethodError()); }
|
if (m->is_abstract()) { THROW(vmSymbols::java_lang_AbstractMethodError()); }
|
||||||
|
|
||||||
java_lang_invoke_MethodHandle::init_vmslots(mh());
|
java_lang_invoke_MethodHandle::init_vmslots(mh());
|
||||||
|
int vmargslot = m->size_of_parameters() - 1;
|
||||||
|
assert(java_lang_invoke_BoundMethodHandle::vmargslot(mh()) == vmargslot, "");
|
||||||
|
|
||||||
if (VerifyMethodHandles) {
|
if (VerifyMethodHandles) {
|
||||||
verify_BoundMethodHandle_with_receiver(mh, m, CHECK);
|
verify_BoundMethodHandle_with_receiver(mh, m, CHECK);
|
||||||
|
@ -1642,14 +1779,9 @@ void MethodHandles::verify_BoundMethodHandle(Handle mh, Handle target, int argnu
|
||||||
DEBUG_ONLY(int this_pushes = decode_MethodHandle_stack_pushes(mh()));
|
DEBUG_ONLY(int this_pushes = decode_MethodHandle_stack_pushes(mh()));
|
||||||
if (direct_to_method) {
|
if (direct_to_method) {
|
||||||
assert(this_pushes == slots_pushed, "BMH pushes one or two stack slots");
|
assert(this_pushes == slots_pushed, "BMH pushes one or two stack slots");
|
||||||
assert(slots_pushed <= MethodHandlePushLimit, "");
|
|
||||||
} else {
|
} else {
|
||||||
int target_pushes = decode_MethodHandle_stack_pushes(target());
|
int target_pushes = decode_MethodHandle_stack_pushes(target());
|
||||||
assert(this_pushes == slots_pushed + target_pushes, "BMH stack motion must be correct");
|
assert(this_pushes == slots_pushed + target_pushes, "BMH stack motion must be correct");
|
||||||
// do not blow the stack; use a Java-based adapter if this limit is exceeded
|
|
||||||
// FIXME
|
|
||||||
// if (slots_pushed + target_pushes > MethodHandlePushLimit)
|
|
||||||
// err = "too many bound parameters";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1672,10 +1804,11 @@ void MethodHandles::init_BoundMethodHandle(Handle mh, Handle target, int argnum,
|
||||||
}
|
}
|
||||||
|
|
||||||
java_lang_invoke_MethodHandle::init_vmslots(mh());
|
java_lang_invoke_MethodHandle::init_vmslots(mh());
|
||||||
|
int argslot = java_lang_invoke_BoundMethodHandle::vmargslot(mh());
|
||||||
|
|
||||||
if (VerifyMethodHandles) {
|
if (VerifyMethodHandles) {
|
||||||
int insert_after = argnum - 1;
|
int insert_after = argnum - 1;
|
||||||
verify_vmargslot(mh, insert_after, java_lang_invoke_BoundMethodHandle::vmargslot(mh()), CHECK);
|
verify_vmargslot(mh, insert_after, argslot, CHECK);
|
||||||
verify_vmslots(mh, CHECK);
|
verify_vmslots(mh, CHECK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1769,6 +1902,7 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) {
|
||||||
Handle target(THREAD, java_lang_invoke_AdapterMethodHandle::vmtarget(mh()));
|
Handle target(THREAD, java_lang_invoke_AdapterMethodHandle::vmtarget(mh()));
|
||||||
Handle src_mtype(THREAD, java_lang_invoke_MethodHandle::type(mh()));
|
Handle src_mtype(THREAD, java_lang_invoke_MethodHandle::type(mh()));
|
||||||
Handle dst_mtype(THREAD, java_lang_invoke_MethodHandle::type(target()));
|
Handle dst_mtype(THREAD, java_lang_invoke_MethodHandle::type(target()));
|
||||||
|
Handle arg_mtype;
|
||||||
|
|
||||||
const char* err = NULL;
|
const char* err = NULL;
|
||||||
|
|
||||||
|
@ -1777,25 +1911,29 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) {
|
||||||
switch (ek) {
|
switch (ek) {
|
||||||
case _adapter_check_cast: // target type of cast
|
case _adapter_check_cast: // target type of cast
|
||||||
case _adapter_ref_to_prim: // wrapper type from which to unbox
|
case _adapter_ref_to_prim: // wrapper type from which to unbox
|
||||||
case _adapter_prim_to_ref: // wrapper type to box into
|
|
||||||
case _adapter_collect_args: // array type to collect into
|
|
||||||
case _adapter_spread_args: // array type to spread from
|
case _adapter_spread_args: // array type to spread from
|
||||||
if (!java_lang_Class::is_instance(argument())
|
if (!java_lang_Class::is_instance(argument())
|
||||||
|| java_lang_Class::is_primitive(argument()))
|
|| java_lang_Class::is_primitive(argument()))
|
||||||
{ err = "adapter requires argument of type java.lang.Class"; break; }
|
{ err = "adapter requires argument of type java.lang.Class"; break; }
|
||||||
if (ek == _adapter_collect_args ||
|
if (ek == _adapter_spread_args) {
|
||||||
ek == _adapter_spread_args) {
|
|
||||||
// Make sure it is a suitable collection type. (Array, for now.)
|
// Make sure it is a suitable collection type. (Array, for now.)
|
||||||
Klass* ak = Klass::cast(java_lang_Class::as_klassOop(argument()));
|
Klass* ak = Klass::cast(java_lang_Class::as_klassOop(argument()));
|
||||||
if (!ak->oop_is_objArray()) {
|
if (!ak->oop_is_array())
|
||||||
{ err = "adapter requires argument of type java.lang.Class<Object[]>"; break; }
|
{ err = "spread adapter requires argument representing an array class"; break; }
|
||||||
}
|
BasicType et = arrayKlass::cast(ak->as_klassOop())->element_type();
|
||||||
|
if (et != dest && stack_move <= 0)
|
||||||
|
{ err = "spread adapter requires array class argument of correct type"; break; }
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case _adapter_flyby:
|
case _adapter_prim_to_ref: // boxer MH to use
|
||||||
case _adapter_ricochet:
|
case _adapter_collect_args: // method handle which collects the args
|
||||||
|
case _adapter_fold_args: // method handle which collects the args
|
||||||
|
if (!UseRicochetFrames) {
|
||||||
|
{ err = "box/collect/fold operators are not supported"; break; }
|
||||||
|
}
|
||||||
if (!java_lang_invoke_MethodHandle::is_instance(argument()))
|
if (!java_lang_invoke_MethodHandle::is_instance(argument()))
|
||||||
{ err = "MethodHandle adapter argument required"; break; }
|
{ err = "MethodHandle adapter argument required"; break; }
|
||||||
|
arg_mtype = Handle(THREAD, java_lang_invoke_MethodHandle::type(argument()));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (argument.not_null())
|
if (argument.not_null())
|
||||||
|
@ -1806,6 +1944,7 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) {
|
||||||
|
|
||||||
if (err == NULL) {
|
if (err == NULL) {
|
||||||
// Check that the src/dest types are supplied if needed.
|
// Check that the src/dest types are supplied if needed.
|
||||||
|
// Also check relevant parameter or return types.
|
||||||
switch (ek) {
|
switch (ek) {
|
||||||
case _adapter_check_cast:
|
case _adapter_check_cast:
|
||||||
if (src != T_OBJECT || dest != T_OBJECT) {
|
if (src != T_OBJECT || dest != T_OBJECT) {
|
||||||
|
@ -1828,8 +1967,7 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case _adapter_prim_to_ref:
|
case _adapter_prim_to_ref:
|
||||||
if (!is_java_primitive(src) || dest != T_OBJECT
|
if (!is_java_primitive(src) || dest != T_OBJECT) {
|
||||||
|| argument() != Klass::cast(SystemDictionary::box_klass(src))->java_mirror()) {
|
|
||||||
err = "adapter requires primitive src conversion subfield"; break;
|
err = "adapter requires primitive src conversion subfield"; break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1840,14 +1978,12 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) {
|
||||||
err = "adapter requires src/dest conversion subfields for swap"; break;
|
err = "adapter requires src/dest conversion subfields for swap"; break;
|
||||||
}
|
}
|
||||||
int swap_size = type2size[src];
|
int swap_size = type2size[src];
|
||||||
oop src_mtype = java_lang_invoke_AdapterMethodHandle::type(mh());
|
int slot_limit = java_lang_invoke_MethodHandle::vmslots(target());
|
||||||
oop dest_mtype = java_lang_invoke_AdapterMethodHandle::type(target());
|
|
||||||
int slot_limit = java_lang_invoke_AdapterMethodHandle::vmslots(target());
|
|
||||||
int src_slot = argslot;
|
int src_slot = argslot;
|
||||||
int dest_slot = vminfo;
|
int dest_slot = vminfo;
|
||||||
bool rotate_up = (src_slot > dest_slot); // upward rotation
|
bool rotate_up = (src_slot > dest_slot); // upward rotation
|
||||||
int src_arg = argnum;
|
int src_arg = argnum;
|
||||||
int dest_arg = argument_slot_to_argnum(dest_mtype, dest_slot);
|
int dest_arg = argument_slot_to_argnum(dst_mtype(), dest_slot);
|
||||||
verify_vmargslot(mh, dest_arg, dest_slot, CHECK);
|
verify_vmargslot(mh, dest_arg, dest_slot, CHECK);
|
||||||
if (!(dest_slot >= src_slot + swap_size) &&
|
if (!(dest_slot >= src_slot + swap_size) &&
|
||||||
!(src_slot >= dest_slot + swap_size)) {
|
!(src_slot >= dest_slot + swap_size)) {
|
||||||
|
@ -1855,8 +1991,8 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) {
|
||||||
} else if (ek == _adapter_swap_args && !(src_slot > dest_slot)) {
|
} else if (ek == _adapter_swap_args && !(src_slot > dest_slot)) {
|
||||||
err = "source of swap must be deeper in stack";
|
err = "source of swap must be deeper in stack";
|
||||||
} else if (ek == _adapter_swap_args) {
|
} else if (ek == _adapter_swap_args) {
|
||||||
err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype, dest_arg),
|
err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype(), dest_arg),
|
||||||
java_lang_invoke_MethodType::ptype(dest_mtype, src_arg),
|
java_lang_invoke_MethodType::ptype(dst_mtype(), src_arg),
|
||||||
dest_arg);
|
dest_arg);
|
||||||
} else if (ek == _adapter_rot_args) {
|
} else if (ek == _adapter_rot_args) {
|
||||||
if (rotate_up) {
|
if (rotate_up) {
|
||||||
|
@ -1864,8 +2000,8 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) {
|
||||||
// rotate up: [dest_slot..src_slot-ss] --> [dest_slot+ss..src_slot]
|
// rotate up: [dest_slot..src_slot-ss] --> [dest_slot+ss..src_slot]
|
||||||
// that is: [src_arg+1..dest_arg] --> [src_arg..dest_arg-1]
|
// that is: [src_arg+1..dest_arg] --> [src_arg..dest_arg-1]
|
||||||
for (int i = src_arg+1; i <= dest_arg && err == NULL; i++) {
|
for (int i = src_arg+1; i <= dest_arg && err == NULL; i++) {
|
||||||
err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype, i),
|
err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype(), i),
|
||||||
java_lang_invoke_MethodType::ptype(dest_mtype, i-1),
|
java_lang_invoke_MethodType::ptype(dst_mtype(), i-1),
|
||||||
i);
|
i);
|
||||||
}
|
}
|
||||||
} else { // rotate down
|
} else { // rotate down
|
||||||
|
@ -1873,28 +2009,54 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) {
|
||||||
// rotate down: [src_slot+ss..dest_slot] --> [src_slot..dest_slot-ss]
|
// rotate down: [src_slot+ss..dest_slot] --> [src_slot..dest_slot-ss]
|
||||||
// that is: [dest_arg..src_arg-1] --> [dst_arg+1..src_arg]
|
// that is: [dest_arg..src_arg-1] --> [dst_arg+1..src_arg]
|
||||||
for (int i = dest_arg; i <= src_arg-1 && err == NULL; i++) {
|
for (int i = dest_arg; i <= src_arg-1 && err == NULL; i++) {
|
||||||
err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype, i),
|
err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype(), i),
|
||||||
java_lang_invoke_MethodType::ptype(dest_mtype, i+1),
|
java_lang_invoke_MethodType::ptype(dst_mtype(), i+1),
|
||||||
i);
|
i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (err == NULL)
|
if (err == NULL)
|
||||||
err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype, src_arg),
|
err = check_argument_type_change(java_lang_invoke_MethodType::ptype(src_mtype(), src_arg),
|
||||||
java_lang_invoke_MethodType::ptype(dest_mtype, dest_arg),
|
java_lang_invoke_MethodType::ptype(dst_mtype(), dest_arg),
|
||||||
src_arg);
|
src_arg);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case _adapter_collect_args:
|
|
||||||
case _adapter_spread_args:
|
case _adapter_spread_args:
|
||||||
|
case _adapter_collect_args:
|
||||||
|
case _adapter_fold_args:
|
||||||
{
|
{
|
||||||
BasicType coll_type = (ek == _adapter_collect_args) ? dest : src;
|
bool is_spread = (ek == _adapter_spread_args);
|
||||||
BasicType elem_type = (ek == _adapter_collect_args) ? src : dest;
|
bool is_fold = (ek == _adapter_fold_args);
|
||||||
if (coll_type != T_OBJECT || elem_type != T_OBJECT) {
|
BasicType coll_type = is_spread ? src : dest;
|
||||||
err = "adapter requires src/dest subfields"; break;
|
BasicType elem_type = is_spread ? dest : src;
|
||||||
// later:
|
// coll_type is type of args in collected form (or T_VOID if none)
|
||||||
// - consider making coll be a primitive array
|
// elem_type is common type of args in spread form (or T_VOID if missing or heterogeneous)
|
||||||
// - consider making coll be a heterogeneous collection
|
if (coll_type == 0 || elem_type == 0) {
|
||||||
|
err = "adapter requires src/dest subfields for spread or collect"; break;
|
||||||
|
}
|
||||||
|
if (is_spread && coll_type != T_OBJECT) {
|
||||||
|
err = "spread adapter requires object type for argument bundle"; break;
|
||||||
|
}
|
||||||
|
Handle spread_mtype = (is_spread ? dst_mtype : src_mtype);
|
||||||
|
int spread_slot = argslot;
|
||||||
|
int spread_arg = argnum;
|
||||||
|
int slots_pushed = stack_move / stack_move_unit();
|
||||||
|
int coll_slot_count = type2size[coll_type];
|
||||||
|
int spread_slot_count = (is_spread ? slots_pushed : -slots_pushed) + coll_slot_count;
|
||||||
|
if (is_fold) spread_slot_count = argument_slot_count(arg_mtype());
|
||||||
|
if (!is_spread) {
|
||||||
|
int init_slots = argument_slot_count(src_mtype());
|
||||||
|
int coll_slots = argument_slot_count(arg_mtype());
|
||||||
|
if (spread_slot_count > init_slots ||
|
||||||
|
spread_slot_count != coll_slots) {
|
||||||
|
err = "collect adapter has inconsistent arg counts"; break;
|
||||||
|
}
|
||||||
|
int next_slots = argument_slot_count(dst_mtype());
|
||||||
|
int unchanged_slots_in = (init_slots - spread_slot_count);
|
||||||
|
int unchanged_slots_out = (next_slots - coll_slot_count - (is_fold ? spread_slot_count : 0));
|
||||||
|
if (unchanged_slots_in != unchanged_slots_out) {
|
||||||
|
err = "collect adapter continuation has inconsistent arg counts"; break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1929,8 +2091,9 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case _adapter_collect_args:
|
case _adapter_collect_args:
|
||||||
if (slots_pushed > 1) {
|
case _adapter_fold_args:
|
||||||
err = "adapter requires conversion subfield slots_pushed <= 1";
|
if (slots_pushed > 2) {
|
||||||
|
err = "adapter requires conversion subfield slots_pushed <= 2";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case _adapter_spread_args:
|
case _adapter_spread_args:
|
||||||
|
@ -1950,23 +2113,24 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err == NULL) {
|
if (err == NULL) {
|
||||||
// Make sure this adapter does not push too deeply.
|
// Make sure this adapter's stack pushing is accurately recorded.
|
||||||
int slots_pushed = stack_move / stack_move_unit();
|
int slots_pushed = stack_move / stack_move_unit();
|
||||||
int this_vmslots = java_lang_invoke_MethodHandle::vmslots(mh());
|
int this_vmslots = java_lang_invoke_MethodHandle::vmslots(mh());
|
||||||
int target_vmslots = java_lang_invoke_MethodHandle::vmslots(target());
|
int target_vmslots = java_lang_invoke_MethodHandle::vmslots(target());
|
||||||
|
int target_pushes = decode_MethodHandle_stack_pushes(target());
|
||||||
if (slots_pushed != (target_vmslots - this_vmslots)) {
|
if (slots_pushed != (target_vmslots - this_vmslots)) {
|
||||||
err = "stack_move inconsistent with previous and current MethodType vmslots";
|
err = "stack_move inconsistent with previous and current MethodType vmslots";
|
||||||
} else if (slots_pushed > 0) {
|
} else {
|
||||||
// verify stack_move against MethodHandlePushLimit
|
int this_pushes = decode_MethodHandle_stack_pushes(mh());
|
||||||
int target_pushes = decode_MethodHandle_stack_pushes(target());
|
if (slots_pushed + target_pushes != this_pushes) {
|
||||||
// do not blow the stack; use a Java-based adapter if this limit is exceeded
|
if (this_pushes == 0)
|
||||||
if (slots_pushed + target_pushes > MethodHandlePushLimit) {
|
err = "adapter push count not initialized";
|
||||||
err = "adapter pushes too many parameters";
|
else
|
||||||
|
err = "adapter push count is wrong";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// While we're at it, check that the stack motion decoder works:
|
// While we're at it, check that the stack motion decoder works:
|
||||||
DEBUG_ONLY(int target_pushes = decode_MethodHandle_stack_pushes(target()));
|
|
||||||
DEBUG_ONLY(int this_pushes = decode_MethodHandle_stack_pushes(mh()));
|
DEBUG_ONLY(int this_pushes = decode_MethodHandle_stack_pushes(mh()));
|
||||||
assert(this_pushes == slots_pushed + target_pushes, "AMH stack motion must be correct");
|
assert(this_pushes == slots_pushed + target_pushes, "AMH stack motion must be correct");
|
||||||
}
|
}
|
||||||
|
@ -1975,6 +2139,9 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) {
|
||||||
switch (ek) {
|
switch (ek) {
|
||||||
case _adapter_swap_args:
|
case _adapter_swap_args:
|
||||||
case _adapter_rot_args:
|
case _adapter_rot_args:
|
||||||
|
case _adapter_prim_to_ref:
|
||||||
|
case _adapter_collect_args:
|
||||||
|
case _adapter_fold_args:
|
||||||
break; // OK
|
break; // OK
|
||||||
default:
|
default:
|
||||||
err = "vminfo subfield is reserved to the JVM";
|
err = "vminfo subfield is reserved to the JVM";
|
||||||
|
@ -2019,6 +2186,7 @@ void MethodHandles::verify_AdapterMethodHandle(Handle mh, int argnum, TRAPS) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnum, TRAPS) {
|
void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnum, TRAPS) {
|
||||||
|
Handle argument = java_lang_invoke_AdapterMethodHandle::argument(mh());
|
||||||
int argslot = java_lang_invoke_AdapterMethodHandle::vmargslot(mh());
|
int argslot = java_lang_invoke_AdapterMethodHandle::vmargslot(mh());
|
||||||
jint conversion = java_lang_invoke_AdapterMethodHandle::conversion(mh());
|
jint conversion = java_lang_invoke_AdapterMethodHandle::conversion(mh());
|
||||||
jint conv_op = adapter_conversion_op(conversion);
|
jint conv_op = adapter_conversion_op(conversion);
|
||||||
|
@ -2026,6 +2194,7 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu
|
||||||
// adjust the adapter code to the internal EntryKind enumeration:
|
// adjust the adapter code to the internal EntryKind enumeration:
|
||||||
EntryKind ek_orig = adapter_entry_kind(conv_op);
|
EntryKind ek_orig = adapter_entry_kind(conv_op);
|
||||||
EntryKind ek_opt = ek_orig; // may be optimized
|
EntryKind ek_opt = ek_orig; // may be optimized
|
||||||
|
EntryKind ek_try; // temp
|
||||||
|
|
||||||
// Finalize the vmtarget field (Java initialized it to null).
|
// Finalize the vmtarget field (Java initialized it to null).
|
||||||
if (!java_lang_invoke_MethodHandle::is_instance(target())) {
|
if (!java_lang_invoke_MethodHandle::is_instance(target())) {
|
||||||
|
@ -2034,17 +2203,23 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu
|
||||||
}
|
}
|
||||||
java_lang_invoke_AdapterMethodHandle::set_vmtarget(mh(), target());
|
java_lang_invoke_AdapterMethodHandle::set_vmtarget(mh(), target());
|
||||||
|
|
||||||
if (VerifyMethodHandles) {
|
|
||||||
verify_AdapterMethodHandle(mh, argnum, CHECK);
|
|
||||||
}
|
|
||||||
|
|
||||||
int stack_move = adapter_conversion_stack_move(conversion);
|
int stack_move = adapter_conversion_stack_move(conversion);
|
||||||
BasicType src = adapter_conversion_src_type(conversion);
|
BasicType src = adapter_conversion_src_type(conversion);
|
||||||
BasicType dest = adapter_conversion_dest_type(conversion);
|
BasicType dest = adapter_conversion_dest_type(conversion);
|
||||||
int vminfo = adapter_conversion_vminfo(conversion); // should be zero
|
int vminfo = adapter_conversion_vminfo(conversion); // should be zero
|
||||||
|
|
||||||
|
int slots_pushed = stack_move / stack_move_unit();
|
||||||
|
|
||||||
|
if (VerifyMethodHandles) {
|
||||||
|
verify_AdapterMethodHandle(mh, argnum, CHECK);
|
||||||
|
}
|
||||||
|
|
||||||
const char* err = NULL;
|
const char* err = NULL;
|
||||||
|
|
||||||
|
if (!conv_op_supported(conv_op)) {
|
||||||
|
err = "adapter not yet implemented in the JVM";
|
||||||
|
}
|
||||||
|
|
||||||
// Now it's time to finish the case analysis and pick a MethodHandleEntry.
|
// Now it's time to finish the case analysis and pick a MethodHandleEntry.
|
||||||
switch (ek_orig) {
|
switch (ek_orig) {
|
||||||
case _adapter_retype_only:
|
case _adapter_retype_only:
|
||||||
|
@ -2073,20 +2248,20 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu
|
||||||
} else if (src == T_DOUBLE && dest == T_FLOAT) {
|
} else if (src == T_DOUBLE && dest == T_FLOAT) {
|
||||||
ek_opt = _adapter_opt_d2f;
|
ek_opt = _adapter_opt_d2f;
|
||||||
} else {
|
} else {
|
||||||
assert(false, "");
|
goto throw_not_impl; // runs user code, hence could block
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1 *4+ 2:
|
case 1 *4+ 2:
|
||||||
if (src == T_INT && dest == T_LONG) {
|
if ((src == T_INT || is_subword_type(src)) && dest == T_LONG) {
|
||||||
ek_opt = _adapter_opt_i2l;
|
ek_opt = _adapter_opt_i2l;
|
||||||
} else if (src == T_FLOAT && dest == T_DOUBLE) {
|
} else if (src == T_FLOAT && dest == T_DOUBLE) {
|
||||||
ek_opt = _adapter_opt_f2d;
|
ek_opt = _adapter_opt_f2d;
|
||||||
} else {
|
} else {
|
||||||
assert(false, "");
|
goto throw_not_impl; // runs user code, hence could block
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false, "");
|
goto throw_not_impl; // runs user code, hence could block
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2103,14 +2278,54 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu
|
||||||
ek_opt = _adapter_opt_unboxl;
|
ek_opt = _adapter_opt_unboxl;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false, "");
|
goto throw_not_impl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case _adapter_prim_to_ref:
|
case _adapter_prim_to_ref:
|
||||||
goto throw_not_impl; // allocates, hence could block
|
{
|
||||||
|
assert(UseRicochetFrames, "else don't come here");
|
||||||
|
// vminfo will be the location to insert the return value
|
||||||
|
vminfo = argslot;
|
||||||
|
ek_opt = _adapter_opt_collect_ref;
|
||||||
|
ensure_vmlayout_field(target, CHECK);
|
||||||
|
// for MethodHandleWalk:
|
||||||
|
if (java_lang_invoke_AdapterMethodHandle::is_instance(argument()))
|
||||||
|
ensure_vmlayout_field(argument, CHECK);
|
||||||
|
if (!OptimizeMethodHandles) break;
|
||||||
|
switch (type2size[src]) {
|
||||||
|
case 1:
|
||||||
|
ek_try = EntryKind(_adapter_opt_filter_S0_ref + argslot);
|
||||||
|
if (ek_try < _adapter_opt_collect_LAST &&
|
||||||
|
ek_adapter_opt_collect_slot(ek_try) == argslot) {
|
||||||
|
assert(ek_adapter_opt_collect_count(ek_try) == 1 &&
|
||||||
|
ek_adapter_opt_collect_type(ek_try) == T_OBJECT, "");
|
||||||
|
ek_opt = ek_try;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// else downgrade to variable slot:
|
||||||
|
ek_opt = _adapter_opt_collect_1_ref;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
ek_try = EntryKind(_adapter_opt_collect_2_S0_ref + argslot);
|
||||||
|
if (ek_try < _adapter_opt_collect_LAST &&
|
||||||
|
ek_adapter_opt_collect_slot(ek_try) == argslot) {
|
||||||
|
assert(ek_adapter_opt_collect_count(ek_try) == 2 &&
|
||||||
|
ek_adapter_opt_collect_type(ek_try) == T_OBJECT, "");
|
||||||
|
ek_opt = ek_try;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// else downgrade to variable slot:
|
||||||
|
ek_opt = _adapter_opt_collect_2_ref;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto throw_not_impl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case _adapter_swap_args:
|
case _adapter_swap_args:
|
||||||
case _adapter_rot_args:
|
case _adapter_rot_args:
|
||||||
|
@ -2130,19 +2345,17 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu
|
||||||
rotate > 0 ? _adapter_opt_rot_2_up : _adapter_opt_rot_2_down);
|
rotate > 0 ? _adapter_opt_rot_2_up : _adapter_opt_rot_2_down);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false, "");
|
goto throw_not_impl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case _adapter_collect_args:
|
|
||||||
goto throw_not_impl; // allocates, hence could block
|
|
||||||
|
|
||||||
case _adapter_spread_args:
|
case _adapter_spread_args:
|
||||||
{
|
{
|
||||||
// vminfo will be the required length of the array
|
#ifdef TARGET_ARCH_NYI_6939861
|
||||||
int slots_pushed = stack_move / stack_move_unit();
|
// ports before 6939861 supported only three kinds of spread ops
|
||||||
|
if (!UseRicochetFrames) {
|
||||||
int array_size = slots_pushed + 1;
|
int array_size = slots_pushed + 1;
|
||||||
assert(array_size >= 0, "");
|
assert(array_size >= 0, "");
|
||||||
vminfo = array_size;
|
vminfo = array_size;
|
||||||
|
@ -2151,14 +2364,165 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu
|
||||||
case 1: ek_opt = _adapter_opt_spread_1; break;
|
case 1: ek_opt = _adapter_opt_spread_1; break;
|
||||||
default: ek_opt = _adapter_opt_spread_more; break;
|
default: ek_opt = _adapter_opt_spread_more; break;
|
||||||
}
|
}
|
||||||
if ((vminfo & CONV_VMINFO_MASK) != vminfo)
|
break;
|
||||||
goto throw_not_impl; // overflow
|
}
|
||||||
|
#endif //TARGET_ARCH_NYI_6939861
|
||||||
|
// vminfo will be the required length of the array
|
||||||
|
int array_size = (slots_pushed + 1) / (type2size[dest] == 2 ? 2 : 1);
|
||||||
|
vminfo = array_size;
|
||||||
|
// general case
|
||||||
|
switch (dest) {
|
||||||
|
case T_BOOLEAN : // fall through to T_BYTE:
|
||||||
|
case T_BYTE : ek_opt = _adapter_opt_spread_byte; break;
|
||||||
|
case T_CHAR : ek_opt = _adapter_opt_spread_char; break;
|
||||||
|
case T_SHORT : ek_opt = _adapter_opt_spread_short; break;
|
||||||
|
case T_INT : ek_opt = _adapter_opt_spread_int; break;
|
||||||
|
case T_LONG : ek_opt = _adapter_opt_spread_long; break;
|
||||||
|
case T_FLOAT : ek_opt = _adapter_opt_spread_float; break;
|
||||||
|
case T_DOUBLE : ek_opt = _adapter_opt_spread_double; break;
|
||||||
|
case T_OBJECT : ek_opt = _adapter_opt_spread_ref; break;
|
||||||
|
case T_VOID : if (array_size != 0) goto throw_not_impl;
|
||||||
|
ek_opt = _adapter_opt_spread_ref; break;
|
||||||
|
default : goto throw_not_impl;
|
||||||
|
}
|
||||||
|
assert(array_size == 0 || // it doesn't matter what the spreader is
|
||||||
|
(ek_adapter_opt_spread_count(ek_opt) == -1 &&
|
||||||
|
(ek_adapter_opt_spread_type(ek_opt) == dest ||
|
||||||
|
(ek_adapter_opt_spread_type(ek_opt) == T_BYTE && dest == T_BOOLEAN))),
|
||||||
|
err_msg("dest=%d ek_opt=%d", dest, ek_opt));
|
||||||
|
|
||||||
|
if (array_size <= 0) {
|
||||||
|
// since the general case does not handle length 0, this case is required:
|
||||||
|
ek_opt = _adapter_opt_spread_0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (dest == T_OBJECT) {
|
||||||
|
ek_try = EntryKind(_adapter_opt_spread_1_ref - 1 + array_size);
|
||||||
|
if (ek_try < _adapter_opt_spread_LAST &&
|
||||||
|
ek_adapter_opt_spread_count(ek_try) == array_size) {
|
||||||
|
assert(ek_adapter_opt_spread_type(ek_try) == dest, "");
|
||||||
|
ek_opt = ek_try;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case _adapter_flyby:
|
case _adapter_collect_args:
|
||||||
case _adapter_ricochet:
|
{
|
||||||
goto throw_not_impl; // runs Java code, hence could block
|
assert(UseRicochetFrames, "else don't come here");
|
||||||
|
int elem_slots = argument_slot_count(java_lang_invoke_MethodHandle::type(argument()));
|
||||||
|
// vminfo will be the location to insert the return value
|
||||||
|
vminfo = argslot;
|
||||||
|
ensure_vmlayout_field(target, CHECK);
|
||||||
|
ensure_vmlayout_field(argument, CHECK);
|
||||||
|
|
||||||
|
// general case:
|
||||||
|
switch (dest) {
|
||||||
|
default : if (!is_subword_type(dest)) goto throw_not_impl;
|
||||||
|
// else fall through:
|
||||||
|
case T_INT : ek_opt = _adapter_opt_collect_int; break;
|
||||||
|
case T_LONG : ek_opt = _adapter_opt_collect_long; break;
|
||||||
|
case T_FLOAT : ek_opt = _adapter_opt_collect_float; break;
|
||||||
|
case T_DOUBLE : ek_opt = _adapter_opt_collect_double; break;
|
||||||
|
case T_OBJECT : ek_opt = _adapter_opt_collect_ref; break;
|
||||||
|
case T_VOID : ek_opt = _adapter_opt_collect_void; break;
|
||||||
|
}
|
||||||
|
assert(ek_adapter_opt_collect_slot(ek_opt) == -1 &&
|
||||||
|
ek_adapter_opt_collect_count(ek_opt) == -1 &&
|
||||||
|
(ek_adapter_opt_collect_type(ek_opt) == dest ||
|
||||||
|
ek_adapter_opt_collect_type(ek_opt) == T_INT && is_subword_type(dest)),
|
||||||
|
"");
|
||||||
|
|
||||||
|
if (dest == T_OBJECT && elem_slots == 1 && OptimizeMethodHandles) {
|
||||||
|
// filter operation on a ref
|
||||||
|
ek_try = EntryKind(_adapter_opt_filter_S0_ref + argslot);
|
||||||
|
if (ek_try < _adapter_opt_collect_LAST &&
|
||||||
|
ek_adapter_opt_collect_slot(ek_try) == argslot) {
|
||||||
|
assert(ek_adapter_opt_collect_count(ek_try) == elem_slots &&
|
||||||
|
ek_adapter_opt_collect_type(ek_try) == dest, "");
|
||||||
|
ek_opt = ek_try;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ek_opt = _adapter_opt_collect_1_ref;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dest == T_OBJECT && elem_slots == 2 && OptimizeMethodHandles) {
|
||||||
|
// filter of two arguments
|
||||||
|
ek_try = EntryKind(_adapter_opt_collect_2_S0_ref + argslot);
|
||||||
|
if (ek_try < _adapter_opt_collect_LAST &&
|
||||||
|
ek_adapter_opt_collect_slot(ek_try) == argslot) {
|
||||||
|
assert(ek_adapter_opt_collect_count(ek_try) == elem_slots &&
|
||||||
|
ek_adapter_opt_collect_type(ek_try) == dest, "");
|
||||||
|
ek_opt = ek_try;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ek_opt = _adapter_opt_collect_2_ref;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dest == T_OBJECT && OptimizeMethodHandles) {
|
||||||
|
// try to use a fixed length adapter
|
||||||
|
ek_try = EntryKind(_adapter_opt_collect_0_ref + elem_slots);
|
||||||
|
if (ek_try < _adapter_opt_collect_LAST &&
|
||||||
|
ek_adapter_opt_collect_count(ek_try) == elem_slots) {
|
||||||
|
assert(ek_adapter_opt_collect_slot(ek_try) == -1 &&
|
||||||
|
ek_adapter_opt_collect_type(ek_try) == dest, "");
|
||||||
|
ek_opt = ek_try;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case _adapter_fold_args:
|
||||||
|
{
|
||||||
|
assert(UseRicochetFrames, "else don't come here");
|
||||||
|
int elem_slots = argument_slot_count(java_lang_invoke_MethodHandle::type(argument()));
|
||||||
|
// vminfo will be the location to insert the return value
|
||||||
|
vminfo = argslot + elem_slots;
|
||||||
|
ensure_vmlayout_field(target, CHECK);
|
||||||
|
ensure_vmlayout_field(argument, CHECK);
|
||||||
|
|
||||||
|
switch (dest) {
|
||||||
|
default : if (!is_subword_type(dest)) goto throw_not_impl;
|
||||||
|
// else fall through:
|
||||||
|
case T_INT : ek_opt = _adapter_opt_fold_int; break;
|
||||||
|
case T_LONG : ek_opt = _adapter_opt_fold_long; break;
|
||||||
|
case T_FLOAT : ek_opt = _adapter_opt_fold_float; break;
|
||||||
|
case T_DOUBLE : ek_opt = _adapter_opt_fold_double; break;
|
||||||
|
case T_OBJECT : ek_opt = _adapter_opt_fold_ref; break;
|
||||||
|
case T_VOID : ek_opt = _adapter_opt_fold_void; break;
|
||||||
|
}
|
||||||
|
assert(ek_adapter_opt_collect_slot(ek_opt) == -1 &&
|
||||||
|
ek_adapter_opt_collect_count(ek_opt) == -1 &&
|
||||||
|
(ek_adapter_opt_collect_type(ek_opt) == dest ||
|
||||||
|
ek_adapter_opt_collect_type(ek_opt) == T_INT && is_subword_type(dest)),
|
||||||
|
"");
|
||||||
|
|
||||||
|
if (dest == T_OBJECT && elem_slots == 0 && OptimizeMethodHandles) {
|
||||||
|
// if there are no args, just pretend it's a collect
|
||||||
|
ek_opt = _adapter_opt_collect_0_ref;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dest == T_OBJECT && OptimizeMethodHandles) {
|
||||||
|
// try to use a fixed length adapter
|
||||||
|
ek_try = EntryKind(_adapter_opt_fold_1_ref - 1 + elem_slots);
|
||||||
|
if (ek_try < _adapter_opt_fold_LAST &&
|
||||||
|
ek_adapter_opt_collect_count(ek_try) == elem_slots) {
|
||||||
|
assert(ek_adapter_opt_collect_slot(ek_try) == -1 &&
|
||||||
|
ek_adapter_opt_collect_type(ek_try) == dest, "");
|
||||||
|
ek_opt = ek_try;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// should have failed much earlier; must be a missing case here
|
// should have failed much earlier; must be a missing case here
|
||||||
|
@ -2166,11 +2530,36 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu
|
||||||
// and fall through:
|
// and fall through:
|
||||||
|
|
||||||
throw_not_impl:
|
throw_not_impl:
|
||||||
// FIXME: these adapters are NYI
|
if (err == NULL)
|
||||||
err = "adapter not yet implemented in the JVM";
|
err = "unknown adapter type";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (err == NULL && (vminfo & CONV_VMINFO_MASK) != vminfo) {
|
||||||
|
// should not happen, since vminfo is used to encode arg/slot indexes < 255
|
||||||
|
err = "vminfo overflow";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == NULL && !have_entry(ek_opt)) {
|
||||||
|
err = "adapter stub for this kind of method handle is missing";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == NULL && ek_opt == ek_orig) {
|
||||||
|
switch (ek_opt) {
|
||||||
|
case _adapter_prim_to_prim:
|
||||||
|
case _adapter_ref_to_prim:
|
||||||
|
case _adapter_prim_to_ref:
|
||||||
|
case _adapter_swap_args:
|
||||||
|
case _adapter_rot_args:
|
||||||
|
case _adapter_collect_args:
|
||||||
|
case _adapter_fold_args:
|
||||||
|
case _adapter_spread_args:
|
||||||
|
// should be handled completely by optimized cases; see above
|
||||||
|
err = "init_AdapterMethodHandle should not issue this";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (err != NULL) {
|
if (err != NULL) {
|
||||||
throw_InternalError_for_bad_conversion(conversion, err, THREAD);
|
throw_InternalError_for_bad_conversion(conversion, err, THREAD);
|
||||||
return;
|
return;
|
||||||
|
@ -2190,6 +2579,26 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu
|
||||||
// Java code can publish it in global data structures.
|
// Java code can publish it in global data structures.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MethodHandles::ensure_vmlayout_field(Handle target, TRAPS) {
|
||||||
|
Handle mtype(THREAD, java_lang_invoke_MethodHandle::type(target()));
|
||||||
|
Handle mtform(THREAD, java_lang_invoke_MethodType::form(mtype()));
|
||||||
|
if (mtform.is_null()) { THROW(vmSymbols::java_lang_InternalError()); }
|
||||||
|
if (java_lang_invoke_MethodTypeForm::vmlayout_offset_in_bytes() > 0) {
|
||||||
|
if (java_lang_invoke_MethodTypeForm::vmlayout(mtform()) == NULL) {
|
||||||
|
// fill it in
|
||||||
|
Handle erased_mtype(THREAD, java_lang_invoke_MethodTypeForm::erasedType(mtform()));
|
||||||
|
TempNewSymbol erased_signature
|
||||||
|
= java_lang_invoke_MethodType::as_signature(erased_mtype(), /*intern:*/true, CHECK);
|
||||||
|
methodOop cookie
|
||||||
|
= SystemDictionary::find_method_handle_invoke(vmSymbols::invokeExact_name(),
|
||||||
|
erased_signature,
|
||||||
|
SystemDictionaryHandles::Object_klass(),
|
||||||
|
THREAD);
|
||||||
|
java_lang_invoke_MethodTypeForm::init_vmlayout(mtform(), cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Here are the native methods on sun.invoke.MethodHandleImpl.
|
// Here are the native methods on sun.invoke.MethodHandleImpl.
|
||||||
// They are the private interface between this JVM and the HotSpot-specific
|
// They are the private interface between this JVM and the HotSpot-specific
|
||||||
|
@ -2360,8 +2769,10 @@ JVM_END
|
||||||
|
|
||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
#define EACH_NAMED_CON(template) \
|
#define EACH_NAMED_CON(template) \
|
||||||
template(MethodHandles,GC_JVM_PUSH_LIMIT) \
|
/* hold back this one until JDK stabilizes */ \
|
||||||
template(MethodHandles,GC_JVM_STACK_MOVE_UNIT) \
|
/* template(MethodHandles,GC_JVM_PUSH_LIMIT) */ \
|
||||||
|
/* hold back this one until JDK stabilizes */ \
|
||||||
|
/* template(MethodHandles,GC_JVM_STACK_MOVE_UNIT) */ \
|
||||||
template(MethodHandles,ETF_HANDLE_OR_METHOD_NAME) \
|
template(MethodHandles,ETF_HANDLE_OR_METHOD_NAME) \
|
||||||
template(MethodHandles,ETF_DIRECT_HANDLE) \
|
template(MethodHandles,ETF_DIRECT_HANDLE) \
|
||||||
template(MethodHandles,ETF_METHOD_NAME) \
|
template(MethodHandles,ETF_METHOD_NAME) \
|
||||||
|
@ -2385,9 +2796,8 @@ JVM_END
|
||||||
template(java_lang_invoke_AdapterMethodHandle,OP_DROP_ARGS) \
|
template(java_lang_invoke_AdapterMethodHandle,OP_DROP_ARGS) \
|
||||||
template(java_lang_invoke_AdapterMethodHandle,OP_COLLECT_ARGS) \
|
template(java_lang_invoke_AdapterMethodHandle,OP_COLLECT_ARGS) \
|
||||||
template(java_lang_invoke_AdapterMethodHandle,OP_SPREAD_ARGS) \
|
template(java_lang_invoke_AdapterMethodHandle,OP_SPREAD_ARGS) \
|
||||||
template(java_lang_invoke_AdapterMethodHandle,OP_FLYBY) \
|
/* hold back this one until JDK stabilizes */ \
|
||||||
template(java_lang_invoke_AdapterMethodHandle,OP_RICOCHET) \
|
/*template(java_lang_invoke_AdapterMethodHandle,CONV_OP_LIMIT)*/ \
|
||||||
template(java_lang_invoke_AdapterMethodHandle,CONV_OP_LIMIT) \
|
|
||||||
template(java_lang_invoke_AdapterMethodHandle,CONV_OP_MASK) \
|
template(java_lang_invoke_AdapterMethodHandle,CONV_OP_MASK) \
|
||||||
template(java_lang_invoke_AdapterMethodHandle,CONV_VMINFO_MASK) \
|
template(java_lang_invoke_AdapterMethodHandle,CONV_VMINFO_MASK) \
|
||||||
template(java_lang_invoke_AdapterMethodHandle,CONV_VMINFO_SHIFT) \
|
template(java_lang_invoke_AdapterMethodHandle,CONV_VMINFO_SHIFT) \
|
||||||
|
|
|
@ -66,8 +66,8 @@ class MethodHandles: AllStatic {
|
||||||
_adapter_drop_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_DROP_ARGS,
|
_adapter_drop_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_DROP_ARGS,
|
||||||
_adapter_collect_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS,
|
_adapter_collect_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_COLLECT_ARGS,
|
||||||
_adapter_spread_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_SPREAD_ARGS,
|
_adapter_spread_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_SPREAD_ARGS,
|
||||||
_adapter_flyby = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_FLYBY,
|
_adapter_fold_args = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_FOLD_ARGS,
|
||||||
_adapter_ricochet = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::OP_RICOCHET,
|
_adapter_unused_13 = _adapter_mh_first + 13, //hole in the CONV_OP enumeration
|
||||||
_adapter_mh_last = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::CONV_OP_LIMIT - 1,
|
_adapter_mh_last = _adapter_mh_first + java_lang_invoke_AdapterMethodHandle::CONV_OP_LIMIT - 1,
|
||||||
|
|
||||||
// Optimized adapter types
|
// Optimized adapter types
|
||||||
|
@ -93,10 +93,99 @@ class MethodHandles: AllStatic {
|
||||||
_adapter_opt_unboxi,
|
_adapter_opt_unboxi,
|
||||||
_adapter_opt_unboxl,
|
_adapter_opt_unboxl,
|
||||||
|
|
||||||
// spreading (array length cases 0, 1, >=2)
|
// %% Maybe tame the following with a VM_SYMBOLS_DO type macro?
|
||||||
_adapter_opt_spread_0,
|
|
||||||
_adapter_opt_spread_1,
|
// how a blocking adapter returns (platform-dependent)
|
||||||
_adapter_opt_spread_more,
|
_adapter_opt_return_ref,
|
||||||
|
_adapter_opt_return_int,
|
||||||
|
_adapter_opt_return_long,
|
||||||
|
_adapter_opt_return_float,
|
||||||
|
_adapter_opt_return_double,
|
||||||
|
_adapter_opt_return_void,
|
||||||
|
_adapter_opt_return_S0_ref, // return ref to S=0 (last slot)
|
||||||
|
_adapter_opt_return_S1_ref, // return ref to S=1 (2nd-to-last slot)
|
||||||
|
_adapter_opt_return_S2_ref,
|
||||||
|
_adapter_opt_return_S3_ref,
|
||||||
|
_adapter_opt_return_S4_ref,
|
||||||
|
_adapter_opt_return_S5_ref,
|
||||||
|
_adapter_opt_return_any, // dynamically select r/i/l/f/d
|
||||||
|
_adapter_opt_return_FIRST = _adapter_opt_return_ref,
|
||||||
|
_adapter_opt_return_LAST = _adapter_opt_return_any,
|
||||||
|
|
||||||
|
// spreading (array length cases 0, 1, ...)
|
||||||
|
_adapter_opt_spread_0, // spread empty array to N=0 arguments
|
||||||
|
_adapter_opt_spread_1_ref, // spread Object[] to N=1 argument
|
||||||
|
_adapter_opt_spread_2_ref, // spread Object[] to N=2 arguments
|
||||||
|
_adapter_opt_spread_3_ref, // spread Object[] to N=3 arguments
|
||||||
|
_adapter_opt_spread_4_ref, // spread Object[] to N=4 arguments
|
||||||
|
_adapter_opt_spread_5_ref, // spread Object[] to N=5 arguments
|
||||||
|
_adapter_opt_spread_ref, // spread Object[] to N arguments
|
||||||
|
_adapter_opt_spread_byte, // spread byte[] or boolean[] to N arguments
|
||||||
|
_adapter_opt_spread_char, // spread char[], etc., to N arguments
|
||||||
|
_adapter_opt_spread_short, // spread short[], etc., to N arguments
|
||||||
|
_adapter_opt_spread_int, // spread int[], short[], etc., to N arguments
|
||||||
|
_adapter_opt_spread_long, // spread long[] to N arguments
|
||||||
|
_adapter_opt_spread_float, // spread float[] to N arguments
|
||||||
|
_adapter_opt_spread_double, // spread double[] to N arguments
|
||||||
|
_adapter_opt_spread_FIRST = _adapter_opt_spread_0,
|
||||||
|
_adapter_opt_spread_LAST = _adapter_opt_spread_double,
|
||||||
|
|
||||||
|
// blocking filter/collect conversions
|
||||||
|
// These collect N arguments and replace them (at slot S) by a return value
|
||||||
|
// which is passed to the final target, along with the unaffected arguments.
|
||||||
|
// collect_{N}_{T} collects N arguments at any position into a T value
|
||||||
|
// collect_{N}_S{S}_{T} collects N arguments at slot S into a T value
|
||||||
|
// collect_{T} collects any number of arguments at any position
|
||||||
|
// filter_S{S}_{T} is the same as collect_1_S{S}_{T} (a unary collection)
|
||||||
|
// (collect_2 is also usable as a filter, with long or double arguments)
|
||||||
|
_adapter_opt_collect_ref, // combine N arguments, replace with a reference
|
||||||
|
_adapter_opt_collect_int, // combine N arguments, replace with an int, short, etc.
|
||||||
|
_adapter_opt_collect_long, // combine N arguments, replace with a long
|
||||||
|
_adapter_opt_collect_float, // combine N arguments, replace with a float
|
||||||
|
_adapter_opt_collect_double, // combine N arguments, replace with a double
|
||||||
|
_adapter_opt_collect_void, // combine N arguments, replace with nothing
|
||||||
|
// if there is a small fixed number to push, do so without a loop:
|
||||||
|
_adapter_opt_collect_0_ref, // collect N=0 arguments, insert a reference
|
||||||
|
_adapter_opt_collect_1_ref, // collect N=1 argument, replace with a reference
|
||||||
|
_adapter_opt_collect_2_ref, // combine N=2 arguments, replace with a reference
|
||||||
|
_adapter_opt_collect_3_ref, // combine N=3 arguments, replace with a reference
|
||||||
|
_adapter_opt_collect_4_ref, // combine N=4 arguments, replace with a reference
|
||||||
|
_adapter_opt_collect_5_ref, // combine N=5 arguments, replace with a reference
|
||||||
|
// filters are an important special case because they never move arguments:
|
||||||
|
_adapter_opt_filter_S0_ref, // filter N=1 argument at S=0, replace with a reference
|
||||||
|
_adapter_opt_filter_S1_ref, // filter N=1 argument at S=1, replace with a reference
|
||||||
|
_adapter_opt_filter_S2_ref, // filter N=1 argument at S=2, replace with a reference
|
||||||
|
_adapter_opt_filter_S3_ref, // filter N=1 argument at S=3, replace with a reference
|
||||||
|
_adapter_opt_filter_S4_ref, // filter N=1 argument at S=4, replace with a reference
|
||||||
|
_adapter_opt_filter_S5_ref, // filter N=1 argument at S=5, replace with a reference
|
||||||
|
// these move arguments, but they are important for boxing
|
||||||
|
_adapter_opt_collect_2_S0_ref, // combine last N=2 arguments, replace with a reference
|
||||||
|
_adapter_opt_collect_2_S1_ref, // combine N=2 arguments at S=1, replace with a reference
|
||||||
|
_adapter_opt_collect_2_S2_ref, // combine N=2 arguments at S=2, replace with a reference
|
||||||
|
_adapter_opt_collect_2_S3_ref, // combine N=2 arguments at S=3, replace with a reference
|
||||||
|
_adapter_opt_collect_2_S4_ref, // combine N=2 arguments at S=4, replace with a reference
|
||||||
|
_adapter_opt_collect_2_S5_ref, // combine N=2 arguments at S=5, replace with a reference
|
||||||
|
_adapter_opt_collect_FIRST = _adapter_opt_collect_ref,
|
||||||
|
_adapter_opt_collect_LAST = _adapter_opt_collect_2_S5_ref,
|
||||||
|
|
||||||
|
// blocking folding conversions
|
||||||
|
// these are like collects, but retain all the N arguments for the final target
|
||||||
|
//_adapter_opt_fold_0_ref, // same as _adapter_opt_collect_0_ref
|
||||||
|
// fold_{N}_{T} processes N arguments at any position into a T value, which it inserts
|
||||||
|
// fold_{T} processes any number of arguments at any position
|
||||||
|
_adapter_opt_fold_ref, // process N arguments, prepend a reference
|
||||||
|
_adapter_opt_fold_int, // process N arguments, prepend an int, short, etc.
|
||||||
|
_adapter_opt_fold_long, // process N arguments, prepend a long
|
||||||
|
_adapter_opt_fold_float, // process N arguments, prepend a float
|
||||||
|
_adapter_opt_fold_double, // process N arguments, prepend a double
|
||||||
|
_adapter_opt_fold_void, // process N arguments, but leave the list unchanged
|
||||||
|
_adapter_opt_fold_1_ref, // process N=1 argument, prepend a reference
|
||||||
|
_adapter_opt_fold_2_ref, // process N=2 arguments, prepend a reference
|
||||||
|
_adapter_opt_fold_3_ref, // process N=3 arguments, prepend a reference
|
||||||
|
_adapter_opt_fold_4_ref, // process N=4 arguments, prepend a reference
|
||||||
|
_adapter_opt_fold_5_ref, // process N=5 arguments, prepend a reference
|
||||||
|
_adapter_opt_fold_FIRST = _adapter_opt_fold_ref,
|
||||||
|
_adapter_opt_fold_LAST = _adapter_opt_fold_5_ref,
|
||||||
|
|
||||||
_EK_LIMIT,
|
_EK_LIMIT,
|
||||||
_EK_FIRST = 0
|
_EK_FIRST = 0
|
||||||
|
@ -110,6 +199,7 @@ class MethodHandles: AllStatic {
|
||||||
enum { // import java_lang_invoke_AdapterMethodHandle::CONV_OP_*
|
enum { // import java_lang_invoke_AdapterMethodHandle::CONV_OP_*
|
||||||
CONV_OP_LIMIT = java_lang_invoke_AdapterMethodHandle::CONV_OP_LIMIT,
|
CONV_OP_LIMIT = java_lang_invoke_AdapterMethodHandle::CONV_OP_LIMIT,
|
||||||
CONV_OP_MASK = java_lang_invoke_AdapterMethodHandle::CONV_OP_MASK,
|
CONV_OP_MASK = java_lang_invoke_AdapterMethodHandle::CONV_OP_MASK,
|
||||||
|
CONV_TYPE_MASK = java_lang_invoke_AdapterMethodHandle::CONV_TYPE_MASK,
|
||||||
CONV_VMINFO_MASK = java_lang_invoke_AdapterMethodHandle::CONV_VMINFO_MASK,
|
CONV_VMINFO_MASK = java_lang_invoke_AdapterMethodHandle::CONV_VMINFO_MASK,
|
||||||
CONV_VMINFO_SHIFT = java_lang_invoke_AdapterMethodHandle::CONV_VMINFO_SHIFT,
|
CONV_VMINFO_SHIFT = java_lang_invoke_AdapterMethodHandle::CONV_VMINFO_SHIFT,
|
||||||
CONV_OP_SHIFT = java_lang_invoke_AdapterMethodHandle::CONV_OP_SHIFT,
|
CONV_OP_SHIFT = java_lang_invoke_AdapterMethodHandle::CONV_OP_SHIFT,
|
||||||
|
@ -123,6 +213,7 @@ class MethodHandles: AllStatic {
|
||||||
static MethodHandleEntry* _entries[_EK_LIMIT];
|
static MethodHandleEntry* _entries[_EK_LIMIT];
|
||||||
static const char* _entry_names[_EK_LIMIT+1];
|
static const char* _entry_names[_EK_LIMIT+1];
|
||||||
static jobject _raise_exception_method;
|
static jobject _raise_exception_method;
|
||||||
|
static address _adapter_return_handlers[CONV_TYPE_MASK+1];
|
||||||
|
|
||||||
// Adapters.
|
// Adapters.
|
||||||
static MethodHandlesAdapterBlob* _adapter_code;
|
static MethodHandlesAdapterBlob* _adapter_code;
|
||||||
|
@ -147,39 +238,195 @@ class MethodHandles: AllStatic {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some adapter helper functions.
|
// Some adapter helper functions.
|
||||||
static void get_ek_bound_mh_info(EntryKind ek, BasicType& arg_type, int& arg_mask, int& arg_slots) {
|
static EntryKind ek_original_kind(EntryKind ek) {
|
||||||
|
if (ek <= _adapter_mh_last) return ek;
|
||||||
|
switch (ek) {
|
||||||
|
case _adapter_opt_swap_1:
|
||||||
|
case _adapter_opt_swap_2:
|
||||||
|
return _adapter_swap_args;
|
||||||
|
case _adapter_opt_rot_1_up:
|
||||||
|
case _adapter_opt_rot_1_down:
|
||||||
|
case _adapter_opt_rot_2_up:
|
||||||
|
case _adapter_opt_rot_2_down:
|
||||||
|
return _adapter_rot_args;
|
||||||
|
case _adapter_opt_i2i:
|
||||||
|
case _adapter_opt_l2i:
|
||||||
|
case _adapter_opt_d2f:
|
||||||
|
case _adapter_opt_i2l:
|
||||||
|
case _adapter_opt_f2d:
|
||||||
|
return _adapter_prim_to_prim;
|
||||||
|
case _adapter_opt_unboxi:
|
||||||
|
case _adapter_opt_unboxl:
|
||||||
|
return _adapter_ref_to_prim;
|
||||||
|
}
|
||||||
|
if (ek >= _adapter_opt_spread_FIRST && ek <= _adapter_opt_spread_LAST)
|
||||||
|
return _adapter_spread_args;
|
||||||
|
if (ek >= _adapter_opt_collect_FIRST && ek <= _adapter_opt_collect_LAST)
|
||||||
|
return _adapter_collect_args;
|
||||||
|
if (ek >= _adapter_opt_fold_FIRST && ek <= _adapter_opt_fold_LAST)
|
||||||
|
return _adapter_fold_args;
|
||||||
|
if (ek >= _adapter_opt_return_FIRST && ek <= _adapter_opt_return_LAST)
|
||||||
|
return _adapter_opt_return_any;
|
||||||
|
assert(false, "oob");
|
||||||
|
return _EK_LIMIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ek_supported(MethodHandles::EntryKind ek);
|
||||||
|
|
||||||
|
static BasicType ek_bound_mh_arg_type(EntryKind ek) {
|
||||||
switch (ek) {
|
switch (ek) {
|
||||||
case _bound_int_mh : // fall-thru
|
case _bound_int_mh : // fall-thru
|
||||||
case _bound_int_direct_mh : arg_type = T_INT; arg_mask = _INSERT_INT_MASK; break;
|
case _bound_int_direct_mh : return T_INT;
|
||||||
case _bound_long_mh : // fall-thru
|
case _bound_long_mh : // fall-thru
|
||||||
case _bound_long_direct_mh: arg_type = T_LONG; arg_mask = _INSERT_LONG_MASK; break;
|
case _bound_long_direct_mh : return T_LONG;
|
||||||
case _bound_ref_mh : // fall-thru
|
default : return T_OBJECT;
|
||||||
case _bound_ref_direct_mh : arg_type = T_OBJECT; arg_mask = _INSERT_REF_MASK; break;
|
|
||||||
default: ShouldNotReachHere();
|
|
||||||
}
|
}
|
||||||
arg_slots = type2size[arg_type];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_ek_adapter_opt_swap_rot_info(EntryKind ek, int& swap_bytes, int& rotate) {
|
static int ek_adapter_opt_swap_slots(EntryKind ek) {
|
||||||
int swap_slots = 0;
|
|
||||||
switch (ek) {
|
switch (ek) {
|
||||||
case _adapter_opt_swap_1: swap_slots = 1; rotate = 0; break;
|
case _adapter_opt_swap_1 : return 1;
|
||||||
case _adapter_opt_swap_2: swap_slots = 2; rotate = 0; break;
|
case _adapter_opt_swap_2 : return 2;
|
||||||
case _adapter_opt_rot_1_up: swap_slots = 1; rotate = 1; break;
|
case _adapter_opt_rot_1_up : return 1;
|
||||||
case _adapter_opt_rot_1_down: swap_slots = 1; rotate = -1; break;
|
case _adapter_opt_rot_1_down : return 1;
|
||||||
case _adapter_opt_rot_2_up: swap_slots = 2; rotate = 1; break;
|
case _adapter_opt_rot_2_up : return 2;
|
||||||
case _adapter_opt_rot_2_down: swap_slots = 2; rotate = -1; break;
|
case _adapter_opt_rot_2_down : return 2;
|
||||||
default: ShouldNotReachHere();
|
default : ShouldNotReachHere(); return -1;
|
||||||
}
|
}
|
||||||
// Return the size of the stack slots to move in bytes.
|
|
||||||
swap_bytes = swap_slots * Interpreter::stackElementSize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_ek_adapter_opt_spread_info(EntryKind ek) {
|
static int ek_adapter_opt_swap_mode(EntryKind ek) {
|
||||||
|
switch (ek) {
|
||||||
|
case _adapter_opt_swap_1 : return 0;
|
||||||
|
case _adapter_opt_swap_2 : return 0;
|
||||||
|
case _adapter_opt_rot_1_up : return 1;
|
||||||
|
case _adapter_opt_rot_1_down : return -1;
|
||||||
|
case _adapter_opt_rot_2_up : return 1;
|
||||||
|
case _adapter_opt_rot_2_down : return -1;
|
||||||
|
default : ShouldNotReachHere(); return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ek_adapter_opt_collect_count(EntryKind ek) {
|
||||||
|
assert(ek >= _adapter_opt_collect_FIRST && ek <= _adapter_opt_collect_LAST ||
|
||||||
|
ek >= _adapter_opt_fold_FIRST && ek <= _adapter_opt_fold_LAST, "");
|
||||||
|
switch (ek) {
|
||||||
|
case _adapter_opt_collect_0_ref : return 0;
|
||||||
|
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_fold_1_ref :
|
||||||
|
case _adapter_opt_collect_1_ref : return 1;
|
||||||
|
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_2_ref :
|
||||||
|
case _adapter_opt_collect_2_ref : return 2;
|
||||||
|
case _adapter_opt_fold_3_ref :
|
||||||
|
case _adapter_opt_collect_3_ref : return 3;
|
||||||
|
case _adapter_opt_fold_4_ref :
|
||||||
|
case _adapter_opt_collect_4_ref : return 4;
|
||||||
|
case _adapter_opt_fold_5_ref :
|
||||||
|
case _adapter_opt_collect_5_ref : return 5;
|
||||||
|
default : return -1; // sentinel value for "variable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ek_adapter_opt_collect_slot(EntryKind ek) {
|
||||||
|
assert(ek >= _adapter_opt_collect_FIRST && ek <= _adapter_opt_collect_LAST ||
|
||||||
|
ek >= _adapter_opt_fold_FIRST && ek <= _adapter_opt_fold_LAST, "");
|
||||||
|
switch (ek) {
|
||||||
|
case _adapter_opt_collect_2_S0_ref :
|
||||||
|
case _adapter_opt_filter_S0_ref : return 0;
|
||||||
|
case _adapter_opt_collect_2_S1_ref :
|
||||||
|
case _adapter_opt_filter_S1_ref : return 1;
|
||||||
|
case _adapter_opt_collect_2_S2_ref :
|
||||||
|
case _adapter_opt_filter_S2_ref : return 2;
|
||||||
|
case _adapter_opt_collect_2_S3_ref :
|
||||||
|
case _adapter_opt_filter_S3_ref : return 3;
|
||||||
|
case _adapter_opt_collect_2_S4_ref :
|
||||||
|
case _adapter_opt_filter_S4_ref : return 4;
|
||||||
|
case _adapter_opt_collect_2_S5_ref :
|
||||||
|
case _adapter_opt_filter_S5_ref : return 5;
|
||||||
|
default : return -1; // sentinel value for "variable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BasicType ek_adapter_opt_collect_type(EntryKind ek) {
|
||||||
|
assert(ek >= _adapter_opt_collect_FIRST && ek <= _adapter_opt_collect_LAST ||
|
||||||
|
ek >= _adapter_opt_fold_FIRST && ek <= _adapter_opt_fold_LAST, "");
|
||||||
|
switch (ek) {
|
||||||
|
case _adapter_opt_fold_int :
|
||||||
|
case _adapter_opt_collect_int : return T_INT;
|
||||||
|
case _adapter_opt_fold_long :
|
||||||
|
case _adapter_opt_collect_long : return T_LONG;
|
||||||
|
case _adapter_opt_fold_float :
|
||||||
|
case _adapter_opt_collect_float : return T_FLOAT;
|
||||||
|
case _adapter_opt_fold_double :
|
||||||
|
case _adapter_opt_collect_double : return T_DOUBLE;
|
||||||
|
case _adapter_opt_fold_void :
|
||||||
|
case _adapter_opt_collect_void : return T_VOID;
|
||||||
|
default : return T_OBJECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ek_adapter_opt_return_slot(EntryKind ek) {
|
||||||
|
assert(ek >= _adapter_opt_return_FIRST && ek <= _adapter_opt_return_LAST, "");
|
||||||
|
switch (ek) {
|
||||||
|
case _adapter_opt_return_S0_ref : return 0;
|
||||||
|
case _adapter_opt_return_S1_ref : return 1;
|
||||||
|
case _adapter_opt_return_S2_ref : return 2;
|
||||||
|
case _adapter_opt_return_S3_ref : return 3;
|
||||||
|
case _adapter_opt_return_S4_ref : return 4;
|
||||||
|
case _adapter_opt_return_S5_ref : return 5;
|
||||||
|
default : return -1; // sentinel value for "variable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BasicType ek_adapter_opt_return_type(EntryKind ek) {
|
||||||
|
assert(ek >= _adapter_opt_return_FIRST && ek <= _adapter_opt_return_LAST, "");
|
||||||
|
switch (ek) {
|
||||||
|
case _adapter_opt_return_int : return T_INT;
|
||||||
|
case _adapter_opt_return_long : return T_LONG;
|
||||||
|
case _adapter_opt_return_float : return T_FLOAT;
|
||||||
|
case _adapter_opt_return_double : return T_DOUBLE;
|
||||||
|
case _adapter_opt_return_void : return T_VOID;
|
||||||
|
case _adapter_opt_return_any : return T_CONFLICT; // sentinel value for "variable"
|
||||||
|
default : return T_OBJECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ek_adapter_opt_spread_count(EntryKind ek) {
|
||||||
|
assert(ek >= _adapter_opt_spread_FIRST && ek <= _adapter_opt_spread_LAST, "");
|
||||||
switch (ek) {
|
switch (ek) {
|
||||||
case _adapter_opt_spread_0 : return 0;
|
case _adapter_opt_spread_0 : return 0;
|
||||||
case _adapter_opt_spread_1: return 1;
|
case _adapter_opt_spread_1_ref : return 1;
|
||||||
default : return -1;
|
case _adapter_opt_spread_2_ref : return 2;
|
||||||
|
case _adapter_opt_spread_3_ref : return 3;
|
||||||
|
case _adapter_opt_spread_4_ref : return 4;
|
||||||
|
case _adapter_opt_spread_5_ref : return 5;
|
||||||
|
default : return -1; // sentinel value for "variable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static BasicType ek_adapter_opt_spread_type(EntryKind ek) {
|
||||||
|
assert(ek >= _adapter_opt_spread_FIRST && ek <= _adapter_opt_spread_LAST, "");
|
||||||
|
switch (ek) {
|
||||||
|
// (there is no _adapter_opt_spread_boolean; we use byte)
|
||||||
|
case _adapter_opt_spread_byte : return T_BYTE;
|
||||||
|
case _adapter_opt_spread_char : return T_CHAR;
|
||||||
|
case _adapter_opt_spread_short : return T_SHORT;
|
||||||
|
case _adapter_opt_spread_int : return T_INT;
|
||||||
|
case _adapter_opt_spread_long : return T_LONG;
|
||||||
|
case _adapter_opt_spread_float : return T_FLOAT;
|
||||||
|
case _adapter_opt_spread_double : return T_DOUBLE;
|
||||||
|
default : return T_OBJECT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,12 +475,21 @@ class MethodHandles: AllStatic {
|
||||||
// Bit mask of conversion_op values. May vary by platform.
|
// Bit mask of conversion_op values. May vary by platform.
|
||||||
static int adapter_conversion_ops_supported_mask();
|
static int adapter_conversion_ops_supported_mask();
|
||||||
|
|
||||||
|
static bool conv_op_supported(int conv_op) {
|
||||||
|
assert(conv_op_valid(conv_op), "");
|
||||||
|
return ((adapter_conversion_ops_supported_mask() & nth_bit(conv_op)) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Offset in words that the interpreter stack pointer moves when an argument is pushed.
|
// Offset in words that the interpreter stack pointer moves when an argument is pushed.
|
||||||
// The stack_move value must always be a multiple of this.
|
// The stack_move value must always be a multiple of this.
|
||||||
static int stack_move_unit() {
|
static int stack_move_unit() {
|
||||||
return frame::interpreter_frame_expression_stack_direction() * Interpreter::stackElementWords;
|
return frame::interpreter_frame_expression_stack_direction() * Interpreter::stackElementWords;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adapter frame traversal. (Implementation-specific.)
|
||||||
|
static frame ricochet_frame_sender(const frame& fr, RegisterMap* reg_map);
|
||||||
|
static void ricochet_frame_oops_do(const frame& fr, OopClosure* blk, const RegisterMap* reg_map);
|
||||||
|
|
||||||
enum { CONV_VMINFO_SIGN_FLAG = 0x80 };
|
enum { CONV_VMINFO_SIGN_FLAG = 0x80 };
|
||||||
// Shift values for prim-to-prim conversions.
|
// Shift values for prim-to-prim conversions.
|
||||||
static int adapter_prim_to_prim_subword_vminfo(BasicType dest) {
|
static int adapter_prim_to_prim_subword_vminfo(BasicType dest) {
|
||||||
|
@ -429,6 +685,7 @@ class MethodHandles: AllStatic {
|
||||||
|
|
||||||
// Fill in the fields of an AdapterMethodHandle mh. (MH.type must be pre-filled.)
|
// Fill in the fields of an AdapterMethodHandle mh. (MH.type must be pre-filled.)
|
||||||
static void init_AdapterMethodHandle(Handle mh, Handle target, int argnum, TRAPS);
|
static void init_AdapterMethodHandle(Handle mh, Handle target, int argnum, TRAPS);
|
||||||
|
static void ensure_vmlayout_field(Handle target, TRAPS);
|
||||||
|
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
static bool spot_check_entry_names();
|
static bool spot_check_entry_names();
|
||||||
|
@ -441,6 +698,8 @@ class MethodHandles: AllStatic {
|
||||||
KlassHandle receiver_klass,
|
KlassHandle receiver_klass,
|
||||||
TRAPS);
|
TRAPS);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static bool is_float_fixed_reinterpretation_cast(BasicType src, BasicType dst);
|
||||||
static bool same_basic_type_for_arguments(BasicType src, BasicType dst,
|
static bool same_basic_type_for_arguments(BasicType src, BasicType dst,
|
||||||
bool raw = false,
|
bool raw = false,
|
||||||
bool for_return = false);
|
bool for_return = false);
|
||||||
|
@ -448,12 +707,54 @@ class MethodHandles: AllStatic {
|
||||||
return same_basic_type_for_arguments(src, dst, raw, true);
|
return same_basic_type_for_arguments(src, dst, raw, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum { // arg_mask values
|
static Symbol* convert_to_signature(oop type_str, bool polymorphic, TRAPS);
|
||||||
|
|
||||||
|
#ifdef TARGET_ARCH_x86
|
||||||
|
# include "methodHandles_x86.hpp"
|
||||||
|
#endif
|
||||||
|
#ifdef TARGET_ARCH_sparc
|
||||||
|
#define TARGET_ARCH_NYI_6939861 1 //FIXME
|
||||||
|
//# include "methodHandles_sparc.hpp"
|
||||||
|
#endif
|
||||||
|
#ifdef TARGET_ARCH_zero
|
||||||
|
#define TARGET_ARCH_NYI_6939861 1 //FIXME
|
||||||
|
//# include "methodHandles_zero.hpp"
|
||||||
|
#endif
|
||||||
|
#ifdef TARGET_ARCH_arm
|
||||||
|
#define TARGET_ARCH_NYI_6939861 1 //FIXME
|
||||||
|
//# include "methodHandles_arm.hpp"
|
||||||
|
#endif
|
||||||
|
#ifdef TARGET_ARCH_ppc
|
||||||
|
#define TARGET_ARCH_NYI_6939861 1 //FIXME
|
||||||
|
//# include "methodHandles_ppc.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef TARGET_ARCH_NYI_6939861
|
||||||
|
// Here are some backward compatible declarations until the 6939861 ports are updated.
|
||||||
|
#define _adapter_flyby (_EK_LIMIT + 10)
|
||||||
|
#define _adapter_ricochet (_EK_LIMIT + 11)
|
||||||
|
#define _adapter_opt_spread_1 _adapter_opt_spread_1_ref
|
||||||
|
#define _adapter_opt_spread_more _adapter_opt_spread_ref
|
||||||
|
enum {
|
||||||
_INSERT_NO_MASK = -1,
|
_INSERT_NO_MASK = -1,
|
||||||
_INSERT_REF_MASK = 0,
|
_INSERT_REF_MASK = 0,
|
||||||
_INSERT_INT_MASK = 1,
|
_INSERT_INT_MASK = 1,
|
||||||
_INSERT_LONG_MASK = 3
|
_INSERT_LONG_MASK = 3
|
||||||
};
|
};
|
||||||
|
static void get_ek_bound_mh_info(EntryKind ek, BasicType& arg_type, int& arg_mask, int& arg_slots) {
|
||||||
|
arg_type = ek_bound_mh_arg_type(ek);
|
||||||
|
arg_mask = 0;
|
||||||
|
arg_slots = type2size[arg_type];;
|
||||||
|
}
|
||||||
|
static void get_ek_adapter_opt_swap_rot_info(EntryKind ek, int& swap_bytes, int& rotate) {
|
||||||
|
int swap_slots = ek_adapter_opt_swap_slots(ek);
|
||||||
|
rotate = ek_adapter_opt_swap_mode(ek);
|
||||||
|
swap_bytes = swap_slots * Interpreter::stackElementSize;
|
||||||
|
}
|
||||||
|
static int get_ek_adapter_opt_spread_info(EntryKind ek) {
|
||||||
|
return ek_adapter_opt_spread_count(ek);
|
||||||
|
}
|
||||||
|
|
||||||
static void insert_arg_slots(MacroAssembler* _masm,
|
static void insert_arg_slots(MacroAssembler* _masm,
|
||||||
RegisterOrConstant arg_slots,
|
RegisterOrConstant arg_slots,
|
||||||
int arg_mask,
|
int arg_mask,
|
||||||
|
@ -466,8 +767,7 @@ class MethodHandles: AllStatic {
|
||||||
Register temp_reg, Register temp2_reg, Register temp3_reg = noreg);
|
Register temp_reg, Register temp2_reg, Register temp3_reg = noreg);
|
||||||
|
|
||||||
static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN;
|
static void trace_method_handle(MacroAssembler* _masm, const char* adaptername) PRODUCT_RETURN;
|
||||||
|
#endif //TARGET_ARCH_NYI_6939861
|
||||||
static Symbol* convert_to_signature(oop type_str, bool polymorphic, TRAPS);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,25 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2010, 2011 Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
|
|
|
@ -1,6 +1,25 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2010, 2011 Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
* 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.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SHARE_VM_RUNTIME_ADVANCEDTHRESHOLDPOLICY_HPP
|
#ifndef SHARE_VM_RUNTIME_ADVANCEDTHRESHOLDPOLICY_HPP
|
||||||
|
|
|
@ -90,12 +90,14 @@ bool DeoptimizationMarker::_is_active = false;
|
||||||
|
|
||||||
Deoptimization::UnrollBlock::UnrollBlock(int size_of_deoptimized_frame,
|
Deoptimization::UnrollBlock::UnrollBlock(int size_of_deoptimized_frame,
|
||||||
int caller_adjustment,
|
int caller_adjustment,
|
||||||
|
int caller_actual_parameters,
|
||||||
int number_of_frames,
|
int number_of_frames,
|
||||||
intptr_t* frame_sizes,
|
intptr_t* frame_sizes,
|
||||||
address* frame_pcs,
|
address* frame_pcs,
|
||||||
BasicType return_type) {
|
BasicType return_type) {
|
||||||
_size_of_deoptimized_frame = size_of_deoptimized_frame;
|
_size_of_deoptimized_frame = size_of_deoptimized_frame;
|
||||||
_caller_adjustment = caller_adjustment;
|
_caller_adjustment = caller_adjustment;
|
||||||
|
_caller_actual_parameters = caller_actual_parameters;
|
||||||
_number_of_frames = number_of_frames;
|
_number_of_frames = number_of_frames;
|
||||||
_frame_sizes = frame_sizes;
|
_frame_sizes = frame_sizes;
|
||||||
_frame_pcs = frame_pcs;
|
_frame_pcs = frame_pcs;
|
||||||
|
@ -373,6 +375,28 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
||||||
popframe_extra_args = in_words(thread->popframe_preserved_args_size_in_words());
|
popframe_extra_args = in_words(thread->popframe_preserved_args_size_in_words());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find the current pc for sender of the deoptee. Since the sender may have been deoptimized
|
||||||
|
// itself since the deoptee vframeArray was created we must get a fresh value of the pc rather
|
||||||
|
// than simply use array->sender.pc(). This requires us to walk the current set of frames
|
||||||
|
//
|
||||||
|
frame deopt_sender = stub_frame.sender(&dummy_map); // First is the deoptee frame
|
||||||
|
deopt_sender = deopt_sender.sender(&dummy_map); // Now deoptee caller
|
||||||
|
|
||||||
|
// It's possible that the number of paramters at the call site is
|
||||||
|
// different than number of arguments in the callee when method
|
||||||
|
// handles are used. If the caller is interpreted get the real
|
||||||
|
// value so that the proper amount of space can be added to it's
|
||||||
|
// frame.
|
||||||
|
int caller_actual_parameters = callee_parameters;
|
||||||
|
if (deopt_sender.is_interpreted_frame()) {
|
||||||
|
methodHandle method = deopt_sender.interpreter_frame_method();
|
||||||
|
Bytecode_invoke cur = Bytecode_invoke_check(method,
|
||||||
|
deopt_sender.interpreter_frame_bci());
|
||||||
|
Symbol* signature = method->constants()->signature_ref_at(cur.index());
|
||||||
|
ArgumentSizeComputer asc(signature);
|
||||||
|
caller_actual_parameters = asc.size() + (cur.has_receiver() ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// frame_sizes/frame_pcs[0] oldest frame (int or c2i)
|
// frame_sizes/frame_pcs[0] oldest frame (int or c2i)
|
||||||
// frame_sizes/frame_pcs[1] next oldest frame (int)
|
// frame_sizes/frame_pcs[1] next oldest frame (int)
|
||||||
|
@ -391,7 +415,13 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
||||||
// frame[number_of_frames - 1 ] = on_stack_size(youngest)
|
// frame[number_of_frames - 1 ] = on_stack_size(youngest)
|
||||||
// frame[number_of_frames - 2 ] = on_stack_size(sender(youngest))
|
// frame[number_of_frames - 2 ] = on_stack_size(sender(youngest))
|
||||||
// frame[number_of_frames - 3 ] = on_stack_size(sender(sender(youngest)))
|
// frame[number_of_frames - 3 ] = on_stack_size(sender(sender(youngest)))
|
||||||
frame_sizes[number_of_frames - 1 - index] = BytesPerWord * array->element(index)->on_stack_size(callee_parameters,
|
int caller_parms = callee_parameters;
|
||||||
|
if (index == array->frames() - 1) {
|
||||||
|
// Use the value from the interpreted caller
|
||||||
|
caller_parms = caller_actual_parameters;
|
||||||
|
}
|
||||||
|
frame_sizes[number_of_frames - 1 - index] = BytesPerWord * array->element(index)->on_stack_size(caller_parms,
|
||||||
|
callee_parameters,
|
||||||
callee_locals,
|
callee_locals,
|
||||||
index == 0,
|
index == 0,
|
||||||
popframe_extra_args);
|
popframe_extra_args);
|
||||||
|
@ -418,28 +448,6 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
||||||
// Compute information for handling adapters and adjusting the frame size of the caller.
|
// Compute information for handling adapters and adjusting the frame size of the caller.
|
||||||
int caller_adjustment = 0;
|
int caller_adjustment = 0;
|
||||||
|
|
||||||
// Find the current pc for sender of the deoptee. Since the sender may have been deoptimized
|
|
||||||
// itself since the deoptee vframeArray was created we must get a fresh value of the pc rather
|
|
||||||
// than simply use array->sender.pc(). This requires us to walk the current set of frames
|
|
||||||
//
|
|
||||||
frame deopt_sender = stub_frame.sender(&dummy_map); // First is the deoptee frame
|
|
||||||
deopt_sender = deopt_sender.sender(&dummy_map); // Now deoptee caller
|
|
||||||
|
|
||||||
// It's possible that the number of paramters at the call site is
|
|
||||||
// different than number of arguments in the callee when method
|
|
||||||
// handles are used. If the caller is interpreted get the real
|
|
||||||
// value so that the proper amount of space can be added to it's
|
|
||||||
// frame.
|
|
||||||
int sender_callee_parameters = callee_parameters;
|
|
||||||
if (deopt_sender.is_interpreted_frame()) {
|
|
||||||
methodHandle method = deopt_sender.interpreter_frame_method();
|
|
||||||
Bytecode_invoke cur = Bytecode_invoke_check(method,
|
|
||||||
deopt_sender.interpreter_frame_bci());
|
|
||||||
Symbol* signature = method->constants()->signature_ref_at(cur.index());
|
|
||||||
ArgumentSizeComputer asc(signature);
|
|
||||||
sender_callee_parameters = asc.size() + (cur.has_receiver() ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the amount the oldest interpreter frame will have to adjust
|
// Compute the amount the oldest interpreter frame will have to adjust
|
||||||
// its caller's stack by. If the caller is a compiled frame then
|
// its caller's stack by. If the caller is a compiled frame then
|
||||||
// we pretend that the callee has no parameters so that the
|
// we pretend that the callee has no parameters so that the
|
||||||
|
@ -454,11 +462,11 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
||||||
|
|
||||||
if (deopt_sender.is_compiled_frame()) {
|
if (deopt_sender.is_compiled_frame()) {
|
||||||
caller_adjustment = last_frame_adjust(0, callee_locals);
|
caller_adjustment = last_frame_adjust(0, callee_locals);
|
||||||
} else if (callee_locals > sender_callee_parameters) {
|
} else if (callee_locals > caller_actual_parameters) {
|
||||||
// The caller frame may need extending to accommodate
|
// The caller frame may need extending to accommodate
|
||||||
// non-parameter locals of the first unpacked interpreted frame.
|
// non-parameter locals of the first unpacked interpreted frame.
|
||||||
// Compute that adjustment.
|
// Compute that adjustment.
|
||||||
caller_adjustment = last_frame_adjust(sender_callee_parameters, callee_locals);
|
caller_adjustment = last_frame_adjust(caller_actual_parameters, callee_locals);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the sender is deoptimized the we must retrieve the address of the handler
|
// If the sender is deoptimized the we must retrieve the address of the handler
|
||||||
|
@ -473,6 +481,7 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
||||||
|
|
||||||
UnrollBlock* info = new UnrollBlock(array->frame_size() * BytesPerWord,
|
UnrollBlock* info = new UnrollBlock(array->frame_size() * BytesPerWord,
|
||||||
caller_adjustment * BytesPerWord,
|
caller_adjustment * BytesPerWord,
|
||||||
|
caller_actual_parameters,
|
||||||
number_of_frames,
|
number_of_frames,
|
||||||
frame_sizes,
|
frame_sizes,
|
||||||
frame_pcs,
|
frame_pcs,
|
||||||
|
@ -570,7 +579,7 @@ JRT_LEAF(BasicType, Deoptimization::unpack_frames(JavaThread* thread, int exec_m
|
||||||
UnrollBlock* info = array->unroll_block();
|
UnrollBlock* info = array->unroll_block();
|
||||||
|
|
||||||
// Unpack the interpreter frames and any adapter frame (c2 only) we might create.
|
// Unpack the interpreter frames and any adapter frame (c2 only) we might create.
|
||||||
array->unpack_to_stack(stub_frame, exec_mode);
|
array->unpack_to_stack(stub_frame, exec_mode, info->caller_actual_parameters());
|
||||||
|
|
||||||
BasicType bt = info->return_type();
|
BasicType bt = info->return_type();
|
||||||
|
|
||||||
|
|
|
@ -138,6 +138,9 @@ class Deoptimization : AllStatic {
|
||||||
intptr_t* _register_block; // Block for storing callee-saved registers.
|
intptr_t* _register_block; // Block for storing callee-saved registers.
|
||||||
BasicType _return_type; // Tells if we have to restore double or long return value
|
BasicType _return_type; // Tells if we have to restore double or long return value
|
||||||
intptr_t _initial_fp; // FP of the sender frame
|
intptr_t _initial_fp; // FP of the sender frame
|
||||||
|
int _caller_actual_parameters; // The number of actual arguments at the
|
||||||
|
// interpreted caller of the deoptimized frame
|
||||||
|
|
||||||
// The following fields are used as temps during the unpacking phase
|
// The following fields are used as temps during the unpacking phase
|
||||||
// (which is tight on registers, especially on x86). They really ought
|
// (which is tight on registers, especially on x86). They really ought
|
||||||
// to be PD variables but that involves moving this class into its own
|
// to be PD variables but that involves moving this class into its own
|
||||||
|
@ -149,6 +152,7 @@ class Deoptimization : AllStatic {
|
||||||
// Constructor
|
// Constructor
|
||||||
UnrollBlock(int size_of_deoptimized_frame,
|
UnrollBlock(int size_of_deoptimized_frame,
|
||||||
int caller_adjustment,
|
int caller_adjustment,
|
||||||
|
int caller_actual_parameters,
|
||||||
int number_of_frames,
|
int number_of_frames,
|
||||||
intptr_t* frame_sizes,
|
intptr_t* frame_sizes,
|
||||||
address* frames_pcs,
|
address* frames_pcs,
|
||||||
|
@ -168,6 +172,8 @@ class Deoptimization : AllStatic {
|
||||||
|
|
||||||
void set_initial_fp(intptr_t fp) { _initial_fp = fp; }
|
void set_initial_fp(intptr_t fp) { _initial_fp = fp; }
|
||||||
|
|
||||||
|
int caller_actual_parameters() const { return _caller_actual_parameters; }
|
||||||
|
|
||||||
// Accessors used by the code generator for the unpack stub.
|
// Accessors used by the code generator for the unpack stub.
|
||||||
static int size_of_deoptimized_frame_offset_in_bytes() { return offset_of(UnrollBlock, _size_of_deoptimized_frame); }
|
static int size_of_deoptimized_frame_offset_in_bytes() { return offset_of(UnrollBlock, _size_of_deoptimized_frame); }
|
||||||
static int caller_adjustment_offset_in_bytes() { return offset_of(UnrollBlock, _caller_adjustment); }
|
static int caller_adjustment_offset_in_bytes() { return offset_of(UnrollBlock, _caller_adjustment); }
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "oops/methodOop.hpp"
|
#include "oops/methodOop.hpp"
|
||||||
#include "oops/oop.inline.hpp"
|
#include "oops/oop.inline.hpp"
|
||||||
#include "oops/oop.inline2.hpp"
|
#include "oops/oop.inline2.hpp"
|
||||||
|
#include "prims/methodHandles.hpp"
|
||||||
#include "runtime/frame.inline.hpp"
|
#include "runtime/frame.inline.hpp"
|
||||||
#include "runtime/handles.inline.hpp"
|
#include "runtime/handles.inline.hpp"
|
||||||
#include "runtime/javaCalls.hpp"
|
#include "runtime/javaCalls.hpp"
|
||||||
|
@ -169,6 +170,11 @@ void frame::set_pc(address newpc ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// type testers
|
// type testers
|
||||||
|
bool frame::is_ricochet_frame() const {
|
||||||
|
RicochetBlob* rcb = SharedRuntime::ricochet_blob();
|
||||||
|
return (_cb == rcb && rcb != NULL && rcb->returns_to_bounce_addr(_pc));
|
||||||
|
}
|
||||||
|
|
||||||
bool frame::is_deoptimized_frame() const {
|
bool frame::is_deoptimized_frame() const {
|
||||||
assert(_deopt_state != unknown, "not answerable");
|
assert(_deopt_state != unknown, "not answerable");
|
||||||
return _deopt_state == is_deoptimized;
|
return _deopt_state == is_deoptimized;
|
||||||
|
@ -341,12 +347,18 @@ frame frame::java_sender() const {
|
||||||
|
|
||||||
frame frame::real_sender(RegisterMap* map) const {
|
frame frame::real_sender(RegisterMap* map) const {
|
||||||
frame result = sender(map);
|
frame result = sender(map);
|
||||||
while (result.is_runtime_frame()) {
|
while (result.is_runtime_frame() ||
|
||||||
|
result.is_ricochet_frame()) {
|
||||||
result = result.sender(map);
|
result = result.sender(map);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frame frame::sender_for_ricochet_frame(RegisterMap* map) const {
|
||||||
|
assert(is_ricochet_frame(), "");
|
||||||
|
return MethodHandles::ricochet_frame_sender(*this, map);
|
||||||
|
}
|
||||||
|
|
||||||
// Note: called by profiler - NOT for current thread
|
// Note: called by profiler - NOT for current thread
|
||||||
frame frame::profile_find_Java_sender_frame(JavaThread *thread) {
|
frame frame::profile_find_Java_sender_frame(JavaThread *thread) {
|
||||||
// If we don't recognize this frame, walk back up the stack until we do
|
// If we don't recognize this frame, walk back up the stack until we do
|
||||||
|
@ -529,6 +541,7 @@ jint frame::interpreter_frame_expression_stack_size() const {
|
||||||
const char* frame::print_name() const {
|
const char* frame::print_name() const {
|
||||||
if (is_native_frame()) return "Native";
|
if (is_native_frame()) return "Native";
|
||||||
if (is_interpreted_frame()) return "Interpreted";
|
if (is_interpreted_frame()) return "Interpreted";
|
||||||
|
if (is_ricochet_frame()) return "Ricochet";
|
||||||
if (is_compiled_frame()) {
|
if (is_compiled_frame()) {
|
||||||
if (is_deoptimized_frame()) return "Deoptimized";
|
if (is_deoptimized_frame()) return "Deoptimized";
|
||||||
return "Compiled";
|
return "Compiled";
|
||||||
|
@ -715,6 +728,8 @@ void frame::print_on_error(outputStream* st, char* buf, int buflen, bool verbose
|
||||||
st->print("v ~RuntimeStub::%s", ((RuntimeStub *)_cb)->name());
|
st->print("v ~RuntimeStub::%s", ((RuntimeStub *)_cb)->name());
|
||||||
} else if (_cb->is_deoptimization_stub()) {
|
} else if (_cb->is_deoptimization_stub()) {
|
||||||
st->print("v ~DeoptimizationBlob");
|
st->print("v ~DeoptimizationBlob");
|
||||||
|
} else if (_cb->is_ricochet_stub()) {
|
||||||
|
st->print("v ~RichochetBlob");
|
||||||
} else if (_cb->is_exception_stub()) {
|
} else if (_cb->is_exception_stub()) {
|
||||||
st->print("v ~ExceptionBlob");
|
st->print("v ~ExceptionBlob");
|
||||||
} else if (_cb->is_safepoint_stub()) {
|
} else if (_cb->is_safepoint_stub()) {
|
||||||
|
@ -978,6 +993,9 @@ void frame::oops_interpreted_arguments_do(Symbol* signature, bool has_receiver,
|
||||||
|
|
||||||
void frame::oops_code_blob_do(OopClosure* f, CodeBlobClosure* cf, const RegisterMap* reg_map) {
|
void frame::oops_code_blob_do(OopClosure* f, CodeBlobClosure* cf, const RegisterMap* reg_map) {
|
||||||
assert(_cb != NULL, "sanity check");
|
assert(_cb != NULL, "sanity check");
|
||||||
|
if (_cb == SharedRuntime::ricochet_blob()) {
|
||||||
|
oops_ricochet_do(f, reg_map);
|
||||||
|
}
|
||||||
if (_cb->oop_maps() != NULL) {
|
if (_cb->oop_maps() != NULL) {
|
||||||
OopMapSet::oops_do(this, reg_map, f);
|
OopMapSet::oops_do(this, reg_map, f);
|
||||||
|
|
||||||
|
@ -996,6 +1014,11 @@ void frame::oops_code_blob_do(OopClosure* f, CodeBlobClosure* cf, const Register
|
||||||
cf->do_code_blob(_cb);
|
cf->do_code_blob(_cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void frame::oops_ricochet_do(OopClosure* f, const RegisterMap* map) {
|
||||||
|
assert(is_ricochet_frame(), "");
|
||||||
|
MethodHandles::ricochet_frame_oops_do(*this, f, map);
|
||||||
|
}
|
||||||
|
|
||||||
class CompiledArgumentOopFinder: public SignatureInfo {
|
class CompiledArgumentOopFinder: public SignatureInfo {
|
||||||
protected:
|
protected:
|
||||||
OopClosure* _f;
|
OopClosure* _f;
|
||||||
|
@ -1400,7 +1423,7 @@ void FrameValues::describe(int owner, intptr_t* location, const char* descriptio
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool FrameValues::validate() {
|
void FrameValues::validate() {
|
||||||
_values.sort(compare);
|
_values.sort(compare);
|
||||||
bool error = false;
|
bool error = false;
|
||||||
FrameValue prev;
|
FrameValue prev;
|
||||||
|
@ -1423,19 +1446,32 @@ bool FrameValues::validate() {
|
||||||
prev = fv;
|
prev = fv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return error;
|
assert(!error, "invalid layout");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void FrameValues::print() {
|
void FrameValues::print() {
|
||||||
_values.sort(compare);
|
_values.sort(compare);
|
||||||
intptr_t* v0 = _values.at(0).location;
|
JavaThread* thread = JavaThread::current();
|
||||||
intptr_t* v1 = _values.at(_values.length() - 1).location;
|
|
||||||
|
// Sometimes values like the fp can be invalid values if the
|
||||||
|
// register map wasn't updated during the walk. Trim out values
|
||||||
|
// that aren't actually in the stack of the thread.
|
||||||
|
int min_index = 0;
|
||||||
|
int max_index = _values.length() - 1;
|
||||||
|
intptr_t* v0 = _values.at(min_index).location;
|
||||||
|
while (!thread->is_in_stack((address)v0)) {
|
||||||
|
v0 = _values.at(++min_index).location;
|
||||||
|
}
|
||||||
|
intptr_t* v1 = _values.at(max_index).location;
|
||||||
|
while (!thread->is_in_stack((address)v1)) {
|
||||||
|
v1 = _values.at(--max_index).location;
|
||||||
|
}
|
||||||
intptr_t* min = MIN2(v0, v1);
|
intptr_t* min = MIN2(v0, v1);
|
||||||
intptr_t* max = MAX2(v0, v1);
|
intptr_t* max = MAX2(v0, v1);
|
||||||
intptr_t* cur = max;
|
intptr_t* cur = max;
|
||||||
intptr_t* last = NULL;
|
intptr_t* last = NULL;
|
||||||
for (int i = _values.length() - 1; i >= 0; i--) {
|
for (int i = max_index; i >= min_index; i--) {
|
||||||
FrameValue fv = _values.at(i);
|
FrameValue fv = _values.at(i);
|
||||||
while (cur > fv.location) {
|
while (cur > fv.location) {
|
||||||
tty->print_cr(" " INTPTR_FORMAT ": " INTPTR_FORMAT, cur, *cur);
|
tty->print_cr(" " INTPTR_FORMAT ": " INTPTR_FORMAT, cur, *cur);
|
||||||
|
|
|
@ -135,6 +135,7 @@ class frame VALUE_OBJ_CLASS_SPEC {
|
||||||
bool is_interpreted_frame() const;
|
bool is_interpreted_frame() const;
|
||||||
bool is_java_frame() const;
|
bool is_java_frame() const;
|
||||||
bool is_entry_frame() const; // Java frame called from C?
|
bool is_entry_frame() const; // Java frame called from C?
|
||||||
|
bool is_ricochet_frame() const;
|
||||||
bool is_native_frame() const;
|
bool is_native_frame() const;
|
||||||
bool is_runtime_frame() const;
|
bool is_runtime_frame() const;
|
||||||
bool is_compiled_frame() const;
|
bool is_compiled_frame() const;
|
||||||
|
@ -175,6 +176,7 @@ class frame VALUE_OBJ_CLASS_SPEC {
|
||||||
// Helper methods for better factored code in frame::sender
|
// Helper methods for better factored code in frame::sender
|
||||||
frame sender_for_compiled_frame(RegisterMap* map) const;
|
frame sender_for_compiled_frame(RegisterMap* map) const;
|
||||||
frame sender_for_entry_frame(RegisterMap* map) const;
|
frame sender_for_entry_frame(RegisterMap* map) const;
|
||||||
|
frame sender_for_ricochet_frame(RegisterMap* map) const;
|
||||||
frame sender_for_interpreter_frame(RegisterMap* map) const;
|
frame sender_for_interpreter_frame(RegisterMap* map) const;
|
||||||
frame sender_for_native_frame(RegisterMap* map) const;
|
frame sender_for_native_frame(RegisterMap* map) const;
|
||||||
|
|
||||||
|
@ -400,6 +402,7 @@ class frame VALUE_OBJ_CLASS_SPEC {
|
||||||
// Oops-do's
|
// Oops-do's
|
||||||
void oops_compiled_arguments_do(Symbol* signature, bool has_receiver, const RegisterMap* reg_map, OopClosure* f);
|
void oops_compiled_arguments_do(Symbol* signature, bool has_receiver, const RegisterMap* reg_map, OopClosure* f);
|
||||||
void oops_interpreted_do(OopClosure* f, const RegisterMap* map, bool query_oop_map_cache = true);
|
void oops_interpreted_do(OopClosure* f, const RegisterMap* map, bool query_oop_map_cache = true);
|
||||||
|
void oops_ricochet_do(OopClosure* f, const RegisterMap* map);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void oops_interpreted_arguments_do(Symbol* signature, bool has_receiver, OopClosure* f);
|
void oops_interpreted_arguments_do(Symbol* signature, bool has_receiver, OopClosure* f);
|
||||||
|
@ -508,7 +511,7 @@ class FrameValues {
|
||||||
// Used by frame functions to describe locations.
|
// Used by frame functions to describe locations.
|
||||||
void describe(int owner, intptr_t* location, const char* description, int priority = 0);
|
void describe(int owner, intptr_t* location, const char* description, int priority = 0);
|
||||||
|
|
||||||
bool validate();
|
void validate();
|
||||||
void print();
|
void print();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1460,8 +1460,10 @@ class CommandLineFlags {
|
||||||
product(intx, ParallelGCBufferWastePct, 10, \
|
product(intx, ParallelGCBufferWastePct, 10, \
|
||||||
"wasted fraction of parallel allocation buffer.") \
|
"wasted fraction of parallel allocation buffer.") \
|
||||||
\
|
\
|
||||||
product(bool, ParallelGCRetainPLAB, true, \
|
diagnostic(bool, ParallelGCRetainPLAB, false, \
|
||||||
"Retain parallel allocation buffers across scavenges.") \
|
"Retain parallel allocation buffers across scavenges; " \
|
||||||
|
" -- disabled because this currently conflicts with " \
|
||||||
|
" parallel card scanning under certain conditions ") \
|
||||||
\
|
\
|
||||||
product(intx, TargetPLABWastePct, 10, \
|
product(intx, TargetPLABWastePct, 10, \
|
||||||
"target wasted space in last buffer as pct of overall allocation")\
|
"target wasted space in last buffer as pct of overall allocation")\
|
||||||
|
@ -1495,6 +1497,14 @@ class CommandLineFlags {
|
||||||
product(uintx, ParGCDesiredObjsFromOverflowList, 20, \
|
product(uintx, ParGCDesiredObjsFromOverflowList, 20, \
|
||||||
"The desired number of objects to claim from the overflow list") \
|
"The desired number of objects to claim from the overflow list") \
|
||||||
\
|
\
|
||||||
|
diagnostic(intx, ParGCStridesPerThread, 2, \
|
||||||
|
"The number of strides per worker thread that we divide up the " \
|
||||||
|
"card table scanning work into") \
|
||||||
|
\
|
||||||
|
diagnostic(intx, ParGCCardsPerStrideChunk, 256, \
|
||||||
|
"The number of cards in each chunk of the parallel chunks used " \
|
||||||
|
"during card table scanning") \
|
||||||
|
\
|
||||||
product(uintx, CMSParPromoteBlocksToClaim, 16, \
|
product(uintx, CMSParPromoteBlocksToClaim, 16, \
|
||||||
"Number of blocks to attempt to claim when refilling CMS LAB for "\
|
"Number of blocks to attempt to claim when refilling CMS LAB for "\
|
||||||
"parallel GC.") \
|
"parallel GC.") \
|
||||||
|
@ -3708,6 +3718,10 @@ class CommandLineFlags {
|
||||||
diagnostic(bool, OptimizeMethodHandles, true, \
|
diagnostic(bool, OptimizeMethodHandles, true, \
|
||||||
"when constructing method handles, try to improve them") \
|
"when constructing method handles, try to improve them") \
|
||||||
\
|
\
|
||||||
|
diagnostic(bool, UseRicochetFrames, true, \
|
||||||
|
"use ricochet stack frames for method handle combination, " \
|
||||||
|
"if the platform supports them") \
|
||||||
|
\
|
||||||
experimental(bool, TrustFinalNonStaticFields, false, \
|
experimental(bool, TrustFinalNonStaticFields, false, \
|
||||||
"trust final non-static declarations for constant folding") \
|
"trust final non-static declarations for constant folding") \
|
||||||
\
|
\
|
||||||
|
|
|
@ -88,6 +88,8 @@ HS_DTRACE_PROBE_DECL7(hotspot, method__entry, int,
|
||||||
HS_DTRACE_PROBE_DECL7(hotspot, method__return, int,
|
HS_DTRACE_PROBE_DECL7(hotspot, method__return, int,
|
||||||
char*, int, char*, int, char*, int);
|
char*, int, char*, int, char*, int);
|
||||||
|
|
||||||
|
RicochetBlob* SharedRuntime::_ricochet_blob = NULL;
|
||||||
|
|
||||||
// Implementation of SharedRuntime
|
// Implementation of SharedRuntime
|
||||||
|
|
||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
|
@ -460,6 +462,10 @@ address SharedRuntime::raw_exception_handler_for_return_address(JavaThread* thre
|
||||||
if (Interpreter::contains(return_address)) {
|
if (Interpreter::contains(return_address)) {
|
||||||
return Interpreter::rethrow_exception_entry();
|
return Interpreter::rethrow_exception_entry();
|
||||||
}
|
}
|
||||||
|
// Ricochet frame unwind code
|
||||||
|
if (SharedRuntime::ricochet_blob() != NULL && SharedRuntime::ricochet_blob()->returns_to_bounce_addr(return_address)) {
|
||||||
|
return SharedRuntime::ricochet_blob()->exception_addr();
|
||||||
|
}
|
||||||
|
|
||||||
guarantee(blob == NULL || !blob->is_runtime_stub(), "caller should have skipped stub");
|
guarantee(blob == NULL || !blob->is_runtime_stub(), "caller should have skipped stub");
|
||||||
guarantee(!VtableStubs::contains(return_address), "NULL exceptions in vtables should have been handled already!");
|
guarantee(!VtableStubs::contains(return_address), "NULL exceptions in vtables should have been handled already!");
|
||||||
|
@ -1174,6 +1180,7 @@ JRT_BLOCK_ENTRY(address, SharedRuntime::handle_wrong_method_ic_miss(JavaThread*
|
||||||
assert(stub_frame.is_runtime_frame(), "sanity check");
|
assert(stub_frame.is_runtime_frame(), "sanity check");
|
||||||
frame caller_frame = stub_frame.sender(®_map);
|
frame caller_frame = stub_frame.sender(®_map);
|
||||||
assert(!caller_frame.is_interpreted_frame() && !caller_frame.is_entry_frame(), "unexpected frame");
|
assert(!caller_frame.is_interpreted_frame() && !caller_frame.is_entry_frame(), "unexpected frame");
|
||||||
|
assert(!caller_frame.is_ricochet_frame(), "unexpected frame");
|
||||||
#endif /* ASSERT */
|
#endif /* ASSERT */
|
||||||
|
|
||||||
methodHandle callee_method;
|
methodHandle callee_method;
|
||||||
|
@ -1222,6 +1229,7 @@ JRT_BLOCK_ENTRY(address, SharedRuntime::handle_wrong_method(JavaThread* thread))
|
||||||
|
|
||||||
if (caller_frame.is_interpreted_frame() ||
|
if (caller_frame.is_interpreted_frame() ||
|
||||||
caller_frame.is_entry_frame() ||
|
caller_frame.is_entry_frame() ||
|
||||||
|
caller_frame.is_ricochet_frame() ||
|
||||||
is_mh_invoke_via_adapter) {
|
is_mh_invoke_via_adapter) {
|
||||||
methodOop callee = thread->callee_target();
|
methodOop callee = thread->callee_target();
|
||||||
guarantee(callee != NULL && callee->is_method(), "bad handshake");
|
guarantee(callee != NULL && callee->is_method(), "bad handshake");
|
||||||
|
|
|
@ -58,6 +58,8 @@ class SharedRuntime: AllStatic {
|
||||||
static RuntimeStub* _resolve_virtual_call_blob;
|
static RuntimeStub* _resolve_virtual_call_blob;
|
||||||
static RuntimeStub* _resolve_static_call_blob;
|
static RuntimeStub* _resolve_static_call_blob;
|
||||||
|
|
||||||
|
static RicochetBlob* _ricochet_blob;
|
||||||
|
|
||||||
static SafepointBlob* _polling_page_safepoint_handler_blob;
|
static SafepointBlob* _polling_page_safepoint_handler_blob;
|
||||||
static SafepointBlob* _polling_page_return_handler_blob;
|
static SafepointBlob* _polling_page_return_handler_blob;
|
||||||
#ifdef COMPILER2
|
#ifdef COMPILER2
|
||||||
|
@ -213,6 +215,16 @@ class SharedRuntime: AllStatic {
|
||||||
return _resolve_static_call_blob->entry_point();
|
return _resolve_static_call_blob->entry_point();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static RicochetBlob* ricochet_blob() {
|
||||||
|
#ifdef X86
|
||||||
|
// Currently only implemented on x86
|
||||||
|
assert(!EnableInvokeDynamic || _ricochet_blob != NULL, "oops");
|
||||||
|
#endif
|
||||||
|
return _ricochet_blob;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void generate_ricochet_blob();
|
||||||
|
|
||||||
static SafepointBlob* polling_page_return_handler_blob() { return _polling_page_return_handler_blob; }
|
static SafepointBlob* polling_page_return_handler_blob() { return _polling_page_return_handler_blob; }
|
||||||
static SafepointBlob* polling_page_safepoint_handler_blob() { return _polling_page_safepoint_handler_blob; }
|
static SafepointBlob* polling_page_safepoint_handler_blob() { return _polling_page_safepoint_handler_blob; }
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
|
|
@ -154,7 +154,8 @@ void vframeArrayElement::fill_in(compiledVFrame* vf) {
|
||||||
|
|
||||||
int unpack_counter = 0;
|
int unpack_counter = 0;
|
||||||
|
|
||||||
void vframeArrayElement::unpack_on_stack(int callee_parameters,
|
void vframeArrayElement::unpack_on_stack(int caller_actual_parameters,
|
||||||
|
int callee_parameters,
|
||||||
int callee_locals,
|
int callee_locals,
|
||||||
frame* caller,
|
frame* caller,
|
||||||
bool is_top_frame,
|
bool is_top_frame,
|
||||||
|
@ -270,6 +271,7 @@ void vframeArrayElement::unpack_on_stack(int callee_parameters,
|
||||||
temps + callee_parameters,
|
temps + callee_parameters,
|
||||||
popframe_preserved_args_size_in_words,
|
popframe_preserved_args_size_in_words,
|
||||||
locks,
|
locks,
|
||||||
|
caller_actual_parameters,
|
||||||
callee_parameters,
|
callee_parameters,
|
||||||
callee_locals,
|
callee_locals,
|
||||||
caller,
|
caller,
|
||||||
|
@ -415,7 +417,8 @@ void vframeArrayElement::unpack_on_stack(int callee_parameters,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int vframeArrayElement::on_stack_size(int callee_parameters,
|
int vframeArrayElement::on_stack_size(int caller_actual_parameters,
|
||||||
|
int callee_parameters,
|
||||||
int callee_locals,
|
int callee_locals,
|
||||||
bool is_top_frame,
|
bool is_top_frame,
|
||||||
int popframe_extra_stack_expression_els) const {
|
int popframe_extra_stack_expression_els) const {
|
||||||
|
@ -426,6 +429,7 @@ int vframeArrayElement::on_stack_size(int callee_parameters,
|
||||||
temps + callee_parameters,
|
temps + callee_parameters,
|
||||||
popframe_extra_stack_expression_els,
|
popframe_extra_stack_expression_els,
|
||||||
locks,
|
locks,
|
||||||
|
caller_actual_parameters,
|
||||||
callee_parameters,
|
callee_parameters,
|
||||||
callee_locals,
|
callee_locals,
|
||||||
is_top_frame);
|
is_top_frame);
|
||||||
|
@ -496,7 +500,7 @@ void vframeArray::fill_in(JavaThread* thread,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void vframeArray::unpack_to_stack(frame &unpack_frame, int exec_mode) {
|
void vframeArray::unpack_to_stack(frame &unpack_frame, int exec_mode, int caller_actual_parameters) {
|
||||||
// stack picture
|
// stack picture
|
||||||
// unpack_frame
|
// unpack_frame
|
||||||
// [new interpreter frames ] (frames are skeletal but walkable)
|
// [new interpreter frames ] (frames are skeletal but walkable)
|
||||||
|
@ -525,7 +529,8 @@ void vframeArray::unpack_to_stack(frame &unpack_frame, int exec_mode) {
|
||||||
for (index = frames() - 1; index >= 0 ; index--) {
|
for (index = frames() - 1; index >= 0 ; index--) {
|
||||||
int callee_parameters = index == 0 ? 0 : element(index-1)->method()->size_of_parameters();
|
int callee_parameters = index == 0 ? 0 : element(index-1)->method()->size_of_parameters();
|
||||||
int callee_locals = index == 0 ? 0 : element(index-1)->method()->max_locals();
|
int callee_locals = index == 0 ? 0 : element(index-1)->method()->max_locals();
|
||||||
element(index)->unpack_on_stack(callee_parameters,
|
element(index)->unpack_on_stack(caller_actual_parameters,
|
||||||
|
callee_parameters,
|
||||||
callee_locals,
|
callee_locals,
|
||||||
&caller_frame,
|
&caller_frame,
|
||||||
index == 0,
|
index == 0,
|
||||||
|
@ -534,6 +539,7 @@ void vframeArray::unpack_to_stack(frame &unpack_frame, int exec_mode) {
|
||||||
Deoptimization::unwind_callee_save_values(element(index)->iframe(), this);
|
Deoptimization::unwind_callee_save_values(element(index)->iframe(), this);
|
||||||
}
|
}
|
||||||
caller_frame = *element(index)->iframe();
|
caller_frame = *element(index)->iframe();
|
||||||
|
caller_actual_parameters = callee_parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -83,13 +83,15 @@ class vframeArrayElement : public _ValueObj {
|
||||||
|
|
||||||
// Returns the on stack word size for this frame
|
// Returns the on stack word size for this frame
|
||||||
// callee_parameters is the number of callee locals residing inside this frame
|
// callee_parameters is the number of callee locals residing inside this frame
|
||||||
int on_stack_size(int callee_parameters,
|
int on_stack_size(int caller_actual_parameters,
|
||||||
|
int callee_parameters,
|
||||||
int callee_locals,
|
int callee_locals,
|
||||||
bool is_top_frame,
|
bool is_top_frame,
|
||||||
int popframe_extra_stack_expression_els) const;
|
int popframe_extra_stack_expression_els) const;
|
||||||
|
|
||||||
// Unpacks the element to skeletal interpreter frame
|
// Unpacks the element to skeletal interpreter frame
|
||||||
void unpack_on_stack(int callee_parameters,
|
void unpack_on_stack(int caller_actual_parameters,
|
||||||
|
int callee_parameters,
|
||||||
int callee_locals,
|
int callee_locals,
|
||||||
frame* caller,
|
frame* caller,
|
||||||
bool is_top_frame,
|
bool is_top_frame,
|
||||||
|
@ -190,7 +192,7 @@ class vframeArray: public CHeapObj {
|
||||||
int frame_size() const { return _frame_size; }
|
int frame_size() const { return _frame_size; }
|
||||||
|
|
||||||
// Unpack the array on the stack passed in stack interval
|
// Unpack the array on the stack passed in stack interval
|
||||||
void unpack_to_stack(frame &unpack_frame, int exec_mode);
|
void unpack_to_stack(frame &unpack_frame, int exec_mode, int caller_actual_parameters);
|
||||||
|
|
||||||
// Deallocates monitor chunks allocated during deoptimization.
|
// Deallocates monitor chunks allocated during deoptimization.
|
||||||
// This should be called when the array is not used anymore.
|
// This should be called when the array is not used anymore.
|
||||||
|
|
|
@ -1649,6 +1649,9 @@ int VM_HeapDumper::do_thread(JavaThread* java_thread, u4 thread_serial_num) {
|
||||||
if (fr->is_entry_frame()) {
|
if (fr->is_entry_frame()) {
|
||||||
last_entry_frame = fr;
|
last_entry_frame = fr;
|
||||||
}
|
}
|
||||||
|
if (fr->is_ricochet_frame()) {
|
||||||
|
fr->oops_ricochet_do(&blk, vf->register_map());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
vf = vf->sender();
|
vf = vf->sender();
|
||||||
}
|
}
|
||||||
|
|
45
hotspot/test/compiler/7042153/Test7042153.java
Normal file
45
hotspot/test/compiler/7042153/Test7042153.java
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @bug 7042153
|
||||||
|
* @summary Bad folding of IfOps with unloaded constant arguments in C1
|
||||||
|
*
|
||||||
|
* @run main/othervm -Xcomp Test7042153
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
|
||||||
|
public class Test7042153 {
|
||||||
|
static public class Bar { }
|
||||||
|
static public class Foo { }
|
||||||
|
|
||||||
|
static volatile boolean z;
|
||||||
|
public static void main(String [] args) {
|
||||||
|
Class cx = Bar.class;
|
||||||
|
Class cy = Foo.class;
|
||||||
|
z = (cx == cy);
|
||||||
|
}
|
||||||
|
}
|
|
@ -116,3 +116,4 @@ be3758943770a0a3dd4be6a1cb4063507c4d7062 jdk7-b138
|
||||||
28c7c0ed2444607829ba11ad827f8d52197a2830 jdk7-b139
|
28c7c0ed2444607829ba11ad827f8d52197a2830 jdk7-b139
|
||||||
c8136fd161c83917f87e93b14fa2ba3483f9be83 jdk7-b140
|
c8136fd161c83917f87e93b14fa2ba3483f9be83 jdk7-b140
|
||||||
e1b5ef243445bf836d095fd44866e1771ef99374 jdk7-b141
|
e1b5ef243445bf836d095fd44866e1771ef99374 jdk7-b141
|
||||||
|
7d067af4b25e4b7e6b28bef48527d67f8650e6c5 jdk7-b142
|
||||||
|
|
|
@ -116,3 +116,4 @@ cc956c8a8255583535597e9a63db23c510e9a063 jdk7-b138
|
||||||
c025078c8362076503bb83b8e4da14ba7b347940 jdk7-b139
|
c025078c8362076503bb83b8e4da14ba7b347940 jdk7-b139
|
||||||
82a9022c4f21b1313023c8303b557a17c4106701 jdk7-b140
|
82a9022c4f21b1313023c8303b557a17c4106701 jdk7-b140
|
||||||
66826b0aec5a1834da3821c35cf85ac154e9b04d jdk7-b141
|
66826b0aec5a1834da3821c35cf85ac154e9b04d jdk7-b141
|
||||||
|
0ef3ef823c39eee3d670e58027c3219cb66f0283 jdk7-b142
|
||||||
|
|
|
@ -25,8 +25,8 @@
|
||||||
|
|
||||||
drops.master.copy.base=${drops.dir}
|
drops.master.copy.base=${drops.dir}
|
||||||
|
|
||||||
jaxws_src.bundle.name=jdk7-jaxws2_2_4-b01-2011_04_08.zip
|
jaxws_src.bundle.name=jdk7-jaxws2_2_4-b02-2011_05_09.zip
|
||||||
jaxws_src.bundle.md5.checksum=9f35dd731c99ddb62db650aaf20e5bf4
|
jaxws_src.bundle.md5.checksum=0972a58090e8b2735d91a2f5d1ae1964
|
||||||
jaxws_src.master.bundle.dir=${drops.master.copy.base}
|
jaxws_src.master.bundle.dir=${drops.master.copy.base}
|
||||||
jaxws_src.master.bundle.url.base=http://download.java.net/glassfish/components/jax-ws/openjdk/jdk7
|
jaxws_src.master.bundle.url.base=http://download.java.net/glassfish/components/jax-ws/openjdk/jdk7
|
||||||
|
|
||||||
|
|
|
@ -116,3 +116,4 @@ aa13e7702cd9d8aca9aa38f1227f966990866944 jdk7-b136
|
||||||
d80954a89b49fda47c0c5cace65a17f5a758b8bd jdk7-b139
|
d80954a89b49fda47c0c5cace65a17f5a758b8bd jdk7-b139
|
||||||
9315c733fb17ddfb9fb44be7e0ffea37bf3c727d jdk7-b140
|
9315c733fb17ddfb9fb44be7e0ffea37bf3c727d jdk7-b140
|
||||||
63eeefe118da18c75ba3d36266768cd1ccaaca6b jdk7-b141
|
63eeefe118da18c75ba3d36266768cd1ccaaca6b jdk7-b141
|
||||||
|
312612e89ece62633f4809706dec00bcd5fe7c2d jdk7-b142
|
||||||
|
|
|
@ -38,7 +38,7 @@ FILES_java = \
|
||||||
com/sun/nio/sctp/SctpMultiChannel.java \
|
com/sun/nio/sctp/SctpMultiChannel.java \
|
||||||
com/sun/nio/sctp/SctpServerChannel.java \
|
com/sun/nio/sctp/SctpServerChannel.java \
|
||||||
com/sun/nio/sctp/SctpSocketOption.java \
|
com/sun/nio/sctp/SctpSocketOption.java \
|
||||||
com/sun/nio/sctp/SctpStandardSocketOption.java \
|
com/sun/nio/sctp/SctpStandardSocketOptions.java \
|
||||||
com/sun/nio/sctp/SendFailedNotification.java \
|
com/sun/nio/sctp/SendFailedNotification.java \
|
||||||
com/sun/nio/sctp/ShutdownNotification.java \
|
com/sun/nio/sctp/ShutdownNotification.java \
|
||||||
\
|
\
|
||||||
|
|
|
@ -49,6 +49,7 @@ SUNWprivate_1.1 {
|
||||||
Java_sun_management_Flag_setStringValue;
|
Java_sun_management_Flag_setStringValue;
|
||||||
Java_sun_management_GarbageCollectorImpl_getCollectionCount;
|
Java_sun_management_GarbageCollectorImpl_getCollectionCount;
|
||||||
Java_sun_management_GarbageCollectorImpl_getCollectionTime;
|
Java_sun_management_GarbageCollectorImpl_getCollectionTime;
|
||||||
|
Java_sun_management_GarbageCollectorImpl_setNotificationEnabled;
|
||||||
Java_sun_management_GcInfoBuilder_fillGcAttributeInfo;
|
Java_sun_management_GcInfoBuilder_fillGcAttributeInfo;
|
||||||
Java_sun_management_GcInfoBuilder_getLastGcInfo0;
|
Java_sun_management_GcInfoBuilder_getLastGcInfo0;
|
||||||
Java_sun_management_GcInfoBuilder_getNumGcExtAttributes;
|
Java_sun_management_GcInfoBuilder_getNumGcExtAttributes;
|
||||||
|
|
|
@ -71,7 +71,7 @@ FILES_src = \
|
||||||
java/nio/charset/CoderMalfunctionError.java \
|
java/nio/charset/CoderMalfunctionError.java \
|
||||||
java/nio/charset/CodingErrorAction.java \
|
java/nio/charset/CodingErrorAction.java \
|
||||||
java/nio/charset/MalformedInputException.java \
|
java/nio/charset/MalformedInputException.java \
|
||||||
java/nio/charset/StandardCharset.java \
|
java/nio/charset/StandardCharsets.java \
|
||||||
java/nio/charset/UnmappableCharacterException.java \
|
java/nio/charset/UnmappableCharacterException.java \
|
||||||
\
|
\
|
||||||
java/nio/charset/spi/CharsetProvider.java \
|
java/nio/charset/spi/CharsetProvider.java \
|
||||||
|
@ -116,7 +116,7 @@ FILES_src = \
|
||||||
java/nio/file/SimpleFileVisitor.java \
|
java/nio/file/SimpleFileVisitor.java \
|
||||||
java/nio/file/StandardCopyOption.java \
|
java/nio/file/StandardCopyOption.java \
|
||||||
java/nio/file/StandardOpenOption.java \
|
java/nio/file/StandardOpenOption.java \
|
||||||
java/nio/file/StandardWatchEventKind.java \
|
java/nio/file/StandardWatchEventKinds.java \
|
||||||
java/nio/file/TempFileHelper.java \
|
java/nio/file/TempFileHelper.java \
|
||||||
java/nio/file/WatchEvent.java \
|
java/nio/file/WatchEvent.java \
|
||||||
java/nio/file/WatchKey.java \
|
java/nio/file/WatchKey.java \
|
||||||
|
|
|
@ -85,16 +85,21 @@ REMOTE_impls = \
|
||||||
sun.rmi.registry.RegistryImpl \
|
sun.rmi.registry.RegistryImpl \
|
||||||
sun.rmi.transport.DGCImpl
|
sun.rmi.transport.DGCImpl
|
||||||
|
|
||||||
ifeq ($(PLATFORM), windows)
|
#
|
||||||
build: stubs
|
# The java-rmi.cgi script in bin/ only gets delivered in certain situations
|
||||||
else # PLATFORM
|
#
|
||||||
ifneq ($(ARCH_DATA_MODEL), 32)
|
BUILD_TARGETS = stubs
|
||||||
build: stubs
|
ifeq ($(PLATFORM), linux)
|
||||||
else # ARCH_DATA_MODEL
|
BUILD_TARGETS += bin
|
||||||
build: stubs bin
|
endif
|
||||||
|
ifeq ($(PLATFORM), solaris)
|
||||||
|
ifeq ($(ARCH_DATA_MODEL), 32)
|
||||||
|
BUILD_TARGETS += bin
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
build: $(BUILD_TARGETS)
|
||||||
|
|
||||||
clean clobber:: bin.clean
|
clean clobber:: bin.clean
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -158,7 +158,6 @@ SUNWprivate_1.1 {
|
||||||
Java_sun_awt_X11_XRobotPeer_mouseReleaseImpl;
|
Java_sun_awt_X11_XRobotPeer_mouseReleaseImpl;
|
||||||
Java_sun_awt_X11_XRobotPeer_mouseWheelImpl;
|
Java_sun_awt_X11_XRobotPeer_mouseWheelImpl;
|
||||||
Java_sun_awt_X11_XRobotPeer_setup;
|
Java_sun_awt_X11_XRobotPeer_setup;
|
||||||
Java_sun_awt_X11_XRobotPeer__1dispose;
|
|
||||||
Java_sun_awt_X11_XToolkit_getNumberOfButtonsImpl;
|
Java_sun_awt_X11_XToolkit_getNumberOfButtonsImpl;
|
||||||
Java_java_awt_Component_initIDs;
|
Java_java_awt_Component_initIDs;
|
||||||
Java_java_awt_Container_initIDs;
|
Java_java_awt_Container_initIDs;
|
||||||
|
|
|
@ -0,0 +1,237 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 only, as
|
||||||
|
* published by the Free Software Foundation. Oracle designates this
|
||||||
|
* particular file as subject to the "Classpath" exception as provided
|
||||||
|
* by Oracle in the LICENSE file that accompanied this code.
|
||||||
|
*
|
||||||
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* version 2 for more details (a copy is included in the LICENSE file that
|
||||||
|
* accompanied this code).
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License version
|
||||||
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*
|
||||||
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||||
|
* or visit www.oracle.com if you need additional information or have any
|
||||||
|
* questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.sun.management;
|
||||||
|
|
||||||
|
import javax.management.Notification;
|
||||||
|
import javax.management.openmbean.CompositeData;
|
||||||
|
import javax.management.openmbean.CompositeDataView;
|
||||||
|
import javax.management.openmbean.CompositeType;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import sun.management.GarbageCollectionNotifInfoCompositeData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The information about a garbage collection
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* A garbage collection notification is emitted by {@link GarbageCollectorMXBean}
|
||||||
|
* when the Java virtual machine completes a garbage collection action
|
||||||
|
* The notification emitted will contain the garbage collection notification
|
||||||
|
* information about the status of the memory:
|
||||||
|
* <u1>
|
||||||
|
* <li>The name of the garbage collector used perform the collection.</li>
|
||||||
|
* <li>The action performed by the garbage collector.</li>
|
||||||
|
* <li>The cause of the garbage collection action.</li>
|
||||||
|
* <li>A {@link GcInfo} object containing some statistics about the GC cycle
|
||||||
|
(start time, end time) and the memory usage before and after
|
||||||
|
the GC cycle.</li>
|
||||||
|
* </u1>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* A {@link CompositeData CompositeData} representing
|
||||||
|
* the {@code GarbageCollectionNotificationInfo} object
|
||||||
|
* is stored in the
|
||||||
|
* {@linkplain javax.management.Notification#setUserData userdata}
|
||||||
|
* of a {@linkplain javax.management.Notification notification}.
|
||||||
|
* The {@link #from from} method is provided to convert from
|
||||||
|
* a {@code CompositeData} to a {@code GarbageCollectionNotificationInfo}
|
||||||
|
* object. For example:
|
||||||
|
*
|
||||||
|
* <blockquote><pre>
|
||||||
|
* Notification notif;
|
||||||
|
*
|
||||||
|
* // receive the notification emitted by a GarbageCollectorMXBean and set to notif
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* String notifType = notif.getType();
|
||||||
|
* if (notifType.equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) {
|
||||||
|
* // retrieve the garbage collection notification information
|
||||||
|
* CompositeData cd = (CompositeData) notif.getUserData();
|
||||||
|
* GarbageCollectionNotificationInfo info = GarbageCollectionNotificationInfo.from(cd);
|
||||||
|
* ....
|
||||||
|
* }
|
||||||
|
* </pre></blockquote>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The type of the notification emitted by a {@code GarbageCollectorMXBean} is:
|
||||||
|
* <ul>
|
||||||
|
* <li>A {@linkplain #GARBAGE_COLLECTION_NOTIFICATION garbage collection notification}.
|
||||||
|
* <br>Used by every notification emitted by the garbage collector, the details about
|
||||||
|
* the notification are provided in the {@linkplain #getGcAction action} String
|
||||||
|
* <p></li>
|
||||||
|
* </ul>
|
||||||
|
**/
|
||||||
|
|
||||||
|
public class GarbageCollectionNotificationInfo implements CompositeDataView {
|
||||||
|
|
||||||
|
private final String gcName;
|
||||||
|
private final String gcAction;
|
||||||
|
private final String gcCause;
|
||||||
|
private final GcInfo gcInfo;
|
||||||
|
private final CompositeData cdata;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification type denoting that
|
||||||
|
* the Java virtual machine has completed a garbage collection cycle.
|
||||||
|
* This notification is emitted by a {@link GarbageCollectorMXBean}.
|
||||||
|
* The value of this notification type is
|
||||||
|
* {@code com.sun.management.gc.notification}.
|
||||||
|
*/
|
||||||
|
public static final String GARBAGE_COLLECTION_NOTIFICATION =
|
||||||
|
"com.sun.management.gc.notification";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a {@code GarbageCollectionNotificationInfo} object.
|
||||||
|
*
|
||||||
|
* @param gcName The name of the garbage collector used to perform the collection
|
||||||
|
* @param gcAction The name of the action performed by the garbage collector
|
||||||
|
* @param gcCause The cause the garbage collection action
|
||||||
|
* @param gcInfo a GcInfo object providing statistics about the GC cycle
|
||||||
|
*/
|
||||||
|
public GarbageCollectionNotificationInfo(String gcName,
|
||||||
|
String gcAction,
|
||||||
|
String gcCause,
|
||||||
|
GcInfo gcInfo) {
|
||||||
|
if (gcName == null) {
|
||||||
|
throw new NullPointerException("Null gcName");
|
||||||
|
}
|
||||||
|
if (gcAction == null) {
|
||||||
|
throw new NullPointerException("Null gcAction");
|
||||||
|
}
|
||||||
|
if (gcCause == null) {
|
||||||
|
throw new NullPointerException("Null gcCause");
|
||||||
|
}
|
||||||
|
this.gcName = gcName;
|
||||||
|
this.gcAction = gcAction;
|
||||||
|
this.gcCause = gcCause;
|
||||||
|
this.gcInfo = gcInfo;
|
||||||
|
this.cdata = new GarbageCollectionNotifInfoCompositeData(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
GarbageCollectionNotificationInfo(CompositeData cd) {
|
||||||
|
GarbageCollectionNotifInfoCompositeData.validateCompositeData(cd);
|
||||||
|
|
||||||
|
this.gcName = GarbageCollectionNotifInfoCompositeData.getGcName(cd);
|
||||||
|
this.gcAction = GarbageCollectionNotifInfoCompositeData.getGcAction(cd);
|
||||||
|
this.gcCause = GarbageCollectionNotifInfoCompositeData.getGcCause(cd);
|
||||||
|
this.gcInfo = GarbageCollectionNotifInfoCompositeData.getGcInfo(cd);
|
||||||
|
this.cdata = cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the garbage collector used to perform the collection
|
||||||
|
*
|
||||||
|
* @return the name of the garbage collector used to perform the collection
|
||||||
|
*/
|
||||||
|
public String getGcName() {
|
||||||
|
return gcName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the action of the performed by the garbage collector
|
||||||
|
*
|
||||||
|
* @return the the action of the performed by the garbage collector
|
||||||
|
*/
|
||||||
|
public String getGcAction() {
|
||||||
|
return gcAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the cause the garbage collection
|
||||||
|
*
|
||||||
|
* @return the the cause the garbage collection
|
||||||
|
*/
|
||||||
|
public String getGcCause() {
|
||||||
|
return gcCause;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the GC information related to the last garbage collection
|
||||||
|
*
|
||||||
|
* @return the GC information related to the
|
||||||
|
* last garbage collection
|
||||||
|
*/
|
||||||
|
public GcInfo getGcInfo() {
|
||||||
|
return gcInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a {@code GarbageCollectionNotificationInfo} object represented by the
|
||||||
|
* given {@code CompositeData}.
|
||||||
|
* The given {@code CompositeData} must contain
|
||||||
|
* the following attributes:
|
||||||
|
* <blockquote>
|
||||||
|
* <table border>
|
||||||
|
* <tr>
|
||||||
|
* <th align=left>Attribute Name</th>
|
||||||
|
* <th align=left>Type</th>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>gcName</td>
|
||||||
|
* <td>{@code java.lang.String}</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>gcAction</td>
|
||||||
|
* <td>{@code java.lang.String}</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>gcCause</td>
|
||||||
|
* <td>{@code java.lang.String}</td>
|
||||||
|
* </tr>
|
||||||
|
* <tr>
|
||||||
|
* <td>gcInfo</td>
|
||||||
|
* <td>{@code javax.management.openmbean.CompositeData}</td>
|
||||||
|
* </tr>
|
||||||
|
* </table>
|
||||||
|
* </blockquote>
|
||||||
|
*
|
||||||
|
* @param cd {@code CompositeData} representing a
|
||||||
|
* {@code GarbageCollectionNotificationInfo}
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if {@code cd} does not
|
||||||
|
* represent a {@code GarbaageCollectionNotificationInfo} object.
|
||||||
|
*
|
||||||
|
* @return a {@code GarbageCollectionNotificationInfo} object represented
|
||||||
|
* by {@code cd} if {@code cd} is not {@code null};
|
||||||
|
* {@code null} otherwise.
|
||||||
|
*/
|
||||||
|
public static GarbageCollectionNotificationInfo from(CompositeData cd) {
|
||||||
|
if (cd == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cd instanceof GarbageCollectionNotifInfoCompositeData) {
|
||||||
|
return ((GarbageCollectionNotifInfoCompositeData) cd).getGarbageCollectionNotifInfo();
|
||||||
|
} else {
|
||||||
|
return new GarbageCollectionNotificationInfo(cd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompositeData toCompositeData(CompositeType ct) {
|
||||||
|
return cdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -179,7 +179,7 @@ public abstract class MessageInfo {
|
||||||
* completely received. For messages being sent {@code true} indicates that
|
* completely received. For messages being sent {@code true} indicates that
|
||||||
* the message is complete, {@code false} indicates that the message is not
|
* the message is complete, {@code false} indicates that the message is not
|
||||||
* complete. How the send channel interprets this value depends on the value
|
* complete. How the send channel interprets this value depends on the value
|
||||||
* of its {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE
|
* of its {@link SctpStandardSocketOptions#SCTP_EXPLICIT_COMPLETE
|
||||||
* SCTP_EXPLICIT_COMPLETE} socket option.
|
* SCTP_EXPLICIT_COMPLETE} socket option.
|
||||||
*
|
*
|
||||||
* @return {@code true} if, and only if, the message is complete
|
* @return {@code true} if, and only if, the message is complete
|
||||||
|
@ -192,7 +192,7 @@ public abstract class MessageInfo {
|
||||||
* <P> For messages being sent {@code true} indicates that
|
* <P> For messages being sent {@code true} indicates that
|
||||||
* the message is complete, {@code false} indicates that the message is not
|
* the message is complete, {@code false} indicates that the message is not
|
||||||
* complete. How the send channel interprets this value depends on the value
|
* complete. How the send channel interprets this value depends on the value
|
||||||
* of its {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE
|
* of its {@link SctpStandardSocketOptions#SCTP_EXPLICIT_COMPLETE
|
||||||
* SCTP_EXPLICIT_COMPLETE} socket option.
|
* SCTP_EXPLICIT_COMPLETE} socket option.
|
||||||
*
|
*
|
||||||
* @param complete
|
* @param complete
|
||||||
|
|
|
@ -65,55 +65,55 @@ import java.nio.channels.SelectionKey;
|
||||||
* <th>Description</th>
|
* <th>Description</th>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SCTP_DISABLE_FRAGMENTS
|
* <td> {@link SctpStandardSocketOptions#SCTP_DISABLE_FRAGMENTS
|
||||||
* SCTP_DISABLE_FRAGMENTS} </td>
|
* SCTP_DISABLE_FRAGMENTS} </td>
|
||||||
* <td> Enables or disables message fragmentation </td>
|
* <td> Enables or disables message fragmentation </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE
|
* <td> {@link SctpStandardSocketOptions#SCTP_EXPLICIT_COMPLETE
|
||||||
* SCTP_EXPLICIT_COMPLETE} </td>
|
* SCTP_EXPLICIT_COMPLETE} </td>
|
||||||
* <td> Enables or disables explicit message completion </td>
|
* <td> Enables or disables explicit message completion </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE
|
* <td> {@link SctpStandardSocketOptions#SCTP_FRAGMENT_INTERLEAVE
|
||||||
* SCTP_FRAGMENT_INTERLEAVE} </td>
|
* SCTP_FRAGMENT_INTERLEAVE} </td>
|
||||||
* <td> Controls how the presentation of messages occur for the message
|
* <td> Controls how the presentation of messages occur for the message
|
||||||
* receiver </td>
|
* receiver </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS
|
* <td> {@link SctpStandardSocketOptions#SCTP_INIT_MAXSTREAMS
|
||||||
* SCTP_INIT_MAXSTREAMS} </td>
|
* SCTP_INIT_MAXSTREAMS} </td>
|
||||||
* <td> The maximum number of streams requested by the local endpoint during
|
* <td> The maximum number of streams requested by the local endpoint during
|
||||||
* association initialization </td>
|
* association initialization </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SCTP_NODELAY SCTP_NODELAY} </td>
|
* <td> {@link SctpStandardSocketOptions#SCTP_NODELAY SCTP_NODELAY} </td>
|
||||||
* <td> Enables or disable a Nagle-like algorithm </td>
|
* <td> Enables or disable a Nagle-like algorithm </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SCTP_PRIMARY_ADDR
|
* <td> {@link SctpStandardSocketOptions#SCTP_PRIMARY_ADDR
|
||||||
* SCTP_PRIMARY_ADDR} </td>
|
* SCTP_PRIMARY_ADDR} </td>
|
||||||
* <td> Requests that the local SCTP stack use the given peer address as the
|
* <td> Requests that the local SCTP stack use the given peer address as the
|
||||||
* association primary </td>
|
* association primary </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SCTP_SET_PEER_PRIMARY_ADDR
|
* <td> {@link SctpStandardSocketOptions#SCTP_SET_PEER_PRIMARY_ADDR
|
||||||
* SCTP_SET_PEER_PRIMARY_ADDR} </td>
|
* SCTP_SET_PEER_PRIMARY_ADDR} </td>
|
||||||
* <td> Requests that the peer mark the enclosed address as the association
|
* <td> Requests that the peer mark the enclosed address as the association
|
||||||
* primary </td>
|
* primary </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SO_SNDBUF
|
* <td> {@link SctpStandardSocketOptions#SO_SNDBUF
|
||||||
* SO_SNDBUF} </td>
|
* SO_SNDBUF} </td>
|
||||||
* <td> The size of the socket send buffer </td>
|
* <td> The size of the socket send buffer </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SO_RCVBUF
|
* <td> {@link SctpStandardSocketOptions#SO_RCVBUF
|
||||||
* SO_RCVBUF} </td>
|
* SO_RCVBUF} </td>
|
||||||
* <td> The size of the socket receive buffer </td>
|
* <td> The size of the socket receive buffer </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SO_LINGER
|
* <td> {@link SctpStandardSocketOptions#SO_LINGER
|
||||||
* SO_LINGER} </td>
|
* SO_LINGER} </td>
|
||||||
* <td> Linger on close if data is present (when configured in blocking mode
|
* <td> Linger on close if data is present (when configured in blocking mode
|
||||||
* only) </td>
|
* only) </td>
|
||||||
|
@ -449,7 +449,7 @@ public abstract class SctpChannel
|
||||||
* <P> This is a convience method and is equivalent to evaluating the
|
* <P> This is a convience method and is equivalent to evaluating the
|
||||||
* following expression:
|
* following expression:
|
||||||
* <blockquote><pre>
|
* <blockquote><pre>
|
||||||
* setOption(SctpStandardSocketOption.SCTP_INIT_MAXSTREAMS, SctpStandardSocketOption.InitMaxStreams.create(maxInStreams, maxOutStreams))
|
* setOption(SctpStandardSocketOptions.SCTP_INIT_MAXSTREAMS, SctpStandardSocketOption.InitMaxStreams.create(maxInStreams, maxOutStreams))
|
||||||
* .connect(remote);
|
* .connect(remote);
|
||||||
* </pre></blockquote>
|
* </pre></blockquote>
|
||||||
*
|
*
|
||||||
|
@ -651,7 +651,7 @@ public abstract class SctpChannel
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* If an I/O error occurs
|
* If an I/O error occurs
|
||||||
*
|
*
|
||||||
* @see SctpStandardSocketOption
|
* @see SctpStandardSocketOptions
|
||||||
*/
|
*/
|
||||||
public abstract <T> T getOption(SctpSocketOption<T> name)
|
public abstract <T> T getOption(SctpSocketOption<T> name)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
@ -680,7 +680,7 @@ public abstract class SctpChannel
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* If an I/O error occurs
|
* If an I/O error occurs
|
||||||
*
|
*
|
||||||
* @see SctpStandardSocketOption
|
* @see SctpStandardSocketOptions
|
||||||
*/
|
*/
|
||||||
public abstract <T> SctpChannel setOption(SctpSocketOption<T> name, T value)
|
public abstract <T> SctpChannel setOption(SctpSocketOption<T> name, T value)
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
@ -731,7 +731,7 @@ public abstract class SctpChannel
|
||||||
* MessageInfo} will return {@code false}, and more invocations of this
|
* MessageInfo} will return {@code false}, and more invocations of this
|
||||||
* method will be necessary to completely consume the messgae. Only
|
* method will be necessary to completely consume the messgae. Only
|
||||||
* one message at a time will be partially delivered in any stream. The
|
* one message at a time will be partially delivered in any stream. The
|
||||||
* socket option {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE
|
* socket option {@link SctpStandardSocketOptions#SCTP_FRAGMENT_INTERLEAVE
|
||||||
* SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of
|
* SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of
|
||||||
* messages occurs.
|
* messages occurs.
|
||||||
*
|
*
|
||||||
|
@ -804,7 +804,7 @@ public abstract class SctpChannel
|
||||||
* and sufficient room becomes available, then the remaining bytes in the
|
* and sufficient room becomes available, then the remaining bytes in the
|
||||||
* given byte buffer are transmitted as a single message. Sending a message
|
* given byte buffer are transmitted as a single message. Sending a message
|
||||||
* is atomic unless explicit message completion {@link
|
* is atomic unless explicit message completion {@link
|
||||||
* SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}
|
* SctpStandardSocketOptions#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}
|
||||||
* socket option is enabled on this channel's socket.
|
* socket option is enabled on this channel's socket.
|
||||||
*
|
*
|
||||||
* <P> The message is transferred from the byte buffer as if by a regular
|
* <P> The message is transferred from the byte buffer as if by a regular
|
||||||
|
|
|
@ -69,55 +69,55 @@ import java.nio.channels.SelectionKey;
|
||||||
* <th>Description</th>
|
* <th>Description</th>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SCTP_DISABLE_FRAGMENTS
|
* <td> {@link SctpStandardSocketOptions#SCTP_DISABLE_FRAGMENTS
|
||||||
* SCTP_DISABLE_FRAGMENTS} </td>
|
* SCTP_DISABLE_FRAGMENTS} </td>
|
||||||
* <td> Enables or disables message fragmentation </td>
|
* <td> Enables or disables message fragmentation </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE
|
* <td> {@link SctpStandardSocketOptions#SCTP_EXPLICIT_COMPLETE
|
||||||
* SCTP_EXPLICIT_COMPLETE} </td>
|
* SCTP_EXPLICIT_COMPLETE} </td>
|
||||||
* <td> Enables or disables explicit message completion </td>
|
* <td> Enables or disables explicit message completion </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE
|
* <td> {@link SctpStandardSocketOptions#SCTP_FRAGMENT_INTERLEAVE
|
||||||
* SCTP_FRAGMENT_INTERLEAVE} </td>
|
* SCTP_FRAGMENT_INTERLEAVE} </td>
|
||||||
* <td> Controls how the presentation of messages occur for the message
|
* <td> Controls how the presentation of messages occur for the message
|
||||||
* receiver </td>
|
* receiver </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS
|
* <td> {@link SctpStandardSocketOptions#SCTP_INIT_MAXSTREAMS
|
||||||
* SCTP_INIT_MAXSTREAMS} </td>
|
* SCTP_INIT_MAXSTREAMS} </td>
|
||||||
* <td> The maximum number of streams requested by the local endpoint during
|
* <td> The maximum number of streams requested by the local endpoint during
|
||||||
* association initialization </td>
|
* association initialization </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SCTP_NODELAY SCTP_NODELAY} </td>
|
* <td> {@link SctpStandardSocketOptions#SCTP_NODELAY SCTP_NODELAY} </td>
|
||||||
* <td> Enables or disable a Nagle-like algorithm </td>
|
* <td> Enables or disable a Nagle-like algorithm </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SCTP_PRIMARY_ADDR
|
* <td> {@link SctpStandardSocketOptions#SCTP_PRIMARY_ADDR
|
||||||
* SCTP_PRIMARY_ADDR} </td>
|
* SCTP_PRIMARY_ADDR} </td>
|
||||||
* <td> Requests that the local SCTP stack use the given peer address as the
|
* <td> Requests that the local SCTP stack use the given peer address as the
|
||||||
* association primary </td>
|
* association primary </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SCTP_SET_PEER_PRIMARY_ADDR
|
* <td> {@link SctpStandardSocketOptions#SCTP_SET_PEER_PRIMARY_ADDR
|
||||||
* SCTP_SET_PEER_PRIMARY_ADDR} </td>
|
* SCTP_SET_PEER_PRIMARY_ADDR} </td>
|
||||||
* <td> Requests that the peer mark the enclosed address as the association
|
* <td> Requests that the peer mark the enclosed address as the association
|
||||||
* primary </td>
|
* primary </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SO_SNDBUF
|
* <td> {@link SctpStandardSocketOptions#SO_SNDBUF
|
||||||
* SO_SNDBUF} </td>
|
* SO_SNDBUF} </td>
|
||||||
* <td> The size of the socket send buffer </td>
|
* <td> The size of the socket send buffer </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SO_RCVBUF
|
* <td> {@link SctpStandardSocketOptions#SO_RCVBUF
|
||||||
* SO_RCVBUF} </td>
|
* SO_RCVBUF} </td>
|
||||||
* <td> The size of the socket receive buffer </td>
|
* <td> The size of the socket receive buffer </td>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SO_LINGER
|
* <td> {@link SctpStandardSocketOptions#SO_LINGER
|
||||||
* SO_LINGER} </td>
|
* SO_LINGER} </td>
|
||||||
* <td> Linger on close if data is present (when configured in blocking mode
|
* <td> Linger on close if data is present (when configured in blocking mode
|
||||||
* only) </td>
|
* only) </td>
|
||||||
|
@ -450,7 +450,7 @@ public abstract class SctpMultiChannel
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* If an I/O error occurs
|
* If an I/O error occurs
|
||||||
*
|
*
|
||||||
* @see SctpStandardSocketOption
|
* @see SctpStandardSocketOptions
|
||||||
*/
|
*/
|
||||||
public abstract <T> T getOption(SctpSocketOption<T> name,
|
public abstract <T> T getOption(SctpSocketOption<T> name,
|
||||||
Association association)
|
Association association)
|
||||||
|
@ -489,7 +489,7 @@ public abstract class SctpMultiChannel
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* If an I/O error occurs
|
* If an I/O error occurs
|
||||||
*
|
*
|
||||||
* @see SctpStandardSocketOption
|
* @see SctpStandardSocketOptions
|
||||||
*/
|
*/
|
||||||
public abstract <T> SctpMultiChannel setOption(SctpSocketOption<T> name,
|
public abstract <T> SctpMultiChannel setOption(SctpSocketOption<T> name,
|
||||||
T value,
|
T value,
|
||||||
|
@ -542,7 +542,7 @@ public abstract class SctpMultiChannel
|
||||||
* MessageInfo} will return {@code false}, and more invocations of this
|
* MessageInfo} will return {@code false}, and more invocations of this
|
||||||
* method will be necessary to completely consume the messgae. Only
|
* method will be necessary to completely consume the messgae. Only
|
||||||
* one message at a time will be partially delivered in any stream. The
|
* one message at a time will be partially delivered in any stream. The
|
||||||
* socket option {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE
|
* socket option {@link SctpStandardSocketOptions#SCTP_FRAGMENT_INTERLEAVE
|
||||||
* SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of
|
* SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of
|
||||||
* messages occurs.
|
* messages occurs.
|
||||||
*
|
*
|
||||||
|
@ -635,14 +635,14 @@ public abstract class SctpMultiChannel
|
||||||
* underlying output buffer, then the remaining bytes in the given byte
|
* underlying output buffer, then the remaining bytes in the given byte
|
||||||
* buffer are transmitted as a single message. Sending a message
|
* buffer are transmitted as a single message. Sending a message
|
||||||
* is atomic unless explicit message completion {@link
|
* is atomic unless explicit message completion {@link
|
||||||
* SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}
|
* SctpStandardSocketOptions#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}
|
||||||
* socket option is enabled on this channel's socket.
|
* socket option is enabled on this channel's socket.
|
||||||
*
|
*
|
||||||
* <P> If this channel is in non-blocking mode, there is sufficient room
|
* <P> If this channel is in non-blocking mode, there is sufficient room
|
||||||
* in the underlying output buffer, and an implicit association setup is
|
* in the underlying output buffer, and an implicit association setup is
|
||||||
* required, then the remaining bytes in the given byte buffer are
|
* required, then the remaining bytes in the given byte buffer are
|
||||||
* transmitted as a single message, subject to {@link
|
* transmitted as a single message, subject to {@link
|
||||||
* SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}.
|
* SctpStandardSocketOptions#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}.
|
||||||
* If for any reason the message cannot
|
* If for any reason the message cannot
|
||||||
* be delivered an {@link AssociationChangeNotification association
|
* be delivered an {@link AssociationChangeNotification association
|
||||||
* changed} notification is put on the SCTP stack with its {@code event} parameter set
|
* changed} notification is put on the SCTP stack with its {@code event} parameter set
|
||||||
|
|
|
@ -53,7 +53,7 @@ import java.nio.channels.spi.AbstractSelectableChannel;
|
||||||
* <th>Description</th>
|
* <th>Description</th>
|
||||||
* </tr>
|
* </tr>
|
||||||
* <tr>
|
* <tr>
|
||||||
* <td> {@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS
|
* <td> {@link SctpStandardSocketOptions#SCTP_INIT_MAXSTREAMS
|
||||||
* SCTP_INIT_MAXSTREAMS} </td>
|
* SCTP_INIT_MAXSTREAMS} </td>
|
||||||
* <td> The maximum number of streams requested by the local endpoint during
|
* <td> The maximum number of streams requested by the local endpoint during
|
||||||
* association initialization </td>
|
* association initialization </td>
|
||||||
|
@ -360,7 +360,7 @@ public abstract class SctpServerChannel
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* If an I/O error occurs
|
* If an I/O error occurs
|
||||||
*
|
*
|
||||||
* @see SctpStandardSocketOption
|
* @see SctpStandardSocketOptions
|
||||||
*/
|
*/
|
||||||
public abstract <T> T getOption(SctpSocketOption<T> name) throws IOException;
|
public abstract <T> T getOption(SctpSocketOption<T> name) throws IOException;
|
||||||
|
|
||||||
|
@ -388,7 +388,7 @@ public abstract class SctpServerChannel
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* If an I/O error occurs
|
* If an I/O error occurs
|
||||||
*
|
*
|
||||||
* @see SctpStandardSocketOption
|
* @see SctpStandardSocketOptions
|
||||||
*/
|
*/
|
||||||
public abstract <T> SctpServerChannel setOption(SctpSocketOption<T> name,
|
public abstract <T> SctpServerChannel setOption(SctpSocketOption<T> name,
|
||||||
T value)
|
T value)
|
||||||
|
|
|
@ -33,6 +33,6 @@ import java.net.SocketOption;
|
||||||
*
|
*
|
||||||
* @since 1.7
|
* @since 1.7
|
||||||
*
|
*
|
||||||
* @see SctpStandardSocketOption
|
* @see SctpStandardSocketOptions
|
||||||
*/
|
*/
|
||||||
public interface SctpSocketOption<T> extends SocketOption<T> { }
|
public interface SctpSocketOption<T> extends SocketOption<T> { }
|
||||||
|
|
|
@ -34,8 +34,8 @@ import sun.nio.ch.SctpStdSocketOption;
|
||||||
*
|
*
|
||||||
* @since 1.7
|
* @since 1.7
|
||||||
*/
|
*/
|
||||||
public class SctpStandardSocketOption {
|
public class SctpStandardSocketOptions {
|
||||||
private SctpStandardSocketOption() {}
|
private SctpStandardSocketOptions() {}
|
||||||
/**
|
/**
|
||||||
* Enables or disables message fragmentation.
|
* Enables or disables message fragmentation.
|
||||||
*
|
*
|
||||||
|
@ -127,7 +127,7 @@ public class SctpStandardSocketOption {
|
||||||
* association initialization.
|
* association initialization.
|
||||||
*
|
*
|
||||||
* <P> The value of this socket option is an {@link
|
* <P> The value of this socket option is an {@link
|
||||||
* SctpStandardSocketOption.InitMaxStreams InitMaxStreams}, that represents
|
* SctpStandardSocketOptions.InitMaxStreams InitMaxStreams}, that represents
|
||||||
* the maximum number of inbound and outbound streams that an association
|
* the maximum number of inbound and outbound streams that an association
|
||||||
* on the channel is prepared to support.
|
* on the channel is prepared to support.
|
||||||
*
|
*
|
||||||
|
@ -157,9 +157,9 @@ public class SctpStandardSocketOption {
|
||||||
* the endpoints default value.
|
* the endpoints default value.
|
||||||
*/
|
*/
|
||||||
public static final SctpSocketOption
|
public static final SctpSocketOption
|
||||||
<SctpStandardSocketOption.InitMaxStreams> SCTP_INIT_MAXSTREAMS =
|
<SctpStandardSocketOptions.InitMaxStreams> SCTP_INIT_MAXSTREAMS =
|
||||||
new SctpStdSocketOption<SctpStandardSocketOption.InitMaxStreams>(
|
new SctpStdSocketOption<SctpStandardSocketOptions.InitMaxStreams>(
|
||||||
"SCTP_INIT_MAXSTREAMS", SctpStandardSocketOption.InitMaxStreams.class);
|
"SCTP_INIT_MAXSTREAMS", SctpStandardSocketOptions.InitMaxStreams.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables or disables a Nagle-like algorithm.
|
* Enables or disables a Nagle-like algorithm.
|
||||||
|
@ -310,7 +310,7 @@ public class SctpStandardSocketOption {
|
||||||
* This class is used to set the maximum number of inbound/outbound streams
|
* This class is used to set the maximum number of inbound/outbound streams
|
||||||
* used by the local endpoint during association initialization. An
|
* used by the local endpoint during association initialization. An
|
||||||
* instance of this class is used to set the {@link
|
* instance of this class is used to set the {@link
|
||||||
* SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS SCTP_INIT_MAXSTREAMS}
|
* SctpStandardSocketOptions#SCTP_INIT_MAXSTREAMS SCTP_INIT_MAXSTREAMS}
|
||||||
* socket option.
|
* socket option.
|
||||||
*
|
*
|
||||||
* @since 1.7
|
* @since 1.7
|
|
@ -2887,11 +2887,12 @@ public abstract class Component implements ImageObserver, MenuContainer,
|
||||||
/**
|
/**
|
||||||
* Invalidates this component and its ancestors.
|
* Invalidates this component and its ancestors.
|
||||||
* <p>
|
* <p>
|
||||||
* All the ancestors of this component up to the nearest validate root are
|
* By default, all the ancestors of the component up to the top-most
|
||||||
* marked invalid also. If there is no a validate root container for this
|
* container of the hierarchy are marked invalid. If the {@code
|
||||||
* component, all of its ancestors up to the root of the hierarchy are
|
* java.awt.smartInvalidate} system property is set to {@code true},
|
||||||
* marked invalid as well. Marking a container <i>invalid</i> indicates
|
* invalidation stops on the nearest validate root of this component.
|
||||||
* that the container needs to be laid out.
|
* Marking a container <i>invalid</i> indicates that the container needs to
|
||||||
|
* be laid out.
|
||||||
* <p>
|
* <p>
|
||||||
* This method is called automatically when any layout-related information
|
* This method is called automatically when any layout-related information
|
||||||
* changes (e.g. setting the bounds of the component, or adding the
|
* changes (e.g. setting the bounds of the component, or adding the
|
||||||
|
|
|
@ -41,6 +41,8 @@ import java.io.ObjectStreamField;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
import java.security.AccessController;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.EventListener;
|
import java.util.EventListener;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -60,6 +62,8 @@ import sun.awt.dnd.SunDropTargetEvent;
|
||||||
|
|
||||||
import sun.java2d.pipe.Region;
|
import sun.java2d.pipe.Region;
|
||||||
|
|
||||||
|
import sun.security.action.GetBooleanAction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A generic Abstract Window Toolkit(AWT) container object is a component
|
* A generic Abstract Window Toolkit(AWT) container object is a component
|
||||||
* that can contain other AWT components.
|
* that can contain other AWT components.
|
||||||
|
@ -1506,12 +1510,18 @@ public class Container extends Component {
|
||||||
* Layout-related changes, such as bounds of the validate root descendants,
|
* Layout-related changes, such as bounds of the validate root descendants,
|
||||||
* do not affect the layout of the validate root parent. This peculiarity
|
* do not affect the layout of the validate root parent. This peculiarity
|
||||||
* enables the {@code invalidate()} method to stop invalidating the
|
* enables the {@code invalidate()} method to stop invalidating the
|
||||||
* component hierarchy when the method encounters a validate root.
|
* component hierarchy when the method encounters a validate root. However,
|
||||||
|
* to preserve backward compatibility this new optimized behavior is
|
||||||
|
* enabled only when the {@code java.awt.smartInvalidate} system property
|
||||||
|
* value is set to {@code true}.
|
||||||
* <p>
|
* <p>
|
||||||
* If a component hierarchy contains validate roots, the {@code validate()}
|
* If a component hierarchy contains validate roots and the new optimized
|
||||||
* method must be invoked on the validate root of a previously invalidated
|
* {@code invalidate()} behavior is enabled, the {@code validate()} method
|
||||||
* component, rather than on the top-level container (such as a {@code
|
* must be invoked on the validate root of a previously invalidated
|
||||||
* Frame} object) to restore the validity of the hierarchy later.
|
* component to restore the validity of the hierarchy later. Otherwise,
|
||||||
|
* calling the {@code validate()} method on the top-level container (such
|
||||||
|
* as a {@code Frame} object) should be used to restore the validity of the
|
||||||
|
* component hierarchy.
|
||||||
* <p>
|
* <p>
|
||||||
* The {@code Window} class and the {@code Applet} class are the validate
|
* The {@code Window} class and the {@code Applet} class are the validate
|
||||||
* roots in AWT. Swing introduces more validate roots.
|
* roots in AWT. Swing introduces more validate roots.
|
||||||
|
@ -1527,13 +1537,20 @@ public class Container extends Component {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final boolean isJavaAwtSmartInvalidate;
|
||||||
|
static {
|
||||||
|
// Don't lazy-read because every app uses invalidate()
|
||||||
|
isJavaAwtSmartInvalidate = AccessController.doPrivileged(
|
||||||
|
new GetBooleanAction("java.awt.smartInvalidate"));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidates the parent of the container unless the container
|
* Invalidates the parent of the container unless the container
|
||||||
* is a validate root.
|
* is a validate root.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
void invalidateParent() {
|
void invalidateParent() {
|
||||||
if (!isValidateRoot()) {
|
if (!isJavaAwtSmartInvalidate || !isValidateRoot()) {
|
||||||
super.invalidateParent();
|
super.invalidateParent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1572,9 +1589,8 @@ public class Container extends Component {
|
||||||
* automatically. Note that the ancestors of the container may be
|
* automatically. Note that the ancestors of the container may be
|
||||||
* invalidated also (see {@link Component#invalidate} for details.)
|
* invalidated also (see {@link Component#invalidate} for details.)
|
||||||
* Therefore, to restore the validity of the hierarchy, the {@code
|
* Therefore, to restore the validity of the hierarchy, the {@code
|
||||||
* validate()} method should be invoked on a validate root of an
|
* validate()} method should be invoked on the top-most invalid
|
||||||
* invalidated component, or on the top-most container if the hierarchy
|
* container of the hierarchy.
|
||||||
* does not contain validate roots.
|
|
||||||
* <p>
|
* <p>
|
||||||
* Validating the container may be a quite time-consuming operation. For
|
* Validating the container may be a quite time-consuming operation. For
|
||||||
* performance reasons a developer may postpone the validation of the
|
* performance reasons a developer may postpone the validation of the
|
||||||
|
|
|
@ -466,10 +466,7 @@ public abstract class Toolkit {
|
||||||
*/
|
*/
|
||||||
protected void loadSystemColors(int[] systemColors)
|
protected void loadSystemColors(int[] systemColors)
|
||||||
throws HeadlessException {
|
throws HeadlessException {
|
||||||
if (GraphicsEnvironment.isHeadless()){
|
GraphicsEnvironment.checkHeadless();
|
||||||
throw new HeadlessException();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -504,10 +501,7 @@ public abstract class Toolkit {
|
||||||
*/
|
*/
|
||||||
public void setDynamicLayout(boolean dynamic)
|
public void setDynamicLayout(boolean dynamic)
|
||||||
throws HeadlessException {
|
throws HeadlessException {
|
||||||
if (GraphicsEnvironment.isHeadless()){
|
GraphicsEnvironment.checkHeadless();
|
||||||
throw new HeadlessException();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -531,9 +525,8 @@ public abstract class Toolkit {
|
||||||
*/
|
*/
|
||||||
protected boolean isDynamicLayoutSet()
|
protected boolean isDynamicLayoutSet()
|
||||||
throws HeadlessException {
|
throws HeadlessException {
|
||||||
if (GraphicsEnvironment.isHeadless()){
|
GraphicsEnvironment.checkHeadless();
|
||||||
throw new HeadlessException();
|
|
||||||
}
|
|
||||||
if (this != Toolkit.getDefaultToolkit()) {
|
if (this != Toolkit.getDefaultToolkit()) {
|
||||||
return Toolkit.getDefaultToolkit().isDynamicLayoutSet();
|
return Toolkit.getDefaultToolkit().isDynamicLayoutSet();
|
||||||
} else {
|
} else {
|
||||||
|
@ -569,9 +562,8 @@ public abstract class Toolkit {
|
||||||
*/
|
*/
|
||||||
public boolean isDynamicLayoutActive()
|
public boolean isDynamicLayoutActive()
|
||||||
throws HeadlessException {
|
throws HeadlessException {
|
||||||
if (GraphicsEnvironment.isHeadless()){
|
GraphicsEnvironment.checkHeadless();
|
||||||
throw new HeadlessException();
|
|
||||||
}
|
|
||||||
if (this != Toolkit.getDefaultToolkit()) {
|
if (this != Toolkit.getDefaultToolkit()) {
|
||||||
return Toolkit.getDefaultToolkit().isDynamicLayoutActive();
|
return Toolkit.getDefaultToolkit().isDynamicLayoutActive();
|
||||||
} else {
|
} else {
|
||||||
|
@ -615,9 +607,7 @@ public abstract class Toolkit {
|
||||||
*/
|
*/
|
||||||
public Insets getScreenInsets(GraphicsConfiguration gc)
|
public Insets getScreenInsets(GraphicsConfiguration gc)
|
||||||
throws HeadlessException {
|
throws HeadlessException {
|
||||||
if (GraphicsEnvironment.isHeadless()){
|
GraphicsEnvironment.checkHeadless();
|
||||||
throw new HeadlessException();
|
|
||||||
}
|
|
||||||
if (this != Toolkit.getDefaultToolkit()) {
|
if (this != Toolkit.getDefaultToolkit()) {
|
||||||
return Toolkit.getDefaultToolkit().getScreenInsets(gc);
|
return Toolkit.getDefaultToolkit().getScreenInsets(gc);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1200,10 +1190,7 @@ public abstract class Toolkit {
|
||||||
* security manager's <code>checkPermission</code> method with a <code>
|
* security manager's <code>checkPermission</code> method with a <code>
|
||||||
* RuntimePermission("queuePrintJob")</code> permission.
|
* RuntimePermission("queuePrintJob")</code> permission.
|
||||||
*
|
*
|
||||||
* @param frame the parent of the print dialog. May be null if and only
|
* @param frame the parent of the print dialog. May not be null.
|
||||||
* if jobAttributes is not null and jobAttributes.getDialog()
|
|
||||||
* returns JobAttributes.DialogType.NONE or
|
|
||||||
* JobAttributes.DialogType.COMMON.
|
|
||||||
* @param jobtitle the title of the PrintJob. A null title is equivalent
|
* @param jobtitle the title of the PrintJob. A null title is equivalent
|
||||||
* to "".
|
* to "".
|
||||||
* @param jobAttributes a set of job attributes which will control the
|
* @param jobAttributes a set of job attributes which will control the
|
||||||
|
@ -1359,9 +1346,8 @@ public abstract class Toolkit {
|
||||||
* @since 1.4
|
* @since 1.4
|
||||||
*/
|
*/
|
||||||
public Clipboard getSystemSelection() throws HeadlessException {
|
public Clipboard getSystemSelection() throws HeadlessException {
|
||||||
if (GraphicsEnvironment.isHeadless()){
|
GraphicsEnvironment.checkHeadless();
|
||||||
throw new HeadlessException();
|
|
||||||
}
|
|
||||||
if (this != Toolkit.getDefaultToolkit()) {
|
if (this != Toolkit.getDefaultToolkit()) {
|
||||||
return Toolkit.getDefaultToolkit().getSystemSelection();
|
return Toolkit.getDefaultToolkit().getSystemSelection();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1391,9 +1377,7 @@ public abstract class Toolkit {
|
||||||
* @since JDK1.1
|
* @since JDK1.1
|
||||||
*/
|
*/
|
||||||
public int getMenuShortcutKeyMask() throws HeadlessException {
|
public int getMenuShortcutKeyMask() throws HeadlessException {
|
||||||
if (GraphicsEnvironment.isHeadless()){
|
GraphicsEnvironment.checkHeadless();
|
||||||
throw new HeadlessException();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Event.CTRL_MASK;
|
return Event.CTRL_MASK;
|
||||||
}
|
}
|
||||||
|
@ -1418,7 +1402,10 @@ public abstract class Toolkit {
|
||||||
* @since 1.3
|
* @since 1.3
|
||||||
*/
|
*/
|
||||||
public boolean getLockingKeyState(int keyCode)
|
public boolean getLockingKeyState(int keyCode)
|
||||||
throws UnsupportedOperationException {
|
throws UnsupportedOperationException
|
||||||
|
{
|
||||||
|
GraphicsEnvironment.checkHeadless();
|
||||||
|
|
||||||
if (! (keyCode == KeyEvent.VK_CAPS_LOCK || keyCode == KeyEvent.VK_NUM_LOCK ||
|
if (! (keyCode == KeyEvent.VK_CAPS_LOCK || keyCode == KeyEvent.VK_NUM_LOCK ||
|
||||||
keyCode == KeyEvent.VK_SCROLL_LOCK || keyCode == KeyEvent.VK_KANA_LOCK)) {
|
keyCode == KeyEvent.VK_SCROLL_LOCK || keyCode == KeyEvent.VK_KANA_LOCK)) {
|
||||||
throw new IllegalArgumentException("invalid key for Toolkit.getLockingKeyState");
|
throw new IllegalArgumentException("invalid key for Toolkit.getLockingKeyState");
|
||||||
|
@ -1449,7 +1436,10 @@ public abstract class Toolkit {
|
||||||
* @since 1.3
|
* @since 1.3
|
||||||
*/
|
*/
|
||||||
public void setLockingKeyState(int keyCode, boolean on)
|
public void setLockingKeyState(int keyCode, boolean on)
|
||||||
throws UnsupportedOperationException {
|
throws UnsupportedOperationException
|
||||||
|
{
|
||||||
|
GraphicsEnvironment.checkHeadless();
|
||||||
|
|
||||||
if (! (keyCode == KeyEvent.VK_CAPS_LOCK || keyCode == KeyEvent.VK_NUM_LOCK ||
|
if (! (keyCode == KeyEvent.VK_CAPS_LOCK || keyCode == KeyEvent.VK_NUM_LOCK ||
|
||||||
keyCode == KeyEvent.VK_SCROLL_LOCK || keyCode == KeyEvent.VK_KANA_LOCK)) {
|
keyCode == KeyEvent.VK_SCROLL_LOCK || keyCode == KeyEvent.VK_KANA_LOCK)) {
|
||||||
throw new IllegalArgumentException("invalid key for Toolkit.setLockingKeyState");
|
throw new IllegalArgumentException("invalid key for Toolkit.setLockingKeyState");
|
||||||
|
@ -1523,9 +1513,8 @@ public abstract class Toolkit {
|
||||||
*/
|
*/
|
||||||
public Dimension getBestCursorSize(int preferredWidth,
|
public Dimension getBestCursorSize(int preferredWidth,
|
||||||
int preferredHeight) throws HeadlessException {
|
int preferredHeight) throws HeadlessException {
|
||||||
if (GraphicsEnvironment.isHeadless()){
|
GraphicsEnvironment.checkHeadless();
|
||||||
throw new HeadlessException();
|
|
||||||
}
|
|
||||||
// Override to implement custom cursor support.
|
// Override to implement custom cursor support.
|
||||||
if (this != Toolkit.getDefaultToolkit()) {
|
if (this != Toolkit.getDefaultToolkit()) {
|
||||||
return Toolkit.getDefaultToolkit().
|
return Toolkit.getDefaultToolkit().
|
||||||
|
@ -1553,9 +1542,8 @@ public abstract class Toolkit {
|
||||||
* @since 1.2
|
* @since 1.2
|
||||||
*/
|
*/
|
||||||
public int getMaximumCursorColors() throws HeadlessException {
|
public int getMaximumCursorColors() throws HeadlessException {
|
||||||
if (GraphicsEnvironment.isHeadless()){
|
GraphicsEnvironment.checkHeadless();
|
||||||
throw new HeadlessException();
|
|
||||||
}
|
|
||||||
// Override to implement custom cursor support.
|
// Override to implement custom cursor support.
|
||||||
if (this != Toolkit.getDefaultToolkit()) {
|
if (this != Toolkit.getDefaultToolkit()) {
|
||||||
return Toolkit.getDefaultToolkit().getMaximumCursorColors();
|
return Toolkit.getDefaultToolkit().getMaximumCursorColors();
|
||||||
|
@ -1605,9 +1593,8 @@ public abstract class Toolkit {
|
||||||
public boolean isFrameStateSupported(int state)
|
public boolean isFrameStateSupported(int state)
|
||||||
throws HeadlessException
|
throws HeadlessException
|
||||||
{
|
{
|
||||||
if (GraphicsEnvironment.isHeadless()){
|
GraphicsEnvironment.checkHeadless();
|
||||||
throw new HeadlessException();
|
|
||||||
}
|
|
||||||
if (this != Toolkit.getDefaultToolkit()) {
|
if (this != Toolkit.getDefaultToolkit()) {
|
||||||
return Toolkit.getDefaultToolkit().
|
return Toolkit.getDefaultToolkit().
|
||||||
isFrameStateSupported(state);
|
isFrameStateSupported(state);
|
||||||
|
@ -2614,9 +2601,8 @@ public abstract class Toolkit {
|
||||||
* @since 1.7
|
* @since 1.7
|
||||||
*/
|
*/
|
||||||
public boolean areExtraMouseButtonsEnabled() throws HeadlessException {
|
public boolean areExtraMouseButtonsEnabled() throws HeadlessException {
|
||||||
if (GraphicsEnvironment.isHeadless()){
|
GraphicsEnvironment.checkHeadless();
|
||||||
throw new HeadlessException();
|
|
||||||
}
|
|
||||||
return Toolkit.getDefaultToolkit().areExtraMouseButtonsEnabled();
|
return Toolkit.getDefaultToolkit().areExtraMouseButtonsEnabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
|
@ -32,7 +32,7 @@ import java.lang.annotation.*;
|
||||||
* constructor does not perform potentially unsafe operations on its
|
* constructor does not perform potentially unsafe operations on its
|
||||||
* varargs parameter. Applying this annotation to a method or
|
* varargs parameter. Applying this annotation to a method or
|
||||||
* constructor suppresses unchecked warnings about a
|
* constructor suppresses unchecked warnings about a
|
||||||
* <i>non-reifiable</i> variable-arity (vararg) type and suppresses
|
* <i>non-reifiable</i> variable arity (vararg) type and suppresses
|
||||||
* unchecked warnings about parameterized array creation at call
|
* unchecked warnings about parameterized array creation at call
|
||||||
* sites.
|
* sites.
|
||||||
*
|
*
|
||||||
|
@ -41,11 +41,10 @@ import java.lang.annotation.*;
|
||||||
* additional usage restrictions on this annotation type; it is a
|
* additional usage restrictions on this annotation type; it is a
|
||||||
* compile-time error if a method or constructor declaration is
|
* compile-time error if a method or constructor declaration is
|
||||||
* annotated with a {@code @SafeVarargs} annotation, and either:
|
* annotated with a {@code @SafeVarargs} annotation, and either:
|
||||||
|
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li> the declaration is a fixed-arity method or constructor
|
* <li> the declaration is a fixed arity method or constructor
|
||||||
*
|
*
|
||||||
* <li> the declaration is a variable-arity method that is neither
|
* <li> the declaration is a variable arity method that is neither
|
||||||
* {@code static} nor {@code final}.
|
* {@code static} nor {@code final}.
|
||||||
*
|
*
|
||||||
* </ul>
|
* </ul>
|
||||||
|
@ -55,15 +54,28 @@ import java.lang.annotation.*;
|
||||||
*
|
*
|
||||||
* <ul>
|
* <ul>
|
||||||
*
|
*
|
||||||
* <li> The variable-arity parameter has a reifiable element type,
|
* <li> The variable arity parameter has a reifiable element type,
|
||||||
* which includes primitive types, {@code Object}, and {@code String}.
|
* which includes primitive types, {@code Object}, and {@code String}.
|
||||||
* (The unchecked warnings this annotation type suppresses already do
|
* (The unchecked warnings this annotation type suppresses already do
|
||||||
* not occur for a reifiable element type.)
|
* not occur for a reifiable element type.)
|
||||||
*
|
*
|
||||||
* <li> The body of the method or constructor declaration performs
|
* <li> The body of the method or constructor declaration performs
|
||||||
* potentially unsafe operations, such as an assignment to an element
|
* potentially unsafe operations, such as an assignment to an element
|
||||||
* of the variable-arity parameter's array that generates an unchecked
|
* of the variable arity parameter's array that generates an unchecked
|
||||||
* warning.
|
* warning. Some unsafe operations do not trigger an unchecked
|
||||||
|
* warning. For example, the aliasing in
|
||||||
|
*
|
||||||
|
* <blockquote><pre>
|
||||||
|
* @SafeVarargs // Not actually safe!
|
||||||
|
* static void m(List<String>... stringLists) {
|
||||||
|
* Object[] array = stringLists;
|
||||||
|
* List<Integer> tmpList = Arrays.asList(42);
|
||||||
|
* array[0] = tmpList; // Semantically invalid, but compiles without warnings
|
||||||
|
* String s = stringLists[0].get(0); // Oh no, ClassCastException at runtime!
|
||||||
|
* }
|
||||||
|
* </pre></blockquote>
|
||||||
|
*
|
||||||
|
* leads to a {@code ClassCastException} at runtime.
|
||||||
*
|
*
|
||||||
* <p>Future versions of the platform may mandate compiler errors for
|
* <p>Future versions of the platform may mandate compiler errors for
|
||||||
* such unsafe operations.
|
* such unsafe operations.
|
||||||
|
|
|
@ -336,7 +336,10 @@ public class Throwable implements Serializable {
|
||||||
* Disabling of suppression should only occur in exceptional
|
* Disabling of suppression should only occur in exceptional
|
||||||
* circumstances where special requirements exist, such as a
|
* circumstances where special requirements exist, such as a
|
||||||
* virtual machine reusing exception objects under low-memory
|
* virtual machine reusing exception objects under low-memory
|
||||||
* situations.
|
* situations. Circumstances where a given exception object is
|
||||||
|
* repeatedly caught and rethrown, such as to implement control
|
||||||
|
* flow between two sub-systems, is another situation where
|
||||||
|
* immutable throwable objects would be appropriate.
|
||||||
*
|
*
|
||||||
* @param message the detail message.
|
* @param message the detail message.
|
||||||
* @param cause the cause. (A {@code null} value is permitted,
|
* @param cause the cause. (A {@code null} value is permitted,
|
||||||
|
@ -423,6 +426,18 @@ public class Throwable implements Serializable {
|
||||||
* {@link #Throwable(String,Throwable)}, this method cannot be called
|
* {@link #Throwable(String,Throwable)}, this method cannot be called
|
||||||
* even once.
|
* even once.
|
||||||
*
|
*
|
||||||
|
* <p>An example of using this method on a legacy throwable type
|
||||||
|
* without other support for setting the cause is:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* try {
|
||||||
|
* lowLevelOp();
|
||||||
|
* } catch (LowLevelException le) {
|
||||||
|
* throw (HighLevelException)
|
||||||
|
* new HighLevelException().initCause(le); // Legacy constructor
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
* @param cause the cause (which is saved for later retrieval by the
|
* @param cause the cause (which is saved for later retrieval by the
|
||||||
* {@link #getCause()} method). (A {@code null} value is
|
* {@link #getCause()} method). (A {@code null} value is
|
||||||
* permitted, and indicates that the cause is nonexistent or
|
* permitted, and indicates that the cause is nonexistent or
|
||||||
|
@ -762,7 +777,8 @@ public class Throwable implements Serializable {
|
||||||
* @see java.lang.Throwable#printStackTrace()
|
* @see java.lang.Throwable#printStackTrace()
|
||||||
*/
|
*/
|
||||||
public synchronized Throwable fillInStackTrace() {
|
public synchronized Throwable fillInStackTrace() {
|
||||||
if (stackTrace != null) {
|
if (stackTrace != null ||
|
||||||
|
backtrace != null /* Out of protocol state */ ) {
|
||||||
fillInStackTrace(0);
|
fillInStackTrace(0);
|
||||||
stackTrace = UNASSIGNED_STACK;
|
stackTrace = UNASSIGNED_STACK;
|
||||||
}
|
}
|
||||||
|
@ -788,7 +804,8 @@ public class Throwable implements Serializable {
|
||||||
* this throwable is permitted to return a zero-length array from this
|
* this throwable is permitted to return a zero-length array from this
|
||||||
* method. Generally speaking, the array returned by this method will
|
* method. Generally speaking, the array returned by this method will
|
||||||
* contain one element for every frame that would be printed by
|
* contain one element for every frame that would be printed by
|
||||||
* {@code printStackTrace}.
|
* {@code printStackTrace}. Writes to the returned array do not
|
||||||
|
* affect future calls to this method.
|
||||||
*
|
*
|
||||||
* @return an array of stack trace elements representing the stack trace
|
* @return an array of stack trace elements representing the stack trace
|
||||||
* pertaining to this throwable.
|
* pertaining to this throwable.
|
||||||
|
@ -801,7 +818,8 @@ public class Throwable implements Serializable {
|
||||||
private synchronized StackTraceElement[] getOurStackTrace() {
|
private synchronized StackTraceElement[] getOurStackTrace() {
|
||||||
// Initialize stack trace field with information from
|
// Initialize stack trace field with information from
|
||||||
// backtrace if this is the first call to this method
|
// backtrace if this is the first call to this method
|
||||||
if (stackTrace == UNASSIGNED_STACK) {
|
if (stackTrace == UNASSIGNED_STACK ||
|
||||||
|
(stackTrace == null && backtrace != null) /* Out of protocol state */) {
|
||||||
int depth = getStackTraceDepth();
|
int depth = getStackTraceDepth();
|
||||||
stackTrace = new StackTraceElement[depth];
|
stackTrace = new StackTraceElement[depth];
|
||||||
for (int i=0; i < depth; i++)
|
for (int i=0; i < depth; i++)
|
||||||
|
@ -849,7 +867,8 @@ public class Throwable implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (this.stackTrace == null) // Immutable stack
|
if (this.stackTrace == null && // Immutable stack
|
||||||
|
backtrace == null) // Test for out of protocol state
|
||||||
return;
|
return;
|
||||||
this.stackTrace = defensiveCopy;
|
this.stackTrace = defensiveCopy;
|
||||||
}
|
}
|
||||||
|
@ -971,8 +990,8 @@ public class Throwable implements Serializable {
|
||||||
/**
|
/**
|
||||||
* Appends the specified exception to the exceptions that were
|
* Appends the specified exception to the exceptions that were
|
||||||
* suppressed in order to deliver this exception. This method is
|
* suppressed in order to deliver this exception. This method is
|
||||||
* typically called (automatically and implicitly) by the {@code
|
* thread-safe and typically called (automatically and implicitly)
|
||||||
* try}-with-resources statement.
|
* by the {@code try}-with-resources statement.
|
||||||
*
|
*
|
||||||
* <p>The suppression behavior is enabled <em>unless</em> disabled
|
* <p>The suppression behavior is enabled <em>unless</em> disabled
|
||||||
* {@linkplain #Throwable(String, Throwable, boolean, boolean) via
|
* {@linkplain #Throwable(String, Throwable, boolean, boolean) via
|
||||||
|
@ -1043,7 +1062,9 @@ public class Throwable implements Serializable {
|
||||||
*
|
*
|
||||||
* If no exceptions were suppressed or {@linkplain
|
* If no exceptions were suppressed or {@linkplain
|
||||||
* #Throwable(String, Throwable, boolean, boolean) suppression is
|
* #Throwable(String, Throwable, boolean, boolean) suppression is
|
||||||
* disabled}, an empty array is returned.
|
* disabled}, an empty array is returned. This method is
|
||||||
|
* thread-safe. Writes to the returned array do not affect future
|
||||||
|
* calls to this method.
|
||||||
*
|
*
|
||||||
* @return an array containing all of the exceptions that were
|
* @return an array containing all of the exceptions that were
|
||||||
* suppressed to deliver this exception.
|
* suppressed to deliver this exception.
|
||||||
|
|
|
@ -27,6 +27,7 @@ package java.lang.invoke;
|
||||||
|
|
||||||
import sun.invoke.util.VerifyType;
|
import sun.invoke.util.VerifyType;
|
||||||
import sun.invoke.util.Wrapper;
|
import sun.invoke.util.Wrapper;
|
||||||
|
import sun.invoke.util.ValueConversions;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
import static java.lang.invoke.MethodHandleNatives.Constants.*;
|
||||||
import static java.lang.invoke.MethodHandleStatics.*;
|
import static java.lang.invoke.MethodHandleStatics.*;
|
||||||
|
@ -55,29 +56,35 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
this(target, newType, conv, null);
|
this(target, newType, conv, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getConversion() { return conversion; }
|
||||||
|
|
||||||
// TO DO: When adapting another MH with a null conversion, clone
|
// TO DO: When adapting another MH with a null conversion, clone
|
||||||
// the target and change its type, instead of adding another layer.
|
// the target and change its type, instead of adding another layer.
|
||||||
|
|
||||||
/** Can a JVM-level adapter directly implement the proposed
|
/** Can a JVM-level adapter directly implement the proposed
|
||||||
* argument conversions, as if by MethodHandles.convertArguments?
|
* argument conversions, as if by MethodHandles.convertArguments?
|
||||||
*/
|
*/
|
||||||
static boolean canPairwiseConvert(MethodType newType, MethodType oldType) {
|
static boolean canPairwiseConvert(MethodType newType, MethodType oldType, int level) {
|
||||||
// same number of args, of course
|
// same number of args, of course
|
||||||
int len = newType.parameterCount();
|
int len = newType.parameterCount();
|
||||||
if (len != oldType.parameterCount())
|
if (len != oldType.parameterCount())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Check return type. (Not much can be done with it.)
|
// Check return type.
|
||||||
Class<?> exp = newType.returnType();
|
Class<?> exp = newType.returnType();
|
||||||
Class<?> ret = oldType.returnType();
|
Class<?> ret = oldType.returnType();
|
||||||
if (!VerifyType.isNullConversion(ret, exp))
|
if (!VerifyType.isNullConversion(ret, exp)) {
|
||||||
|
if (!convOpSupported(OP_COLLECT_ARGS))
|
||||||
return false;
|
return false;
|
||||||
|
if (!canConvertArgument(ret, exp, level))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check args pairwise.
|
// Check args pairwise.
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
Class<?> src = newType.parameterType(i); // source type
|
Class<?> src = newType.parameterType(i); // source type
|
||||||
Class<?> dst = oldType.parameterType(i); // destination type
|
Class<?> dst = oldType.parameterType(i); // destination type
|
||||||
if (!canConvertArgument(src, dst))
|
if (!canConvertArgument(src, dst, level))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,11 +94,14 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
/** Can a JVM-level adapter directly implement the proposed
|
/** Can a JVM-level adapter directly implement the proposed
|
||||||
* argument conversion, as if by MethodHandles.convertArguments?
|
* argument conversion, as if by MethodHandles.convertArguments?
|
||||||
*/
|
*/
|
||||||
static boolean canConvertArgument(Class<?> src, Class<?> dst) {
|
static boolean canConvertArgument(Class<?> src, Class<?> dst, int level) {
|
||||||
// ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes,
|
// ? Retool this logic to use RETYPE_ONLY, CHECK_CAST, etc., as opcodes,
|
||||||
// so we don't need to repeat so much decision making.
|
// so we don't need to repeat so much decision making.
|
||||||
if (VerifyType.isNullConversion(src, dst)) {
|
if (VerifyType.isNullConversion(src, dst)) {
|
||||||
return true;
|
return true;
|
||||||
|
} else if (convOpSupported(OP_COLLECT_ARGS)) {
|
||||||
|
// If we can build filters, we can convert anything to anything.
|
||||||
|
return true;
|
||||||
} else if (src.isPrimitive()) {
|
} else if (src.isPrimitive()) {
|
||||||
if (dst.isPrimitive())
|
if (dst.isPrimitive())
|
||||||
return canPrimCast(src, dst);
|
return canPrimCast(src, dst);
|
||||||
|
@ -99,7 +109,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
return canBoxArgument(src, dst);
|
return canBoxArgument(src, dst);
|
||||||
} else {
|
} else {
|
||||||
if (dst.isPrimitive())
|
if (dst.isPrimitive())
|
||||||
return canUnboxArgument(src, dst);
|
return canUnboxArgument(src, dst, level);
|
||||||
else
|
else
|
||||||
return true; // any two refs can be interconverted
|
return true; // any two refs can be interconverted
|
||||||
}
|
}
|
||||||
|
@ -109,21 +119,20 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
* Create a JVM-level adapter method handle to conform the given method
|
* Create a JVM-level adapter method handle to conform the given method
|
||||||
* handle to the similar newType, using only pairwise argument conversions.
|
* handle to the similar newType, using only pairwise argument conversions.
|
||||||
* For each argument, convert incoming argument to the exact type needed.
|
* For each argument, convert incoming argument to the exact type needed.
|
||||||
* Only null conversions are allowed on the return value (until
|
* The argument conversions allowed are casting, boxing and unboxing,
|
||||||
* the JVM supports ricochet adapters).
|
|
||||||
* The argument conversions allowed are casting, unboxing,
|
|
||||||
* integral widening or narrowing, and floating point widening or narrowing.
|
* integral widening or narrowing, and floating point widening or narrowing.
|
||||||
* @param newType required call type
|
* @param newType required call type
|
||||||
* @param target original method handle
|
* @param target original method handle
|
||||||
|
* @param level which strength of conversion is allowed
|
||||||
* @return an adapter to the original handle with the desired new type,
|
* @return an adapter to the original handle with the desired new type,
|
||||||
* or the original target if the types are already identical
|
* or the original target if the types are already identical
|
||||||
* or null if the adaptation cannot be made
|
* or null if the adaptation cannot be made
|
||||||
*/
|
*/
|
||||||
static MethodHandle makePairwiseConvert(MethodType newType, MethodHandle target) {
|
static MethodHandle makePairwiseConvert(MethodType newType, MethodHandle target, int level) {
|
||||||
MethodType oldType = target.type();
|
MethodType oldType = target.type();
|
||||||
if (newType == oldType) return target;
|
if (newType == oldType) return target;
|
||||||
|
|
||||||
if (!canPairwiseConvert(newType, oldType))
|
if (!canPairwiseConvert(newType, oldType, level))
|
||||||
return null;
|
return null;
|
||||||
// (after this point, it is an assertion error to fail to convert)
|
// (after this point, it is an assertion error to fail to convert)
|
||||||
|
|
||||||
|
@ -138,9 +147,14 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Class<?> needReturn = newType.returnType();
|
||||||
|
Class<?> haveReturn = oldType.returnType();
|
||||||
|
boolean retConv = !VerifyType.isNullConversion(haveReturn, needReturn);
|
||||||
|
|
||||||
// Now build a chain of one or more adapters.
|
// Now build a chain of one or more adapters.
|
||||||
MethodHandle adapter = target;
|
MethodHandle adapter = target, adapter2;
|
||||||
MethodType midType = oldType.changeReturnType(newType.returnType());
|
MethodType midType = oldType;
|
||||||
for (int i = 0; i <= lastConv; i++) {
|
for (int i = 0; i <= lastConv; i++) {
|
||||||
Class<?> src = newType.parameterType(i); // source type
|
Class<?> src = newType.parameterType(i); // source type
|
||||||
Class<?> dst = midType.parameterType(i); // destination type
|
Class<?> dst = midType.parameterType(i); // destination type
|
||||||
|
@ -149,22 +163,23 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Work the current type backward toward the desired caller type:
|
// Work the current type backward toward the desired caller type:
|
||||||
if (i != lastConv) {
|
|
||||||
midType = midType.changeParameterType(i, src);
|
midType = midType.changeParameterType(i, src);
|
||||||
} else {
|
if (i == lastConv) {
|
||||||
// When doing the last (or only) real conversion,
|
// When doing the last (or only) real conversion,
|
||||||
// force all remaining null conversions to happen also.
|
// force all remaining null conversions to happen also.
|
||||||
assert(VerifyType.isNullConversion(newType, midType.changeParameterType(i, src)));
|
MethodType lastMidType = newType;
|
||||||
midType = newType;
|
if (retConv) lastMidType = lastMidType.changeReturnType(haveReturn);
|
||||||
|
assert(VerifyType.isNullConversion(lastMidType, midType));
|
||||||
|
midType = lastMidType;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tricky case analysis follows.
|
// Tricky case analysis follows.
|
||||||
// It parallels canConvertArgument() above.
|
// It parallels canConvertArgument() above.
|
||||||
if (src.isPrimitive()) {
|
if (src.isPrimitive()) {
|
||||||
if (dst.isPrimitive()) {
|
if (dst.isPrimitive()) {
|
||||||
adapter = makePrimCast(midType, adapter, i, dst);
|
adapter2 = makePrimCast(midType, adapter, i, dst);
|
||||||
} else {
|
} else {
|
||||||
adapter = makeBoxArgument(midType, adapter, i, dst);
|
adapter2 = makeBoxArgument(midType, adapter, i, src);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (dst.isPrimitive()) {
|
if (dst.isPrimitive()) {
|
||||||
|
@ -174,29 +189,53 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
// conversions supported by reflect.Method.invoke.
|
// conversions supported by reflect.Method.invoke.
|
||||||
// Those conversions require a big nest of if/then/else logic,
|
// Those conversions require a big nest of if/then/else logic,
|
||||||
// which we prefer to make a user responsibility.
|
// which we prefer to make a user responsibility.
|
||||||
adapter = makeUnboxArgument(midType, adapter, i, dst);
|
adapter2 = makeUnboxArgument(midType, adapter, i, dst, level);
|
||||||
} else {
|
} else {
|
||||||
// Simple reference conversion.
|
// Simple reference conversion.
|
||||||
// Note: Do not check for a class hierarchy relation
|
// Note: Do not check for a class hierarchy relation
|
||||||
// between src and dst. In all cases a 'null' argument
|
// between src and dst. In all cases a 'null' argument
|
||||||
// will pass the cast conversion.
|
// will pass the cast conversion.
|
||||||
adapter = makeCheckCast(midType, adapter, i, dst);
|
adapter2 = makeCheckCast(midType, adapter, i, dst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(adapter != null);
|
assert(adapter2 != null) : Arrays.asList(src, dst, midType, adapter, i, target, newType);
|
||||||
assert(adapter.type() == midType);
|
assert(adapter2.type() == midType);
|
||||||
|
adapter = adapter2;
|
||||||
|
}
|
||||||
|
if (retConv) {
|
||||||
|
adapter2 = makeReturnConversion(adapter, haveReturn, needReturn);
|
||||||
|
assert(adapter2 != null);
|
||||||
|
adapter = adapter2;
|
||||||
}
|
}
|
||||||
if (adapter.type() != newType) {
|
if (adapter.type() != newType) {
|
||||||
// Only trivial conversions remain.
|
// Only trivial conversions remain.
|
||||||
adapter = makeRetypeOnly(newType, adapter);
|
adapter2 = makeRetypeOnly(newType, adapter);
|
||||||
assert(adapter != null);
|
assert(adapter2 != null);
|
||||||
|
adapter = adapter2;
|
||||||
// Actually, that's because there were no non-trivial ones:
|
// Actually, that's because there were no non-trivial ones:
|
||||||
assert(lastConv == -1);
|
assert(lastConv == -1 || retConv);
|
||||||
}
|
}
|
||||||
assert(adapter.type() == newType);
|
assert(adapter.type() == newType);
|
||||||
return adapter;
|
return adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static MethodHandle makeReturnConversion(MethodHandle target, Class<?> haveReturn, Class<?> needReturn) {
|
||||||
|
MethodHandle adjustReturn;
|
||||||
|
if (haveReturn == void.class) {
|
||||||
|
// synthesize a zero value for the given void
|
||||||
|
Object zero = Wrapper.forBasicType(needReturn).zero();
|
||||||
|
adjustReturn = MethodHandles.constant(needReturn, zero);
|
||||||
|
} else {
|
||||||
|
MethodType needConversion = MethodType.methodType(needReturn, haveReturn);
|
||||||
|
adjustReturn = MethodHandles.identity(needReturn).asType(needConversion);
|
||||||
|
}
|
||||||
|
if (!canCollectArguments(adjustReturn.type(), target.type(), 0, false)) {
|
||||||
|
assert(MethodHandleNatives.workaroundWithoutRicochetFrames()); // this code is deprecated
|
||||||
|
throw new InternalError("NYI");
|
||||||
|
}
|
||||||
|
return makeCollectArguments(adjustReturn, target, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a JVM-level adapter method handle to permute the arguments
|
* Create a JVM-level adapter method handle to permute the arguments
|
||||||
* of the given method.
|
* of the given method.
|
||||||
|
@ -224,7 +263,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
if (argumentMap.length != oldType.parameterCount())
|
if (argumentMap.length != oldType.parameterCount())
|
||||||
throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap));
|
throw newIllegalArgumentException("bad permutation: "+Arrays.toString(argumentMap));
|
||||||
if (nullPermutation) {
|
if (nullPermutation) {
|
||||||
MethodHandle res = makePairwiseConvert(newType, target);
|
MethodHandle res = makePairwiseConvert(newType, target, 0);
|
||||||
// well, that was easy
|
// well, that was easy
|
||||||
if (res == null)
|
if (res == null)
|
||||||
throw newIllegalArgumentException("cannot convert pairwise: "+newType);
|
throw newIllegalArgumentException("cannot convert pairwise: "+newType);
|
||||||
|
@ -310,11 +349,25 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
return (spChange & CONV_STACK_MOVE_MASK) << CONV_STACK_MOVE_SHIFT;
|
return (spChange & CONV_STACK_MOVE_MASK) << CONV_STACK_MOVE_SHIFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int extractStackMove(int convOp) {
|
||||||
|
int spChange = convOp >> CONV_STACK_MOVE_SHIFT;
|
||||||
|
return spChange / MethodHandleNatives.JVM_STACK_MOVE_UNIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int extractStackMove(MethodHandle target) {
|
||||||
|
if (target instanceof AdapterMethodHandle) {
|
||||||
|
AdapterMethodHandle amh = (AdapterMethodHandle) target;
|
||||||
|
return extractStackMove(amh.getConversion());
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Construct an adapter conversion descriptor for a single-argument conversion. */
|
/** Construct an adapter conversion descriptor for a single-argument conversion. */
|
||||||
private static long makeConv(int convOp, int argnum, int src, int dest) {
|
private static long makeConv(int convOp, int argnum, int src, int dest) {
|
||||||
assert(src == (src & 0xF));
|
assert(src == (src & CONV_TYPE_MASK));
|
||||||
assert(dest == (dest & 0xF));
|
assert(dest == (dest & CONV_TYPE_MASK));
|
||||||
assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF);
|
assert(convOp >= OP_CHECK_CAST && convOp <= OP_PRIM_TO_REF || convOp == OP_COLLECT_ARGS);
|
||||||
int stackMove = type2size(dest) - type2size(src);
|
int stackMove = type2size(dest) - type2size(src);
|
||||||
return ((long) argnum << 32 |
|
return ((long) argnum << 32 |
|
||||||
(long) convOp << CONV_OP_SHIFT |
|
(long) convOp << CONV_OP_SHIFT |
|
||||||
|
@ -323,11 +376,10 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
insertStackMove(stackMove)
|
insertStackMove(stackMove)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
private static long makeConv(int convOp, int argnum, int stackMove) {
|
private static long makeDupConv(int convOp, int argnum, int stackMove) {
|
||||||
assert(convOp >= OP_DUP_ARGS && convOp <= OP_SPREAD_ARGS);
|
// simple argument motion, requiring one slot to specify
|
||||||
|
assert(convOp == OP_DUP_ARGS || convOp == OP_DROP_ARGS);
|
||||||
byte src = 0, dest = 0;
|
byte src = 0, dest = 0;
|
||||||
if (convOp >= OP_COLLECT_ARGS && convOp <= OP_SPREAD_ARGS)
|
|
||||||
src = dest = T_OBJECT;
|
|
||||||
return ((long) argnum << 32 |
|
return ((long) argnum << 32 |
|
||||||
(long) convOp << CONV_OP_SHIFT |
|
(long) convOp << CONV_OP_SHIFT |
|
||||||
(int) src << CONV_SRC_TYPE_SHIFT |
|
(int) src << CONV_SRC_TYPE_SHIFT |
|
||||||
|
@ -336,7 +388,8 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
private static long makeSwapConv(int convOp, int srcArg, byte type, int destSlot) {
|
private static long makeSwapConv(int convOp, int srcArg, byte type, int destSlot) {
|
||||||
assert(convOp >= OP_SWAP_ARGS && convOp <= OP_ROT_ARGS);
|
// more complex argument motion, requiring two slots to specify
|
||||||
|
assert(convOp == OP_SWAP_ARGS || convOp == OP_ROT_ARGS);
|
||||||
return ((long) srcArg << 32 |
|
return ((long) srcArg << 32 |
|
||||||
(long) convOp << CONV_OP_SHIFT |
|
(long) convOp << CONV_OP_SHIFT |
|
||||||
(int) type << CONV_SRC_TYPE_SHIFT |
|
(int) type << CONV_SRC_TYPE_SHIFT |
|
||||||
|
@ -344,6 +397,18 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
(int) destSlot << CONV_VMINFO_SHIFT
|
(int) destSlot << CONV_VMINFO_SHIFT
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
private static long makeSpreadConv(int convOp, int argnum, int src, int dest, int stackMove) {
|
||||||
|
// spreading or collecting, at a particular slot location
|
||||||
|
assert(convOp == OP_SPREAD_ARGS || convOp == OP_COLLECT_ARGS || convOp == OP_FOLD_ARGS);
|
||||||
|
// src = spread ? T_OBJECT (for array) : common type of collected args (else void)
|
||||||
|
// dest = spread ? element type of array : result type of collector (can be void)
|
||||||
|
return ((long) argnum << 32 |
|
||||||
|
(long) convOp << CONV_OP_SHIFT |
|
||||||
|
(int) src << CONV_SRC_TYPE_SHIFT |
|
||||||
|
(int) dest << CONV_DEST_TYPE_SHIFT |
|
||||||
|
insertStackMove(stackMove)
|
||||||
|
);
|
||||||
|
}
|
||||||
private static long makeConv(int convOp) {
|
private static long makeConv(int convOp) {
|
||||||
assert(convOp == OP_RETYPE_ONLY || convOp == OP_RETYPE_RAW);
|
assert(convOp == OP_RETYPE_ONLY || convOp == OP_RETYPE_RAW);
|
||||||
return ((long)-1 << 32) | (convOp << CONV_OP_SHIFT); // stackMove, src, dst all zero
|
return ((long)-1 << 32) | (convOp << CONV_OP_SHIFT); // stackMove, src, dst all zero
|
||||||
|
@ -570,14 +635,10 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
static boolean canPrimCast(Class<?> src, Class<?> dst) {
|
static boolean canPrimCast(Class<?> src, Class<?> dst) {
|
||||||
if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) {
|
if (src == dst || !src.isPrimitive() || !dst.isPrimitive()) {
|
||||||
return false;
|
return false;
|
||||||
} else if (Wrapper.forPrimitiveType(dst).isFloating()) {
|
|
||||||
// both must be floating types
|
|
||||||
return Wrapper.forPrimitiveType(src).isFloating();
|
|
||||||
} else {
|
} else {
|
||||||
// both are integral, and all combinations work fine
|
boolean sflt = Wrapper.forPrimitiveType(src).isFloating();
|
||||||
assert(Wrapper.forPrimitiveType(src).isIntegral() &&
|
boolean dflt = Wrapper.forPrimitiveType(dst).isFloating();
|
||||||
Wrapper.forPrimitiveType(dst).isIntegral());
|
return !(sflt | dflt); // no float support at present
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -589,6 +650,29 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
*/
|
*/
|
||||||
static MethodHandle makePrimCast(MethodType newType, MethodHandle target,
|
static MethodHandle makePrimCast(MethodType newType, MethodHandle target,
|
||||||
int arg, Class<?> convType) {
|
int arg, Class<?> convType) {
|
||||||
|
Class<?> src = newType.parameterType(arg);
|
||||||
|
if (canPrimCast(src, convType))
|
||||||
|
return makePrimCastOnly(newType, target, arg, convType);
|
||||||
|
Class<?> dst = convType;
|
||||||
|
boolean sflt = Wrapper.forPrimitiveType(src).isFloating();
|
||||||
|
boolean dflt = Wrapper.forPrimitiveType(dst).isFloating();
|
||||||
|
if (sflt | dflt) {
|
||||||
|
MethodHandle convMethod;
|
||||||
|
if (sflt)
|
||||||
|
convMethod = ((src == double.class)
|
||||||
|
? ValueConversions.convertFromDouble(dst)
|
||||||
|
: ValueConversions.convertFromFloat(dst));
|
||||||
|
else
|
||||||
|
convMethod = ((dst == double.class)
|
||||||
|
? ValueConversions.convertToDouble(src)
|
||||||
|
: ValueConversions.convertToFloat(src));
|
||||||
|
long conv = makeConv(OP_COLLECT_ARGS, arg, basicType(src), basicType(dst));
|
||||||
|
return new AdapterMethodHandle(target, newType, conv, convMethod);
|
||||||
|
}
|
||||||
|
throw new InternalError("makePrimCast");
|
||||||
|
}
|
||||||
|
static MethodHandle makePrimCastOnly(MethodType newType, MethodHandle target,
|
||||||
|
int arg, Class<?> convType) {
|
||||||
MethodType oldType = target.type();
|
MethodType oldType = target.type();
|
||||||
if (!canPrimCast(newType, oldType, arg, convType))
|
if (!canPrimCast(newType, oldType, arg, convType))
|
||||||
return null;
|
return null;
|
||||||
|
@ -602,7 +686,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
* The convType is the unboxed type; it can be either a primitive or wrapper.
|
* The convType is the unboxed type; it can be either a primitive or wrapper.
|
||||||
*/
|
*/
|
||||||
static boolean canUnboxArgument(MethodType newType, MethodType targetType,
|
static boolean canUnboxArgument(MethodType newType, MethodType targetType,
|
||||||
int arg, Class<?> convType) {
|
int arg, Class<?> convType, int level) {
|
||||||
if (!convOpSupported(OP_REF_TO_PRIM)) return false;
|
if (!convOpSupported(OP_REF_TO_PRIM)) return false;
|
||||||
Class<?> src = newType.parameterType(arg);
|
Class<?> src = newType.parameterType(arg);
|
||||||
Class<?> dst = targetType.parameterType(arg);
|
Class<?> dst = targetType.parameterType(arg);
|
||||||
|
@ -616,21 +700,31 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
return (diff == arg+1); // arg is sole non-trivial diff
|
return (diff == arg+1); // arg is sole non-trivial diff
|
||||||
}
|
}
|
||||||
/** Can an primitive unboxing adapter validly convert src to dst? */
|
/** Can an primitive unboxing adapter validly convert src to dst? */
|
||||||
static boolean canUnboxArgument(Class<?> src, Class<?> dst) {
|
static boolean canUnboxArgument(Class<?> src, Class<?> dst, int level) {
|
||||||
return (!src.isPrimitive() && Wrapper.asPrimitiveType(dst).isPrimitive());
|
assert(dst.isPrimitive());
|
||||||
|
// if we have JVM support for boxing, we can also do complex unboxing
|
||||||
|
if (convOpSupported(OP_PRIM_TO_REF)) return true;
|
||||||
|
Wrapper dw = Wrapper.forPrimitiveType(dst);
|
||||||
|
// Level 0 means cast and unbox. This works on any reference.
|
||||||
|
if (level == 0) return !src.isPrimitive();
|
||||||
|
assert(level >= 0 && level <= 2);
|
||||||
|
// Levels 1 and 2 allow widening and/or narrowing conversions.
|
||||||
|
// These are not supported directly by the JVM.
|
||||||
|
// But if the input reference is monomorphic, we can do it.
|
||||||
|
return dw.wrapperType() == src;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Factory method: Unbox the given argument.
|
/** Factory method: Unbox the given argument.
|
||||||
* Return null if this cannot be done.
|
* Return null if this cannot be done.
|
||||||
*/
|
*/
|
||||||
static MethodHandle makeUnboxArgument(MethodType newType, MethodHandle target,
|
static MethodHandle makeUnboxArgument(MethodType newType, MethodHandle target,
|
||||||
int arg, Class<?> convType) {
|
int arg, Class<?> convType, int level) {
|
||||||
MethodType oldType = target.type();
|
MethodType oldType = target.type();
|
||||||
Class<?> src = newType.parameterType(arg);
|
Class<?> src = newType.parameterType(arg);
|
||||||
Class<?> dst = oldType.parameterType(arg);
|
Class<?> dst = oldType.parameterType(arg);
|
||||||
Class<?> boxType = Wrapper.asWrapperType(convType);
|
Class<?> boxType = Wrapper.asWrapperType(convType);
|
||||||
Class<?> primType = Wrapper.asPrimitiveType(convType);
|
Class<?> primType = Wrapper.asPrimitiveType(convType);
|
||||||
if (!canUnboxArgument(newType, oldType, arg, convType))
|
if (!canUnboxArgument(newType, oldType, arg, convType, level))
|
||||||
return null;
|
return null;
|
||||||
MethodType castDone = newType;
|
MethodType castDone = newType;
|
||||||
if (!VerifyType.isNullConversion(src, boxType))
|
if (!VerifyType.isNullConversion(src, boxType))
|
||||||
|
@ -642,20 +736,47 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
return makeCheckCast(newType, adapter, arg, boxType);
|
return makeCheckCast(newType, adapter, arg, boxType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Can a boxing conversion validly convert src to dst? */
|
||||||
|
static boolean canBoxArgument(MethodType newType, MethodType targetType,
|
||||||
|
int arg, Class<?> convType) {
|
||||||
|
if (!convOpSupported(OP_PRIM_TO_REF)) return false;
|
||||||
|
Class<?> src = newType.parameterType(arg);
|
||||||
|
Class<?> dst = targetType.parameterType(arg);
|
||||||
|
Class<?> boxType = Wrapper.asWrapperType(convType);
|
||||||
|
convType = Wrapper.asPrimitiveType(convType);
|
||||||
|
if (!canCheckCast(boxType, dst)
|
||||||
|
|| boxType == convType
|
||||||
|
|| !VerifyType.isNullConversion(src, convType))
|
||||||
|
return false;
|
||||||
|
int diff = diffTypes(newType, targetType, false);
|
||||||
|
return (diff == arg+1); // arg is sole non-trivial diff
|
||||||
|
}
|
||||||
|
|
||||||
/** Can an primitive boxing adapter validly convert src to dst? */
|
/** Can an primitive boxing adapter validly convert src to dst? */
|
||||||
static boolean canBoxArgument(Class<?> src, Class<?> dst) {
|
static boolean canBoxArgument(Class<?> src, Class<?> dst) {
|
||||||
if (!convOpSupported(OP_PRIM_TO_REF)) return false;
|
if (!convOpSupported(OP_PRIM_TO_REF)) return false;
|
||||||
throw new UnsupportedOperationException("NYI");
|
return (src.isPrimitive() && !dst.isPrimitive());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Factory method: Unbox the given argument.
|
/** Factory method: Box the given argument.
|
||||||
* Return null if this cannot be done.
|
* Return null if this cannot be done.
|
||||||
*/
|
*/
|
||||||
static MethodHandle makeBoxArgument(MethodType newType, MethodHandle target,
|
static MethodHandle makeBoxArgument(MethodType newType, MethodHandle target,
|
||||||
int arg, Class<?> convType) {
|
int arg, Class<?> convType) {
|
||||||
// this is difficult to do in the JVM because it must GC
|
MethodType oldType = target.type();
|
||||||
|
Class<?> src = newType.parameterType(arg);
|
||||||
|
Class<?> dst = oldType.parameterType(arg);
|
||||||
|
Class<?> boxType = Wrapper.asWrapperType(convType);
|
||||||
|
Class<?> primType = Wrapper.asPrimitiveType(convType);
|
||||||
|
if (!canBoxArgument(newType, oldType, arg, convType)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (!VerifyType.isNullConversion(boxType, dst))
|
||||||
|
target = makeCheckCast(oldType.changeParameterType(arg, boxType), target, arg, dst);
|
||||||
|
MethodHandle boxerMethod = ValueConversions.box(Wrapper.forPrimitiveType(primType));
|
||||||
|
long conv = makeConv(OP_PRIM_TO_REF, arg, basicType(primType), T_OBJECT);
|
||||||
|
return new AdapterMethodHandle(target, newType, conv, boxerMethod);
|
||||||
|
}
|
||||||
|
|
||||||
/** Can an adapter simply drop arguments to convert the target to newType? */
|
/** Can an adapter simply drop arguments to convert the target to newType? */
|
||||||
static boolean canDropArguments(MethodType newType, MethodType targetType,
|
static boolean canDropArguments(MethodType newType, MethodType targetType,
|
||||||
|
@ -699,7 +820,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
int slotCount = keep1InSlot - dropSlot;
|
int slotCount = keep1InSlot - dropSlot;
|
||||||
assert(slotCount >= dropArgCount);
|
assert(slotCount >= dropArgCount);
|
||||||
assert(target.type().parameterSlotCount() + slotCount == newType.parameterSlotCount());
|
assert(target.type().parameterSlotCount() + slotCount == newType.parameterSlotCount());
|
||||||
long conv = makeConv(OP_DROP_ARGS, dropArgPos + dropArgCount - 1, -slotCount);
|
long conv = makeDupConv(OP_DROP_ARGS, dropArgPos + dropArgCount - 1, -slotCount);
|
||||||
return new AdapterMethodHandle(target, newType, conv);
|
return new AdapterMethodHandle(target, newType, conv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -739,7 +860,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
int keep1InSlot = newType.parameterSlotDepth(dupArgPos);
|
int keep1InSlot = newType.parameterSlotDepth(dupArgPos);
|
||||||
int slotCount = keep1InSlot - dupSlot;
|
int slotCount = keep1InSlot - dupSlot;
|
||||||
assert(target.type().parameterSlotCount() - slotCount == newType.parameterSlotCount());
|
assert(target.type().parameterSlotCount() - slotCount == newType.parameterSlotCount());
|
||||||
long conv = makeConv(OP_DUP_ARGS, dupArgPos + dupArgCount - 1, slotCount);
|
long conv = makeDupConv(OP_DUP_ARGS, dupArgPos + dupArgCount - 1, slotCount);
|
||||||
return new AdapterMethodHandle(target, newType, conv);
|
return new AdapterMethodHandle(target, newType, conv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -900,7 +1021,7 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
for (int i = 0; i < spreadArgCount; i++) {
|
for (int i = 0; i < spreadArgCount; i++) {
|
||||||
Class<?> src = VerifyType.spreadArgElementType(spreadArgType, i);
|
Class<?> src = VerifyType.spreadArgElementType(spreadArgType, i);
|
||||||
Class<?> dst = targetType.parameterType(spreadArgPos + i);
|
Class<?> dst = targetType.parameterType(spreadArgPos + i);
|
||||||
if (src == null || !VerifyType.isNullConversion(src, dst))
|
if (src == null || !canConvertArgument(src, dst, 1))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -910,24 +1031,100 @@ class AdapterMethodHandle extends BoundMethodHandle {
|
||||||
/** Factory method: Spread selected argument. */
|
/** Factory method: Spread selected argument. */
|
||||||
static MethodHandle makeSpreadArguments(MethodType newType, MethodHandle target,
|
static MethodHandle makeSpreadArguments(MethodType newType, MethodHandle target,
|
||||||
Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
|
Class<?> spreadArgType, int spreadArgPos, int spreadArgCount) {
|
||||||
|
// FIXME: Get rid of newType; derive new arguments from structure of spreadArgType
|
||||||
MethodType targetType = target.type();
|
MethodType targetType = target.type();
|
||||||
if (!canSpreadArguments(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount))
|
if (!canSpreadArguments(newType, targetType, spreadArgType, spreadArgPos, spreadArgCount))
|
||||||
return null;
|
return null;
|
||||||
|
// dest is not significant; remove?
|
||||||
|
int dest = T_VOID;
|
||||||
|
for (int i = 0; i < spreadArgCount; i++) {
|
||||||
|
Class<?> arg = VerifyType.spreadArgElementType(spreadArgType, i);
|
||||||
|
if (arg == null) arg = Object.class;
|
||||||
|
int dest2 = basicType(arg);
|
||||||
|
if (dest == T_VOID) dest = dest2;
|
||||||
|
else if (dest != dest2) dest = T_VOID;
|
||||||
|
if (dest == T_VOID) break;
|
||||||
|
targetType = targetType.changeParameterType(spreadArgPos + i, arg);
|
||||||
|
}
|
||||||
|
target = target.asType(targetType);
|
||||||
|
int arrayArgSize = 1; // always a reference
|
||||||
// in arglist: [0: ...keep1 | spos: spreadArg | spos+1: keep2... ]
|
// in arglist: [0: ...keep1 | spos: spreadArg | spos+1: keep2... ]
|
||||||
// out arglist: [0: ...keep1 | spos: spread... | spos+scount: keep2... ]
|
// out arglist: [0: ...keep1 | spos: spread... | spos+scount: keep2... ]
|
||||||
int keep2OutPos = spreadArgPos + spreadArgCount;
|
int keep2OutPos = spreadArgPos + spreadArgCount;
|
||||||
int spreadSlot = targetType.parameterSlotDepth(keep2OutPos);
|
int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos); // leading edge of |spread...|
|
||||||
int keep1OutSlot = targetType.parameterSlotDepth(spreadArgPos);
|
int spreadSlot = targetType.parameterSlotDepth(keep2OutPos); // trailing edge of |spread...|
|
||||||
int slotCount = keep1OutSlot - spreadSlot;
|
assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+arrayArgSize));
|
||||||
assert(spreadSlot == newType.parameterSlotDepth(spreadArgPos+1));
|
int slotCount = keep1OutSlot - spreadSlot; // slots in |spread...|
|
||||||
assert(slotCount >= spreadArgCount);
|
assert(slotCount >= spreadArgCount);
|
||||||
long conv = makeConv(OP_SPREAD_ARGS, spreadArgPos, slotCount-1);
|
int stackMove = - arrayArgSize + slotCount; // pop array, push N slots
|
||||||
|
long conv = makeSpreadConv(OP_SPREAD_ARGS, spreadArgPos, T_OBJECT, dest, stackMove);
|
||||||
MethodHandle res = new AdapterMethodHandle(target, newType, conv, spreadArgType);
|
MethodHandle res = new AdapterMethodHandle(target, newType, conv, spreadArgType);
|
||||||
assert(res.type().parameterType(spreadArgPos) == spreadArgType);
|
assert(res.type().parameterType(spreadArgPos) == spreadArgType);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TO DO: makeCollectArguments, makeFlyby, makeRicochet
|
/** Can an adapter collect a series of arguments, replacing them by zero or one results? */
|
||||||
|
static boolean canCollectArguments(MethodType targetType,
|
||||||
|
MethodType collectorType, int collectArgPos, boolean retainOriginalArgs) {
|
||||||
|
if (!convOpSupported(retainOriginalArgs ? OP_FOLD_ARGS : OP_COLLECT_ARGS)) return false;
|
||||||
|
int collectArgCount = collectorType.parameterCount();
|
||||||
|
Class<?> rtype = collectorType.returnType();
|
||||||
|
assert(rtype == void.class || targetType.parameterType(collectArgPos) == rtype)
|
||||||
|
// [(Object)Object[], (Object[])Object[], 0, 1]
|
||||||
|
: Arrays.asList(targetType, collectorType, collectArgPos, collectArgCount)
|
||||||
|
;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Factory method: Collect or filter selected argument(s). */
|
||||||
|
static MethodHandle makeCollectArguments(MethodHandle target,
|
||||||
|
MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) {
|
||||||
|
assert(canCollectArguments(target.type(), collector.type(), collectArgPos, retainOriginalArgs));
|
||||||
|
MethodType targetType = target.type();
|
||||||
|
MethodType collectorType = collector.type();
|
||||||
|
int collectArgCount = collectorType.parameterCount();
|
||||||
|
Class<?> collectValType = collectorType.returnType();
|
||||||
|
int collectValCount = (collectValType == void.class ? 0 : 1);
|
||||||
|
int collectValSlots = collectorType.returnSlotCount();
|
||||||
|
MethodType newType = targetType
|
||||||
|
.dropParameterTypes(collectArgPos, collectArgPos+collectValCount);
|
||||||
|
if (!retainOriginalArgs) {
|
||||||
|
newType = newType
|
||||||
|
.insertParameterTypes(collectArgPos, collectorType.parameterList());
|
||||||
|
} else {
|
||||||
|
// parameter types at the fold point must be the same
|
||||||
|
assert(diffParamTypes(newType, collectArgPos, targetType, collectValCount, collectArgCount, false) == 0)
|
||||||
|
: Arrays.asList(target, collector, collectArgPos, retainOriginalArgs);
|
||||||
|
}
|
||||||
|
// in arglist: [0: ...keep1 | cpos: collect... | cpos+cacount: keep2... ]
|
||||||
|
// out arglist: [0: ...keep1 | cpos: collectVal? | cpos+cvcount: keep2... ]
|
||||||
|
// out(retain): [0: ...keep1 | cpos: cV? coll... | cpos+cvc+cac: keep2... ]
|
||||||
|
int keep2InPos = collectArgPos + collectArgCount;
|
||||||
|
int keep1InSlot = newType.parameterSlotDepth(collectArgPos); // leading edge of |collect...|
|
||||||
|
int collectSlot = newType.parameterSlotDepth(keep2InPos); // trailing edge of |collect...|
|
||||||
|
int slotCount = keep1InSlot - collectSlot; // slots in |collect...|
|
||||||
|
assert(slotCount >= collectArgCount);
|
||||||
|
assert(collectSlot == targetType.parameterSlotDepth(
|
||||||
|
collectArgPos + collectValCount + (retainOriginalArgs ? collectArgCount : 0) ));
|
||||||
|
int dest = basicType(collectValType);
|
||||||
|
int src = T_VOID;
|
||||||
|
// src is not significant; remove?
|
||||||
|
for (int i = 0; i < collectArgCount; i++) {
|
||||||
|
int src2 = basicType(collectorType.parameterType(i));
|
||||||
|
if (src == T_VOID) src = src2;
|
||||||
|
else if (src != src2) src = T_VOID;
|
||||||
|
if (src == T_VOID) break;
|
||||||
|
}
|
||||||
|
int stackMove = collectValSlots; // push 0..2 results
|
||||||
|
if (!retainOriginalArgs) stackMove -= slotCount; // pop N arguments
|
||||||
|
int lastCollectArg = keep2InPos-1;
|
||||||
|
long conv = makeSpreadConv(retainOriginalArgs ? OP_FOLD_ARGS : OP_COLLECT_ARGS,
|
||||||
|
lastCollectArg, src, dest, stackMove);
|
||||||
|
MethodHandle res = new AdapterMethodHandle(target, newType, conv, collector);
|
||||||
|
assert(res.type().parameterList().subList(collectArgPos, collectArgPos+collectArgCount)
|
||||||
|
.equals(collector.type().parameterList()));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|
|
@ -273,9 +273,9 @@ public class CallSite {
|
||||||
Object binding;
|
Object binding;
|
||||||
info = maybeReBox(info);
|
info = maybeReBox(info);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
binding = bootstrapMethod.invokeGeneric(caller, name, type);
|
binding = bootstrapMethod.invoke(caller, name, type);
|
||||||
} else if (!info.getClass().isArray()) {
|
} else if (!info.getClass().isArray()) {
|
||||||
binding = bootstrapMethod.invokeGeneric(caller, name, type, info);
|
binding = bootstrapMethod.invoke(caller, name, type, info);
|
||||||
} else {
|
} else {
|
||||||
Object[] argv = (Object[]) info;
|
Object[] argv = (Object[]) info;
|
||||||
maybeReBoxElements(argv);
|
maybeReBoxElements(argv);
|
||||||
|
@ -283,10 +283,10 @@ public class CallSite {
|
||||||
throw new BootstrapMethodError("too many bootstrap method arguments");
|
throw new BootstrapMethodError("too many bootstrap method arguments");
|
||||||
MethodType bsmType = bootstrapMethod.type();
|
MethodType bsmType = bootstrapMethod.type();
|
||||||
if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class)
|
if (bsmType.parameterCount() == 4 && bsmType.parameterType(3) == Object[].class)
|
||||||
binding = bootstrapMethod.invokeGeneric(caller, name, type, argv);
|
binding = bootstrapMethod.invoke(caller, name, type, argv);
|
||||||
else
|
else
|
||||||
binding = MethodHandles.spreadInvoker(bsmType, 3)
|
binding = MethodHandles.spreadInvoker(bsmType, 3)
|
||||||
.invokeGeneric(bootstrapMethod, caller, name, type, argv);
|
.invoke(bootstrapMethod, caller, name, type, argv);
|
||||||
}
|
}
|
||||||
//System.out.println("BSM for "+name+type+" => "+binding);
|
//System.out.println("BSM for "+name+type+" => "+binding);
|
||||||
if (binding instanceof CallSite) {
|
if (binding instanceof CallSite) {
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue