Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openanolis
dragonwell8_hotspot
提交
c812e04e
D
dragonwell8_hotspot
项目概览
openanolis
/
dragonwell8_hotspot
通知
2
Star
2
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
dragonwell8_hotspot
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
c812e04e
编写于
9月 02, 2011
作者:
N
never
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
7071307: MethodHandle bimorphic inlining should consider the frequency
Reviewed-by: twisti, roland, kvn, iveresov
上级
3a1c76b6
变更
20
隐藏空白更改
内联
并排
Showing
20 changed file
with
346 addition
and
61 deletion
+346
-61
src/cpu/sparc/vm/methodHandles_sparc.cpp
src/cpu/sparc/vm/methodHandles_sparc.cpp
+9
-0
src/cpu/x86/vm/methodHandles_x86.cpp
src/cpu/x86/vm/methodHandles_x86.cpp
+7
-0
src/share/vm/ci/ciCallProfile.hpp
src/share/vm/ci/ciCallProfile.hpp
+11
-0
src/share/vm/ci/ciMethodHandle.cpp
src/share/vm/ci/ciMethodHandle.cpp
+17
-6
src/share/vm/ci/ciMethodHandle.hpp
src/share/vm/ci/ciMethodHandle.hpp
+22
-6
src/share/vm/ci/ciObject.cpp
src/share/vm/ci/ciObject.cpp
+12
-2
src/share/vm/classfile/javaClasses.cpp
src/share/vm/classfile/javaClasses.cpp
+20
-0
src/share/vm/classfile/javaClasses.hpp
src/share/vm/classfile/javaClasses.hpp
+28
-0
src/share/vm/classfile/systemDictionary.hpp
src/share/vm/classfile/systemDictionary.hpp
+9
-8
src/share/vm/classfile/vmSymbols.hpp
src/share/vm/classfile/vmSymbols.hpp
+7
-0
src/share/vm/oops/methodDataOop.hpp
src/share/vm/oops/methodDataOop.hpp
+9
-0
src/share/vm/opto/bytecodeInfo.cpp
src/share/vm/opto/bytecodeInfo.cpp
+15
-1
src/share/vm/opto/callGenerator.cpp
src/share/vm/opto/callGenerator.cpp
+19
-9
src/share/vm/opto/idealGraphPrinter.cpp
src/share/vm/opto/idealGraphPrinter.cpp
+10
-13
src/share/vm/opto/idealGraphPrinter.hpp
src/share/vm/opto/idealGraphPrinter.hpp
+2
-2
src/share/vm/opto/matcher.cpp
src/share/vm/opto/matcher.cpp
+3
-0
src/share/vm/prims/methodHandleWalk.cpp
src/share/vm/prims/methodHandleWalk.cpp
+116
-10
src/share/vm/prims/methodHandleWalk.hpp
src/share/vm/prims/methodHandleWalk.hpp
+12
-2
src/share/vm/prims/methodHandles.cpp
src/share/vm/prims/methodHandles.cpp
+13
-2
src/share/vm/prims/methodHandles.hpp
src/share/vm/prims/methodHandles.hpp
+5
-0
未找到文件。
src/cpu/sparc/vm/methodHandles_sparc.cpp
浏览文件 @
c812e04e
...
...
@@ -1262,6 +1262,15 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
}
break
;
case
_adapter_opt_profiling
:
if
(
java_lang_invoke_CountingMethodHandle
::
vmcount_offset_in_bytes
()
!=
0
)
{
Address
G3_mh_vmcount
(
G3_method_handle
,
java_lang_invoke_CountingMethodHandle
::
vmcount_offset_in_bytes
());
__
ld
(
G3_mh_vmcount
,
O1_scratch
);
__
add
(
O1_scratch
,
1
,
O1_scratch
);
__
st
(
O1_scratch
,
G3_mh_vmcount
);
}
// fall through
case
_adapter_retype_only
:
case
_adapter_retype_raw
:
// Immediately jump to the next MH layer:
...
...
src/cpu/x86/vm/methodHandles_x86.cpp
浏览文件 @
c812e04e
...
...
@@ -1343,6 +1343,13 @@ void MethodHandles::generate_method_handle_stub(MacroAssembler* _masm, MethodHan
}
break
;
case
_adapter_opt_profiling
:
if
(
java_lang_invoke_CountingMethodHandle
::
vmcount_offset_in_bytes
()
!=
0
)
{
Address
rcx_mh_vmcount
(
rcx_recv
,
java_lang_invoke_CountingMethodHandle
::
vmcount_offset_in_bytes
());
__
incrementl
(
rcx_mh_vmcount
);
}
// fall through
case
_adapter_retype_only
:
case
_adapter_retype_raw
:
// immediately jump to the next MH layer:
...
...
src/share/vm/ci/ciCallProfile.hpp
浏览文件 @
c812e04e
...
...
@@ -79,6 +79,17 @@ public:
assert
(
i
<
_limit
,
"out of Call Profile MorphismLimit"
);
return
_receiver
[
i
];
}
// Rescale the current profile based on the incoming scale
ciCallProfile
rescale
(
double
scale
)
{
assert
(
scale
>=
0
&&
scale
<=
1.0
,
"out of range"
);
ciCallProfile
call
=
*
this
;
call
.
_count
=
(
int
)(
call
.
_count
*
scale
);
for
(
int
i
=
0
;
i
<
_morphism
;
i
++
)
{
call
.
_receiver_count
[
i
]
=
(
int
)(
call
.
_receiver_count
[
i
]
*
scale
);
}
return
call
;
}
};
#endif // SHARE_VM_CI_CICALLPROFILE_HPP
src/share/vm/ci/ciMethodHandle.cpp
浏览文件 @
c812e04e
...
...
@@ -37,7 +37,7 @@
// ciMethodHandle::get_adapter
//
// Return an adapter for this MethodHandle.
ciMethod
*
ciMethodHandle
::
get_adapter_impl
(
bool
is_invokedynamic
)
const
{
ciMethod
*
ciMethodHandle
::
get_adapter_impl
(
bool
is_invokedynamic
)
{
VM_ENTRY_MARK
;
Handle
h
(
get_oop
());
methodHandle
callee
(
_callee
->
get_methodOop
());
...
...
@@ -73,7 +73,7 @@ ciMethod* ciMethodHandle::get_adapter_impl(bool is_invokedynamic) const {
// ciMethodHandle::get_adapter
//
// Return an adapter for this MethodHandle.
ciMethod
*
ciMethodHandle
::
get_adapter
(
bool
is_invokedynamic
)
const
{
ciMethod
*
ciMethodHandle
::
get_adapter
(
bool
is_invokedynamic
)
{
ciMethod
*
result
=
get_adapter_impl
(
is_invokedynamic
);
if
(
result
)
{
// Fake up the MDO maturity.
...
...
@@ -86,11 +86,22 @@ ciMethod* ciMethodHandle::get_adapter(bool is_invokedynamic) const {
}
#ifndef PRODUCT
// ------------------------------------------------------------------
// ciMethodHandle::print_impl
// ciMethodHandle::print_
chain_
impl
//
// Implementation of the print method.
void
ciMethodHandle
::
print_impl
(
outputStream
*
st
)
{
st
->
print
(
" type="
)
;
get_oop
()
->
print
(
);
void
ciMethodHandle
::
print_
chain_
impl
(
outputStream
*
st
)
{
ASSERT_IN_VM
;
MethodHandleChain
::
print
(
get_oop
()
);
}
// ------------------------------------------------------------------
// ciMethodHandle::print_chain
//
// Implementation of the print_chain method.
void
ciMethodHandle
::
print_chain
(
outputStream
*
st
)
{
GUARDED_VM_ENTRY
(
print_chain_impl
(
st
););
}
#endif
src/share/vm/ci/ciMethodHandle.hpp
浏览文件 @
c812e04e
...
...
@@ -37,19 +37,23 @@ private:
ciMethod
*
_callee
;
ciMethod
*
_caller
;
ciCallProfile
_profile
;
ciMethod
*
_method_handle_adapter
;
ciMethod
*
_invokedynamic_adapter
;
// Return an adapter for this MethodHandle.
ciMethod
*
get_adapter_impl
(
bool
is_invokedynamic
)
const
;
ciMethod
*
get_adapter
(
bool
is_invokedynamic
)
const
;
ciMethod
*
get_adapter_impl
(
bool
is_invokedynamic
);
ciMethod
*
get_adapter
(
bool
is_invokedynamic
);
protected:
void
print_
impl
(
outputStream
*
st
)
;
void
print_
chain_impl
(
outputStream
*
st
)
PRODUCT_RETURN
;
public:
ciMethodHandle
(
instanceHandle
h_i
)
:
ciInstance
(
h_i
),
_callee
(
NULL
),
_caller
(
NULL
)
_caller
(
NULL
),
_method_handle_adapter
(
NULL
),
_invokedynamic_adapter
(
NULL
)
{}
// What kind of ciObject is this?
...
...
@@ -60,10 +64,22 @@ public:
void
set_call_profile
(
ciCallProfile
profile
)
{
_profile
=
profile
;
}
// Return an adapter for a MethodHandle call.
ciMethod
*
get_method_handle_adapter
()
const
{
return
get_adapter
(
false
);
}
ciMethod
*
get_method_handle_adapter
()
{
if
(
_method_handle_adapter
==
NULL
)
{
_method_handle_adapter
=
get_adapter
(
false
);
}
return
_method_handle_adapter
;
}
// Return an adapter for an invokedynamic call.
ciMethod
*
get_invokedynamic_adapter
()
const
{
return
get_adapter
(
true
);
}
ciMethod
*
get_invokedynamic_adapter
()
{
if
(
_invokedynamic_adapter
==
NULL
)
{
_invokedynamic_adapter
=
get_adapter
(
true
);
}
return
_invokedynamic_adapter
;
}
void
print_chain
(
outputStream
*
st
=
tty
)
PRODUCT_RETURN
;
};
#endif // SHARE_VM_CI_CIMETHODHANDLE_HPP
src/share/vm/ci/ciObject.cpp
浏览文件 @
c812e04e
...
...
@@ -194,16 +194,26 @@ bool ciObject::can_be_constant() {
// ciObject::should_be_constant()
bool
ciObject
::
should_be_constant
()
{
if
(
ScavengeRootsInCode
>=
2
)
return
true
;
// force everybody to be a constant
if
(
!
JavaObjectsInPerm
&&
!
is_null_object
())
{
if
(
is_null_object
())
return
true
;
ciEnv
*
env
=
CURRENT_ENV
;
if
(
!
JavaObjectsInPerm
)
{
// We want Strings and Classes to be embeddable by default since
// they used to be in the perm world. Not all Strings used to be
// embeddable but there's no easy way to distinguish the interned
// from the regulars ones so just treat them all that way.
ciEnv
*
env
=
CURRENT_ENV
;
if
(
klass
()
==
env
->
String_klass
()
||
klass
()
==
env
->
Class_klass
())
{
return
true
;
}
}
if
(
EnableInvokeDynamic
&&
(
klass
()
->
is_subclass_of
(
env
->
MethodHandle_klass
())
||
klass
()
->
is_subclass_of
(
env
->
CallSite_klass
())))
{
assert
(
ScavengeRootsInCode
>=
1
,
"must be"
);
// We want to treat these aggressively.
return
true
;
}
return
handle
()
==
NULL
||
is_perm
();
}
...
...
src/share/vm/classfile/javaClasses.cpp
浏览文件 @
c812e04e
...
...
@@ -2324,6 +2324,8 @@ int java_lang_invoke_BoundMethodHandle::_vmargslot_offset;
int
java_lang_invoke_AdapterMethodHandle
::
_conversion_offset
;
int
java_lang_invoke_CountingMethodHandle
::
_vmcount_offset
;
void
java_lang_invoke_MethodHandle
::
compute_offsets
()
{
klassOop
k
=
SystemDictionary
::
MethodHandle_klass
();
if
(
k
!=
NULL
&&
EnableInvokeDynamic
)
{
...
...
@@ -2372,6 +2374,23 @@ void java_lang_invoke_AdapterMethodHandle::compute_offsets() {
}
}
void
java_lang_invoke_CountingMethodHandle
::
compute_offsets
()
{
klassOop
k
=
SystemDictionary
::
CountingMethodHandle_klass
();
if
(
k
!=
NULL
&&
EnableInvokeDynamic
)
{
compute_offset
(
_vmcount_offset
,
k
,
vmSymbols
::
vmcount_name
(),
vmSymbols
::
int_signature
(),
true
);
}
}
int
java_lang_invoke_CountingMethodHandle
::
vmcount
(
oop
mh
)
{
assert
(
is_instance
(
mh
),
"CMH only"
);
return
mh
->
int_field
(
_vmcount_offset
);
}
void
java_lang_invoke_CountingMethodHandle
::
set_vmcount
(
oop
mh
,
int
count
)
{
assert
(
is_instance
(
mh
),
"CMH only"
);
mh
->
int_field_put
(
_vmcount_offset
,
count
);
}
oop
java_lang_invoke_MethodHandle
::
type
(
oop
mh
)
{
return
mh
->
obj_field
(
_type_offset
);
}
...
...
@@ -3043,6 +3062,7 @@ void JavaClasses::compute_offsets() {
java_lang_invoke_MethodType
::
compute_offsets
();
java_lang_invoke_MethodTypeForm
::
compute_offsets
();
java_lang_invoke_CallSite
::
compute_offsets
();
java_lang_invoke_CountingMethodHandle
::
compute_offsets
();
}
java_security_AccessControlContext
::
compute_offsets
();
// Initialize reflection classes. The layouts of these classes
...
...
src/share/vm/classfile/javaClasses.hpp
浏览文件 @
c812e04e
...
...
@@ -981,6 +981,34 @@ class java_lang_invoke_AdapterMethodHandle: public java_lang_invoke_BoundMethodH
};
// A simple class that maintains an invocation count
class
java_lang_invoke_CountingMethodHandle
:
public
java_lang_invoke_MethodHandle
{
friend
class
JavaClasses
;
private:
static
int
_vmcount_offset
;
static
void
compute_offsets
();
public:
// Accessors
static
int
vmcount
(
oop
mh
);
static
void
set_vmcount
(
oop
mh
,
int
count
);
// Testers
static
bool
is_subclass
(
klassOop
klass
)
{
return
SystemDictionary
::
CountingMethodHandle_klass
()
!=
NULL
&&
Klass
::
cast
(
klass
)
->
is_subclass_of
(
SystemDictionary
::
CountingMethodHandle_klass
());
}
static
bool
is_instance
(
oop
obj
)
{
return
obj
!=
NULL
&&
is_subclass
(
obj
->
klass
());
}
// Accessors for code generation:
static
int
vmcount_offset_in_bytes
()
{
return
_vmcount_offset
;
}
};
// Interface to java.lang.invoke.MemberName objects
// (These are a private interface for Java code to query the class hierarchy.)
...
...
src/share/vm/classfile/systemDictionary.hpp
浏览文件 @
c812e04e
...
...
@@ -133,14 +133,14 @@ class SymbolPropertyTable;
template(reflect_Method_klass, java_lang_reflect_Method, Pre) \
template(reflect_Constructor_klass, java_lang_reflect_Constructor, Pre) \
\
/* NOTE: needed too early in bootstrapping process to have checks based on JDK version */
\
/* Universe::is_gte_jdk14x_version() is not set up by this point. */
\
/* It's okay if this turns out to be NULL in non-1.4 JDKs. */
\
template(reflect_MagicAccessorImpl_klass, sun_reflect_MagicAccessorImpl, Opt) \
template(reflect_MethodAccessorImpl_klass, sun_reflect_MethodAccessorImpl, Opt_Only_JDK14NewRef) \
template(reflect_ConstructorAccessorImpl_klass, sun_reflect_ConstructorAccessorImpl, Opt_Only_JDK14NewRef) \
template(reflect_DelegatingClassLoader_klass, sun_reflect_DelegatingClassLoader, Opt) \
template(reflect_ConstantPool_klass, sun_reflect_ConstantPool, Opt_Only_JDK15) \
/* NOTE: needed too early in bootstrapping process to have checks based on JDK version */
\
/* Universe::is_gte_jdk14x_version() is not set up by this point. */
\
/* It's okay if this turns out to be NULL in non-1.4 JDKs. */
\
template(reflect_MagicAccessorImpl_klass, sun_reflect_MagicAccessorImpl, Opt)
\
template(reflect_MethodAccessorImpl_klass, sun_reflect_MethodAccessorImpl, Opt_Only_JDK14NewRef)
\
template(reflect_ConstructorAccessorImpl_klass, sun_reflect_ConstructorAccessorImpl, Opt_Only_JDK14NewRef)
\
template(reflect_DelegatingClassLoader_klass, sun_reflect_DelegatingClassLoader, Opt)
\
template(reflect_ConstantPool_klass, sun_reflect_ConstantPool, Opt_Only_JDK15)
\
template(reflect_UnsafeStaticFieldAccessorImpl_klass, sun_reflect_UnsafeStaticFieldAccessorImpl, Opt_Only_JDK15) \
\
/* support for dynamic typing; it's OK if these are NULL in earlier JDKs */
\
...
...
@@ -155,6 +155,7 @@ class SymbolPropertyTable;
template(BootstrapMethodError_klass, java_lang_BootstrapMethodError, Pre_JSR292) \
template(WrongMethodTypeException_klass, java_lang_invoke_WrongMethodTypeException, Pre_JSR292) \
template(CallSite_klass, java_lang_invoke_CallSite, Pre_JSR292) \
template(CountingMethodHandle_klass, java_lang_invoke_CountingMethodHandle, Opt) \
template(ConstantCallSite_klass, java_lang_invoke_ConstantCallSite, Pre_JSR292) \
template(MutableCallSite_klass, java_lang_invoke_MutableCallSite, Pre_JSR292) \
template(VolatileCallSite_klass, java_lang_invoke_VolatileCallSite, Pre_JSR292) \
...
...
src/share/vm/classfile/vmSymbols.hpp
浏览文件 @
c812e04e
...
...
@@ -218,6 +218,7 @@
template(returnType_name, "returnType") \
template(signature_name, "signature") \
template(slot_name, "slot") \
template(selectAlternative_name, "selectAlternative") \
\
/* Support for annotations (JDK 1.5 and above) */
\
\
...
...
@@ -246,9 +247,11 @@
template(java_lang_invoke_MethodTypeForm_signature, "Ljava/lang/invoke/MethodTypeForm;") \
template(java_lang_invoke_MemberName, "java/lang/invoke/MemberName") \
template(java_lang_invoke_MethodHandleNatives, "java/lang/invoke/MethodHandleNatives") \
template(java_lang_invoke_MethodHandleImpl, "java/lang/invoke/MethodHandleImpl") \
template(java_lang_invoke_AdapterMethodHandle, "java/lang/invoke/AdapterMethodHandle") \
template(java_lang_invoke_BoundMethodHandle, "java/lang/invoke/BoundMethodHandle") \
template(java_lang_invoke_DirectMethodHandle, "java/lang/invoke/DirectMethodHandle") \
template(java_lang_invoke_CountingMethodHandle, "java/lang/invoke/CountingMethodHandle") \
/* internal up-calls made only by the JVM, via class sun.invoke.MethodHandleNatives: */
\
template(findMethodHandleType_name, "findMethodHandleType") \
template(findMethodHandleType_signature, "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;") \
...
...
@@ -263,6 +266,7 @@
template(setTarget_signature, "(Ljava/lang/invoke/MethodHandle;)V") \
NOT_LP64( do_alias(machine_word_signature, int_signature) ) \
LP64_ONLY( do_alias(machine_word_signature, long_signature) ) \
template(selectAlternative_signature, "(ZLjava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;)Ljava/lang/invoke/MethodHandle;") \
\
/* common method and field names */
\
template(object_initializer_name, "<init>") \
...
...
@@ -347,6 +351,7 @@
template(vmmethod_name, "vmmethod") \
template(vmtarget_name, "vmtarget") \
template(vmentry_name, "vmentry") \
template(vmcount_name, "vmcount") \
template(vmslots_name, "vmslots") \
template(vmlayout_name, "vmlayout") \
template(vmindex_name, "vmindex") \
...
...
@@ -910,6 +915,8 @@
do_intrinsic(_invokeVarargs, java_lang_invoke_MethodHandle, invokeVarargs_name, object_array_object_signature, F_R) \
do_intrinsic(_invokeDynamic, java_lang_invoke_InvokeDynamic, star_name, object_array_object_signature, F_SN) \
\
do_intrinsic(_selectAlternative, java_lang_invoke_MethodHandleImpl, selectAlternative_name, selectAlternative_signature, F_S) \
\
/* unboxing methods: */
\
do_intrinsic(_booleanValue, java_lang_Boolean, booleanValue_name, void_boolean_signature, F_R) \
do_name( booleanValue_name, "booleanValue") \
...
...
src/share/vm/oops/methodDataOop.hpp
浏览文件 @
c812e04e
...
...
@@ -600,6 +600,11 @@ public:
uint
taken
()
{
return
uint_at
(
taken_off_set
);
}
void
set_taken
(
uint
cnt
)
{
set_uint_at
(
taken_off_set
,
cnt
);
}
// Saturating counter
uint
inc_taken
()
{
uint
cnt
=
taken
()
+
1
;
...
...
@@ -926,6 +931,10 @@ public:
return
uint_at
(
not_taken_off_set
);
}
void
set_not_taken
(
uint
cnt
)
{
set_uint_at
(
not_taken_off_set
,
cnt
);
}
uint
inc_not_taken
()
{
uint
cnt
=
not_taken
()
+
1
;
// Did we wrap? Will compiler screw us??
...
...
src/share/vm/opto/bytecodeInfo.cpp
浏览文件 @
c812e04e
...
...
@@ -141,7 +141,21 @@ const char* InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_
assert
(
mha_profile
,
"must exist"
);
CounterData
*
cd
=
mha_profile
->
as_CounterData
();
invoke_count
=
cd
->
count
();
call_site_count
=
invoke_count
;
// use the same value
if
(
invoke_count
==
0
)
{
return
"method handle not reached"
;
}
if
(
_caller_jvms
!=
NULL
&&
_caller_jvms
->
method
()
!=
NULL
&&
_caller_jvms
->
method
()
->
method_data
()
!=
NULL
&&
!
_caller_jvms
->
method
()
->
method_data
()
->
is_empty
())
{
ciMethodData
*
mdo
=
_caller_jvms
->
method
()
->
method_data
();
ciProfileData
*
mha_profile
=
mdo
->
bci_to_data
(
_caller_jvms
->
bci
());
assert
(
mha_profile
,
"must exist"
);
CounterData
*
cd
=
mha_profile
->
as_CounterData
();
call_site_count
=
cd
->
count
();
}
else
{
call_site_count
=
invoke_count
;
// use the same value
}
}
assert
(
invoke_count
!=
0
,
"require invocation count greater than zero"
);
...
...
src/share/vm/opto/callGenerator.cpp
浏览文件 @
c812e04e
...
...
@@ -149,7 +149,6 @@ JVMState* DirectCallGenerator::generate(JVMState* jvms) {
call
->
set_optimized_virtual
(
true
);
if
(
method
()
->
is_method_handle_invoke
())
{
call
->
set_method_handle_invoke
(
true
);
kit
.
C
->
set_has_method_handle_invokes
(
true
);
}
}
kit
.
set_arguments_for_java_call
(
call
);
...
...
@@ -207,7 +206,6 @@ JVMState* DynamicCallGenerator::generate(JVMState* jvms) {
call
->
set_optimized_virtual
(
true
);
// Take extra care (in the presence of argument motion) not to trash the SP:
call
->
set_method_handle_invoke
(
true
);
kit
.
C
->
set_has_method_handle_invokes
(
true
);
// Pass the target MethodHandle as first argument and shift the
// other arguments.
...
...
@@ -706,18 +704,30 @@ CallGenerator* CallGenerator::for_method_handle_inline(Node* method_handle, JVMS
}
}
else
if
(
method_handle
->
Opcode
()
==
Op_Phi
&&
method_handle
->
req
()
==
3
&&
method_handle
->
in
(
1
)
->
Opcode
()
==
Op_ConP
&&
method_handle
->
in
(
2
)
->
Opcode
()
==
Op_ConP
)
{
float
prob
=
PROB_FAIR
;
Node
*
meth_region
=
method_handle
->
in
(
0
);
if
(
meth_region
->
is_Region
()
&&
meth_region
->
in
(
1
)
->
is_Proj
()
&&
meth_region
->
in
(
2
)
->
is_Proj
()
&&
meth_region
->
in
(
1
)
->
in
(
0
)
==
meth_region
->
in
(
2
)
->
in
(
0
)
&&
meth_region
->
in
(
1
)
->
in
(
0
)
->
is_If
())
{
// If diamond, so grab the probability of the test to drive the inlining below
prob
=
meth_region
->
in
(
1
)
->
in
(
0
)
->
as_If
()
->
_prob
;
if
(
meth_region
->
in
(
1
)
->
is_IfTrue
())
{
prob
=
1
-
prob
;
}
}
// selectAlternative idiom merging two constant MethodHandles.
// Generate a guard so that each can be inlined. We might want to
// do more inputs at later point but this gets the most common
// case.
const
TypeOopPtr
*
oop_ptr
=
method_handle
->
in
(
1
)
->
bottom_type
()
->
is_oopptr
();
ciObject
*
const_oop
=
oop_ptr
->
const_oop
();
ciMethodHandle
*
mh
=
const_oop
->
as_method_handle
();
CallGenerator
*
cg1
=
for_method_handle_inline
(
method_handle
->
in
(
1
),
jvms
,
caller
,
callee
,
profile
);
CallGenerator
*
cg2
=
for_method_handle_inline
(
method_handle
->
in
(
2
),
jvms
,
caller
,
callee
,
profile
);
CallGenerator
*
cg1
=
for_method_handle_inline
(
method_handle
->
in
(
1
),
jvms
,
caller
,
callee
,
profile
.
rescale
(
1.0
-
prob
));
CallGenerator
*
cg2
=
for_method_handle_inline
(
method_handle
->
in
(
2
),
jvms
,
caller
,
callee
,
profile
.
rescale
(
prob
));
if
(
cg1
!=
NULL
&&
cg2
!=
NULL
)
{
return
new
PredictedDynamicCallGenerator
(
mh
,
cg2
,
cg1
,
PROB_FAIR
);
const
TypeOopPtr
*
oop_ptr
=
method_handle
->
in
(
1
)
->
bottom_type
()
->
is_oopptr
();
ciObject
*
const_oop
=
oop_ptr
->
const_oop
();
ciMethodHandle
*
mh
=
const_oop
->
as_method_handle
();
return
new
PredictedDynamicCallGenerator
(
mh
,
cg2
,
cg1
,
prob
);
}
}
return
NULL
;
...
...
src/share/vm/opto/idealGraphPrinter.cpp
浏览文件 @
c812e04e
...
...
@@ -375,9 +375,9 @@ intptr_t IdealGraphPrinter::get_node_id(Node *n) {
return
(
intptr_t
)(
n
);
}
void
IdealGraphPrinter
::
visit_node
(
Node
*
n
,
void
*
param
)
{
void
IdealGraphPrinter
::
visit_node
(
Node
*
n
,
bool
edges
,
VectorSet
*
temp_set
)
{
if
(
param
)
{
if
(
edges
)
{
// Output edge
intptr_t
dest_id
=
get_node_id
(
n
);
...
...
@@ -599,16 +599,11 @@ void IdealGraphPrinter::visit_node(Node *n, void *param) {
#ifdef ASSERT
if
(
node
->
debug_orig
()
!=
NULL
)
{
temp_set
->
Clear
();
stringStream
dorigStream
;
Node
*
dorig
=
node
->
debug_orig
();
if
(
dorig
)
{
while
(
dorig
&&
temp_set
->
test_set
(
dorig
->
_idx
)
)
{
dorigStream
.
print
(
"%d "
,
dorig
->
_idx
);
Node
*
first
=
dorig
;
dorig
=
first
->
debug_orig
();
while
(
dorig
&&
dorig
!=
first
)
{
dorigStream
.
print
(
"%d "
,
dorig
->
_idx
);
dorig
=
dorig
->
debug_orig
();
}
}
print_prop
(
"debug_orig"
,
dorigStream
.
as_string
());
}
...
...
@@ -629,7 +624,7 @@ void IdealGraphPrinter::visit_node(Node *n, void *param) {
}
}
void
IdealGraphPrinter
::
walk_nodes
(
Node
*
start
,
void
*
param
)
{
void
IdealGraphPrinter
::
walk_nodes
(
Node
*
start
,
bool
edges
,
VectorSet
*
temp_set
)
{
VectorSet
visited
(
Thread
::
current
()
->
resource_area
());
...
...
@@ -650,7 +645,7 @@ void IdealGraphPrinter::walk_nodes(Node *start, void *param) {
while
(
nodeStack
.
length
()
>
0
)
{
Node
*
n
=
nodeStack
.
pop
();
visit_node
(
n
,
param
);
visit_node
(
n
,
edges
,
temp_set
);
if
(
_traverse_outs
)
{
for
(
DUIterator
i
=
n
->
outs
();
n
->
has_out
(
i
);
i
++
)
{
...
...
@@ -689,12 +684,14 @@ void IdealGraphPrinter::print(Compile* compile, const char *name, Node *node, in
print_attr
(
GRAPH_NAME_PROPERTY
,
(
const
char
*
)
name
);
end_head
();
VectorSet
temp_set
(
Thread
::
current
()
->
resource_area
());
head
(
NODES_ELEMENT
);
walk_nodes
(
node
,
NULL
);
walk_nodes
(
node
,
false
,
&
temp_set
);
tail
(
NODES_ELEMENT
);
head
(
EDGES_ELEMENT
);
walk_nodes
(
node
,
(
void
*
)
1
);
walk_nodes
(
node
,
true
,
&
temp_set
);
tail
(
EDGES_ELEMENT
);
if
(
C
->
cfg
()
!=
NULL
)
{
head
(
CONTROL_FLOW_ELEMENT
);
...
...
src/share/vm/opto/idealGraphPrinter.hpp
浏览文件 @
c812e04e
...
...
@@ -104,8 +104,8 @@ private:
void
print_indent
();
void
print_method
(
ciMethod
*
method
,
int
bci
,
InlineTree
*
tree
);
void
print_inline_tree
(
InlineTree
*
tree
);
void
visit_node
(
Node
*
n
,
void
*
param
);
void
walk_nodes
(
Node
*
start
,
void
*
param
);
void
visit_node
(
Node
*
n
,
bool
edges
,
VectorSet
*
temp_set
);
void
walk_nodes
(
Node
*
start
,
bool
edges
,
VectorSet
*
temp_set
);
void
begin_elem
(
const
char
*
s
);
void
end_elem
();
void
begin_head
(
const
char
*
s
);
...
...
src/share/vm/opto/matcher.cpp
浏览文件 @
c812e04e
...
...
@@ -1106,6 +1106,9 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) {
mcall_java
->
_optimized_virtual
=
call_java
->
is_optimized_virtual
();
is_method_handle_invoke
=
call_java
->
is_method_handle_invoke
();
mcall_java
->
_method_handle_invoke
=
is_method_handle_invoke
;
if
(
is_method_handle_invoke
)
{
C
->
set_has_method_handle_invokes
(
true
);
}
if
(
mcall_java
->
is_MachCallStaticJava
()
)
mcall_java
->
as_MachCallStaticJava
()
->
_name
=
call_java
->
as_CallStaticJava
()
->
_name
;
...
...
src/share/vm/prims/methodHandleWalk.cpp
浏览文件 @
c812e04e
...
...
@@ -182,10 +182,6 @@ void MethodHandleChain::print(oopDesc* m) {
HandleMark
hm
;
ResourceMark
rm
;
Handle
mh
(
m
);
print
(
mh
);
}
void
MethodHandleChain
::
print
(
Handle
mh
)
{
EXCEPTION_MARK
;
MethodHandleChain
mhc
(
mh
,
THREAD
);
if
(
HAS_PENDING_EXCEPTION
)
{
...
...
@@ -222,16 +218,33 @@ void MethodHandleChain::print_impl(TRAPS) {
if
(
o
!=
NULL
)
{
if
(
o
->
is_instance
())
{
tty
->
print
(
" instance %s"
,
o
->
klass
()
->
klass_part
()
->
internal_name
());
if
(
java_lang_invoke_CountingMethodHandle
::
is_instance
(
o
))
{
tty
->
print
(
" vmcount: %d"
,
java_lang_invoke_CountingMethodHandle
::
vmcount
(
o
));
}
}
else
{
o
->
print
();
}
}
oop
vmt
=
chain
.
vmtarget_oop
();
if
(
vmt
!=
NULL
)
{
if
(
vmt
->
is_method
())
{
tty
->
print
(
" "
);
methodOop
(
vmt
)
->
print_short_name
(
tty
);
}
else
if
(
java_lang_invoke_MethodHandle
::
is_instance
(
vmt
))
{
tty
->
print
(
" method handle "
INTPTR_FORMAT
,
vmt
);
}
else
{
ShouldNotReachHere
();
}
}
}
else
if
(
chain
.
is_adapter
())
{
tty
->
print
(
"adapter: arg_slot %d conversion op %s"
,
chain
.
adapter_arg_slot
(),
adapter_op_to_string
(
chain
.
adapter_conversion_op
()));
switch
(
chain
.
adapter_conversion_op
())
{
case
java_lang_invoke_AdapterMethodHandle
::
OP_RETYPE_ONLY
:
if
(
java_lang_invoke_CountingMethodHandle
::
is_instance
(
chain
.
method_handle_oop
()))
{
tty
->
print
(
" vmcount: %d"
,
java_lang_invoke_CountingMethodHandle
::
vmcount
(
chain
.
method_handle_oop
()));
}
case
java_lang_invoke_AdapterMethodHandle
::
OP_RETYPE_RAW
:
case
java_lang_invoke_AdapterMethodHandle
::
OP_CHECK_CAST
:
case
java_lang_invoke_AdapterMethodHandle
::
OP_PRIM_TO_PRIM
:
...
...
@@ -907,7 +920,10 @@ MethodHandleCompiler::MethodHandleCompiler(Handle root, Symbol* name, Symbol* si
_non_bcp_klasses
(
THREAD
,
5
),
_cur_stack
(
0
),
_max_stack
(
0
),
_rtype
(
T_ILLEGAL
)
_rtype
(
T_ILLEGAL
),
_selectAlternative_bci
(
-
1
),
_taken_count
(
0
),
_not_taken_count
(
0
)
{
// Element zero is always the null constant.
...
...
@@ -1115,11 +1131,50 @@ void MethodHandleCompiler::emit_bc(Bytecodes::Code op, int index, int args_size)
_bytecode
.
push
(
0
);
break
;
case
Bytecodes
::
_ifeq
:
assert
((
unsigned
short
)
index
==
index
,
"index does not fit in 16-bit"
);
_bytecode
.
push
(
op
);
_bytecode
.
push
(
index
>>
8
);
_bytecode
.
push
(
index
);
break
;
default:
ShouldNotReachHere
();
}
}
void
MethodHandleCompiler
::
update_branch_dest
(
int
src
,
int
dst
)
{
switch
(
_bytecode
.
at
(
src
))
{
case
Bytecodes
::
_ifeq
:
dst
-=
src
;
// compute the offset
assert
((
unsigned
short
)
dst
==
dst
,
"index does not fit in 16-bit"
);
_bytecode
.
at_put
(
src
+
1
,
dst
>>
8
);
_bytecode
.
at_put
(
src
+
2
,
dst
);
break
;
default:
ShouldNotReachHere
();
}
}
void
MethodHandleCompiler
::
emit_load
(
ArgToken
arg
)
{
TokenType
tt
=
arg
.
token_type
();
BasicType
bt
=
arg
.
basic_type
();
switch
(
tt
)
{
case
tt_parameter
:
case
tt_temporary
:
emit_load
(
bt
,
arg
.
index
());
break
;
case
tt_constant
:
emit_load_constant
(
arg
);
break
;
case
tt_illegal
:
case
tt_void
:
default:
ShouldNotReachHere
();
}
}
void
MethodHandleCompiler
::
emit_load
(
BasicType
bt
,
int
index
)
{
if
(
index
<=
3
)
{
...
...
@@ -1318,6 +1373,29 @@ MethodHandleCompiler::make_conversion(BasicType type, klassOop tk, Bytecodes::Co
jvalue
MethodHandleCompiler
::
zero_jvalue
=
{
0
};
jvalue
MethodHandleCompiler
::
one_jvalue
=
{
1
};
// Fetch any values from CountingMethodHandles and capture them for profiles
bool
MethodHandleCompiler
::
fetch_counts
(
ArgToken
arg1
,
ArgToken
arg2
)
{
int
count1
=
-
1
,
count2
=
-
1
;
if
(
arg1
.
token_type
()
==
tt_constant
&&
arg1
.
basic_type
()
==
T_OBJECT
&&
java_lang_invoke_CountingMethodHandle
::
is_instance
(
arg1
.
object
()()))
{
count1
=
java_lang_invoke_CountingMethodHandle
::
vmcount
(
arg1
.
object
()());
}
if
(
arg2
.
token_type
()
==
tt_constant
&&
arg2
.
basic_type
()
==
T_OBJECT
&&
java_lang_invoke_CountingMethodHandle
::
is_instance
(
arg2
.
object
()()))
{
count2
=
java_lang_invoke_CountingMethodHandle
::
vmcount
(
arg2
.
object
()());
}
int
total
=
count1
+
count2
;
if
(
count1
!=
-
1
&&
count2
!=
-
1
&&
total
!=
0
)
{
// Normalize the collect counts to the invoke_count
tty
->
print
(
"counts %d %d scaled by %d = "
,
count2
,
count1
,
_invoke_count
);
if
(
count1
!=
0
)
_not_taken_count
=
(
int
)(
_invoke_count
*
count1
/
(
double
)
total
);
if
(
count2
!=
0
)
_taken_count
=
(
int
)(
_invoke_count
*
count2
/
(
double
)
total
);
tty
->
print_cr
(
"%d %d"
,
_taken_count
,
_not_taken_count
);
return
true
;
}
return
false
;
}
// Emit bytecodes for the given invoke instruction.
MethodHandleWalker
::
ArgToken
MethodHandleCompiler
::
make_invoke
(
methodHandle
m
,
vmIntrinsics
::
ID
iid
,
...
...
@@ -1367,6 +1445,29 @@ MethodHandleCompiler::make_invoke(methodHandle m, vmIntrinsics::ID iid,
}
}
if
(
m
->
intrinsic_id
()
==
vmIntrinsics
::
_selectAlternative
&&
fetch_counts
(
argv
[
1
],
argv
[
2
]))
{
assert
(
argc
==
3
,
"three arguments"
);
assert
(
tailcall
,
"only"
);
// do inline bytecodes so we can drop profile data into it,
// 0: iload_0
emit_load
(
argv
[
0
]);
// 1: ifeq 8
_selectAlternative_bci
=
_bytecode
.
length
();
emit_bc
(
Bytecodes
::
_ifeq
,
0
);
// emit placeholder offset
// 4: aload_1
emit_load
(
argv
[
1
]);
// 5: areturn;
emit_bc
(
Bytecodes
::
_areturn
);
// 8: aload_2
update_branch_dest
(
_selectAlternative_bci
,
cur_bci
());
emit_load
(
argv
[
2
]);
// 9: areturn
emit_bc
(
Bytecodes
::
_areturn
);
return
ArgToken
();
// Dummy return value.
}
check_non_bcp_klass
(
klass
,
CHECK_
(
zero
));
if
(
m
->
is_method_handle_invoke
())
{
check_non_bcp_klasses
(
m
->
method_handle_type
(),
CHECK_
(
zero
));
...
...
@@ -1377,10 +1478,6 @@ MethodHandleCompiler::make_invoke(methodHandle m, vmIntrinsics::ID iid,
assert
(
argc
==
asc
.
size
()
+
((
op
==
Bytecodes
::
_invokestatic
||
op
==
Bytecodes
::
_invokedynamic
)
?
0
:
1
),
"argc mismatch"
);
// Inline the method.
InvocationCounter
*
ic
=
m
->
invocation_counter
();
ic
->
set_carry_flag
();
for
(
int
i
=
0
;
i
<
argc
;
i
++
)
{
ArgToken
arg
=
argv
[
i
];
TokenType
tt
=
arg
.
token_type
();
...
...
@@ -1686,7 +1783,7 @@ constantPoolHandle MethodHandleCompiler::get_constant_pool(TRAPS) const {
}
methodHandle
MethodHandleCompiler
::
get_method_oop
(
TRAPS
)
const
{
methodHandle
MethodHandleCompiler
::
get_method_oop
(
TRAPS
)
{
methodHandle
empty
;
// Create a method that holds the generated bytecode. invokedynamic
// has no receiver, normal MH calls do.
...
...
@@ -1765,6 +1862,7 @@ methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const {
assert
(
m
->
method_data
()
==
NULL
,
"there should not be an MDO yet"
);
m
->
set_method_data
(
mdo
);
bool
found_selectAlternative
=
false
;
// 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
();
...
...
@@ -1774,7 +1872,15 @@ methodHandle MethodHandleCompiler::get_method_oop(TRAPS) const {
CounterData
*
counter_data
=
profile_data
->
as_CounterData
();
counter_data
->
set_count
(
_invoke_count
);
}
if
(
profile_data
->
is_BranchData
()
&&
profile_data
->
bci
()
==
_selectAlternative_bci
)
{
BranchData
*
bd
=
profile_data
->
as_BranchData
();
bd
->
set_taken
(
_taken_count
);
bd
->
set_not_taken
(
_not_taken_count
);
found_selectAlternative
=
true
;
}
}
assert
(
_selectAlternative_bci
==
-
1
||
found_selectAlternative
,
"must have found profile entry"
);
}
#ifndef PRODUCT
...
...
src/share/vm/prims/methodHandleWalk.hpp
浏览文件 @
c812e04e
...
...
@@ -74,6 +74,7 @@ public:
set_method_handle
(
MethodHandle_vmtarget_oop
(),
THREAD
);
}
Handle
root
()
{
return
_root
;
}
Handle
method_handle
()
{
return
_method_handle
;
}
oop
method_handle_oop
()
{
return
_method_handle
();
}
oop
method_type_oop
()
{
return
MethodHandle_type_oop
();
}
...
...
@@ -110,7 +111,6 @@ public:
// the signature for each method. The signatures are printed in
// slot order to make it easier to understand.
void
print
();
static
void
print
(
Handle
mh
);
static
void
print
(
oopDesc
*
mh
);
#endif
};
...
...
@@ -277,6 +277,10 @@ private:
KlassHandle
_target_klass
;
Thread
*
_thread
;
int
_selectAlternative_bci
;
// These are used for capturing profiles from GWTs
int
_taken_count
;
int
_not_taken_count
;
// Values used by the compiler.
static
jvalue
zero_jvalue
;
static
jvalue
one_jvalue
;
...
...
@@ -372,6 +376,7 @@ private:
unsigned
char
*
bytecode
()
const
{
return
_bytecode
.
adr_at
(
0
);
}
int
bytecode_length
()
const
{
return
_bytecode
.
length
();
}
int
cur_bci
()
const
{
return
_bytecode
.
length
();
}
// Fake constant pool.
int
cpool_oop_put
(
int
tag
,
Handle
con
)
{
...
...
@@ -436,6 +441,8 @@ private:
}
void
emit_bc
(
Bytecodes
::
Code
op
,
int
index
=
0
,
int
args_size
=
-
1
);
void
update_branch_dest
(
int
src
,
int
dst
);
void
emit_load
(
ArgToken
arg
);
void
emit_load
(
BasicType
bt
,
int
index
);
void
emit_store
(
BasicType
bt
,
int
index
);
void
emit_load_constant
(
ArgToken
arg
);
...
...
@@ -455,11 +462,14 @@ private:
virtual
ArgToken
make_fetch
(
BasicType
type
,
klassOop
tk
,
Bytecodes
::
Code
op
,
const
ArgToken
&
base
,
const
ArgToken
&
offset
,
TRAPS
);
virtual
ArgToken
make_invoke
(
methodHandle
m
,
vmIntrinsics
::
ID
iid
,
Bytecodes
::
Code
op
,
bool
tailcall
,
int
argc
,
ArgToken
*
argv
,
TRAPS
);
// Check for profiling information on a GWT and return true if it's found
bool
fetch_counts
(
ArgToken
a1
,
ArgToken
a2
);
// Get a real constant pool.
constantPoolHandle
get_constant_pool
(
TRAPS
)
const
;
// Get a real methodOop.
methodHandle
get_method_oop
(
TRAPS
)
const
;
methodHandle
get_method_oop
(
TRAPS
);
public:
MethodHandleCompiler
(
Handle
root
,
Symbol
*
name
,
Symbol
*
signature
,
int
invoke_count
,
bool
for_invokedynamic
,
TRAPS
);
...
...
src/share/vm/prims/methodHandles.cpp
浏览文件 @
c812e04e
...
...
@@ -158,6 +158,8 @@ const char* MethodHandles::_entry_names[_EK_LIMIT+1] = {
"adapter_fold/4/ref"
,
"adapter_fold/5/ref"
,
"adapter_opt_profiling"
,
NULL
};
...
...
@@ -2653,6 +2655,11 @@ void MethodHandles::init_AdapterMethodHandle(Handle mh, Handle target, int argnu
// Finalize the conversion field. (Note that it is final to Java code.)
java_lang_invoke_AdapterMethodHandle
::
set_conversion
(
mh
(),
new_conversion
);
if
(
java_lang_invoke_CountingMethodHandle
::
is_instance
(
mh
()))
{
assert
(
ek_orig
==
_adapter_retype_only
,
"only one handled"
);
ek_opt
=
_adapter_opt_profiling
;
}
// Done!
java_lang_invoke_MethodHandle
::
set_vmentry
(
mh
(),
entry
(
ek_opt
));
...
...
@@ -2905,8 +2912,12 @@ JVM_ENTRY(jint, MHN_getConstant(JNIEnv *env, jobject igcls, jint which)) {
return
MethodHandles
::
stack_move_unit
();
case
MethodHandles
::
GC_CONV_OP_IMPLEMENTED_MASK
:
return
MethodHandles
::
adapter_conversion_ops_supported_mask
();
case
MethodHandles
::
GC_OP_ROT_ARGS_DOWN_LIMIT_BIAS
:
return
MethodHandles
::
OP_ROT_ARGS_DOWN_LIMIT_BIAS
;
case
MethodHandles
::
GC_COUNT_GWT
:
#ifdef COMPILER2
return
true
;
#else
return
false
;
#endif
}
return
0
;
}
...
...
src/share/vm/prims/methodHandles.hpp
浏览文件 @
c812e04e
...
...
@@ -187,6 +187,8 @@ class MethodHandles: AllStatic {
_adapter_opt_fold_FIRST
=
_adapter_opt_fold_ref
,
_adapter_opt_fold_LAST
=
_adapter_opt_fold_5_ref
,
_adapter_opt_profiling
,
_EK_LIMIT
,
_EK_FIRST
=
0
};
...
...
@@ -266,6 +268,8 @@ class MethodHandles: AllStatic {
return
_adapter_fold_args
;
if
(
ek
>=
_adapter_opt_return_FIRST
&&
ek
<=
_adapter_opt_return_LAST
)
return
_adapter_opt_return_any
;
if
(
ek
==
_adapter_opt_profiling
)
return
_adapter_retype_only
;
assert
(
false
,
"oob"
);
return
_EK_LIMIT
;
}
...
...
@@ -582,6 +586,7 @@ class MethodHandles: AllStatic {
GC_JVM_STACK_MOVE_UNIT
=
1
,
GC_CONV_OP_IMPLEMENTED_MASK
=
2
,
GC_OP_ROT_ARGS_DOWN_LIMIT_BIAS
=
3
,
GC_COUNT_GWT
=
4
,
// format of result from getTarget / encode_target:
ETF_HANDLE_OR_METHOD_NAME
=
0
,
// all available data (immediate MH or method)
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录