Merge
This commit is contained in:
commit
b18f1d282e
1
.hgtags
1
.hgtags
@ -513,3 +513,4 @@ ef57958c7c511162da8d9a75f0b977f0f7ac464e jdk-12+7
|
||||
8f594f75e0547d4ca16649cb3501659e3155e81b jdk-12+10
|
||||
f0f5d23449d31f1b3580c8a73313918cafeaefd7 jdk-12+11
|
||||
15094d12a632f452a2064318a4e416d0c7a9ce0c jdk-12+12
|
||||
511a9946f83e3e3c7b9dbe1840367063fb39b4e1 jdk-12+13
|
||||
|
@ -531,8 +531,8 @@ define SetupRunJtregTestBody
|
||||
$1_JTREG_BASIC_OPTIONS += $$(addprefix -exclude:, $$($1_JTREG_PROBLEM_LIST))
|
||||
endif
|
||||
|
||||
ifneq ($$(JIB_JAR), )
|
||||
$1_JTREG_BASIC_OPTIONS += -cpa:$$(JIB_JAR)
|
||||
ifneq ($$(JIB_HOME), )
|
||||
$1_JTREG_BASIC_OPTIONS += -e:JIB_HOME=$$(JIB_HOME)
|
||||
endif
|
||||
|
||||
$1_JTREG_BASIC_OPTIONS += -e:TEST_IMAGE_GRAAL_DIR=${TEST_IMAGE_DIR}/hotspot/jtreg/graal
|
||||
|
@ -723,7 +723,7 @@ SETFILE:=@SETFILE@
|
||||
XATTR:=@XATTR@
|
||||
JT_HOME:=@JT_HOME@
|
||||
JTREGEXE:=@JTREGEXE@
|
||||
JIB_JAR:=@JIB_JAR@
|
||||
JIB_HOME:=@JIB_HOME@
|
||||
XCODEBUILD=@XCODEBUILD@
|
||||
DTRACE := @DTRACE@
|
||||
FIXPATH:=@FIXPATH@
|
||||
|
@ -1144,5 +1144,5 @@ AC_DEFUN_ONCE([TOOLCHAIN_SETUP_JIB],
|
||||
fi
|
||||
fi
|
||||
|
||||
AC_SUBST(JIB_JAR)
|
||||
AC_SUBST(JIB_HOME)
|
||||
])
|
||||
|
@ -840,7 +840,7 @@ var getJibProfilesDependencies = function (input, common) {
|
||||
linux_x64: "gcc7.3.0-OEL6.4+1.0",
|
||||
macosx_x64: "Xcode9.4-MacOSX10.13+1.0",
|
||||
solaris_x64: "SS12u4-Solaris11u1+1.0",
|
||||
solaris_sparcv9: "SS12u4-Solaris11u1+1.1",
|
||||
solaris_sparcv9: "SS12u6-Solaris11u3+1.0",
|
||||
windows_x64: "VS2017-15.5.5+1.0",
|
||||
linux_aarch64: (input.profile != null && input.profile.indexOf("arm64") >= 0
|
||||
? "gcc-linaro-aarch64-linux-gnu-4.8-2013.11_linux+1.0"
|
||||
@ -961,9 +961,9 @@ var getJibProfilesDependencies = function (input, common) {
|
||||
ext: "zip",
|
||||
classifier: "distribution",
|
||||
revision: "3.0-SNAPSHOT",
|
||||
environment_name: "JIB_JAR",
|
||||
environment_name: "JIB_HOME",
|
||||
environment_value: input.get("jib", "install_path")
|
||||
+ "/jib-3.0-SNAPSHOT-distribution/lib/jib-3.0-SNAPSHOT.jar"
|
||||
+ "/jib-3.0-SNAPSHOT-distribution"
|
||||
},
|
||||
|
||||
ant: {
|
||||
|
@ -34,18 +34,19 @@
|
||||
# install in a separate temporary image.
|
||||
#
|
||||
# The Solaris Studio installation must contain at least these packages:
|
||||
# developer/developerstudio-126/backend 12.6-1.0.0.0 i--
|
||||
# developer/developerstudio-126/c++ 12.6-1.0.0.0 i--
|
||||
# developer/developerstudio-126/cc 12.6-1.0.0.0 i--
|
||||
# developer/developerstudio-126/dbx (solarisstudio) 12.6-1.0.0.0 i--
|
||||
# developer/developerstudio-126/library/c++-libs 12.6-1.0.0.0 i--
|
||||
# developer/developerstudio-126/library/math-libs 12.6-1.0.0.0 i--
|
||||
# developer/developerstudio-126/library/c-libs 12.6-1.0.0.0 i--
|
||||
# developer/developerstudio-126/library/studio-gccrt 12.6-1.0.0.0 i--
|
||||
# developer/developerstudio-126/studio-common 12.6-1.0.0.0 i--
|
||||
# developer/developerstudio-126/studio-ja 12.6-1.0.0.0 i--
|
||||
# developer/developerstudio-126/studio-legal 12.6-1.0.0.0 i--
|
||||
# developer/developerstudio-126/studio-zhCN 12.6-1.0.0.0 i--
|
||||
#developer/developerstudio-126/backend 12.6-1.0.0.1
|
||||
#developer/developerstudio-126/c++ 12.6-1.0.2.0
|
||||
#developer/developerstudio-126/cc 12.6-1.0.1.0
|
||||
#developer/developerstudio-126/dbx 12.6-1.0.0.1
|
||||
#developer/developerstudio-126/library/c++-libs 12.6-1.0.2.0
|
||||
#developer/developerstudio-126/library/c-libs 12.6-1.0.0.1
|
||||
#developer/developerstudio-126/library/f90-libs 12.6-1.0.0.1
|
||||
#developer/developerstudio-126/library/math-libs 12.6-1.0.0.1
|
||||
#developer/developerstudio-126/library/studio-gccrt 12.6-1.0.0.1
|
||||
#developer/developerstudio-126/studio-common 12.6-1.0.0.1
|
||||
#developer/developerstudio-126/studio-ja 12.6-1.0.0.1
|
||||
#developer/developerstudio-126/studio-legal 12.6-1.0.0.1
|
||||
#developer/developerstudio-126/studio-zhCN 12.6-1.0.0.1
|
||||
#
|
||||
# erik.joelsson@oracle.com
|
||||
|
||||
@ -93,7 +94,7 @@ if [ ! -d $INSTALL_ROOT ]; then
|
||||
pkg -R $INSTALL_ROOT set-publisher -P -g ${PUBLISHER_URI} solaris
|
||||
sudo pkg -R $INSTALL_ROOT install --accept entire@$SOLARIS_ENTIRE_VERSION \
|
||||
system/install developer/gnu-binutils system/library/mmheap system/picl \
|
||||
developer/assembler
|
||||
developer/assembler system/library/freetype-2
|
||||
else
|
||||
echo "Skipping installing packages"
|
||||
fi
|
||||
|
@ -36,9 +36,7 @@ ifeq ($(TOOLCHAIN_TYPE), gcc)
|
||||
LAUNCHER_CFLAGS += -fvisibility=hidden
|
||||
LDFLAGS_JDKEXE += -Wl,--exclude-libs,ALL
|
||||
else ifeq ($(TOOLCHAIN_TYPE), clang)
|
||||
ifneq ($(OPENJDK_TARGET_OS), macosx)
|
||||
LAUNCHER_CFLAGS += -fvisibility=hidden
|
||||
endif
|
||||
LAUNCHER_CFLAGS += -fvisibility=hidden
|
||||
else ifeq ($(TOOLCHAIN_TYPE), solstudio)
|
||||
LAUNCHER_CFLAGS += -xldscope=hidden
|
||||
else ifeq ($(TOOLCHAIN_TYPE), xlc)
|
||||
|
@ -244,7 +244,7 @@ ifeq ($(OPENJDK_TARGET_OS), aix)
|
||||
EXCLUDE_FILES := $(LIBJLI_EXCLUDE_FILES), \
|
||||
EXTRA_FILES := $(LIBJLI_EXTRA_FILES), \
|
||||
OPTIMIZATION := HIGH, \
|
||||
CFLAGS := $(STATIC_LIBRARY_FLAGS) $(LIBJLI_CFLAGS_JDKLIB) $(LIBJLI_CFLAGS) \
|
||||
CFLAGS := $(STATIC_LIBRARY_FLAGS) $(CFLAGS_JDKLIB) $(LIBJLI_CFLAGS) \
|
||||
$(addprefix -I, $(LIBJLI_SRC_DIRS)), \
|
||||
ARFLAGS := $(ARFLAGS), \
|
||||
OBJECT_DIR := $(SUPPORT_OUTPUTDIR)/native/$(MODULE)/libjli_static))
|
||||
|
@ -46,11 +46,9 @@ ifeq ($(TOOLCHAIN_TYPE), gcc)
|
||||
LDFLAGS_JDKLIB += -Wl,--exclude-libs,ALL
|
||||
EXPORT_ALL_SYMBOLS := -fvisibility=default
|
||||
else ifeq ($(TOOLCHAIN_TYPE), clang)
|
||||
ifneq ($(OPENJDK_TARGET_OS), macosx)
|
||||
CFLAGS_JDKLIB += -fvisibility=hidden
|
||||
CXXFLAGS_JDKLIB += -fvisibility=hidden
|
||||
EXPORT_ALL_SYMBOLS := -fvisibility=default
|
||||
endif
|
||||
CFLAGS_JDKLIB += -fvisibility=hidden
|
||||
CXXFLAGS_JDKLIB += -fvisibility=hidden
|
||||
EXPORT_ALL_SYMBOLS := -fvisibility=default
|
||||
else ifeq ($(TOOLCHAIN_TYPE), solstudio)
|
||||
CFLAGS_JDKLIB += -xldscope=hidden
|
||||
CXXFLAGS_JDKLIB += -xldscope=hidden
|
||||
|
@ -1025,37 +1025,17 @@ int LIR_Assembler::array_element_size(BasicType type) const {
|
||||
return exact_log2(elem_size);
|
||||
}
|
||||
|
||||
void LIR_Assembler::arithmetic_idiv(LIR_Op3* op, bool is_irem) {
|
||||
Register Rdividend = op->in_opr1()->as_register();
|
||||
Register Rdivisor = op->in_opr2()->as_register();
|
||||
Register Rscratch = op->in_opr3()->as_register();
|
||||
Register Rresult = op->result_opr()->as_register();
|
||||
int divisor = -1;
|
||||
|
||||
/*
|
||||
TODO: For some reason, using the Rscratch that gets passed in is
|
||||
not possible because the register allocator does not see the tmp reg
|
||||
as used, and assignes it the same register as Rdividend. We use rscratch1
|
||||
instead.
|
||||
|
||||
assert(Rdividend != Rscratch, "");
|
||||
assert(Rdivisor != Rscratch, "");
|
||||
*/
|
||||
|
||||
if (Rdivisor == noreg && is_power_of_2(divisor)) {
|
||||
// convert division by a power of two into some shifts and logical operations
|
||||
}
|
||||
|
||||
__ corrected_idivl(Rresult, Rdividend, Rdivisor, is_irem, rscratch1);
|
||||
}
|
||||
|
||||
void LIR_Assembler::emit_op3(LIR_Op3* op) {
|
||||
switch (op->code()) {
|
||||
case lir_idiv:
|
||||
arithmetic_idiv(op, false);
|
||||
break;
|
||||
case lir_irem:
|
||||
arithmetic_idiv(op, true);
|
||||
arithmetic_idiv(op->code(),
|
||||
op->in_opr1(),
|
||||
op->in_opr2(),
|
||||
op->in_opr3(),
|
||||
op->result_opr(),
|
||||
op->info());
|
||||
break;
|
||||
case lir_fmad:
|
||||
__ fmaddd(op->result_opr()->as_double_reg(),
|
||||
@ -1752,16 +1732,43 @@ void LIR_Assembler::arith_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr
|
||||
}
|
||||
|
||||
} else if (right->is_constant()) {
|
||||
jlong c = right->as_constant_ptr()->as_jlong_bits();
|
||||
jlong c = right->as_constant_ptr()->as_jlong();
|
||||
Register dreg = as_reg(dest);
|
||||
assert(code == lir_add || code == lir_sub, "mismatched arithmetic op");
|
||||
if (c == 0 && dreg == lreg_lo) {
|
||||
COMMENT("effective nop elided");
|
||||
return;
|
||||
}
|
||||
switch (code) {
|
||||
case lir_add: __ add(dreg, lreg_lo, c); break;
|
||||
case lir_sub: __ sub(dreg, lreg_lo, c); break;
|
||||
case lir_add:
|
||||
case lir_sub:
|
||||
if (c == 0 && dreg == lreg_lo) {
|
||||
COMMENT("effective nop elided");
|
||||
return;
|
||||
}
|
||||
code == lir_add ? __ add(dreg, lreg_lo, c) : __ sub(dreg, lreg_lo, c);
|
||||
break;
|
||||
case lir_div:
|
||||
assert(c > 0 && is_power_of_2_long(c), "divisor must be power-of-2 constant");
|
||||
if (c == 1) {
|
||||
// move lreg_lo to dreg if divisor is 1
|
||||
__ mov(dreg, lreg_lo);
|
||||
} else {
|
||||
unsigned int shift = exact_log2_long(c);
|
||||
// use rscratch1 as intermediate result register
|
||||
__ asr(rscratch1, lreg_lo, 63);
|
||||
__ add(rscratch1, lreg_lo, rscratch1, Assembler::LSR, 64 - shift);
|
||||
__ asr(dreg, rscratch1, shift);
|
||||
}
|
||||
break;
|
||||
case lir_rem:
|
||||
assert(c > 0 && is_power_of_2_long(c), "divisor must be power-of-2 constant");
|
||||
if (c == 1) {
|
||||
// move 0 to dreg if divisor is 1
|
||||
__ mov(dreg, zr);
|
||||
} else {
|
||||
// use rscratch1 as intermediate result register
|
||||
__ negs(rscratch1, lreg_lo);
|
||||
__ andr(dreg, lreg_lo, c - 1);
|
||||
__ andr(rscratch1, rscratch1, c - 1);
|
||||
__ csneg(dreg, dreg, rscratch1, Assembler::MI);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
@ -1862,7 +1869,51 @@ void LIR_Assembler::logic_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr
|
||||
|
||||
|
||||
|
||||
void LIR_Assembler::arithmetic_idiv(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr temp, LIR_Opr result, CodeEmitInfo* info) { Unimplemented(); }
|
||||
void LIR_Assembler::arithmetic_idiv(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr illegal, LIR_Opr result, CodeEmitInfo* info) {
|
||||
|
||||
// opcode check
|
||||
assert((code == lir_idiv) || (code == lir_irem), "opcode must be idiv or irem");
|
||||
bool is_irem = (code == lir_irem);
|
||||
|
||||
// operand check
|
||||
assert(left->is_single_cpu(), "left must be register");
|
||||
assert(right->is_single_cpu() || right->is_constant(), "right must be register or constant");
|
||||
assert(result->is_single_cpu(), "result must be register");
|
||||
Register lreg = left->as_register();
|
||||
Register dreg = result->as_register();
|
||||
|
||||
// power-of-2 constant check and codegen
|
||||
if (right->is_constant()) {
|
||||
int c = right->as_constant_ptr()->as_jint();
|
||||
assert(c > 0 && is_power_of_2(c), "divisor must be power-of-2 constant");
|
||||
if (is_irem) {
|
||||
if (c == 1) {
|
||||
// move 0 to dreg if divisor is 1
|
||||
__ movw(dreg, zr);
|
||||
} else {
|
||||
// use rscratch1 as intermediate result register
|
||||
__ negsw(rscratch1, lreg);
|
||||
__ andw(dreg, lreg, c - 1);
|
||||
__ andw(rscratch1, rscratch1, c - 1);
|
||||
__ csnegw(dreg, dreg, rscratch1, Assembler::MI);
|
||||
}
|
||||
} else {
|
||||
if (c == 1) {
|
||||
// move lreg to dreg if divisor is 1
|
||||
__ movw(dreg, lreg);
|
||||
} else {
|
||||
unsigned int shift = exact_log2(c);
|
||||
// use rscratch1 as intermediate result register
|
||||
__ asrw(rscratch1, lreg, 31);
|
||||
__ addw(rscratch1, lreg, rscratch1, Assembler::LSR, 32 - shift);
|
||||
__ asrw(dreg, rscratch1, shift);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Register rreg = right->as_register();
|
||||
__ corrected_idivl(dreg, lreg, rreg, is_irem, rscratch1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LIR_Assembler::comp_op(LIR_Condition condition, LIR_Opr opr1, LIR_Opr opr2, LIR_Op2* op) {
|
||||
@ -2792,7 +2843,10 @@ void LIR_Assembler::align_backward_branch_target() {
|
||||
}
|
||||
|
||||
|
||||
void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest) {
|
||||
void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest, LIR_Opr tmp) {
|
||||
// tmp must be unused
|
||||
assert(tmp->is_illegal(), "wasting a register if tmp is allocated");
|
||||
|
||||
if (left->is_single_cpu()) {
|
||||
assert(dest->is_single_cpu(), "expect single result reg");
|
||||
__ negw(dest->as_register(), left->as_register());
|
||||
|
@ -75,8 +75,6 @@ friend class ArrayCopyStub;
|
||||
_deopt_handler_size = 7 * NativeInstruction::instruction_size
|
||||
};
|
||||
|
||||
void arithmetic_idiv(LIR_Op3* op, bool is_irem);
|
||||
|
||||
public:
|
||||
|
||||
void store_parameter(Register r, int offset_from_esp_in_words);
|
||||
|
@ -440,17 +440,26 @@ void LIRGenerator::do_ArithmeticOp_Long(ArithmeticOp* x) {
|
||||
|
||||
if (x->op() == Bytecodes::_ldiv || x->op() == Bytecodes::_lrem) {
|
||||
|
||||
// the check for division by zero destroys the right operand
|
||||
right.set_destroys_register();
|
||||
|
||||
// check for division by zero (destroys registers of right operand!)
|
||||
CodeEmitInfo* info = state_for(x);
|
||||
|
||||
left.load_item();
|
||||
right.load_item();
|
||||
|
||||
__ cmp(lir_cond_equal, right.result(), LIR_OprFact::longConst(0));
|
||||
__ branch(lir_cond_equal, T_LONG, new DivByZeroStub(info));
|
||||
bool need_zero_check = true;
|
||||
if (right.is_constant()) {
|
||||
jlong c = right.get_jlong_constant();
|
||||
// no need to do div-by-zero check if the divisor is a non-zero constant
|
||||
if (c != 0) need_zero_check = false;
|
||||
// do not load right if the divisor is a power-of-2 constant
|
||||
if (c > 0 && is_power_of_2_long(c)) {
|
||||
right.dont_load_item();
|
||||
} else {
|
||||
right.load_item();
|
||||
}
|
||||
} else {
|
||||
right.load_item();
|
||||
}
|
||||
if (need_zero_check) {
|
||||
CodeEmitInfo* info = state_for(x);
|
||||
__ cmp(lir_cond_equal, right.result(), LIR_OprFact::longConst(0));
|
||||
__ branch(lir_cond_equal, T_LONG, new DivByZeroStub(info));
|
||||
}
|
||||
|
||||
rlock_result(x);
|
||||
switch (x->op()) {
|
||||
@ -506,19 +515,32 @@ void LIRGenerator::do_ArithmeticOp_Int(ArithmeticOp* x) {
|
||||
// do not need to load right, as we can handle stack and constants
|
||||
if (x->op() == Bytecodes::_idiv || x->op() == Bytecodes::_irem) {
|
||||
|
||||
right_arg->load_item();
|
||||
rlock_result(x);
|
||||
bool need_zero_check = true;
|
||||
if (right.is_constant()) {
|
||||
jint c = right.get_jint_constant();
|
||||
// no need to do div-by-zero check if the divisor is a non-zero constant
|
||||
if (c != 0) need_zero_check = false;
|
||||
// do not load right if the divisor is a power-of-2 constant
|
||||
if (c > 0 && is_power_of_2(c)) {
|
||||
right_arg->dont_load_item();
|
||||
} else {
|
||||
right_arg->load_item();
|
||||
}
|
||||
} else {
|
||||
right_arg->load_item();
|
||||
}
|
||||
if (need_zero_check) {
|
||||
CodeEmitInfo* info = state_for(x);
|
||||
__ cmp(lir_cond_equal, right_arg->result(), LIR_OprFact::longConst(0));
|
||||
__ branch(lir_cond_equal, T_INT, new DivByZeroStub(info));
|
||||
}
|
||||
|
||||
CodeEmitInfo* info = state_for(x);
|
||||
LIR_Opr tmp = new_register(T_INT);
|
||||
__ cmp(lir_cond_equal, right_arg->result(), LIR_OprFact::longConst(0));
|
||||
__ branch(lir_cond_equal, T_INT, new DivByZeroStub(info));
|
||||
info = state_for(x);
|
||||
|
||||
LIR_Opr ill = LIR_OprFact::illegalOpr;
|
||||
if (x->op() == Bytecodes::_irem) {
|
||||
__ irem(left_arg->result(), right_arg->result(), x->operand(), tmp, NULL);
|
||||
__ irem(left_arg->result(), right_arg->result(), x->operand(), ill, NULL);
|
||||
} else if (x->op() == Bytecodes::_idiv) {
|
||||
__ idiv(left_arg->result(), right_arg->result(), x->operand(), tmp, NULL);
|
||||
__ idiv(left_arg->result(), right_arg->result(), x->operand(), ill, NULL);
|
||||
}
|
||||
|
||||
} else if (x->op() == Bytecodes::_iadd || x->op() == Bytecodes::_isub) {
|
||||
|
@ -822,6 +822,15 @@ address MacroAssembler::emit_trampoline_stub(int insts_call_instruction_offset,
|
||||
return stub_start_addr;
|
||||
}
|
||||
|
||||
void MacroAssembler::c2bool(Register x) {
|
||||
// implements x == 0 ? 0 : 1
|
||||
// note: must only look at least-significant byte of x
|
||||
// since C-style booleans are stored in one byte
|
||||
// only! (was bug)
|
||||
tst(x, 0xff);
|
||||
cset(x, Assembler::NE);
|
||||
}
|
||||
|
||||
address MacroAssembler::ic_call(address entry, jint method_index) {
|
||||
RelocationHolder rh = virtual_call_Relocation::spec(pc(), method_index);
|
||||
// address const_ptr = long_constant((jlong)Universe::non_oop_word());
|
||||
|
@ -782,6 +782,9 @@ public:
|
||||
|
||||
void resolve_jobject(Register value, Register thread, Register tmp);
|
||||
|
||||
// C 'boolean' to Java boolean: x == 0 ? 0 : 1
|
||||
void c2bool(Register x);
|
||||
|
||||
// oop manipulations
|
||||
void load_klass(Register dst, Register src);
|
||||
void store_klass(Register dst, Register src);
|
||||
|
@ -1924,7 +1924,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
|
||||
|
||||
// Unpack native results.
|
||||
switch (ret_type) {
|
||||
case T_BOOLEAN: __ ubfx(r0, r0, 0, 8); break;
|
||||
case T_BOOLEAN: __ c2bool(r0); break;
|
||||
case T_CHAR : __ ubfx(r0, r0, 0, 16); break;
|
||||
case T_BYTE : __ sbfx(r0, r0, 0, 8); break;
|
||||
case T_SHORT : __ sbfx(r0, r0, 0, 16); break;
|
||||
|
@ -557,7 +557,7 @@ address TemplateInterpreterGenerator::generate_result_handler_for(
|
||||
BasicType type) {
|
||||
address entry = __ pc();
|
||||
switch (type) {
|
||||
case T_BOOLEAN: __ uxtb(r0, r0); break;
|
||||
case T_BOOLEAN: __ c2bool(r0); break;
|
||||
case T_CHAR : __ uxth(r0, r0); break;
|
||||
case T_BYTE : __ sxtb(r0, r0); break;
|
||||
case T_SHORT : __ sxth(r0, r0); break;
|
||||
|
@ -3265,7 +3265,9 @@ void LIR_Assembler::align_backward_branch_target() {
|
||||
}
|
||||
|
||||
|
||||
void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest) {
|
||||
void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest, LIR_Opr tmp) {
|
||||
// tmp must be unused
|
||||
assert(tmp->is_illegal(), "wasting a register if tmp is allocated");
|
||||
|
||||
if (left->is_single_cpu()) {
|
||||
assert (dest->type() == T_INT, "unexpected result type");
|
||||
|
@ -2840,7 +2840,9 @@ void LIR_Assembler::emit_delay(LIR_OpDelay* op) {
|
||||
}
|
||||
|
||||
|
||||
void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest) {
|
||||
void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest, LIR_Opr tmp) {
|
||||
// tmp must be unused
|
||||
assert(tmp->is_illegal(), "wasting a register if tmp is allocated");
|
||||
assert(left->is_register(), "can only handle registers");
|
||||
|
||||
if (left->is_single_cpu()) {
|
||||
|
@ -2850,7 +2850,9 @@ void LIR_Assembler::emit_delay(LIR_OpDelay* op) {
|
||||
ShouldNotCallThis(); // There are no delay slots on ZARCH_64.
|
||||
}
|
||||
|
||||
void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest) {
|
||||
void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest, LIR_Opr tmp) {
|
||||
// tmp must be unused
|
||||
assert(tmp->is_illegal(), "wasting a register if tmp is allocated");
|
||||
assert(left->is_register(), "can only handle registers");
|
||||
|
||||
if (left->is_single_cpu()) {
|
||||
|
@ -3024,7 +3024,9 @@ void LIR_Assembler::emit_delay(LIR_OpDelay* op) {
|
||||
}
|
||||
|
||||
|
||||
void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest) {
|
||||
void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest, LIR_Opr tmp) {
|
||||
// tmp must be unused
|
||||
assert(tmp->is_illegal(), "wasting a register if tmp is allocated");
|
||||
assert(left->is_register(), "can only handle registers");
|
||||
|
||||
if (left->is_single_cpu()) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2097,6 +2097,7 @@ private:
|
||||
|
||||
// Andn packed integers
|
||||
void pandn(XMMRegister dst, XMMRegister src);
|
||||
void vpandn(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
|
||||
|
||||
// Or packed integers
|
||||
void por(XMMRegister dst, XMMRegister src);
|
||||
@ -2134,6 +2135,7 @@ private:
|
||||
void vextracti32x4(Address dst, XMMRegister src, uint8_t imm8);
|
||||
void vextracti64x2(XMMRegister dst, XMMRegister src, uint8_t imm8);
|
||||
void vextracti64x4(XMMRegister dst, XMMRegister src, uint8_t imm8);
|
||||
void vextracti64x4(Address dst, XMMRegister src, uint8_t imm8);
|
||||
|
||||
// vextractf forms
|
||||
void vextractf128(XMMRegister dst, XMMRegister src, uint8_t imm8);
|
||||
@ -2144,28 +2146,24 @@ private:
|
||||
void vextractf64x4(XMMRegister dst, XMMRegister src, uint8_t imm8);
|
||||
void vextractf64x4(Address dst, XMMRegister src, uint8_t imm8);
|
||||
|
||||
// legacy xmm sourced word/dword replicate
|
||||
void vpbroadcastw(XMMRegister dst, XMMRegister src);
|
||||
void vpbroadcastd(XMMRegister dst, XMMRegister src);
|
||||
|
||||
// xmm/mem sourced byte/word/dword/qword replicate
|
||||
void evpbroadcastb(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
void evpbroadcastb(XMMRegister dst, Address src, int vector_len);
|
||||
void evpbroadcastw(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
void evpbroadcastw(XMMRegister dst, Address src, int vector_len);
|
||||
void evpbroadcastd(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
void evpbroadcastd(XMMRegister dst, Address src, int vector_len);
|
||||
void evpbroadcastq(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
void evpbroadcastq(XMMRegister dst, Address src, int vector_len);
|
||||
void vpbroadcastb(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
void vpbroadcastb(XMMRegister dst, Address src, int vector_len);
|
||||
void vpbroadcastw(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
void vpbroadcastw(XMMRegister dst, Address src, int vector_len);
|
||||
void vpbroadcastd(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
void vpbroadcastd(XMMRegister dst, Address src, int vector_len);
|
||||
void vpbroadcastq(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
void vpbroadcastq(XMMRegister dst, Address src, int vector_len);
|
||||
|
||||
void evbroadcasti64x2(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
void evbroadcasti64x2(XMMRegister dst, Address src, int vector_len);
|
||||
|
||||
// scalar single/double precision replicate
|
||||
void evpbroadcastss(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
void evpbroadcastss(XMMRegister dst, Address src, int vector_len);
|
||||
void evpbroadcastsd(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
void evpbroadcastsd(XMMRegister dst, Address src, int vector_len);
|
||||
void vpbroadcastss(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
void vpbroadcastss(XMMRegister dst, Address src, int vector_len);
|
||||
void vpbroadcastsd(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
void vpbroadcastsd(XMMRegister dst, Address src, int vector_len);
|
||||
|
||||
// gpr sourced byte/word/dword/qword replicate
|
||||
void evpbroadcastb(XMMRegister dst, Register src, int vector_len);
|
||||
|
@ -68,7 +68,6 @@ static jlong *float_signflip_pool = double_quadword(&fp_signmask_pool[3*2], (jl
|
||||
static jlong *double_signflip_pool = double_quadword(&fp_signmask_pool[4*2], (jlong)UCONST64(0x8000000000000000), (jlong)UCONST64(0x8000000000000000));
|
||||
|
||||
|
||||
|
||||
NEEDS_CLEANUP // remove this definitions ?
|
||||
const Register IC_Klass = rax; // where the IC klass is cached
|
||||
const Register SYNC_header = rax; // synchronization header
|
||||
@ -650,7 +649,7 @@ void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_cod
|
||||
|
||||
case T_FLOAT: {
|
||||
if (dest->is_single_xmm()) {
|
||||
if (c->is_zero_float()) {
|
||||
if (LP64_ONLY(UseAVX < 2 &&) c->is_zero_float()) {
|
||||
__ xorps(dest->as_xmm_float_reg(), dest->as_xmm_float_reg());
|
||||
} else {
|
||||
__ movflt(dest->as_xmm_float_reg(),
|
||||
@ -672,7 +671,7 @@ void LIR_Assembler::const2reg(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_cod
|
||||
|
||||
case T_DOUBLE: {
|
||||
if (dest->is_double_xmm()) {
|
||||
if (c->is_zero_double()) {
|
||||
if (LP64_ONLY(UseAVX < 2 &&) c->is_zero_double()) {
|
||||
__ xorpd(dest->as_xmm_double_reg(), dest->as_xmm_double_reg());
|
||||
} else {
|
||||
__ movdbl(dest->as_xmm_double_reg(),
|
||||
@ -2395,16 +2394,24 @@ void LIR_Assembler::arith_fpu_implementation(LIR_Code code, int left_index, int
|
||||
}
|
||||
|
||||
|
||||
void LIR_Assembler::intrinsic_op(LIR_Code code, LIR_Opr value, LIR_Opr unused, LIR_Opr dest, LIR_Op* op) {
|
||||
void LIR_Assembler::intrinsic_op(LIR_Code code, LIR_Opr value, LIR_Opr tmp, LIR_Opr dest, LIR_Op* op) {
|
||||
if (value->is_double_xmm()) {
|
||||
switch(code) {
|
||||
case lir_abs :
|
||||
{
|
||||
if (dest->as_xmm_double_reg() != value->as_xmm_double_reg()) {
|
||||
__ movdbl(dest->as_xmm_double_reg(), value->as_xmm_double_reg());
|
||||
#ifdef _LP64
|
||||
if (UseAVX > 2 && !VM_Version::supports_avx512vl()) {
|
||||
assert(tmp->is_valid(), "need temporary");
|
||||
__ vpandn(dest->as_xmm_double_reg(), tmp->as_xmm_double_reg(), value->as_xmm_double_reg(), 2);
|
||||
} else {
|
||||
#endif
|
||||
if (dest->as_xmm_double_reg() != value->as_xmm_double_reg()) {
|
||||
__ movdbl(dest->as_xmm_double_reg(), value->as_xmm_double_reg());
|
||||
}
|
||||
assert(!tmp->is_valid(), "do not need temporary");
|
||||
__ andpd(dest->as_xmm_double_reg(),
|
||||
ExternalAddress((address)double_signmask_pool));
|
||||
}
|
||||
__ andpd(dest->as_xmm_double_reg(),
|
||||
ExternalAddress((address)double_signmask_pool));
|
||||
}
|
||||
break;
|
||||
|
||||
@ -3734,7 +3741,7 @@ void LIR_Assembler::align_backward_branch_target() {
|
||||
}
|
||||
|
||||
|
||||
void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest) {
|
||||
void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest, LIR_Opr tmp) {
|
||||
if (left->is_single_cpu()) {
|
||||
__ negl(left->as_register());
|
||||
move_regs(left->as_register(), dest->as_register());
|
||||
@ -3759,24 +3766,36 @@ void LIR_Assembler::negate(LIR_Opr left, LIR_Opr dest) {
|
||||
#endif // _LP64
|
||||
|
||||
} else if (dest->is_single_xmm()) {
|
||||
if (left->as_xmm_float_reg() != dest->as_xmm_float_reg()) {
|
||||
__ movflt(dest->as_xmm_float_reg(), left->as_xmm_float_reg());
|
||||
#ifdef _LP64
|
||||
if (UseAVX > 2 && !VM_Version::supports_avx512vl()) {
|
||||
assert(tmp->is_valid(), "need temporary");
|
||||
assert_different_registers(left->as_xmm_float_reg(), tmp->as_xmm_float_reg());
|
||||
__ vpxor(dest->as_xmm_float_reg(), tmp->as_xmm_float_reg(), left->as_xmm_float_reg(), 2);
|
||||
}
|
||||
if (UseAVX > 0) {
|
||||
__ vnegatess(dest->as_xmm_float_reg(), dest->as_xmm_float_reg(),
|
||||
ExternalAddress((address)float_signflip_pool));
|
||||
} else {
|
||||
else
|
||||
#endif
|
||||
{
|
||||
assert(!tmp->is_valid(), "do not need temporary");
|
||||
if (left->as_xmm_float_reg() != dest->as_xmm_float_reg()) {
|
||||
__ movflt(dest->as_xmm_float_reg(), left->as_xmm_float_reg());
|
||||
}
|
||||
__ xorps(dest->as_xmm_float_reg(),
|
||||
ExternalAddress((address)float_signflip_pool));
|
||||
}
|
||||
} else if (dest->is_double_xmm()) {
|
||||
if (left->as_xmm_double_reg() != dest->as_xmm_double_reg()) {
|
||||
__ movdbl(dest->as_xmm_double_reg(), left->as_xmm_double_reg());
|
||||
#ifdef _LP64
|
||||
if (UseAVX > 2 && !VM_Version::supports_avx512vl()) {
|
||||
assert(tmp->is_valid(), "need temporary");
|
||||
assert_different_registers(left->as_xmm_double_reg(), tmp->as_xmm_double_reg());
|
||||
__ vpxor(dest->as_xmm_double_reg(), tmp->as_xmm_double_reg(), left->as_xmm_double_reg(), 2);
|
||||
}
|
||||
if (UseAVX > 0) {
|
||||
__ vnegatesd(dest->as_xmm_double_reg(), dest->as_xmm_double_reg(),
|
||||
ExternalAddress((address)double_signflip_pool));
|
||||
} else {
|
||||
else
|
||||
#endif
|
||||
{
|
||||
assert(!tmp->is_valid(), "do not need temporary");
|
||||
if (left->as_xmm_double_reg() != dest->as_xmm_double_reg()) {
|
||||
__ movdbl(dest->as_xmm_double_reg(), left->as_xmm_double_reg());
|
||||
}
|
||||
__ xorpd(dest->as_xmm_double_reg(),
|
||||
ExternalAddress((address)double_signflip_pool));
|
||||
}
|
||||
|
@ -320,7 +320,21 @@ void LIRGenerator::do_NegateOp(NegateOp* x) {
|
||||
value.set_destroys_register();
|
||||
value.load_item();
|
||||
LIR_Opr reg = rlock(x);
|
||||
__ negate(value.result(), reg);
|
||||
|
||||
LIR_Opr tmp = LIR_OprFact::illegalOpr;
|
||||
#ifdef _LP64
|
||||
if (UseAVX > 2 && !VM_Version::supports_avx512vl()) {
|
||||
if (x->type()->tag() == doubleTag) {
|
||||
tmp = new_register(T_DOUBLE);
|
||||
__ move(LIR_OprFact::doubleConst(-0.0), tmp);
|
||||
}
|
||||
else if (x->type()->tag() == floatTag) {
|
||||
tmp = new_register(T_FLOAT);
|
||||
__ move(LIR_OprFact::floatConst(-0.0), tmp);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
__ negate(value.result(), reg, tmp);
|
||||
|
||||
set_result(x, round_item(reg));
|
||||
}
|
||||
@ -748,8 +762,17 @@ void LIRGenerator::do_MathIntrinsic(Intrinsic* x) {
|
||||
LIR_Opr calc_input = value.result();
|
||||
LIR_Opr calc_result = rlock_result(x);
|
||||
|
||||
LIR_Opr tmp = LIR_OprFact::illegalOpr;
|
||||
#ifdef _LP64
|
||||
if (UseAVX > 2 && (!VM_Version::supports_avx512vl()) &&
|
||||
(x->id() == vmIntrinsics::_dabs)) {
|
||||
tmp = new_register(T_DOUBLE);
|
||||
__ move(LIR_OprFact::doubleConst(-0.0), tmp);
|
||||
}
|
||||
#endif
|
||||
|
||||
switch(x->id()) {
|
||||
case vmIntrinsics::_dabs: __ abs (calc_input, calc_result, LIR_OprFact::illegalOpr); break;
|
||||
case vmIntrinsics::_dabs: __ abs (calc_input, calc_result, tmp); break;
|
||||
case vmIntrinsics::_dsqrt: __ sqrt (calc_input, calc_result, LIR_OprFact::illegalOpr); break;
|
||||
default: ShouldNotReachHere();
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ define_pd_global(bool, ThreadLocalHandshakes, false);
|
||||
product(bool, UseStoreImmI16, true, \
|
||||
"Use store immediate 16-bits value instruction on x86") \
|
||||
\
|
||||
product(intx, UseAVX, 2, \
|
||||
product(intx, UseAVX, 3, \
|
||||
"Highest supported AVX instructions set on x86/x64") \
|
||||
range(0, 99) \
|
||||
\
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -482,10 +482,6 @@ class MacroAssembler: public Assembler {
|
||||
// from register xmm0. Otherwise, the value is stored from the FPU stack.
|
||||
void store_double(Address dst);
|
||||
|
||||
// Save/restore ZMM (512bit) register on stack.
|
||||
void push_zmm(XMMRegister reg);
|
||||
void pop_zmm(XMMRegister reg);
|
||||
|
||||
// pushes double TOS element of FPU stack on CPU stack; pops from FPU stack
|
||||
void push_fTOS();
|
||||
|
||||
@ -1214,9 +1210,11 @@ public:
|
||||
void vpand(XMMRegister dst, XMMRegister nds, Address src, int vector_len) { Assembler::vpand(dst, nds, src, vector_len); }
|
||||
void vpand(XMMRegister dst, XMMRegister nds, AddressLiteral src, int vector_len);
|
||||
|
||||
void vpbroadcastw(XMMRegister dst, XMMRegister src);
|
||||
void vpbroadcastw(XMMRegister dst, XMMRegister src, int vector_len);
|
||||
void vpbroadcastw(XMMRegister dst, Address src, int vector_len) { Assembler::vpbroadcastw(dst, src, vector_len); }
|
||||
|
||||
void vpcmpeqb(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
|
||||
|
||||
void vpcmpeqw(XMMRegister dst, XMMRegister nds, XMMRegister src, int vector_len);
|
||||
|
||||
void vpmovzxbw(XMMRegister dst, Address src, int vector_len);
|
||||
|
@ -403,7 +403,7 @@ class VM_Version_StubGenerator: public StubCodeGenerator {
|
||||
__ movdl(xmm0, rcx);
|
||||
__ movl(rcx, 0xffff);
|
||||
__ kmovwl(k1, rcx);
|
||||
__ evpbroadcastd(xmm0, xmm0, Assembler::AVX_512bit);
|
||||
__ vpbroadcastd(xmm0, xmm0, Assembler::AVX_512bit);
|
||||
__ evmovdqul(xmm7, xmm0, Assembler::AVX_512bit);
|
||||
#ifdef _LP64
|
||||
__ evmovdqul(xmm8, xmm0, Assembler::AVX_512bit);
|
||||
@ -885,7 +885,7 @@ void VM_Version::get_processor_features() {
|
||||
FLAG_SET_DEFAULT(UseSHA, false);
|
||||
}
|
||||
|
||||
if (supports_sha() && UseSHA) {
|
||||
if (supports_sha() && supports_sse4_1() && UseSHA) {
|
||||
if (FLAG_IS_DEFAULT(UseSHA1Intrinsics)) {
|
||||
FLAG_SET_DEFAULT(UseSHA1Intrinsics, true);
|
||||
}
|
||||
@ -894,7 +894,7 @@ void VM_Version::get_processor_features() {
|
||||
FLAG_SET_DEFAULT(UseSHA1Intrinsics, false);
|
||||
}
|
||||
|
||||
if (UseSHA) {
|
||||
if (supports_sse4_1() && UseSHA) {
|
||||
if (FLAG_IS_DEFAULT(UseSHA256Intrinsics)) {
|
||||
FLAG_SET_DEFAULT(UseSHA256Intrinsics, true);
|
||||
}
|
||||
|
@ -816,7 +816,10 @@ public:
|
||||
static bool supports_avx512cd() { return (_features & CPU_AVX512CD) != 0; }
|
||||
static bool supports_avx512bw() { return (_features & CPU_AVX512BW) != 0; }
|
||||
static bool supports_avx512vl() { return (_features & CPU_AVX512VL) != 0; }
|
||||
static bool supports_avx512vlbw() { return (supports_avx512bw() && supports_avx512vl()); }
|
||||
static bool supports_avx512vlbw() { return (supports_evex() && supports_avx512bw() && supports_avx512vl()); }
|
||||
static bool supports_avx512vldq() { return (supports_evex() && supports_avx512dq() && supports_avx512vl()); }
|
||||
static bool supports_avx512vlbwdq() { return (supports_evex() && supports_avx512vl() &&
|
||||
supports_avx512bw() && supports_avx512dq()); }
|
||||
static bool supports_avx512novl() { return (supports_evex() && !supports_avx512vl()); }
|
||||
static bool supports_avx512nobw() { return (supports_evex() && !supports_avx512bw()); }
|
||||
static bool supports_avx256only() { return (supports_avx2() && !supports_evex()); }
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4101,6 +4101,15 @@ operand regF() %{
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
// Float register operands
|
||||
operand vlRegF() %{
|
||||
constraint(ALLOC_IN_RC(float_reg_vl));
|
||||
match(RegF);
|
||||
|
||||
format %{ %}
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
// XMM Double register operands
|
||||
operand regD() %{
|
||||
predicate( UseSSE>=2 );
|
||||
@ -4110,6 +4119,15 @@ operand regD() %{
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
// Double register operands
|
||||
operand vlRegD() %{
|
||||
constraint(ALLOC_IN_RC(double_reg_vl));
|
||||
match(RegD);
|
||||
|
||||
format %{ %}
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
// Vectors : note, we use legacy registers to avoid extra (unneeded in 32-bit VM)
|
||||
// runtime code generation via reg_class_dynamic.
|
||||
operand vecS() %{
|
||||
@ -4120,6 +4138,14 @@ operand vecS() %{
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
operand legVecS() %{
|
||||
constraint(ALLOC_IN_RC(vectors_reg_legacy));
|
||||
match(VecS);
|
||||
|
||||
format %{ %}
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
operand vecD() %{
|
||||
constraint(ALLOC_IN_RC(vectord_reg_legacy));
|
||||
match(VecD);
|
||||
@ -4128,6 +4154,14 @@ operand vecD() %{
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
operand legVecD() %{
|
||||
constraint(ALLOC_IN_RC(vectord_reg_legacy));
|
||||
match(VecD);
|
||||
|
||||
format %{ %}
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
operand vecX() %{
|
||||
constraint(ALLOC_IN_RC(vectorx_reg_legacy));
|
||||
match(VecX);
|
||||
@ -4136,6 +4170,14 @@ operand vecX() %{
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
operand legVecX() %{
|
||||
constraint(ALLOC_IN_RC(vectorx_reg_legacy));
|
||||
match(VecX);
|
||||
|
||||
format %{ %}
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
operand vecY() %{
|
||||
constraint(ALLOC_IN_RC(vectory_reg_legacy));
|
||||
match(VecY);
|
||||
@ -4144,6 +4186,14 @@ operand vecY() %{
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
operand legVecY() %{
|
||||
constraint(ALLOC_IN_RC(vectory_reg_legacy));
|
||||
match(VecY);
|
||||
|
||||
format %{ %}
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
//----------Memory Operands----------------------------------------------------
|
||||
// Direct Memory Operand
|
||||
operand direct(immP addr) %{
|
||||
@ -6515,6 +6565,26 @@ instruct storeD(memory mem, regD src) %{
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
// Load Double
|
||||
instruct MoveD2VL(vlRegD dst, regD src) %{
|
||||
match(Set dst src);
|
||||
format %{ "movsd $dst,$src\t! load double (8 bytes)" %}
|
||||
ins_encode %{
|
||||
__ movdbl($dst$$XMMRegister, $src$$XMMRegister);
|
||||
%}
|
||||
ins_pipe( fpu_reg_reg );
|
||||
%}
|
||||
|
||||
// Load Double
|
||||
instruct MoveVL2D(regD dst, vlRegD src) %{
|
||||
match(Set dst src);
|
||||
format %{ "movsd $dst,$src\t! load double (8 bytes)" %}
|
||||
ins_encode %{
|
||||
__ movdbl($dst$$XMMRegister, $src$$XMMRegister);
|
||||
%}
|
||||
ins_pipe( fpu_reg_reg );
|
||||
%}
|
||||
|
||||
// Store XMM register to memory (single-precision floating point)
|
||||
// MOVSS instruction
|
||||
instruct storeF(memory mem, regF src) %{
|
||||
@ -6528,6 +6598,26 @@ instruct storeF(memory mem, regF src) %{
|
||||
ins_pipe( pipe_slow );
|
||||
%}
|
||||
|
||||
// Load Float
|
||||
instruct MoveF2VL(vlRegF dst, regF src) %{
|
||||
match(Set dst src);
|
||||
format %{ "movss $dst,$src\t! load float (4 bytes)" %}
|
||||
ins_encode %{
|
||||
__ movflt($dst$$XMMRegister, $src$$XMMRegister);
|
||||
%}
|
||||
ins_pipe( fpu_reg_reg );
|
||||
%}
|
||||
|
||||
// Load Float
|
||||
instruct MoveVL2F(regF dst, vlRegF src) %{
|
||||
match(Set dst src);
|
||||
format %{ "movss $dst,$src\t! load float (4 bytes)" %}
|
||||
ins_encode %{
|
||||
__ movflt($dst$$XMMRegister, $src$$XMMRegister);
|
||||
%}
|
||||
ins_pipe( fpu_reg_reg );
|
||||
%}
|
||||
|
||||
// Store Float
|
||||
instruct storeFPR( memory mem, regFPR1 src) %{
|
||||
predicate(UseSSE==0);
|
||||
|
@ -3656,6 +3656,15 @@ operand regF() %{
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
// Float register operands
|
||||
operand vlRegF() %{
|
||||
constraint(ALLOC_IN_RC(float_reg_vl));
|
||||
match(RegF);
|
||||
|
||||
format %{ %}
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
// Double register operands
|
||||
operand regD() %{
|
||||
constraint(ALLOC_IN_RC(double_reg));
|
||||
@ -3665,9 +3674,27 @@ operand regD() %{
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
// Double register operands
|
||||
operand vlRegD() %{
|
||||
constraint(ALLOC_IN_RC(double_reg_vl));
|
||||
match(RegD);
|
||||
|
||||
format %{ %}
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
// Vectors
|
||||
operand vecS() %{
|
||||
constraint(ALLOC_IN_RC(vectors_reg));
|
||||
constraint(ALLOC_IN_RC(vectors_reg_vlbwdq));
|
||||
match(VecS);
|
||||
|
||||
format %{ %}
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
// Vectors
|
||||
operand legVecS() %{
|
||||
constraint(ALLOC_IN_RC(vectors_reg_legacy));
|
||||
match(VecS);
|
||||
|
||||
format %{ %}
|
||||
@ -3675,7 +3702,15 @@ operand vecS() %{
|
||||
%}
|
||||
|
||||
operand vecD() %{
|
||||
constraint(ALLOC_IN_RC(vectord_reg));
|
||||
constraint(ALLOC_IN_RC(vectord_reg_vlbwdq));
|
||||
match(VecD);
|
||||
|
||||
format %{ %}
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
operand legVecD() %{
|
||||
constraint(ALLOC_IN_RC(vectord_reg_legacy));
|
||||
match(VecD);
|
||||
|
||||
format %{ %}
|
||||
@ -3683,7 +3718,15 @@ operand vecD() %{
|
||||
%}
|
||||
|
||||
operand vecX() %{
|
||||
constraint(ALLOC_IN_RC(vectorx_reg));
|
||||
constraint(ALLOC_IN_RC(vectorx_reg_vlbwdq));
|
||||
match(VecX);
|
||||
|
||||
format %{ %}
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
operand legVecX() %{
|
||||
constraint(ALLOC_IN_RC(vectorx_reg_legacy));
|
||||
match(VecX);
|
||||
|
||||
format %{ %}
|
||||
@ -3691,7 +3734,15 @@ operand vecX() %{
|
||||
%}
|
||||
|
||||
operand vecY() %{
|
||||
constraint(ALLOC_IN_RC(vectory_reg));
|
||||
constraint(ALLOC_IN_RC(vectory_reg_vlbwdq));
|
||||
match(VecY);
|
||||
|
||||
format %{ %}
|
||||
interface(REG_INTER);
|
||||
%}
|
||||
|
||||
operand legVecY() %{
|
||||
constraint(ALLOC_IN_RC(vectory_reg_legacy));
|
||||
match(VecY);
|
||||
|
||||
format %{ %}
|
||||
@ -5287,6 +5338,26 @@ instruct loadF(regF dst, memory mem)
|
||||
ins_pipe(pipe_slow); // XXX
|
||||
%}
|
||||
|
||||
// Load Float
|
||||
instruct MoveF2VL(vlRegF dst, regF src) %{
|
||||
match(Set dst src);
|
||||
format %{ "movss $dst,$src\t! load float (4 bytes)" %}
|
||||
ins_encode %{
|
||||
__ movflt($dst$$XMMRegister, $src$$XMMRegister);
|
||||
%}
|
||||
ins_pipe( fpu_reg_reg );
|
||||
%}
|
||||
|
||||
// Load Float
|
||||
instruct MoveVL2F(regF dst, vlRegF src) %{
|
||||
match(Set dst src);
|
||||
format %{ "movss $dst,$src\t! load float (4 bytes)" %}
|
||||
ins_encode %{
|
||||
__ movflt($dst$$XMMRegister, $src$$XMMRegister);
|
||||
%}
|
||||
ins_pipe( fpu_reg_reg );
|
||||
%}
|
||||
|
||||
// Load Double
|
||||
instruct loadD_partial(regD dst, memory mem)
|
||||
%{
|
||||
@ -5314,6 +5385,26 @@ instruct loadD(regD dst, memory mem)
|
||||
ins_pipe(pipe_slow); // XXX
|
||||
%}
|
||||
|
||||
// Load Double
|
||||
instruct MoveD2VL(vlRegD dst, regD src) %{
|
||||
match(Set dst src);
|
||||
format %{ "movsd $dst,$src\t! load double (8 bytes)" %}
|
||||
ins_encode %{
|
||||
__ movdbl($dst$$XMMRegister, $src$$XMMRegister);
|
||||
%}
|
||||
ins_pipe( fpu_reg_reg );
|
||||
%}
|
||||
|
||||
// Load Double
|
||||
instruct MoveVL2D(regD dst, vlRegD src) %{
|
||||
match(Set dst src);
|
||||
format %{ "movsd $dst,$src\t! load double (8 bytes)" %}
|
||||
ins_encode %{
|
||||
__ movdbl($dst$$XMMRegister, $src$$XMMRegister);
|
||||
%}
|
||||
ins_pipe( fpu_reg_reg );
|
||||
%}
|
||||
|
||||
// Load Effective Address
|
||||
instruct leaP8(rRegP dst, indOffset8 mem)
|
||||
%{
|
||||
@ -10858,7 +10949,7 @@ instruct rep_stos_large(rcx_RegL cnt, rdi_RegP base, regD tmp, rax_RegI zero,
|
||||
%}
|
||||
|
||||
instruct string_compareL(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rdx_RegI cnt2,
|
||||
rax_RegI result, regD tmp1, rFlagsReg cr)
|
||||
rax_RegI result, legVecS tmp1, rFlagsReg cr)
|
||||
%{
|
||||
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LL);
|
||||
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
@ -10874,7 +10965,7 @@ instruct string_compareL(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rdx_RegI c
|
||||
%}
|
||||
|
||||
instruct string_compareU(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rdx_RegI cnt2,
|
||||
rax_RegI result, regD tmp1, rFlagsReg cr)
|
||||
rax_RegI result, legVecS tmp1, rFlagsReg cr)
|
||||
%{
|
||||
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UU);
|
||||
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
@ -10890,7 +10981,7 @@ instruct string_compareU(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rdx_RegI c
|
||||
%}
|
||||
|
||||
instruct string_compareLU(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rdx_RegI cnt2,
|
||||
rax_RegI result, regD tmp1, rFlagsReg cr)
|
||||
rax_RegI result, legVecS tmp1, rFlagsReg cr)
|
||||
%{
|
||||
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::LU);
|
||||
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
@ -10906,7 +10997,7 @@ instruct string_compareLU(rdi_RegP str1, rcx_RegI cnt1, rsi_RegP str2, rdx_RegI
|
||||
%}
|
||||
|
||||
instruct string_compareUL(rsi_RegP str1, rdx_RegI cnt1, rdi_RegP str2, rcx_RegI cnt2,
|
||||
rax_RegI result, regD tmp1, rFlagsReg cr)
|
||||
rax_RegI result, legVecS tmp1, rFlagsReg cr)
|
||||
%{
|
||||
predicate(((StrCompNode*)n)->encoding() == StrIntrinsicNode::UL);
|
||||
match(Set result (StrComp (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
@ -10923,7 +11014,7 @@ instruct string_compareUL(rsi_RegP str1, rdx_RegI cnt1, rdi_RegP str2, rcx_RegI
|
||||
|
||||
// fast search of substring with known size.
|
||||
instruct string_indexof_conL(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, immI int_cnt2,
|
||||
rbx_RegI result, regD vec, rax_RegI cnt2, rcx_RegI tmp, rFlagsReg cr)
|
||||
rbx_RegI result, legVecS vec, rax_RegI cnt2, rcx_RegI tmp, rFlagsReg cr)
|
||||
%{
|
||||
predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::LL));
|
||||
match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2)));
|
||||
@ -10952,7 +11043,7 @@ instruct string_indexof_conL(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, immI i
|
||||
|
||||
// fast search of substring with known size.
|
||||
instruct string_indexof_conU(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, immI int_cnt2,
|
||||
rbx_RegI result, regD vec, rax_RegI cnt2, rcx_RegI tmp, rFlagsReg cr)
|
||||
rbx_RegI result, legVecS vec, rax_RegI cnt2, rcx_RegI tmp, rFlagsReg cr)
|
||||
%{
|
||||
predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UU));
|
||||
match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2)));
|
||||
@ -10981,7 +11072,7 @@ instruct string_indexof_conU(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, immI i
|
||||
|
||||
// fast search of substring with known size.
|
||||
instruct string_indexof_conUL(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, immI int_cnt2,
|
||||
rbx_RegI result, regD vec, rax_RegI cnt2, rcx_RegI tmp, rFlagsReg cr)
|
||||
rbx_RegI result, legVecS vec, rax_RegI cnt2, rcx_RegI tmp, rFlagsReg cr)
|
||||
%{
|
||||
predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UL));
|
||||
match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 int_cnt2)));
|
||||
@ -11009,7 +11100,7 @@ instruct string_indexof_conUL(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, immI
|
||||
%}
|
||||
|
||||
instruct string_indexofL(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, rax_RegI cnt2,
|
||||
rbx_RegI result, regD vec, rcx_RegI tmp, rFlagsReg cr)
|
||||
rbx_RegI result, legVecS vec, rcx_RegI tmp, rFlagsReg cr)
|
||||
%{
|
||||
predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::LL));
|
||||
match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
@ -11026,7 +11117,7 @@ instruct string_indexofL(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, rax_RegI c
|
||||
%}
|
||||
|
||||
instruct string_indexofU(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, rax_RegI cnt2,
|
||||
rbx_RegI result, regD vec, rcx_RegI tmp, rFlagsReg cr)
|
||||
rbx_RegI result, legVecS vec, rcx_RegI tmp, rFlagsReg cr)
|
||||
%{
|
||||
predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UU));
|
||||
match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
@ -11043,7 +11134,7 @@ instruct string_indexofU(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, rax_RegI c
|
||||
%}
|
||||
|
||||
instruct string_indexofUL(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, rax_RegI cnt2,
|
||||
rbx_RegI result, regD vec, rcx_RegI tmp, rFlagsReg cr)
|
||||
rbx_RegI result, legVecS vec, rcx_RegI tmp, rFlagsReg cr)
|
||||
%{
|
||||
predicate(UseSSE42Intrinsics && (((StrIndexOfNode*)n)->encoding() == StrIntrinsicNode::UL));
|
||||
match(Set result (StrIndexOf (Binary str1 cnt1) (Binary str2 cnt2)));
|
||||
@ -11060,7 +11151,7 @@ instruct string_indexofUL(rdi_RegP str1, rdx_RegI cnt1, rsi_RegP str2, rax_RegI
|
||||
%}
|
||||
|
||||
instruct string_indexofU_char(rdi_RegP str1, rdx_RegI cnt1, rax_RegI ch,
|
||||
rbx_RegI result, regD vec1, regD vec2, regD vec3, rcx_RegI tmp, rFlagsReg cr)
|
||||
rbx_RegI result, legVecS vec1, legVecS vec2, legVecS vec3, rcx_RegI tmp, rFlagsReg cr)
|
||||
%{
|
||||
predicate(UseSSE42Intrinsics);
|
||||
match(Set result (StrIndexOfChar (Binary str1 cnt1) ch));
|
||||
@ -11075,7 +11166,7 @@ instruct string_indexofU_char(rdi_RegP str1, rdx_RegI cnt1, rax_RegI ch,
|
||||
|
||||
// fast string equals
|
||||
instruct string_equals(rdi_RegP str1, rsi_RegP str2, rcx_RegI cnt, rax_RegI result,
|
||||
regD tmp1, regD tmp2, rbx_RegI tmp3, rFlagsReg cr)
|
||||
legVecS tmp1, legVecS tmp2, rbx_RegI tmp3, rFlagsReg cr)
|
||||
%{
|
||||
match(Set result (StrEquals (Binary str1 str2) cnt));
|
||||
effect(TEMP tmp1, TEMP tmp2, USE_KILL str1, USE_KILL str2, USE_KILL cnt, KILL tmp3, KILL cr);
|
||||
@ -11091,7 +11182,7 @@ instruct string_equals(rdi_RegP str1, rsi_RegP str2, rcx_RegI cnt, rax_RegI resu
|
||||
|
||||
// fast array equals
|
||||
instruct array_equalsB(rdi_RegP ary1, rsi_RegP ary2, rax_RegI result,
|
||||
regD tmp1, regD tmp2, rcx_RegI tmp3, rbx_RegI tmp4, rFlagsReg cr)
|
||||
legVecS tmp1, legVecS tmp2, rcx_RegI tmp3, rbx_RegI tmp4, rFlagsReg cr)
|
||||
%{
|
||||
predicate(((AryEqNode*)n)->encoding() == StrIntrinsicNode::LL);
|
||||
match(Set result (AryEq ary1 ary2));
|
||||
@ -11107,7 +11198,7 @@ instruct array_equalsB(rdi_RegP ary1, rsi_RegP ary2, rax_RegI result,
|
||||
%}
|
||||
|
||||
instruct array_equalsC(rdi_RegP ary1, rsi_RegP ary2, rax_RegI result,
|
||||
regD tmp1, regD tmp2, rcx_RegI tmp3, rbx_RegI tmp4, rFlagsReg cr)
|
||||
legVecS tmp1, legVecS tmp2, rcx_RegI tmp3, rbx_RegI tmp4, rFlagsReg cr)
|
||||
%{
|
||||
predicate(((AryEqNode*)n)->encoding() == StrIntrinsicNode::UU);
|
||||
match(Set result (AryEq ary1 ary2));
|
||||
@ -11123,7 +11214,7 @@ instruct array_equalsC(rdi_RegP ary1, rsi_RegP ary2, rax_RegI result,
|
||||
%}
|
||||
|
||||
instruct has_negatives(rsi_RegP ary1, rcx_RegI len, rax_RegI result,
|
||||
regD tmp1, regD tmp2, rbx_RegI tmp3, rFlagsReg cr)
|
||||
legVecS tmp1, legVecS tmp2, rbx_RegI tmp3, rFlagsReg cr)
|
||||
%{
|
||||
match(Set result (HasNegatives ary1 len));
|
||||
effect(TEMP tmp1, TEMP tmp2, USE_KILL ary1, USE_KILL len, KILL tmp3, KILL cr);
|
||||
@ -11138,7 +11229,7 @@ instruct has_negatives(rsi_RegP ary1, rcx_RegI len, rax_RegI result,
|
||||
%}
|
||||
|
||||
// fast char[] to byte[] compression
|
||||
instruct string_compress(rsi_RegP src, rdi_RegP dst, rdx_RegI len, regD tmp1, regD tmp2, regD tmp3, regD tmp4,
|
||||
instruct string_compress(rsi_RegP src, rdi_RegP dst, rdx_RegI len, legVecS tmp1, legVecS tmp2, legVecS tmp3, legVecS tmp4,
|
||||
rcx_RegI tmp5, rax_RegI result, rFlagsReg cr) %{
|
||||
match(Set result (StrCompressedCopy src (Binary dst len)));
|
||||
effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr);
|
||||
@ -11154,7 +11245,7 @@ instruct string_compress(rsi_RegP src, rdi_RegP dst, rdx_RegI len, regD tmp1, re
|
||||
|
||||
// fast byte[] to char[] inflation
|
||||
instruct string_inflate(Universe dummy, rsi_RegP src, rdi_RegP dst, rdx_RegI len,
|
||||
regD tmp1, rcx_RegI tmp2, rFlagsReg cr) %{
|
||||
legVecS tmp1, rcx_RegI tmp2, rFlagsReg cr) %{
|
||||
match(Set dummy (StrInflatedCopy src (Binary dst len)));
|
||||
effect(TEMP tmp1, TEMP tmp2, USE_KILL src, USE_KILL dst, USE_KILL len, KILL cr);
|
||||
|
||||
@ -11168,7 +11259,7 @@ instruct string_inflate(Universe dummy, rsi_RegP src, rdi_RegP dst, rdx_RegI len
|
||||
|
||||
// encode char[] to byte[] in ISO_8859_1
|
||||
instruct encode_iso_array(rsi_RegP src, rdi_RegP dst, rdx_RegI len,
|
||||
regD tmp1, regD tmp2, regD tmp3, regD tmp4,
|
||||
legVecS tmp1, legVecS tmp2, legVecS tmp3, legVecS tmp4,
|
||||
rcx_RegI tmp5, rax_RegI result, rFlagsReg cr) %{
|
||||
match(Set result (EncodeISOArray src (Binary dst len)));
|
||||
effect(TEMP tmp1, TEMP tmp2, TEMP tmp3, TEMP tmp4, USE_KILL src, USE_KILL dst, USE_KILL len, KILL tmp5, KILL cr);
|
||||
|
@ -472,7 +472,6 @@ void LIR_OpVisitState::visit(LIR_Op* op) {
|
||||
case lir_pop: // input always valid, result and info always invalid
|
||||
case lir_return: // input always valid, result and info always invalid
|
||||
case lir_leal: // input and result always valid, info always invalid
|
||||
case lir_neg: // input and result always valid, info always invalid
|
||||
case lir_monaddr: // input and result always valid, info always invalid
|
||||
case lir_null_check: // input and info always valid, result always invalid
|
||||
case lir_move: // input and result always valid, may have info
|
||||
@ -580,6 +579,7 @@ void LIR_OpVisitState::visit(LIR_Op* op) {
|
||||
case lir_rem:
|
||||
case lir_sqrt:
|
||||
case lir_abs:
|
||||
case lir_neg:
|
||||
case lir_logic_and:
|
||||
case lir_logic_or:
|
||||
case lir_logic_xor:
|
||||
@ -1662,7 +1662,6 @@ const char * LIR_Op::name() const {
|
||||
case lir_null_check: s = "null_check"; break;
|
||||
case lir_return: s = "return"; break;
|
||||
case lir_safepoint: s = "safepoint"; break;
|
||||
case lir_neg: s = "neg"; break;
|
||||
case lir_leal: s = "leal"; break;
|
||||
case lir_branch: s = "branch"; break;
|
||||
case lir_cond_float_branch: s = "flt_cond_br"; break;
|
||||
@ -1690,6 +1689,7 @@ const char * LIR_Op::name() const {
|
||||
case lir_div_strictfp: s = "div_strictfp"; break;
|
||||
case lir_rem: s = "rem"; break;
|
||||
case lir_abs: s = "abs"; break;
|
||||
case lir_neg: s = "neg"; break;
|
||||
case lir_sqrt: s = "sqrt"; break;
|
||||
case lir_logic_and: s = "logic_and"; break;
|
||||
case lir_logic_or: s = "logic_or"; break;
|
||||
|
@ -911,7 +911,6 @@ enum LIR_Code {
|
||||
, lir_null_check
|
||||
, lir_return
|
||||
, lir_leal
|
||||
, lir_neg
|
||||
, lir_branch
|
||||
, lir_cond_float_branch
|
||||
, lir_move
|
||||
@ -939,6 +938,7 @@ enum LIR_Code {
|
||||
, lir_rem
|
||||
, lir_sqrt
|
||||
, lir_abs
|
||||
, lir_neg
|
||||
, lir_tan
|
||||
, lir_log10
|
||||
, lir_logic_and
|
||||
@ -2075,7 +2075,6 @@ class LIR_List: public CompilationResourceObj {
|
||||
|
||||
void branch_destination(Label* lbl) { append(new LIR_OpLabel(lbl)); }
|
||||
|
||||
void negate(LIR_Opr from, LIR_Opr to) { append(new LIR_Op1(lir_neg, from, to)); }
|
||||
void leal(LIR_Opr from, LIR_Opr result_reg, LIR_PatchCode patch_code = lir_patch_none, CodeEmitInfo* info = NULL) { append(new LIR_Op1(lir_leal, from, result_reg, T_ILLEGAL, patch_code, info)); }
|
||||
|
||||
// result is a stack location for old backend and vreg for UseLinearScan
|
||||
@ -2159,6 +2158,7 @@ class LIR_List: public CompilationResourceObj {
|
||||
LIR_Opr t1, LIR_Opr t2, LIR_Opr result = LIR_OprFact::illegalOpr);
|
||||
|
||||
void abs (LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_abs , from, tmp, to)); }
|
||||
void negate(LIR_Opr from, LIR_Opr to, LIR_Opr tmp = LIR_OprFact::illegalOpr) { append(new LIR_Op2(lir_neg, from, tmp, to)); }
|
||||
void sqrt(LIR_Opr from, LIR_Opr to, LIR_Opr tmp) { append(new LIR_Op2(lir_sqrt, from, tmp, to)); }
|
||||
void fmad(LIR_Opr from, LIR_Opr from1, LIR_Opr from2, LIR_Opr to) { append(new LIR_Op3(lir_fmad, from, from1, from2, to)); }
|
||||
void fmaf(LIR_Opr from, LIR_Opr from1, LIR_Opr from2, LIR_Opr to) { append(new LIR_Op3(lir_fmaf, from, from1, from2, to)); }
|
||||
|
@ -554,10 +554,6 @@ void LIR_Assembler::emit_op1(LIR_Op1* op) {
|
||||
pop(op->in_opr());
|
||||
break;
|
||||
|
||||
case lir_neg:
|
||||
negate(op->in_opr(), op->result_opr());
|
||||
break;
|
||||
|
||||
case lir_leal:
|
||||
leal(op->in_opr(), op->result_opr(), op->patch_code(), op->info());
|
||||
break;
|
||||
@ -750,6 +746,10 @@ void LIR_Assembler::emit_op2(LIR_Op2* op) {
|
||||
intrinsic_op(op->code(), op->in_opr1(), op->in_opr2(), op->result_opr(), op);
|
||||
break;
|
||||
|
||||
case lir_neg:
|
||||
negate(op->in_opr1(), op->result_opr(), op->in_opr2());
|
||||
break;
|
||||
|
||||
case lir_logic_and:
|
||||
case lir_logic_or:
|
||||
case lir_logic_xor:
|
||||
|
@ -239,7 +239,7 @@ class LIR_Assembler: public CompilationResourceObj {
|
||||
void align_backward_branch_target();
|
||||
void align_call(LIR_Code code);
|
||||
|
||||
void negate(LIR_Opr left, LIR_Opr dest);
|
||||
void negate(LIR_Opr left, LIR_Opr dest, LIR_Opr tmp = LIR_OprFact::illegalOpr);
|
||||
void leal(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_code, CodeEmitInfo* info);
|
||||
|
||||
void rt_call(LIR_Opr result, address dest, const LIR_OprList* args, LIR_Opr tmp, CodeEmitInfo* info);
|
||||
|
@ -605,9 +605,10 @@ void ClassLoaderData::unload() {
|
||||
// if they are not already on the _klasses list.
|
||||
free_deallocate_list_C_heap_structures();
|
||||
|
||||
// Tell serviceability tools these classes are unloading
|
||||
// Clean up class dependencies and tell serviceability tools
|
||||
// these classes are unloading. Must be called
|
||||
// after erroneous classes are released.
|
||||
classes_do(InstanceKlass::notify_unload_class);
|
||||
classes_do(InstanceKlass::unload_class);
|
||||
|
||||
// Clean up global class iterator for compiler
|
||||
static_klass_iterator.adjust_saved_class(this);
|
||||
|
@ -218,18 +218,6 @@ int DependencyContext::remove_all_dependents() {
|
||||
return marked;
|
||||
}
|
||||
|
||||
void DependencyContext::wipe() {
|
||||
assert_locked_or_safepoint(CodeCache_lock);
|
||||
nmethodBucket* b = dependencies();
|
||||
set_dependencies(NULL);
|
||||
set_has_stale_entries(false);
|
||||
while (b != NULL) {
|
||||
nmethodBucket* next = b->next();
|
||||
delete b;
|
||||
b = next;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
void DependencyContext::print_dependent_nmethods(bool verbose) {
|
||||
int idx = 0;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -141,10 +141,6 @@ class DependencyContext : public StackObj {
|
||||
|
||||
void expunge_stale_entries();
|
||||
|
||||
// Unsafe deallocation of nmethodBuckets. Used in IK::release_C_heap_structures
|
||||
// to clean up the context possibly containing live entries pointing to unloaded nmethods.
|
||||
void wipe();
|
||||
|
||||
#ifndef PRODUCT
|
||||
void print_dependent_nmethods(bool verbose);
|
||||
bool is_dependent_nmethod(nmethod* nm);
|
||||
|
@ -3169,18 +3169,24 @@ void G1CollectedHeap::preserve_mark_during_evac_failure(uint worker_id, oop obj,
|
||||
}
|
||||
|
||||
bool G1ParEvacuateFollowersClosure::offer_termination() {
|
||||
EventGCPhaseParallel event;
|
||||
G1ParScanThreadState* const pss = par_scan_state();
|
||||
start_term_time();
|
||||
const bool res = terminator()->offer_termination();
|
||||
end_term_time();
|
||||
event.commit(GCId::current(), pss->worker_id(), G1GCPhaseTimes::phase_name(G1GCPhaseTimes::Termination));
|
||||
return res;
|
||||
}
|
||||
|
||||
void G1ParEvacuateFollowersClosure::do_void() {
|
||||
EventGCPhaseParallel event;
|
||||
G1ParScanThreadState* const pss = par_scan_state();
|
||||
pss->trim_queue();
|
||||
event.commit(GCId::current(), pss->worker_id(), G1GCPhaseTimes::phase_name(G1GCPhaseTimes::ObjCopy));
|
||||
do {
|
||||
EventGCPhaseParallel event;
|
||||
pss->steal_and_trim_queue(queues());
|
||||
event.commit(GCId::current(), pss->worker_id(), G1GCPhaseTimes::phase_name(G1GCPhaseTimes::ObjCopy));
|
||||
} while (!offer_termination());
|
||||
}
|
||||
|
||||
@ -4050,6 +4056,7 @@ public:
|
||||
break;
|
||||
}
|
||||
|
||||
EventGCPhaseParallel event;
|
||||
double start_time = os::elapsedTime();
|
||||
|
||||
end = MIN2(end, _num_work_items);
|
||||
@ -4064,9 +4071,11 @@ public:
|
||||
if (is_young) {
|
||||
young_time += time_taken;
|
||||
has_young_time = true;
|
||||
event.commit(GCId::current(), worker_id, G1GCPhaseTimes::phase_name(G1GCPhaseTimes::YoungFreeCSet));
|
||||
} else {
|
||||
non_young_time += time_taken;
|
||||
has_non_young_time = true;
|
||||
event.commit(GCId::current(), worker_id, G1GCPhaseTimes::phase_name(G1GCPhaseTimes::NonYoungFreeCSet));
|
||||
}
|
||||
start_time = end_time;
|
||||
}
|
||||
|
@ -465,6 +465,48 @@ void G1GCPhaseTimes::print() {
|
||||
}
|
||||
}
|
||||
|
||||
const char* G1GCPhaseTimes::phase_name(GCParPhases phase) {
|
||||
static const char* names[] = {
|
||||
"GCWorkerStart",
|
||||
"ExtRootScan",
|
||||
"ThreadRoots",
|
||||
"StringTableRoots",
|
||||
"UniverseRoots",
|
||||
"JNIRoots",
|
||||
"ObjectSynchronizerRoots",
|
||||
"ManagementRoots",
|
||||
"SystemDictionaryRoots",
|
||||
"CLDGRoots",
|
||||
"JVMTIRoots",
|
||||
"CMRefRoots",
|
||||
"WaitForStrongCLD",
|
||||
"WeakCLDRoots",
|
||||
"SATBFiltering",
|
||||
"UpdateRS",
|
||||
"ScanHCC",
|
||||
"ScanRS",
|
||||
"CodeRoots",
|
||||
#if INCLUDE_AOT
|
||||
"AOTCodeRoots",
|
||||
#endif
|
||||
"ObjCopy",
|
||||
"Termination",
|
||||
"Other",
|
||||
"GCWorkerTotal",
|
||||
"GCWorkerEnd",
|
||||
"StringDedupQueueFixup",
|
||||
"StringDedupTableFixup",
|
||||
"RedirtyCards",
|
||||
"YoungFreeCSet",
|
||||
"NonYoungFreeCSet"
|
||||
//GCParPhasesSentinel only used to tell end of enum
|
||||
};
|
||||
|
||||
STATIC_ASSERT(ARRAY_SIZE(names) == G1GCPhaseTimes::GCParPhasesSentinel); // GCParPhases enum and corresponding string array should have the same "length", this tries to assert it
|
||||
|
||||
return names[phase];
|
||||
}
|
||||
|
||||
G1EvacPhaseWithTrimTimeTracker::G1EvacPhaseWithTrimTimeTracker(G1ParScanThreadState* pss, Tickspan& total_time, Tickspan& trim_time) :
|
||||
_pss(pss),
|
||||
_start(Ticks::now()),
|
||||
@ -490,7 +532,7 @@ void G1EvacPhaseWithTrimTimeTracker::stop() {
|
||||
}
|
||||
|
||||
G1GCParPhaseTimesTracker::G1GCParPhaseTimesTracker(G1GCPhaseTimes* phase_times, G1GCPhaseTimes::GCParPhases phase, uint worker_id) :
|
||||
_start_time(), _phase(phase), _phase_times(phase_times), _worker_id(worker_id) {
|
||||
_start_time(), _phase(phase), _phase_times(phase_times), _worker_id(worker_id), _event() {
|
||||
if (_phase_times != NULL) {
|
||||
_start_time = Ticks::now();
|
||||
}
|
||||
@ -499,6 +541,7 @@ G1GCParPhaseTimesTracker::G1GCParPhaseTimesTracker(G1GCPhaseTimes* phase_times,
|
||||
G1GCParPhaseTimesTracker::~G1GCParPhaseTimesTracker() {
|
||||
if (_phase_times != NULL) {
|
||||
_phase_times->record_time_secs(_phase, _worker_id, (Ticks::now() - _start_time).seconds());
|
||||
_event.commit(GCId::current(), _worker_id, G1GCPhaseTimes::phase_name(_phase));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include "gc/shared/referenceProcessorPhaseTimes.hpp"
|
||||
#include "gc/shared/weakProcessorPhaseTimes.hpp"
|
||||
#include "jfr/jfrEvents.hpp"
|
||||
#include "logging/logLevel.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "utilities/macros.hpp"
|
||||
@ -190,6 +191,7 @@ class G1GCPhaseTimes : public CHeapObj<mtGC> {
|
||||
G1GCPhaseTimes(STWGCTimer* gc_timer, uint max_gc_threads);
|
||||
void note_gc_start();
|
||||
void print();
|
||||
static const char* phase_name(GCParPhases phase);
|
||||
|
||||
// record the time a phase took in seconds
|
||||
void record_time_secs(GCParPhases phase, uint worker_i, double secs);
|
||||
@ -385,6 +387,7 @@ protected:
|
||||
G1GCPhaseTimes::GCParPhases _phase;
|
||||
G1GCPhaseTimes* _phase_times;
|
||||
uint _worker_id;
|
||||
EventGCPhaseParallel _event;
|
||||
public:
|
||||
G1GCParPhaseTimesTracker(G1GCPhaseTimes* phase_times, G1GCPhaseTimes::GCParPhases phase, uint worker_id);
|
||||
virtual ~G1GCParPhaseTimesTracker();
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "gc/g1/heapRegionRemSet.hpp"
|
||||
#include "gc/shared/gcTraceTime.inline.hpp"
|
||||
#include "gc/shared/suspendibleThreadSet.hpp"
|
||||
#include "jfr/jfrEvents.hpp"
|
||||
#include "memory/iterator.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "oops/access.inline.hpp"
|
||||
@ -339,6 +340,7 @@ void G1ScanRSForRegionClosure::scan_card(MemRegion mr, uint region_idx_for_card)
|
||||
}
|
||||
|
||||
void G1ScanRSForRegionClosure::scan_rem_set_roots(HeapRegion* r) {
|
||||
EventGCPhaseParallel event;
|
||||
uint const region_idx = r->hrm_index();
|
||||
|
||||
if (_scan_state->claim_iter(region_idx)) {
|
||||
@ -392,10 +394,13 @@ void G1ScanRSForRegionClosure::scan_rem_set_roots(HeapRegion* r) {
|
||||
|
||||
scan_card(mr, region_idx_for_card);
|
||||
}
|
||||
event.commit(GCId::current(), _worker_i, G1GCPhaseTimes::phase_name(G1GCPhaseTimes::ScanRS));
|
||||
}
|
||||
|
||||
void G1ScanRSForRegionClosure::scan_strong_code_roots(HeapRegion* r) {
|
||||
EventGCPhaseParallel event;
|
||||
r->strong_code_roots_do(_pss->closures()->weak_codeblobs());
|
||||
event.commit(GCId::current(), _worker_i, G1GCPhaseTimes::phase_name(G1GCPhaseTimes::CodeRoots));
|
||||
}
|
||||
|
||||
bool G1ScanRSForRegionClosure::do_heap_region(HeapRegion* r) {
|
||||
|
@ -310,7 +310,7 @@ void C2Access::fixup_decorators() {
|
||||
|
||||
//--------------------------- atomic operations---------------------------------
|
||||
|
||||
static void pin_atomic_op(C2AtomicAccess& access) {
|
||||
void BarrierSetC2::pin_atomic_op(C2AtomicAccess& access) const {
|
||||
if (!access.needs_pinning()) {
|
||||
return;
|
||||
}
|
||||
|
@ -76,14 +76,12 @@ public:
|
||||
|
||||
// This class wraps a node and a pointer type.
|
||||
class C2AccessValuePtr: public C2AccessValue {
|
||||
int _alias_idx;
|
||||
|
||||
public:
|
||||
C2AccessValuePtr(Node* node, const TypePtr* type) :
|
||||
C2AccessValue(node, reinterpret_cast<const Type*>(type)) {}
|
||||
|
||||
const TypePtr* type() const { return reinterpret_cast<const TypePtr*>(_type); }
|
||||
int alias_idx() const { return _alias_idx; }
|
||||
};
|
||||
|
||||
// This class wraps a bunch of context parameters thare are passed around in the
|
||||
@ -175,6 +173,7 @@ protected:
|
||||
Node* new_val, const Type* value_type) const;
|
||||
virtual Node* atomic_xchg_at_resolved(C2AtomicAccess& access, Node* new_val, const Type* val_type) const;
|
||||
virtual Node* atomic_add_at_resolved(C2AtomicAccess& access, Node* new_val, const Type* val_type) const;
|
||||
void pin_atomic_op(C2AtomicAccess& access) const;
|
||||
|
||||
public:
|
||||
// This is the entry-point for the backend to perform accesses through the Access API.
|
||||
|
@ -435,6 +435,13 @@
|
||||
<Field type="string" name="name" label="Name" />
|
||||
</Event>
|
||||
|
||||
<Event name="GCPhaseParallel" category="Java Virtual Machine, GC, Phases" label="GC Phase Parallel"
|
||||
startTime="true" thread="true" description="GC phases for parallel workers">
|
||||
<Field type="uint" name="gcId" label="GC Identifier" relation="GcId"/>
|
||||
<Field type="uint" name="gcWorkerId" label="GC Worker Identifier" />
|
||||
<Field type="string" name="name" label="Name" />
|
||||
</Event>
|
||||
|
||||
<Event name="AllocationRequiringGC" category="Java Virtual Machine, GC, Detailed" label="Allocation Requiring GC" thread="true" stackTrace="true"
|
||||
startTime="false">
|
||||
<Field type="uint" name="gcId" label="Pending GC Identifier" relation="GcId" />
|
||||
|
@ -2417,7 +2417,10 @@ static void clear_all_breakpoints(Method* m) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void InstanceKlass::notify_unload_class(InstanceKlass* ik) {
|
||||
void InstanceKlass::unload_class(InstanceKlass* ik) {
|
||||
// Release dependencies.
|
||||
ik->dependencies().remove_all_dependents();
|
||||
|
||||
// notify the debugger
|
||||
if (JvmtiExport::should_post_class_unload()) {
|
||||
JvmtiExport::post_class_unload(ik);
|
||||
@ -2462,16 +2465,8 @@ void InstanceKlass::release_C_heap_structures() {
|
||||
FreeHeap(jmeths);
|
||||
}
|
||||
|
||||
// Release dependencies.
|
||||
// It is desirable to use DC::remove_all_dependents() here, but, unfortunately,
|
||||
// it is not safe (see JDK-8143408). The problem is that the klass dependency
|
||||
// context can contain live dependencies, since there's a race between nmethod &
|
||||
// klass unloading. If the klass is dead when nmethod unloading happens, relevant
|
||||
// dependencies aren't removed from the context associated with the class (see
|
||||
// nmethod::flush_dependencies). It ends up during klass unloading as seemingly
|
||||
// live dependencies pointing to unloaded nmethods and causes a crash in
|
||||
// DC::remove_all_dependents() when it touches unloaded nmethod.
|
||||
dependencies().wipe();
|
||||
assert(_dep_context == DependencyContext::EMPTY,
|
||||
"dependencies should already be cleaned");
|
||||
|
||||
#if INCLUDE_JVMTI
|
||||
// Deallocate breakpoint records
|
||||
|
@ -1180,7 +1180,7 @@ public:
|
||||
bool on_stack() const { return _constants->on_stack(); }
|
||||
|
||||
// callbacks for actions during class unloading
|
||||
static void notify_unload_class(InstanceKlass* ik);
|
||||
static void unload_class(InstanceKlass* ik);
|
||||
static void release_C_heap_structures(InstanceKlass* ik);
|
||||
|
||||
// Naming
|
||||
|
@ -605,7 +605,7 @@ void GraphKit::builtin_throw(Deoptimization::DeoptReason reason, Node* arg) {
|
||||
|
||||
Node *adr = basic_plus_adr(ex_node, ex_node, offset);
|
||||
const TypeOopPtr* val_type = TypeOopPtr::make_from_klass(env()->String_klass());
|
||||
Node *store = access_store_at(control(), ex_node, adr, adr_typ, null(), val_type, T_OBJECT, IN_HEAP);
|
||||
Node *store = access_store_at(ex_node, adr, adr_typ, null(), val_type, T_OBJECT, IN_HEAP);
|
||||
|
||||
add_exception_state(make_exception_state(ex_node));
|
||||
return;
|
||||
@ -1544,8 +1544,7 @@ Node* GraphKit::store_to_memory(Node* ctl, Node* adr, Node *val, BasicType bt,
|
||||
return st;
|
||||
}
|
||||
|
||||
Node* GraphKit::access_store_at(Node* ctl,
|
||||
Node* obj,
|
||||
Node* GraphKit::access_store_at(Node* obj,
|
||||
Node* adr,
|
||||
const TypePtr* adr_type,
|
||||
Node* val,
|
||||
@ -1559,7 +1558,6 @@ Node* GraphKit::access_store_at(Node* ctl,
|
||||
val = _gvn.makecon(TypePtr::NULL_PTR);
|
||||
}
|
||||
|
||||
set_control(ctl);
|
||||
if (stopped()) {
|
||||
return top(); // Dead path ?
|
||||
}
|
||||
@ -1612,8 +1610,7 @@ Node* GraphKit::access_load(Node* adr, // actual adress to load val at
|
||||
}
|
||||
}
|
||||
|
||||
Node* GraphKit::access_atomic_cmpxchg_val_at(Node* ctl,
|
||||
Node* obj,
|
||||
Node* GraphKit::access_atomic_cmpxchg_val_at(Node* obj,
|
||||
Node* adr,
|
||||
const TypePtr* adr_type,
|
||||
int alias_idx,
|
||||
@ -1622,7 +1619,6 @@ Node* GraphKit::access_atomic_cmpxchg_val_at(Node* ctl,
|
||||
const Type* value_type,
|
||||
BasicType bt,
|
||||
DecoratorSet decorators) {
|
||||
set_control(ctl);
|
||||
C2AccessValuePtr addr(adr, adr_type);
|
||||
C2AtomicAccess access(this, decorators | C2_READ_ACCESS | C2_WRITE_ACCESS,
|
||||
bt, obj, addr, alias_idx);
|
||||
@ -1633,8 +1629,7 @@ Node* GraphKit::access_atomic_cmpxchg_val_at(Node* ctl,
|
||||
}
|
||||
}
|
||||
|
||||
Node* GraphKit::access_atomic_cmpxchg_bool_at(Node* ctl,
|
||||
Node* obj,
|
||||
Node* GraphKit::access_atomic_cmpxchg_bool_at(Node* obj,
|
||||
Node* adr,
|
||||
const TypePtr* adr_type,
|
||||
int alias_idx,
|
||||
@ -1643,7 +1638,6 @@ Node* GraphKit::access_atomic_cmpxchg_bool_at(Node* ctl,
|
||||
const Type* value_type,
|
||||
BasicType bt,
|
||||
DecoratorSet decorators) {
|
||||
set_control(ctl);
|
||||
C2AccessValuePtr addr(adr, adr_type);
|
||||
C2AtomicAccess access(this, decorators | C2_READ_ACCESS | C2_WRITE_ACCESS,
|
||||
bt, obj, addr, alias_idx);
|
||||
@ -1654,8 +1648,7 @@ Node* GraphKit::access_atomic_cmpxchg_bool_at(Node* ctl,
|
||||
}
|
||||
}
|
||||
|
||||
Node* GraphKit::access_atomic_xchg_at(Node* ctl,
|
||||
Node* obj,
|
||||
Node* GraphKit::access_atomic_xchg_at(Node* obj,
|
||||
Node* adr,
|
||||
const TypePtr* adr_type,
|
||||
int alias_idx,
|
||||
@ -1663,7 +1656,6 @@ Node* GraphKit::access_atomic_xchg_at(Node* ctl,
|
||||
const Type* value_type,
|
||||
BasicType bt,
|
||||
DecoratorSet decorators) {
|
||||
set_control(ctl);
|
||||
C2AccessValuePtr addr(adr, adr_type);
|
||||
C2AtomicAccess access(this, decorators | C2_READ_ACCESS | C2_WRITE_ACCESS,
|
||||
bt, obj, addr, alias_idx);
|
||||
@ -1674,8 +1666,7 @@ Node* GraphKit::access_atomic_xchg_at(Node* ctl,
|
||||
}
|
||||
}
|
||||
|
||||
Node* GraphKit::access_atomic_add_at(Node* ctl,
|
||||
Node* obj,
|
||||
Node* GraphKit::access_atomic_add_at(Node* obj,
|
||||
Node* adr,
|
||||
const TypePtr* adr_type,
|
||||
int alias_idx,
|
||||
@ -1683,7 +1674,6 @@ Node* GraphKit::access_atomic_add_at(Node* ctl,
|
||||
const Type* value_type,
|
||||
BasicType bt,
|
||||
DecoratorSet decorators) {
|
||||
set_control(ctl);
|
||||
C2AccessValuePtr addr(adr, adr_type);
|
||||
C2AtomicAccess access(this, decorators | C2_READ_ACCESS | C2_WRITE_ACCESS, bt, obj, addr, alias_idx);
|
||||
if (access.is_raw()) {
|
||||
@ -1693,8 +1683,7 @@ Node* GraphKit::access_atomic_add_at(Node* ctl,
|
||||
}
|
||||
}
|
||||
|
||||
void GraphKit::access_clone(Node* ctl, Node* src, Node* dst, Node* size, bool is_array) {
|
||||
set_control(ctl);
|
||||
void GraphKit::access_clone(Node* src, Node* dst, Node* size, bool is_array) {
|
||||
return _barrier_set->clone(this, src, dst, size, is_array);
|
||||
}
|
||||
|
||||
@ -3849,14 +3838,14 @@ void GraphKit::final_sync(IdealKit& ideal) {
|
||||
sync_kit(ideal);
|
||||
}
|
||||
|
||||
Node* GraphKit::load_String_length(Node* ctrl, Node* str) {
|
||||
Node* len = load_array_length(load_String_value(ctrl, str));
|
||||
Node* coder = load_String_coder(ctrl, str);
|
||||
Node* GraphKit::load_String_length(Node* str, bool set_ctrl) {
|
||||
Node* len = load_array_length(load_String_value(str, set_ctrl));
|
||||
Node* coder = load_String_coder(str, set_ctrl);
|
||||
// Divide length by 2 if coder is UTF16
|
||||
return _gvn.transform(new RShiftINode(len, coder));
|
||||
}
|
||||
|
||||
Node* GraphKit::load_String_value(Node* ctrl, Node* str) {
|
||||
Node* GraphKit::load_String_value(Node* str, bool set_ctrl) {
|
||||
int value_offset = java_lang_String::value_offset_in_bytes();
|
||||
const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(),
|
||||
false, NULL, 0);
|
||||
@ -3866,7 +3855,7 @@ Node* GraphKit::load_String_value(Node* ctrl, Node* str) {
|
||||
ciTypeArrayKlass::make(T_BYTE), true, 0);
|
||||
Node* p = basic_plus_adr(str, str, value_offset);
|
||||
Node* load = access_load_at(str, p, value_field_type, value_type, T_OBJECT,
|
||||
IN_HEAP | C2_CONTROL_DEPENDENT_LOAD);
|
||||
IN_HEAP | (set_ctrl ? C2_CONTROL_DEPENDENT_LOAD : 0) | MO_UNORDERED);
|
||||
// String.value field is known to be @Stable.
|
||||
if (UseImplicitStableValues) {
|
||||
load = cast_array_to_stable(load, value_type);
|
||||
@ -3874,7 +3863,7 @@ Node* GraphKit::load_String_value(Node* ctrl, Node* str) {
|
||||
return load;
|
||||
}
|
||||
|
||||
Node* GraphKit::load_String_coder(Node* ctrl, Node* str) {
|
||||
Node* GraphKit::load_String_coder(Node* str, bool set_ctrl) {
|
||||
if (!CompactStrings) {
|
||||
return intcon(java_lang_String::CODER_UTF16);
|
||||
}
|
||||
@ -3883,27 +3872,31 @@ Node* GraphKit::load_String_coder(Node* ctrl, Node* str) {
|
||||
false, NULL, 0);
|
||||
const TypePtr* coder_field_type = string_type->add_offset(coder_offset);
|
||||
int coder_field_idx = C->get_alias_index(coder_field_type);
|
||||
return make_load(ctrl, basic_plus_adr(str, str, coder_offset),
|
||||
TypeInt::BYTE, T_BYTE, coder_field_idx, MemNode::unordered);
|
||||
|
||||
Node* p = basic_plus_adr(str, str, coder_offset);
|
||||
Node* load = access_load_at(str, p, coder_field_type, TypeInt::BYTE, T_BYTE,
|
||||
IN_HEAP | (set_ctrl ? C2_CONTROL_DEPENDENT_LOAD : 0) | MO_UNORDERED);
|
||||
return load;
|
||||
}
|
||||
|
||||
void GraphKit::store_String_value(Node* ctrl, Node* str, Node* value) {
|
||||
void GraphKit::store_String_value(Node* str, Node* value) {
|
||||
int value_offset = java_lang_String::value_offset_in_bytes();
|
||||
const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(),
|
||||
false, NULL, 0);
|
||||
const TypePtr* value_field_type = string_type->add_offset(value_offset);
|
||||
access_store_at(ctrl, str, basic_plus_adr(str, value_offset), value_field_type,
|
||||
value, TypeAryPtr::BYTES, T_OBJECT, IN_HEAP);
|
||||
|
||||
access_store_at(str, basic_plus_adr(str, value_offset), value_field_type,
|
||||
value, TypeAryPtr::BYTES, T_OBJECT, IN_HEAP | MO_UNORDERED);
|
||||
}
|
||||
|
||||
void GraphKit::store_String_coder(Node* ctrl, Node* str, Node* value) {
|
||||
void GraphKit::store_String_coder(Node* str, Node* value) {
|
||||
int coder_offset = java_lang_String::coder_offset_in_bytes();
|
||||
const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(),
|
||||
false, NULL, 0);
|
||||
const TypePtr* coder_field_type = string_type->add_offset(coder_offset);
|
||||
int coder_field_idx = C->get_alias_index(coder_field_type);
|
||||
store_to_memory(ctrl, basic_plus_adr(str, coder_offset),
|
||||
value, T_BYTE, coder_field_idx, MemNode::unordered);
|
||||
|
||||
access_store_at(str, basic_plus_adr(str, coder_offset), coder_field_type,
|
||||
value, TypeInt::BYTE, T_BYTE, IN_HEAP | MO_UNORDERED);
|
||||
}
|
||||
|
||||
// Capture src and dst memory state with a MergeMemNode
|
||||
|
@ -572,8 +572,7 @@ class GraphKit : public Phase {
|
||||
|
||||
// Perform decorated accesses
|
||||
|
||||
Node* access_store_at(Node* ctl,
|
||||
Node* obj, // containing obj
|
||||
Node* access_store_at(Node* obj, // containing obj
|
||||
Node* adr, // actual adress to store val at
|
||||
const TypePtr* adr_type,
|
||||
Node* val,
|
||||
@ -593,8 +592,7 @@ class GraphKit : public Phase {
|
||||
BasicType bt,
|
||||
DecoratorSet decorators);
|
||||
|
||||
Node* access_atomic_cmpxchg_val_at(Node* ctl,
|
||||
Node* obj,
|
||||
Node* access_atomic_cmpxchg_val_at(Node* obj,
|
||||
Node* adr,
|
||||
const TypePtr* adr_type,
|
||||
int alias_idx,
|
||||
@ -604,8 +602,7 @@ class GraphKit : public Phase {
|
||||
BasicType bt,
|
||||
DecoratorSet decorators);
|
||||
|
||||
Node* access_atomic_cmpxchg_bool_at(Node* ctl,
|
||||
Node* obj,
|
||||
Node* access_atomic_cmpxchg_bool_at(Node* obj,
|
||||
Node* adr,
|
||||
const TypePtr* adr_type,
|
||||
int alias_idx,
|
||||
@ -615,8 +612,7 @@ class GraphKit : public Phase {
|
||||
BasicType bt,
|
||||
DecoratorSet decorators);
|
||||
|
||||
Node* access_atomic_xchg_at(Node* ctl,
|
||||
Node* obj,
|
||||
Node* access_atomic_xchg_at(Node* obj,
|
||||
Node* adr,
|
||||
const TypePtr* adr_type,
|
||||
int alias_idx,
|
||||
@ -625,8 +621,7 @@ class GraphKit : public Phase {
|
||||
BasicType bt,
|
||||
DecoratorSet decorators);
|
||||
|
||||
Node* access_atomic_add_at(Node* ctl,
|
||||
Node* obj,
|
||||
Node* access_atomic_add_at(Node* obj,
|
||||
Node* adr,
|
||||
const TypePtr* adr_type,
|
||||
int alias_idx,
|
||||
@ -635,7 +630,7 @@ class GraphKit : public Phase {
|
||||
BasicType bt,
|
||||
DecoratorSet decorators);
|
||||
|
||||
void access_clone(Node* ctl, Node* src, Node* dst, Node* size, bool is_array);
|
||||
void access_clone(Node* src, Node* dst, Node* size, bool is_array);
|
||||
|
||||
Node* access_resolve(Node* n, DecoratorSet decorators);
|
||||
|
||||
@ -849,11 +844,11 @@ class GraphKit : public Phase {
|
||||
bool deoptimize_on_exception = false);
|
||||
|
||||
// java.lang.String helpers
|
||||
Node* load_String_length(Node* ctrl, Node* str);
|
||||
Node* load_String_value(Node* ctrl, Node* str);
|
||||
Node* load_String_coder(Node* ctrl, Node* str);
|
||||
void store_String_value(Node* ctrl, Node* str, Node* value);
|
||||
void store_String_coder(Node* ctrl, Node* str, Node* value);
|
||||
Node* load_String_length(Node* str, bool set_ctrl);
|
||||
Node* load_String_value(Node* str, bool set_ctrl);
|
||||
Node* load_String_coder(Node* str, bool set_ctrl);
|
||||
void store_String_value(Node* str, Node* value);
|
||||
void store_String_coder(Node* str, Node* value);
|
||||
Node* capture_memory(const TypePtr* src_type, const TypePtr* dst_type);
|
||||
Node* compress_string(Node* src, const TypeAryPtr* src_type, Node* dst, Node* count);
|
||||
void inflate_string(Node* src, Node* dst, const TypeAryPtr* dst_type, Node* count);
|
||||
|
@ -543,10 +543,7 @@ bool LibraryCallKit::try_to_inline(int predicate) {
|
||||
|
||||
case vmIntrinsics::_notify:
|
||||
case vmIntrinsics::_notifyAll:
|
||||
if (ObjectMonitor::Knob_InlineNotify) {
|
||||
return inline_notify(intrinsic_id());
|
||||
}
|
||||
return false;
|
||||
return inline_notify(intrinsic_id());
|
||||
|
||||
case vmIntrinsics::_addExactI: return inline_math_addExactI(false /* add */);
|
||||
case vmIntrinsics::_addExactL: return inline_math_addExactL(false /* add */);
|
||||
@ -1761,11 +1758,9 @@ bool LibraryCallKit::inline_string_char_access(bool is_store) {
|
||||
return false;
|
||||
}
|
||||
if (is_store) {
|
||||
(void) store_to_memory(control(), adr, ch, T_CHAR, TypeAryPtr::BYTES, MemNode::unordered,
|
||||
false, false, true /* mismatched */);
|
||||
access_store_at(value, adr, TypeAryPtr::BYTES, ch, TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED);
|
||||
} else {
|
||||
ch = make_load(control(), adr, TypeInt::CHAR, T_CHAR, TypeAryPtr::BYTES, MemNode::unordered,
|
||||
LoadNode::DependsOnlyOnTest, false, false, true /* mismatched */);
|
||||
ch = access_load_at(value, adr, TypeAryPtr::BYTES, TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED | C2_CONTROL_DEPENDENT_LOAD);
|
||||
set_result(ch);
|
||||
}
|
||||
return true;
|
||||
@ -2515,7 +2510,7 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c
|
||||
val = ConvL2X(val);
|
||||
val = gvn().transform(new CastX2PNode(val));
|
||||
}
|
||||
access_store_at(control(), heap_base_oop, adr, adr_type, val, value_type, type, decorators);
|
||||
access_store_at(heap_base_oop, adr, adr_type, val, value_type, type, decorators);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -2734,24 +2729,24 @@ bool LibraryCallKit::inline_unsafe_load_store(const BasicType type, const LoadSt
|
||||
Node* result = NULL;
|
||||
switch (kind) {
|
||||
case LS_cmp_exchange: {
|
||||
result = access_atomic_cmpxchg_val_at(control(), base, adr, adr_type, alias_idx,
|
||||
result = access_atomic_cmpxchg_val_at(base, adr, adr_type, alias_idx,
|
||||
oldval, newval, value_type, type, decorators);
|
||||
break;
|
||||
}
|
||||
case LS_cmp_swap_weak:
|
||||
decorators |= C2_WEAK_CMPXCHG;
|
||||
case LS_cmp_swap: {
|
||||
result = access_atomic_cmpxchg_bool_at(control(), base, adr, adr_type, alias_idx,
|
||||
result = access_atomic_cmpxchg_bool_at(base, adr, adr_type, alias_idx,
|
||||
oldval, newval, value_type, type, decorators);
|
||||
break;
|
||||
}
|
||||
case LS_get_set: {
|
||||
result = access_atomic_xchg_at(control(), base, adr, adr_type, alias_idx,
|
||||
result = access_atomic_xchg_at(base, adr, adr_type, alias_idx,
|
||||
newval, value_type, type, decorators);
|
||||
break;
|
||||
}
|
||||
case LS_get_add: {
|
||||
result = access_atomic_add_at(control(), base, adr, adr_type, alias_idx,
|
||||
result = access_atomic_add_at(base, adr, adr_type, alias_idx,
|
||||
newval, value_type, type, decorators);
|
||||
break;
|
||||
}
|
||||
@ -4235,7 +4230,7 @@ void LibraryCallKit::copy_to_clone(Node* obj, Node* alloc_obj, Node* obj_size, b
|
||||
// TODO: generate fields copies for small objects instead.
|
||||
Node* size = _gvn.transform(obj_size);
|
||||
|
||||
access_clone(control(), obj, alloc_obj, size, is_array);
|
||||
access_clone(obj, alloc_obj, size, is_array);
|
||||
|
||||
// Do not let reads from the cloned object float above the arraycopy.
|
||||
if (alloc != NULL) {
|
||||
|
@ -104,7 +104,7 @@ void Parse::array_store(BasicType bt) {
|
||||
|
||||
const TypeAryPtr* adr_type = TypeAryPtr::get_array_body_type(bt);
|
||||
|
||||
access_store_at(control(), array, adr, adr_type, val, elemtype, bt, MO_UNORDERED | IN_HEAP | IS_ARRAY);
|
||||
access_store_at(array, adr, adr_type, val, elemtype, bt, MO_UNORDERED | IN_HEAP | IS_ARRAY);
|
||||
}
|
||||
|
||||
|
||||
|
@ -264,7 +264,7 @@ void Parse::do_put_xxx(Node* obj, ciField* field, bool is_field) {
|
||||
field_type = Type::BOTTOM;
|
||||
}
|
||||
}
|
||||
access_store_at(control(), obj, adr, adr_type, val, field_type, bt, decorators);
|
||||
access_store_at(obj, adr, adr_type, val, field_type, bt, decorators);
|
||||
|
||||
if (is_field) {
|
||||
// Remember we wrote a volatile field.
|
||||
@ -351,7 +351,7 @@ Node* Parse::expand_multianewarray(ciArrayKlass* array_klass, Node* *lengths, in
|
||||
Node* elem = expand_multianewarray(array_klass_1, &lengths[1], ndimensions-1, nargs);
|
||||
intptr_t offset = header + ((intptr_t)i << LogBytesPerHeapOop);
|
||||
Node* eaddr = basic_plus_adr(array, offset);
|
||||
access_store_at(control(), array, eaddr, adr_type, elem, elemtype, T_OBJECT, IN_HEAP | IS_ARRAY);
|
||||
access_store_at(array, eaddr, adr_type, elem, elemtype, T_OBJECT, IN_HEAP | IS_ARRAY);
|
||||
}
|
||||
}
|
||||
return array;
|
||||
|
@ -437,9 +437,9 @@ void Parse::set_md_flag_at(ciMethodData* md, ciProfileData* data, int flag_const
|
||||
Node* adr_node = method_data_addressing(md, data, DataLayout::flags_offset());
|
||||
|
||||
const TypePtr* adr_type = _gvn.type(adr_node)->is_ptr();
|
||||
Node* flags = make_load(NULL, adr_node, TypeInt::BYTE, T_BYTE, adr_type, MemNode::unordered);
|
||||
Node* flags = make_load(NULL, adr_node, TypeInt::INT, T_INT, adr_type, MemNode::unordered);
|
||||
Node* incr = _gvn.transform(new OrINode(flags, _gvn.intcon(flag_constant)));
|
||||
store_to_memory(NULL, adr_node, incr, T_BYTE, adr_type, MemNode::unordered);
|
||||
store_to_memory(NULL, adr_node, incr, T_INT, adr_type, MemNode::unordered);
|
||||
}
|
||||
|
||||
//----------------------------profile_taken_branch-----------------------------
|
||||
|
@ -1547,7 +1547,7 @@ void PhaseStringOpts::copy_constant_string(GraphKit& kit, IdealKit& ideal, ciTyp
|
||||
|
||||
// Compress copy contents of the byte/char String str into dst_array starting at index start.
|
||||
Node* PhaseStringOpts::copy_string(GraphKit& kit, Node* str, Node* dst_array, Node* dst_coder, Node* start) {
|
||||
Node* src_array = kit.load_String_value(kit.control(), str);
|
||||
Node* src_array = kit.load_String_value(str, true);
|
||||
src_array = kit.access_resolve(src_array, ACCESS_READ);
|
||||
|
||||
IdealKit ideal(&kit, true, true);
|
||||
@ -1580,7 +1580,7 @@ Node* PhaseStringOpts::copy_string(GraphKit& kit, Node* str, Node* dst_array, No
|
||||
// Non-constant source string
|
||||
if (CompactStrings) {
|
||||
// Emit runtime check for coder
|
||||
Node* coder = kit.load_String_coder(__ ctrl(), str);
|
||||
Node* coder = kit.load_String_coder(str, true);
|
||||
__ if_then(coder, BoolTest::eq, __ ConI(java_lang_String::CODER_LATIN1)); {
|
||||
// Source is Latin1
|
||||
copy_latin1_string(kit, ideal, src_array, count, dst_array, dst_coder, start);
|
||||
@ -1796,8 +1796,8 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) {
|
||||
// replace the argument with the null checked version
|
||||
arg = null_string;
|
||||
sc->set_argument(argi, arg);
|
||||
count = kit.load_String_length(kit.control(), arg);
|
||||
arg_coder = kit.load_String_coder(kit.control(), arg);
|
||||
count = kit.load_String_length(arg, true);
|
||||
arg_coder = kit.load_String_coder(arg, true);
|
||||
} else if (!type->higher_equal(TypeInstPtr::NOTNULL)) {
|
||||
// s = s != null ? s : "null";
|
||||
// length = length + (s.count - s.offset);
|
||||
@ -1820,14 +1820,14 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) {
|
||||
// replace the argument with the null checked version
|
||||
arg = phi;
|
||||
sc->set_argument(argi, arg);
|
||||
count = kit.load_String_length(kit.control(), arg);
|
||||
arg_coder = kit.load_String_coder(kit.control(), arg);
|
||||
count = kit.load_String_length(arg, true);
|
||||
arg_coder = kit.load_String_coder(arg, true);
|
||||
} else {
|
||||
// A corresponding nullcheck will be connected during IGVN MemNode::Ideal_common_DU_postCCP
|
||||
// kit.control might be a different test, that can be hoisted above the actual nullcheck
|
||||
// in case, that the control input is not null, Ideal_common_DU_postCCP will not look for a nullcheck.
|
||||
count = kit.load_String_length(NULL, arg);
|
||||
arg_coder = kit.load_String_coder(NULL, arg);
|
||||
count = kit.load_String_length(arg, false);
|
||||
arg_coder = kit.load_String_coder(arg, false);
|
||||
}
|
||||
if (arg->is_Con()) {
|
||||
// Constant string. Get constant coder and length.
|
||||
@ -1918,7 +1918,7 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) {
|
||||
sc->mode(0) == StringConcat::StringNullCheckMode)) {
|
||||
// Handle the case when there is only a single String argument.
|
||||
// In this case, we can just pull the value from the String itself.
|
||||
dst_array = kit.load_String_value(kit.control(), sc->argument(0));
|
||||
dst_array = kit.load_String_value(sc->argument(0), true);
|
||||
} else {
|
||||
// Allocate destination byte array according to coder
|
||||
dst_array = allocate_byte_array(kit, NULL, __ LShiftI(length, coder));
|
||||
@ -1959,8 +1959,8 @@ void PhaseStringOpts::replace_string_concat(StringConcat* sc) {
|
||||
}
|
||||
|
||||
// Initialize the string
|
||||
kit.store_String_value(kit.control(), result, dst_array);
|
||||
kit.store_String_coder(kit.control(), result, coder);
|
||||
kit.store_String_value(result, dst_array);
|
||||
kit.store_String_coder(result, coder);
|
||||
|
||||
// The value field is final. Emit a barrier here to ensure that the effect
|
||||
// of the initialization is committed to memory before any code publishes
|
||||
|
@ -1522,6 +1522,37 @@ Node *BoolNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
return new BoolNode( ncmp, _test._test );
|
||||
}
|
||||
|
||||
// Change "bool eq/ne (cmp (phi (X -X) 0))" into "bool eq/ne (cmp X 0)"
|
||||
// since zero check of conditional negation of an integer is equal to
|
||||
// zero check of the integer directly.
|
||||
if ((_test._test == BoolTest::eq || _test._test == BoolTest::ne) &&
|
||||
(cop == Op_CmpI) &&
|
||||
(cmp2_type == TypeInt::ZERO) &&
|
||||
(cmp1_op == Op_Phi)) {
|
||||
// There should be a diamond phi with true path at index 1 or 2
|
||||
PhiNode *phi = cmp1->as_Phi();
|
||||
int idx_true = phi->is_diamond_phi();
|
||||
if (idx_true != 0) {
|
||||
// True input is in(idx_true) while false input is in(3 - idx_true)
|
||||
Node *tin = phi->in(idx_true);
|
||||
Node *fin = phi->in(3 - idx_true);
|
||||
if ((tin->Opcode() == Op_SubI) &&
|
||||
(phase->type(tin->in(1)) == TypeInt::ZERO) &&
|
||||
(tin->in(2) == fin)) {
|
||||
// Found conditional negation at true path, create a new CmpINode without that
|
||||
Node *ncmp = phase->transform(new CmpINode(fin, cmp2));
|
||||
return new BoolNode(ncmp, _test._test);
|
||||
}
|
||||
if ((fin->Opcode() == Op_SubI) &&
|
||||
(phase->type(fin->in(1)) == TypeInt::ZERO) &&
|
||||
(fin->in(2) == tin)) {
|
||||
// Found conditional negation at false path, create a new CmpINode without that
|
||||
Node *ncmp = phase->transform(new CmpINode(tin, cmp2));
|
||||
return new BoolNode(ncmp, _test._test);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Change (-A vs 0) into (A vs 0) by commuting the test. Disallow in the
|
||||
// most general case because negating 0x80000000 does nothing. Needed for
|
||||
// the CmpF3/SubI/CmpI idiom.
|
||||
|
@ -542,6 +542,7 @@ static SpecialFlag const special_jvm_flags[] = {
|
||||
{ "CreateMinidumpOnCrash", JDK_Version::jdk(9), JDK_Version::undefined(), JDK_Version::undefined() },
|
||||
{ "MustCallLoadClassInternal", JDK_Version::jdk(10), JDK_Version::jdk(11), JDK_Version::jdk(12) },
|
||||
{ "UnsyncloadClass", JDK_Version::jdk(10), JDK_Version::jdk(11), JDK_Version::jdk(12) },
|
||||
{ "TLABStats", JDK_Version::jdk(12), JDK_Version::undefined(), JDK_Version::undefined() },
|
||||
|
||||
// -------------- Obsolete Flags - sorted by expired_in --------------
|
||||
{ "CheckAssertionStatusDirectives",JDK_Version::undefined(), JDK_Version::jdk(11), JDK_Version::jdk(12) },
|
||||
@ -577,6 +578,7 @@ static SpecialFlag const special_jvm_flags[] = {
|
||||
{ "EmitSync", JDK_Version::undefined(), JDK_Version::jdk(12), JDK_Version::jdk(13) },
|
||||
{ "SyncVerbose", JDK_Version::undefined(), JDK_Version::jdk(12), JDK_Version::jdk(13) },
|
||||
{ "SyncFlags", JDK_Version::undefined(), JDK_Version::jdk(12), JDK_Version::jdk(13) },
|
||||
{ "SyncKnobs", JDK_Version::undefined(), JDK_Version::jdk(12), JDK_Version::jdk(13) },
|
||||
|
||||
#ifdef TEST_VERIFY_SPECIAL_JVM_FLAGS
|
||||
{ "dep > obs", JDK_Version::jdk(9), JDK_Version::jdk(8), JDK_Version::undefined() },
|
||||
|
@ -827,9 +827,6 @@ define_pd_global(uint64_t,MaxRAM, 1ULL*G);
|
||||
"Use LWP-based instead of libthread-based synchronization " \
|
||||
"(SPARC only)") \
|
||||
\
|
||||
experimental(ccstr, SyncKnobs, NULL, \
|
||||
"(Unstable) Various monitor synchronization tunables") \
|
||||
\
|
||||
product(intx, MonitorBound, 0, "Bound Monitor population") \
|
||||
range(0, max_jint) \
|
||||
\
|
||||
|
@ -101,39 +101,15 @@
|
||||
// The knob* variables are effectively final. Once set they should
|
||||
// never be modified hence. Consider using __read_mostly with GCC.
|
||||
|
||||
int ObjectMonitor::Knob_ExitRelease = 0;
|
||||
int ObjectMonitor::Knob_InlineNotify = 1;
|
||||
int ObjectMonitor::Knob_Verbose = 0;
|
||||
int ObjectMonitor::Knob_VerifyInUse = 0;
|
||||
int ObjectMonitor::Knob_VerifyMatch = 0;
|
||||
int ObjectMonitor::Knob_SpinLimit = 5000; // derived by an external tool -
|
||||
|
||||
static int Knob_ReportSettings = 0;
|
||||
static int Knob_SpinBase = 0; // Floor AKA SpinMin
|
||||
static int Knob_SpinBackOff = 0; // spin-loop backoff
|
||||
static int Knob_CASPenalty = -1; // Penalty for failed CAS
|
||||
static int Knob_OXPenalty = -1; // Penalty for observed _owner change
|
||||
static int Knob_SpinSetSucc = 1; // spinners set the _succ field
|
||||
static int Knob_SpinEarly = 1;
|
||||
static int Knob_SuccEnabled = 1; // futile wake throttling
|
||||
static int Knob_SuccRestrict = 0; // Limit successors + spinners to at-most-one
|
||||
static int Knob_MaxSpinners = -1; // Should be a function of # CPUs
|
||||
static int Knob_Bonus = 100; // spin success bonus
|
||||
static int Knob_BonusB = 100; // spin success bonus
|
||||
static int Knob_Penalty = 200; // spin failure penalty
|
||||
static int Knob_Poverty = 1000;
|
||||
static int Knob_SpinAfterFutile = 1; // Spin after returning from park()
|
||||
static int Knob_FixedSpin = 0;
|
||||
static int Knob_OState = 3; // Spinner checks thread state of _owner
|
||||
static int Knob_UsePause = 1;
|
||||
static int Knob_ExitPolicy = 0;
|
||||
static int Knob_PreSpin = 10; // 20-100 likely better
|
||||
static int Knob_ResetEvent = 0;
|
||||
static int BackOffMask = 0;
|
||||
|
||||
static int Knob_FastHSSEC = 0;
|
||||
static int Knob_MoveNotifyee = 2; // notify() - disposition of notifyee
|
||||
static int Knob_QMode = 0; // EntryList-cxq policy - queue discipline
|
||||
static volatile int InitDone = 0;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -299,7 +275,7 @@ void ObjectMonitor::enter(TRAPS) {
|
||||
// transitions. The following spin is strictly optional ...
|
||||
// Note that if we acquire the monitor from an initial spin
|
||||
// we forgo posting JVMTI events and firing DTRACE probes.
|
||||
if (Knob_SpinEarly && TrySpin (Self) > 0) {
|
||||
if (TrySpin(Self) > 0) {
|
||||
assert(_owner == Self, "invariant");
|
||||
assert(_recursions == 0, "invariant");
|
||||
assert(((oop)(object()))->mark() == markOopDesc::encode(this), "invariant");
|
||||
@ -461,7 +437,7 @@ void ObjectMonitor::EnterI(TRAPS) {
|
||||
// to the owner. This has subtle but beneficial affinity
|
||||
// effects.
|
||||
|
||||
if (TrySpin (Self) > 0) {
|
||||
if (TrySpin(Self) > 0) {
|
||||
assert(_owner == Self, "invariant");
|
||||
assert(_succ != Self, "invariant");
|
||||
assert(_Responsible != Self, "invariant");
|
||||
@ -583,20 +559,14 @@ void ObjectMonitor::EnterI(TRAPS) {
|
||||
// We can defer clearing _succ until after the spin completes
|
||||
// TrySpin() must tolerate being called with _succ == Self.
|
||||
// Try yet another round of adaptive spinning.
|
||||
if ((Knob_SpinAfterFutile & 1) && TrySpin(Self) > 0) break;
|
||||
if (TrySpin(Self) > 0) break;
|
||||
|
||||
// We can find that we were unpark()ed and redesignated _succ while
|
||||
// we were spinning. That's harmless. If we iterate and call park(),
|
||||
// park() will consume the event and return immediately and we'll
|
||||
// just spin again. This pattern can repeat, leaving _succ to simply
|
||||
// spin on a CPU. Enable Knob_ResetEvent to clear pending unparks().
|
||||
// Alternately, we can sample fired() here, and if set, forgo spinning
|
||||
// in the next iteration.
|
||||
// spin on a CPU.
|
||||
|
||||
if ((Knob_ResetEvent & 1) && Self->_ParkEvent->fired()) {
|
||||
Self->_ParkEvent->reset();
|
||||
OrderAccess::fence();
|
||||
}
|
||||
if (_succ == Self) _succ = NULL;
|
||||
|
||||
// Invariant: after clearing _succ a thread *must* retry _owner before parking.
|
||||
@ -675,9 +645,7 @@ void ObjectMonitor::EnterI(TRAPS) {
|
||||
// contended slow-path from EnterI(). We use ReenterI() only for
|
||||
// monitor reentry in wait().
|
||||
//
|
||||
// In the future we should reconcile EnterI() and ReenterI(), adding
|
||||
// Knob_Reset and Knob_SpinAfterFutile support and restructuring the
|
||||
// loop accordingly.
|
||||
// In the future we should reconcile EnterI() and ReenterI().
|
||||
|
||||
void ObjectMonitor::ReenterI(Thread * Self, ObjectWaiter * SelfNode) {
|
||||
assert(Self != NULL, "invariant");
|
||||
@ -929,181 +897,60 @@ void ObjectMonitor::exit(bool not_suspended, TRAPS) {
|
||||
for (;;) {
|
||||
assert(THREAD == _owner, "invariant");
|
||||
|
||||
if (Knob_ExitPolicy == 0) {
|
||||
// release semantics: prior loads and stores from within the critical section
|
||||
// must not float (reorder) past the following store that drops the lock.
|
||||
// On SPARC that requires MEMBAR #loadstore|#storestore.
|
||||
// But of course in TSO #loadstore|#storestore is not required.
|
||||
// I'd like to write one of the following:
|
||||
// A. OrderAccess::release() ; _owner = NULL
|
||||
// B. OrderAccess::loadstore(); OrderAccess::storestore(); _owner = NULL;
|
||||
// Unfortunately OrderAccess::release() and OrderAccess::loadstore() both
|
||||
// store into a _dummy variable. That store is not needed, but can result
|
||||
// in massive wasteful coherency traffic on classic SMP systems.
|
||||
// Instead, I use release_store(), which is implemented as just a simple
|
||||
// ST on x64, x86 and SPARC.
|
||||
OrderAccess::release_store(&_owner, (void*)NULL); // drop the lock
|
||||
OrderAccess::storeload(); // See if we need to wake a successor
|
||||
if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
|
||||
return;
|
||||
}
|
||||
// Other threads are blocked trying to acquire the lock.
|
||||
// release semantics: prior loads and stores from within the critical section
|
||||
// must not float (reorder) past the following store that drops the lock.
|
||||
// On SPARC that requires MEMBAR #loadstore|#storestore.
|
||||
// But of course in TSO #loadstore|#storestore is not required.
|
||||
OrderAccess::release_store(&_owner, (void*)NULL); // drop the lock
|
||||
OrderAccess::storeload(); // See if we need to wake a successor
|
||||
if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
|
||||
return;
|
||||
}
|
||||
// Other threads are blocked trying to acquire the lock.
|
||||
|
||||
// Normally the exiting thread is responsible for ensuring succession,
|
||||
// but if other successors are ready or other entering threads are spinning
|
||||
// then this thread can simply store NULL into _owner and exit without
|
||||
// waking a successor. The existence of spinners or ready successors
|
||||
// guarantees proper succession (liveness). Responsibility passes to the
|
||||
// ready or running successors. The exiting thread delegates the duty.
|
||||
// More precisely, if a successor already exists this thread is absolved
|
||||
// of the responsibility of waking (unparking) one.
|
||||
//
|
||||
// The _succ variable is critical to reducing futile wakeup frequency.
|
||||
// _succ identifies the "heir presumptive" thread that has been made
|
||||
// ready (unparked) but that has not yet run. We need only one such
|
||||
// successor thread to guarantee progress.
|
||||
// See http://www.usenix.org/events/jvm01/full_papers/dice/dice.pdf
|
||||
// section 3.3 "Futile Wakeup Throttling" for details.
|
||||
//
|
||||
// Note that spinners in Enter() also set _succ non-null.
|
||||
// In the current implementation spinners opportunistically set
|
||||
// _succ so that exiting threads might avoid waking a successor.
|
||||
// Another less appealing alternative would be for the exiting thread
|
||||
// to drop the lock and then spin briefly to see if a spinner managed
|
||||
// to acquire the lock. If so, the exiting thread could exit
|
||||
// immediately without waking a successor, otherwise the exiting
|
||||
// thread would need to dequeue and wake a successor.
|
||||
// (Note that we'd need to make the post-drop spin short, but no
|
||||
// shorter than the worst-case round-trip cache-line migration time.
|
||||
// The dropped lock needs to become visible to the spinner, and then
|
||||
// the acquisition of the lock by the spinner must become visible to
|
||||
// the exiting thread).
|
||||
// Normally the exiting thread is responsible for ensuring succession,
|
||||
// but if other successors are ready or other entering threads are spinning
|
||||
// then this thread can simply store NULL into _owner and exit without
|
||||
// waking a successor. The existence of spinners or ready successors
|
||||
// guarantees proper succession (liveness). Responsibility passes to the
|
||||
// ready or running successors. The exiting thread delegates the duty.
|
||||
// More precisely, if a successor already exists this thread is absolved
|
||||
// of the responsibility of waking (unparking) one.
|
||||
//
|
||||
// The _succ variable is critical to reducing futile wakeup frequency.
|
||||
// _succ identifies the "heir presumptive" thread that has been made
|
||||
// ready (unparked) but that has not yet run. We need only one such
|
||||
// successor thread to guarantee progress.
|
||||
// See http://www.usenix.org/events/jvm01/full_papers/dice/dice.pdf
|
||||
// section 3.3 "Futile Wakeup Throttling" for details.
|
||||
//
|
||||
// Note that spinners in Enter() also set _succ non-null.
|
||||
// In the current implementation spinners opportunistically set
|
||||
// _succ so that exiting threads might avoid waking a successor.
|
||||
// Another less appealing alternative would be for the exiting thread
|
||||
// to drop the lock and then spin briefly to see if a spinner managed
|
||||
// to acquire the lock. If so, the exiting thread could exit
|
||||
// immediately without waking a successor, otherwise the exiting
|
||||
// thread would need to dequeue and wake a successor.
|
||||
// (Note that we'd need to make the post-drop spin short, but no
|
||||
// shorter than the worst-case round-trip cache-line migration time.
|
||||
// The dropped lock needs to become visible to the spinner, and then
|
||||
// the acquisition of the lock by the spinner must become visible to
|
||||
// the exiting thread).
|
||||
|
||||
// It appears that an heir-presumptive (successor) must be made ready.
|
||||
// Only the current lock owner can manipulate the EntryList or
|
||||
// drain _cxq, so we need to reacquire the lock. If we fail
|
||||
// to reacquire the lock the responsibility for ensuring succession
|
||||
// falls to the new owner.
|
||||
//
|
||||
if (!Atomic::replace_if_null(THREAD, &_owner)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) {
|
||||
OrderAccess::release_store(&_owner, (void*)NULL); // drop the lock
|
||||
OrderAccess::storeload();
|
||||
// Ratify the previously observed values.
|
||||
if (_cxq == NULL || _succ != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// inopportune interleaving -- the exiting thread (this thread)
|
||||
// in the fast-exit path raced an entering thread in the slow-enter
|
||||
// path.
|
||||
// We have two choices:
|
||||
// A. Try to reacquire the lock.
|
||||
// If the CAS() fails return immediately, otherwise
|
||||
// we either restart/rerun the exit operation, or simply
|
||||
// fall-through into the code below which wakes a successor.
|
||||
// B. If the elements forming the EntryList|cxq are TSM
|
||||
// we could simply unpark() the lead thread and return
|
||||
// without having set _succ.
|
||||
if (!Atomic::replace_if_null(THREAD, &_owner)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// It appears that an heir-presumptive (successor) must be made ready.
|
||||
// Only the current lock owner can manipulate the EntryList or
|
||||
// drain _cxq, so we need to reacquire the lock. If we fail
|
||||
// to reacquire the lock the responsibility for ensuring succession
|
||||
// falls to the new owner.
|
||||
//
|
||||
if (!Atomic::replace_if_null(THREAD, &_owner)) {
|
||||
return;
|
||||
}
|
||||
|
||||
guarantee(_owner == THREAD, "invariant");
|
||||
|
||||
ObjectWaiter * w = NULL;
|
||||
int QMode = Knob_QMode;
|
||||
|
||||
if (QMode == 2 && _cxq != NULL) {
|
||||
// QMode == 2 : cxq has precedence over EntryList.
|
||||
// Try to directly wake a successor from the cxq.
|
||||
// If successful, the successor will need to unlink itself from cxq.
|
||||
w = _cxq;
|
||||
assert(w != NULL, "invariant");
|
||||
assert(w->TState == ObjectWaiter::TS_CXQ, "Invariant");
|
||||
ExitEpilog(Self, w);
|
||||
return;
|
||||
}
|
||||
|
||||
if (QMode == 3 && _cxq != NULL) {
|
||||
// Aggressively drain cxq into EntryList at the first opportunity.
|
||||
// This policy ensure that recently-run threads live at the head of EntryList.
|
||||
// Drain _cxq into EntryList - bulk transfer.
|
||||
// First, detach _cxq.
|
||||
// The following loop is tantamount to: w = swap(&cxq, NULL)
|
||||
w = _cxq;
|
||||
for (;;) {
|
||||
assert(w != NULL, "Invariant");
|
||||
ObjectWaiter * u = Atomic::cmpxchg((ObjectWaiter*)NULL, &_cxq, w);
|
||||
if (u == w) break;
|
||||
w = u;
|
||||
}
|
||||
assert(w != NULL, "invariant");
|
||||
|
||||
ObjectWaiter * q = NULL;
|
||||
ObjectWaiter * p;
|
||||
for (p = w; p != NULL; p = p->_next) {
|
||||
guarantee(p->TState == ObjectWaiter::TS_CXQ, "Invariant");
|
||||
p->TState = ObjectWaiter::TS_ENTER;
|
||||
p->_prev = q;
|
||||
q = p;
|
||||
}
|
||||
|
||||
// Append the RATs to the EntryList
|
||||
// TODO: organize EntryList as a CDLL so we can locate the tail in constant-time.
|
||||
ObjectWaiter * Tail;
|
||||
for (Tail = _EntryList; Tail != NULL && Tail->_next != NULL;
|
||||
Tail = Tail->_next)
|
||||
/* empty */;
|
||||
if (Tail == NULL) {
|
||||
_EntryList = w;
|
||||
} else {
|
||||
Tail->_next = w;
|
||||
w->_prev = Tail;
|
||||
}
|
||||
|
||||
// Fall thru into code that tries to wake a successor from EntryList
|
||||
}
|
||||
|
||||
if (QMode == 4 && _cxq != NULL) {
|
||||
// Aggressively drain cxq into EntryList at the first opportunity.
|
||||
// This policy ensure that recently-run threads live at the head of EntryList.
|
||||
|
||||
// Drain _cxq into EntryList - bulk transfer.
|
||||
// First, detach _cxq.
|
||||
// The following loop is tantamount to: w = swap(&cxq, NULL)
|
||||
w = _cxq;
|
||||
for (;;) {
|
||||
assert(w != NULL, "Invariant");
|
||||
ObjectWaiter * u = Atomic::cmpxchg((ObjectWaiter*)NULL, &_cxq, w);
|
||||
if (u == w) break;
|
||||
w = u;
|
||||
}
|
||||
assert(w != NULL, "invariant");
|
||||
|
||||
ObjectWaiter * q = NULL;
|
||||
ObjectWaiter * p;
|
||||
for (p = w; p != NULL; p = p->_next) {
|
||||
guarantee(p->TState == ObjectWaiter::TS_CXQ, "Invariant");
|
||||
p->TState = ObjectWaiter::TS_ENTER;
|
||||
p->_prev = q;
|
||||
q = p;
|
||||
}
|
||||
|
||||
// Prepend the RATs to the EntryList
|
||||
if (_EntryList != NULL) {
|
||||
q->_next = _EntryList;
|
||||
_EntryList->_prev = q;
|
||||
}
|
||||
_EntryList = w;
|
||||
|
||||
// Fall thru into code that tries to wake a successor from EntryList
|
||||
}
|
||||
|
||||
w = _EntryList;
|
||||
if (w != NULL) {
|
||||
@ -1150,34 +997,14 @@ void ObjectMonitor::exit(bool not_suspended, TRAPS) {
|
||||
// TODO-FIXME: consider changing EntryList from a DLL to a CDLL so
|
||||
// we have faster access to the tail.
|
||||
|
||||
if (QMode == 1) {
|
||||
// QMode == 1 : drain cxq to EntryList, reversing order
|
||||
// We also reverse the order of the list.
|
||||
ObjectWaiter * s = NULL;
|
||||
ObjectWaiter * t = w;
|
||||
ObjectWaiter * u = NULL;
|
||||
while (t != NULL) {
|
||||
guarantee(t->TState == ObjectWaiter::TS_CXQ, "invariant");
|
||||
t->TState = ObjectWaiter::TS_ENTER;
|
||||
u = t->_next;
|
||||
t->_prev = u;
|
||||
t->_next = s;
|
||||
s = t;
|
||||
t = u;
|
||||
}
|
||||
_EntryList = s;
|
||||
assert(s != NULL, "invariant");
|
||||
} else {
|
||||
// QMode == 0 or QMode == 2
|
||||
_EntryList = w;
|
||||
ObjectWaiter * q = NULL;
|
||||
ObjectWaiter * p;
|
||||
for (p = w; p != NULL; p = p->_next) {
|
||||
guarantee(p->TState == ObjectWaiter::TS_CXQ, "Invariant");
|
||||
p->TState = ObjectWaiter::TS_ENTER;
|
||||
p->_prev = q;
|
||||
q = p;
|
||||
}
|
||||
_EntryList = w;
|
||||
ObjectWaiter * q = NULL;
|
||||
ObjectWaiter * p;
|
||||
for (p = w; p != NULL; p = p->_next) {
|
||||
guarantee(p->TState == ObjectWaiter::TS_CXQ, "Invariant");
|
||||
p->TState = ObjectWaiter::TS_ENTER;
|
||||
p->_prev = q;
|
||||
q = p;
|
||||
}
|
||||
|
||||
// In 1-0 mode we need: ST EntryList; MEMBAR #storestore; ST _owner = NULL
|
||||
@ -1226,22 +1053,8 @@ void ObjectMonitor::exit(bool not_suspended, TRAPS) {
|
||||
// ST Self->_suspend_equivalent = false
|
||||
// MEMBAR
|
||||
// LD Self_>_suspend_flags
|
||||
//
|
||||
// UPDATE 2007-10-6: since I've replaced the native Mutex/Monitor subsystem
|
||||
// with a more efficient implementation, the need to use "FastHSSEC" has
|
||||
// decreased. - Dave
|
||||
|
||||
|
||||
bool ObjectMonitor::ExitSuspendEquivalent(JavaThread * jSelf) {
|
||||
const int Mode = Knob_FastHSSEC;
|
||||
if (Mode && !jSelf->is_external_suspend()) {
|
||||
assert(jSelf->is_suspend_equivalent(), "invariant");
|
||||
jSelf->clear_suspend_equivalent();
|
||||
if (2 == Mode) OrderAccess::storeload();
|
||||
if (!jSelf->is_external_suspend()) return false;
|
||||
// We raced a suspension -- fall thru into the slow path
|
||||
jSelf->set_suspend_equivalent();
|
||||
}
|
||||
return jSelf->handle_special_suspend_equivalent_condition();
|
||||
}
|
||||
|
||||
@ -1255,7 +1068,7 @@ void ObjectMonitor::ExitEpilog(Thread * Self, ObjectWaiter * Wakee) {
|
||||
// 2. ST _owner = NULL
|
||||
// 3. unpark(wakee)
|
||||
|
||||
_succ = Knob_SuccEnabled ? Wakee->_thread : NULL;
|
||||
_succ = Wakee->_thread;
|
||||
ParkEvent * Trigger = Wakee->_event;
|
||||
|
||||
// Hygiene -- once we've set _owner = NULL we can't safely dereference Wakee again.
|
||||
@ -1348,12 +1161,6 @@ void ObjectMonitor::check_slow(TRAPS) {
|
||||
THROW_MSG(vmSymbols::java_lang_IllegalMonitorStateException(), "current thread not owner");
|
||||
}
|
||||
|
||||
static int Adjust(volatile int * adr, int dx) {
|
||||
int v;
|
||||
for (v = *adr; Atomic::cmpxchg(v + dx, adr, v) != v; v = *adr) /* empty */;
|
||||
return v;
|
||||
}
|
||||
|
||||
static void post_monitor_wait_event(EventJavaMonitorWait* event,
|
||||
ObjectMonitor* monitor,
|
||||
jlong notifier_tid,
|
||||
@ -1599,8 +1406,6 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
|
||||
// we might just dequeue a thread from the WaitSet and directly unpark() it.
|
||||
|
||||
void ObjectMonitor::INotify(Thread * Self) {
|
||||
const int policy = Knob_MoveNotifyee;
|
||||
|
||||
Thread::SpinAcquire(&_WaitSetLock, "WaitSet - notify");
|
||||
ObjectWaiter * iterator = DequeueWaiter();
|
||||
if (iterator != NULL) {
|
||||
@ -1611,9 +1416,9 @@ void ObjectMonitor::INotify(Thread * Self) {
|
||||
// or head (policy == 0).
|
||||
// b. push it onto the front of the _cxq (policy == 2).
|
||||
// For now we use (b).
|
||||
if (policy != 4) {
|
||||
iterator->TState = ObjectWaiter::TS_ENTER;
|
||||
}
|
||||
|
||||
iterator->TState = ObjectWaiter::TS_ENTER;
|
||||
|
||||
iterator->_notified = 1;
|
||||
iterator->_notifier_tid = JFR_THREAD_ID(Self);
|
||||
|
||||
@ -1624,67 +1429,19 @@ void ObjectMonitor::INotify(Thread * Self) {
|
||||
assert(list != iterator, "invariant");
|
||||
}
|
||||
|
||||
if (policy == 0) { // prepend to EntryList
|
||||
if (list == NULL) {
|
||||
iterator->_next = iterator->_prev = NULL;
|
||||
_EntryList = iterator;
|
||||
} else {
|
||||
list->_prev = iterator;
|
||||
iterator->_next = list;
|
||||
iterator->_prev = NULL;
|
||||
_EntryList = iterator;
|
||||
}
|
||||
} else if (policy == 1) { // append to EntryList
|
||||
if (list == NULL) {
|
||||
iterator->_next = iterator->_prev = NULL;
|
||||
_EntryList = iterator;
|
||||
} else {
|
||||
// CONSIDER: finding the tail currently requires a linear-time walk of
|
||||
// the EntryList. We can make tail access constant-time by converting to
|
||||
// a CDLL instead of using our current DLL.
|
||||
ObjectWaiter * tail;
|
||||
for (tail = list; tail->_next != NULL; tail = tail->_next) {}
|
||||
assert(tail != NULL && tail->_next == NULL, "invariant");
|
||||
tail->_next = iterator;
|
||||
iterator->_prev = tail;
|
||||
iterator->_next = NULL;
|
||||
}
|
||||
} else if (policy == 2) { // prepend to cxq
|
||||
if (list == NULL) {
|
||||
iterator->_next = iterator->_prev = NULL;
|
||||
_EntryList = iterator;
|
||||
} else {
|
||||
iterator->TState = ObjectWaiter::TS_CXQ;
|
||||
for (;;) {
|
||||
ObjectWaiter * front = _cxq;
|
||||
iterator->_next = front;
|
||||
if (Atomic::cmpxchg(iterator, &_cxq, front) == front) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (policy == 3) { // append to cxq
|
||||
// prepend to cxq
|
||||
if (list == NULL) {
|
||||
iterator->_next = iterator->_prev = NULL;
|
||||
_EntryList = iterator;
|
||||
} else {
|
||||
iterator->TState = ObjectWaiter::TS_CXQ;
|
||||
for (;;) {
|
||||
ObjectWaiter * tail = _cxq;
|
||||
if (tail == NULL) {
|
||||
iterator->_next = NULL;
|
||||
if (Atomic::replace_if_null(iterator, &_cxq)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
while (tail->_next != NULL) tail = tail->_next;
|
||||
tail->_next = iterator;
|
||||
iterator->_prev = tail;
|
||||
iterator->_next = NULL;
|
||||
ObjectWaiter * front = _cxq;
|
||||
iterator->_next = front;
|
||||
if (Atomic::cmpxchg(iterator, &_cxq, front) == front) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ParkEvent * ev = iterator->_event;
|
||||
iterator->TState = ObjectWaiter::TS_RUN;
|
||||
OrderAccess::fence();
|
||||
ev->unpark();
|
||||
}
|
||||
|
||||
// _WaitSetLock protects the wait queue, not the EntryList. We could
|
||||
@ -1695,9 +1452,7 @@ void ObjectMonitor::INotify(Thread * Self) {
|
||||
// on _WaitSetLock so it's not profitable to reduce the length of the
|
||||
// critical section.
|
||||
|
||||
if (policy < 4) {
|
||||
iterator->wait_reenter_begin(this);
|
||||
}
|
||||
iterator->wait_reenter_begin(this);
|
||||
}
|
||||
Thread::SpinRelease(&_WaitSetLock);
|
||||
}
|
||||
@ -1854,33 +1609,19 @@ int ObjectMonitor::TrySpin(Thread * Self) {
|
||||
// hold the duration constant but vary the frequency.
|
||||
|
||||
ctr = _SpinDuration;
|
||||
if (ctr < Knob_SpinBase) ctr = Knob_SpinBase;
|
||||
if (ctr <= 0) return 0;
|
||||
|
||||
if (Knob_SuccRestrict && _succ != NULL) return 0;
|
||||
if (Knob_OState && NotRunnable (Self, (Thread *) _owner)) {
|
||||
if (NotRunnable(Self, (Thread *) _owner)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int MaxSpin = Knob_MaxSpinners;
|
||||
if (MaxSpin >= 0) {
|
||||
if (_Spinner > MaxSpin) {
|
||||
return 0;
|
||||
}
|
||||
// Slightly racy, but benign ...
|
||||
Adjust(&_Spinner, 1);
|
||||
}
|
||||
|
||||
// We're good to spin ... spin ingress.
|
||||
// CONSIDER: use Prefetch::write() to avoid RTS->RTO upgrades
|
||||
// when preparing to LD...CAS _owner, etc and the CAS is likely
|
||||
// to succeed.
|
||||
int hits = 0;
|
||||
int msk = 0;
|
||||
int caspty = Knob_CASPenalty;
|
||||
int oxpty = Knob_OXPenalty;
|
||||
int sss = Knob_SpinSetSucc;
|
||||
if (sss && _succ == NULL) _succ = Self;
|
||||
if (_succ == NULL) {
|
||||
_succ = Self;
|
||||
}
|
||||
Thread * prv = NULL;
|
||||
|
||||
// There are three ways to exit the following loop:
|
||||
@ -1903,32 +1644,7 @@ int ObjectMonitor::TrySpin(Thread * Self) {
|
||||
if (SafepointMechanism::poll(Self)) {
|
||||
goto Abort; // abrupt spin egress
|
||||
}
|
||||
if (Knob_UsePause & 1) SpinPause();
|
||||
}
|
||||
|
||||
if (Knob_UsePause & 2) SpinPause();
|
||||
|
||||
// Exponential back-off ... Stay off the bus to reduce coherency traffic.
|
||||
// This is useful on classic SMP systems, but is of less utility on
|
||||
// N1-style CMT platforms.
|
||||
//
|
||||
// Trade-off: lock acquisition latency vs coherency bandwidth.
|
||||
// Lock hold times are typically short. A histogram
|
||||
// of successful spin attempts shows that we usually acquire
|
||||
// the lock early in the spin. That suggests we want to
|
||||
// sample _owner frequently in the early phase of the spin,
|
||||
// but then back-off and sample less frequently as the spin
|
||||
// progresses. The back-off makes a good citizen on SMP big
|
||||
// SMP systems. Oversampling _owner can consume excessive
|
||||
// coherency bandwidth. Relatedly, if we _oversample _owner we
|
||||
// can inadvertently interfere with the the ST m->owner=null.
|
||||
// executed by the lock owner.
|
||||
if (ctr & msk) continue;
|
||||
++hits;
|
||||
if ((hits & 0xF) == 0) {
|
||||
// The 0xF, above, corresponds to the exponent.
|
||||
// Consider: (msk+1)|msk
|
||||
msk = ((msk << 2)|3) & BackOffMask;
|
||||
SpinPause();
|
||||
}
|
||||
|
||||
// Probe _owner with TATAS
|
||||
@ -1947,10 +1663,9 @@ int ObjectMonitor::TrySpin(Thread * Self) {
|
||||
if (ox == NULL) {
|
||||
// The CAS succeeded -- this thread acquired ownership
|
||||
// Take care of some bookkeeping to exit spin state.
|
||||
if (sss && _succ == Self) {
|
||||
if (_succ == Self) {
|
||||
_succ = NULL;
|
||||
}
|
||||
if (MaxSpin > 0) Adjust(&_Spinner, -1);
|
||||
|
||||
// Increase _SpinDuration :
|
||||
// The spin was successful (profitable) so we tend toward
|
||||
@ -1968,22 +1683,17 @@ int ObjectMonitor::TrySpin(Thread * Self) {
|
||||
}
|
||||
|
||||
// The CAS failed ... we can take any of the following actions:
|
||||
// * penalize: ctr -= Knob_CASPenalty
|
||||
// * penalize: ctr -= CASPenalty
|
||||
// * exit spin with prejudice -- goto Abort;
|
||||
// * exit spin without prejudice.
|
||||
// * Since CAS is high-latency, retry again immediately.
|
||||
prv = ox;
|
||||
if (caspty == -2) break;
|
||||
if (caspty == -1) goto Abort;
|
||||
ctr -= caspty;
|
||||
continue;
|
||||
goto Abort;
|
||||
}
|
||||
|
||||
// Did lock ownership change hands ?
|
||||
if (ox != prv && prv != NULL) {
|
||||
if (oxpty == -2) break;
|
||||
if (oxpty == -1) goto Abort;
|
||||
ctr -= oxpty;
|
||||
goto Abort;
|
||||
}
|
||||
prv = ox;
|
||||
|
||||
@ -1991,10 +1701,12 @@ int ObjectMonitor::TrySpin(Thread * Self) {
|
||||
// The owner must be executing in order to drop the lock.
|
||||
// Spinning while the owner is OFFPROC is idiocy.
|
||||
// Consider: ctr -= RunnablePenalty ;
|
||||
if (Knob_OState && NotRunnable (Self, ox)) {
|
||||
if (NotRunnable(Self, ox)) {
|
||||
goto Abort;
|
||||
}
|
||||
if (sss && _succ == NULL) _succ = Self;
|
||||
if (_succ == NULL) {
|
||||
_succ = Self;
|
||||
}
|
||||
}
|
||||
|
||||
// Spin failed with prejudice -- reduce _SpinDuration.
|
||||
@ -2012,8 +1724,7 @@ int ObjectMonitor::TrySpin(Thread * Self) {
|
||||
}
|
||||
|
||||
Abort:
|
||||
if (MaxSpin >= 0) Adjust(&_Spinner, -1);
|
||||
if (sss && _succ == Self) {
|
||||
if (_succ == Self) {
|
||||
_succ = NULL;
|
||||
// Invariant: after setting succ=null a contending thread
|
||||
// must recheck-retry _owner before parking. This usually happens
|
||||
@ -2204,29 +1915,6 @@ void ObjectMonitor::Initialize() {
|
||||
}
|
||||
}
|
||||
|
||||
static char * kvGet(char * kvList, const char * Key) {
|
||||
if (kvList == NULL) return NULL;
|
||||
size_t n = strlen(Key);
|
||||
char * Search;
|
||||
for (Search = kvList; *Search; Search += strlen(Search) + 1) {
|
||||
if (strncmp (Search, Key, n) == 0) {
|
||||
if (Search[n] == '=') return Search + n + 1;
|
||||
if (Search[n] == 0) return(char *) "1";
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int kvGetInt(char * kvList, const char * Key, int Default) {
|
||||
char * v = kvGet(kvList, Key);
|
||||
int rslt = v ? ::strtol(v, NULL, 0) : Default;
|
||||
if (Knob_ReportSettings && v != NULL) {
|
||||
tty->print_cr("INFO: SyncKnob: %s %d(%d)", Key, rslt, Default) ;
|
||||
tty->flush();
|
||||
}
|
||||
return rslt;
|
||||
}
|
||||
|
||||
void ObjectMonitor::DeferredInitialize() {
|
||||
if (InitDone > 0) return;
|
||||
if (Atomic::cmpxchg (-1, &InitDone, 0) != 0) {
|
||||
@ -2237,70 +1925,13 @@ void ObjectMonitor::DeferredInitialize() {
|
||||
// One-shot global initialization ...
|
||||
// The initialization is idempotent, so we don't need locks.
|
||||
// In the future consider doing this via os::init_2().
|
||||
// SyncKnobs consist of <Key>=<Value> pairs in the style
|
||||
// of environment variables. Start by converting ':' to NUL.
|
||||
|
||||
if (SyncKnobs == NULL) SyncKnobs = "";
|
||||
|
||||
size_t sz = strlen(SyncKnobs);
|
||||
char * knobs = (char *) os::malloc(sz + 2, mtInternal);
|
||||
if (knobs == NULL) {
|
||||
vm_exit_out_of_memory(sz + 2, OOM_MALLOC_ERROR, "Parse SyncKnobs");
|
||||
guarantee(0, "invariant");
|
||||
}
|
||||
strcpy(knobs, SyncKnobs);
|
||||
knobs[sz+1] = 0;
|
||||
for (char * p = knobs; *p; p++) {
|
||||
if (*p == ':') *p = 0;
|
||||
}
|
||||
|
||||
#define SETKNOB(x) { Knob_##x = kvGetInt(knobs, #x, Knob_##x); }
|
||||
SETKNOB(ReportSettings);
|
||||
SETKNOB(ExitRelease);
|
||||
SETKNOB(InlineNotify);
|
||||
SETKNOB(Verbose);
|
||||
SETKNOB(VerifyInUse);
|
||||
SETKNOB(VerifyMatch);
|
||||
SETKNOB(FixedSpin);
|
||||
SETKNOB(SpinLimit);
|
||||
SETKNOB(SpinBase);
|
||||
SETKNOB(SpinBackOff);
|
||||
SETKNOB(CASPenalty);
|
||||
SETKNOB(OXPenalty);
|
||||
SETKNOB(SpinSetSucc);
|
||||
SETKNOB(SuccEnabled);
|
||||
SETKNOB(SuccRestrict);
|
||||
SETKNOB(Penalty);
|
||||
SETKNOB(Bonus);
|
||||
SETKNOB(BonusB);
|
||||
SETKNOB(Poverty);
|
||||
SETKNOB(SpinAfterFutile);
|
||||
SETKNOB(UsePause);
|
||||
SETKNOB(SpinEarly);
|
||||
SETKNOB(OState);
|
||||
SETKNOB(MaxSpinners);
|
||||
SETKNOB(PreSpin);
|
||||
SETKNOB(ExitPolicy);
|
||||
SETKNOB(QMode);
|
||||
SETKNOB(ResetEvent);
|
||||
SETKNOB(MoveNotifyee);
|
||||
SETKNOB(FastHSSEC);
|
||||
#undef SETKNOB
|
||||
|
||||
if (os::is_MP()) {
|
||||
BackOffMask = (1 << Knob_SpinBackOff) - 1;
|
||||
if (Knob_ReportSettings) {
|
||||
tty->print_cr("INFO: BackOffMask=0x%X", BackOffMask);
|
||||
}
|
||||
// CONSIDER: BackOffMask = ROUNDUP_NEXT_POWER2 (ncpus-1)
|
||||
} else {
|
||||
if (!os::is_MP()) {
|
||||
Knob_SpinLimit = 0;
|
||||
Knob_SpinBase = 0;
|
||||
Knob_PreSpin = 0;
|
||||
Knob_FixedSpin = -1;
|
||||
}
|
||||
|
||||
os::free(knobs);
|
||||
OrderAccess::fence();
|
||||
InitDone = 1;
|
||||
}
|
||||
|
@ -195,11 +195,6 @@ class ObjectMonitor {
|
||||
static PerfCounter * _sync_Deflations;
|
||||
static PerfLongVariable * _sync_MonExtant;
|
||||
|
||||
static int Knob_ExitRelease;
|
||||
static int Knob_InlineNotify;
|
||||
static int Knob_Verbose;
|
||||
static int Knob_VerifyInUse;
|
||||
static int Knob_VerifyMatch;
|
||||
static int Knob_SpinLimit;
|
||||
|
||||
void* operator new (size_t size) throw();
|
||||
|
@ -598,7 +598,8 @@ private:
|
||||
|
||||
public:
|
||||
ParallelSPCleanupThreadClosure(DeflateMonitorCounters* counters) :
|
||||
_nmethod_cl(NMethodSweeper::prepare_mark_active_nmethods()), _counters(counters) {}
|
||||
_nmethod_cl(UseCodeAging ? NMethodSweeper::prepare_reset_hotness_counters() : NULL),
|
||||
_counters(counters) {}
|
||||
|
||||
void do_thread(Thread* thread) {
|
||||
ObjectSynchronizer::deflate_thread_local_monitors(thread, _counters);
|
||||
|
@ -28,15 +28,19 @@
|
||||
#include "code/icBuffer.hpp"
|
||||
#include "code/nmethod.hpp"
|
||||
#include "compiler/compileBroker.hpp"
|
||||
#include "gc/shared/collectedHeap.hpp"
|
||||
#include "gc/shared/workgroup.hpp"
|
||||
#include "jfr/jfrEvents.hpp"
|
||||
#include "logging/log.hpp"
|
||||
#include "logging/logStream.hpp"
|
||||
#include "memory/allocation.inline.hpp"
|
||||
#include "memory/resourceArea.hpp"
|
||||
#include "memory/universe.hpp"
|
||||
#include "oops/method.hpp"
|
||||
#include "runtime/atomic.hpp"
|
||||
#include "runtime/compilationPolicy.hpp"
|
||||
#include "runtime/interfaceSupport.inline.hpp"
|
||||
#include "runtime/handshake.hpp"
|
||||
#include "runtime/mutexLocker.hpp"
|
||||
#include "runtime/orderAccess.hpp"
|
||||
#include "runtime/os.hpp"
|
||||
@ -197,6 +201,38 @@ bool NMethodSweeper::wait_for_stack_scanning() {
|
||||
return _current.end();
|
||||
}
|
||||
|
||||
class NMethodMarkingThreadClosure : public ThreadClosure {
|
||||
private:
|
||||
CodeBlobClosure* _cl;
|
||||
public:
|
||||
NMethodMarkingThreadClosure(CodeBlobClosure* cl) : _cl(cl) {}
|
||||
void do_thread(Thread* thread) {
|
||||
if (thread->is_Java_thread() && ! thread->is_Code_cache_sweeper_thread()) {
|
||||
JavaThread* jt = (JavaThread*) thread;
|
||||
jt->nmethods_do(_cl);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class NMethodMarkingTask : public AbstractGangTask {
|
||||
private:
|
||||
NMethodMarkingThreadClosure* _cl;
|
||||
public:
|
||||
NMethodMarkingTask(NMethodMarkingThreadClosure* cl) :
|
||||
AbstractGangTask("Parallel NMethod Marking"),
|
||||
_cl(cl) {
|
||||
Threads::change_thread_claim_parity();
|
||||
}
|
||||
|
||||
~NMethodMarkingTask() {
|
||||
Threads::assert_all_threads_claimed();
|
||||
}
|
||||
|
||||
void work(uint worker_id) {
|
||||
Threads::possibly_parallel_threads_do(true, _cl);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Scans the stacks of all Java threads and marks activations of not-entrant methods.
|
||||
* No need to synchronize access, since 'mark_active_nmethods' is always executed at a
|
||||
@ -205,12 +241,56 @@ bool NMethodSweeper::wait_for_stack_scanning() {
|
||||
void NMethodSweeper::mark_active_nmethods() {
|
||||
CodeBlobClosure* cl = prepare_mark_active_nmethods();
|
||||
if (cl != NULL) {
|
||||
Threads::nmethods_do(cl);
|
||||
WorkGang* workers = Universe::heap()->get_safepoint_workers();
|
||||
if (workers != NULL) {
|
||||
NMethodMarkingThreadClosure tcl(cl);
|
||||
NMethodMarkingTask task(&tcl);
|
||||
workers->run_task(&task);
|
||||
} else {
|
||||
Threads::nmethods_do(cl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CodeBlobClosure* NMethodSweeper::prepare_mark_active_nmethods() {
|
||||
#ifdef ASSERT
|
||||
if (ThreadLocalHandshakes) {
|
||||
assert(Thread::current()->is_Code_cache_sweeper_thread(), "must be executed under CodeCache_lock and in sweeper thread");
|
||||
assert_lock_strong(CodeCache_lock);
|
||||
} else {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "must be executed at a safepoint");
|
||||
}
|
||||
#endif
|
||||
|
||||
// If we do not want to reclaim not-entrant or zombie methods there is no need
|
||||
// to scan stacks
|
||||
if (!MethodFlushing) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Increase time so that we can estimate when to invoke the sweeper again.
|
||||
_time_counter++;
|
||||
|
||||
// Check for restart
|
||||
assert(_current.method() == NULL, "should only happen between sweeper cycles");
|
||||
assert(wait_for_stack_scanning(), "should only happen between sweeper cycles");
|
||||
|
||||
_seen = 0;
|
||||
_current = CompiledMethodIterator();
|
||||
// Initialize to first nmethod
|
||||
_current.next();
|
||||
_traversals += 1;
|
||||
_total_time_this_sweep = Tickspan();
|
||||
|
||||
if (PrintMethodFlushing) {
|
||||
tty->print_cr("### Sweep: stack traversal %ld", _traversals);
|
||||
}
|
||||
return &mark_activation_closure;
|
||||
}
|
||||
|
||||
CodeBlobClosure* NMethodSweeper::prepare_reset_hotness_counters() {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "must be executed at a safepoint");
|
||||
|
||||
// If we do not want to reclaim not-entrant or zombie methods there is no need
|
||||
// to scan stacks
|
||||
if (!MethodFlushing) {
|
||||
@ -231,24 +311,7 @@ CodeBlobClosure* NMethodSweeper::prepare_mark_active_nmethods() {
|
||||
}
|
||||
}
|
||||
|
||||
if (wait_for_stack_scanning()) {
|
||||
_seen = 0;
|
||||
_current = CompiledMethodIterator();
|
||||
// Initialize to first nmethod
|
||||
_current.next();
|
||||
_traversals += 1;
|
||||
_total_time_this_sweep = Tickspan();
|
||||
|
||||
if (PrintMethodFlushing) {
|
||||
tty->print_cr("### Sweep: stack traversal %ld", _traversals);
|
||||
}
|
||||
return &mark_activation_closure;
|
||||
|
||||
} else {
|
||||
// Only set hotness counter
|
||||
return &set_hotness_closure;
|
||||
}
|
||||
|
||||
return &set_hotness_closure;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -258,8 +321,20 @@ CodeBlobClosure* NMethodSweeper::prepare_mark_active_nmethods() {
|
||||
void NMethodSweeper::do_stack_scanning() {
|
||||
assert(!CodeCache_lock->owned_by_self(), "just checking");
|
||||
if (wait_for_stack_scanning()) {
|
||||
VM_MarkActiveNMethods op;
|
||||
VMThread::execute(&op);
|
||||
if (ThreadLocalHandshakes) {
|
||||
CodeBlobClosure* code_cl;
|
||||
{
|
||||
MutexLockerEx ccl(CodeCache_lock, Mutex::_no_safepoint_check_flag);
|
||||
code_cl = prepare_mark_active_nmethods();
|
||||
}
|
||||
if (code_cl != NULL) {
|
||||
NMethodMarkingThreadClosure tcl(code_cl);
|
||||
Handshake::execute(&tcl);
|
||||
}
|
||||
} else {
|
||||
VM_MarkActiveNMethods op;
|
||||
VMThread::execute(&op);
|
||||
}
|
||||
_should_sweep = true;
|
||||
}
|
||||
}
|
||||
|
@ -117,6 +117,7 @@ class NMethodSweeper : public AllStatic {
|
||||
|
||||
static void mark_active_nmethods(); // Invoked at the end of each safepoint
|
||||
static CodeBlobClosure* prepare_mark_active_nmethods();
|
||||
static CodeBlobClosure* prepare_reset_hotness_counters();
|
||||
static void sweeper_loop();
|
||||
static void notify(int code_blob_type); // Possibly start the sweeper thread.
|
||||
static void force_sweep();
|
||||
|
@ -1050,40 +1050,14 @@ static void InduceScavenge(Thread * Self, const char * Whence) {
|
||||
// TODO: assert thread state is reasonable
|
||||
|
||||
if (ForceMonitorScavenge == 0 && Atomic::xchg (1, &ForceMonitorScavenge) == 0) {
|
||||
if (ObjectMonitor::Knob_Verbose) {
|
||||
tty->print_cr("INFO: Monitor scavenge - Induced STW @%s (%d)",
|
||||
Whence, ForceMonitorScavenge) ;
|
||||
tty->flush();
|
||||
}
|
||||
// Induce a 'null' safepoint to scavenge monitors
|
||||
// Must VM_Operation instance be heap allocated as the op will be enqueue and posted
|
||||
// to the VMthread and have a lifespan longer than that of this activation record.
|
||||
// The VMThread will delete the op when completed.
|
||||
VMThread::execute(new VM_ScavengeMonitors());
|
||||
|
||||
if (ObjectMonitor::Knob_Verbose) {
|
||||
tty->print_cr("INFO: Monitor scavenge - STW posted @%s (%d)",
|
||||
Whence, ForceMonitorScavenge) ;
|
||||
tty->flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectSynchronizer::verifyInUse(Thread *Self) {
|
||||
ObjectMonitor* mid;
|
||||
int in_use_tally = 0;
|
||||
for (mid = Self->omInUseList; mid != NULL; mid = mid->FreeNext) {
|
||||
in_use_tally++;
|
||||
}
|
||||
assert(in_use_tally == Self->omInUseCount, "in-use count off");
|
||||
|
||||
int free_tally = 0;
|
||||
for (mid = Self->omFreeList; mid != NULL; mid = mid->FreeNext) {
|
||||
free_tally++;
|
||||
}
|
||||
assert(free_tally == Self->omFreeCount, "free count off");
|
||||
}
|
||||
|
||||
ObjectMonitor* ObjectSynchronizer::omAlloc(Thread * Self) {
|
||||
// A large MAXPRIVATE value reduces both list lock contention
|
||||
// and list coherency traffic, but also tends to increase the
|
||||
@ -1110,9 +1084,6 @@ ObjectMonitor* ObjectSynchronizer::omAlloc(Thread * Self) {
|
||||
m->FreeNext = Self->omInUseList;
|
||||
Self->omInUseList = m;
|
||||
Self->omInUseCount++;
|
||||
if (ObjectMonitor::Knob_VerifyInUse) {
|
||||
verifyInUse(Self);
|
||||
}
|
||||
} else {
|
||||
m->FreeNext = NULL;
|
||||
}
|
||||
@ -1250,9 +1221,6 @@ void ObjectSynchronizer::omRelease(Thread * Self, ObjectMonitor * m,
|
||||
}
|
||||
extracted = true;
|
||||
Self->omInUseCount--;
|
||||
if (ObjectMonitor::Knob_VerifyInUse) {
|
||||
verifyInUse(Self);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1763,14 +1731,6 @@ void ObjectSynchronizer::finish_deflate_idle_monitors(DeflateMonitorCounters* co
|
||||
|
||||
// Consider: audit gFreeList to ensure that gMonitorFreeCount and list agree.
|
||||
|
||||
if (ObjectMonitor::Knob_Verbose) {
|
||||
tty->print_cr("INFO: Deflate: InCirc=%d InUse=%d Scavenged=%d "
|
||||
"ForceMonitorScavenge=%d : pop=%d free=%d",
|
||||
counters->nInCirculation, counters->nInuse, counters->nScavenged, ForceMonitorScavenge,
|
||||
gMonitorPopulation, gMonitorFreeCount);
|
||||
tty->flush();
|
||||
}
|
||||
|
||||
ForceMonitorScavenge = 0; // Reset
|
||||
|
||||
OM_PERFDATA_OP(Deflations, inc(counters->nScavenged));
|
||||
@ -1796,9 +1756,6 @@ void ObjectSynchronizer::deflate_thread_local_monitors(Thread* thread, DeflateMo
|
||||
// Adjust counters
|
||||
counters->nInCirculation += thread->omInUseCount;
|
||||
thread->omInUseCount -= deflated_count;
|
||||
if (ObjectMonitor::Knob_VerifyInUse) {
|
||||
verifyInUse(thread);
|
||||
}
|
||||
counters->nScavenged += deflated_count;
|
||||
counters->nInuse += thread->omInUseCount;
|
||||
|
||||
@ -1827,15 +1784,6 @@ class ReleaseJavaMonitorsClosure: public MonitorClosure {
|
||||
ReleaseJavaMonitorsClosure(Thread* thread) : THREAD(thread) {}
|
||||
void do_monitor(ObjectMonitor* mid) {
|
||||
if (mid->owner() == THREAD) {
|
||||
if (ObjectMonitor::Knob_VerifyMatch != 0) {
|
||||
ResourceMark rm;
|
||||
Handle obj(THREAD, (oop) mid->object());
|
||||
tty->print("INFO: unexpected locked object:");
|
||||
javaVFrame::print_locked_object_class_name(tty, obj, "locked");
|
||||
fatal("exiting JavaThread=" INTPTR_FORMAT
|
||||
" unexpectedly owns ObjectMonitor=" INTPTR_FORMAT,
|
||||
p2i(THREAD), p2i(mid));
|
||||
}
|
||||
(void)mid->complete_exit(CHECK);
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +105,6 @@ class ObjectSynchronizer : AllStatic {
|
||||
static void reenter (Handle obj, intptr_t recursion, TRAPS);
|
||||
|
||||
// thread-specific and global objectMonitor free list accessors
|
||||
static void verifyInUse(Thread * Self);
|
||||
static ObjectMonitor * omAlloc(Thread * Self);
|
||||
static void omRelease(Thread * Self, ObjectMonitor * m,
|
||||
bool FromPerThreadAlloc);
|
||||
|
@ -1953,13 +1953,9 @@ void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
|
||||
// between JNI-acquired and regular Java monitors. We can only see
|
||||
// regular Java monitors here if monitor enter-exit matching is broken.
|
||||
//
|
||||
// Optionally release any monitors for regular JavaThread exits. This
|
||||
// is provided as a work around for any bugs in monitor enter-exit
|
||||
// matching. This can be expensive so it is not enabled by default.
|
||||
//
|
||||
// ensure_join() ignores IllegalThreadStateExceptions, and so does
|
||||
// ObjectSynchronizer::release_monitors_owned_by_thread().
|
||||
if (exit_type == jni_detach || ObjectMonitor::Knob_ExitRelease) {
|
||||
if (exit_type == jni_detach) {
|
||||
// Sanity check even though JNI DetachCurrentThread() would have
|
||||
// returned JNI_ERR if there was a Java frame. JavaThread exit
|
||||
// should be done executing Java code by the time we get here.
|
||||
|
@ -255,11 +255,6 @@ void javaVFrame::print_lock_info_on(outputStream* st, int frame_count) {
|
||||
}
|
||||
}
|
||||
print_locked_object_class_name(st, Handle(THREAD, monitor->owner()), lock_state);
|
||||
if (ObjectMonitor::Knob_Verbose && mark != NULL) {
|
||||
st->print("\t- lockbits=");
|
||||
mark->print_on(st);
|
||||
st->cr();
|
||||
}
|
||||
|
||||
found_first_monitor = true;
|
||||
}
|
||||
|
@ -220,6 +220,8 @@ const char* Abstract_VM_Version::internal_vm_info_string() {
|
||||
#define HOTSPOT_BUILD_COMPILER "MS VC++ 14.0 (VS2015)"
|
||||
#elif _MSC_VER == 1912
|
||||
#define HOTSPOT_BUILD_COMPILER "MS VC++ 15.5 (VS2017)"
|
||||
#elif _MSC_VER == 1913
|
||||
#define HOTSPOT_BUILD_COMPILER "MS VC++ 15.6 (VS2017)"
|
||||
#else
|
||||
#define HOTSPOT_BUILD_COMPILER "unknown MS VC++:" XSTR(_MSC_VER)
|
||||
#endif
|
||||
|
@ -802,7 +802,13 @@ public final class DateTimeFormatterBuilder {
|
||||
return store.getText(value, style);
|
||||
}
|
||||
@Override
|
||||
public Iterator<Entry<String, Long>> getTextIterator(TemporalField field, TextStyle style, Locale locale) {
|
||||
public Iterator<Entry<String, Long>> getTextIterator(Chronology chrono,
|
||||
TemporalField field, TextStyle style, Locale locale) {
|
||||
return store.getTextIterator(style);
|
||||
}
|
||||
@Override
|
||||
public Iterator<Entry<String, Long>> getTextIterator(TemporalField field,
|
||||
TextStyle style, Locale locale) {
|
||||
return store.getTextIterator(style);
|
||||
}
|
||||
};
|
||||
|
@ -28,6 +28,7 @@ package java.util;
|
||||
import jdk.internal.HotSpotIntrinsicCandidate;
|
||||
import jdk.internal.util.ArraysSupport;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.function.BinaryOperator;
|
||||
@ -4288,21 +4289,41 @@ public class Arrays {
|
||||
// Misc
|
||||
|
||||
/**
|
||||
* Returns a fixed-size list backed by the specified array. (Changes to
|
||||
* the returned list "write through" to the array.) This method acts
|
||||
* as bridge between array-based and collection-based APIs, in
|
||||
* combination with {@link Collection#toArray}. The returned list is
|
||||
* serializable and implements {@link RandomAccess}.
|
||||
* Returns a fixed-size list backed by the specified array. Changes made to
|
||||
* the array will be visible in the returned list, and changes made to the
|
||||
* list will be visible in the array. The returned list is
|
||||
* {@link Serializable} and implements {@link RandomAccess}.
|
||||
*
|
||||
* <p>The returned list implements the optional {@code Collection} methods, except
|
||||
* those that would change the size of the returned list. Those methods leave
|
||||
* the list unchanged and throw {@link UnsupportedOperationException}.
|
||||
*
|
||||
* @apiNote
|
||||
* This method acts as bridge between array-based and collection-based
|
||||
* APIs, in combination with {@link Collection#toArray}.
|
||||
*
|
||||
* <p>This method provides a way to wrap an existing array:
|
||||
* <pre>{@code
|
||||
* Integer[] numbers = ...
|
||||
* ...
|
||||
* List<Integer> values = Arrays.asList(numbers);
|
||||
* }</pre>
|
||||
*
|
||||
* <p>This method also provides a convenient way to create a fixed-size
|
||||
* list initialized to contain several elements:
|
||||
* <pre>
|
||||
* List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
|
||||
* </pre>
|
||||
* <pre>{@code
|
||||
* List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
|
||||
* }</pre>
|
||||
*
|
||||
* <p><em>The list returned by this method is modifiable.</em>
|
||||
* To create an unmodifiable list, use
|
||||
* {@link Collections#unmodifiableList Collections.unmodifiableList}
|
||||
* or <a href="List.html#unmodifiable">Unmodifiable Lists</a>.
|
||||
*
|
||||
* @param <T> the class of the objects in the array
|
||||
* @param a the array by which the list will be backed
|
||||
* @return a list view of the specified array
|
||||
* @throws NullPointerException if the specified array is {@code null}
|
||||
*/
|
||||
@SafeVarargs
|
||||
@SuppressWarnings("varargs")
|
||||
|
@ -369,8 +369,12 @@ public class Attributes implements Map<Object,Object>, Cloneable {
|
||||
* Reads attributes from the specified input stream.
|
||||
* XXX Need to handle UTF8 values.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
void read(Manifest.FastInputStream is, byte[] lbuf) throws IOException {
|
||||
read(is, lbuf, null, 0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
int read(Manifest.FastInputStream is, byte[] lbuf, String filename, int lineNumber) throws IOException {
|
||||
String name = null, value;
|
||||
byte[] lastline = null;
|
||||
|
||||
@ -378,8 +382,11 @@ public class Attributes implements Map<Object,Object>, Cloneable {
|
||||
while ((len = is.readLine(lbuf)) != -1) {
|
||||
boolean lineContinued = false;
|
||||
byte c = lbuf[--len];
|
||||
lineNumber++;
|
||||
|
||||
if (c != '\n' && c != '\r') {
|
||||
throw new IOException("line too long");
|
||||
throw new IOException("line too long ("
|
||||
+ Manifest.getErrorPosition(filename, lineNumber) + ")");
|
||||
}
|
||||
if (len > 0 && lbuf[len-1] == '\r') {
|
||||
--len;
|
||||
@ -391,7 +398,8 @@ public class Attributes implements Map<Object,Object>, Cloneable {
|
||||
if (lbuf[0] == ' ') {
|
||||
// continuation of previous line
|
||||
if (name == null) {
|
||||
throw new IOException("misplaced continuation line");
|
||||
throw new IOException("misplaced continuation line ("
|
||||
+ Manifest.getErrorPosition(filename, lineNumber) + ")");
|
||||
}
|
||||
lineContinued = true;
|
||||
byte[] buf = new byte[lastline.length + len - 1];
|
||||
@ -406,11 +414,13 @@ public class Attributes implements Map<Object,Object>, Cloneable {
|
||||
} else {
|
||||
while (lbuf[i++] != ':') {
|
||||
if (i >= len) {
|
||||
throw new IOException("invalid header field");
|
||||
throw new IOException("invalid header field ("
|
||||
+ Manifest.getErrorPosition(filename, lineNumber) + ")");
|
||||
}
|
||||
}
|
||||
if (lbuf[i++] != ' ') {
|
||||
throw new IOException("invalid header field");
|
||||
throw new IOException("invalid header field ("
|
||||
+ Manifest.getErrorPosition(filename, lineNumber) + ")");
|
||||
}
|
||||
name = new String(lbuf, 0, 0, i - 2);
|
||||
if (is.peek() == ' ') {
|
||||
@ -433,9 +443,11 @@ public class Attributes implements Map<Object,Object>, Cloneable {
|
||||
+ "entry in the jar file.");
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IOException("invalid header field name: " + name);
|
||||
throw new IOException("invalid header field name: " + name
|
||||
+ " (" + Manifest.getErrorPosition(filename, lineNumber) + ")");
|
||||
}
|
||||
}
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -417,12 +417,12 @@ class JarFile extends ZipFile {
|
||||
if (manEntry != null) {
|
||||
if (verify) {
|
||||
byte[] b = getBytes(manEntry);
|
||||
man = new Manifest(new ByteArrayInputStream(b));
|
||||
man = new Manifest(new ByteArrayInputStream(b), getName());
|
||||
if (!jvInitialized) {
|
||||
jv = new JarVerifier(b);
|
||||
}
|
||||
} else {
|
||||
man = new Manifest(super.getInputStream(manEntry));
|
||||
man = new Manifest(super.getInputStream(manEntry), getName());
|
||||
}
|
||||
manRef = new SoftReference<>(man);
|
||||
}
|
||||
|
@ -25,14 +25,15 @@
|
||||
|
||||
package java.util.jar;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import sun.security.util.SecurityProperties;
|
||||
|
||||
/**
|
||||
* The Manifest class is used to maintain Manifest entry names and their
|
||||
@ -47,16 +48,24 @@ import java.util.Iterator;
|
||||
* @since 1.2
|
||||
*/
|
||||
public class Manifest implements Cloneable {
|
||||
|
||||
private static final boolean jarInfoInExceptionText =
|
||||
SecurityProperties.includedInExceptions("jar");
|
||||
|
||||
// manifest main attributes
|
||||
private Attributes attr = new Attributes();
|
||||
|
||||
// manifest entries
|
||||
private Map<String, Attributes> entries = new HashMap<>();
|
||||
|
||||
// name of the corresponding jar archive if available.
|
||||
private final String jarFilename;
|
||||
|
||||
/**
|
||||
* Constructs a new, empty Manifest.
|
||||
*/
|
||||
public Manifest() {
|
||||
jarFilename = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -66,15 +75,29 @@ public class Manifest implements Cloneable {
|
||||
* @throws IOException if an I/O error has occurred
|
||||
*/
|
||||
public Manifest(InputStream is) throws IOException {
|
||||
this();
|
||||
read(is);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new Manifest from the specified input stream.
|
||||
*
|
||||
* @param is the input stream containing manifest data
|
||||
* @param jarFilename the name of the corresponding jar archive if available
|
||||
* @throws IOException if an I/O error has occured
|
||||
*/
|
||||
Manifest(InputStream is, String jarFilename) throws IOException {
|
||||
read(is);
|
||||
this.jarFilename = jarFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new Manifest that is a copy of the specified Manifest.
|
||||
*
|
||||
* @param man the Manifest to copy
|
||||
*/
|
||||
public Manifest(Manifest man) {
|
||||
this();
|
||||
attr.putAll(man.getMainAttributes());
|
||||
entries.putAll(man.getEntries());
|
||||
}
|
||||
@ -179,6 +202,14 @@ public class Manifest implements Cloneable {
|
||||
return;
|
||||
}
|
||||
|
||||
static String getErrorPosition(String filename, final int lineNumber) {
|
||||
if (filename == null || !jarInfoInExceptionText) {
|
||||
return "line " + lineNumber;
|
||||
}
|
||||
|
||||
return "manifest of " + filename + ":" + lineNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the Manifest from the specified InputStream. The entry
|
||||
* names and attributes read will be merged in with the current
|
||||
@ -193,7 +224,7 @@ public class Manifest implements Cloneable {
|
||||
// Line buffer
|
||||
byte[] lbuf = new byte[512];
|
||||
// Read the main attributes for the manifest
|
||||
attr.read(fis, lbuf);
|
||||
int lineNumber = attr.read(fis, lbuf, jarFilename, 0);
|
||||
// Total number of entries, attributes read
|
||||
int ecount = 0, acount = 0;
|
||||
// Average size of entry attributes
|
||||
@ -206,8 +237,11 @@ public class Manifest implements Cloneable {
|
||||
|
||||
while ((len = fis.readLine(lbuf)) != -1) {
|
||||
byte c = lbuf[--len];
|
||||
lineNumber++;
|
||||
|
||||
if (c != '\n' && c != '\r') {
|
||||
throw new IOException("manifest line too long");
|
||||
throw new IOException("manifest line too long ("
|
||||
+ getErrorPosition(jarFilename, lineNumber) + ")");
|
||||
}
|
||||
if (len > 0 && lbuf[len-1] == '\r') {
|
||||
--len;
|
||||
@ -220,7 +254,8 @@ public class Manifest implements Cloneable {
|
||||
if (name == null) {
|
||||
name = parseName(lbuf, len);
|
||||
if (name == null) {
|
||||
throw new IOException("invalid manifest format");
|
||||
throw new IOException("invalid manifest format"
|
||||
+ getErrorPosition(jarFilename, lineNumber) + ")");
|
||||
}
|
||||
if (fis.peek() == ' ') {
|
||||
// name is wrapped
|
||||
@ -246,7 +281,7 @@ public class Manifest implements Cloneable {
|
||||
attr = new Attributes(asize);
|
||||
entries.put(name, attr);
|
||||
}
|
||||
attr.read(fis, lbuf);
|
||||
lineNumber = attr.read(fis, lbuf, jarFilename, lineNumber);
|
||||
ecount++;
|
||||
acount += attr.size();
|
||||
//XXX: Fix for when the average is 0. When it is 0,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -1884,6 +1884,102 @@ public final class Collectors {
|
||||
(l, r) -> { l.combine(r); return l; }, CH_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code Collector} that is a composite of two downstream collectors.
|
||||
* Every element passed to the resulting collector is processed by both downstream
|
||||
* collectors, then their results are merged using the specified merge function
|
||||
* into the final result.
|
||||
*
|
||||
* <p>The resulting collector functions do the following:
|
||||
*
|
||||
* <ul>
|
||||
* <li>supplier: creates a result container that contains result containers
|
||||
* obtained by calling each collector's supplier
|
||||
* <li>accumulator: calls each collector's accumulator with its result container
|
||||
* and the input element
|
||||
* <li>combiner: calls each collector's combiner with two result containers
|
||||
* <li>finisher: calls each collector's finisher with its result container,
|
||||
* then calls the supplied merger and returns its result.
|
||||
* </ul>
|
||||
*
|
||||
* <p>The resulting collector is {@link Collector.Characteristics#UNORDERED} if both downstream
|
||||
* collectors are unordered and {@link Collector.Characteristics#CONCURRENT} if both downstream
|
||||
* collectors are concurrent.
|
||||
*
|
||||
* @param <T> the type of the input elements
|
||||
* @param <R1> the result type of the first collector
|
||||
* @param <R2> the result type of the second collector
|
||||
* @param <R> the final result type
|
||||
* @param downstream1 the first downstream collector
|
||||
* @param downstream2 the second downstream collector
|
||||
* @param merger the function which merges two results into the single one
|
||||
* @return a {@code Collector} which aggregates the results of two supplied collectors.
|
||||
* @since 12
|
||||
*/
|
||||
public static <T, R1, R2, R>
|
||||
Collector<T, ?, R> teeing(Collector<? super T, ?, R1> downstream1,
|
||||
Collector<? super T, ?, R2> downstream2,
|
||||
BiFunction<? super R1, ? super R2, R> merger) {
|
||||
return teeing0(downstream1, downstream2, merger);
|
||||
}
|
||||
|
||||
private static <T, A1, A2, R1, R2, R>
|
||||
Collector<T, ?, R> teeing0(Collector<? super T, A1, R1> downstream1,
|
||||
Collector<? super T, A2, R2> downstream2,
|
||||
BiFunction<? super R1, ? super R2, R> merger) {
|
||||
Objects.requireNonNull(downstream1, "downstream1");
|
||||
Objects.requireNonNull(downstream2, "downstream2");
|
||||
Objects.requireNonNull(merger, "merger");
|
||||
|
||||
Supplier<A1> c1Supplier = Objects.requireNonNull(downstream1.supplier(), "downstream1 supplier");
|
||||
Supplier<A2> c2Supplier = Objects.requireNonNull(downstream2.supplier(), "downstream2 supplier");
|
||||
BiConsumer<A1, ? super T> c1Accumulator =
|
||||
Objects.requireNonNull(downstream1.accumulator(), "downstream1 accumulator");
|
||||
BiConsumer<A2, ? super T> c2Accumulator =
|
||||
Objects.requireNonNull(downstream2.accumulator(), "downstream2 accumulator");
|
||||
BinaryOperator<A1> c1Combiner = Objects.requireNonNull(downstream1.combiner(), "downstream1 combiner");
|
||||
BinaryOperator<A2> c2Combiner = Objects.requireNonNull(downstream2.combiner(), "downstream2 combiner");
|
||||
Function<A1, R1> c1Finisher = Objects.requireNonNull(downstream1.finisher(), "downstream1 finisher");
|
||||
Function<A2, R2> c2Finisher = Objects.requireNonNull(downstream2.finisher(), "downstream2 finisher");
|
||||
|
||||
Set<Collector.Characteristics> characteristics;
|
||||
Set<Collector.Characteristics> c1Characteristics = downstream1.characteristics();
|
||||
Set<Collector.Characteristics> c2Characteristics = downstream2.characteristics();
|
||||
if (CH_ID.containsAll(c1Characteristics) || CH_ID.containsAll(c2Characteristics)) {
|
||||
characteristics = CH_NOID;
|
||||
} else {
|
||||
EnumSet<Collector.Characteristics> c = EnumSet.noneOf(Collector.Characteristics.class);
|
||||
c.addAll(c1Characteristics);
|
||||
c.retainAll(c2Characteristics);
|
||||
c.remove(Collector.Characteristics.IDENTITY_FINISH);
|
||||
characteristics = Collections.unmodifiableSet(c);
|
||||
}
|
||||
|
||||
class PairBox {
|
||||
A1 left = c1Supplier.get();
|
||||
A2 right = c2Supplier.get();
|
||||
|
||||
void add(T t) {
|
||||
c1Accumulator.accept(left, t);
|
||||
c2Accumulator.accept(right, t);
|
||||
}
|
||||
|
||||
PairBox combine(PairBox other) {
|
||||
left = c1Combiner.apply(left, other.left);
|
||||
right = c2Combiner.apply(right, other.right);
|
||||
return this;
|
||||
}
|
||||
|
||||
R get() {
|
||||
R1 r1 = c1Finisher.apply(left);
|
||||
R2 r2 = c2Finisher.apply(right);
|
||||
return merger.apply(r1, r2);
|
||||
}
|
||||
}
|
||||
|
||||
return new CollectorImpl<>(PairBox::new, PairBox::add, PairBox::combine, PairBox::get, characteristics);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation class used by partitioningBy.
|
||||
*/
|
||||
|
@ -30,43 +30,14 @@ import java.lang.reflect.Constructor;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.Security;
|
||||
|
||||
import sun.security.util.SecurityProperties;
|
||||
|
||||
public final class SocketExceptions {
|
||||
private SocketExceptions() {}
|
||||
|
||||
/**
|
||||
* Security or system property which specifies categories of
|
||||
* (potentially sensitive) information that may be included
|
||||
* in exception text. This class only defines one category:
|
||||
* "hostInfo" which represents the hostname and port number
|
||||
* of the remote peer relating to a socket exception.
|
||||
* The property value is a comma separated list of
|
||||
* case insignificant category names.
|
||||
*/
|
||||
private static final String enhancedTextPropname = "jdk.includeInExceptions";
|
||||
|
||||
private static final boolean enhancedExceptionText = initTextProp();
|
||||
|
||||
private static boolean initTextProp() {
|
||||
return AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
|
||||
public Boolean run() {
|
||||
String val = System.getProperty(enhancedTextPropname);
|
||||
if (val == null) {
|
||||
val = Security.getProperty(enhancedTextPropname);
|
||||
if (val == null)
|
||||
return false;
|
||||
}
|
||||
String[] tokens = val.split(",");
|
||||
for (String token : tokens) {
|
||||
if (token.equalsIgnoreCase("hostinfo"))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static final boolean enhancedExceptionText =
|
||||
SecurityProperties.includedInExceptions("hostInfo");
|
||||
|
||||
/**
|
||||
* Utility which takes an exception and returns either the same exception
|
||||
@ -74,8 +45,9 @@ public final class SocketExceptions {
|
||||
* and detail message enhanced with addressing information from the
|
||||
* given InetSocketAddress.
|
||||
*
|
||||
* If the system/security property "jdk.net.enhanceExceptionText" is not
|
||||
* set or is false, then the original exception is returned.
|
||||
* If the system/security property "jdk.includeInExceptions" is not
|
||||
* set or does not contain the category hostInfo,
|
||||
* then the original exception is returned.
|
||||
*
|
||||
* Only specific IOException subtypes are supported.
|
||||
*/
|
||||
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018 SAP SE. 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 sun.security.util;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.Security;
|
||||
|
||||
public class SecurityProperties {
|
||||
|
||||
/**
|
||||
* Returns the value of the security property propName, which can be overridden
|
||||
* by a system property of the same name
|
||||
*
|
||||
* @param propName the name of the system or security property
|
||||
* @return the value of the system or security property
|
||||
*/
|
||||
public static String privilegedGetOverridable(String propName) {
|
||||
return AccessController.doPrivileged((PrivilegedAction<String>)
|
||||
() -> {
|
||||
String val = System.getProperty(propName);
|
||||
if (val == null) {
|
||||
return Security.getProperty(propName);
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true in case the system or security property "jdk.includeInExceptions"
|
||||
* contains the category refName
|
||||
*
|
||||
* @param refName the category to check
|
||||
* @return true in case the system or security property "jdk.includeInExceptions"
|
||||
* contains refName, false otherwise
|
||||
*/
|
||||
public static boolean includedInExceptions(String refName) {
|
||||
String val = privilegedGetOverridable("jdk.includeInExceptions");
|
||||
if (val == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String[] tokens = val.split(",");
|
||||
for (String token : tokens) {
|
||||
token = token.trim();
|
||||
if (token.equalsIgnoreCase(refName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -1081,7 +1081,10 @@ jceks.key.serialFilter = java.base/java.lang.Enum;java.base/java.security.KeyRep
|
||||
# java.nio.channels package will contain enhanced exception
|
||||
# message information
|
||||
#
|
||||
# jar - enables more detailed information in the IOExceptions thrown
|
||||
# by classes in the java.util.jar package
|
||||
#
|
||||
# The property setting in this file can be overridden by a system property of
|
||||
# the same name, with the same syntax and possible values.
|
||||
#
|
||||
#jdk.includeInExceptions=hostInfo
|
||||
#jdk.includeInExceptions=hostInfo,jar
|
||||
|
@ -47,7 +47,7 @@
|
||||
#ifndef SO_REUSEPORT
|
||||
#ifdef __linux__
|
||||
#define SO_REUSEPORT 15
|
||||
#elif __solaris__
|
||||
#elif defined(__solaris__)
|
||||
#define SO_REUSEPORT 0x100e
|
||||
#elif defined(AIX) || defined(MACOSX)
|
||||
#define SO_REUSEPORT 0x0200
|
||||
|
@ -37,14 +37,14 @@
|
||||
#include <pthread.h>
|
||||
/* Also defined in net/linux_close.c */
|
||||
#define INTERRUPT_SIGNAL (__SIGRTMAX - 2)
|
||||
#elif _AIX
|
||||
#elif defined(_AIX)
|
||||
#include <pthread.h>
|
||||
/* Also defined in net/aix_close.c */
|
||||
#define INTERRUPT_SIGNAL (SIGRTMAX - 1)
|
||||
#elif __solaris__
|
||||
#elif defined(__solaris__)
|
||||
#include <thread.h>
|
||||
#define INTERRUPT_SIGNAL (SIGRTMAX - 2)
|
||||
#elif _ALLBSD_SOURCE
|
||||
#elif defined(_ALLBSD_SOURCE)
|
||||
#include <pthread.h>
|
||||
/* Also defined in net/bsd_close.c */
|
||||
#define INTERRUPT_SIGNAL SIGIO
|
||||
|
@ -40,7 +40,7 @@
|
||||
#ifndef SO_REUSEPORT
|
||||
#ifdef __linux__
|
||||
#define SO_REUSEPORT 15
|
||||
#elif __solaris__
|
||||
#elif defined(__solaris__)
|
||||
#define SO_REUSEPORT 0x100e
|
||||
#elif defined(AIX) || defined(MACOSX)
|
||||
#define SO_REUSEPORT 0x0200
|
||||
|
@ -26,9 +26,11 @@
|
||||
#ifndef macosx_port_awt_debug_h
|
||||
#define macosx_port_awt_debug_h
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
bool ShouldPrintVerboseDebugging();
|
||||
JNIEXPORT bool ShouldPrintVerboseDebugging();
|
||||
|
||||
#define kInternalError "java/lang/InternalError"
|
||||
|
||||
|
@ -32,7 +32,7 @@
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <JavaNativeFoundation/JavaNativeFoundation.h>
|
||||
|
||||
@interface NSApplicationAWT : NSApplication <NSUserNotificationCenterDelegate> {
|
||||
JNIEXPORT @interface NSApplicationAWT : NSApplication <NSUserNotificationCenterDelegate> {
|
||||
NSString *fApplicationName;
|
||||
NSWindow *eventTransparentWindow;
|
||||
NSTimeInterval dummyEventTimestamp;
|
||||
@ -57,5 +57,5 @@
|
||||
|
||||
@end
|
||||
|
||||
void OSXAPP_SetApplicationDelegate(id <NSApplicationDelegate> delegate);
|
||||
JNIEXPORT void OSXAPP_SetApplicationDelegate(id <NSApplicationDelegate> delegate);
|
||||
|
||||
|
@ -23,11 +23,17 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Must include this before JavaNativeFoundation.h to get jni.h from build
|
||||
*/
|
||||
#include "jni.h"
|
||||
#include "jni_util.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <JavaNativeFoundation/JavaNativeFoundation.h>
|
||||
|
||||
|
||||
@interface PropertiesUtilities : NSObject
|
||||
JNIEXPORT @interface PropertiesUtilities : NSObject
|
||||
|
||||
+ (NSString *) javaSystemPropertyForKey:(NSString *)key withEnv:(JNIEnv *)env;
|
||||
|
||||
|
@ -26,6 +26,8 @@
|
||||
#ifndef __THREADUTILITIES_H
|
||||
#define __THREADUTILITIES_H
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
#import <pthread.h>
|
||||
|
||||
#import "AWT_debug.h"
|
||||
@ -135,6 +137,6 @@ __attribute__((visibility("default")))
|
||||
+ (void)performOnMainThread:(SEL)aSelector on:(id)target withObject:(id)arg waitUntilDone:(BOOL)wait;
|
||||
@end
|
||||
|
||||
void OSXAPP_SetJavaVM(JavaVM *vm);
|
||||
JNIEXPORT void OSXAPP_SetJavaVM(JavaVM *vm);
|
||||
|
||||
#endif /* __THREADUTILITIES_H */
|
||||
|
@ -433,7 +433,9 @@ public class Flags {
|
||||
SYSTEM_MODULE(Flags.SYSTEM_MODULE),
|
||||
DEPRECATED_ANNOTATION(Flags.DEPRECATED_ANNOTATION),
|
||||
DEPRECATED_REMOVAL(Flags.DEPRECATED_REMOVAL),
|
||||
HAS_RESOURCE(Flags.HAS_RESOURCE);
|
||||
HAS_RESOURCE(Flags.HAS_RESOURCE),
|
||||
POTENTIALLY_AMBIGUOUS(Flags.POTENTIALLY_AMBIGUOUS),
|
||||
ANONCONSTR_BASED(Flags.ANONCONSTR_BASED);
|
||||
|
||||
Flag(long flag) {
|
||||
this.value = flag;
|
||||
|
@ -166,10 +166,10 @@ public class Analyzer {
|
||||
res = EnumSet.allOf(AnalyzerMode.class);
|
||||
}
|
||||
for (AnalyzerMode mode : values()) {
|
||||
if (modes.contains(mode.opt)) {
|
||||
res.add(mode);
|
||||
} else if (modes.contains("-" + mode.opt) || !mode.feature.allowedInSource(source)) {
|
||||
if (modes.contains("-" + mode.opt) || !mode.feature.allowedInSource(source)) {
|
||||
res.remove(mode);
|
||||
} else if (modes.contains(mode.opt)) {
|
||||
res.add(mode);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
|
@ -2689,12 +2689,14 @@ public class Attr extends JCTree.Visitor {
|
||||
*/
|
||||
@Override
|
||||
public void visitLambda(final JCLambda that) {
|
||||
boolean wrongContext = false;
|
||||
if (pt().isErroneous() || (pt().hasTag(NONE) && pt() != Type.recoveryType)) {
|
||||
if (pt().hasTag(NONE) && (env.info.enclVar == null || !env.info.enclVar.type.isErroneous())) {
|
||||
//lambda only allowed in assignment or method invocation/cast context
|
||||
log.error(that.pos(), Errors.UnexpectedLambda);
|
||||
}
|
||||
resultInfo = recoveryInfo;
|
||||
wrongContext = true;
|
||||
}
|
||||
//create an environment for attribution of the lambda expression
|
||||
final Env<AttrContext> localEnv = lambdaEnv(that, env);
|
||||
@ -2811,7 +2813,8 @@ public class Attr extends JCTree.Visitor {
|
||||
|
||||
checkAccessibleTypes(that, localEnv, resultInfo.checkContext.inferenceContext(), lambdaType, currentTarget);
|
||||
}
|
||||
result = check(that, currentTarget, KindSelector.VAL, resultInfo);
|
||||
result = wrongContext ? that.type = types.createErrorType(pt())
|
||||
: check(that, currentTarget, KindSelector.VAL, resultInfo);
|
||||
} catch (Types.FunctionDescriptorLookupError ex) {
|
||||
JCDiagnostic cause = ex.getDiagnostic();
|
||||
resultInfo.checkContext.report(that, cause);
|
||||
@ -5342,14 +5345,6 @@ public class Attr extends JCTree.Visitor {
|
||||
super.visitUnary(that);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLambda(JCLambda that) {
|
||||
super.visitLambda(that);
|
||||
if (that.target == null) {
|
||||
that.target = syms.unknownType;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReference(JCMemberReference that) {
|
||||
super.visitReference(that);
|
||||
@ -5357,9 +5352,6 @@ public class Attr extends JCTree.Visitor {
|
||||
that.sym = new MethodSymbol(0, names.empty, dummyMethodType(),
|
||||
syms.noSymbol);
|
||||
}
|
||||
if (that.target == null) {
|
||||
that.target = syms.unknownType;
|
||||
}
|
||||
}
|
||||
}
|
||||
// </editor-fold>
|
||||
|
@ -50,8 +50,9 @@ import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.CodeSigner;
|
||||
import java.security.CodeSource;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -63,6 +64,8 @@ import java.util.Map;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.NestingKind;
|
||||
@ -182,7 +185,7 @@ public class Main {
|
||||
public void run(String[] runtimeArgs, String[] args) throws Fault, InvocationTargetException {
|
||||
Path file = getFile(args);
|
||||
|
||||
Context context = new Context();
|
||||
Context context = new Context(file.toAbsolutePath());
|
||||
String mainClassName = compile(file, getJavacOpts(runtimeArgs), context);
|
||||
|
||||
String[] appArgs = Arrays.copyOfRange(args, 1, args.length);
|
||||
@ -193,7 +196,7 @@ public class Main {
|
||||
* Returns the path for the filename found in the first of an array of arguments.
|
||||
*
|
||||
* @param args the array
|
||||
* @return the path
|
||||
* @return the path, as given in the array of args
|
||||
* @throws Fault if there is a problem determining the path, or if the file does not exist
|
||||
*/
|
||||
private Path getFile(String[] args) throws Fault {
|
||||
@ -396,12 +399,10 @@ public class Main {
|
||||
*/
|
||||
private void execute(String mainClassName, String[] appArgs, Context context)
|
||||
throws Fault, InvocationTargetException {
|
||||
System.setProperty("jdk.launcher.sourcefile", context.file.toString());
|
||||
ClassLoader cl = context.getClassLoader(ClassLoader.getSystemClassLoader());
|
||||
try {
|
||||
Class<?> appClass = Class.forName(mainClassName, true, cl);
|
||||
if (appClass.getClassLoader() != cl) {
|
||||
throw new Fault(Errors.UnexpectedClass(mainClassName));
|
||||
}
|
||||
Method main = appClass.getDeclaredMethod("main", String[].class);
|
||||
int PUBLIC_STATIC = Modifier.PUBLIC | Modifier.STATIC;
|
||||
if ((main.getModifiers() & PUBLIC_STATIC) != PUBLIC_STATIC) {
|
||||
@ -481,14 +482,19 @@ public class Main {
|
||||
* a class loader.
|
||||
*/
|
||||
private static class Context {
|
||||
private Map<String, byte[]> inMemoryClasses = new HashMap<>();
|
||||
private final Path file;
|
||||
private final Map<String, byte[]> inMemoryClasses = new HashMap<>();
|
||||
|
||||
Context(Path file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
JavaFileManager getFileManager(StandardJavaFileManager delegate) {
|
||||
return new MemoryFileManager(inMemoryClasses, delegate);
|
||||
}
|
||||
|
||||
ClassLoader getClassLoader(ClassLoader parent) {
|
||||
return new MemoryClassLoader(inMemoryClasses, parent);
|
||||
return new MemoryClassLoader(inMemoryClasses, parent, file);
|
||||
}
|
||||
}
|
||||
|
||||
@ -535,36 +541,126 @@ public class Main {
|
||||
}
|
||||
|
||||
/**
|
||||
* An in-memory classloader, that uses an in-memory cache written by {@link MemoryFileManager}.
|
||||
* An in-memory classloader, that uses an in-memory cache of classes written by
|
||||
* {@link MemoryFileManager}.
|
||||
*
|
||||
* <p>The classloader uses the standard parent-delegation model, just providing
|
||||
* {@code findClass} to find classes in the in-memory cache.
|
||||
* <p>The classloader inverts the standard parent-delegation model, giving preference
|
||||
* to classes defined in the source file before classes known to the parent (such
|
||||
* as any like-named classes that might be found on the application class path.)
|
||||
*/
|
||||
private static class MemoryClassLoader extends ClassLoader {
|
||||
/**
|
||||
* The map of classes known to this class loader, indexed by
|
||||
* The map of all classes found in the source file, indexed by
|
||||
* {@link ClassLoader#name binary name}.
|
||||
*/
|
||||
private final Map<String, byte[]> map;
|
||||
private final Map<String, byte[]> sourceFileClasses;
|
||||
|
||||
MemoryClassLoader(Map<String, byte[]> map, ClassLoader parent) {
|
||||
/**
|
||||
* A minimal protection domain, specifying a code source of the source file itself,
|
||||
* used for classes found in the source file and defined by this loader.
|
||||
*/
|
||||
private final ProtectionDomain domain;
|
||||
|
||||
MemoryClassLoader(Map<String, byte[]> sourceFileClasses, ClassLoader parent, Path file) {
|
||||
super(parent);
|
||||
this.map = map;
|
||||
this.sourceFileClasses = sourceFileClasses;
|
||||
CodeSource codeSource;
|
||||
try {
|
||||
codeSource = new CodeSource(file.toUri().toURL(), (CodeSigner[]) null);
|
||||
} catch (MalformedURLException e) {
|
||||
codeSource = null;
|
||||
}
|
||||
domain = new ProtectionDomain(codeSource, null, this, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override loadClass to check for classes defined in the source file
|
||||
* before checking for classes in the parent class loader,
|
||||
* including those on the classpath.
|
||||
*
|
||||
* {@code loadClass(String name)} calls this method, and so will have the same behavior.
|
||||
*
|
||||
* @param name the name of the class to load
|
||||
* @param resolve whether or not to resolve the class
|
||||
* @return the class
|
||||
* @throws ClassNotFoundException if the class is not found
|
||||
*/
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
synchronized (getClassLoadingLock(name)) {
|
||||
Class<?> c = findLoadedClass(name);
|
||||
if (c == null) {
|
||||
if (sourceFileClasses.containsKey(name)) {
|
||||
c = findClass(name);
|
||||
} else {
|
||||
c = getParent().loadClass(name);
|
||||
}
|
||||
if (resolve) {
|
||||
resolveClass(c);
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Override getResource to check for resources (i.e. class files) defined in the
|
||||
* source file before checking resources in the parent class loader,
|
||||
* including those on the class path.
|
||||
*
|
||||
* {@code getResourceAsStream(String name)} calls this method,
|
||||
* and so will have the same behavior.
|
||||
*
|
||||
* @param name the name of the resource
|
||||
* @return a URL for the resource, or null if not found
|
||||
*/
|
||||
@Override
|
||||
public URL getResource(String name) {
|
||||
if (sourceFileClasses.containsKey(toBinaryName(name))) {
|
||||
return findResource(name);
|
||||
} else {
|
||||
return getParent().getResource(name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override getResources to check for resources (i.e. class files) defined in the
|
||||
* source file before checking resources in the parent class loader,
|
||||
* including those on the class path.
|
||||
*
|
||||
* @param name the name of the resource
|
||||
* @return an enumeration of the resources in this loader and in the application class loader
|
||||
*/
|
||||
@Override
|
||||
public Enumeration<URL> getResources(String name) throws IOException {
|
||||
URL u = findResource(name);
|
||||
Enumeration<URL> e = getParent().getResources(name);
|
||||
if (u == null) {
|
||||
return e;
|
||||
} else {
|
||||
List<URL> list = new ArrayList<>();
|
||||
list.add(u);
|
||||
while (e.hasMoreElements()) {
|
||||
list.add(e.nextElement());
|
||||
}
|
||||
return Collections.enumeration(list);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
byte[] bytes = map.get(name);
|
||||
byte[] bytes = sourceFileClasses.get(name);
|
||||
if (bytes == null) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
return defineClass(name, bytes, 0, bytes.length);
|
||||
return defineClass(name, bytes, 0, bytes.length, domain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL findResource(String name) {
|
||||
String binaryName = toBinaryName(name);
|
||||
if (binaryName == null || map.get(binaryName) == null) {
|
||||
if (binaryName == null || sourceFileClasses.get(binaryName) == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -628,7 +724,7 @@ public class Main {
|
||||
if (!u.getProtocol().equalsIgnoreCase(PROTOCOL)) {
|
||||
throw new IllegalArgumentException(u.toString());
|
||||
}
|
||||
return new MemoryURLConnection(u, map.get(toBinaryName(u.getPath())));
|
||||
return new MemoryURLConnection(u, sourceFileClasses.get(toBinaryName(u.getPath())));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -328,7 +328,7 @@ public enum Option {
|
||||
|
||||
ENCODING("-encoding", "opt.arg.encoding", "opt.encoding", STANDARD, FILEMANAGER),
|
||||
|
||||
SOURCE("-source", "opt.arg.release", "opt.source", STANDARD, BASIC) {
|
||||
SOURCE("--source -source", "opt.arg.release", "opt.source", STANDARD, BASIC) {
|
||||
@Override
|
||||
public void process(OptionHelper helper, String option, String operand) throws InvalidValueException {
|
||||
Source source = Source.lookup(operand);
|
||||
@ -349,7 +349,7 @@ public enum Option {
|
||||
}
|
||||
},
|
||||
|
||||
TARGET("-target", "opt.arg.release", "opt.target", STANDARD, BASIC) {
|
||||
TARGET("--target -target", "opt.arg.release", "opt.target", STANDARD, BASIC) {
|
||||
@Override
|
||||
public void process(OptionHelper helper, String option, String operand) throws InvalidValueException {
|
||||
Target target = Target.lookup(operand);
|
||||
|
@ -108,10 +108,6 @@ launcher.err.main.not.void=\
|
||||
launcher.err.cant.find.class=\
|
||||
can''t find class: {0}
|
||||
|
||||
# 0: string
|
||||
launcher.err.unexpected.class=\
|
||||
class found on application class path: {0}
|
||||
|
||||
# 0: string
|
||||
launcher.err.cant.find.main.method=\
|
||||
can''t find main(String[]) method in class: {0}
|
||||
|
@ -380,8 +380,8 @@ public class Start extends ToolOption.Helper {
|
||||
String platformString = compOpts.get("--release");
|
||||
|
||||
if (platformString != null) {
|
||||
if (compOpts.isSet("-source")) {
|
||||
usageError("main.release.bootclasspath.conflict", "-source");
|
||||
if (compOpts.isSet(Option.SOURCE.primaryName)) {
|
||||
usageError("main.release.bootclasspath.conflict", Option.SOURCE.primaryName);
|
||||
}
|
||||
if (fileManagerOpts.containsKey(Option.BOOT_CLASS_PATH)) {
|
||||
usageError("main.release.bootclasspath.conflict", Option.BOOT_CLASS_PATH.getPrimaryName());
|
||||
|
@ -172,6 +172,13 @@ public enum ToolOption {
|
||||
},
|
||||
|
||||
SOURCE("-source", true) {
|
||||
@Override
|
||||
public void process(Helper helper, String arg) {
|
||||
helper.setCompilerOpt("--source", arg);
|
||||
}
|
||||
},
|
||||
|
||||
SOURCE2("--source", true) {
|
||||
@Override
|
||||
public void process(Helper helper, String arg) {
|
||||
helper.setCompilerOpt(opt, arg);
|
||||
|
@ -208,12 +208,6 @@ public class HtmlConfiguration extends BaseConfiguration {
|
||||
*/
|
||||
public HtmlVersion htmlVersion = null;
|
||||
|
||||
/**
|
||||
* Flag to enable/disable use of module directories when generating docs for modules
|
||||
* Default: on (module directories are enabled).
|
||||
*/
|
||||
public boolean useModuleDirectories = true;
|
||||
|
||||
/**
|
||||
* Collected set of doclint options
|
||||
*/
|
||||
@ -840,13 +834,6 @@ public class HtmlConfiguration extends BaseConfiguration {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
new XOption(resources, "--no-module-directories") {
|
||||
@Override
|
||||
public boolean process(String option, List<String> args) {
|
||||
useModuleDirectories = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
Set<Doclet.Option> oset = new TreeSet<>();
|
||||
|
@ -355,12 +355,24 @@ public class HtmlDocletWriter {
|
||||
/**
|
||||
* Returns a TagletWriter that knows how to write HTML.
|
||||
*
|
||||
* @param isFirstSentence true if we want to write the first sentence
|
||||
* @return a TagletWriter that knows how to write HTML.
|
||||
*/
|
||||
public TagletWriter getTagletWriterInstance(boolean isFirstSentence) {
|
||||
return new TagletWriterImpl(this, isFirstSentence);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a TagletWriter that knows how to write HTML.
|
||||
*
|
||||
* @param isFirstSentence true if we want to write the first sentence
|
||||
* @param inSummary true if tags are to be added in a summary section
|
||||
* @return a TagletWriter
|
||||
*/
|
||||
public TagletWriter getTagletWriterInstance(boolean isFirstSentence, boolean inSummary) {
|
||||
return new TagletWriterImpl(this, isFirstSentence, inSummary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Package link, with target frame.
|
||||
*
|
||||
@ -610,7 +622,7 @@ public class HtmlDocletWriter {
|
||||
return links.createLink(pathString(packageElement, DocPaths.PACKAGE_SUMMARY),
|
||||
label);
|
||||
} else {
|
||||
DocLink crossPkgLink = getCrossPackageLink(utils.getPackageName(packageElement));
|
||||
DocLink crossPkgLink = getCrossPackageLink(packageElement);
|
||||
if (crossPkgLink != null) {
|
||||
return links.createLink(crossPkgLink, label);
|
||||
} else {
|
||||
@ -693,11 +705,10 @@ public class HtmlDocletWriter {
|
||||
|
||||
/*************************************************************
|
||||
* Return a class cross link to external class documentation.
|
||||
* The name must be fully qualified to determine which package
|
||||
* the class is in. The -link option does not allow users to
|
||||
* The -link option does not allow users to
|
||||
* link to external classes in the "default" package.
|
||||
*
|
||||
* @param qualifiedClassName the qualified name of the external class.
|
||||
* @param classElement the class element
|
||||
* @param refMemName the name of the member being referenced. This should
|
||||
* be null or empty string if no member is being referenced.
|
||||
* @param label the label for the external link.
|
||||
@ -705,19 +716,15 @@ public class HtmlDocletWriter {
|
||||
* @param code true if the label should be code font.
|
||||
* @return the link
|
||||
*/
|
||||
public Content getCrossClassLink(String qualifiedClassName, String refMemName,
|
||||
public Content getCrossClassLink(TypeElement classElement, String refMemName,
|
||||
Content label, boolean strong, boolean code) {
|
||||
String className = "";
|
||||
String packageName = qualifiedClassName == null ? "" : qualifiedClassName;
|
||||
int periodIndex;
|
||||
while ((periodIndex = packageName.lastIndexOf('.')) != -1) {
|
||||
className = packageName.substring(periodIndex + 1, packageName.length()) +
|
||||
(className.length() > 0 ? "." + className : "");
|
||||
if (classElement != null) {
|
||||
String className = utils.getSimpleName(classElement);
|
||||
PackageElement packageElement = utils.containingPackage(classElement);
|
||||
Content defaultLabel = new StringContent(className);
|
||||
if (code)
|
||||
defaultLabel = HtmlTree.CODE(defaultLabel);
|
||||
packageName = packageName.substring(0, periodIndex);
|
||||
if (getCrossPackageLink(packageName) != null) {
|
||||
if (getCrossPackageLink(packageElement) != null) {
|
||||
/*
|
||||
The package exists in external documentation, so link to the external
|
||||
class (assuming that it exists). This is definitely a limitation of
|
||||
@ -725,13 +732,13 @@ public class HtmlDocletWriter {
|
||||
exists, but no way to determine if the external class exists. We just
|
||||
have to assume that it does.
|
||||
*/
|
||||
DocLink link = configuration.extern.getExternalLink(packageName, pathToRoot,
|
||||
DocLink link = configuration.extern.getExternalLink(packageElement, pathToRoot,
|
||||
className + ".html", refMemName);
|
||||
return links.createLink(link,
|
||||
(label == null) || label.isEmpty() ? defaultLabel : label,
|
||||
strong,
|
||||
resources.getText("doclet.Href_Class_Or_Interface_Title", packageName),
|
||||
"", true);
|
||||
resources.getText("doclet.Href_Class_Or_Interface_Title",
|
||||
utils.getPackageName(packageElement)), "", true);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@ -744,14 +751,14 @@ public class HtmlDocletWriter {
|
||||
return configuration.extern.isExternal(typeElement);
|
||||
}
|
||||
|
||||
public DocLink getCrossPackageLink(String pkgName) {
|
||||
return configuration.extern.getExternalLink(pkgName, pathToRoot,
|
||||
public DocLink getCrossPackageLink(PackageElement element) {
|
||||
return configuration.extern.getExternalLink(element, pathToRoot,
|
||||
DocPaths.PACKAGE_SUMMARY.getPath());
|
||||
}
|
||||
|
||||
public DocLink getCrossModuleLink(String mdleName) {
|
||||
return configuration.extern.getExternalLink(mdleName, pathToRoot,
|
||||
docPaths.moduleSummary(mdleName).getPath());
|
||||
public DocLink getCrossModuleLink(ModuleElement element) {
|
||||
return configuration.extern.getExternalLink(element, pathToRoot,
|
||||
docPaths.moduleSummary(utils.getModuleName(element)).getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1024,17 +1031,13 @@ public class HtmlDocletWriter {
|
||||
return getPackageLink(refPackage, label);
|
||||
} else {
|
||||
// @see is not referencing an included class, module or package. Check for cross links.
|
||||
Content classCrossLink;
|
||||
DocLink elementCrossLink = (configuration.extern.isModule(refClassName))
|
||||
? getCrossModuleLink(refClassName) : getCrossPackageLink(refClassName);
|
||||
? getCrossModuleLink(utils.elementUtils.getModuleElement(refClassName)) :
|
||||
(refPackage != null) ? getCrossPackageLink(refPackage) : null;
|
||||
if (elementCrossLink != null) {
|
||||
// Element cross link found
|
||||
return links.createLink(elementCrossLink,
|
||||
(label.isEmpty() ? text : label), true);
|
||||
} else if ((classCrossLink = getCrossClassLink(refClassName,
|
||||
refMemName, label, false, !isLinkPlain)) != null) {
|
||||
// Class cross link found (possibly to a member in the class)
|
||||
return classCrossLink;
|
||||
} else {
|
||||
// No cross link found so print warning
|
||||
messages.warning(ch.getDocTreePath(see),
|
||||
@ -1136,7 +1139,7 @@ public class HtmlDocletWriter {
|
||||
public void addInlineComment(Element element, DocTree tag, Content htmltree) {
|
||||
CommentHelper ch = utils.getCommentHelper(element);
|
||||
List<? extends DocTree> description = ch.getDescription(configuration, tag);
|
||||
addCommentTags(element, tag, description, false, false, htmltree);
|
||||
addCommentTags(element, tag, description, false, false, false, htmltree);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1160,7 +1163,7 @@ public class HtmlDocletWriter {
|
||||
*/
|
||||
public void addInlineDeprecatedComment(Element e, DocTree tag, Content htmltree) {
|
||||
CommentHelper ch = utils.getCommentHelper(e);
|
||||
addCommentTags(e, ch.getBody(configuration, tag), true, false, htmltree);
|
||||
addCommentTags(e, ch.getBody(configuration, tag), true, false, false, htmltree);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1181,13 +1184,13 @@ public class HtmlDocletWriter {
|
||||
* @param htmltree the documentation tree to which the summary will be added
|
||||
*/
|
||||
public void addSummaryComment(Element element, List<? extends DocTree> firstSentenceTags, Content htmltree) {
|
||||
addCommentTags(element, firstSentenceTags, false, true, htmltree);
|
||||
addCommentTags(element, firstSentenceTags, false, true, true, htmltree);
|
||||
}
|
||||
|
||||
public void addSummaryDeprecatedComment(Element element, DocTree tag, Content htmltree) {
|
||||
CommentHelper ch = utils.getCommentHelper(element);
|
||||
List<? extends DocTree> body = ch.getBody(configuration, tag);
|
||||
addCommentTags(element, ch.getFirstSentenceTrees(configuration, body), true, true, htmltree);
|
||||
addCommentTags(element, ch.getFirstSentenceTrees(configuration, body), true, true, true, htmltree);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1197,7 +1200,7 @@ public class HtmlDocletWriter {
|
||||
* @param htmltree the documentation tree to which the inline comments will be added
|
||||
*/
|
||||
public void addInlineComment(Element element, Content htmltree) {
|
||||
addCommentTags(element, utils.getFullBody(element), false, false, htmltree);
|
||||
addCommentTags(element, utils.getFullBody(element), false, false, false, htmltree);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1207,11 +1210,12 @@ public class HtmlDocletWriter {
|
||||
* @param tags the first sentence tags for the doc
|
||||
* @param depr true if it is deprecated
|
||||
* @param first true if the first sentence tags should be added
|
||||
* @param inSummary true if the comment tags are added into the summary section
|
||||
* @param htmltree the documentation tree to which the comment tags will be added
|
||||
*/
|
||||
private void addCommentTags(Element element, List<? extends DocTree> tags, boolean depr,
|
||||
boolean first, Content htmltree) {
|
||||
addCommentTags(element, null, tags, depr, first, htmltree);
|
||||
boolean first, boolean inSummary, Content htmltree) {
|
||||
addCommentTags(element, null, tags, depr, first, inSummary, htmltree);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1222,15 +1226,16 @@ public class HtmlDocletWriter {
|
||||
* @param tags the first sentence tags for the doc
|
||||
* @param depr true if it is deprecated
|
||||
* @param first true if the first sentence tags should be added
|
||||
* @param inSummary true if the comment tags are added into the summary section
|
||||
* @param htmltree the documentation tree to which the comment tags will be added
|
||||
*/
|
||||
private void addCommentTags(Element element, DocTree holderTag, List<? extends DocTree> tags, boolean depr,
|
||||
boolean first, Content htmltree) {
|
||||
boolean first, boolean inSummary, Content htmltree) {
|
||||
if(configuration.nocomment){
|
||||
return;
|
||||
}
|
||||
Content div;
|
||||
Content result = commentTagsToContent(null, element, tags, first);
|
||||
Content result = commentTagsToContent(null, element, tags, first, inSummary);
|
||||
if (depr) {
|
||||
div = HtmlTree.DIV(HtmlStyle.deprecationComment, result);
|
||||
htmltree.addContent(div);
|
||||
@ -1276,10 +1281,10 @@ public class HtmlDocletWriter {
|
||||
private boolean commentRemoved = false;
|
||||
|
||||
/**
|
||||
* Converts inline tags and text to text strings, expanding the
|
||||
* Converts inline tags and text to Content, expanding the
|
||||
* inline tags along the way. Called wherever text can contain
|
||||
* an inline tag, such as in comments or in free-form text arguments
|
||||
* to non-inline tags.
|
||||
* to block tags.
|
||||
*
|
||||
* @param holderTag specific tag where comment resides
|
||||
* @param element specific element where comment resides
|
||||
@ -1290,6 +1295,25 @@ public class HtmlDocletWriter {
|
||||
*/
|
||||
public Content commentTagsToContent(DocTree holderTag, Element element,
|
||||
List<? extends DocTree> tags, boolean isFirstSentence) {
|
||||
return commentTagsToContent(holderTag, element, tags, isFirstSentence, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts inline tags and text to text strings, expanding the
|
||||
* inline tags along the way. Called wherever text can contain
|
||||
* an inline tag, such as in comments or in free-form text arguments
|
||||
* to block tags.
|
||||
*
|
||||
* @param holderTag specific tag where comment resides
|
||||
* @param element specific element where comment resides
|
||||
* @param tags array of text tags and inline tags (often alternating)
|
||||
present in the text of interest for this element
|
||||
* @param isFirstSentence true if text is first sentence
|
||||
* @param inSummary if the comment tags are added into the summary section
|
||||
* @return a Content object
|
||||
*/
|
||||
public Content commentTagsToContent(DocTree holderTag, Element element,
|
||||
List<? extends DocTree> tags, boolean isFirstSentence, boolean inSummary) {
|
||||
|
||||
final Content result = new ContentBuilder() {
|
||||
@Override
|
||||
@ -1454,7 +1478,7 @@ public class HtmlDocletWriter {
|
||||
public Boolean visitIndex(IndexTree node, Content p) {
|
||||
Content output = TagletWriter.getInlineTagOutput(element,
|
||||
configuration.tagletManager, holderTag, tag,
|
||||
getTagletWriterInstance(isFirstSentence));
|
||||
getTagletWriterInstance(isFirstSentence, inSummary));
|
||||
if (output != null) {
|
||||
result.addContent(output);
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ public class LinkFactoryImpl extends LinkFactory {
|
||||
}
|
||||
} else {
|
||||
Content crossLink = m_writer.getCrossClassLink(
|
||||
typeElement.getQualifiedName().toString(), classLinkInfo.where,
|
||||
typeElement, classLinkInfo.where,
|
||||
label, classLinkInfo.isStrong, true);
|
||||
if (crossLink != null) {
|
||||
link.addContent(crossLink);
|
||||
|
@ -396,14 +396,14 @@ public class ModuleWriterImpl extends HtmlDocletWriter implements ModuleSummaryW
|
||||
(utils.getBlockTags(mdle, DocTree.Kind.PROVIDES)).forEach((tree) -> {
|
||||
TypeElement t = ch.getServiceType(configuration, tree);
|
||||
if (t != null) {
|
||||
providesTrees.put(t, commentTagsToContent(tree, mdle, ch.getDescription(configuration, tree), false));
|
||||
providesTrees.put(t, commentTagsToContent(tree, mdle, ch.getDescription(configuration, tree), false, true));
|
||||
}
|
||||
});
|
||||
// Generate the map of all services listed using @uses, and the description.
|
||||
(utils.getBlockTags(mdle, DocTree.Kind.USES)).forEach((tree) -> {
|
||||
TypeElement t = ch.getServiceType(configuration, tree);
|
||||
if (t != null) {
|
||||
usesTrees.put(t, commentTagsToContent(tree, mdle, ch.getDescription(configuration, tree), false));
|
||||
usesTrees.put(t, commentTagsToContent(tree, mdle, ch.getDescription(configuration, tree), false, true));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -71,12 +71,18 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
private final HtmlDocletWriter htmlWriter;
|
||||
private final HtmlConfiguration configuration;
|
||||
private final Utils utils;
|
||||
private final boolean inSummary;
|
||||
|
||||
public TagletWriterImpl(HtmlDocletWriter htmlWriter, boolean isFirstSentence) {
|
||||
this(htmlWriter, isFirstSentence, false);
|
||||
}
|
||||
|
||||
public TagletWriterImpl(HtmlDocletWriter htmlWriter, boolean isFirstSentence, boolean inSummary) {
|
||||
super(isFirstSentence);
|
||||
this.htmlWriter = htmlWriter;
|
||||
configuration = htmlWriter.configuration;
|
||||
this.utils = configuration.utils;
|
||||
this.inSummary = inSummary;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,53 +113,58 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
String desc = ch.getText(itt.getDescription());
|
||||
|
||||
String anchorName = htmlWriter.links.getName(tagText);
|
||||
Content result = HtmlTree.A_ID(HtmlStyle.searchTagResult, anchorName, new StringContent(tagText));
|
||||
if (configuration.createindex && !tagText.isEmpty()) {
|
||||
SearchIndexItem si = new SearchIndexItem();
|
||||
si.setLabel(tagText);
|
||||
si.setDescription(desc);
|
||||
DocPaths docPaths = configuration.docPaths;
|
||||
new SimpleElementVisitor9<Void, Void>() {
|
||||
@Override
|
||||
public Void visitModule(ModuleElement e, Void p) {
|
||||
si.setUrl(docPaths.moduleSummary(e).getPath() + "#" + anchorName);
|
||||
si.setHolder(utils.getFullyQualifiedName(element));
|
||||
return null;
|
||||
}
|
||||
Content result = null;
|
||||
if (isFirstSentence && inSummary) {
|
||||
result = new StringContent(tagText);
|
||||
} else {
|
||||
result = HtmlTree.A_ID(HtmlStyle.searchTagResult, anchorName, new StringContent(tagText));
|
||||
if (configuration.createindex && !tagText.isEmpty()) {
|
||||
SearchIndexItem si = new SearchIndexItem();
|
||||
si.setLabel(tagText);
|
||||
si.setDescription(desc);
|
||||
DocPaths docPaths = configuration.docPaths;
|
||||
new SimpleElementVisitor9<Void, Void>() {
|
||||
@Override
|
||||
public Void visitModule(ModuleElement e, Void p) {
|
||||
si.setUrl(docPaths.moduleSummary(e).getPath() + "#" + anchorName);
|
||||
si.setHolder(utils.getFullyQualifiedName(element));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPackage(PackageElement e, Void p) {
|
||||
si.setUrl(docPaths.forPackage(e).getPath()
|
||||
+ "/" + DocPaths.PACKAGE_SUMMARY.getPath() + "#" + anchorName);
|
||||
si.setHolder(utils.getSimpleName(element));
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Void visitPackage(PackageElement e, Void p) {
|
||||
si.setUrl(docPaths.forPackage(e).getPath()
|
||||
+ "/" + DocPaths.PACKAGE_SUMMARY.getPath() + "#" + anchorName);
|
||||
si.setHolder(utils.getSimpleName(element));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitType(TypeElement e, Void p) {
|
||||
si.setUrl(docPaths.forClass(e).getPath() + "#" + anchorName);
|
||||
si.setHolder(utils.getFullyQualifiedName(e));
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Void visitType(TypeElement e, Void p) {
|
||||
si.setUrl(docPaths.forClass(e).getPath() + "#" + anchorName);
|
||||
si.setHolder(utils.getFullyQualifiedName(e));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitVariable(VariableElement e, Void p) {
|
||||
TypeElement te = utils.getEnclosingTypeElement(e);
|
||||
si.setUrl(docPaths.forClass(te).getPath() + "#" + anchorName);
|
||||
si.setHolder(utils.getFullyQualifiedName(e) + "." + utils.getSimpleName(e));
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public Void visitVariable(VariableElement e, Void p) {
|
||||
TypeElement te = utils.getEnclosingTypeElement(e);
|
||||
si.setUrl(docPaths.forClass(te).getPath() + "#" + anchorName);
|
||||
si.setHolder(utils.getFullyQualifiedName(e) + "." + utils.getSimpleName(e));
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void defaultAction(Element e, Void p) {
|
||||
TypeElement te = utils.getEnclosingTypeElement(e);
|
||||
si.setUrl(docPaths.forClass(te).getPath() + "#" + anchorName);
|
||||
si.setHolder(utils.getFullyQualifiedName(e));
|
||||
return null;
|
||||
}
|
||||
}.visit(element);
|
||||
si.setCategory(configuration.getContent("doclet.SearchTags").toString());
|
||||
configuration.tagSearchIndex.add(si);
|
||||
@Override
|
||||
protected Void defaultAction(Element e, Void p) {
|
||||
TypeElement te = utils.getEnclosingTypeElement(e);
|
||||
si.setUrl(docPaths.forClass(te).getPath() + "#" + anchorName);
|
||||
si.setHolder(utils.getFullyQualifiedName(e));
|
||||
return null;
|
||||
}
|
||||
}.visit(element);
|
||||
si.setCategory(configuration.getContent("doclet.SearchTags").toString());
|
||||
configuration.tagSearchIndex.add(si);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -236,7 +247,7 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
body.addContent(HtmlTree.CODE(new RawHtml(paramName)));
|
||||
body.addContent(" - ");
|
||||
List<? extends DocTree> description = ch.getDescription(configuration, paramTag);
|
||||
body.addContent(htmlWriter.commentTagsToContent(paramTag, element, description, false));
|
||||
body.addContent(htmlWriter.commentTagsToContent(paramTag, element, description, false, inSummary));
|
||||
HtmlTree result = HtmlTree.DD(body);
|
||||
return result;
|
||||
}
|
||||
@ -264,7 +275,7 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
result.addContent(HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.returnLabel,
|
||||
new StringContent(configuration.getText("doclet.Returns")))));
|
||||
result.addContent(HtmlTree.DD(htmlWriter.commentTagsToContent(
|
||||
returnTag, element, ch.getDescription(configuration, returnTag), false)));
|
||||
returnTag, element, ch.getDescription(configuration, returnTag), false, inSummary)));
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -333,7 +344,7 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
body.addContent(", ");
|
||||
}
|
||||
List<? extends DocTree> bodyTags = ch.getBody(configuration, simpleTag);
|
||||
body.addContent(htmlWriter.commentTagsToContent(simpleTag, element, bodyTags, false));
|
||||
body.addContent(htmlWriter.commentTagsToContent(simpleTag, element, bodyTags, false, inSummary));
|
||||
many = true;
|
||||
}
|
||||
result.addContent(HtmlTree.DD(body));
|
||||
@ -348,7 +359,7 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
result.addContent(HtmlTree.DT(HtmlTree.SPAN(HtmlStyle.simpleTagLabel, new RawHtml(header))));
|
||||
CommentHelper ch = utils.getCommentHelper(element);
|
||||
List<? extends DocTree> description = ch.getDescription(configuration, simpleTag);
|
||||
Content body = htmlWriter.commentTagsToContent(simpleTag, element, description, false);
|
||||
Content body = htmlWriter.commentTagsToContent(simpleTag, element, description, false, inSummary);
|
||||
result.addContent(HtmlTree.DD(body));
|
||||
return result;
|
||||
}
|
||||
@ -382,7 +393,7 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
}
|
||||
body.addContent(HtmlTree.CODE(excName));
|
||||
List<? extends DocTree> description = ch.getDescription(configuration, throwsTag);
|
||||
Content desc = htmlWriter.commentTagsToContent(throwsTag, element, description, false);
|
||||
Content desc = htmlWriter.commentTagsToContent(throwsTag, element, description, false, inSummary);
|
||||
if (desc != null && !desc.isEmpty()) {
|
||||
body.addContent(" - ");
|
||||
body.addContent(desc);
|
||||
@ -429,7 +440,7 @@ public class TagletWriterImpl extends TagletWriter {
|
||||
public Content commentTagsToOutput(DocTree holderTag,
|
||||
Element holder, List<? extends DocTree> tags, boolean isFirstSentence) {
|
||||
return htmlWriter.commentTagsToContent(holderTag, holder,
|
||||
tags, isFirstSentence);
|
||||
tags, isFirstSentence, inSummary);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -866,8 +866,7 @@ public class Navigation {
|
||||
contents.packageLabel)));
|
||||
} else {
|
||||
DocLink crossPkgLink = configuration.extern.getExternalLink(
|
||||
configuration.utils.getPackageName(packageElement), pathToRoot,
|
||||
DocPaths.PACKAGE_SUMMARY.getPath());
|
||||
packageElement, pathToRoot, DocPaths.PACKAGE_SUMMARY.getPath());
|
||||
if (crossPkgLink != null) {
|
||||
tree.addContent(HtmlTree.LI(links.createLink(crossPkgLink, contents.packageLabel)));
|
||||
} else {
|
||||
|
@ -295,6 +295,11 @@ public abstract class BaseConfiguration {
|
||||
// A list of pairs containing urls and package list
|
||||
private final List<Pair<String, String>> linkOfflineList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Flag to enable/disable use of module directories when generating docs for modules
|
||||
* Default: on (module directories are enabled).
|
||||
*/
|
||||
public boolean useModuleDirectories = true;
|
||||
|
||||
public boolean dumpOnError = false;
|
||||
|
||||
@ -740,6 +745,13 @@ public abstract class BaseConfiguration {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
new XOption(resources, "--no-module-directories") {
|
||||
@Override
|
||||
public boolean process(String option, List<String> args) {
|
||||
useModuleDirectories = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
Set<Doclet.Option> set = new TreeSet<>();
|
||||
set.addAll(Arrays.asList(options));
|
||||
|
@ -226,16 +226,15 @@ doclet.Enum_Constant=Enum Constant
|
||||
doclet.Description=Description
|
||||
doclet.ConstantField=Constant Field
|
||||
doclet.Value=Value
|
||||
doclet.linkMismatch_PackagedLinkedtoModule=The code being documented uses packages in the unnamed module, \
|
||||
but the packages defined in {0} are in named modules.
|
||||
doclet.linkMismatch_ModuleLinkedtoPackage=The code being documented uses modules but the packages defined \
|
||||
in {0} are in the unnamed module.
|
||||
|
||||
#Documentation for Enums
|
||||
doclet.enum_values_doc.fullbody=\
|
||||
Returns an array containing the constants of this enum type, in\n\
|
||||
the order they are declared. This method may be used to iterate\n\
|
||||
over the constants as follows:\n\
|
||||
<pre>\n\
|
||||
for ({0} c : {0}.values())\n\
|
||||
System.out.println(c);\n\
|
||||
</pre>
|
||||
the order they are declared.
|
||||
|
||||
doclet.enum_values_doc.return=\
|
||||
an array containing the constants of this enum type, in the order they are declared
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user