# HG changeset patch # User zgu # Date 1352152531 28800 # Node ID 8940ddc1036f414b49444a9c37f150fade50237e # Parent 69ad7823b1ca25fe533d26a3ca2999e525f247fc# Parent 9cc901118f6b755e2dd2fb52dc7975dcb5db407b Merge diff -r 69ad7823b1ca -r 8940ddc1036f .hgtags --- a/.hgtags Mon Nov 05 15:30:22 2012 -0500 +++ b/.hgtags Mon Nov 05 13:55:31 2012 -0800 @@ -287,3 +287,7 @@ b261523fe66c40a02968f0aa7e73602491bb3386 hs25-b05 4547dc71db765276e027b0c2780b724bae0a07d3 jdk8-b61 d0337c31c8be7716369b4e7c3bd5f352983c6a06 hs25-b06 +dccd40de8db1fa96f186e6179907818d75320440 jdk8-b62 +dc16fe422c535ecd4e9f80fb814a1bb9704da6f5 hs25-b07 +acabb5c282f59be7e3238920b2ea06b684ab68f7 jdk8-b63 +8cb93eadfb6dcab88d91b8e2cd3e0e07d0ac4048 hs25-b08 diff -r 69ad7823b1ca -r 8940ddc1036f make/Makefile --- a/make/Makefile Mon Nov 05 15:30:22 2012 -0500 +++ b/make/Makefile Mon Nov 05 13:55:31 2012 -0800 @@ -453,14 +453,30 @@ ifeq ($(JVM_VARIANT_ZEROSHARK), true) $(EXPORT_JRE_LIB_ARCH_DIR)/%.$(LIBRARY_SUFFIX): $(SHARK_DIR)/%.$(LIBRARY_SUFFIX) $(install-file) + $(EXPORT_JRE_LIB_ARCH_DIR)/%.debuginfo): $(SHARK_DIR)/%.debuginfo + $(install-file) + $(EXPORT_JRE_LIB_ARCH_DIR)/%.diz: $(SHARK_DIR)/%.diz + $(install-file) $(EXPORT_SERVER_DIR)/%.$(LIBRARY_SUFFIX): $(SHARK_DIR)/%.$(LIBRARY_SUFFIX) $(install-file) + $(EXPORT_SERVER_DIR)/%.debuginfo: $(SHARK_DIR)/%.debuginfo + $(install-file) + $(EXPORT_SERVER_DIR)/%.diz: $(SHARK_DIR)/%.diz + $(install-file) endif ifeq ($(JVM_VARIANT_ZERO), true) $(EXPORT_JRE_LIB_ARCH_DIR)/%.$(LIBRARY_SUFFIX): $(ZERO_DIR)/%.$(LIBRARY_SUFFIX) $(install-file) + $(EXPORT_JRE_LIB_ARCH_DIR)/%.debuginfo: $(ZERO_DIR)/%.debuginfo + $(install-file) + $(EXPORT_JRE_LIB_ARCH_DIR)/%.diz: $(ZERO_DIR)/%.diz + $(install-file) $(EXPORT_SERVER_DIR)/%.$(LIBRARY_SUFFIX): $(ZERO_DIR)/%.$(LIBRARY_SUFFIX) $(install-file) + $(EXPORT_SERVER_DIR)/%.debuginfo: $(ZERO_DIR)/%.debuginfo + $(install-file) + $(EXPORT_SERVER_DIR)/%.diz: $(ZERO_DIR)/%.diz + $(install-file) endif ifeq ($(JVM_VARIANT_MINIMAL1), true) $(EXPORT_JRE_LIB_ARCH_DIR)/%.$(LIBRARY_SUFFIX): $(MINIMAL1_DIR)/%.$(LIBRARY_SUFFIX) diff -r 69ad7823b1ca -r 8940ddc1036f make/excludeSrc.make --- a/make/excludeSrc.make Mon Nov 05 15:30:22 2012 -0500 +++ b/make/excludeSrc.make Mon Nov 05 13:55:31 2012 -0800 @@ -79,10 +79,10 @@ CXXFLAGS += -DSERIALGC CFLAGS += -DSERIALGC Src_Files_EXCLUDE += \ - binaryTreeDictionary.cpp cmsAdaptiveSizePolicy.cpp cmsCollectorPolicy.cpp \ + cmsAdaptiveSizePolicy.cpp cmsCollectorPolicy.cpp \ cmsGCAdaptivePolicyCounters.cpp cmsLockVerifier.cpp cmsPermGen.cpp compactibleFreeListSpace.cpp \ - concurrentMarkSweepGeneration.cpp concurrentMarkSweepThread.cpp freeBlockDictionary.cpp \ - freeChunk.cpp freeList.cpp promotionInfo.cpp vmCMSOperations.cpp collectionSetChooser.cpp \ + concurrentMarkSweepGeneration.cpp concurrentMarkSweepThread.cpp \ + freeChunk.cpp adaptiveFreeList.cpp promotionInfo.cpp vmCMSOperations.cpp collectionSetChooser.cpp \ concurrentG1Refine.cpp concurrentG1RefineThread.cpp concurrentMark.cpp concurrentMarkThread.cpp \ dirtyCardQueue.cpp g1AllocRegion.cpp g1BlockOffsetTable.cpp g1CollectedHeap.cpp g1GCPhaseTimes.cpp \ g1CollectorPolicy.cpp g1ErgoVerbose.cpp g1_globals.cpp g1HRPrinter.cpp g1MarkSweep.cpp \ diff -r 69ad7823b1ca -r 8940ddc1036f make/hotspot_version --- a/make/hotspot_version Mon Nov 05 15:30:22 2012 -0500 +++ b/make/hotspot_version Mon Nov 05 13:55:31 2012 -0800 @@ -35,7 +35,7 @@ HS_MAJOR_VER=25 HS_MINOR_VER=0 -HS_BUILD_NUMBER=07 +HS_BUILD_NUMBER=09 JDK_MAJOR_VER=1 JDK_MINOR_VER=8 diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/x86/vm/assembler_x86.cpp --- a/src/cpu/x86/vm/assembler_x86.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/x86/vm/assembler_x86.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -1007,6 +1007,67 @@ emit_simd_arith(0x58, dst, src, VEX_SIMD_F3); } +void Assembler::aesdec(XMMRegister dst, Address src) { + assert(VM_Version::supports_aes(), ""); + InstructionMark im(this); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38); + emit_byte(0xde); + emit_operand(dst, src); +} + +void Assembler::aesdec(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_aes(), ""); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38); + emit_byte(0xde); + emit_byte(0xC0 | encode); +} + +void Assembler::aesdeclast(XMMRegister dst, Address src) { + assert(VM_Version::supports_aes(), ""); + InstructionMark im(this); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38); + emit_byte(0xdf); + emit_operand(dst, src); +} + +void Assembler::aesdeclast(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_aes(), ""); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38); + emit_byte(0xdf); + emit_byte(0xC0 | encode); +} + +void Assembler::aesenc(XMMRegister dst, Address src) { + assert(VM_Version::supports_aes(), ""); + InstructionMark im(this); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38); + emit_byte(0xdc); + emit_operand(dst, src); +} + +void Assembler::aesenc(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_aes(), ""); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38); + emit_byte(0xdc); + emit_byte(0xC0 | encode); +} + +void Assembler::aesenclast(XMMRegister dst, Address src) { + assert(VM_Version::supports_aes(), ""); + InstructionMark im(this); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38); + emit_byte(0xdd); + emit_operand(dst, src); +} + +void Assembler::aesenclast(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_aes(), ""); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38); + emit_byte(0xdd); + emit_byte(0xC0 | encode); +} + + void Assembler::andl(Address dst, int32_t imm32) { InstructionMark im(this); prefix(dst); @@ -2307,6 +2368,22 @@ a_byte(p); } +void Assembler::pshufb(XMMRegister dst, XMMRegister src) { + assert(VM_Version::supports_ssse3(), ""); + int encode = simd_prefix_and_encode(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38); + emit_byte(0x00); + emit_byte(0xC0 | encode); +} + +void Assembler::pshufb(XMMRegister dst, Address src) { + assert(VM_Version::supports_ssse3(), ""); + assert((UseAVX > 0), "SSE mode requires address alignment 16 bytes"); + InstructionMark im(this); + simd_prefix(dst, dst, src, VEX_SIMD_66, VEX_OPCODE_0F_38); + emit_byte(0x00); + emit_operand(dst, src); +} + void Assembler::pshufd(XMMRegister dst, XMMRegister src, int mode) { assert(isByte(mode), "invalid value"); NOT_LP64(assert(VM_Version::supports_sse2(), "")); @@ -8067,6 +8144,15 @@ LP64_ONLY(movq(dst, src)) NOT_LP64(movl(dst, src)); } +void MacroAssembler::movdqu(XMMRegister dst, AddressLiteral src) { + if (reachable(src)) { + Assembler::movdqu(dst, as_Address(src)); + } else { + lea(rscratch1, src); + Assembler::movdqu(dst, Address(rscratch1, 0)); + } +} + void MacroAssembler::movsd(XMMRegister dst, AddressLiteral src) { if (reachable(src)) { Assembler::movsd(dst, as_Address(src)); @@ -8357,6 +8443,17 @@ } } +void MacroAssembler::pshufb(XMMRegister dst, AddressLiteral src) { + // Used in sign-bit flipping with aligned address. + assert((UseAVX > 0) || (((intptr_t)src.target() & 15) == 0), "SSE mode requires address alignment 16 bytes"); + if (reachable(src)) { + Assembler::pshufb(dst, as_Address(src)); + } else { + lea(rscratch1, src); + Assembler::pshufb(dst, Address(rscratch1, 0)); + } +} + // AVX 3-operands instructions void MacroAssembler::vaddsd(XMMRegister dst, XMMRegister nds, AddressLiteral src) { diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/x86/vm/assembler_x86.hpp --- a/src/cpu/x86/vm/assembler_x86.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/x86/vm/assembler_x86.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -875,6 +875,17 @@ void addss(XMMRegister dst, Address src); void addss(XMMRegister dst, XMMRegister src); + // AES instructions + void aesdec(XMMRegister dst, Address src); + void aesdec(XMMRegister dst, XMMRegister src); + void aesdeclast(XMMRegister dst, Address src); + void aesdeclast(XMMRegister dst, XMMRegister src); + void aesenc(XMMRegister dst, Address src); + void aesenc(XMMRegister dst, XMMRegister src); + void aesenclast(XMMRegister dst, Address src); + void aesenclast(XMMRegister dst, XMMRegister src); + + void andl(Address dst, int32_t imm32); void andl(Register dst, int32_t imm32); void andl(Register dst, Address src); @@ -1424,6 +1435,10 @@ void prefetcht2(Address src); void prefetchw(Address src); + // Shuffle Bytes + void pshufb(XMMRegister dst, XMMRegister src); + void pshufb(XMMRegister dst, Address src); + // Shuffle Packed Doublewords void pshufd(XMMRegister dst, XMMRegister src, int mode); void pshufd(XMMRegister dst, Address src, int mode); @@ -2611,6 +2626,12 @@ void divss(XMMRegister dst, Address src) { Assembler::divss(dst, src); } void divss(XMMRegister dst, AddressLiteral src); + // Move Unaligned Double Quadword + void movdqu(Address dst, XMMRegister src) { Assembler::movdqu(dst, src); } + void movdqu(XMMRegister dst, Address src) { Assembler::movdqu(dst, src); } + void movdqu(XMMRegister dst, XMMRegister src) { Assembler::movdqu(dst, src); } + void movdqu(XMMRegister dst, AddressLiteral src); + void movsd(XMMRegister dst, XMMRegister src) { Assembler::movsd(dst, src); } void movsd(Address dst, XMMRegister src) { Assembler::movsd(dst, src); } void movsd(XMMRegister dst, Address src) { Assembler::movsd(dst, src); } @@ -2658,6 +2679,10 @@ void xorps(XMMRegister dst, Address src) { Assembler::xorps(dst, src); } void xorps(XMMRegister dst, AddressLiteral src); + // Shuffle Bytes + void pshufb(XMMRegister dst, XMMRegister src) { Assembler::pshufb(dst, src); } + void pshufb(XMMRegister dst, Address src) { Assembler::pshufb(dst, src); } + void pshufb(XMMRegister dst, AddressLiteral src); // AVX 3-operands instructions void vaddsd(XMMRegister dst, XMMRegister nds, XMMRegister src) { Assembler::vaddsd(dst, nds, src); } diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/x86/vm/stubGenerator_x86_32.cpp --- a/src/cpu/x86/vm/stubGenerator_x86_32.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/x86/vm/stubGenerator_x86_32.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -2137,6 +2137,529 @@ } } + // AES intrinsic stubs + enum {AESBlockSize = 16}; + + address generate_key_shuffle_mask() { + __ align(16); + StubCodeMark mark(this, "StubRoutines", "key_shuffle_mask"); + address start = __ pc(); + __ emit_data(0x00010203, relocInfo::none, 0 ); + __ emit_data(0x04050607, relocInfo::none, 0 ); + __ emit_data(0x08090a0b, relocInfo::none, 0 ); + __ emit_data(0x0c0d0e0f, relocInfo::none, 0 ); + return start; + } + + // Utility routine for loading a 128-bit key word in little endian format + // can optionally specify that the shuffle mask is already in an xmmregister + void load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask=NULL) { + __ movdqu(xmmdst, Address(key, offset)); + if (xmm_shuf_mask != NULL) { + __ pshufb(xmmdst, xmm_shuf_mask); + } else { + __ pshufb(xmmdst, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); + } + } + + // aesenc using specified key+offset + // can optionally specify that the shuffle mask is already in an xmmregister + void aes_enc_key(XMMRegister xmmdst, XMMRegister xmmtmp, Register key, int offset, XMMRegister xmm_shuf_mask=NULL) { + load_key(xmmtmp, key, offset, xmm_shuf_mask); + __ aesenc(xmmdst, xmmtmp); + } + + // aesdec using specified key+offset + // can optionally specify that the shuffle mask is already in an xmmregister + void aes_dec_key(XMMRegister xmmdst, XMMRegister xmmtmp, Register key, int offset, XMMRegister xmm_shuf_mask=NULL) { + load_key(xmmtmp, key, offset, xmm_shuf_mask); + __ aesdec(xmmdst, xmmtmp); + } + + + // Arguments: + // + // Inputs: + // c_rarg0 - source byte array address + // c_rarg1 - destination byte array address + // c_rarg2 - K (key) in little endian int array + // + address generate_aescrypt_encryptBlock() { + assert(UseAES && (UseAVX > 0), "need AES instructions and misaligned SSE support"); + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "aescrypt_encryptBlock"); + Label L_doLast; + address start = __ pc(); + + const Register from = rsi; // source array address + const Register to = rdx; // destination array address + const Register key = rcx; // key array address + const Register keylen = rax; + const Address from_param(rbp, 8+0); + const Address to_param (rbp, 8+4); + const Address key_param (rbp, 8+8); + + const XMMRegister xmm_result = xmm0; + const XMMRegister xmm_temp = xmm1; + const XMMRegister xmm_key_shuf_mask = xmm2; + + __ enter(); // required for proper stackwalking of RuntimeStub frame + __ push(rsi); + __ movptr(from , from_param); + __ movptr(to , to_param); + __ movptr(key , key_param); + + __ movl(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); + // keylen = # of 32-bit words, convert to 128-bit words + __ shrl(keylen, 2); + __ subl(keylen, 11); // every key has at least 11 128-bit words, some have more + + __ movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); + __ movdqu(xmm_result, Address(from, 0)); // get 16 bytes of input + + // For encryption, the java expanded key ordering is just what we need + + load_key(xmm_temp, key, 0x00, xmm_key_shuf_mask); + __ pxor(xmm_result, xmm_temp); + for (int offset = 0x10; offset <= 0x90; offset += 0x10) { + aes_enc_key(xmm_result, xmm_temp, key, offset, xmm_key_shuf_mask); + } + load_key (xmm_temp, key, 0xa0, xmm_key_shuf_mask); + __ cmpl(keylen, 0); + __ jcc(Assembler::equal, L_doLast); + __ aesenc(xmm_result, xmm_temp); // only in 192 and 256 bit keys + aes_enc_key(xmm_result, xmm_temp, key, 0xb0, xmm_key_shuf_mask); + load_key(xmm_temp, key, 0xc0, xmm_key_shuf_mask); + __ subl(keylen, 2); + __ jcc(Assembler::equal, L_doLast); + __ aesenc(xmm_result, xmm_temp); // only in 256 bit keys + aes_enc_key(xmm_result, xmm_temp, key, 0xd0, xmm_key_shuf_mask); + load_key(xmm_temp, key, 0xe0, xmm_key_shuf_mask); + + __ BIND(L_doLast); + __ aesenclast(xmm_result, xmm_temp); + __ movdqu(Address(to, 0), xmm_result); // store the result + __ xorptr(rax, rax); // return 0 + __ pop(rsi); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + } + + + // Arguments: + // + // Inputs: + // c_rarg0 - source byte array address + // c_rarg1 - destination byte array address + // c_rarg2 - K (key) in little endian int array + // + address generate_aescrypt_decryptBlock() { + assert(UseAES && (UseAVX > 0), "need AES instructions and misaligned SSE support"); + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "aescrypt_decryptBlock"); + Label L_doLast; + address start = __ pc(); + + const Register from = rsi; // source array address + const Register to = rdx; // destination array address + const Register key = rcx; // key array address + const Register keylen = rax; + const Address from_param(rbp, 8+0); + const Address to_param (rbp, 8+4); + const Address key_param (rbp, 8+8); + + const XMMRegister xmm_result = xmm0; + const XMMRegister xmm_temp = xmm1; + const XMMRegister xmm_key_shuf_mask = xmm2; + + __ enter(); // required for proper stackwalking of RuntimeStub frame + __ push(rsi); + __ movptr(from , from_param); + __ movptr(to , to_param); + __ movptr(key , key_param); + + __ movl(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); + // keylen = # of 32-bit words, convert to 128-bit words + __ shrl(keylen, 2); + __ subl(keylen, 11); // every key has at least 11 128-bit words, some have more + + __ movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); + __ movdqu(xmm_result, Address(from, 0)); + + // for decryption java expanded key ordering is rotated one position from what we want + // so we start from 0x10 here and hit 0x00 last + // we don't know if the key is aligned, hence not using load-execute form + load_key(xmm_temp, key, 0x10, xmm_key_shuf_mask); + __ pxor (xmm_result, xmm_temp); + for (int offset = 0x20; offset <= 0xa0; offset += 0x10) { + aes_dec_key(xmm_result, xmm_temp, key, offset, xmm_key_shuf_mask); + } + __ cmpl(keylen, 0); + __ jcc(Assembler::equal, L_doLast); + // only in 192 and 256 bit keys + aes_dec_key(xmm_result, xmm_temp, key, 0xb0, xmm_key_shuf_mask); + aes_dec_key(xmm_result, xmm_temp, key, 0xc0, xmm_key_shuf_mask); + __ subl(keylen, 2); + __ jcc(Assembler::equal, L_doLast); + // only in 256 bit keys + aes_dec_key(xmm_result, xmm_temp, key, 0xd0, xmm_key_shuf_mask); + aes_dec_key(xmm_result, xmm_temp, key, 0xe0, xmm_key_shuf_mask); + + __ BIND(L_doLast); + // for decryption the aesdeclast operation is always on key+0x00 + load_key(xmm_temp, key, 0x00, xmm_key_shuf_mask); + __ aesdeclast(xmm_result, xmm_temp); + + __ movdqu(Address(to, 0), xmm_result); // store the result + + __ xorptr(rax, rax); // return 0 + __ pop(rsi); + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + } + + void handleSOERegisters(bool saving) { + const int saveFrameSizeInBytes = 4 * wordSize; + const Address saved_rbx (rbp, -3 * wordSize); + const Address saved_rsi (rbp, -2 * wordSize); + const Address saved_rdi (rbp, -1 * wordSize); + + if (saving) { + __ subptr(rsp, saveFrameSizeInBytes); + __ movptr(saved_rsi, rsi); + __ movptr(saved_rdi, rdi); + __ movptr(saved_rbx, rbx); + } else { + // restoring + __ movptr(rsi, saved_rsi); + __ movptr(rdi, saved_rdi); + __ movptr(rbx, saved_rbx); + } + } + + // Arguments: + // + // Inputs: + // c_rarg0 - source byte array address + // c_rarg1 - destination byte array address + // c_rarg2 - K (key) in little endian int array + // c_rarg3 - r vector byte array address + // c_rarg4 - input length + // + address generate_cipherBlockChaining_encryptAESCrypt() { + assert(UseAES && (UseAVX > 0), "need AES instructions and misaligned SSE support"); + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "cipherBlockChaining_encryptAESCrypt"); + address start = __ pc(); + + Label L_exit, L_key_192_256, L_key_256, L_loopTop_128, L_loopTop_192, L_loopTop_256; + const Register from = rsi; // source array address + const Register to = rdx; // destination array address + const Register key = rcx; // key array address + const Register rvec = rdi; // r byte array initialized from initvector array address + // and left with the results of the last encryption block + const Register len_reg = rbx; // src len (must be multiple of blocksize 16) + const Register pos = rax; + + // xmm register assignments for the loops below + const XMMRegister xmm_result = xmm0; + const XMMRegister xmm_temp = xmm1; + // first 6 keys preloaded into xmm2-xmm7 + const int XMM_REG_NUM_KEY_FIRST = 2; + const int XMM_REG_NUM_KEY_LAST = 7; + const XMMRegister xmm_key0 = as_XMMRegister(XMM_REG_NUM_KEY_FIRST); + + __ enter(); // required for proper stackwalking of RuntimeStub frame + handleSOERegisters(true /*saving*/); + + // load registers from incoming parameters + const Address from_param(rbp, 8+0); + const Address to_param (rbp, 8+4); + const Address key_param (rbp, 8+8); + const Address rvec_param (rbp, 8+12); + const Address len_param (rbp, 8+16); + __ movptr(from , from_param); + __ movptr(to , to_param); + __ movptr(key , key_param); + __ movptr(rvec , rvec_param); + __ movptr(len_reg , len_param); + + const XMMRegister xmm_key_shuf_mask = xmm_temp; // used temporarily to swap key bytes up front + __ movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); + // load up xmm regs 2 thru 7 with keys 0-5 + for (int rnum = XMM_REG_NUM_KEY_FIRST, offset = 0x00; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) { + load_key(as_XMMRegister(rnum), key, offset, xmm_key_shuf_mask); + offset += 0x10; + } + + __ movdqu(xmm_result, Address(rvec, 0x00)); // initialize xmm_result with r vec + + // now split to different paths depending on the keylen (len in ints of AESCrypt.KLE array (52=192, or 60=256)) + __ movl(rax, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); + __ cmpl(rax, 44); + __ jcc(Assembler::notEqual, L_key_192_256); + + // 128 bit code follows here + __ movptr(pos, 0); + __ align(OptoLoopAlignment); + __ BIND(L_loopTop_128); + __ movdqu(xmm_temp, Address(from, pos, Address::times_1, 0)); // get next 16 bytes of input + __ pxor (xmm_result, xmm_temp); // xor with the current r vector + + __ pxor (xmm_result, xmm_key0); // do the aes rounds + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) { + __ aesenc(xmm_result, as_XMMRegister(rnum)); + } + for (int key_offset = 0x60; key_offset <= 0x90; key_offset += 0x10) { + aes_enc_key(xmm_result, xmm_temp, key, key_offset); + } + load_key(xmm_temp, key, 0xa0); + __ aesenclast(xmm_result, xmm_temp); + + __ movdqu(Address(to, pos, Address::times_1, 0), xmm_result); // store into the next 16 bytes of output + // no need to store r to memory until we exit + __ addptr(pos, AESBlockSize); + __ subptr(len_reg, AESBlockSize); + __ jcc(Assembler::notEqual, L_loopTop_128); + + __ BIND(L_exit); + __ movdqu(Address(rvec, 0), xmm_result); // final value of r stored in rvec of CipherBlockChaining object + + handleSOERegisters(false /*restoring*/); + __ movl(rax, 0); // return 0 (why?) + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + __ BIND(L_key_192_256); + // here rax = len in ints of AESCrypt.KLE array (52=192, or 60=256) + __ cmpl(rax, 52); + __ jcc(Assembler::notEqual, L_key_256); + + // 192-bit code follows here (could be changed to use more xmm registers) + __ movptr(pos, 0); + __ align(OptoLoopAlignment); + __ BIND(L_loopTop_192); + __ movdqu(xmm_temp, Address(from, pos, Address::times_1, 0)); // get next 16 bytes of input + __ pxor (xmm_result, xmm_temp); // xor with the current r vector + + __ pxor (xmm_result, xmm_key0); // do the aes rounds + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) { + __ aesenc(xmm_result, as_XMMRegister(rnum)); + } + for (int key_offset = 0x60; key_offset <= 0xb0; key_offset += 0x10) { + aes_enc_key(xmm_result, xmm_temp, key, key_offset); + } + load_key(xmm_temp, key, 0xc0); + __ aesenclast(xmm_result, xmm_temp); + + __ movdqu(Address(to, pos, Address::times_1, 0), xmm_result); // store into the next 16 bytes of output + // no need to store r to memory until we exit + __ addptr(pos, AESBlockSize); + __ subptr(len_reg, AESBlockSize); + __ jcc(Assembler::notEqual, L_loopTop_192); + __ jmp(L_exit); + + __ BIND(L_key_256); + // 256-bit code follows here (could be changed to use more xmm registers) + __ movptr(pos, 0); + __ align(OptoLoopAlignment); + __ BIND(L_loopTop_256); + __ movdqu(xmm_temp, Address(from, pos, Address::times_1, 0)); // get next 16 bytes of input + __ pxor (xmm_result, xmm_temp); // xor with the current r vector + + __ pxor (xmm_result, xmm_key0); // do the aes rounds + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) { + __ aesenc(xmm_result, as_XMMRegister(rnum)); + } + for (int key_offset = 0x60; key_offset <= 0xd0; key_offset += 0x10) { + aes_enc_key(xmm_result, xmm_temp, key, key_offset); + } + load_key(xmm_temp, key, 0xe0); + __ aesenclast(xmm_result, xmm_temp); + + __ movdqu(Address(to, pos, Address::times_1, 0), xmm_result); // store into the next 16 bytes of output + // no need to store r to memory until we exit + __ addptr(pos, AESBlockSize); + __ subptr(len_reg, AESBlockSize); + __ jcc(Assembler::notEqual, L_loopTop_256); + __ jmp(L_exit); + + return start; + } + + + // CBC AES Decryption. + // In 32-bit stub, because of lack of registers we do not try to parallelize 4 blocks at a time. + // + // Arguments: + // + // Inputs: + // c_rarg0 - source byte array address + // c_rarg1 - destination byte array address + // c_rarg2 - K (key) in little endian int array + // c_rarg3 - r vector byte array address + // c_rarg4 - input length + // + + address generate_cipherBlockChaining_decryptAESCrypt() { + assert(UseAES && (UseAVX > 0), "need AES instructions and misaligned SSE support"); + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "cipherBlockChaining_decryptAESCrypt"); + address start = __ pc(); + + Label L_exit, L_key_192_256, L_key_256; + Label L_singleBlock_loopTop_128; + Label L_singleBlock_loopTop_192, L_singleBlock_loopTop_256; + const Register from = rsi; // source array address + const Register to = rdx; // destination array address + const Register key = rcx; // key array address + const Register rvec = rdi; // r byte array initialized from initvector array address + // and left with the results of the last encryption block + const Register len_reg = rbx; // src len (must be multiple of blocksize 16) + const Register pos = rax; + + // xmm register assignments for the loops below + const XMMRegister xmm_result = xmm0; + const XMMRegister xmm_temp = xmm1; + // first 6 keys preloaded into xmm2-xmm7 + const int XMM_REG_NUM_KEY_FIRST = 2; + const int XMM_REG_NUM_KEY_LAST = 7; + const int FIRST_NON_REG_KEY_offset = 0x70; + const XMMRegister xmm_key_first = as_XMMRegister(XMM_REG_NUM_KEY_FIRST); + + __ enter(); // required for proper stackwalking of RuntimeStub frame + handleSOERegisters(true /*saving*/); + + // load registers from incoming parameters + const Address from_param(rbp, 8+0); + const Address to_param (rbp, 8+4); + const Address key_param (rbp, 8+8); + const Address rvec_param (rbp, 8+12); + const Address len_param (rbp, 8+16); + __ movptr(from , from_param); + __ movptr(to , to_param); + __ movptr(key , key_param); + __ movptr(rvec , rvec_param); + __ movptr(len_reg , len_param); + + // the java expanded key ordering is rotated one position from what we want + // so we start from 0x10 here and hit 0x00 last + const XMMRegister xmm_key_shuf_mask = xmm1; // used temporarily to swap key bytes up front + __ movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); + // load up xmm regs 2 thru 6 with first 5 keys + for (int rnum = XMM_REG_NUM_KEY_FIRST, offset = 0x10; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) { + load_key(as_XMMRegister(rnum), key, offset, xmm_key_shuf_mask); + offset += 0x10; + } + + // inside here, use the rvec register to point to previous block cipher + // with which we xor at the end of each newly decrypted block + const Register prev_block_cipher_ptr = rvec; + + // now split to different paths depending on the keylen (len in ints of AESCrypt.KLE array (52=192, or 60=256)) + __ movl(rax, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); + __ cmpl(rax, 44); + __ jcc(Assembler::notEqual, L_key_192_256); + + + // 128-bit code follows here, parallelized + __ movptr(pos, 0); + __ align(OptoLoopAlignment); + __ BIND(L_singleBlock_loopTop_128); + __ cmpptr(len_reg, 0); // any blocks left?? + __ jcc(Assembler::equal, L_exit); + __ movdqu(xmm_result, Address(from, pos, Address::times_1, 0)); // get next 16 bytes of cipher input + __ pxor (xmm_result, xmm_key_first); // do the aes dec rounds + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) { + __ aesdec(xmm_result, as_XMMRegister(rnum)); + } + for (int key_offset = FIRST_NON_REG_KEY_offset; key_offset <= 0xa0; key_offset += 0x10) { // 128-bit runs up to key offset a0 + aes_dec_key(xmm_result, xmm_temp, key, key_offset); + } + load_key(xmm_temp, key, 0x00); // final key is stored in java expanded array at offset 0 + __ aesdeclast(xmm_result, xmm_temp); + __ movdqu(xmm_temp, Address(prev_block_cipher_ptr, 0x00)); + __ pxor (xmm_result, xmm_temp); // xor with the current r vector + __ movdqu(Address(to, pos, Address::times_1, 0), xmm_result); // store into the next 16 bytes of output + // no need to store r to memory until we exit + __ lea(prev_block_cipher_ptr, Address(from, pos, Address::times_1, 0)); // set up new ptr + __ addptr(pos, AESBlockSize); + __ subptr(len_reg, AESBlockSize); + __ jmp(L_singleBlock_loopTop_128); + + + __ BIND(L_exit); + __ movdqu(xmm_temp, Address(prev_block_cipher_ptr, 0x00)); + __ movptr(rvec , rvec_param); // restore this since used in loop + __ movdqu(Address(rvec, 0), xmm_temp); // final value of r stored in rvec of CipherBlockChaining object + handleSOERegisters(false /*restoring*/); + __ movl(rax, 0); // return 0 (why?) + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + + __ BIND(L_key_192_256); + // here rax = len in ints of AESCrypt.KLE array (52=192, or 60=256) + __ cmpl(rax, 52); + __ jcc(Assembler::notEqual, L_key_256); + + // 192-bit code follows here (could be optimized to use parallelism) + __ movptr(pos, 0); + __ align(OptoLoopAlignment); + __ BIND(L_singleBlock_loopTop_192); + __ movdqu(xmm_result, Address(from, pos, Address::times_1, 0)); // get next 16 bytes of cipher input + __ pxor (xmm_result, xmm_key_first); // do the aes dec rounds + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) { + __ aesdec(xmm_result, as_XMMRegister(rnum)); + } + for (int key_offset = FIRST_NON_REG_KEY_offset; key_offset <= 0xc0; key_offset += 0x10) { // 192-bit runs up to key offset c0 + aes_dec_key(xmm_result, xmm_temp, key, key_offset); + } + load_key(xmm_temp, key, 0x00); // final key is stored in java expanded array at offset 0 + __ aesdeclast(xmm_result, xmm_temp); + __ movdqu(xmm_temp, Address(prev_block_cipher_ptr, 0x00)); + __ pxor (xmm_result, xmm_temp); // xor with the current r vector + __ movdqu(Address(to, pos, Address::times_1, 0), xmm_result); // store into the next 16 bytes of output + // no need to store r to memory until we exit + __ lea(prev_block_cipher_ptr, Address(from, pos, Address::times_1, 0)); // set up new ptr + __ addptr(pos, AESBlockSize); + __ subptr(len_reg, AESBlockSize); + __ jcc(Assembler::notEqual,L_singleBlock_loopTop_192); + __ jmp(L_exit); + + __ BIND(L_key_256); + // 256-bit code follows here (could be optimized to use parallelism) + __ movptr(pos, 0); + __ align(OptoLoopAlignment); + __ BIND(L_singleBlock_loopTop_256); + __ movdqu(xmm_result, Address(from, pos, Address::times_1, 0)); // get next 16 bytes of cipher input + __ pxor (xmm_result, xmm_key_first); // do the aes dec rounds + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) { + __ aesdec(xmm_result, as_XMMRegister(rnum)); + } + for (int key_offset = FIRST_NON_REG_KEY_offset; key_offset <= 0xe0; key_offset += 0x10) { // 256-bit runs up to key offset e0 + aes_dec_key(xmm_result, xmm_temp, key, key_offset); + } + load_key(xmm_temp, key, 0x00); // final key is stored in java expanded array at offset 0 + __ aesdeclast(xmm_result, xmm_temp); + __ movdqu(xmm_temp, Address(prev_block_cipher_ptr, 0x00)); + __ pxor (xmm_result, xmm_temp); // xor with the current r vector + __ movdqu(Address(to, pos, Address::times_1, 0), xmm_result); // store into the next 16 bytes of output + // no need to store r to memory until we exit + __ lea(prev_block_cipher_ptr, Address(from, pos, Address::times_1, 0)); // set up new ptr + __ addptr(pos, AESBlockSize); + __ subptr(len_reg, AESBlockSize); + __ jcc(Assembler::notEqual,L_singleBlock_loopTop_256); + __ jmp(L_exit); + + return start; + } + + public: // Information about frame layout at time of blocking runtime call. // Note that we only have to preserve callee-saved registers since @@ -2332,6 +2855,16 @@ generate_arraycopy_stubs(); generate_math_stubs(); + + // don't bother generating these AES intrinsic stubs unless global flag is set + if (UseAESIntrinsics) { + StubRoutines::x86::_key_shuffle_mask_addr = generate_key_shuffle_mask(); // might be needed by the others + + StubRoutines::_aescrypt_encryptBlock = generate_aescrypt_encryptBlock(); + StubRoutines::_aescrypt_decryptBlock = generate_aescrypt_decryptBlock(); + StubRoutines::_cipherBlockChaining_encryptAESCrypt = generate_cipherBlockChaining_encryptAESCrypt(); + StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_cipherBlockChaining_decryptAESCrypt(); + } } diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/x86/vm/stubGenerator_x86_64.cpp --- a/src/cpu/x86/vm/stubGenerator_x86_64.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/x86/vm/stubGenerator_x86_64.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -2941,6 +2941,548 @@ } } + // AES intrinsic stubs + enum {AESBlockSize = 16}; + + address generate_key_shuffle_mask() { + __ align(16); + StubCodeMark mark(this, "StubRoutines", "key_shuffle_mask"); + address start = __ pc(); + __ emit_data64( 0x0405060700010203, relocInfo::none ); + __ emit_data64( 0x0c0d0e0f08090a0b, relocInfo::none ); + return start; + } + + // Utility routine for loading a 128-bit key word in little endian format + // can optionally specify that the shuffle mask is already in an xmmregister + void load_key(XMMRegister xmmdst, Register key, int offset, XMMRegister xmm_shuf_mask=NULL) { + __ movdqu(xmmdst, Address(key, offset)); + if (xmm_shuf_mask != NULL) { + __ pshufb(xmmdst, xmm_shuf_mask); + } else { + __ pshufb(xmmdst, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); + } + } + + // aesenc using specified key+offset + // can optionally specify that the shuffle mask is already in an xmmregister + void aes_enc_key(XMMRegister xmmdst, XMMRegister xmmtmp, Register key, int offset, XMMRegister xmm_shuf_mask=NULL) { + load_key(xmmtmp, key, offset, xmm_shuf_mask); + __ aesenc(xmmdst, xmmtmp); + } + + // aesdec using specified key+offset + // can optionally specify that the shuffle mask is already in an xmmregister + void aes_dec_key(XMMRegister xmmdst, XMMRegister xmmtmp, Register key, int offset, XMMRegister xmm_shuf_mask=NULL) { + load_key(xmmtmp, key, offset, xmm_shuf_mask); + __ aesdec(xmmdst, xmmtmp); + } + + + // Arguments: + // + // Inputs: + // c_rarg0 - source byte array address + // c_rarg1 - destination byte array address + // c_rarg2 - K (key) in little endian int array + // + address generate_aescrypt_encryptBlock() { + assert(UseAES && (UseAVX > 0), "need AES instructions and misaligned SSE support"); + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "aescrypt_encryptBlock"); + Label L_doLast; + address start = __ pc(); + + const Register from = c_rarg0; // source array address + const Register to = c_rarg1; // destination array address + const Register key = c_rarg2; // key array address + const Register keylen = rax; + + const XMMRegister xmm_result = xmm0; + const XMMRegister xmm_temp = xmm1; + const XMMRegister xmm_key_shuf_mask = xmm2; + + __ enter(); // required for proper stackwalking of RuntimeStub frame + + __ movl(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); + // keylen = # of 32-bit words, convert to 128-bit words + __ shrl(keylen, 2); + __ subl(keylen, 11); // every key has at least 11 128-bit words, some have more + + __ movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); + __ movdqu(xmm_result, Address(from, 0)); // get 16 bytes of input + + // For encryption, the java expanded key ordering is just what we need + // we don't know if the key is aligned, hence not using load-execute form + + load_key(xmm_temp, key, 0x00, xmm_key_shuf_mask); + __ pxor(xmm_result, xmm_temp); + for (int offset = 0x10; offset <= 0x90; offset += 0x10) { + aes_enc_key(xmm_result, xmm_temp, key, offset, xmm_key_shuf_mask); + } + load_key (xmm_temp, key, 0xa0, xmm_key_shuf_mask); + __ cmpl(keylen, 0); + __ jcc(Assembler::equal, L_doLast); + __ aesenc(xmm_result, xmm_temp); // only in 192 and 256 bit keys + aes_enc_key(xmm_result, xmm_temp, key, 0xb0, xmm_key_shuf_mask); + load_key(xmm_temp, key, 0xc0, xmm_key_shuf_mask); + __ subl(keylen, 2); + __ jcc(Assembler::equal, L_doLast); + __ aesenc(xmm_result, xmm_temp); // only in 256 bit keys + aes_enc_key(xmm_result, xmm_temp, key, 0xd0, xmm_key_shuf_mask); + load_key(xmm_temp, key, 0xe0, xmm_key_shuf_mask); + + __ BIND(L_doLast); + __ aesenclast(xmm_result, xmm_temp); + __ movdqu(Address(to, 0), xmm_result); // store the result + __ xorptr(rax, rax); // return 0 + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + } + + + // Arguments: + // + // Inputs: + // c_rarg0 - source byte array address + // c_rarg1 - destination byte array address + // c_rarg2 - K (key) in little endian int array + // + address generate_aescrypt_decryptBlock() { + assert(UseAES && (UseAVX > 0), "need AES instructions and misaligned SSE support"); + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "aescrypt_decryptBlock"); + Label L_doLast; + address start = __ pc(); + + const Register from = c_rarg0; // source array address + const Register to = c_rarg1; // destination array address + const Register key = c_rarg2; // key array address + const Register keylen = rax; + + const XMMRegister xmm_result = xmm0; + const XMMRegister xmm_temp = xmm1; + const XMMRegister xmm_key_shuf_mask = xmm2; + + __ enter(); // required for proper stackwalking of RuntimeStub frame + + __ movl(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); + // keylen = # of 32-bit words, convert to 128-bit words + __ shrl(keylen, 2); + __ subl(keylen, 11); // every key has at least 11 128-bit words, some have more + + __ movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); + __ movdqu(xmm_result, Address(from, 0)); + + // for decryption java expanded key ordering is rotated one position from what we want + // so we start from 0x10 here and hit 0x00 last + // we don't know if the key is aligned, hence not using load-execute form + load_key(xmm_temp, key, 0x10, xmm_key_shuf_mask); + __ pxor (xmm_result, xmm_temp); + for (int offset = 0x20; offset <= 0xa0; offset += 0x10) { + aes_dec_key(xmm_result, xmm_temp, key, offset, xmm_key_shuf_mask); + } + __ cmpl(keylen, 0); + __ jcc(Assembler::equal, L_doLast); + // only in 192 and 256 bit keys + aes_dec_key(xmm_result, xmm_temp, key, 0xb0, xmm_key_shuf_mask); + aes_dec_key(xmm_result, xmm_temp, key, 0xc0, xmm_key_shuf_mask); + __ subl(keylen, 2); + __ jcc(Assembler::equal, L_doLast); + // only in 256 bit keys + aes_dec_key(xmm_result, xmm_temp, key, 0xd0, xmm_key_shuf_mask); + aes_dec_key(xmm_result, xmm_temp, key, 0xe0, xmm_key_shuf_mask); + + __ BIND(L_doLast); + // for decryption the aesdeclast operation is always on key+0x00 + load_key(xmm_temp, key, 0x00, xmm_key_shuf_mask); + __ aesdeclast(xmm_result, xmm_temp); + + __ movdqu(Address(to, 0), xmm_result); // store the result + + __ xorptr(rax, rax); // return 0 + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + return start; + } + + + // Arguments: + // + // Inputs: + // c_rarg0 - source byte array address + // c_rarg1 - destination byte array address + // c_rarg2 - K (key) in little endian int array + // c_rarg3 - r vector byte array address + // c_rarg4 - input length + // + address generate_cipherBlockChaining_encryptAESCrypt() { + assert(UseAES && (UseAVX > 0), "need AES instructions and misaligned SSE support"); + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "cipherBlockChaining_encryptAESCrypt"); + address start = __ pc(); + + Label L_exit, L_key_192_256, L_key_256, L_loopTop_128, L_loopTop_192, L_loopTop_256; + const Register from = c_rarg0; // source array address + const Register to = c_rarg1; // destination array address + const Register key = c_rarg2; // key array address + const Register rvec = c_rarg3; // r byte array initialized from initvector array address + // and left with the results of the last encryption block +#ifndef _WIN64 + const Register len_reg = c_rarg4; // src len (must be multiple of blocksize 16) +#else + const Address len_mem(rsp, 6 * wordSize); // length is on stack on Win64 + const Register len_reg = r10; // pick the first volatile windows register +#endif + const Register pos = rax; + + // xmm register assignments for the loops below + const XMMRegister xmm_result = xmm0; + const XMMRegister xmm_temp = xmm1; + // keys 0-10 preloaded into xmm2-xmm12 + const int XMM_REG_NUM_KEY_FIRST = 2; + const int XMM_REG_NUM_KEY_LAST = 12; + const XMMRegister xmm_key0 = as_XMMRegister(XMM_REG_NUM_KEY_FIRST); + const XMMRegister xmm_key10 = as_XMMRegister(XMM_REG_NUM_KEY_LAST); + + __ enter(); // required for proper stackwalking of RuntimeStub frame + +#ifdef _WIN64 + // on win64, fill len_reg from stack position + __ movl(len_reg, len_mem); + // save the xmm registers which must be preserved 6-12 + __ subptr(rsp, -rsp_after_call_off * wordSize); + for (int i = 6; i <= XMM_REG_NUM_KEY_LAST; i++) { + __ movdqu(xmm_save(i), as_XMMRegister(i)); + } +#endif + + const XMMRegister xmm_key_shuf_mask = xmm_temp; // used temporarily to swap key bytes up front + __ movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); + // load up xmm regs 2 thru 12 with key 0x00 - 0xa0 + for (int rnum = XMM_REG_NUM_KEY_FIRST, offset = 0x00; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) { + load_key(as_XMMRegister(rnum), key, offset, xmm_key_shuf_mask); + offset += 0x10; + } + + __ movdqu(xmm_result, Address(rvec, 0x00)); // initialize xmm_result with r vec + + // now split to different paths depending on the keylen (len in ints of AESCrypt.KLE array (52=192, or 60=256)) + __ movl(rax, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); + __ cmpl(rax, 44); + __ jcc(Assembler::notEqual, L_key_192_256); + + // 128 bit code follows here + __ movptr(pos, 0); + __ align(OptoLoopAlignment); + __ BIND(L_loopTop_128); + __ movdqu(xmm_temp, Address(from, pos, Address::times_1, 0)); // get next 16 bytes of input + __ pxor (xmm_result, xmm_temp); // xor with the current r vector + + __ pxor (xmm_result, xmm_key0); // do the aes rounds + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST - 1; rnum++) { + __ aesenc(xmm_result, as_XMMRegister(rnum)); + } + __ aesenclast(xmm_result, xmm_key10); + + __ movdqu(Address(to, pos, Address::times_1, 0), xmm_result); // store into the next 16 bytes of output + // no need to store r to memory until we exit + __ addptr(pos, AESBlockSize); + __ subptr(len_reg, AESBlockSize); + __ jcc(Assembler::notEqual, L_loopTop_128); + + __ BIND(L_exit); + __ movdqu(Address(rvec, 0), xmm_result); // final value of r stored in rvec of CipherBlockChaining object + +#ifdef _WIN64 + // restore xmm regs belonging to calling function + for (int i = 6; i <= XMM_REG_NUM_KEY_LAST; i++) { + __ movdqu(as_XMMRegister(i), xmm_save(i)); + } +#endif + __ movl(rax, 0); // return 0 (why?) + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + __ BIND(L_key_192_256); + // here rax = len in ints of AESCrypt.KLE array (52=192, or 60=256) + __ cmpl(rax, 52); + __ jcc(Assembler::notEqual, L_key_256); + + // 192-bit code follows here (could be changed to use more xmm registers) + __ movptr(pos, 0); + __ align(OptoLoopAlignment); + __ BIND(L_loopTop_192); + __ movdqu(xmm_temp, Address(from, pos, Address::times_1, 0)); // get next 16 bytes of input + __ pxor (xmm_result, xmm_temp); // xor with the current r vector + + __ pxor (xmm_result, xmm_key0); // do the aes rounds + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) { + __ aesenc(xmm_result, as_XMMRegister(rnum)); + } + aes_enc_key(xmm_result, xmm_temp, key, 0xb0); + load_key(xmm_temp, key, 0xc0); + __ aesenclast(xmm_result, xmm_temp); + + __ movdqu(Address(to, pos, Address::times_1, 0), xmm_result); // store into the next 16 bytes of output + // no need to store r to memory until we exit + __ addptr(pos, AESBlockSize); + __ subptr(len_reg, AESBlockSize); + __ jcc(Assembler::notEqual, L_loopTop_192); + __ jmp(L_exit); + + __ BIND(L_key_256); + // 256-bit code follows here (could be changed to use more xmm registers) + __ movptr(pos, 0); + __ align(OptoLoopAlignment); + __ BIND(L_loopTop_256); + __ movdqu(xmm_temp, Address(from, pos, Address::times_1, 0)); // get next 16 bytes of input + __ pxor (xmm_result, xmm_temp); // xor with the current r vector + + __ pxor (xmm_result, xmm_key0); // do the aes rounds + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) { + __ aesenc(xmm_result, as_XMMRegister(rnum)); + } + aes_enc_key(xmm_result, xmm_temp, key, 0xb0); + aes_enc_key(xmm_result, xmm_temp, key, 0xc0); + aes_enc_key(xmm_result, xmm_temp, key, 0xd0); + load_key(xmm_temp, key, 0xe0); + __ aesenclast(xmm_result, xmm_temp); + + __ movdqu(Address(to, pos, Address::times_1, 0), xmm_result); // store into the next 16 bytes of output + // no need to store r to memory until we exit + __ addptr(pos, AESBlockSize); + __ subptr(len_reg, AESBlockSize); + __ jcc(Assembler::notEqual, L_loopTop_256); + __ jmp(L_exit); + + return start; + } + + + + // This is a version of CBC/AES Decrypt which does 4 blocks in a loop at a time + // to hide instruction latency + // + // Arguments: + // + // Inputs: + // c_rarg0 - source byte array address + // c_rarg1 - destination byte array address + // c_rarg2 - K (key) in little endian int array + // c_rarg3 - r vector byte array address + // c_rarg4 - input length + // + + address generate_cipherBlockChaining_decryptAESCrypt_Parallel() { + assert(UseAES && (UseAVX > 0), "need AES instructions and misaligned SSE support"); + __ align(CodeEntryAlignment); + StubCodeMark mark(this, "StubRoutines", "cipherBlockChaining_decryptAESCrypt"); + address start = __ pc(); + + Label L_exit, L_key_192_256, L_key_256; + Label L_singleBlock_loopTop_128, L_multiBlock_loopTop_128; + Label L_singleBlock_loopTop_192, L_singleBlock_loopTop_256; + const Register from = c_rarg0; // source array address + const Register to = c_rarg1; // destination array address + const Register key = c_rarg2; // key array address + const Register rvec = c_rarg3; // r byte array initialized from initvector array address + // and left with the results of the last encryption block +#ifndef _WIN64 + const Register len_reg = c_rarg4; // src len (must be multiple of blocksize 16) +#else + const Address len_mem(rsp, 6 * wordSize); // length is on stack on Win64 + const Register len_reg = r10; // pick the first volatile windows register +#endif + const Register pos = rax; + + // xmm register assignments for the loops below + const XMMRegister xmm_result = xmm0; + // keys 0-10 preloaded into xmm2-xmm12 + const int XMM_REG_NUM_KEY_FIRST = 5; + const int XMM_REG_NUM_KEY_LAST = 15; + const XMMRegister xmm_key_first = as_XMMRegister(XMM_REG_NUM_KEY_FIRST); + const XMMRegister xmm_key_last = as_XMMRegister(XMM_REG_NUM_KEY_LAST); + + __ enter(); // required for proper stackwalking of RuntimeStub frame + +#ifdef _WIN64 + // on win64, fill len_reg from stack position + __ movl(len_reg, len_mem); + // save the xmm registers which must be preserved 6-15 + __ subptr(rsp, -rsp_after_call_off * wordSize); + for (int i = 6; i <= XMM_REG_NUM_KEY_LAST; i++) { + __ movdqu(xmm_save(i), as_XMMRegister(i)); + } +#endif + // the java expanded key ordering is rotated one position from what we want + // so we start from 0x10 here and hit 0x00 last + const XMMRegister xmm_key_shuf_mask = xmm1; // used temporarily to swap key bytes up front + __ movdqu(xmm_key_shuf_mask, ExternalAddress(StubRoutines::x86::key_shuffle_mask_addr())); + // load up xmm regs 5 thru 15 with key 0x10 - 0xa0 - 0x00 + for (int rnum = XMM_REG_NUM_KEY_FIRST, offset = 0x10; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) { + if (rnum == XMM_REG_NUM_KEY_LAST) offset = 0x00; + load_key(as_XMMRegister(rnum), key, offset, xmm_key_shuf_mask); + offset += 0x10; + } + + const XMMRegister xmm_prev_block_cipher = xmm1; // holds cipher of previous block + // registers holding the four results in the parallelized loop + const XMMRegister xmm_result0 = xmm0; + const XMMRegister xmm_result1 = xmm2; + const XMMRegister xmm_result2 = xmm3; + const XMMRegister xmm_result3 = xmm4; + + __ movdqu(xmm_prev_block_cipher, Address(rvec, 0x00)); // initialize with initial rvec + + // now split to different paths depending on the keylen (len in ints of AESCrypt.KLE array (52=192, or 60=256)) + __ movl(rax, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT))); + __ cmpl(rax, 44); + __ jcc(Assembler::notEqual, L_key_192_256); + + + // 128-bit code follows here, parallelized + __ movptr(pos, 0); + __ align(OptoLoopAlignment); + __ BIND(L_multiBlock_loopTop_128); + __ cmpptr(len_reg, 4*AESBlockSize); // see if at least 4 blocks left + __ jcc(Assembler::less, L_singleBlock_loopTop_128); + + __ movdqu(xmm_result0, Address(from, pos, Address::times_1, 0*AESBlockSize)); // get next 4 blocks into xmmresult registers + __ movdqu(xmm_result1, Address(from, pos, Address::times_1, 1*AESBlockSize)); + __ movdqu(xmm_result2, Address(from, pos, Address::times_1, 2*AESBlockSize)); + __ movdqu(xmm_result3, Address(from, pos, Address::times_1, 3*AESBlockSize)); + +#define DoFour(opc, src_reg) \ + __ opc(xmm_result0, src_reg); \ + __ opc(xmm_result1, src_reg); \ + __ opc(xmm_result2, src_reg); \ + __ opc(xmm_result3, src_reg); + + DoFour(pxor, xmm_key_first); + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST - 1; rnum++) { + DoFour(aesdec, as_XMMRegister(rnum)); + } + DoFour(aesdeclast, xmm_key_last); + // for each result, xor with the r vector of previous cipher block + __ pxor(xmm_result0, xmm_prev_block_cipher); + __ movdqu(xmm_prev_block_cipher, Address(from, pos, Address::times_1, 0*AESBlockSize)); + __ pxor(xmm_result1, xmm_prev_block_cipher); + __ movdqu(xmm_prev_block_cipher, Address(from, pos, Address::times_1, 1*AESBlockSize)); + __ pxor(xmm_result2, xmm_prev_block_cipher); + __ movdqu(xmm_prev_block_cipher, Address(from, pos, Address::times_1, 2*AESBlockSize)); + __ pxor(xmm_result3, xmm_prev_block_cipher); + __ movdqu(xmm_prev_block_cipher, Address(from, pos, Address::times_1, 3*AESBlockSize)); // this will carry over to next set of blocks + + __ movdqu(Address(to, pos, Address::times_1, 0*AESBlockSize), xmm_result0); // store 4 results into the next 64 bytes of output + __ movdqu(Address(to, pos, Address::times_1, 1*AESBlockSize), xmm_result1); + __ movdqu(Address(to, pos, Address::times_1, 2*AESBlockSize), xmm_result2); + __ movdqu(Address(to, pos, Address::times_1, 3*AESBlockSize), xmm_result3); + + __ addptr(pos, 4*AESBlockSize); + __ subptr(len_reg, 4*AESBlockSize); + __ jmp(L_multiBlock_loopTop_128); + + // registers used in the non-parallelized loops + const XMMRegister xmm_prev_block_cipher_save = xmm2; + const XMMRegister xmm_temp = xmm3; + + __ align(OptoLoopAlignment); + __ BIND(L_singleBlock_loopTop_128); + __ cmpptr(len_reg, 0); // any blocks left?? + __ jcc(Assembler::equal, L_exit); + __ movdqu(xmm_result, Address(from, pos, Address::times_1, 0)); // get next 16 bytes of cipher input + __ movdqa(xmm_prev_block_cipher_save, xmm_result); // save for next r vector + __ pxor (xmm_result, xmm_key_first); // do the aes dec rounds + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST - 1; rnum++) { + __ aesdec(xmm_result, as_XMMRegister(rnum)); + } + __ aesdeclast(xmm_result, xmm_key_last); + __ pxor (xmm_result, xmm_prev_block_cipher); // xor with the current r vector + __ movdqu(Address(to, pos, Address::times_1, 0), xmm_result); // store into the next 16 bytes of output + // no need to store r to memory until we exit + __ movdqa(xmm_prev_block_cipher, xmm_prev_block_cipher_save); // set up next r vector with cipher input from this block + + __ addptr(pos, AESBlockSize); + __ subptr(len_reg, AESBlockSize); + __ jmp(L_singleBlock_loopTop_128); + + + __ BIND(L_exit); + __ movdqu(Address(rvec, 0), xmm_prev_block_cipher); // final value of r stored in rvec of CipherBlockChaining object +#ifdef _WIN64 + // restore regs belonging to calling function + for (int i = 6; i <= XMM_REG_NUM_KEY_LAST; i++) { + __ movdqu(as_XMMRegister(i), xmm_save(i)); + } +#endif + __ movl(rax, 0); // return 0 (why?) + __ leave(); // required for proper stackwalking of RuntimeStub frame + __ ret(0); + + + __ BIND(L_key_192_256); + // here rax = len in ints of AESCrypt.KLE array (52=192, or 60=256) + __ cmpl(rax, 52); + __ jcc(Assembler::notEqual, L_key_256); + + // 192-bit code follows here (could be optimized to use parallelism) + __ movptr(pos, 0); + __ align(OptoLoopAlignment); + __ BIND(L_singleBlock_loopTop_192); + __ movdqu(xmm_result, Address(from, pos, Address::times_1, 0)); // get next 16 bytes of cipher input + __ movdqa(xmm_prev_block_cipher_save, xmm_result); // save for next r vector + __ pxor (xmm_result, xmm_key_first); // do the aes dec rounds + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST - 1; rnum++) { + __ aesdec(xmm_result, as_XMMRegister(rnum)); + } + aes_dec_key(xmm_result, xmm_temp, key, 0xb0); // 192-bit key goes up to c0 + aes_dec_key(xmm_result, xmm_temp, key, 0xc0); + __ aesdeclast(xmm_result, xmm_key_last); // xmm15 always came from key+0 + __ pxor (xmm_result, xmm_prev_block_cipher); // xor with the current r vector + __ movdqu(Address(to, pos, Address::times_1, 0), xmm_result); // store into the next 16 bytes of output + // no need to store r to memory until we exit + __ movdqa(xmm_prev_block_cipher, xmm_prev_block_cipher_save); // set up next r vector with cipher input from this block + + __ addptr(pos, AESBlockSize); + __ subptr(len_reg, AESBlockSize); + __ jcc(Assembler::notEqual,L_singleBlock_loopTop_192); + __ jmp(L_exit); + + __ BIND(L_key_256); + // 256-bit code follows here (could be optimized to use parallelism) + __ movptr(pos, 0); + __ align(OptoLoopAlignment); + __ BIND(L_singleBlock_loopTop_256); + __ movdqu(xmm_result, Address(from, pos, Address::times_1, 0)); // get next 16 bytes of cipher input + __ movdqa(xmm_prev_block_cipher_save, xmm_result); // save for next r vector + __ pxor (xmm_result, xmm_key_first); // do the aes dec rounds + for (int rnum = XMM_REG_NUM_KEY_FIRST + 1; rnum <= XMM_REG_NUM_KEY_LAST - 1; rnum++) { + __ aesdec(xmm_result, as_XMMRegister(rnum)); + } + aes_dec_key(xmm_result, xmm_temp, key, 0xb0); // 256-bit key goes up to e0 + aes_dec_key(xmm_result, xmm_temp, key, 0xc0); + aes_dec_key(xmm_result, xmm_temp, key, 0xd0); + aes_dec_key(xmm_result, xmm_temp, key, 0xe0); + __ aesdeclast(xmm_result, xmm_key_last); // xmm15 came from key+0 + __ pxor (xmm_result, xmm_prev_block_cipher); // xor with the current r vector + __ movdqu(Address(to, pos, Address::times_1, 0), xmm_result); // store into the next 16 bytes of output + // no need to store r to memory until we exit + __ movdqa(xmm_prev_block_cipher, xmm_prev_block_cipher_save); // set up next r vector with cipher input from this block + + __ addptr(pos, AESBlockSize); + __ subptr(len_reg, AESBlockSize); + __ jcc(Assembler::notEqual,L_singleBlock_loopTop_256); + __ jmp(L_exit); + + return start; + } + + + #undef __ #define __ masm-> @@ -3135,6 +3677,16 @@ generate_arraycopy_stubs(); generate_math_stubs(); + + // don't bother generating these AES intrinsic stubs unless global flag is set + if (UseAESIntrinsics) { + StubRoutines::x86::_key_shuffle_mask_addr = generate_key_shuffle_mask(); // needed by the others + + StubRoutines::_aescrypt_encryptBlock = generate_aescrypt_encryptBlock(); + StubRoutines::_aescrypt_decryptBlock = generate_aescrypt_decryptBlock(); + StubRoutines::_cipherBlockChaining_encryptAESCrypt = generate_cipherBlockChaining_encryptAESCrypt(); + StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_cipherBlockChaining_decryptAESCrypt_Parallel(); + } } public: diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/x86/vm/stubRoutines_x86_32.cpp --- a/src/cpu/x86/vm/stubRoutines_x86_32.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/x86/vm/stubRoutines_x86_32.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -44,3 +44,4 @@ address StubRoutines::x86::_verify_mxcsr_entry = NULL; address StubRoutines::x86::_verify_fpu_cntrl_wrd_entry = NULL; +address StubRoutines::x86::_key_shuffle_mask_addr = NULL; diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/x86/vm/stubRoutines_x86_32.hpp --- a/src/cpu/x86/vm/stubRoutines_x86_32.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/x86/vm/stubRoutines_x86_32.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -41,10 +41,14 @@ private: static address _verify_mxcsr_entry; static address _verify_fpu_cntrl_wrd_entry; + // shuffle mask for fixing up 128-bit words consisting of big-endian 32-bit integers + static address _key_shuffle_mask_addr; public: static address verify_mxcsr_entry() { return _verify_mxcsr_entry; } static address verify_fpu_cntrl_wrd_entry() { return _verify_fpu_cntrl_wrd_entry; } + static address key_shuffle_mask_addr() { return _key_shuffle_mask_addr; } + }; static bool returns_to_call_stub(address return_pc) { return return_pc == _call_stub_return_address; } diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/x86/vm/stubRoutines_x86_64.cpp --- a/src/cpu/x86/vm/stubRoutines_x86_64.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/x86/vm/stubRoutines_x86_64.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -56,3 +56,4 @@ address StubRoutines::x86::_double_sign_mask = NULL; address StubRoutines::x86::_double_sign_flip = NULL; address StubRoutines::x86::_mxcsr_std = NULL; +address StubRoutines::x86::_key_shuffle_mask_addr = NULL; diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/x86/vm/stubRoutines_x86_64.hpp --- a/src/cpu/x86/vm/stubRoutines_x86_64.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/x86/vm/stubRoutines_x86_64.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -54,6 +54,8 @@ static address _double_sign_mask; static address _double_sign_flip; static address _mxcsr_std; + // shuffle mask for fixing up 128-bit words consisting of big-endian 32-bit integers + static address _key_shuffle_mask_addr; public: @@ -116,6 +118,9 @@ { return _mxcsr_std; } + + static address key_shuffle_mask_addr() { return _key_shuffle_mask_addr; } + }; #endif // CPU_X86_VM_STUBROUTINES_X86_64_HPP diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/x86/vm/vm_version_x86.cpp --- a/src/cpu/x86/vm/vm_version_x86.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/x86/vm/vm_version_x86.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -419,13 +419,16 @@ if (UseAVX < 1) _cpuFeatures &= ~CPU_AVX; + if (!UseAES && !FLAG_IS_DEFAULT(UseAES)) + _cpuFeatures &= ~CPU_AES; + if (logical_processors_per_package() == 1) { // HT processor could be installed on a system which doesn't support HT. _cpuFeatures &= ~CPU_HT; } char buf[256]; - jio_snprintf(buf, sizeof(buf), "(%u cores per cpu, %u threads per core) family %d model %d stepping %d%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + jio_snprintf(buf, sizeof(buf), "(%u cores per cpu, %u threads per core) family %d model %d stepping %d%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", cores_per_cpu(), threads_per_core(), cpu_family(), _model, _stepping, (supports_cmov() ? ", cmov" : ""), @@ -441,6 +444,7 @@ (supports_popcnt() ? ", popcnt" : ""), (supports_avx() ? ", avx" : ""), (supports_avx2() ? ", avx2" : ""), + (supports_aes() ? ", aes" : ""), (supports_mmx_ext() ? ", mmxext" : ""), (supports_3dnow_prefetch() ? ", 3dnowpref" : ""), (supports_lzcnt() ? ", lzcnt": ""), @@ -472,6 +476,29 @@ if (!supports_avx ()) // Drop to 0 if no AVX support UseAVX = 0; + // Use AES instructions if available. + if (supports_aes()) { + if (FLAG_IS_DEFAULT(UseAES)) { + UseAES = true; + } + } else if (UseAES) { + if (!FLAG_IS_DEFAULT(UseAES)) + warning("AES instructions not available on this CPU"); + FLAG_SET_DEFAULT(UseAES, false); + } + + // The AES intrinsic stubs require AES instruction support (of course) + // but also require AVX mode for misaligned SSE access + if (UseAES && (UseAVX > 0)) { + if (FLAG_IS_DEFAULT(UseAESIntrinsics)) { + UseAESIntrinsics = true; + } + } else if (UseAESIntrinsics) { + if (!FLAG_IS_DEFAULT(UseAESIntrinsics)) + warning("AES intrinsics not available on this CPU"); + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } + #ifdef COMPILER2 if (UseFPUForSpilling) { if (UseSSE < 2) { @@ -714,6 +741,9 @@ if (UseAVX > 0) { tty->print(" UseAVX=%d",UseAVX); } + if (UseAES) { + tty->print(" UseAES=1"); + } tty->cr(); tty->print("Allocation"); if (AllocatePrefetchStyle <= 0 || UseSSE == 0 && !supports_3dnow_prefetch()) { diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/x86/vm/vm_version_x86.hpp --- a/src/cpu/x86/vm/vm_version_x86.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/x86/vm/vm_version_x86.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -78,7 +78,9 @@ sse4_2 : 1, : 2, popcnt : 1, - : 3, + : 1, + aes : 1, + : 1, osxsave : 1, avx : 1, : 3; @@ -244,7 +246,8 @@ CPU_TSC = (1 << 15), CPU_TSCINV = (1 << 16), CPU_AVX = (1 << 17), - CPU_AVX2 = (1 << 18) + CPU_AVX2 = (1 << 18), + CPU_AES = (1 << 19) } cpuFeatureFlags; enum { @@ -420,6 +423,8 @@ result |= CPU_TSC; if (_cpuid_info.ext_cpuid7_edx.bits.tsc_invariance != 0) result |= CPU_TSCINV; + if (_cpuid_info.std_cpuid1_ecx.bits.aes != 0) + result |= CPU_AES; // AMD features. if (is_amd()) { @@ -544,6 +549,7 @@ static bool supports_avx() { return (_cpuFeatures & CPU_AVX) != 0; } static bool supports_avx2() { return (_cpuFeatures & CPU_AVX2) != 0; } static bool supports_tsc() { return (_cpuFeatures & CPU_TSC) != 0; } + static bool supports_aes() { return (_cpuFeatures & CPU_AES) != 0; } // Intel features static bool is_intel_family_core() { return is_intel() && diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/x86/vm/x86.ad --- a/src/cpu/x86/vm/x86.ad Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/x86/vm/x86.ad Mon Nov 05 13:55:31 2012 -0800 @@ -4102,9 +4102,158 @@ // ----------------------- LogicalRightShift ----------------------------------- -// Shorts/Chars vector logical right shift produces incorrect Java result +// Shorts vector logical right shift produces incorrect Java result // for negative data because java code convert short value into int with -// sign extension before a shift. +// sign extension before a shift. But char vectors are fine since chars are +// unsigned values. + +instruct vsrl2S(vecS dst, vecS shift) %{ + predicate(n->as_Vector()->length() == 2); + match(Set dst (URShiftVS dst shift)); + format %{ "psrlw $dst,$shift\t! logical right shift packed2S" %} + ins_encode %{ + __ psrlw($dst$$XMMRegister, $shift$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl2S_imm(vecS dst, immI8 shift) %{ + predicate(n->as_Vector()->length() == 2); + match(Set dst (URShiftVS dst shift)); + format %{ "psrlw $dst,$shift\t! logical right shift packed2S" %} + ins_encode %{ + __ psrlw($dst$$XMMRegister, (int)$shift$$constant); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl2S_reg(vecS dst, vecS src, vecS shift) %{ + predicate(UseAVX > 0 && n->as_Vector()->length() == 2); + match(Set dst (URShiftVS src shift)); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed2S" %} + ins_encode %{ + bool vector256 = false; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector256); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl2S_reg_imm(vecS dst, vecS src, immI8 shift) %{ + predicate(UseAVX > 0 && n->as_Vector()->length() == 2); + match(Set dst (URShiftVS src shift)); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed2S" %} + ins_encode %{ + bool vector256 = false; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector256); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl4S(vecD dst, vecS shift) %{ + predicate(n->as_Vector()->length() == 4); + match(Set dst (URShiftVS dst shift)); + format %{ "psrlw $dst,$shift\t! logical right shift packed4S" %} + ins_encode %{ + __ psrlw($dst$$XMMRegister, $shift$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl4S_imm(vecD dst, immI8 shift) %{ + predicate(n->as_Vector()->length() == 4); + match(Set dst (URShiftVS dst shift)); + format %{ "psrlw $dst,$shift\t! logical right shift packed4S" %} + ins_encode %{ + __ psrlw($dst$$XMMRegister, (int)$shift$$constant); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl4S_reg(vecD dst, vecD src, vecS shift) %{ + predicate(UseAVX > 0 && n->as_Vector()->length() == 4); + match(Set dst (URShiftVS src shift)); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed4S" %} + ins_encode %{ + bool vector256 = false; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector256); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl4S_reg_imm(vecD dst, vecD src, immI8 shift) %{ + predicate(UseAVX > 0 && n->as_Vector()->length() == 4); + match(Set dst (URShiftVS src shift)); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed4S" %} + ins_encode %{ + bool vector256 = false; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector256); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl8S(vecX dst, vecS shift) %{ + predicate(n->as_Vector()->length() == 8); + match(Set dst (URShiftVS dst shift)); + format %{ "psrlw $dst,$shift\t! logical right shift packed8S" %} + ins_encode %{ + __ psrlw($dst$$XMMRegister, $shift$$XMMRegister); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl8S_imm(vecX dst, immI8 shift) %{ + predicate(n->as_Vector()->length() == 8); + match(Set dst (URShiftVS dst shift)); + format %{ "psrlw $dst,$shift\t! logical right shift packed8S" %} + ins_encode %{ + __ psrlw($dst$$XMMRegister, (int)$shift$$constant); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl8S_reg(vecX dst, vecX src, vecS shift) %{ + predicate(UseAVX > 0 && n->as_Vector()->length() == 8); + match(Set dst (URShiftVS src shift)); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed8S" %} + ins_encode %{ + bool vector256 = false; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector256); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl8S_reg_imm(vecX dst, vecX src, immI8 shift) %{ + predicate(UseAVX > 0 && n->as_Vector()->length() == 8); + match(Set dst (URShiftVS src shift)); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed8S" %} + ins_encode %{ + bool vector256 = false; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector256); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl16S_reg(vecY dst, vecY src, vecS shift) %{ + predicate(UseAVX > 1 && n->as_Vector()->length() == 16); + match(Set dst (URShiftVS src shift)); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed16S" %} + ins_encode %{ + bool vector256 = true; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, $shift$$XMMRegister, vector256); + %} + ins_pipe( pipe_slow ); +%} + +instruct vsrl16S_reg_imm(vecY dst, vecY src, immI8 shift) %{ + predicate(UseAVX > 1 && n->as_Vector()->length() == 16); + match(Set dst (URShiftVS src shift)); + format %{ "vpsrlw $dst,$src,$shift\t! logical right shift packed16S" %} + ins_encode %{ + bool vector256 = true; + __ vpsrlw($dst$$XMMRegister, $src$$XMMRegister, (int)$shift$$constant, vector256); + %} + ins_pipe( pipe_slow ); +%} // Integers vector logical right shift instruct vsrl2I(vecD dst, vecS shift) %{ diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/zero/vm/cppInterpreterGenerator_zero.hpp --- a/src/cpu/zero/vm/cppInterpreterGenerator_zero.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/zero/vm/cppInterpreterGenerator_zero.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -31,12 +31,17 @@ return _masm; } - protected: - address generate_entry(address entry_point) { - ZeroEntry *entry = (ZeroEntry *) assembler()->pc(); - assembler()->advance(sizeof(ZeroEntry)); + public: + static address generate_entry_impl(MacroAssembler* masm, address entry_point) { + ZeroEntry *entry = (ZeroEntry *) masm->pc(); + masm->advance(sizeof(ZeroEntry)); entry->set_entry_point(entry_point); return (address) entry; } + protected: + address generate_entry(address entry_point) { + return generate_entry_impl(assembler(), entry_point); + } + #endif // CPU_ZERO_VM_CPPINTERPRETERGENERATOR_ZERO_HPP diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/zero/vm/cppInterpreter_zero.cpp --- a/src/cpu/zero/vm/cppInterpreter_zero.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/zero/vm/cppInterpreter_zero.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -180,25 +180,6 @@ method, istate->osr_entry(), istate->osr_buf(), THREAD); return; } - else if (istate->msg() == BytecodeInterpreter::call_method_handle) { - oop method_handle = istate->callee(); - - // Trim back the stack to put the parameters at the top - stack->set_sp(istate->stack() + 1); - - // Make the call - process_method_handle(method_handle, THREAD); - fixup_after_potential_safepoint(); - - // Convert the result - istate->set_stack(stack->sp() - 1); - - // Restore the stack - stack->set_sp(istate->stack_limit() + 1); - - // Resume the interpreter - istate->set_msg(BytecodeInterpreter::method_resume); - } else { ShouldNotReachHere(); } @@ -535,35 +516,35 @@ if (entry->is_volatile()) { switch (entry->flag_state()) { case ctos: - SET_LOCALS_INT(object->char_field_acquire(entry->f2()), 0); + SET_LOCALS_INT(object->char_field_acquire(entry->f2_as_index()), 0); break; case btos: - SET_LOCALS_INT(object->byte_field_acquire(entry->f2()), 0); + SET_LOCALS_INT(object->byte_field_acquire(entry->f2_as_index()), 0); break; case stos: - SET_LOCALS_INT(object->short_field_acquire(entry->f2()), 0); + SET_LOCALS_INT(object->short_field_acquire(entry->f2_as_index()), 0); break; case itos: - SET_LOCALS_INT(object->int_field_acquire(entry->f2()), 0); + SET_LOCALS_INT(object->int_field_acquire(entry->f2_as_index()), 0); break; case ltos: - SET_LOCALS_LONG(object->long_field_acquire(entry->f2()), 0); + SET_LOCALS_LONG(object->long_field_acquire(entry->f2_as_index()), 0); break; case ftos: - SET_LOCALS_FLOAT(object->float_field_acquire(entry->f2()), 0); + SET_LOCALS_FLOAT(object->float_field_acquire(entry->f2_as_index()), 0); break; case dtos: - SET_LOCALS_DOUBLE(object->double_field_acquire(entry->f2()), 0); + SET_LOCALS_DOUBLE(object->double_field_acquire(entry->f2_as_index()), 0); break; case atos: - SET_LOCALS_OBJECT(object->obj_field_acquire(entry->f2()), 0); + SET_LOCALS_OBJECT(object->obj_field_acquire(entry->f2_as_index()), 0); break; default: @@ -573,35 +554,35 @@ else { switch (entry->flag_state()) { case ctos: - SET_LOCALS_INT(object->char_field(entry->f2()), 0); + SET_LOCALS_INT(object->char_field(entry->f2_as_index()), 0); break; case btos: - SET_LOCALS_INT(object->byte_field(entry->f2()), 0); + SET_LOCALS_INT(object->byte_field(entry->f2_as_index()), 0); break; case stos: - SET_LOCALS_INT(object->short_field(entry->f2()), 0); + SET_LOCALS_INT(object->short_field(entry->f2_as_index()), 0); break; case itos: - SET_LOCALS_INT(object->int_field(entry->f2()), 0); + SET_LOCALS_INT(object->int_field(entry->f2_as_index()), 0); break; case ltos: - SET_LOCALS_LONG(object->long_field(entry->f2()), 0); + SET_LOCALS_LONG(object->long_field(entry->f2_as_index()), 0); break; case ftos: - SET_LOCALS_FLOAT(object->float_field(entry->f2()), 0); + SET_LOCALS_FLOAT(object->float_field(entry->f2_as_index()), 0); break; case dtos: - SET_LOCALS_DOUBLE(object->double_field(entry->f2()), 0); + SET_LOCALS_DOUBLE(object->double_field(entry->f2_as_index()), 0); break; case atos: - SET_LOCALS_OBJECT(object->obj_field(entry->f2()), 0); + SET_LOCALS_OBJECT(object->obj_field(entry->f2_as_index()), 0); break; default: @@ -629,516 +610,6 @@ return 0; } -int CppInterpreter::method_handle_entry(Method* method, - intptr_t UNUSED, TRAPS) { - JavaThread *thread = (JavaThread *) THREAD; - ZeroStack *stack = thread->zero_stack(); - int argument_slots = method->size_of_parameters(); - int result_slots = type2size[result_type_of(method)]; - intptr_t *vmslots = stack->sp(); - intptr_t *unwind_sp = vmslots + argument_slots; - - // Find the MethodType - address p = (address) method; - for (jint* pc = method->method_type_offsets_chain(); (*pc) != -1; pc++) { - p = *(address*)(p + (*pc)); - } - oop method_type = (oop) p; - - // The MethodHandle is in the slot after the arguments - int num_vmslots = argument_slots - 1; - oop method_handle = VMSLOTS_OBJECT(num_vmslots); - - // InvokeGeneric requires some extra shuffling - oop mhtype = java_lang_invoke_MethodHandle::type(method_handle); - bool is_exact = mhtype == method_type; - if (!is_exact) { - if (true || // FIXME - method->intrinsic_id() == vmIntrinsics::_invokeExact) { - CALL_VM_NOCHECK_NOFIX( - SharedRuntime::throw_WrongMethodTypeException( - thread, method_type, mhtype)); - // NB all oops trashed! - assert(HAS_PENDING_EXCEPTION, "should do"); - stack->set_sp(unwind_sp); - return 0; - } - assert(method->intrinsic_id() == vmIntrinsics::_invokeGeneric, "should be"); - - // Load up an adapter from the calling type - // NB the x86 code for this (in methodHandles_x86.cpp, search for - // "genericInvoker") is really really odd. I'm hoping it's trying - // to accomodate odd VM/class library combinations I can ignore. - oop adapter = NULL; //FIXME: load the adapter from the CP cache - IF (adapter == NULL) { - CALL_VM_NOCHECK_NOFIX( - SharedRuntime::throw_WrongMethodTypeException( - thread, method_type, mhtype)); - // NB all oops trashed! - assert(HAS_PENDING_EXCEPTION, "should do"); - stack->set_sp(unwind_sp); - return 0; - } - - // Adapters are shared among form-families of method-type. The - // type being called is passed as a trusted first argument so that - // the adapter knows the actual types of its arguments and return - // values. - insert_vmslots(num_vmslots + 1, 1, THREAD); - if (HAS_PENDING_EXCEPTION) { - // NB all oops trashed! - stack->set_sp(unwind_sp); - return 0; - } - - vmslots = stack->sp(); - num_vmslots++; - SET_VMSLOTS_OBJECT(method_type, num_vmslots); - - method_handle = adapter; - } - - // Start processing - process_method_handle(method_handle, THREAD); - if (HAS_PENDING_EXCEPTION) - result_slots = 0; - - // If this is an invokeExact then the eventual callee will not - // have unwound the method handle argument so we have to do it. - // If a result is being returned the it will be above the method - // handle argument we're unwinding. - if (is_exact) { - intptr_t result[2]; - for (int i = 0; i < result_slots; i++) - result[i] = stack->pop(); - stack->pop(); - for (int i = result_slots - 1; i >= 0; i--) - stack->push(result[i]); - } - - // Check - assert(stack->sp() == unwind_sp - result_slots, "should be"); - - // No deoptimized frames on the stack - return 0; -} - -void CppInterpreter::process_method_handle(oop method_handle, TRAPS) { - JavaThread *thread = (JavaThread *) THREAD; - ZeroStack *stack = thread->zero_stack(); - intptr_t *vmslots = stack->sp(); - - bool direct_to_method = false; - BasicType src_rtype = T_ILLEGAL; - BasicType dst_rtype = T_ILLEGAL; - - MethodHandleEntry *entry = - java_lang_invoke_MethodHandle::vmentry(method_handle); - MethodHandles::EntryKind entry_kind = - (MethodHandles::EntryKind) (((intptr_t) entry) & 0xffffffff); - - Method* method = NULL; - switch (entry_kind) { - case MethodHandles::_invokestatic_mh: - direct_to_method = true; - break; - - case MethodHandles::_invokespecial_mh: - case MethodHandles::_invokevirtual_mh: - case MethodHandles::_invokeinterface_mh: - { - oop receiver = - VMSLOTS_OBJECT( - java_lang_invoke_MethodHandle::vmslots(method_handle) - 1); - if (receiver == NULL) { - stack->set_sp(calculate_unwind_sp(stack, method_handle)); - CALL_VM_NOCHECK_NOFIX( - throw_exception( - thread, vmSymbols::java_lang_NullPointerException())); - // NB all oops trashed! - assert(HAS_PENDING_EXCEPTION, "should do"); - return; - } - if (entry_kind != MethodHandles::_invokespecial_mh) { - intptr_t index = java_lang_invoke_DirectMethodHandle::vmindex(method_handle); - InstanceKlass* rcvrKlass = - (InstanceKlass *) receiver->klass(); - if (entry_kind == MethodHandles::_invokevirtual_mh) { - method = (Method*) rcvrKlass->start_of_vtable()[index]; - } - else { - oop iclass = java_lang_invoke_MethodHandle::next_target(method_handle); - itableOffsetEntry* ki = - (itableOffsetEntry *) rcvrKlass->start_of_itable(); - int i, length = rcvrKlass->itable_length(); - for (i = 0; i < length; i++, ki++ ) { - if (ki->interface_klass() == iclass) - break; - } - if (i == length) { - stack->set_sp(calculate_unwind_sp(stack, method_handle)); - CALL_VM_NOCHECK_NOFIX( - throw_exception( - thread, vmSymbols::java_lang_IncompatibleClassChangeError())); - // NB all oops trashed! - assert(HAS_PENDING_EXCEPTION, "should do"); - return; - } - itableMethodEntry* im = ki->first_method_entry(receiver->klass()); - method = im[index].method(); - if (method == NULL) { - stack->set_sp(calculate_unwind_sp(stack, method_handle)); - CALL_VM_NOCHECK_NOFIX( - throw_exception( - thread, vmSymbols::java_lang_AbstractMethodError())); - // NB all oops trashed! - assert(HAS_PENDING_EXCEPTION, "should do"); - return; - } - } - } - } - direct_to_method = true; - break; - - case MethodHandles::_bound_ref_direct_mh: - case MethodHandles::_bound_int_direct_mh: - case MethodHandles::_bound_long_direct_mh: - direct_to_method = true; - // fall through - case MethodHandles::_bound_ref_mh: - case MethodHandles::_bound_int_mh: - case MethodHandles::_bound_long_mh: - { - BasicType arg_type = T_ILLEGAL; - int arg_mask = -1; - int arg_slots = -1; - MethodHandles::get_ek_bound_mh_info( - entry_kind, arg_type, arg_mask, arg_slots); - int arg_slot = - java_lang_invoke_BoundMethodHandle::vmargslot(method_handle); - - // Create the new slot(s) - intptr_t *unwind_sp = calculate_unwind_sp(stack, method_handle); - insert_vmslots(arg_slot, arg_slots, THREAD); - if (HAS_PENDING_EXCEPTION) { - // all oops trashed - stack->set_sp(unwind_sp); - return; - } - vmslots = stack->sp(); - - // Store bound argument into new stack slot - oop arg = java_lang_invoke_BoundMethodHandle::argument(method_handle); - if (arg_type == T_OBJECT) { - assert(arg_slots == 1, "should be"); - SET_VMSLOTS_OBJECT(arg, arg_slot); - } - else { - jvalue arg_value; - arg_type = java_lang_boxing_object::get_value(arg, &arg_value); - switch (arg_type) { - case T_BOOLEAN: - SET_VMSLOTS_INT(arg_value.z, arg_slot); - break; - case T_CHAR: - SET_VMSLOTS_INT(arg_value.c, arg_slot); - break; - case T_BYTE: - SET_VMSLOTS_INT(arg_value.b, arg_slot); - break; - case T_SHORT: - SET_VMSLOTS_INT(arg_value.s, arg_slot); - break; - case T_INT: - SET_VMSLOTS_INT(arg_value.i, arg_slot); - break; - case T_FLOAT: - SET_VMSLOTS_FLOAT(arg_value.f, arg_slot); - break; - case T_LONG: - SET_VMSLOTS_LONG(arg_value.j, arg_slot + 1); - break; - case T_DOUBLE: - SET_VMSLOTS_DOUBLE(arg_value.d, arg_slot + 1); - break; - default: - tty->print_cr("unhandled type %s", type2name(arg_type)); - ShouldNotReachHere(); - } - } - } - break; - - case MethodHandles::_adapter_retype_only: - case MethodHandles::_adapter_retype_raw: - src_rtype = result_type_of_handle( - java_lang_invoke_MethodHandle::next_target(method_handle)); - dst_rtype = result_type_of_handle(method_handle); - break; - - case MethodHandles::_adapter_check_cast: - { - int arg_slot = - java_lang_invoke_AdapterMethodHandle::vmargslot(method_handle); - oop arg = VMSLOTS_OBJECT(arg_slot); - if (arg != NULL) { - Klass* objKlassOop = arg->klass(); - Klass* klassOf = java_lang_Class::as_Klass( - java_lang_invoke_AdapterMethodHandle::argument(method_handle)); - - if (objKlassOop != klassOf && - !objKlassOop->is_subtype_of(klassOf)) { - ResourceMark rm(THREAD); - const char* objName = Klass::cast(objKlassOop)->external_name(); - const char* klassName = Klass::cast(klassOf)->external_name(); - char* message = SharedRuntime::generate_class_cast_message( - objName, klassName); - - stack->set_sp(calculate_unwind_sp(stack, method_handle)); - CALL_VM_NOCHECK_NOFIX( - throw_exception( - thread, vmSymbols::java_lang_ClassCastException(), message)); - // NB all oops trashed! - assert(HAS_PENDING_EXCEPTION, "should do"); - return; - } - } - } - break; - - case MethodHandles::_adapter_dup_args: - { - int arg_slot = - java_lang_invoke_AdapterMethodHandle::vmargslot(method_handle); - int conv = - java_lang_invoke_AdapterMethodHandle::conversion(method_handle); - int num_slots = -MethodHandles::adapter_conversion_stack_move(conv); - assert(num_slots > 0, "should be"); - - // Create the new slot(s) - intptr_t *unwind_sp = calculate_unwind_sp(stack, method_handle); - stack->overflow_check(num_slots, THREAD); - if (HAS_PENDING_EXCEPTION) { - // all oops trashed - stack->set_sp(unwind_sp); - return; - } - - // Duplicate the arguments - for (int i = num_slots - 1; i >= 0; i--) - stack->push(*VMSLOTS_SLOT(arg_slot + i)); - - vmslots = stack->sp(); // unused, but let the compiler figure that out - } - break; - - case MethodHandles::_adapter_drop_args: - { - int arg_slot = - java_lang_invoke_AdapterMethodHandle::vmargslot(method_handle); - int conv = - java_lang_invoke_AdapterMethodHandle::conversion(method_handle); - int num_slots = MethodHandles::adapter_conversion_stack_move(conv); - assert(num_slots > 0, "should be"); - - remove_vmslots(arg_slot, num_slots, THREAD); // doesn't trap - vmslots = stack->sp(); // unused, but let the compiler figure that out - } - break; - - case MethodHandles::_adapter_opt_swap_1: - case MethodHandles::_adapter_opt_swap_2: - case MethodHandles::_adapter_opt_rot_1_up: - case MethodHandles::_adapter_opt_rot_1_down: - case MethodHandles::_adapter_opt_rot_2_up: - case MethodHandles::_adapter_opt_rot_2_down: - { - int arg1 = - java_lang_invoke_AdapterMethodHandle::vmargslot(method_handle); - int conv = - java_lang_invoke_AdapterMethodHandle::conversion(method_handle); - int arg2 = MethodHandles::adapter_conversion_vminfo(conv); - - int swap_bytes = 0, rotate = 0; - MethodHandles::get_ek_adapter_opt_swap_rot_info( - entry_kind, swap_bytes, rotate); - int swap_slots = swap_bytes >> LogBytesPerWord; - - intptr_t tmp; - switch (rotate) { - case 0: // swap - for (int i = 0; i < swap_slots; i++) { - tmp = *VMSLOTS_SLOT(arg1 + i); - SET_VMSLOTS_SLOT(VMSLOTS_SLOT(arg2 + i), arg1 + i); - SET_VMSLOTS_SLOT(&tmp, arg2 + i); - } - break; - - case 1: // up - assert(arg1 - swap_slots > arg2, "should be"); - - tmp = *VMSLOTS_SLOT(arg1); - for (int i = arg1 - swap_slots; i >= arg2; i--) - SET_VMSLOTS_SLOT(VMSLOTS_SLOT(i), i + swap_slots); - SET_VMSLOTS_SLOT(&tmp, arg2); - - break; - - case -1: // down - assert(arg2 - swap_slots > arg1, "should be"); - - tmp = *VMSLOTS_SLOT(arg1); - for (int i = arg1 + swap_slots; i <= arg2; i++) - SET_VMSLOTS_SLOT(VMSLOTS_SLOT(i), i - swap_slots); - SET_VMSLOTS_SLOT(&tmp, arg2); - break; - - default: - ShouldNotReachHere(); - } - } - break; - - case MethodHandles::_adapter_opt_i2l: - { - int arg_slot = - java_lang_invoke_AdapterMethodHandle::vmargslot(method_handle); - int arg = VMSLOTS_INT(arg_slot); - intptr_t *unwind_sp = calculate_unwind_sp(stack, method_handle); - insert_vmslots(arg_slot, 1, THREAD); - if (HAS_PENDING_EXCEPTION) { - // all oops trashed - stack->set_sp(unwind_sp); - return; - } - vmslots = stack->sp(); - arg_slot++; - SET_VMSLOTS_LONG(arg, arg_slot); - } - break; - - case MethodHandles::_adapter_opt_unboxi: - case MethodHandles::_adapter_opt_unboxl: - { - int arg_slot = - java_lang_invoke_AdapterMethodHandle::vmargslot(method_handle); - oop arg = VMSLOTS_OBJECT(arg_slot); - jvalue arg_value; - if (arg == NULL) { - // queue a nullpointer exception for the caller - stack->set_sp(calculate_unwind_sp(stack, method_handle)); - CALL_VM_NOCHECK_NOFIX( - throw_exception( - thread, vmSymbols::java_lang_NullPointerException())); - // NB all oops trashed! - assert(HAS_PENDING_EXCEPTION, "should do"); - return; - } - BasicType arg_type = java_lang_boxing_object::get_value(arg, &arg_value); - if (arg_type == T_LONG || arg_type == T_DOUBLE) { - intptr_t *unwind_sp = calculate_unwind_sp(stack, method_handle); - insert_vmslots(arg_slot, 1, THREAD); - if (HAS_PENDING_EXCEPTION) { - // all oops trashed - stack->set_sp(unwind_sp); - return; - } - vmslots = stack->sp(); - arg_slot++; - } - switch (arg_type) { - case T_BOOLEAN: - SET_VMSLOTS_INT(arg_value.z, arg_slot); - break; - case T_CHAR: - SET_VMSLOTS_INT(arg_value.c, arg_slot); - break; - case T_BYTE: - SET_VMSLOTS_INT(arg_value.b, arg_slot); - break; - case T_SHORT: - SET_VMSLOTS_INT(arg_value.s, arg_slot); - break; - case T_INT: - SET_VMSLOTS_INT(arg_value.i, arg_slot); - break; - case T_FLOAT: - SET_VMSLOTS_FLOAT(arg_value.f, arg_slot); - break; - case T_LONG: - SET_VMSLOTS_LONG(arg_value.j, arg_slot); - break; - case T_DOUBLE: - SET_VMSLOTS_DOUBLE(arg_value.d, arg_slot); - break; - default: - tty->print_cr("unhandled type %s", type2name(arg_type)); - ShouldNotReachHere(); - } - } - break; - - default: - tty->print_cr("unhandled entry_kind %s", - MethodHandles::entry_name(entry_kind)); - ShouldNotReachHere(); - } - - // Continue along the chain - if (direct_to_method) { - if (method == NULL) { - method = - (Method*) java_lang_invoke_MethodHandle::vmtarget(method_handle); - } - address entry_point = method->from_interpreted_entry(); - Interpreter::invoke_method(method, entry_point, THREAD); - } - else { - process_method_handle( - java_lang_invoke_MethodHandle::next_target(method_handle), THREAD); - } - // NB all oops now trashed - - // Adapt the result type, if necessary - if (src_rtype != dst_rtype && !HAS_PENDING_EXCEPTION) { - switch (dst_rtype) { - case T_VOID: - for (int i = 0; i < type2size[src_rtype]; i++) - stack->pop(); - return; - - case T_INT: - switch (src_rtype) { - case T_VOID: - stack->overflow_check(1, CHECK); - stack->push(0); - return; - - case T_BOOLEAN: - case T_CHAR: - case T_BYTE: - case T_SHORT: - return; - } - // INT results sometimes need narrowing - case T_BOOLEAN: - case T_CHAR: - case T_BYTE: - case T_SHORT: - switch (src_rtype) { - case T_INT: - return; - } - } - - tty->print_cr("unhandled conversion:"); - tty->print_cr("src_rtype = %s", type2name(src_rtype)); - tty->print_cr("dst_rtype = %s", type2name(dst_rtype)); - ShouldNotReachHere(); - } -} - // The new slots will be inserted before slot insert_before. // Slots < insert_before will have the same slot number after the insert. // Slots >= insert_before will become old_slot + num_slots. @@ -1380,10 +851,6 @@ entry_point = ((InterpreterGenerator*) this)->generate_abstract_entry(); break; - case Interpreter::method_handle: - entry_point = ((InterpreterGenerator*) this)->generate_method_handle_entry(); - break; - case Interpreter::java_lang_math_sin: case Interpreter::java_lang_math_cos: case Interpreter::java_lang_math_tan: @@ -1391,6 +858,8 @@ case Interpreter::java_lang_math_log: case Interpreter::java_lang_math_log10: case Interpreter::java_lang_math_sqrt: + case Interpreter::java_lang_math_pow: + case Interpreter::java_lang_math_exp: entry_point = ((InterpreterGenerator*) this)->generate_math_entry(kind); break; diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/zero/vm/cppInterpreter_zero.hpp --- a/src/cpu/zero/vm/cppInterpreter_zero.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/zero/vm/cppInterpreter_zero.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -36,7 +36,6 @@ static int native_entry(Method* method, intptr_t UNUSED, TRAPS); static int accessor_entry(Method* method, intptr_t UNUSED, TRAPS); static int empty_entry(Method* method, intptr_t UNUSED, TRAPS); - static int method_handle_entry(Method* method, intptr_t UNUSED, TRAPS); public: // Main loop of normal_entry @@ -44,7 +43,6 @@ private: // Helpers for method_handle_entry - static void process_method_handle(oop method_handle, TRAPS); static void insert_vmslots(int insert_before, int num_slots, TRAPS); static void remove_vmslots(int first_slot, int num_slots, TRAPS); static BasicType result_type_of_handle(oop method_handle); diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/zero/vm/frame_zero.cpp --- a/src/cpu/zero/vm/frame_zero.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/zero/vm/frame_zero.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -351,7 +351,7 @@ switch (offset) { case pc_off: strncpy(fieldbuf, "pc", buflen); - if (method()->is_oop()) { + if (method()->is_method()) { nmethod *code = method()->code(); if (code && code->pc_desc_at(pc())) { SimpleScopeDesc ssd(code, pc()); @@ -367,7 +367,7 @@ case method_off: strncpy(fieldbuf, "method", buflen); - if (method()->is_oop()) { + if (method()->is_method()) { method()->name_and_sig_as_C_string(valuebuf, buflen); } return; @@ -378,7 +378,7 @@ } // Variable part - if (method()->is_oop()) { + if (method()->is_method()) { identify_vp_word(frame_index, addr_of_word(offset), addr_of_word(header_words + 1), unextended_sp() + method()->max_stack(), @@ -430,4 +430,3 @@ // unused... but returns fp() to minimize changes introduced by 7087445 return fp(); } - diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/zero/vm/frame_zero.inline.hpp --- a/src/cpu/zero/vm/frame_zero.inline.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/zero/vm/frame_zero.inline.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -36,6 +36,8 @@ _deopt_state = unknown; } +inline address frame::sender_pc() const { ShouldNotCallThis(); } + inline frame::frame(ZeroFrame* zf, intptr_t* sp) { _zeroframe = zf; _sp = sp; diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/zero/vm/icBuffer_zero.cpp --- a/src/cpu/zero/vm/icBuffer_zero.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/zero/vm/icBuffer_zero.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -40,7 +40,7 @@ } void InlineCacheBuffer::assemble_ic_buffer_code(address code_begin, - Metadata* cached_oop, + void* cached_oop, address entry_point) { // NB ic_stub_code_size() must return the size of the code we generate ShouldNotCallThis(); @@ -51,7 +51,6 @@ ShouldNotCallThis(); } -Metadata* InlineCacheBuffer::ic_buffer_cached_oop(address code_begin) { - // NB ic_stub_code_size() must return the size of the code we generate +void* InlineCacheBuffer::ic_buffer_cached_value(address code_begin) { ShouldNotCallThis(); } diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/zero/vm/methodHandles_zero.cpp --- a/src/cpu/zero/vm/methodHandles_zero.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/zero/vm/methodHandles_zero.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -24,26 +24,159 @@ */ #include "precompiled.hpp" +#include "interpreter/interpreterGenerator.hpp" #include "interpreter/interpreter.hpp" #include "memory/allocation.inline.hpp" #include "prims/methodHandles.hpp" -int MethodHandles::adapter_conversion_ops_supported_mask() { - return ((1<zero_stack(); + InterpreterFrame *frame = thread->top_zero_frame()->as_interpreter_frame(); + interpreterState istate = frame->interpreter_state(); + + // Trim back the stack to put the parameters at the top + stack->set_sp(istate->stack() + 1); + + Interpreter::invoke_method(method, method->from_interpreted_entry(), THREAD); + + // Convert the result + istate->set_stack(stack->sp() - 1); + +} + +oop MethodHandles::popFromStack(TRAPS) { + + JavaThread *thread = (JavaThread *) THREAD; + InterpreterFrame *frame = thread->top_zero_frame()->as_interpreter_frame(); + interpreterState istate = frame->interpreter_state(); + intptr_t* topOfStack = istate->stack(); + + oop top = STACK_OBJECT(-1); + MORE_STACK(-1); + istate->set_stack(topOfStack); + + return top; + +} + +int MethodHandles::method_handle_entry_invokeBasic(Method* method, intptr_t UNUSED, TRAPS) { + + JavaThread *thread = (JavaThread *) THREAD; + InterpreterFrame *frame = thread->top_zero_frame()->as_interpreter_frame(); + interpreterState istate = frame->interpreter_state(); + intptr_t* topOfStack = istate->stack(); + + // 'this' is a MethodHandle. We resolve the target method by accessing this.form.vmentry.vmtarget. + int numArgs = method->size_of_parameters(); + oop lform1 = java_lang_invoke_MethodHandle::form(STACK_OBJECT(-numArgs)); // this.form + oop vmEntry1 = java_lang_invoke_LambdaForm::vmentry(lform1); + Method* vmtarget = (Method*) java_lang_invoke_MemberName::vmtarget(vmEntry1); + + invoke_target(vmtarget, THREAD); + + // No deoptimized frames on the stack + return 0; +} + +int MethodHandles::method_handle_entry_linkToStaticOrSpecial(Method* method, intptr_t UNUSED, TRAPS) { + + // Pop appendix argument from stack. This is a MemberName which we resolve to the + // target method. + oop vmentry = popFromStack(THREAD); + + Method* vmtarget = (Method*) java_lang_invoke_MemberName::vmtarget(vmentry); + + invoke_target(vmtarget, THREAD); + + return 0; } -void MethodHandles::generate_method_handle_stub(MacroAssembler* masm, - MethodHandles::EntryKind ek) { - init_entry(ek, (MethodHandleEntry *) ek); +int MethodHandles::method_handle_entry_linkToInterface(Method* method, intptr_t UNUSED, TRAPS) { + JavaThread *thread = (JavaThread *) THREAD; + InterpreterFrame *frame = thread->top_zero_frame()->as_interpreter_frame(); + interpreterState istate = frame->interpreter_state(); + + // Pop appendix argument from stack. This is a MemberName which we resolve to the + // target method. + oop vmentry = popFromStack(THREAD); + intptr_t* topOfStack = istate->stack(); + + // Resolve target method by looking up in the receiver object's itable. + Klass* clazz = java_lang_Class::as_Klass(java_lang_invoke_MemberName::clazz(vmentry)); + intptr_t vmindex = java_lang_invoke_MemberName::vmindex(vmentry); + Method* target = (Method*) java_lang_invoke_MemberName::vmtarget(vmentry); + + int numArgs = target->size_of_parameters(); + oop recv = STACK_OBJECT(-numArgs); + + InstanceKlass* klass_part = InstanceKlass::cast(recv->klass()); + itableOffsetEntry* ki = (itableOffsetEntry*) klass_part->start_of_itable(); + int i; + for ( i = 0 ; i < klass_part->itable_length() ; i++, ki++ ) { + if (ki->interface_klass() == clazz) break; + } + + itableMethodEntry* im = ki->first_method_entry(recv->klass()); + Method* vmtarget = im[vmindex].method(); + + invoke_target(vmtarget, THREAD); + + return 0; } + +int MethodHandles::method_handle_entry_linkToVirtual(Method* method, intptr_t UNUSED, TRAPS) { + JavaThread *thread = (JavaThread *) THREAD; + + InterpreterFrame *frame = thread->top_zero_frame()->as_interpreter_frame(); + interpreterState istate = frame->interpreter_state(); + + // Pop appendix argument from stack. This is a MemberName which we resolve to the + // target method. + oop vmentry = popFromStack(THREAD); + intptr_t* topOfStack = istate->stack(); + + // Resolve target method by looking up in the receiver object's vtable. + intptr_t vmindex = java_lang_invoke_MemberName::vmindex(vmentry); + Method* target = (Method*) java_lang_invoke_MemberName::vmtarget(vmentry); + int numArgs = target->size_of_parameters(); + oop recv = STACK_OBJECT(-numArgs); + Klass* clazz = recv->klass(); + Klass* klass_part = InstanceKlass::cast(clazz); + klassVtable* vtable = klass_part->vtable(); + Method* vmtarget = vtable->method_at(vmindex); + + invoke_target(vmtarget, THREAD); + + return 0; +} + +int MethodHandles::method_handle_entry_invalid(Method* method, intptr_t UNUSED, TRAPS) { + ShouldNotReachHere(); + return 0; +} + +address MethodHandles::generate_method_handle_interpreter_entry(MacroAssembler* masm, + vmIntrinsics::ID iid) { + switch (iid) { + case vmIntrinsics::_invokeGeneric: + case vmIntrinsics::_compiledLambdaForm: + // Perhaps surprisingly, the symbolic references visible to Java are not directly used. + // They are linked to Java-generated adapters via MethodHandleNatives.linkMethod. + // They all allow an appendix argument. + return InterpreterGenerator::generate_entry_impl(masm, (address) MethodHandles::method_handle_entry_invalid); + case vmIntrinsics::_invokeBasic: + return InterpreterGenerator::generate_entry_impl(masm, (address) MethodHandles::method_handle_entry_invokeBasic); + case vmIntrinsics::_linkToStatic: + case vmIntrinsics::_linkToSpecial: + return InterpreterGenerator::generate_entry_impl(masm, (address) MethodHandles::method_handle_entry_linkToStaticOrSpecial); + case vmIntrinsics::_linkToInterface: + return InterpreterGenerator::generate_entry_impl(masm, (address) MethodHandles::method_handle_entry_linkToInterface); + case vmIntrinsics::_linkToVirtual: + return InterpreterGenerator::generate_entry_impl(masm, (address) MethodHandles::method_handle_entry_linkToVirtual); + default: + ShouldNotReachHere(); + return NULL; + } +} diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/zero/vm/methodHandles_zero.hpp --- a/src/cpu/zero/vm/methodHandles_zero.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/zero/vm/methodHandles_zero.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -26,6 +26,14 @@ // Adapters enum /* platform_dependent_constants */ { - adapter_code_size = 0 + adapter_code_size = sizeof(ZeroEntry) * (Interpreter::method_handle_invoke_LAST - Interpreter::method_handle_invoke_FIRST + 1) }; +private: + static oop popFromStack(TRAPS); + static void invoke_target(Method* method, TRAPS); + static int method_handle_entry_invokeBasic(Method* method, intptr_t UNUSED, TRAPS); + static int method_handle_entry_linkToStaticOrSpecial(Method* method, intptr_t UNUSED, TRAPS); + static int method_handle_entry_linkToVirtual(Method* method, intptr_t UNUSED, TRAPS); + static int method_handle_entry_linkToInterface(Method* method, intptr_t UNUSED, TRAPS); + static int method_handle_entry_invalid(Method* method, intptr_t UNUSED, TRAPS); diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/zero/vm/register_zero.hpp --- a/src/cpu/zero/vm/register_zero.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/zero/vm/register_zero.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -114,5 +114,8 @@ }; CONSTANT_REGISTER_DECLARATION(Register, noreg, (-1)); +#ifndef DONT_USE_REGISTER_DEFINES +#define noreg ((Register)(noreg_RegisterEnumValue)) +#endif #endif // CPU_ZERO_VM_REGISTER_ZERO_HPP diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/zero/vm/relocInfo_zero.cpp --- a/src/cpu/zero/vm/relocInfo_zero.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/zero/vm/relocInfo_zero.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -77,3 +77,7 @@ CodeBuffer* dst) { ShouldNotCallThis(); } + +void metadata_Relocation::pd_fix_value(address x) { + ShouldNotCallThis(); +} diff -r 69ad7823b1ca -r 8940ddc1036f src/cpu/zero/vm/sharedRuntime_zero.cpp --- a/src/cpu/zero/vm/sharedRuntime_zero.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/cpu/zero/vm/sharedRuntime_zero.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -35,6 +35,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/vframeArray.hpp" #include "vmreg_zero.inline.hpp" + #ifdef COMPILER1 #include "c1/c1_Runtime1.hpp" #endif @@ -47,6 +48,12 @@ #endif + +static address zero_null_code_stub() { + address start = ShouldNotCallThisStub(); + return start; +} + int SharedRuntime::java_calling_convention(const BasicType *sig_bt, VMRegPair *regs, int total_args_passed, @@ -63,16 +70,14 @@ AdapterFingerPrint *fingerprint) { return AdapterHandlerLibrary::new_entry( fingerprint, - ShouldNotCallThisStub(), - ShouldNotCallThisStub(), - ShouldNotCallThisStub()); + CAST_FROM_FN_PTR(address,zero_null_code_stub), + CAST_FROM_FN_PTR(address,zero_null_code_stub), + CAST_FROM_FN_PTR(address,zero_null_code_stub)); } nmethod *SharedRuntime::generate_native_wrapper(MacroAssembler *masm, methodHandle method, int compile_id, - int total_args_passed, - int max_arg, BasicType *sig_bt, VMRegPair *regs, BasicType ret_type) { @@ -96,19 +101,20 @@ ShouldNotCallThis(); } +JRT_LEAF(void, zero_stub()) + ShouldNotCallThis(); +JRT_END + static RuntimeStub* generate_empty_runtime_stub(const char* name) { - CodeBuffer buffer(name, 0, 0); - return RuntimeStub::new_runtime_stub(name, &buffer, 0, 0, NULL, false); + return CAST_FROM_FN_PTR(RuntimeStub*,zero_stub); } static SafepointBlob* generate_empty_safepoint_blob() { - CodeBuffer buffer("handler_blob", 0, 0); - return SafepointBlob::create(&buffer, NULL, 0); + return CAST_FROM_FN_PTR(SafepointBlob*,zero_stub); } static DeoptimizationBlob* generate_empty_deopt_blob() { - CodeBuffer buffer("handler_blob", 0, 0); - return DeoptimizationBlob::create(&buffer, NULL, 0, 0, 0, 0); + return CAST_FROM_FN_PTR(DeoptimizationBlob*,zero_stub); } @@ -116,7 +122,7 @@ _deopt_blob = generate_empty_deopt_blob(); } -SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, bool cause_return) { +SafepointBlob* SharedRuntime::generate_handler_blob(address call_ptr, int poll_type) { return generate_empty_safepoint_blob(); } @@ -124,6 +130,7 @@ return generate_empty_runtime_stub("resolve_blob"); } + int SharedRuntime::c_calling_convention(const BasicType *sig_bt, VMRegPair *regs, int total_args_passed) { diff -r 69ad7823b1ca -r 8940ddc1036f src/os/bsd/vm/attachListener_bsd.cpp --- a/src/os/bsd/vm/attachListener_bsd.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/os/bsd/vm/attachListener_bsd.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -342,7 +342,6 @@ // get the credentials of the peer and check the effective uid/guid // - check with jeff on this. -#ifdef _ALLBSD_SOURCE uid_t puid; gid_t pgid; if (::getpeereid(s, &puid, &pgid) != 0) { @@ -350,17 +349,6 @@ RESTARTABLE(::close(s), res); continue; } -#else - struct ucred cred_info; - socklen_t optlen = sizeof(cred_info); - if (::getsockopt(s, SOL_SOCKET, SO_PEERCRED, (void*)&cred_info, &optlen) == -1) { - int res; - RESTARTABLE(::close(s), res); - continue; - } - uid_t puid = cred_info.uid; - gid_t pgid = cred_info.gid; -#endif uid_t euid = geteuid(); gid_t egid = getegid(); diff -r 69ad7823b1ca -r 8940ddc1036f src/os/bsd/vm/osThread_bsd.hpp --- a/src/os/bsd/vm/osThread_bsd.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/os/bsd/vm/osThread_bsd.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -39,18 +39,12 @@ private: -#ifdef _ALLBSD_SOURCE - #ifdef __APPLE__ typedef thread_t thread_id_t; #else typedef pthread_t thread_id_t; #endif -#else - typedef pid_t thread_id_t; -#endif - // _pthread_id is the pthread id, which is used by library calls // (e.g. pthread_kill). pthread_t _pthread_id; diff -r 69ad7823b1ca -r 8940ddc1036f src/os/bsd/vm/os_bsd.cpp --- a/src/os/bsd/vm/os_bsd.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/os/bsd/vm/os_bsd.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -108,14 +108,8 @@ # include # include # include -#ifdef _ALLBSD_SOURCE # include # include -#else -# include -# include -# include -#endif # include # include #ifndef __APPLE__ @@ -150,25 +144,10 @@ // global variables julong os::Bsd::_physical_memory = 0; -#ifndef _ALLBSD_SOURCE -address os::Bsd::_initial_thread_stack_bottom = NULL; -uintptr_t os::Bsd::_initial_thread_stack_size = 0; -#endif int (*os::Bsd::_clock_gettime)(clockid_t, struct timespec *) = NULL; -#ifndef _ALLBSD_SOURCE -int (*os::Bsd::_pthread_getcpuclockid)(pthread_t, clockid_t *) = NULL; -Mutex* os::Bsd::_createThread_lock = NULL; -#endif pthread_t os::Bsd::_main_thread; int os::Bsd::_page_size = -1; -#ifndef _ALLBSD_SOURCE -bool os::Bsd::_is_floating_stack = false; -bool os::Bsd::_is_NPTL = false; -bool os::Bsd::_supports_fast_thread_cpu_time = false; -const char * os::Bsd::_glibc_version = NULL; -const char * os::Bsd::_libpthread_version = NULL; -#endif static jlong initial_time_count=0; @@ -176,7 +155,7 @@ // For diagnostics to print a message once. see run_periodic_checks static sigset_t check_signal_done; -static bool check_signals = true;; +static bool check_signals = true; static pid_t _initial_pid = 0; @@ -198,16 +177,8 @@ } julong os::Bsd::available_memory() { -#ifdef _ALLBSD_SOURCE // XXXBSD: this is just a stopgap implementation return physical_memory() >> 2; -#else - // values in struct sysinfo are "unsigned long" - struct sysinfo si; - sysinfo(&si); - - return (julong)si.freeram * si.mem_unit; -#endif } julong os::physical_memory() { @@ -255,22 +226,6 @@ } -#ifndef _ALLBSD_SOURCE -#ifndef SYS_gettid -// i386: 224, ia64: 1105, amd64: 186, sparc 143 -#ifdef __ia64__ -#define SYS_gettid 1105 -#elif __i386__ -#define SYS_gettid 224 -#elif __amd64__ -#define SYS_gettid 186 -#elif __sparc__ -#define SYS_gettid 143 -#else -#error define gettid for the arch -#endif -#endif -#endif // Cpu architecture string #if defined(ZERO) @@ -302,36 +257,7 @@ #define COMPILER_VARIANT "client" #endif -#ifndef _ALLBSD_SOURCE -// pid_t gettid() -// -// Returns the kernel thread id of the currently running thread. Kernel -// thread id is used to access /proc. -// -// (Note that getpid() on BsdThreads returns kernel thread id too; but -// on NPTL, it returns the same pid for all threads, as required by POSIX.) -// -pid_t os::Bsd::gettid() { - int rslt = syscall(SYS_gettid); - if (rslt == -1) { - // old kernel, no NPTL support - return getpid(); - } else { - return (pid_t)rslt; - } -} - -// Most versions of bsd have a bug where the number of processors are -// determined by looking at the /proc file system. In a chroot environment, -// the system call returns 1. This causes the VM to act as if it is -// a single processor and elide locking (see is_MP() call). -static bool unsafe_chroot_detected = false; -static const char *unstable_chroot_error = "/proc file system not found.\n" - "Java may be unstable running multithreaded in a chroot " - "environment on Bsd when /proc filesystem is not mounted."; -#endif - -#ifdef _ALLBSD_SOURCE + void os::Bsd::initialize_system_info() { int mib[2]; size_t len; @@ -370,24 +296,6 @@ } #endif } -#else -void os::Bsd::initialize_system_info() { - set_processor_count(sysconf(_SC_NPROCESSORS_CONF)); - if (processor_count() == 1) { - pid_t pid = os::Bsd::gettid(); - char fname[32]; - jio_snprintf(fname, sizeof(fname), "/proc/%d", pid); - FILE *fp = fopen(fname, "r"); - if (fp == NULL) { - unsafe_chroot_detected = true; - } else { - fclose(fp); - } - } - _physical_memory = (julong)sysconf(_SC_PHYS_PAGES) * (julong)sysconf(_SC_PAGESIZE); - assert(processor_count() > 0, "bsd error"); -} -#endif #ifdef __APPLE__ static const char *get_home() { @@ -744,171 +652,6 @@ } } -#ifndef _ALLBSD_SOURCE -////////////////////////////////////////////////////////////////////////////// -// detecting pthread library - -void os::Bsd::libpthread_init() { - // Save glibc and pthread version strings. Note that _CS_GNU_LIBC_VERSION - // and _CS_GNU_LIBPTHREAD_VERSION are supported in glibc >= 2.3.2. Use a - // generic name for earlier versions. - // Define macros here so we can build HotSpot on old systems. -# ifndef _CS_GNU_LIBC_VERSION -# define _CS_GNU_LIBC_VERSION 2 -# endif -# ifndef _CS_GNU_LIBPTHREAD_VERSION -# define _CS_GNU_LIBPTHREAD_VERSION 3 -# endif - - size_t n = confstr(_CS_GNU_LIBC_VERSION, NULL, 0); - if (n > 0) { - char *str = (char *)malloc(n); - confstr(_CS_GNU_LIBC_VERSION, str, n); - os::Bsd::set_glibc_version(str); - } else { - // _CS_GNU_LIBC_VERSION is not supported, try gnu_get_libc_version() - static char _gnu_libc_version[32]; - jio_snprintf(_gnu_libc_version, sizeof(_gnu_libc_version), - "glibc %s %s", gnu_get_libc_version(), gnu_get_libc_release()); - os::Bsd::set_glibc_version(_gnu_libc_version); - } - - n = confstr(_CS_GNU_LIBPTHREAD_VERSION, NULL, 0); - if (n > 0) { - char *str = (char *)malloc(n); - confstr(_CS_GNU_LIBPTHREAD_VERSION, str, n); - // Vanilla RH-9 (glibc 2.3.2) has a bug that confstr() always tells - // us "NPTL-0.29" even we are running with BsdThreads. Check if this - // is the case. BsdThreads has a hard limit on max number of threads. - // So sysconf(_SC_THREAD_THREADS_MAX) will return a positive value. - // On the other hand, NPTL does not have such a limit, sysconf() - // will return -1 and errno is not changed. Check if it is really NPTL. - if (strcmp(os::Bsd::glibc_version(), "glibc 2.3.2") == 0 && - strstr(str, "NPTL") && - sysconf(_SC_THREAD_THREADS_MAX) > 0) { - free(str); - os::Bsd::set_libpthread_version("bsdthreads"); - } else { - os::Bsd::set_libpthread_version(str); - } - } else { - // glibc before 2.3.2 only has BsdThreads. - os::Bsd::set_libpthread_version("bsdthreads"); - } - - if (strstr(libpthread_version(), "NPTL")) { - os::Bsd::set_is_NPTL(); - } else { - os::Bsd::set_is_BsdThreads(); - } - - // BsdThreads have two flavors: floating-stack mode, which allows variable - // stack size; and fixed-stack mode. NPTL is always floating-stack. - if (os::Bsd::is_NPTL() || os::Bsd::supports_variable_stack_size()) { - os::Bsd::set_is_floating_stack(); - } -} - -///////////////////////////////////////////////////////////////////////////// -// thread stack - -// Force Bsd kernel to expand current thread stack. If "bottom" is close -// to the stack guard, caller should block all signals. -// -// MAP_GROWSDOWN: -// A special mmap() flag that is used to implement thread stacks. It tells -// kernel that the memory region should extend downwards when needed. This -// allows early versions of BsdThreads to only mmap the first few pages -// when creating a new thread. Bsd kernel will automatically expand thread -// stack as needed (on page faults). -// -// However, because the memory region of a MAP_GROWSDOWN stack can grow on -// demand, if a page fault happens outside an already mapped MAP_GROWSDOWN -// region, it's hard to tell if the fault is due to a legitimate stack -// access or because of reading/writing non-exist memory (e.g. buffer -// overrun). As a rule, if the fault happens below current stack pointer, -// Bsd kernel does not expand stack, instead a SIGSEGV is sent to the -// application (see Bsd kernel fault.c). -// -// This Bsd feature can cause SIGSEGV when VM bangs thread stack for -// stack overflow detection. -// -// Newer version of BsdThreads (since glibc-2.2, or, RH-7.x) and NPTL do -// not use this flag. However, the stack of initial thread is not created -// by pthread, it is still MAP_GROWSDOWN. Also it's possible (though -// unlikely) that user code can create a thread with MAP_GROWSDOWN stack -// and then attach the thread to JVM. -// -// To get around the problem and allow stack banging on Bsd, we need to -// manually expand thread stack after receiving the SIGSEGV. -// -// There are two ways to expand thread stack to address "bottom", we used -// both of them in JVM before 1.5: -// 1. adjust stack pointer first so that it is below "bottom", and then -// touch "bottom" -// 2. mmap() the page in question -// -// Now alternate signal stack is gone, it's harder to use 2. For instance, -// if current sp is already near the lower end of page 101, and we need to -// call mmap() to map page 100, it is possible that part of the mmap() frame -// will be placed in page 100. When page 100 is mapped, it is zero-filled. -// That will destroy the mmap() frame and cause VM to crash. -// -// The following code works by adjusting sp first, then accessing the "bottom" -// page to force a page fault. Bsd kernel will then automatically expand the -// stack mapping. -// -// _expand_stack_to() assumes its frame size is less than page size, which -// should always be true if the function is not inlined. - -#if __GNUC__ < 3 // gcc 2.x does not support noinline attribute -#define NOINLINE -#else -#define NOINLINE __attribute__ ((noinline)) -#endif - -static void _expand_stack_to(address bottom) NOINLINE; - -static void _expand_stack_to(address bottom) { - address sp; - size_t size; - volatile char *p; - - // Adjust bottom to point to the largest address within the same page, it - // gives us a one-page buffer if alloca() allocates slightly more memory. - bottom = (address)align_size_down((uintptr_t)bottom, os::Bsd::page_size()); - bottom += os::Bsd::page_size() - 1; - - // sp might be slightly above current stack pointer; if that's the case, we - // will alloca() a little more space than necessary, which is OK. Don't use - // os::current_stack_pointer(), as its result can be slightly below current - // stack pointer, causing us to not alloca enough to reach "bottom". - sp = (address)&sp; - - if (sp > bottom) { - size = sp - bottom; - p = (volatile char *)alloca(size); - assert(p != NULL && p <= (volatile char *)bottom, "alloca problem?"); - p[0] = '\0'; - } -} - -bool os::Bsd::manually_expand_stack(JavaThread * t, address addr) { - assert(t!=NULL, "just checking"); - assert(t->osthread()->expanding_stack(), "expand should be set"); - assert(t->stack_base() != NULL, "stack_base was not initialized"); - - if (addr < t->stack_base() && addr >= t->stack_yellow_zone_base()) { - sigset_t mask_all, old_sigset; - sigfillset(&mask_all); - pthread_sigmask(SIG_SETMASK, &mask_all, &old_sigset); - _expand_stack_to(addr); - pthread_sigmask(SIG_SETMASK, &old_sigset, NULL); - return true; - } - return false; -} -#endif ////////////////////////////////////////////////////////////////////////////// // create new thread @@ -917,43 +660,7 @@ // check if it's safe to start a new thread static bool _thread_safety_check(Thread* thread) { -#ifdef _ALLBSD_SOURCE - return true; -#else - if (os::Bsd::is_BsdThreads() && !os::Bsd::is_floating_stack()) { - // Fixed stack BsdThreads (SuSE Bsd/x86, and some versions of Redhat) - // Heap is mmap'ed at lower end of memory space. Thread stacks are - // allocated (MAP_FIXED) from high address space. Every thread stack - // occupies a fixed size slot (usually 2Mbytes, but user can change - // it to other values if they rebuild BsdThreads). - // - // Problem with MAP_FIXED is that mmap() can still succeed even part of - // the memory region has already been mmap'ed. That means if we have too - // many threads and/or very large heap, eventually thread stack will - // collide with heap. - // - // Here we try to prevent heap/stack collision by comparing current - // stack bottom with the highest address that has been mmap'ed by JVM - // plus a safety margin for memory maps created by native code. - // - // This feature can be disabled by setting ThreadSafetyMargin to 0 - // - if (ThreadSafetyMargin > 0) { - address stack_bottom = os::current_stack_base() - os::current_stack_size(); - - // not safe if our stack extends below the safety margin - return stack_bottom - ThreadSafetyMargin >= highest_vm_reserved_address(); - } else { - return true; - } - } else { - // Floating stack BsdThreads or NPTL: - // Unlike fixed stack BsdThreads, thread stacks are not MAP_FIXED. When - // there's not enough space left, pthread_create() will fail. If we come - // here, that means enough space has been reserved for stack. - return true; - } -#endif + return true; } #ifdef __APPLE__ @@ -991,7 +698,6 @@ return NULL; } -#ifdef _ALLBSD_SOURCE #ifdef __APPLE__ // thread_id is mach thread on macos osthread->set_thread_id(::mach_thread_self()); @@ -999,17 +705,6 @@ // thread_id is pthread_id on BSD osthread->set_thread_id(::pthread_self()); #endif -#else - // thread_id is kernel thread id (similar to Solaris LWP id) - osthread->set_thread_id(os::Bsd::gettid()); - - if (UseNUMA) { - int lgrp_id = os::numa_get_group_id(); - if (lgrp_id != -1) { - thread->set_lgrp_id(lgrp_id); - } - } -#endif // initialize signal mask for this thread os::Bsd::hotspot_sigmask(thread); @@ -1099,23 +794,9 @@ // let pthread_create() pick the default value. } -#ifndef _ALLBSD_SOURCE - // glibc guard page - pthread_attr_setguardsize(&attr, os::Bsd::default_guard_size(thr_type)); -#endif - ThreadState state; { - -#ifndef _ALLBSD_SOURCE - // Serialize thread creation if we are running with fixed stack BsdThreads - bool lock = os::Bsd::is_BsdThreads() && !os::Bsd::is_floating_stack(); - if (lock) { - os::Bsd::createThread_lock()->lock_without_safepoint_check(); - } -#endif - pthread_t tid; int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread); @@ -1128,9 +809,6 @@ // Need to clean up stuff we've allocated so far thread->set_osthread(NULL); delete osthread; -#ifndef _ALLBSD_SOURCE - if (lock) os::Bsd::createThread_lock()->unlock(); -#endif return false; } @@ -1146,11 +824,6 @@ } } -#ifndef _ALLBSD_SOURCE - if (lock) { - os::Bsd::createThread_lock()->unlock(); - } -#endif } // Aborted due to thread limit being reached @@ -1188,15 +861,11 @@ } // Store pthread info into the OSThread -#ifdef _ALLBSD_SOURCE #ifdef __APPLE__ osthread->set_thread_id(::mach_thread_self()); #else osthread->set_thread_id(::pthread_self()); #endif -#else - osthread->set_thread_id(os::Bsd::gettid()); -#endif osthread->set_pthread_id(::pthread_self()); // initialize floating point control register @@ -1207,35 +876,6 @@ thread->set_osthread(osthread); -#ifndef _ALLBSD_SOURCE - if (UseNUMA) { - int lgrp_id = os::numa_get_group_id(); - if (lgrp_id != -1) { - thread->set_lgrp_id(lgrp_id); - } - } - - if (os::Bsd::is_initial_thread()) { - // If current thread is initial thread, its stack is mapped on demand, - // see notes about MAP_GROWSDOWN. Here we try to force kernel to map - // the entire stack region to avoid SEGV in stack banging. - // It is also useful to get around the heap-stack-gap problem on SuSE - // kernel (see 4821821 for details). We first expand stack to the top - // of yellow zone, then enable stack yellow zone (order is significant, - // enabling yellow zone first will crash JVM on SuSE Bsd), so there - // is no gap between the last two virtual memory regions. - - JavaThread *jt = (JavaThread *)thread; - address addr = jt->stack_yellow_zone_base(); - assert(addr != NULL, "initialization problem?"); - assert(jt->stack_available(addr) > 0, "stack guard should not be enabled"); - - osthread->set_expanding_stack(); - os::Bsd::manually_expand_stack(jt, addr); - osthread->clear_expanding_stack(); - } -#endif - // initialize signal mask for this thread // and save the caller's signal mask os::Bsd::hotspot_sigmask(thread); @@ -1290,247 +930,6 @@ return ThreadLocalStorage::thread(); } -////////////////////////////////////////////////////////////////////////////// -// initial thread - -#ifndef _ALLBSD_SOURCE -// Check if current thread is the initial thread, similar to Solaris thr_main. -bool os::Bsd::is_initial_thread(void) { - char dummy; - // If called before init complete, thread stack bottom will be null. - // Can be called if fatal error occurs before initialization. - if (initial_thread_stack_bottom() == NULL) return false; - assert(initial_thread_stack_bottom() != NULL && - initial_thread_stack_size() != 0, - "os::init did not locate initial thread's stack region"); - if ((address)&dummy >= initial_thread_stack_bottom() && - (address)&dummy < initial_thread_stack_bottom() + initial_thread_stack_size()) - return true; - else return false; -} - -// Find the virtual memory area that contains addr -static bool find_vma(address addr, address* vma_low, address* vma_high) { - FILE *fp = fopen("/proc/self/maps", "r"); - if (fp) { - address low, high; - while (!feof(fp)) { - if (fscanf(fp, "%p-%p", &low, &high) == 2) { - if (low <= addr && addr < high) { - if (vma_low) *vma_low = low; - if (vma_high) *vma_high = high; - fclose (fp); - return true; - } - } - for (;;) { - int ch = fgetc(fp); - if (ch == EOF || ch == (int)'\n') break; - } - } - fclose(fp); - } - return false; -} - -// Locate initial thread stack. This special handling of initial thread stack -// is needed because pthread_getattr_np() on most (all?) Bsd distros returns -// bogus value for initial thread. -void os::Bsd::capture_initial_stack(size_t max_size) { - // stack size is the easy part, get it from RLIMIT_STACK - size_t stack_size; - struct rlimit rlim; - getrlimit(RLIMIT_STACK, &rlim); - stack_size = rlim.rlim_cur; - - // 6308388: a bug in ld.so will relocate its own .data section to the - // lower end of primordial stack; reduce ulimit -s value a little bit - // so we won't install guard page on ld.so's data section. - stack_size -= 2 * page_size(); - - // 4441425: avoid crash with "unlimited" stack size on SuSE 7.1 or Redhat - // 7.1, in both cases we will get 2G in return value. - // 4466587: glibc 2.2.x compiled w/o "--enable-kernel=2.4.0" (RH 7.0, - // SuSE 7.2, Debian) can not handle alternate signal stack correctly - // for initial thread if its stack size exceeds 6M. Cap it at 2M, - // in case other parts in glibc still assumes 2M max stack size. - // FIXME: alt signal stack is gone, maybe we can relax this constraint? -#ifndef IA64 - if (stack_size > 2 * K * K) stack_size = 2 * K * K; -#else - // Problem still exists RH7.2 (IA64 anyway) but 2MB is a little small - if (stack_size > 4 * K * K) stack_size = 4 * K * K; -#endif - - // Try to figure out where the stack base (top) is. This is harder. - // - // When an application is started, glibc saves the initial stack pointer in - // a global variable "__libc_stack_end", which is then used by system - // libraries. __libc_stack_end should be pretty close to stack top. The - // variable is available since the very early days. However, because it is - // a private interface, it could disappear in the future. - // - // Bsd kernel saves start_stack information in /proc//stat. Similar - // to __libc_stack_end, it is very close to stack top, but isn't the real - // stack top. Note that /proc may not exist if VM is running as a chroot - // program, so reading /proc//stat could fail. Also the contents of - // /proc//stat could change in the future (though unlikely). - // - // We try __libc_stack_end first. If that doesn't work, look for - // /proc//stat. If neither of them works, we use current stack pointer - // as a hint, which should work well in most cases. - - uintptr_t stack_start; - - // try __libc_stack_end first - uintptr_t *p = (uintptr_t *)dlsym(RTLD_DEFAULT, "__libc_stack_end"); - if (p && *p) { - stack_start = *p; - } else { - // see if we can get the start_stack field from /proc/self/stat - FILE *fp; - int pid; - char state; - int ppid; - int pgrp; - int session; - int nr; - int tpgrp; - unsigned long flags; - unsigned long minflt; - unsigned long cminflt; - unsigned long majflt; - unsigned long cmajflt; - unsigned long utime; - unsigned long stime; - long cutime; - long cstime; - long prio; - long nice; - long junk; - long it_real; - uintptr_t start; - uintptr_t vsize; - intptr_t rss; - uintptr_t rsslim; - uintptr_t scodes; - uintptr_t ecode; - int i; - - // Figure what the primordial thread stack base is. Code is inspired - // by email from Hans Boehm. /proc/self/stat begins with current pid, - // followed by command name surrounded by parentheses, state, etc. - char stat[2048]; - int statlen; - - fp = fopen("/proc/self/stat", "r"); - if (fp) { - statlen = fread(stat, 1, 2047, fp); - stat[statlen] = '\0'; - fclose(fp); - - // Skip pid and the command string. Note that we could be dealing with - // weird command names, e.g. user could decide to rename java launcher - // to "java 1.4.2 :)", then the stat file would look like - // 1234 (java 1.4.2 :)) R ... ... - // We don't really need to know the command string, just find the last - // occurrence of ")" and then start parsing from there. See bug 4726580. - char * s = strrchr(stat, ')'); - - i = 0; - if (s) { - // Skip blank chars - do s++; while (isspace(*s)); - -#define _UFM UINTX_FORMAT -#define _DFM INTX_FORMAT - - /* 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 */ - /* 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 */ - i = sscanf(s, "%c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld " _UFM _UFM _DFM _UFM _UFM _UFM _UFM, - &state, /* 3 %c */ - &ppid, /* 4 %d */ - &pgrp, /* 5 %d */ - &session, /* 6 %d */ - &nr, /* 7 %d */ - &tpgrp, /* 8 %d */ - &flags, /* 9 %lu */ - &minflt, /* 10 %lu */ - &cminflt, /* 11 %lu */ - &majflt, /* 12 %lu */ - &cmajflt, /* 13 %lu */ - &utime, /* 14 %lu */ - &stime, /* 15 %lu */ - &cutime, /* 16 %ld */ - &cstime, /* 17 %ld */ - &prio, /* 18 %ld */ - &nice, /* 19 %ld */ - &junk, /* 20 %ld */ - &it_real, /* 21 %ld */ - &start, /* 22 UINTX_FORMAT */ - &vsize, /* 23 UINTX_FORMAT */ - &rss, /* 24 INTX_FORMAT */ - &rsslim, /* 25 UINTX_FORMAT */ - &scodes, /* 26 UINTX_FORMAT */ - &ecode, /* 27 UINTX_FORMAT */ - &stack_start); /* 28 UINTX_FORMAT */ - } - -#undef _UFM -#undef _DFM - - if (i != 28 - 2) { - assert(false, "Bad conversion from /proc/self/stat"); - // product mode - assume we are the initial thread, good luck in the - // embedded case. - warning("Can't detect initial thread stack location - bad conversion"); - stack_start = (uintptr_t) &rlim; - } - } else { - // For some reason we can't open /proc/self/stat (for example, running on - // FreeBSD with a Bsd emulator, or inside chroot), this should work for - // most cases, so don't abort: - warning("Can't detect initial thread stack location - no /proc/self/stat"); - stack_start = (uintptr_t) &rlim; - } - } - - // Now we have a pointer (stack_start) very close to the stack top, the - // next thing to do is to figure out the exact location of stack top. We - // can find out the virtual memory area that contains stack_start by - // reading /proc/self/maps, it should be the last vma in /proc/self/maps, - // and its upper limit is the real stack top. (again, this would fail if - // running inside chroot, because /proc may not exist.) - - uintptr_t stack_top; - address low, high; - if (find_vma((address)stack_start, &low, &high)) { - // success, "high" is the true stack top. (ignore "low", because initial - // thread stack grows on demand, its real bottom is high - RLIMIT_STACK.) - stack_top = (uintptr_t)high; - } else { - // failed, likely because /proc/self/maps does not exist - warning("Can't detect initial thread stack location - find_vma failed"); - // best effort: stack_start is normally within a few pages below the real - // stack top, use it as stack top, and reduce stack size so we won't put - // guard page outside stack. - stack_top = stack_start; - stack_size -= 16 * page_size(); - } - - // stack_top could be partially down the page so align it - stack_top = align_size_up(stack_top, page_size()); - - if (max_size && stack_size > max_size) { - _initial_thread_stack_size = max_size; - } else { - _initial_thread_stack_size = stack_size; - } - - _initial_thread_stack_size = align_size_down(_initial_thread_stack_size, page_size()); - _initial_thread_stack_bottom = (address)stack_top - _initial_thread_stack_size; -} -#endif //////////////////////////////////////////////////////////////////////////////// // time support @@ -1576,7 +975,7 @@ void os::Bsd::clock_init() { // XXXDARWIN: Investigate replacement monotonic clock } -#elif defined(_ALLBSD_SOURCE) +#else void os::Bsd::clock_init() { struct timespec res; struct timespec tp; @@ -1586,86 +985,8 @@ _clock_gettime = ::clock_gettime; } } -#else -void os::Bsd::clock_init() { - // we do dlopen's in this particular order due to bug in bsd - // dynamical loader (see 6348968) leading to crash on exit - void* handle = dlopen("librt.so.1", RTLD_LAZY); - if (handle == NULL) { - handle = dlopen("librt.so", RTLD_LAZY); - } - - if (handle) { - int (*clock_getres_func)(clockid_t, struct timespec*) = - (int(*)(clockid_t, struct timespec*))dlsym(handle, "clock_getres"); - int (*clock_gettime_func)(clockid_t, struct timespec*) = - (int(*)(clockid_t, struct timespec*))dlsym(handle, "clock_gettime"); - if (clock_getres_func && clock_gettime_func) { - // See if monotonic clock is supported by the kernel. Note that some - // early implementations simply return kernel jiffies (updated every - // 1/100 or 1/1000 second). It would be bad to use such a low res clock - // for nano time (though the monotonic property is still nice to have). - // It's fixed in newer kernels, however clock_getres() still returns - // 1/HZ. We check if clock_getres() works, but will ignore its reported - // resolution for now. Hopefully as people move to new kernels, this - // won't be a problem. - struct timespec res; - struct timespec tp; - if (clock_getres_func (CLOCK_MONOTONIC, &res) == 0 && - clock_gettime_func(CLOCK_MONOTONIC, &tp) == 0) { - // yes, monotonic clock is supported - _clock_gettime = clock_gettime_func; - } else { - // close librt if there is no monotonic clock - dlclose(handle); - } - } - } -} #endif -#ifndef _ALLBSD_SOURCE -#ifndef SYS_clock_getres - -#if defined(IA32) || defined(AMD64) -#define SYS_clock_getres IA32_ONLY(266) AMD64_ONLY(229) -#define sys_clock_getres(x,y) ::syscall(SYS_clock_getres, x, y) -#else -#warning "SYS_clock_getres not defined for this platform, disabling fast_thread_cpu_time" -#define sys_clock_getres(x,y) -1 -#endif - -#else -#define sys_clock_getres(x,y) ::syscall(SYS_clock_getres, x, y) -#endif - -void os::Bsd::fast_thread_clock_init() { - if (!UseBsdPosixThreadCPUClocks) { - return; - } - clockid_t clockid; - struct timespec tp; - int (*pthread_getcpuclockid_func)(pthread_t, clockid_t *) = - (int(*)(pthread_t, clockid_t *)) dlsym(RTLD_DEFAULT, "pthread_getcpuclockid"); - - // Switch to using fast clocks for thread cpu time if - // the sys_clock_getres() returns 0 error code. - // Note, that some kernels may support the current thread - // clock (CLOCK_THREAD_CPUTIME_ID) but not the clocks - // returned by the pthread_getcpuclockid(). - // If the fast Posix clocks are supported then the sys_clock_getres() - // must return at least tp.tv_sec == 0 which means a resolution - // better than 1 sec. This is extra check for reliability. - - if(pthread_getcpuclockid_func && - pthread_getcpuclockid_func(_main_thread, &clockid) == 0 && - sys_clock_getres(clockid, &tp) == 0 && tp.tv_sec == 0) { - - _supports_fast_thread_cpu_time = true; - _pthread_getcpuclockid = pthread_getcpuclockid_func; - } -} -#endif jlong os::javaTimeNanos() { if (Bsd::supports_monotonic_clock()) { @@ -1978,7 +1299,6 @@ return false; } -#ifdef _ALLBSD_SOURCE // ported from solaris version bool os::dll_address_to_library_name(address addr, char* buf, int buflen, int* offset) { @@ -1994,86 +1314,10 @@ return false; } } -#else -struct _address_to_library_name { - address addr; // input : memory address - size_t buflen; // size of fname - char* fname; // output: library name - address base; // library base addr -}; - -static int address_to_library_name_callback(struct dl_phdr_info *info, - size_t size, void *data) { - int i; - bool found = false; - address libbase = NULL; - struct _address_to_library_name * d = (struct _address_to_library_name *)data; - - // iterate through all loadable segments - for (i = 0; i < info->dlpi_phnum; i++) { - address segbase = (address)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr); - if (info->dlpi_phdr[i].p_type == PT_LOAD) { - // base address of a library is the lowest address of its loaded - // segments. - if (libbase == NULL || libbase > segbase) { - libbase = segbase; - } - // see if 'addr' is within current segment - if (segbase <= d->addr && - d->addr < segbase + info->dlpi_phdr[i].p_memsz) { - found = true; - } - } - } - - // dlpi_name is NULL or empty if the ELF file is executable, return 0 - // so dll_address_to_library_name() can fall through to use dladdr() which - // can figure out executable name from argv[0]. - if (found && info->dlpi_name && info->dlpi_name[0]) { - d->base = libbase; - if (d->fname) { - jio_snprintf(d->fname, d->buflen, "%s", info->dlpi_name); - } - return 1; - } - return 0; -} - -bool os::dll_address_to_library_name(address addr, char* buf, - int buflen, int* offset) { - Dl_info dlinfo; - struct _address_to_library_name data; - - // There is a bug in old glibc dladdr() implementation that it could resolve - // to wrong library name if the .so file has a base address != NULL. Here - // we iterate through the program headers of all loaded libraries to find - // out which library 'addr' really belongs to. This workaround can be - // removed once the minimum requirement for glibc is moved to 2.3.x. - data.addr = addr; - data.fname = buf; - data.buflen = buflen; - data.base = NULL; - int rslt = dl_iterate_phdr(address_to_library_name_callback, (void *)&data); - - if (rslt) { - // buf already contains library name - if (offset) *offset = addr - data.base; - return true; - } else if (dladdr((void*)addr, &dlinfo)){ - if (buf) jio_snprintf(buf, buflen, "%s", dlinfo.dli_fname); - if (offset) *offset = addr - (address)dlinfo.dli_fbase; - return true; - } else { - if (buf) buf[0] = '\0'; - if (offset) *offset = -1; - return false; - } -} -#endif - - // Loads .dll/.so and - // in case of error it checks if .dll/.so was built for the - // same architecture as Hotspot is running on + +// Loads .dll/.so and +// in case of error it checks if .dll/.so was built for the +// same architecture as Hotspot is running on #ifdef __APPLE__ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { @@ -2292,7 +1536,6 @@ void os::print_dll_info(outputStream *st) { st->print_cr("Dynamic libraries:"); -#ifdef _ALLBSD_SOURCE #ifdef RTLD_DI_LINKMAP Dl_info dli; void *handle; @@ -2336,16 +1579,6 @@ #else st->print_cr("Error: Cannot print dynamic libraries."); #endif -#else - char fname[32]; - pid_t pid = os::Bsd::gettid(); - - jio_snprintf(fname, sizeof(fname), "/proc/%d/maps", pid); - - if (!_print_ascii_file(fname, st)) { - st->print("Can not get library information for pid = %d\n", pid); - } -#endif } void os::print_os_info_brief(outputStream* st) { @@ -2374,22 +1607,10 @@ st->print("Memory:"); st->print(" %dk page", os::vm_page_size()>>10); -#ifndef _ALLBSD_SOURCE - // values in struct sysinfo are "unsigned long" - struct sysinfo si; - sysinfo(&si); -#endif - st->print(", physical " UINT64_FORMAT "k", os::physical_memory() >> 10); st->print("(" UINT64_FORMAT "k free)", os::available_memory() >> 10); -#ifndef _ALLBSD_SOURCE - st->print(", swap " UINT64_FORMAT "k", - ((jlong)si.totalswap * si.mem_unit) >> 10); - st->print("(" UINT64_FORMAT "k free)", - ((jlong)si.freeswap * si.mem_unit) >> 10); -#endif st->cr(); // meminfo @@ -2786,42 +2007,13 @@ #endif } -#ifndef _ALLBSD_SOURCE -// Define MAP_HUGETLB here so we can build HotSpot on old systems. -#ifndef MAP_HUGETLB -#define MAP_HUGETLB 0x40000 -#endif - -// Define MADV_HUGEPAGE here so we can build HotSpot on old systems. -#ifndef MADV_HUGEPAGE -#define MADV_HUGEPAGE 14 -#endif -#endif bool os::pd_commit_memory(char* addr, size_t size, size_t alignment_hint, bool exec) { -#ifndef _ALLBSD_SOURCE - if (UseHugeTLBFS && alignment_hint > (size_t)vm_page_size()) { - int prot = exec ? PROT_READ|PROT_WRITE|PROT_EXEC : PROT_READ|PROT_WRITE; - uintptr_t res = - (uintptr_t) ::mmap(addr, size, prot, - MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS|MAP_HUGETLB, - -1, 0); - return res != (uintptr_t) MAP_FAILED; - } -#endif - return commit_memory(addr, size, exec); } void os::pd_realign_memory(char *addr, size_t bytes, size_t alignment_hint) { -#ifndef _ALLBSD_SOURCE - if (UseHugeTLBFS && alignment_hint > (size_t)vm_page_size()) { - // We don't check the return value: madvise(MADV_HUGEPAGE) may not - // be supported or the memory may already be backed by huge pages. - ::madvise(addr, bytes, MADV_HUGEPAGE); - } -#endif } void os::pd_free_memory(char *addr, size_t bytes, size_t alignment_hint) { @@ -2860,111 +2052,6 @@ return end; } -#ifndef _ALLBSD_SOURCE -// Something to do with the numa-aware allocator needs these symbols -extern "C" JNIEXPORT void numa_warn(int number, char *where, ...) { } -extern "C" JNIEXPORT void numa_error(char *where) { } -extern "C" JNIEXPORT int fork1() { return fork(); } - - -// If we are running with libnuma version > 2, then we should -// be trying to use symbols with versions 1.1 -// If we are running with earlier version, which did not have symbol versions, -// we should use the base version. -void* os::Bsd::libnuma_dlsym(void* handle, const char *name) { - void *f = dlvsym(handle, name, "libnuma_1.1"); - if (f == NULL) { - f = dlsym(handle, name); - } - return f; -} - -bool os::Bsd::libnuma_init() { - // sched_getcpu() should be in libc. - set_sched_getcpu(CAST_TO_FN_PTR(sched_getcpu_func_t, - dlsym(RTLD_DEFAULT, "sched_getcpu"))); - - if (sched_getcpu() != -1) { // Does it work? - void *handle = dlopen("libnuma.so.1", RTLD_LAZY); - if (handle != NULL) { - set_numa_node_to_cpus(CAST_TO_FN_PTR(numa_node_to_cpus_func_t, - libnuma_dlsym(handle, "numa_node_to_cpus"))); - set_numa_max_node(CAST_TO_FN_PTR(numa_max_node_func_t, - libnuma_dlsym(handle, "numa_max_node"))); - set_numa_available(CAST_TO_FN_PTR(numa_available_func_t, - libnuma_dlsym(handle, "numa_available"))); - set_numa_tonode_memory(CAST_TO_FN_PTR(numa_tonode_memory_func_t, - libnuma_dlsym(handle, "numa_tonode_memory"))); - set_numa_interleave_memory(CAST_TO_FN_PTR(numa_interleave_memory_func_t, - libnuma_dlsym(handle, "numa_interleave_memory"))); - - - if (numa_available() != -1) { - set_numa_all_nodes((unsigned long*)libnuma_dlsym(handle, "numa_all_nodes")); - // Create a cpu -> node mapping - _cpu_to_node = new (ResourceObj::C_HEAP) GrowableArray(0, true); - rebuild_cpu_to_node_map(); - return true; - } - } - } - return false; -} - -// rebuild_cpu_to_node_map() constructs a table mapping cpud id to node id. -// The table is later used in get_node_by_cpu(). -void os::Bsd::rebuild_cpu_to_node_map() { - const size_t NCPUS = 32768; // Since the buffer size computation is very obscure - // in libnuma (possible values are starting from 16, - // and continuing up with every other power of 2, but less - // than the maximum number of CPUs supported by kernel), and - // is a subject to change (in libnuma version 2 the requirements - // are more reasonable) we'll just hardcode the number they use - // in the library. - const size_t BitsPerCLong = sizeof(long) * CHAR_BIT; - - size_t cpu_num = os::active_processor_count(); - size_t cpu_map_size = NCPUS / BitsPerCLong; - size_t cpu_map_valid_size = - MIN2((cpu_num + BitsPerCLong - 1) / BitsPerCLong, cpu_map_size); - - cpu_to_node()->clear(); - cpu_to_node()->at_grow(cpu_num - 1); - size_t node_num = numa_get_groups_num(); - - unsigned long *cpu_map = NEW_C_HEAP_ARRAY(unsigned long, cpu_map_size); - for (size_t i = 0; i < node_num; i++) { - if (numa_node_to_cpus(i, cpu_map, cpu_map_size * sizeof(unsigned long)) != -1) { - for (size_t j = 0; j < cpu_map_valid_size; j++) { - if (cpu_map[j] != 0) { - for (size_t k = 0; k < BitsPerCLong; k++) { - if (cpu_map[j] & (1UL << k)) { - cpu_to_node()->at_put(j * BitsPerCLong + k, i); - } - } - } - } - } - } - FREE_C_HEAP_ARRAY(unsigned long, cpu_map); -} - -int os::Bsd::get_node_by_cpu(int cpu_id) { - if (cpu_to_node() != NULL && cpu_id >= 0 && cpu_id < cpu_to_node()->length()) { - return cpu_to_node()->at(cpu_id); - } - return -1; -} - -GrowableArray* os::Bsd::_cpu_to_node; -os::Bsd::sched_getcpu_func_t os::Bsd::_sched_getcpu; -os::Bsd::numa_node_to_cpus_func_t os::Bsd::_numa_node_to_cpus; -os::Bsd::numa_max_node_func_t os::Bsd::_numa_max_node; -os::Bsd::numa_available_func_t os::Bsd::_numa_available; -os::Bsd::numa_tonode_memory_func_t os::Bsd::_numa_tonode_memory; -os::Bsd::numa_interleave_memory_func_t os::Bsd::_numa_interleave_memory; -unsigned long* os::Bsd::_numa_all_nodes; -#endif bool os::pd_uncommit_memory(char* addr, size_t size) { #ifdef __OpenBSD__ @@ -3084,42 +2171,7 @@ } bool os::Bsd::hugetlbfs_sanity_check(bool warn, size_t page_size) { - bool result = false; -#ifndef _ALLBSD_SOURCE - void *p = mmap (NULL, page_size, PROT_READ|PROT_WRITE, - MAP_ANONYMOUS|MAP_PRIVATE|MAP_HUGETLB, - -1, 0); - - if (p != (void *) -1) { - // We don't know if this really is a huge page or not. - FILE *fp = fopen("/proc/self/maps", "r"); - if (fp) { - while (!feof(fp)) { - char chars[257]; - long x = 0; - if (fgets(chars, sizeof(chars), fp)) { - if (sscanf(chars, "%lx-%*x", &x) == 1 - && x == (long)p) { - if (strstr (chars, "hugepage")) { - result = true; - break; - } - } - } - } - fclose(fp); - } - munmap (p, page_size); - if (result) - return true; - } - - if (warn) { - warning("HugeTLBFS is not supported by the operating system."); - } -#endif - - return result; + return false; } /* @@ -3164,92 +2216,8 @@ static size_t _large_page_size = 0; void os::large_page_init() { -#ifndef _ALLBSD_SOURCE - if (!UseLargePages) { - UseHugeTLBFS = false; - UseSHM = false; - return; - } - - if (FLAG_IS_DEFAULT(UseHugeTLBFS) && FLAG_IS_DEFAULT(UseSHM)) { - // If UseLargePages is specified on the command line try both methods, - // if it's default, then try only HugeTLBFS. - if (FLAG_IS_DEFAULT(UseLargePages)) { - UseHugeTLBFS = true; - } else { - UseHugeTLBFS = UseSHM = true; - } - } - - if (LargePageSizeInBytes) { - _large_page_size = LargePageSizeInBytes; - } else { - // large_page_size on Bsd is used to round up heap size. x86 uses either - // 2M or 4M page, depending on whether PAE (Physical Address Extensions) - // mode is enabled. AMD64/EM64T uses 2M page in 64bit mode. IA64 can use - // page as large as 256M. - // - // Here we try to figure out page size by parsing /proc/meminfo and looking - // for a line with the following format: - // Hugepagesize: 2048 kB - // - // If we can't determine the value (e.g. /proc is not mounted, or the text - // format has been changed), we'll use the largest page size supported by - // the processor. - -#ifndef ZERO - _large_page_size = IA32_ONLY(4 * M) AMD64_ONLY(2 * M) IA64_ONLY(256 * M) SPARC_ONLY(4 * M) - ARM_ONLY(2 * M) PPC_ONLY(4 * M); -#endif // ZERO - - FILE *fp = fopen("/proc/meminfo", "r"); - if (fp) { - while (!feof(fp)) { - int x = 0; - char buf[16]; - if (fscanf(fp, "Hugepagesize: %d", &x) == 1) { - if (x && fgets(buf, sizeof(buf), fp) && strcmp(buf, " kB\n") == 0) { - _large_page_size = x * K; - break; - } - } else { - // skip to next line - for (;;) { - int ch = fgetc(fp); - if (ch == EOF || ch == (int)'\n') break; - } - } - } - fclose(fp); - } - } - - // print a warning if any large page related flag is specified on command line - bool warn_on_failure = !FLAG_IS_DEFAULT(UseHugeTLBFS); - - const size_t default_page_size = (size_t)Bsd::page_size(); - if (_large_page_size > default_page_size) { - _page_sizes[0] = _large_page_size; - _page_sizes[1] = default_page_size; - _page_sizes[2] = 0; - } - UseHugeTLBFS = UseHugeTLBFS && - Bsd::hugetlbfs_sanity_check(warn_on_failure, _large_page_size); - - if (UseHugeTLBFS) - UseSHM = false; - - UseLargePages = UseHugeTLBFS || UseSHM; - - set_coredump_filter(); -#endif } -#ifndef _ALLBSD_SOURCE -#ifndef SHM_HUGETLB -#define SHM_HUGETLB 04000 -#endif -#endif char* os::reserve_memory_special(size_t bytes, char* req_addr, bool exec) { // "exec" is passed in but not used. Creating the shared image for @@ -3267,11 +2235,7 @@ // Create a large shared memory region to attach to based on size. // Currently, size is the total size of the heap -#ifndef _ALLBSD_SOURCE - int shmid = shmget(key, bytes, SHM_HUGETLB|IPC_CREAT|SHM_R|SHM_W); -#else int shmid = shmget(key, bytes, IPC_CREAT|SHM_R|SHM_W); -#endif if (shmid == -1) { // Possible reasons for shmget failure: // 1. shmmax is too small for Java heap. @@ -3558,7 +2522,7 @@ // this reason, the code should not be used as default (ThreadPriorityPolicy=0). // It is only used when ThreadPriorityPolicy=1 and requires root privilege. -#if defined(_ALLBSD_SOURCE) && !defined(__APPLE__) +#if !defined(__APPLE__) int os::java_to_os_priority[CriticalPriority + 1] = { 19, // 0 Entry should never be used @@ -3578,7 +2542,7 @@ 31 // 11 CriticalPriority }; -#elif defined(__APPLE__) +#else /* Using Mach high-level priority assignments */ int os::java_to_os_priority[CriticalPriority + 1] = { 0, // 0 Entry should never be used (MINPRI_USER) @@ -3599,26 +2563,6 @@ 36 // 11 CriticalPriority }; -#else -int os::java_to_os_priority[CriticalPriority + 1] = { - 19, // 0 Entry should never be used - - 4, // 1 MinPriority - 3, // 2 - 2, // 3 - - 1, // 4 - 0, // 5 NormPriority - -1, // 6 - - -2, // 7 - -3, // 8 - -4, // 9 NearMaxPriority - - -5, // 10 MaxPriority - - -5 // 11 CriticalPriority -}; #endif static int prio_init() { @@ -4179,22 +3123,6 @@ } } -#ifndef _ALLBSD_SOURCE -// This is the fastest way to get thread cpu time on Bsd. -// Returns cpu time (user+sys) for any thread, not only for current. -// POSIX compliant clocks are implemented in the kernels 2.6.16+. -// It might work on 2.6.10+ with a special kernel/glibc patch. -// For reference, please, see IEEE Std 1003.1-2004: -// http://www.unix.org/single_unix_specification - -jlong os::Bsd::fast_thread_cpu_time(clockid_t clockid) { - struct timespec tp; - int rc = os::Bsd::clock_gettime(clockid, &tp); - assert(rc == 0, "clock_gettime is expected to return 0 code"); - - return (tp.tv_sec * NANOSECS_PER_SEC) + tp.tv_nsec; -} -#endif ///// // glibc on Bsd platform uses non-documented flag @@ -4458,10 +3386,6 @@ // this is called _after_ the global arguments have been parsed jint os::init_2(void) { -#ifndef _ALLBSD_SOURCE - Bsd::fast_thread_clock_init(); -#endif - // Allocate a single page and mark it as readable for safepoint polling address polling_page = (address) ::mmap(NULL, Bsd::page_size(), PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); guarantee( polling_page != MAP_FAILED, "os::init_2: failed to allocate polling page" ); @@ -4518,48 +3442,6 @@ JavaThread::set_stack_size_at_create(round_to(threadStackSizeInBytes, vm_page_size())); -#ifndef _ALLBSD_SOURCE - Bsd::capture_initial_stack(JavaThread::stack_size_at_create()); - - Bsd::libpthread_init(); - if (PrintMiscellaneous && (Verbose || WizardMode)) { - tty->print_cr("[HotSpot is running with %s, %s(%s)]\n", - Bsd::glibc_version(), Bsd::libpthread_version(), - Bsd::is_floating_stack() ? "floating stack" : "fixed stack"); - } - - if (UseNUMA) { - if (!Bsd::libnuma_init()) { - UseNUMA = false; - } else { - if ((Bsd::numa_max_node() < 1)) { - // There's only one node(they start from 0), disable NUMA. - UseNUMA = false; - } - } - // With SHM large pages we cannot uncommit a page, so there's not way - // we can make the adaptive lgrp chunk resizing work. If the user specified - // both UseNUMA and UseLargePages (or UseSHM) on the command line - warn and - // disable adaptive resizing. - if (UseNUMA && UseLargePages && UseSHM) { - if (!FLAG_IS_DEFAULT(UseNUMA)) { - if (FLAG_IS_DEFAULT(UseLargePages) && FLAG_IS_DEFAULT(UseSHM)) { - UseLargePages = false; - } else { - warning("UseNUMA is not fully compatible with SHM large pages, disabling adaptive resizing"); - UseAdaptiveSizePolicy = false; - UseAdaptiveNUMAChunkSizing = false; - } - } else { - UseNUMA = false; - } - } - if (!UseNUMA && ForceNUMA) { - UseNUMA = true; - } - } -#endif - if (MaxFDLimit) { // set the number of file descriptors to max. print out error // if getrlimit/setrlimit fails but continue regardless. @@ -4586,11 +3468,6 @@ } } -#ifndef _ALLBSD_SOURCE - // Initialize lock used to serialize thread creation (see os::create_thread) - Bsd::set_createThread_lock(new Mutex(Mutex::leaf, "createThread_lock", false)); -#endif - // at-exit methods are called in the reverse order of their registration. // atexit functions are called on return from main or as a result of a // call to exit(3C). There can be only 32 of these functions registered @@ -4641,15 +3518,7 @@ }; int os::active_processor_count() { -#ifdef _ALLBSD_SOURCE return _processor_count; -#else - // Bsd doesn't yet have a (official) notion of processor sets, - // so just return the number of online processors. - int online_cpus = ::sysconf(_SC_NPROCESSORS_ONLN); - assert(online_cpus > 0 && online_cpus <= processor_count(), "sanity check"); - return online_cpus; -#endif } void os::set_native_thread_name(const char *name) { @@ -4703,25 +3572,7 @@ int os::Bsd::safe_cond_timedwait(pthread_cond_t *_cond, pthread_mutex_t *_mutex, const struct timespec *_abstime) { -#ifdef _ALLBSD_SOURCE return pthread_cond_timedwait(_cond, _mutex, _abstime); -#else - if (is_NPTL()) { - return pthread_cond_timedwait(_cond, _mutex, _abstime); - } else { -#ifndef IA64 - // 6292965: BsdThreads pthread_cond_timedwait() resets FPU control - // word back to default 64bit precision if condvar is signaled. Java - // wants 53bit precision. Save and restore current value. - int fpu = get_fpu_control_word(); -#endif // IA64 - int status = pthread_cond_timedwait(_cond, _mutex, _abstime); -#ifndef IA64 - set_fpu_control_word(fpu); -#endif // IA64 - return status; - } -#endif } //////////////////////////////////////////////////////////////////////////////// @@ -5041,20 +3892,6 @@ return munmap(addr, bytes) == 0; } -#ifndef _ALLBSD_SOURCE -static jlong slow_thread_cpu_time(Thread *thread, bool user_sys_cpu_time); - -static clockid_t thread_cpu_clockid(Thread* thread) { - pthread_t tid = thread->osthread()->pthread_id(); - clockid_t clockid; - - // Get thread clockid - int rc = os::Bsd::pthread_getcpuclockid(tid, &clockid); - assert(rc == 0, "pthread_getcpuclockid is expected to return 0 code"); - return clockid; -} -#endif - // current_thread_cpu_time(bool) and thread_cpu_time(Thread*, bool) // are used by JVM M&M and JVMTI to get user+sys or user CPU time // of a thread. @@ -5065,36 +3902,15 @@ jlong os::current_thread_cpu_time() { #ifdef __APPLE__ return os::thread_cpu_time(Thread::current(), true /* user + sys */); -#elif !defined(_ALLBSD_SOURCE) - if (os::Bsd::supports_fast_thread_cpu_time()) { - return os::Bsd::fast_thread_cpu_time(CLOCK_THREAD_CPUTIME_ID); - } else { - // return user + sys since the cost is the same - return slow_thread_cpu_time(Thread::current(), true /* user + sys */); - } #endif } jlong os::thread_cpu_time(Thread* thread) { -#ifndef _ALLBSD_SOURCE - // consistent with what current_thread_cpu_time() returns - if (os::Bsd::supports_fast_thread_cpu_time()) { - return os::Bsd::fast_thread_cpu_time(thread_cpu_clockid(thread)); - } else { - return slow_thread_cpu_time(thread, true /* user + sys */); - } -#endif } jlong os::current_thread_cpu_time(bool user_sys_cpu_time) { #ifdef __APPLE__ return os::thread_cpu_time(Thread::current(), user_sys_cpu_time); -#elif !defined(_ALLBSD_SOURCE) - if (user_sys_cpu_time && os::Bsd::supports_fast_thread_cpu_time()) { - return os::Bsd::fast_thread_cpu_time(CLOCK_THREAD_CPUTIME_ID); - } else { - return slow_thread_cpu_time(Thread::current(), user_sys_cpu_time); - } #endif } @@ -5118,106 +3934,9 @@ } else { return ((jlong)tinfo.user_time.seconds * 1000000000) + ((jlong)tinfo.user_time.microseconds * (jlong)1000); } -#elif !defined(_ALLBSD_SOURCE) - if (user_sys_cpu_time && os::Bsd::supports_fast_thread_cpu_time()) { - return os::Bsd::fast_thread_cpu_time(thread_cpu_clockid(thread)); - } else { - return slow_thread_cpu_time(thread, user_sys_cpu_time); - } #endif } -#ifndef _ALLBSD_SOURCE -// -// -1 on error. -// - -static jlong slow_thread_cpu_time(Thread *thread, bool user_sys_cpu_time) { - static bool proc_pid_cpu_avail = true; - static bool proc_task_unchecked = true; - static const char *proc_stat_path = "/proc/%d/stat"; - pid_t tid = thread->osthread()->thread_id(); - int i; - char *s; - char stat[2048]; - int statlen; - char proc_name[64]; - int count; - long sys_time, user_time; - char string[64]; - char cdummy; - int idummy; - long ldummy; - FILE *fp; - - // We first try accessing /proc//cpu since this is faster to - // process. If this file is not present (bsd kernels 2.5 and above) - // then we open /proc//stat. - if ( proc_pid_cpu_avail ) { - sprintf(proc_name, "/proc/%d/cpu", tid); - fp = fopen(proc_name, "r"); - if ( fp != NULL ) { - count = fscanf( fp, "%s %lu %lu\n", string, &user_time, &sys_time); - fclose(fp); - if ( count != 3 ) return -1; - - if (user_sys_cpu_time) { - return ((jlong)sys_time + (jlong)user_time) * (1000000000 / clock_tics_per_sec); - } else { - return (jlong)user_time * (1000000000 / clock_tics_per_sec); - } - } - else proc_pid_cpu_avail = false; - } - - // The /proc//stat aggregates per-process usage on - // new Bsd kernels 2.6+ where NPTL is supported. - // The /proc/self/task//stat still has the per-thread usage. - // See bug 6328462. - // There can be no directory /proc/self/task on kernels 2.4 with NPTL - // and possibly in some other cases, so we check its availability. - if (proc_task_unchecked && os::Bsd::is_NPTL()) { - // This is executed only once - proc_task_unchecked = false; - fp = fopen("/proc/self/task", "r"); - if (fp != NULL) { - proc_stat_path = "/proc/self/task/%d/stat"; - fclose(fp); - } - } - - sprintf(proc_name, proc_stat_path, tid); - fp = fopen(proc_name, "r"); - if ( fp == NULL ) return -1; - statlen = fread(stat, 1, 2047, fp); - stat[statlen] = '\0'; - fclose(fp); - - // Skip pid and the command string. Note that we could be dealing with - // weird command names, e.g. user could decide to rename java launcher - // to "java 1.4.2 :)", then the stat file would look like - // 1234 (java 1.4.2 :)) R ... ... - // We don't really need to know the command string, just find the last - // occurrence of ")" and then start parsing from there. See bug 4726580. - s = strrchr(stat, ')'); - i = 0; - if (s == NULL ) return -1; - - // Skip blank chars - do s++; while (isspace(*s)); - - count = sscanf(s,"%c %d %d %d %d %d %lu %lu %lu %lu %lu %lu %lu", - &cdummy, &idummy, &idummy, &idummy, &idummy, &idummy, - &ldummy, &ldummy, &ldummy, &ldummy, &ldummy, - &user_time, &sys_time); - if ( count != 13 ) return -1; - if (user_sys_cpu_time) { - return ((jlong)sys_time + (jlong)user_time) * (1000000000 / clock_tics_per_sec); - } else { - return (jlong)user_time * (1000000000 / clock_tics_per_sec); - } -} -#endif void os::current_thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits @@ -5236,10 +3955,8 @@ bool os::is_thread_cpu_time_supported() { #ifdef __APPLE__ return true; -#elif defined(_ALLBSD_SOURCE) +#else return false; -#else - return true; #endif } diff -r 69ad7823b1ca -r 8940ddc1036f src/os/bsd/vm/os_bsd.hpp --- a/src/os/bsd/vm/os_bsd.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/os/bsd/vm/os_bsd.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -56,19 +56,6 @@ static int sigflags[MAXSIGNUM]; static int (*_clock_gettime)(clockid_t, struct timespec *); -#ifndef _ALLBSD_SOURCE - static int (*_pthread_getcpuclockid)(pthread_t, clockid_t *); - - static address _initial_thread_stack_bottom; - static uintptr_t _initial_thread_stack_size; - - static const char *_glibc_version; - static const char *_libpthread_version; - - static bool _is_floating_stack; - static bool _is_NPTL; - static bool _supports_fast_thread_cpu_time; -#endif static GrowableArray* _cpu_to_node; @@ -76,28 +63,14 @@ static julong _physical_memory; static pthread_t _main_thread; -#ifndef _ALLBSD_SOURCE - static Mutex* _createThread_lock; -#endif static int _page_size; static julong available_memory(); static julong physical_memory() { return _physical_memory; } static void initialize_system_info(); -#ifndef _ALLBSD_SOURCE - static void set_glibc_version(const char *s) { _glibc_version = s; } - static void set_libpthread_version(const char *s) { _libpthread_version = s; } -#endif - static bool supports_variable_stack_size(); -#ifndef _ALLBSD_SOURCE - static void set_is_NPTL() { _is_NPTL = true; } - static void set_is_BsdThreads() { _is_NPTL = false; } - static void set_is_floating_stack() { _is_floating_stack = true; } -#endif - static void rebuild_cpu_to_node_map(); static GrowableArray* cpu_to_node() { return _cpu_to_node; } @@ -106,25 +79,10 @@ public: static void init_thread_fpu_state(); -#ifndef _ALLBSD_SOURCE - static int get_fpu_control_word(); - static void set_fpu_control_word(int fpu_control); -#endif static pthread_t main_thread(void) { return _main_thread; } -#ifndef _ALLBSD_SOURCE - // returns kernel thread id (similar to LWP id on Solaris), which can be - // used to access /proc - static pid_t gettid(); - static void set_createThread_lock(Mutex* lk) { _createThread_lock = lk; } - static Mutex* createThread_lock(void) { return _createThread_lock; } -#endif static void hotspot_sigmask(Thread* thread); -#ifndef _ALLBSD_SOURCE - static address initial_thread_stack_bottom(void) { return _initial_thread_stack_bottom; } - static uintptr_t initial_thread_stack_size(void) { return _initial_thread_stack_size; } -#endif static bool is_initial_thread(void); static int page_size(void) { return _page_size; } @@ -161,23 +119,6 @@ static struct sigaction *get_chained_signal_action(int sig); static bool chained_handler(int sig, siginfo_t* siginfo, void* context); -#ifndef _ALLBSD_SOURCE - // GNU libc and libpthread version strings - static const char *glibc_version() { return _glibc_version; } - static const char *libpthread_version() { return _libpthread_version; } - - // NPTL or BsdThreads? - static bool is_BsdThreads() { return !_is_NPTL; } - static bool is_NPTL() { return _is_NPTL; } - - // NPTL is always floating stack. BsdThreads could be using floating - // stack or fixed stack. - static bool is_floating_stack() { return _is_floating_stack; } - - static void libpthread_init(); - static bool libnuma_init(); - static void* libnuma_dlsym(void* handle, const char* name); -#endif // Minimum stack size a thread can be created with (allowing // the VM to completely create the thread and enter user code) static size_t min_stack_allowed; @@ -186,22 +127,9 @@ static size_t default_stack_size(os::ThreadType thr_type); static size_t default_guard_size(os::ThreadType thr_type); -#ifndef _ALLBSD_SOURCE - static void capture_initial_stack(size_t max_size); - - // Stack overflow handling - static bool manually_expand_stack(JavaThread * t, address addr); - static int max_register_window_saves_before_flushing(); -#endif - // Real-time clock functions static void clock_init(void); -#ifndef _ALLBSD_SOURCE - // fast POSIX clocks support - static void fast_thread_clock_init(void); -#endif - static inline bool supports_monotonic_clock() { return _clock_gettime != NULL; } @@ -210,18 +138,6 @@ return _clock_gettime ? _clock_gettime(clock_id, tp) : -1; } -#ifndef _ALLBSD_SOURCE - static int pthread_getcpuclockid(pthread_t tid, clockid_t *clock_id) { - return _pthread_getcpuclockid ? _pthread_getcpuclockid(tid, clock_id) : -1; - } - - static bool supports_fast_thread_cpu_time() { - return _supports_fast_thread_cpu_time; - } - - static jlong fast_thread_cpu_time(clockid_t clockid); -#endif - // Stack repair handling // none present diff -r 69ad7823b1ca -r 8940ddc1036f src/os_cpu/bsd_x86/vm/bytes_bsd_x86.inline.hpp --- a/src/os_cpu/bsd_x86/vm/bytes_bsd_x86.inline.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/os_cpu/bsd_x86/vm/bytes_bsd_x86.inline.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -25,10 +25,6 @@ #ifndef OS_CPU_BSD_X86_VM_BYTES_BSD_X86_INLINE_HPP #define OS_CPU_BSD_X86_VM_BYTES_BSD_X86_INLINE_HPP -#ifndef _ALLBSD_SOURCE -#include -#endif - #ifdef __APPLE__ #include #endif diff -r 69ad7823b1ca -r 8940ddc1036f src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp --- a/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -76,7 +76,7 @@ # include #endif -#if defined(_ALLBSD_SOURCE) && !defined(__APPLE__) && !defined(__NetBSD__) +#if !defined(__APPLE__) && !defined(__NetBSD__) # include #endif @@ -489,23 +489,6 @@ // to handle_unexpected_exception way down below. thread->disable_stack_red_zone(); tty->print_raw_cr("An irrecoverable stack overflow has occurred."); -#ifndef _ALLBSD_SOURCE - } else { - // Accessing stack address below sp may cause SEGV if current - // thread has MAP_GROWSDOWN stack. This should only happen when - // current thread was created by user code with MAP_GROWSDOWN flag - // and then attached to VM. See notes in os_bsd.cpp. - if (thread->osthread()->expanding_stack() == 0) { - thread->osthread()->set_expanding_stack(); - if (os::Bsd::manually_expand_stack(thread, addr)) { - thread->osthread()->clear_expanding_stack(); - return 1; - } - thread->osthread()->clear_expanding_stack(); - } else { - fatal("recursive segv. expanding stack."); - } -#endif } } } @@ -744,61 +727,21 @@ ShouldNotReachHere(); } -#ifdef _ALLBSD_SOURCE // From solaris_i486.s ported to bsd_i486.s extern "C" void fixcw(); -#endif void os::Bsd::init_thread_fpu_state(void) { #ifndef AMD64 -# ifdef _ALLBSD_SOURCE // Set fpu to 53 bit precision. This happens too early to use a stub. fixcw(); -# else - // set fpu to 53 bit precision - set_fpu_control_word(0x27f); -# endif #endif // !AMD64 } -#ifndef _ALLBSD_SOURCE -int os::Bsd::get_fpu_control_word(void) { -#ifdef AMD64 - return 0; -#else - int fpu_control; - _FPU_GETCW(fpu_control); - return fpu_control & 0xffff; -#endif // AMD64 -} - -void os::Bsd::set_fpu_control_word(int fpu_control) { -#ifndef AMD64 - _FPU_SETCW(fpu_control); -#endif // !AMD64 -} -#endif // Check that the bsd kernel version is 2.4 or higher since earlier // versions do not support SSE without patches. bool os::supports_sse() { -#if defined(AMD64) || defined(_ALLBSD_SOURCE) return true; -#else - struct utsname uts; - if( uname(&uts) != 0 ) return false; // uname fails? - char *minor_string; - int major = strtol(uts.release,&minor_string,10); - int minor = strtol(minor_string+1,NULL,10); - bool result = (major > 2 || (major==2 && minor >= 4)); -#ifndef PRODUCT - if (PrintMiscellaneous && Verbose) { - tty->print("OS version is %d.%d, which %s support SSE/SSE2\n", - major,minor, result ? "DOES" : "does NOT"); - } -#endif - return result; -#endif // AMD64 } bool os::is_allocatable(size_t bytes) { @@ -836,46 +779,7 @@ #define GET_GS() ({int gs; __asm__ volatile("movw %%gs, %w0":"=q"(gs)); gs&0xffff;}) #endif -#ifdef _ALLBSD_SOURCE bool os::Bsd::supports_variable_stack_size() { return true; } -#else -// Test if pthread library can support variable thread stack size. BsdThreads -// in fixed stack mode allocates 2M fixed slot for each thread. BsdThreads -// in floating stack mode and NPTL support variable stack size. -bool os::Bsd::supports_variable_stack_size() { - if (os::Bsd::is_NPTL()) { - // NPTL, yes - return true; - - } else { - // Note: We can't control default stack size when creating a thread. - // If we use non-default stack size (pthread_attr_setstacksize), both - // floating stack and non-floating stack BsdThreads will return the - // same value. This makes it impossible to implement this function by - // detecting thread stack size directly. - // - // An alternative approach is to check %gs. Fixed-stack BsdThreads - // do not use %gs, so its value is 0. Floating-stack BsdThreads use - // %gs (either as LDT selector or GDT selector, depending on kernel) - // to access thread specific data. - // - // Note that %gs is a reserved glibc register since early 2001, so - // applications are not allowed to change its value (Ulrich Drepper from - // Redhat confirmed that all known offenders have been modified to use - // either %fs or TSD). In the worst case scenario, when VM is embedded in - // a native application that plays with %gs, we might see non-zero %gs - // even BsdThreads is running in fixed stack mode. As the result, we'll - // return true and skip _thread_safety_check(), so we may not be able to - // detect stack-heap collisions. But otherwise it's harmless. - // -#ifdef __GNUC__ - return (GET_GS() != 0); -#else - return false; -#endif - } -} -#endif #endif // AMD64 // return default stack size for thr_type @@ -943,7 +847,7 @@ *bottom = (address)((char *)ss.ss_sp - ss.ss_size); *size = ss.ss_size; -#elif defined(_ALLBSD_SOURCE) +#else pthread_attr_t attr; int rslt = pthread_attr_init(&attr); @@ -963,33 +867,6 @@ } pthread_attr_destroy(&attr); -#else - if (os::Bsd::is_initial_thread()) { - // initial thread needs special handling because pthread_getattr_np() - // may return bogus value. - *bottom = os::Bsd::initial_thread_stack_bottom(); - *size = os::Bsd::initial_thread_stack_size(); - } else { - pthread_attr_t attr; - - int rslt = pthread_getattr_np(pthread_self(), &attr); - - // JVM needs to know exact stack location, abort if it fails - if (rslt != 0) { - if (rslt == ENOMEM) { - vm_exit_out_of_memory(0, "pthread_getattr_np"); - } else { - fatal(err_msg("pthread_getattr_np failed with errno = %d", rslt)); - } - } - - if (pthread_attr_getstack(&attr, (void **)bottom, size) != 0) { - fatal("Can not locate current stack attributes!"); - } - - pthread_attr_destroy(&attr); - - } #endif assert(os::current_stack_pointer() >= *bottom && os::current_stack_pointer() < *bottom + *size, "just checking"); diff -r 69ad7823b1ca -r 8940ddc1036f src/os_cpu/bsd_zero/vm/os_bsd_zero.cpp --- a/src/os_cpu/bsd_zero/vm/os_bsd_zero.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/os_cpu/bsd_zero/vm/os_bsd_zero.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -23,7 +23,7 @@ * */ -#if defined(_ALLBSD_SOURCE) && !defined(__APPLE__) && !defined(__NetBSD__) +#if !defined(__APPLE__) && !defined(__NetBSD__) #include # include /* For pthread_attr_get_np */ #endif @@ -178,26 +178,6 @@ thread->disable_stack_red_zone(); ShouldNotCallThis(); } -#ifndef _ALLBSD_SOURCE - else { - // Accessing stack address below sp may cause SEGV if - // current thread has MAP_GROWSDOWN stack. This should - // only happen when current thread was created by user - // code with MAP_GROWSDOWN flag and then attached to VM. - // See notes in os_bsd.cpp. - if (thread->osthread()->expanding_stack() == 0) { - thread->osthread()->set_expanding_stack(); - if (os::Bsd::manually_expand_stack(thread, addr)) { - thread->osthread()->clear_expanding_stack(); - return true; - } - thread->osthread()->clear_expanding_stack(); - } - else { - fatal("recursive segv. expanding stack."); - } - } -#endif } } @@ -266,16 +246,6 @@ // Nothing to do } -#ifndef _ALLBSD_SOURCE -int os::Bsd::get_fpu_control_word() { - ShouldNotCallThis(); -} - -void os::Bsd::set_fpu_control_word(int fpu) { - ShouldNotCallThis(); -} -#endif - bool os::is_allocatable(size_t bytes) { #ifdef _LP64 return true; @@ -339,7 +309,7 @@ stack_top = (address) ss.ss_sp; stack_bytes = ss.ss_size; stack_bottom = stack_top - stack_bytes; -#elif defined(_ALLBSD_SOURCE) +#else pthread_attr_t attr; int rslt = pthread_attr_init(&attr); @@ -362,67 +332,6 @@ pthread_attr_destroy(&attr); stack_top = stack_bottom + stack_bytes; -#else /* Linux */ - pthread_attr_t attr; - int res = pthread_getattr_np(pthread_self(), &attr); - if (res != 0) { - if (res == ENOMEM) { - vm_exit_out_of_memory(0, "pthread_getattr_np"); - } - else { - fatal(err_msg("pthread_getattr_np failed with errno = " INT32_FORMAT, - res)); - } - } - - res = pthread_attr_getstack(&attr, (void **) &stack_bottom, &stack_bytes); - if (res != 0) { - fatal(err_msg("pthread_attr_getstack failed with errno = " INT32_FORMAT, - res)); - } - stack_top = stack_bottom + stack_bytes; - - // The block of memory returned by pthread_attr_getstack() includes - // guard pages where present. We need to trim these off. - size_t page_bytes = os::Bsd::page_size(); - assert(((intptr_t) stack_bottom & (page_bytes - 1)) == 0, "unaligned stack"); - - size_t guard_bytes; - res = pthread_attr_getguardsize(&attr, &guard_bytes); - if (res != 0) { - fatal(err_msg( - "pthread_attr_getguardsize failed with errno = " INT32_FORMAT, res)); - } - int guard_pages = align_size_up(guard_bytes, page_bytes) / page_bytes; - assert(guard_bytes == guard_pages * page_bytes, "unaligned guard"); - -#ifdef IA64 - // IA64 has two stacks sharing the same area of memory, a normal - // stack growing downwards and a register stack growing upwards. - // Guard pages, if present, are in the centre. This code splits - // the stack in two even without guard pages, though in theory - // there's nothing to stop us allocating more to the normal stack - // or more to the register stack if one or the other were found - // to grow faster. - int total_pages = align_size_down(stack_bytes, page_bytes) / page_bytes; - stack_bottom += (total_pages - guard_pages) / 2 * page_bytes; -#endif // IA64 - - stack_bottom += guard_bytes; - - pthread_attr_destroy(&attr); - - // The initial thread has a growable stack, and the size reported - // by pthread_attr_getstack is the maximum size it could possibly - // be given what currently mapped. This can be huge, so we cap it. - if (os::Bsd::is_initial_thread()) { - stack_bytes = stack_top - stack_bottom; - - if (stack_bytes > JavaThread::stack_size_at_create()) - stack_bytes = JavaThread::stack_size_at_create(); - - stack_bottom = stack_top - stack_bytes; - } #endif assert(os::current_stack_pointer() >= stack_bottom, "should do"); diff -r 69ad7823b1ca -r 8940ddc1036f src/share/tools/hsdis/hsdis-demo.c --- a/src/share/tools/hsdis/hsdis-demo.c Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/tools/hsdis/hsdis-demo.c Mon Nov 05 13:55:31 2012 -0800 @@ -85,9 +85,11 @@ #include "dlfcn.h" -#define DECODE_INSTRUCTIONS_NAME "decode_instructions_virtual" +#define DECODE_INSTRUCTIONS_VIRTUAL_NAME "decode_instructions_virtual" +#define DECODE_INSTRUCTIONS_NAME "decode_instructions" #define HSDIS_NAME "hsdis" static void* decode_instructions_pv = 0; +static void* decode_instructions_sv = 0; static const char* hsdis_path[] = { HSDIS_NAME"-"LIBARCH LIB_EXT, "./" HSDIS_NAME"-"LIBARCH LIB_EXT, @@ -101,11 +103,12 @@ void* dllib = NULL; const char* *next_in_path = hsdis_path; while (1) { - decode_instructions_pv = dlsym(dllib, DECODE_INSTRUCTIONS_NAME); - if (decode_instructions_pv != NULL) + decode_instructions_pv = dlsym(dllib, DECODE_INSTRUCTIONS_VIRTUAL_NAME); + decode_instructions_sv = dlsym(dllib, DECODE_INSTRUCTIONS_NAME); + if (decode_instructions_pv != NULL || decode_instructions_sv != NULL) return NULL; if (dllib != NULL) - return "plugin does not defined "DECODE_INSTRUCTIONS_NAME; + return "plugin does not defined "DECODE_INSTRUCTIONS_VIRTUAL_NAME" and "DECODE_INSTRUCTIONS_NAME; for (dllib = NULL; dllib == NULL; ) { const char* next_lib = (*next_in_path++); if (next_lib == NULL) @@ -213,20 +216,44 @@ printf("%s: %s\n", err, dlerror()); exit(1); } - printf("Decoding from %p to %p...\n", from, to); - decode_instructions_ftype decode_instructions - = (decode_instructions_ftype) decode_instructions_pv; + decode_func_vtype decode_instructions_v + = (decode_func_vtype) decode_instructions_pv; + decode_func_stype decode_instructions_s + = (decode_func_stype) decode_instructions_sv; void* res; - if (raw && xml) { - res = (*decode_instructions)(from, to, (unsigned char*)from, to - from, simple_handle_event, stdout, NULL, stdout, options); - } else if (raw) { - res = (*decode_instructions)(from, to, (unsigned char*)from, to - from, simple_handle_event, stdout, NULL, stdout, options); - } else { - res = (*decode_instructions)(from, to, (unsigned char*)from, to - from, - handle_event, (void*) event_cookie, - fprintf_callback, stdout, - options); + if (decode_instructions_pv != NULL) { + printf("\nDecoding from %p to %p...with %s\n", from, to, DECODE_INSTRUCTIONS_VIRTUAL_NAME); + if (raw) { + res = (*decode_instructions_v)(from, to, + (unsigned char*)from, to - from, + simple_handle_event, stdout, + NULL, stdout, + options, 0); + } else { + res = (*decode_instructions_v)(from, to, + (unsigned char*)from, to - from, + handle_event, (void*) event_cookie, + fprintf_callback, stdout, + options, 0); + } + if (res != (void*)to) + printf("*** Result was %p!\n", res); } - if (res != (void*)to) - printf("*** Result was %p!\n", res); + void* sres; + if (decode_instructions_sv != NULL) { + printf("\nDecoding from %p to %p...with old decode_instructions\n", from, to, DECODE_INSTRUCTIONS_NAME); + if (raw) { + sres = (*decode_instructions_s)(from, to, + simple_handle_event, stdout, + NULL, stdout, + options); + } else { + sres = (*decode_instructions_s)(from, to, + handle_event, (void*) event_cookie, + fprintf_callback, stdout, + options); + } + if (sres != (void *)to) + printf("*** Result of decode_instructions %p!\n", sres); + } } diff -r 69ad7823b1ca -r 8940ddc1036f src/share/tools/hsdis/hsdis.c --- a/src/share/tools/hsdis/hsdis.c Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/tools/hsdis/hsdis.c Mon Nov 05 13:55:31 2012 -0800 @@ -99,7 +99,7 @@ unsigned char* buffer, uintptr_t length, event_callback_t event_callback_arg, void* event_stream_arg, printf_callback_t printf_callback_arg, void* printf_stream_arg, - const char* options) { + const char* options, int newline) { struct hsdis_app_data app_data; memset(&app_data, 0, sizeof(app_data)); app_data.start_va = start_va; @@ -110,7 +110,7 @@ app_data.event_stream = event_stream_arg; app_data.printf_callback = printf_callback_arg; app_data.printf_stream = printf_stream_arg; - app_data.do_newline = false; + app_data.do_newline = newline == 0 ? false : true; return decode(&app_data, options); } @@ -132,7 +132,7 @@ event_stream_arg, printf_callback_arg, printf_stream_arg, - options); + options, false); } static void* decode(struct hsdis_app_data* app_data, const char* options) { @@ -173,7 +173,7 @@ if (!app_data->losing) { const char* insn_close = format_insn_close("/insn", &app_data->dinfo, buf, sizeof(buf)); - (*event_callback)(event_stream, insn_close, (void*) p) != NULL; + (*event_callback)(event_stream, insn_close, (void*) p); if (app_data->do_newline) { /* follow each complete insn by a nice newline */ @@ -182,13 +182,14 @@ } } - (*event_callback)(event_stream, "/insns", (void*) p); + if (app_data->losing) (*event_callback)(event_stream, "/insns", (void*) p); return (void*) p; } } /* take the address of the function, for luck, and also test the typedef: */ -const decode_instructions_ftype decode_instructions_address = &decode_instructions_virtual; +const decode_func_vtype decode_func_virtual_address = &decode_instructions_virtual; +const decode_func_stype decode_func_address = &decode_instructions; static const char* format_insn_close(const char* close, disassemble_info* dinfo, diff -r 69ad7823b1ca -r 8940ddc1036f src/share/tools/hsdis/hsdis.h --- a/src/share/tools/hsdis/hsdis.h Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/tools/hsdis/hsdis.h Mon Nov 05 13:55:31 2012 -0800 @@ -47,6 +47,9 @@ where tag is a simple identifier, signifying (as in XML) a element start, element end, and standalone element. (To render as XML, add angle brackets.) */ +#ifndef SHARED_TOOLS_HSDIS_H +#define SHARED_TOOLS_HSDIS_H + extern #ifdef DLL_EXPORT DLL_EXPORT @@ -57,16 +60,37 @@ void* event_stream, int (*printf_callback)(void*, const char*, ...), void* printf_stream, - const char* options); + const char* options, + int newline /* bool value for nice new line */); + +/* This is the compatability interface for older versions of hotspot */ +extern +#ifdef DLL_ENTRY + DLL_ENTRY +#endif +void* decode_instructions(void* start_pv, void* end_pv, + void* (*event_callback)(void*, const char*, void*), + void* event_stream, + int (*printf_callback)(void*, const char*, ...), + void* printf_stream, + const char* options); /* convenience typedefs */ typedef void* (*decode_instructions_event_callback_ftype) (void*, const char*, void*); typedef int (*decode_instructions_printf_callback_ftype) (void*, const char*, ...); -typedef void* (*decode_instructions_ftype) (uintptr_t start_va, uintptr_t end_va, - unsigned char* buffer, uintptr_t length, - decode_instructions_event_callback_ftype event_callback, - void* event_stream, - decode_instructions_printf_callback_ftype printf_callback, - void* printf_stream, - const char* options); +typedef void* (*decode_func_vtype) (uintptr_t start_va, uintptr_t end_va, + unsigned char* buffer, uintptr_t length, + decode_instructions_event_callback_ftype event_callback, + void* event_stream, + decode_instructions_printf_callback_ftype printf_callback, + void* printf_stream, + const char* options, + int newline); +typedef void* (*decode_func_stype) (void* start_pv, void* end_pv, + decode_instructions_event_callback_ftype event_callback, + void* event_stream, + decode_instructions_printf_callback_ftype printf_callback, + void* printf_stream, + const char* options); +#endif /* SHARED_TOOLS_HSDIS_H */ diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/asm/codeBuffer.cpp --- a/src/share/vm/asm/codeBuffer.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/asm/codeBuffer.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -758,7 +758,7 @@ } } - if (dest->blob() == NULL) { + if (dest->blob() == NULL && dest_filled != NULL) { // Destination is a final resting place, not just another buffer. // Normalize uninitialized bytes in the final padding. Copy::fill_to_bytes(dest_filled, dest_end - dest_filled, diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/c1/c1_GraphBuilder.cpp --- a/src/share/vm/c1/c1_GraphBuilder.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/c1/c1_GraphBuilder.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -1844,17 +1844,12 @@ code == Bytecodes::_invokevirtual && target->is_final_method() || code == Bytecodes::_invokedynamic) { ciMethod* inline_target = (cha_monomorphic_target != NULL) ? cha_monomorphic_target : target; - bool success = false; - if (target->is_method_handle_intrinsic()) { - // method handle invokes - success = try_method_handle_inline(target); - } else { - // static binding => check if callee is ok - success = try_inline(inline_target, (cha_monomorphic_target != NULL) || (exact_target != NULL), code, better_receiver); - } + // static binding => check if callee is ok + bool success = try_inline(inline_target, (cha_monomorphic_target != NULL) || (exact_target != NULL), code, better_receiver); + CHECK_BAILOUT(); - clear_inline_bailout(); + if (success) { // Register dependence if JVMTI has either breakpoint // setting or hotswapping of methods capabilities since they may @@ -3201,6 +3196,11 @@ return false; } + // method handle invokes + if (callee->is_method_handle_intrinsic()) { + return try_method_handle_inline(callee); + } + // handle intrinsics if (callee->intrinsic_id() != vmIntrinsics::_none) { if (try_inline_intrinsics(callee)) { @@ -3885,10 +3885,14 @@ ValueType* type = state()->stack_at(args_base)->type(); if (type->is_constant()) { ciMethod* target = type->as_ObjectType()->constant_value()->as_method_handle()->get_vmtarget(); - guarantee(!target->is_method_handle_intrinsic(), "should not happen"); // XXX remove - Bytecodes::Code bc = target->is_static() ? Bytecodes::_invokestatic : Bytecodes::_invokevirtual; - if (try_inline(target, /*holder_known*/ true, bc)) { - return true; + // We don't do CHA here so only inline static and statically bindable methods. + if (target->is_static() || target->can_be_statically_bound()) { + Bytecodes::Code bc = target->is_static() ? Bytecodes::_invokestatic : Bytecodes::_invokevirtual; + if (try_inline(target, /*holder_known*/ true, bc)) { + return true; + } + } else { + print_inlining(target, "not static or statically bindable", /*success*/ false); } } else { print_inlining(callee, "receiver not constant", /*success*/ false); @@ -3941,9 +3945,14 @@ } j += t->size(); // long and double take two slots } - Bytecodes::Code bc = target->is_static() ? Bytecodes::_invokestatic : Bytecodes::_invokevirtual; - if (try_inline(target, /*holder_known*/ true, bc)) { - return true; + // We don't do CHA here so only inline static and statically bindable methods. + if (target->is_static() || target->can_be_statically_bound()) { + Bytecodes::Code bc = target->is_static() ? Bytecodes::_invokestatic : Bytecodes::_invokevirtual; + if (try_inline(target, /*holder_known*/ true, bc)) { + return true; + } + } else { + print_inlining(target, "not static or statically bindable", /*success*/ false); } } } else { diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/classfile/bytecodeAssembler.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/classfile/bytecodeAssembler.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" + +#include "classfile/bytecodeAssembler.hpp" +#include "interpreter/bytecodes.hpp" +#include "memory/oopFactory.hpp" +#include "oops/constantPool.hpp" + +#ifdef TARGET_ARCH_x86 +# include "bytes_x86.hpp" +#endif +#ifdef TARGET_ARCH_sparc +# include "bytes_sparc.hpp" +#endif +#ifdef TARGET_ARCH_zero +# include "bytes_zero.hpp" +#endif +#ifdef TARGET_ARCH_arm +# include "bytes_arm.hpp" +#endif +#ifdef TARGET_ARCH_ppc +# include "bytes_ppc.hpp" +#endif + +u2 BytecodeConstantPool::find_or_add(BytecodeCPEntry const& bcpe) { + u2 index; + u2* probe = _indices.get(bcpe); + if (probe == NULL) { + index = _entries.length(); + _entries.append(bcpe); + _indices.put(bcpe, index); + } else { + index = *probe; + } + return index + _orig->length(); +} + +ConstantPool* BytecodeConstantPool::create_constant_pool(TRAPS) const { + if (_entries.length() == 0) { + return _orig; + } + + ConstantPool* cp = ConstantPool::allocate( + _orig->pool_holder()->class_loader_data(), + _orig->length() + _entries.length(), CHECK_NULL); + + cp->set_pool_holder(_orig->pool_holder()); + _orig->copy_cp_to(1, _orig->length() - 1, cp, 1, CHECK_NULL); + + for (int i = 0; i < _entries.length(); ++i) { + BytecodeCPEntry entry = _entries.at(i); + int idx = i + _orig->length(); + switch (entry._tag) { + case BytecodeCPEntry::UTF8: + cp->symbol_at_put(idx, entry._u.utf8); + entry._u.utf8->increment_refcount(); + break; + case BytecodeCPEntry::KLASS: + cp->unresolved_klass_at_put( + idx, cp->symbol_at(entry._u.klass)); + break; + case BytecodeCPEntry::STRING: + cp->unresolved_string_at_put( + idx, cp->symbol_at(entry._u.string)); + break; + case BytecodeCPEntry::NAME_AND_TYPE: + cp->name_and_type_at_put(idx, + entry._u.name_and_type.name_index, + entry._u.name_and_type.type_index); + break; + case BytecodeCPEntry::METHODREF: + cp->method_at_put(idx, + entry._u.methodref.class_index, + entry._u.methodref.name_and_type_index); + break; + default: + ShouldNotReachHere(); + } + } + return cp; +} + +void BytecodeAssembler::append(u1 imm_u1) { + _code->append(imm_u1); +} + +void BytecodeAssembler::append(u2 imm_u2) { + _code->append(0); + _code->append(0); + Bytes::put_Java_u2(_code->adr_at(_code->length() - 2), imm_u2); +} + +void BytecodeAssembler::append(u4 imm_u4) { + _code->append(0); + _code->append(0); + _code->append(0); + _code->append(0); + Bytes::put_Java_u4(_code->adr_at(_code->length() - 4), imm_u4); +} + +void BytecodeAssembler::xload(u4 index, u1 onebyteop, u1 twobyteop) { + if (index < 4) { + _code->append(onebyteop + index); + } else { + _code->append(twobyteop); + _code->append((u2)index); + } +} + +void BytecodeAssembler::dup() { + _code->append(Bytecodes::_dup); +} + +void BytecodeAssembler::_new(Symbol* sym) { + u2 cpool_index = _cp->klass(sym); + _code->append(Bytecodes::_new); + append(cpool_index); +} + +void BytecodeAssembler::load_string(Symbol* sym) { + u2 cpool_index = _cp->string(sym); + if (cpool_index < 0x100) { + ldc(cpool_index); + } else { + ldc_w(cpool_index); + } +} + +void BytecodeAssembler::ldc(u1 index) { + _code->append(Bytecodes::_ldc); + append(index); +} + +void BytecodeAssembler::ldc_w(u2 index) { + _code->append(Bytecodes::_ldc_w); + append(index); +} + +void BytecodeAssembler::athrow() { + _code->append(Bytecodes::_athrow); +} + +void BytecodeAssembler::iload(u4 index) { + xload(index, Bytecodes::_iload_0, Bytecodes::_iload); +} + +void BytecodeAssembler::lload(u4 index) { + xload(index, Bytecodes::_lload_0, Bytecodes::_lload); +} + +void BytecodeAssembler::fload(u4 index) { + xload(index, Bytecodes::_fload_0, Bytecodes::_fload); +} + +void BytecodeAssembler::dload(u4 index) { + xload(index, Bytecodes::_dload_0, Bytecodes::_dload); +} + +void BytecodeAssembler::aload(u4 index) { + xload(index, Bytecodes::_aload_0, Bytecodes::_aload); +} + +void BytecodeAssembler::load(BasicType bt, u4 index) { + switch (bt) { + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: iload(index); break; + case T_FLOAT: fload(index); break; + case T_DOUBLE: dload(index); break; + case T_LONG: lload(index); break; + case T_OBJECT: + case T_ARRAY: aload(index); break; + default: + ShouldNotReachHere(); + } +} + +void BytecodeAssembler::checkcast(Symbol* sym) { + u2 cpool_index = _cp->klass(sym); + _code->append(Bytecodes::_checkcast); + append(cpool_index); +} + +void BytecodeAssembler::invokespecial(Method* method) { + invokespecial(method->klass_name(), method->name(), method->signature()); +} + +void BytecodeAssembler::invokespecial(Symbol* klss, Symbol* name, Symbol* sig) { + u2 methodref_index = _cp->methodref(klss, name, sig); + _code->append(Bytecodes::_invokespecial); + append(methodref_index); +} + +void BytecodeAssembler::invokevirtual(Method* method) { + invokevirtual(method->klass_name(), method->name(), method->signature()); +} + +void BytecodeAssembler::invokevirtual(Symbol* klss, Symbol* name, Symbol* sig) { + u2 methodref_index = _cp->methodref(klss, name, sig); + _code->append(Bytecodes::_invokevirtual); + append(methodref_index); +} + +void BytecodeAssembler::ireturn() { + _code->append(Bytecodes::_ireturn); +} + +void BytecodeAssembler::lreturn() { + _code->append(Bytecodes::_lreturn); +} + +void BytecodeAssembler::freturn() { + _code->append(Bytecodes::_freturn); +} + +void BytecodeAssembler::dreturn() { + _code->append(Bytecodes::_dreturn); +} + +void BytecodeAssembler::areturn() { + _code->append(Bytecodes::_areturn); +} + +void BytecodeAssembler::_return() { + _code->append(Bytecodes::_return); +} + +void BytecodeAssembler::_return(BasicType bt) { + switch (bt) { + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: ireturn(); break; + case T_FLOAT: freturn(); break; + case T_DOUBLE: dreturn(); break; + case T_LONG: lreturn(); break; + case T_OBJECT: + case T_ARRAY: areturn(); break; + case T_VOID: _return(); break; + default: + ShouldNotReachHere(); + } +} diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/classfile/bytecodeAssembler.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/classfile/bytecodeAssembler.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_CLASSFILE_BYTECODEASSEMBLER_HPP +#define SHARE_VM_CLASSFILE_BYTECODEASSEMBLER_HPP + +#include "memory/allocation.hpp" +#include "oops/method.hpp" +#include "oops/symbol.hpp" +#include "utilities/globalDefinitions.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/resourceHash.hpp" + + +/** + * Bytecode Assembler + * + * These classes are used to synthesize code for creating new methods from + * within the VM. This is only a partial implementation of an assembler; + * only the bytecodes that are needed by clients are implemented at this time. + * This is used during default method analysis to create overpass methods + * and add them to a call during parsing. Other uses (such as creating + * bridges) may come later. Any missing bytecodes can be implemented on an + * as-need basis. + */ + +class BytecodeBuffer : public GrowableArray { + public: + BytecodeBuffer() : GrowableArray(20) {} +}; + +// Entries in a yet-to-be-created constant pool. Limited types for now. +class BytecodeCPEntry VALUE_OBJ_CLASS_SPEC { + public: + enum tag { + ERROR_TAG, + UTF8, + KLASS, + STRING, + NAME_AND_TYPE, + METHODREF + }; + + u1 _tag; + union { + Symbol* utf8; + u2 klass; + u2 string; + struct { + u2 name_index; + u2 type_index; + } name_and_type; + struct { + u2 class_index; + u2 name_and_type_index; + } methodref; + uintptr_t hash; + } _u; + + BytecodeCPEntry() : _tag(ERROR_TAG) { _u.hash = 0; } + BytecodeCPEntry(u1 tag) : _tag(tag) { _u.hash = 0; } + + static BytecodeCPEntry utf8(Symbol* symbol) { + BytecodeCPEntry bcpe(UTF8); + bcpe._u.utf8 = symbol; + return bcpe; + } + + static BytecodeCPEntry klass(u2 index) { + BytecodeCPEntry bcpe(KLASS); + bcpe._u.klass = index; + return bcpe; + } + + static BytecodeCPEntry string(u2 index) { + BytecodeCPEntry bcpe(STRING); + bcpe._u.string = index; + return bcpe; + } + + static BytecodeCPEntry name_and_type(u2 name, u2 type) { + BytecodeCPEntry bcpe(NAME_AND_TYPE); + bcpe._u.name_and_type.name_index = name; + bcpe._u.name_and_type.type_index = type; + return bcpe; + } + + static BytecodeCPEntry methodref(u2 class_index, u2 nat) { + BytecodeCPEntry bcpe(METHODREF); + bcpe._u.methodref.class_index = class_index; + bcpe._u.methodref.name_and_type_index = nat; + return bcpe; + } + + static bool equals(BytecodeCPEntry const& e0, BytecodeCPEntry const& e1) { + return e0._tag == e1._tag && e0._u.hash == e1._u.hash; + } + + static unsigned hash(BytecodeCPEntry const& e0) { + return (unsigned)(e0._tag ^ e0._u.hash); + } +}; + +class BytecodeConstantPool : ResourceObj { + private: + typedef ResourceHashtable IndexHash; + + ConstantPool* _orig; + GrowableArray _entries; + IndexHash _indices; + + u2 find_or_add(BytecodeCPEntry const& bcpe); + + public: + + BytecodeConstantPool(ConstantPool* orig) : _orig(orig) {} + + BytecodeCPEntry const& at(u2 index) const { return _entries.at(index); } + + InstanceKlass* pool_holder() const { + return InstanceKlass::cast(_orig->pool_holder()); + } + + u2 utf8(Symbol* sym) { + return find_or_add(BytecodeCPEntry::utf8(sym)); + } + + u2 klass(Symbol* class_name) { + return find_or_add(BytecodeCPEntry::klass(utf8(class_name))); + } + + u2 string(Symbol* str) { + return find_or_add(BytecodeCPEntry::string(utf8(str))); + } + + u2 name_and_type(Symbol* name, Symbol* sig) { + return find_or_add(BytecodeCPEntry::name_and_type(utf8(name), utf8(sig))); + } + + u2 methodref(Symbol* class_name, Symbol* name, Symbol* sig) { + return find_or_add(BytecodeCPEntry::methodref( + klass(class_name), name_and_type(name, sig))); + } + + ConstantPool* create_constant_pool(TRAPS) const; +}; + +// Partial bytecode assembler - only what we need for creating +// overpass methods for default methods is implemented +class BytecodeAssembler : StackObj { + private: + BytecodeBuffer* _code; + BytecodeConstantPool* _cp; + + void append(u1 imm_u1); + void append(u2 imm_u2); + void append(u4 imm_u4); + + void xload(u4 index, u1 quick, u1 twobyte); + + public: + BytecodeAssembler(BytecodeBuffer* buffer, BytecodeConstantPool* cp) + : _code(buffer), _cp(cp) {} + + void aload(u4 index); + void areturn(); + void athrow(); + void checkcast(Symbol* sym); + void dload(u4 index); + void dreturn(); + void dup(); + void fload(u4 index); + void freturn(); + void iload(u4 index); + void invokespecial(Method* method); + void invokespecial(Symbol* cls, Symbol* name, Symbol* sig); + void invokevirtual(Method* method); + void invokevirtual(Symbol* cls, Symbol* name, Symbol* sig); + void ireturn(); + void ldc(u1 index); + void ldc_w(u2 index); + void lload(u4 index); + void lreturn(); + void _new(Symbol* sym); + void _return(); + + void load_string(Symbol* sym); + void load(BasicType bt, u4 index); + void _return(BasicType bt); +}; + +#endif // SHARE_VM_CLASSFILE_BYTECODEASSEMBLER_HPP diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/classfile/classFileParser.cpp --- a/src/share/vm/classfile/classFileParser.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/classfile/classFileParser.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -27,6 +27,8 @@ #include "classfile/classLoader.hpp" #include "classfile/classLoaderData.hpp" #include "classfile/classLoaderData.inline.hpp" +#include "classfile/defaultMethods.hpp" +#include "classfile/genericSignatures.hpp" #include "classfile/javaClasses.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" @@ -84,6 +86,9 @@ // - to check NameAndType_info signatures more aggressively #define JAVA_7_VERSION 51 +// Extension method support. +#define JAVA_8_VERSION 52 + void ClassFileParser::parse_constant_pool_entries(ClassLoaderData* loader_data, constantPoolHandle cp, int length, TRAPS) { // Use a local copy of ClassFileStream. It helps the C++ compiler to optimize @@ -785,6 +790,7 @@ ClassLoaderData* loader_data, Handle protection_domain, Symbol* class_name, + bool* has_default_methods, TRAPS) { ClassFileStream* cfs = stream(); assert(length > 0, "only called for length>0"); @@ -821,6 +827,9 @@ if (!Klass::cast(interf())->is_interface()) { THROW_MSG_(vmSymbols::java_lang_IncompatibleClassChangeError(), "Implementing class", NULL); } + if (InstanceKlass::cast(interf())->has_default_methods()) { + *has_default_methods = true; + } interfaces->at_put(index, interf()); } @@ -1928,7 +1937,8 @@ if (method_attribute_name == vmSymbols::tag_code()) { // Parse Code attribute if (_need_verify) { - guarantee_property(!access_flags.is_native() && !access_flags.is_abstract(), + guarantee_property( + !access_flags.is_native() && !access_flags.is_abstract(), "Code attribute in native or abstract methods in class file %s", CHECK_(nullHandle)); } @@ -2125,7 +2135,9 @@ runtime_visible_annotations_length = method_attribute_length; runtime_visible_annotations = cfs->get_u1_buffer(); assert(runtime_visible_annotations != NULL, "null visible annotations"); - parse_annotations(runtime_visible_annotations, runtime_visible_annotations_length, cp, &parsed_annotations, CHECK_(nullHandle)); + parse_annotations(runtime_visible_annotations, + runtime_visible_annotations_length, cp, &parsed_annotations, + CHECK_(nullHandle)); cfs->skip_u1(runtime_visible_annotations_length, CHECK_(nullHandle)); } else if (PreserveAllAnnotations && method_attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { runtime_invisible_annotations_length = method_attribute_length; @@ -2169,12 +2181,10 @@ } // All sizing information for a Method* is finally available, now create it - Method* m = Method::allocate(loader_data, code_length, access_flags, - linenumber_table_length, - total_lvt_length, - exception_table_length, - checked_exceptions_length, - CHECK_(nullHandle)); + Method* m = Method::allocate( + loader_data, code_length, access_flags, linenumber_table_length, + total_lvt_length, exception_table_length, checked_exceptions_length, + ConstMethod::NORMAL, CHECK_(nullHandle)); ClassLoadingService::add_class_method_size(m->size()*HeapWordSize); @@ -2204,7 +2214,6 @@ // Fill in code attribute information m->set_max_stack(max_stack); m->set_max_locals(max_locals); - m->constMethod()->set_stackmap_data(stackmap_data); // Copy byte codes @@ -2356,6 +2365,7 @@ Array** methods_annotations, Array** methods_parameter_annotations, Array** methods_default_annotations, + bool* has_default_methods, TRAPS) { ClassFileStream* cfs = stream(); AnnotationArray* method_annotations = NULL; @@ -2382,6 +2392,10 @@ if (method->is_final()) { *has_final_method = true; } + if (is_interface && !method->is_abstract() && !method->is_static()) { + // default method + *has_default_methods = true; + } methods->at_put(index, method()); if (*methods_annotations == NULL) { *methods_annotations = @@ -2907,6 +2921,34 @@ } +#ifndef PRODUCT +static void parseAndPrintGenericSignatures( + instanceKlassHandle this_klass, TRAPS) { + assert(ParseAllGenericSignatures == true, "Shouldn't call otherwise"); + ResourceMark rm; + + if (this_klass->generic_signature() != NULL) { + using namespace generic; + ClassDescriptor* spec = ClassDescriptor::parse_generic_signature(this_klass(), CHECK); + + tty->print_cr("Parsing %s", this_klass->generic_signature()->as_C_string()); + spec->print_on(tty); + + for (int i = 0; i < this_klass->methods()->length(); ++i) { + Method* m = this_klass->methods()->at(i); + MethodDescriptor* method_spec = MethodDescriptor::parse_generic_signature(m, spec); + Symbol* sig = m->generic_signature(); + if (sig == NULL) { + sig = m->signature(); + } + tty->print_cr("Parsing %s", sig->as_C_string()); + method_spec->print_on(tty); + } + } +} +#endif // ndef PRODUCT + + instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, Handle class_loader, Handle protection_domain, @@ -2923,6 +2965,8 @@ unsigned char *cached_class_file_bytes = NULL; jint cached_class_file_length; ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader()); + bool has_default_methods = false; + ResourceMark rm(THREAD); ClassFileStream* cfs = stream(); // Timing @@ -3138,7 +3182,9 @@ if (itfs_len == 0) { local_interfaces = Universe::the_empty_klass_array(); } else { - local_interfaces = parse_interfaces(cp, itfs_len, loader_data, protection_domain, _class_name, CHECK_(nullHandle)); + local_interfaces = parse_interfaces( + cp, itfs_len, loader_data, protection_domain, _class_name, + &has_default_methods, CHECK_(nullHandle)); } u2 java_fields_count = 0; @@ -3164,6 +3210,7 @@ &methods_annotations, &methods_parameter_annotations, &methods_default_annotations, + &has_default_methods, CHECK_(nullHandle)); // Additional attributes @@ -3193,6 +3240,11 @@ super_klass = instanceKlassHandle(THREAD, kh()); } if (super_klass.not_null()) { + + if (super_klass->has_default_methods()) { + has_default_methods = true; + } + if (super_klass->is_interface()) { ResourceMark rm(THREAD); Exceptions::fthrow( @@ -3229,14 +3281,11 @@ int itable_size = 0; int num_miranda_methods = 0; - klassVtable::compute_vtable_size_and_num_mirandas(vtable_size, - num_miranda_methods, - super_klass(), - methods, - access_flags, - class_loader, - class_name, - local_interfaces, + GrowableArray all_mirandas(20); + + klassVtable::compute_vtable_size_and_num_mirandas( + &vtable_size, &num_miranda_methods, &all_mirandas, super_klass(), methods, + access_flags, class_loader, class_name, local_interfaces, CHECK_(nullHandle)); // Size of Java itable (in words) @@ -3656,6 +3705,7 @@ this_klass->set_minor_version(minor_version); this_klass->set_major_version(major_version); + this_klass->set_has_default_methods(has_default_methods); // Set up Method*::intrinsic_id as soon as we know the names of methods. // (We used to do this lazily, but now we query it in Rewriter, @@ -3673,6 +3723,16 @@ cached_class_file_length); } + // Fill in field values obtained by parse_classfile_attributes + if (parsed_annotations.has_any_annotations()) + parsed_annotations.apply_to(this_klass); + // Create annotations + if (_annotations != NULL && this_klass->annotations() == NULL) { + Annotations* anno = Annotations::allocate(loader_data, CHECK_NULL); + this_klass->set_annotations(anno); + } + apply_parsed_class_attributes(this_klass); + // Miranda methods if ((num_miranda_methods > 0) || // if this class introduced new miranda methods or @@ -3682,18 +3742,6 @@ this_klass->set_has_miranda_methods(); // then set a flag } - // Fill in field values obtained by parse_classfile_attributes - if (parsed_annotations.has_any_annotations()) { - parsed_annotations.apply_to(this_klass); - } - // Create annotations - if (_annotations != NULL && this_klass->annotations() == NULL) { - Annotations* anno = Annotations::allocate(loader_data, CHECK_NULL); - this_klass->set_annotations(anno); - } - apply_parsed_class_attributes(this_klass); - - // Compute transitive closure of interfaces this class implements this_klass->set_transitive_interfaces(transitive_interfaces); // Fill in information needed to compute superclasses. @@ -3702,6 +3750,7 @@ // Initialize itable offset tables klassItable::setup_itable_offset_table(this_klass); + // Compute transitive closure of interfaces this class implements // Do final class setup fill_oop_maps(this_klass, nonstatic_oop_map_count, nonstatic_oop_offsets, nonstatic_oop_counts); @@ -3726,6 +3775,21 @@ check_illegal_static_method(this_klass, CHECK_(nullHandle)); } + +#ifdef ASSERT + if (ParseAllGenericSignatures) { + parseAndPrintGenericSignatures(this_klass, CHECK_(nullHandle)); + } +#endif + + // Generate any default methods - default methods are interface methods + // that have a default implementation. This is new with Lambda project. + if (has_default_methods && !access_flags.is_interface() && + local_interfaces->length() > 0) { + DefaultMethods::generate_default_methods( + this_klass(), &all_mirandas, CHECK_(nullHandle)); + } + // Allocate mirror and initialize static fields java_lang_Class::create_mirror(this_klass, CHECK_(nullHandle)); @@ -3744,6 +3808,7 @@ false /* not shared class */); if (TraceClassLoading) { + ResourceMark rm; // print in a single call to reduce interleaving of output if (cfs->source() != NULL) { tty->print("[Loaded %s from %s]\n", this_klass->external_name(), @@ -3758,13 +3823,13 @@ tty->print("[Loaded %s]\n", this_klass->external_name()); } } else { - ResourceMark rm; tty->print("[Loaded %s from %s]\n", this_klass->external_name(), InstanceKlass::cast(class_loader->klass())->external_name()); } } if (TraceClassResolution) { + ResourceMark rm; // print out the superclass. const char * from = Klass::cast(this_klass())->external_name(); if (this_klass->java_super() != NULL) { @@ -3785,6 +3850,7 @@ #ifndef PRODUCT if( PrintCompactFieldsSavings ) { + ResourceMark rm; if( nonstatic_field_size < orig_nonstatic_field_size ) { tty->print("[Saved %d of %d bytes in %s]\n", (orig_nonstatic_field_size - nonstatic_field_size)*heapOopSize, @@ -3811,7 +3877,6 @@ return this_klass; } - unsigned int ClassFileParser::compute_oop_map_count(instanceKlassHandle super, unsigned int nonstatic_oop_map_count, @@ -4263,13 +4328,16 @@ const bool is_strict = (flags & JVM_ACC_STRICT) != 0; const bool is_synchronized = (flags & JVM_ACC_SYNCHRONIZED) != 0; const bool major_gte_15 = _major_version >= JAVA_1_5_VERSION; + const bool major_gte_8 = _major_version >= JAVA_8_VERSION; const bool is_initializer = (name == vmSymbols::object_initializer_name()); bool is_illegal = false; if (is_interface) { - if (!is_abstract || !is_public || is_static || is_final || - is_native || (major_gte_15 && (is_synchronized || is_strict))) { + if (!is_public || is_static || is_final || is_native || + ((is_synchronized || is_strict) && major_gte_15 && + (!major_gte_8 || is_abstract)) || + (!major_gte_8 && !is_abstract)) { is_illegal = true; } } else { // not interface diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/classfile/classFileParser.hpp --- a/src/share/vm/classfile/classFileParser.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/classfile/classFileParser.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -151,6 +151,7 @@ ClassLoaderData* loader_data, Handle protection_domain, Symbol* class_name, + bool* has_default_methods, TRAPS); void record_defined_class_dependencies(instanceKlassHandle defined_klass, TRAPS); @@ -188,6 +189,7 @@ Array** methods_annotations, Array** methods_parameter_annotations, Array** methods_default_annotations, + bool* has_default_method, TRAPS); Array* sort_methods(ClassLoaderData* loader_data, Array* methods, diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/classfile/defaultMethods.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/classfile/defaultMethods.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -0,0 +1,1387 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "classfile/bytecodeAssembler.hpp" +#include "classfile/defaultMethods.hpp" +#include "classfile/genericSignatures.hpp" +#include "classfile/symbolTable.hpp" +#include "memory/allocation.hpp" +#include "memory/metadataFactory.hpp" +#include "memory/resourceArea.hpp" +#include "runtime/signature.hpp" +#include "runtime/thread.hpp" +#include "oops/instanceKlass.hpp" +#include "oops/klass.hpp" +#include "oops/method.hpp" +#include "utilities/accessFlags.hpp" +#include "utilities/exceptions.hpp" +#include "utilities/ostream.hpp" +#include "utilities/pair.hpp" +#include "utilities/resourceHash.hpp" + +typedef enum { QUALIFIED, DISQUALIFIED } QualifiedState; + +// Because we use an iterative algorithm when iterating over the type +// hierarchy, we can't use traditional scoped objects which automatically do +// cleanup in the destructor when the scope is exited. PseudoScope (and +// PseudoScopeMark) provides a similar functionality, but for when you want a +// scoped object in non-stack memory (such as in resource memory, as we do +// here). You've just got to remember to call 'destroy()' on the scope when +// leaving it (and marks have to be explicitly added). +class PseudoScopeMark : public ResourceObj { + public: + virtual void destroy() = 0; +}; + +class PseudoScope : public ResourceObj { + private: + GrowableArray _marks; + public: + + static PseudoScope* cast(void* data) { + return static_cast(data); + } + + void add_mark(PseudoScopeMark* psm) { + _marks.append(psm); + } + + void destroy() { + for (int i = 0; i < _marks.length(); ++i) { + _marks.at(i)->destroy(); + } + } +}; + +class ContextMark : public PseudoScopeMark { + private: + generic::Context::Mark _mark; + public: + ContextMark(const generic::Context::Mark& cm) : _mark(cm) {} + virtual void destroy() { _mark.destroy(); } +}; + +#ifndef PRODUCT +static void print_slot(outputStream* str, Symbol* name, Symbol* signature) { + ResourceMark rm; + str->print("%s%s", name->as_C_string(), signature->as_C_string()); +} + +static void print_method(outputStream* str, Method* mo, bool with_class=true) { + ResourceMark rm; + if (with_class) { + str->print("%s.", mo->klass_name()->as_C_string()); + } + print_slot(str, mo->name(), mo->signature()); +} +#endif // ndef PRODUCT + +/** + * Perform a depth-first iteration over the class hierarchy, applying + * algorithmic logic as it goes. + * + * This class is one half of the inheritance hierarchy analysis mechanism. + * It is meant to be used in conjunction with another class, the algorithm, + * which is indicated by the ALGO template parameter. This class can be + * paired with any algorithm class that provides the required methods. + * + * This class contains all the mechanics for iterating over the class hierarchy + * starting at a particular root, without recursing (thus limiting stack growth + * from this point). It visits each superclass (if present) and superinterface + * in a depth-first manner, with callbacks to the ALGO class as each class is + * encountered (visit()), The algorithm can cut-off further exploration of a + * particular branch by returning 'false' from a visit() call. + * + * The ALGO class, must provide a visit() method, which each of which will be + * called once for each node in the inheritance tree during the iteration. In + * addition, it can provide a memory block via new_node_data(InstanceKlass*), + * which it can use for node-specific storage (and access via the + * current_data() and data_at_depth(int) methods). + * + * Bare minimum needed to be an ALGO class: + * class Algo : public HierarchyVisitor { + * void* new_node_data(InstanceKlass* cls) { return NULL; } + * void free_node_data(void* data) { return; } + * bool visit() { return true; } + * }; + */ +template +class HierarchyVisitor : StackObj { + private: + + class Node : public ResourceObj { + public: + InstanceKlass* _class; + bool _super_was_visited; + int _interface_index; + void* _algorithm_data; + + Node(InstanceKlass* cls, void* data, bool visit_super) + : _class(cls), _super_was_visited(!visit_super), + _interface_index(0), _algorithm_data(data) {} + + int number_of_interfaces() { return _class->local_interfaces()->length(); } + int interface_index() { return _interface_index; } + void set_super_visited() { _super_was_visited = true; } + void increment_visited_interface() { ++_interface_index; } + void set_all_interfaces_visited() { + _interface_index = number_of_interfaces(); + } + bool has_visited_super() { return _super_was_visited; } + bool has_visited_all_interfaces() { + return interface_index() >= number_of_interfaces(); + } + InstanceKlass* interface_at(int index) { + return InstanceKlass::cast(_class->local_interfaces()->at(index)); + } + InstanceKlass* next_super() { return _class->java_super(); } + InstanceKlass* next_interface() { + return interface_at(interface_index()); + } + }; + + bool _cancelled; + GrowableArray _path; + + Node* current_top() const { return _path.top(); } + bool has_more_nodes() const { return !_path.is_empty(); } + void push(InstanceKlass* cls, void* data) { + assert(cls != NULL, "Requires a valid instance class"); + Node* node = new Node(cls, data, has_super(cls)); + _path.push(node); + } + void pop() { _path.pop(); } + + void reset_iteration() { + _cancelled = false; + _path.clear(); + } + bool is_cancelled() const { return _cancelled; } + + static bool has_super(InstanceKlass* cls) { + return cls->super() != NULL && !cls->is_interface(); + } + + Node* node_at_depth(int i) const { + return (i >= _path.length()) ? NULL : _path.at(_path.length() - i - 1); + } + + protected: + + // Accessors available to the algorithm + int current_depth() const { return _path.length() - 1; } + + InstanceKlass* class_at_depth(int i) { + Node* n = node_at_depth(i); + return n == NULL ? NULL : n->_class; + } + InstanceKlass* current_class() { return class_at_depth(0); } + + void* data_at_depth(int i) { + Node* n = node_at_depth(i); + return n == NULL ? NULL : n->_algorithm_data; + } + void* current_data() { return data_at_depth(0); } + + void cancel_iteration() { _cancelled = true; } + + public: + + void run(InstanceKlass* root) { + ALGO* algo = static_cast(this); + + reset_iteration(); + + void* algo_data = algo->new_node_data(root); + push(root, algo_data); + bool top_needs_visit = true; + + do { + Node* top = current_top(); + if (top_needs_visit) { + if (algo->visit() == false) { + // algorithm does not want to continue along this path. Arrange + // it so that this state is immediately popped off the stack + top->set_super_visited(); + top->set_all_interfaces_visited(); + } + top_needs_visit = false; + } + + if (top->has_visited_super() && top->has_visited_all_interfaces()) { + algo->free_node_data(top->_algorithm_data); + pop(); + } else { + InstanceKlass* next = NULL; + if (top->has_visited_super() == false) { + next = top->next_super(); + top->set_super_visited(); + } else { + next = top->next_interface(); + top->increment_visited_interface(); + } + assert(next != NULL, "Otherwise we shouldn't be here"); + algo_data = algo->new_node_data(next); + push(next, algo_data); + top_needs_visit = true; + } + } while (!is_cancelled() && has_more_nodes()); + } +}; + +#ifndef PRODUCT +class PrintHierarchy : public HierarchyVisitor { + public: + + bool visit() { + InstanceKlass* cls = current_class(); + streamIndentor si(tty, current_depth() * 2); + tty->indent().print_cr("%s", cls->name()->as_C_string()); + return true; + } + + void* new_node_data(InstanceKlass* cls) { return NULL; } + void free_node_data(void* data) { return; } +}; +#endif // ndef PRODUCT + +// Used to register InstanceKlass objects and all related metadata structures +// (Methods, ConstantPools) as "in-use" by the current thread so that they can't +// be deallocated by class redefinition while we're using them. The classes are +// de-registered when this goes out of scope. +// +// Once a class is registered, we need not bother with methodHandles or +// constantPoolHandles for it's associated metadata. +class KeepAliveRegistrar : public StackObj { + private: + Thread* _thread; + GrowableArray _keep_alive; + + public: + KeepAliveRegistrar(Thread* thread) : _thread(thread), _keep_alive(20) { + assert(thread == Thread::current(), "Must be current thread"); + } + + ~KeepAliveRegistrar() { + for (int i = _keep_alive.length() - 1; i >= 0; --i) { + ConstantPool* cp = _keep_alive.at(i); + int idx = _thread->metadata_handles()->find_from_end(cp); + assert(idx > 0, "Must be in the list"); + _thread->metadata_handles()->remove_at(idx); + } + } + + // Register a class as 'in-use' by the thread. It's fine to register a class + // multiple times (though perhaps inefficient) + void register_class(InstanceKlass* ik) { + ConstantPool* cp = ik->constants(); + _keep_alive.push(cp); + _thread->metadata_handles()->push(cp); + } +}; + +class KeepAliveVisitor : public HierarchyVisitor { + private: + KeepAliveRegistrar* _registrar; + + public: + KeepAliveVisitor(KeepAliveRegistrar* registrar) : _registrar(registrar) {} + + void* new_node_data(InstanceKlass* cls) { return NULL; } + void free_node_data(void* data) { return; } + + bool visit() { + _registrar->register_class(current_class()); + return true; + } +}; + +// A method family contains a set of all methods that implement a single +// language-level method. Because of erasure, these methods may have different +// signatures. As members of the set are collected while walking over the +// hierarchy, they are tagged with a qualification state. The qualification +// state for an erased method is set to disqualified if there exists a path +// from the root of hierarchy to the method that contains an interleaving +// language-equivalent method defined in an interface. +class MethodFamily : public ResourceObj { + private: + + generic::MethodDescriptor* _descriptor; // language-level description + GrowableArray > _members; + ResourceHashtable _member_index; + + Method* _selected_target; // Filled in later, if a unique target exists + Symbol* _exception_message; // If no unique target is found + + bool contains_method(Method* method) { + int* lookup = _member_index.get(method); + return lookup != NULL; + } + + void add_method(Method* method, QualifiedState state) { + Pair entry(method, state); + _member_index.put(method, _members.length()); + _members.append(entry); + } + + void disqualify_method(Method* method) { + int* index = _member_index.get(method); + assert(index != NULL && *index >= 0 && *index < _members.length(), "bad index"); + _members.at(*index).second = DISQUALIFIED; + } + + Symbol* generate_no_defaults_message(TRAPS) const; + Symbol* generate_abstract_method_message(Method* method, TRAPS) const; + Symbol* generate_conflicts_message(GrowableArray* methods, TRAPS) const; + + public: + + MethodFamily(generic::MethodDescriptor* canonical_desc) + : _descriptor(canonical_desc), _selected_target(NULL), + _exception_message(NULL) {} + + generic::MethodDescriptor* descriptor() const { return _descriptor; } + + bool descriptor_matches(generic::MethodDescriptor* md, generic::Context* ctx) { + return descriptor()->covariant_match(md, ctx); + } + + void set_target_if_empty(Method* m) { + if (_selected_target == NULL && !m->is_overpass()) { + _selected_target = m; + } + } + + void record_qualified_method(Method* m) { + // If the method already exists in the set as qualified, this operation is + // redundant. If it already exists as disqualified, then we leave it as + // disqualfied. Thus we only add to the set if it's not already in the + // set. + if (!contains_method(m)) { + add_method(m, QUALIFIED); + } + } + + void record_disqualified_method(Method* m) { + // If not in the set, add it as disqualified. If it's already in the set, + // then set the state to disqualified no matter what the previous state was. + if (!contains_method(m)) { + add_method(m, DISQUALIFIED); + } else { + disqualify_method(m); + } + } + + bool has_target() const { return _selected_target != NULL; } + bool throws_exception() { return _exception_message != NULL; } + + Method* get_selected_target() { return _selected_target; } + Symbol* get_exception_message() { return _exception_message; } + + // Either sets the target or the exception error message + void determine_target(InstanceKlass* root, TRAPS) { + if (has_target() || throws_exception()) { + return; + } + + GrowableArray qualified_methods; + for (int i = 0; i < _members.length(); ++i) { + Pair entry = _members.at(i); + if (entry.second == QUALIFIED) { + qualified_methods.append(entry.first); + } + } + + if (qualified_methods.length() == 0) { + _exception_message = generate_no_defaults_message(CHECK); + } else if (qualified_methods.length() == 1) { + Method* method = qualified_methods.at(0); + if (method->is_abstract()) { + _exception_message = generate_abstract_method_message(method, CHECK); + } else { + _selected_target = qualified_methods.at(0); + } + } else { + _exception_message = generate_conflicts_message(&qualified_methods,CHECK); + } + + assert((has_target() ^ throws_exception()) == 1, + "One and only one must be true"); + } + + bool contains_signature(Symbol* query) { + for (int i = 0; i < _members.length(); ++i) { + if (query == _members.at(i).first->signature()) { + return true; + } + } + return false; + } + +#ifndef PRODUCT + void print_on(outputStream* str) const { + print_on(str, 0); + } + + void print_on(outputStream* str, int indent) const { + streamIndentor si(str, indent * 2); + + generic::Context ctx(NULL); // empty, as _descriptor already canonicalized + TempNewSymbol family = descriptor()->reify_signature(&ctx, Thread::current()); + str->indent().print_cr("Logical Method %s:", family->as_C_string()); + + streamIndentor si2(str); + for (int i = 0; i < _members.length(); ++i) { + str->indent(); + print_method(str, _members.at(i).first); + if (_members.at(i).second == DISQUALIFIED) { + str->print(" (disqualified)"); + } + str->print_cr(""); + } + + if (_selected_target != NULL) { + print_selected(str, 1); + } + } + + void print_selected(outputStream* str, int indent) const { + assert(has_target(), "Should be called otherwise"); + streamIndentor si(str, indent * 2); + str->indent().print("Selected method: "); + print_method(str, _selected_target); + str->print_cr(""); + } + + void print_exception(outputStream* str, int indent) { + assert(throws_exception(), "Should be called otherwise"); + streamIndentor si(str, indent * 2); + str->indent().print_cr("%s", _exception_message->as_C_string()); + } +#endif // ndef PRODUCT +}; + +Symbol* MethodFamily::generate_no_defaults_message(TRAPS) const { + return SymbolTable::new_symbol("No qualifying defaults found", CHECK_NULL); +} + +Symbol* MethodFamily::generate_abstract_method_message(Method* method, TRAPS) const { + Symbol* klass = method->klass_name(); + Symbol* name = method->name(); + Symbol* sig = method->signature(); + stringStream ss; + ss.print("Method "); + ss.write((const char*)klass->bytes(), klass->utf8_length()); + ss.print("."); + ss.write((const char*)name->bytes(), name->utf8_length()); + ss.write((const char*)sig->bytes(), sig->utf8_length()); + ss.print(" is abstract"); + return SymbolTable::new_symbol(ss.base(), (int)ss.size(), CHECK_NULL); +} + +Symbol* MethodFamily::generate_conflicts_message(GrowableArray* methods, TRAPS) const { + stringStream ss; + ss.print("Conflicting default methods:"); + for (int i = 0; i < methods->length(); ++i) { + Method* method = methods->at(i); + Symbol* klass = method->klass_name(); + Symbol* name = method->name(); + ss.print(" "); + ss.write((const char*)klass->bytes(), klass->utf8_length()); + ss.print("."); + ss.write((const char*)name->bytes(), name->utf8_length()); + } + return SymbolTable::new_symbol(ss.base(), (int)ss.size(), CHECK_NULL); +} + +class StateRestorer; + +// StatefulMethodFamily is a wrapper around MethodFamily that maintains the +// qualification state during hierarchy visitation, and applies that state +// when adding members to the MethodFamily. +class StatefulMethodFamily : public ResourceObj { + friend class StateRestorer; + private: + MethodFamily* _method; + QualifiedState _qualification_state; + + void set_qualification_state(QualifiedState state) { + _qualification_state = state; + } + + public: + StatefulMethodFamily(generic::MethodDescriptor* md, generic::Context* ctx) { + _method = new MethodFamily(md->canonicalize(ctx)); + _qualification_state = QUALIFIED; + } + + void set_target_if_empty(Method* m) { _method->set_target_if_empty(m); } + + MethodFamily* get_method_family() { return _method; } + + bool descriptor_matches(generic::MethodDescriptor* md, generic::Context* ctx) { + return _method->descriptor_matches(md, ctx); + } + + StateRestorer* record_method_and_dq_further(Method* mo); +}; + +class StateRestorer : public PseudoScopeMark { + private: + StatefulMethodFamily* _method; + QualifiedState _state_to_restore; + public: + StateRestorer(StatefulMethodFamily* dm, QualifiedState state) + : _method(dm), _state_to_restore(state) {} + ~StateRestorer() { destroy(); } + void restore_state() { _method->set_qualification_state(_state_to_restore); } + virtual void destroy() { restore_state(); } +}; + +StateRestorer* StatefulMethodFamily::record_method_and_dq_further(Method* mo) { + StateRestorer* mark = new StateRestorer(this, _qualification_state); + if (_qualification_state == QUALIFIED) { + _method->record_qualified_method(mo); + } else { + _method->record_disqualified_method(mo); + } + // Everything found "above"??? this method in the hierarchy walk is set to + // disqualified + set_qualification_state(DISQUALIFIED); + return mark; +} + +class StatefulMethodFamilies : public ResourceObj { + private: + GrowableArray _methods; + + public: + StatefulMethodFamily* find_matching( + generic::MethodDescriptor* md, generic::Context* ctx) { + for (int i = 0; i < _methods.length(); ++i) { + StatefulMethodFamily* existing = _methods.at(i); + if (existing->descriptor_matches(md, ctx)) { + return existing; + } + } + return NULL; + } + + StatefulMethodFamily* find_matching_or_create( + generic::MethodDescriptor* md, generic::Context* ctx) { + StatefulMethodFamily* method = find_matching(md, ctx); + if (method == NULL) { + method = new StatefulMethodFamily(md, ctx); + _methods.append(method); + } + return method; + } + + void extract_families_into(GrowableArray* array) { + for (int i = 0; i < _methods.length(); ++i) { + array->append(_methods.at(i)->get_method_family()); + } + } +}; + +// Represents a location corresponding to a vtable slot for methods that +// neither the class nor any of it's ancestors provide an implementaion. +// Default methods may be present to fill this slot. +class EmptyVtableSlot : public ResourceObj { + private: + Symbol* _name; + Symbol* _signature; + int _size_of_parameters; + MethodFamily* _binding; + + public: + EmptyVtableSlot(Method* method) + : _name(method->name()), _signature(method->signature()), + _size_of_parameters(method->size_of_parameters()), _binding(NULL) {} + + Symbol* name() const { return _name; } + Symbol* signature() const { return _signature; } + int size_of_parameters() const { return _size_of_parameters; } + + void bind_family(MethodFamily* lm) { _binding = lm; } + bool is_bound() { return _binding != NULL; } + MethodFamily* get_binding() { return _binding; } + +#ifndef PRODUCT + void print_on(outputStream* str) const { + print_slot(str, name(), signature()); + } +#endif // ndef PRODUCT +}; + +static GrowableArray* find_empty_vtable_slots( + InstanceKlass* klass, GrowableArray* mirandas, TRAPS) { + + assert(klass != NULL, "Must be valid class"); + + GrowableArray* slots = new GrowableArray(); + + // All miranda methods are obvious candidates + for (int i = 0; i < mirandas->length(); ++i) { + EmptyVtableSlot* slot = new EmptyVtableSlot(mirandas->at(i)); + slots->append(slot); + } + + // Also any overpasses in our superclasses, that we haven't implemented. + // (can't use the vtable because it is not guaranteed to be initialized yet) + InstanceKlass* super = klass->java_super(); + while (super != NULL) { + for (int i = 0; i < super->methods()->length(); ++i) { + Method* m = super->methods()->at(i); + if (m->is_overpass()) { + // m is a method that would have been a miranda if not for the + // default method processing that occurred on behalf of our superclass, + // so it's a method we want to re-examine in this new context. That is, + // unless we have a real implementation of it in the current class. + Method* impl = klass->lookup_method(m->name(), m->signature()); + if (impl == NULL || impl->is_overpass()) { + slots->append(new EmptyVtableSlot(m)); + } + } + } + super = super->java_super(); + } + +#ifndef PRODUCT + if (TraceDefaultMethods) { + tty->print_cr("Slots that need filling:"); + streamIndentor si(tty); + for (int i = 0; i < slots->length(); ++i) { + tty->indent(); + slots->at(i)->print_on(tty); + tty->print_cr(""); + } + } +#endif // ndef PRODUCT + return slots; +} + +// Iterates over the type hierarchy looking for all methods with a specific +// method name. The result of this is a set of method families each of +// which is populated with a set of methods that implement the same +// language-level signature. +class FindMethodsByName : public HierarchyVisitor { + private: + // Context data + Thread* THREAD; + generic::DescriptorCache* _cache; + Symbol* _method_name; + generic::Context* _ctx; + StatefulMethodFamilies _families; + + public: + + FindMethodsByName(generic::DescriptorCache* cache, Symbol* name, + generic::Context* ctx, Thread* thread) : + _cache(cache), _method_name(name), _ctx(ctx), THREAD(thread) {} + + void get_discovered_families(GrowableArray* methods) { + _families.extract_families_into(methods); + } + + void* new_node_data(InstanceKlass* cls) { return new PseudoScope(); } + void free_node_data(void* node_data) { + PseudoScope::cast(node_data)->destroy(); + } + + bool visit() { + PseudoScope* scope = PseudoScope::cast(current_data()); + InstanceKlass* klass = current_class(); + InstanceKlass* sub = current_depth() > 0 ? class_at_depth(1) : NULL; + + ContextMark* cm = new ContextMark(_ctx->mark()); + scope->add_mark(cm); // will restore context when scope is freed + + _ctx->apply_type_arguments(sub, klass, THREAD); + + int start, end = 0; + start = klass->find_method_by_name(_method_name, &end); + if (start != -1) { + for (int i = start; i < end; ++i) { + Method* m = klass->methods()->at(i); + // This gets the method's parameter list with its generic type + // parameters resolved + generic::MethodDescriptor* md = _cache->descriptor_for(m, THREAD); + + // Find all methods on this hierarchy that match this method + // (name, signature). This class collects other families of this + // method name. + StatefulMethodFamily* family = + _families.find_matching_or_create(md, _ctx); + + if (klass->is_interface()) { + // ??? + StateRestorer* restorer = family->record_method_and_dq_further(m); + scope->add_mark(restorer); + } else { + // This is the rule that methods in classes "win" (bad word) over + // methods in interfaces. This works because of single inheritance + family->set_target_if_empty(m); + } + } + } + return true; + } +}; + +#ifndef PRODUCT +static void print_families( + GrowableArray* methods, Symbol* match) { + streamIndentor si(tty, 4); + if (methods->length() == 0) { + tty->indent(); + tty->print_cr("No Logical Method found"); + } + for (int i = 0; i < methods->length(); ++i) { + tty->indent(); + MethodFamily* lm = methods->at(i); + if (lm->contains_signature(match)) { + tty->print_cr(""); + } else { + tty->print_cr(""); + } + lm->print_on(tty, 1); + } +} +#endif // ndef PRODUCT + +static void merge_in_new_methods(InstanceKlass* klass, + GrowableArray* new_methods, TRAPS); +static void create_overpasses( + GrowableArray* slots, InstanceKlass* klass, TRAPS); + +// This is the guts of the default methods implementation. This is called just +// after the classfile has been parsed if some ancestor has default methods. +// +// First if finds any name/signature slots that need any implementation (either +// because they are miranda or a superclass's implementation is an overpass +// itself). For each slot, iterate over the hierarchy, using generic signature +// information to partition any methods that match the name into method families +// where each family contains methods whose signatures are equivalent at the +// language level (i.e., their reified parameters match and return values are +// covariant). Check those sets to see if they contain a signature that matches +// the slot we're looking at (if we're lucky, there might be other empty slots +// that we can fill using the same analysis). +// +// For each slot filled, we generate an overpass method that either calls the +// unique default method candidate using invokespecial, or throws an exception +// (in the case of no default method candidates, or more than one valid +// candidate). These methods are then added to the class's method list. If +// the method set we're using contains methods (qualified or not) with a +// different runtime signature than the method we're creating, then we have to +// create bridges with those signatures too. +void DefaultMethods::generate_default_methods( + InstanceKlass* klass, GrowableArray* mirandas, TRAPS) { + + // This resource mark is the bound for all memory allocation that takes + // place during default method processing. After this goes out of scope, + // all (Resource) objects' memory will be reclaimed. Be careful if adding an + // embedded resource mark under here as that memory can't be used outside + // whatever scope it's in. + ResourceMark rm(THREAD); + + generic::DescriptorCache cache; + + // Keep entire hierarchy alive for the duration of the computation + KeepAliveRegistrar keepAlive(THREAD); + KeepAliveVisitor loadKeepAlive(&keepAlive); + loadKeepAlive.run(klass); + +#ifndef PRODUCT + if (TraceDefaultMethods) { + ResourceMark rm; // be careful with these! + tty->print_cr("Class %s requires default method processing", + klass->name()->as_klass_external_name()); + PrintHierarchy printer; + printer.run(klass); + } +#endif // ndef PRODUCT + + GrowableArray* empty_slots = + find_empty_vtable_slots(klass, mirandas, CHECK); + + for (int i = 0; i < empty_slots->length(); ++i) { + EmptyVtableSlot* slot = empty_slots->at(i); +#ifndef PRODUCT + if (TraceDefaultMethods) { + streamIndentor si(tty, 2); + tty->indent().print("Looking for default methods for slot "); + slot->print_on(tty); + tty->print_cr(""); + } +#endif // ndef PRODUCT + if (slot->is_bound()) { +#ifndef PRODUCT + if (TraceDefaultMethods) { + streamIndentor si(tty, 4); + tty->indent().print_cr("Already bound to logical method:"); + slot->get_binding()->print_on(tty, 1); + } +#endif // ndef PRODUCT + continue; // covered by previous processing + } + + generic::Context ctx(&cache); + FindMethodsByName visitor(&cache, slot->name(), &ctx, CHECK); + visitor.run(klass); + + GrowableArray discovered_families; + visitor.get_discovered_families(&discovered_families); + +#ifndef PRODUCT + if (TraceDefaultMethods) { + print_families(&discovered_families, slot->signature()); + } +#endif // ndef PRODUCT + + // Find and populate any other slots that match the discovered families + for (int j = i; j < empty_slots->length(); ++j) { + EmptyVtableSlot* open_slot = empty_slots->at(j); + + if (slot->name() == open_slot->name()) { + for (int k = 0; k < discovered_families.length(); ++k) { + MethodFamily* lm = discovered_families.at(k); + + if (lm->contains_signature(open_slot->signature())) { + lm->determine_target(klass, CHECK); + open_slot->bind_family(lm); + } + } + } + } + } + +#ifndef PRODUCT + if (TraceDefaultMethods) { + tty->print_cr("Creating overpasses..."); + } +#endif // ndef PRODUCT + + create_overpasses(empty_slots, klass, CHECK); + +#ifndef PRODUCT + if (TraceDefaultMethods) { + tty->print_cr("Default method processing complete"); + } +#endif // ndef PRODUCT +} + + +/** + * Generic analysis was used upon interface '_target' and found a unique + * default method candidate with generic signature '_method_desc'. This + * method is only viable if it would also be in the set of default method + * candidates if we ran a full analysis on the current class. + * + * The only reason that the method would not be in the set of candidates for + * the current class is if that there's another covariantly matching method + * which is "more specific" than the found method -- i.e., one could find a + * path in the interface hierarchy in which the matching method appears + * before we get to '_target'. + * + * In order to determine this, we examine all of the implemented + * interfaces. If we find path that leads to the '_target' interface, then + * we examine that path to see if there are any methods that would shadow + * the selected method along that path. + */ +class ShadowChecker : public HierarchyVisitor { + private: + generic::DescriptorCache* _cache; + Thread* THREAD; + + InstanceKlass* _target; + + Symbol* _method_name; + InstanceKlass* _method_holder; + generic::MethodDescriptor* _method_desc; + bool _found_shadow; + + bool path_has_shadow() { + generic::Context ctx(_cache); + + for (int i = current_depth() - 1; i > 0; --i) { + InstanceKlass* ik = class_at_depth(i); + InstanceKlass* sub = class_at_depth(i + 1); + ctx.apply_type_arguments(sub, ik, THREAD); + + if (ik->is_interface()) { + int end; + int start = ik->find_method_by_name(_method_name, &end); + if (start != -1) { + for (int j = start; j < end; ++j) { + Method* mo = ik->methods()->at(j); + generic::MethodDescriptor* md = _cache->descriptor_for(mo, THREAD); + if (_method_desc->covariant_match(md, &ctx)) { + return true; + } + } + } + } + } + return false; + } + + public: + + ShadowChecker(generic::DescriptorCache* cache, Thread* thread, + Symbol* name, InstanceKlass* holder, generic::MethodDescriptor* desc, + InstanceKlass* target) + : _cache(cache), THREAD(thread), _method_name(name), _method_holder(holder), + _method_desc(desc), _target(target), _found_shadow(false) {} + + void* new_node_data(InstanceKlass* cls) { return NULL; } + void free_node_data(void* data) { return; } + + bool visit() { + InstanceKlass* ik = current_class(); + if (ik == _target && current_depth() == 1) { + return false; // This was the specified super -- no need to search it + } + if (ik == _method_holder || ik == _target) { + // We found a path that should be examined to see if it shadows _method + if (path_has_shadow()) { + _found_shadow = true; + cancel_iteration(); + } + return false; // no need to continue up hierarchy + } + return true; + } + + bool found_shadow() { return _found_shadow; } +}; + +// This is called during linktime when we find an invokespecial call that +// refers to a direct superinterface. It indicates that we should find the +// default method in the hierarchy of that superinterface, and if that method +// would have been a candidate from the point of view of 'this' class, then we +// return that method. +Method* DefaultMethods::find_super_default( + Klass* cls, Klass* super, Symbol* method_name, Symbol* sig, TRAPS) { + + ResourceMark rm(THREAD); + + assert(cls != NULL && super != NULL, "Need real classes"); + + InstanceKlass* current_class = InstanceKlass::cast(cls); + InstanceKlass* direction = InstanceKlass::cast(super); + + // Keep entire hierarchy alive for the duration of the computation + KeepAliveRegistrar keepAlive(THREAD); + KeepAliveVisitor loadKeepAlive(&keepAlive); + loadKeepAlive.run(current_class); + +#ifndef PRODUCT + if (TraceDefaultMethods) { + tty->print_cr("Finding super default method %s.%s%s from %s", + direction->name()->as_C_string(), + method_name->as_C_string(), sig->as_C_string(), + current_class->name()->as_C_string()); + } +#endif // ndef PRODUCT + + if (!direction->is_interface()) { + // We should not be here + return NULL; + } + + generic::DescriptorCache cache; + generic::Context ctx(&cache); + + // Prime the initial generic context for current -> direction + ctx.apply_type_arguments(current_class, direction, CHECK_NULL); + + FindMethodsByName visitor(&cache, method_name, &ctx, CHECK_NULL); + visitor.run(direction); + + GrowableArray families; + visitor.get_discovered_families(&families); + +#ifndef PRODUCT + if (TraceDefaultMethods) { + print_families(&families, sig); + } +#endif // ndef PRODUCT + + MethodFamily* selected_family = NULL; + + for (int i = 0; i < families.length(); ++i) { + MethodFamily* lm = families.at(i); + if (lm->contains_signature(sig)) { + lm->determine_target(current_class, CHECK_NULL); + selected_family = lm; + } + } + + if (selected_family->has_target()) { + Method* target = selected_family->get_selected_target(); + InstanceKlass* holder = InstanceKlass::cast(target->method_holder()); + + // Verify that the identified method is valid from the context of + // the current class + ShadowChecker checker(&cache, THREAD, target->name(), + holder, selected_family->descriptor(), direction); + checker.run(current_class); + + if (checker.found_shadow()) { +#ifndef PRODUCT + if (TraceDefaultMethods) { + tty->print_cr(" Only candidate found was shadowed."); + } +#endif // ndef PRODUCT + THROW_MSG_(vmSymbols::java_lang_AbstractMethodError(), + "Accessible default method not found", NULL); + } else { +#ifndef PRODUCT + if (TraceDefaultMethods) { + tty->print(" Returning "); + print_method(tty, target, true); + tty->print_cr(""); + } +#endif // ndef PRODUCT + return target; + } + } else { + assert(selected_family->throws_exception(), "must have target or throw"); + THROW_MSG_(vmSymbols::java_lang_AbstractMethodError(), + selected_family->get_exception_message()->as_C_string(), NULL); + } +} + + +static int assemble_redirect( + BytecodeConstantPool* cp, BytecodeBuffer* buffer, + Symbol* incoming, Method* target, TRAPS) { + + BytecodeAssembler assem(buffer, cp); + + SignatureStream in(incoming, true); + SignatureStream out(target->signature(), true); + u2 parameter_count = 0; + + assem.aload(parameter_count++); // load 'this' + + while (!in.at_return_type()) { + assert(!out.at_return_type(), "Parameter counts do not match"); + BasicType bt = in.type(); + assert(out.type() == bt, "Parameter types are not compatible"); + assem.load(bt, parameter_count); + if (in.is_object() && in.as_symbol(THREAD) != out.as_symbol(THREAD)) { + assem.checkcast(out.as_symbol(THREAD)); + } else if (bt == T_LONG || bt == T_DOUBLE) { + ++parameter_count; // longs and doubles use two slots + } + ++parameter_count; + in.next(); + out.next(); + } + assert(out.at_return_type(), "Parameter counts do not match"); + assert(in.type() == out.type(), "Return types are not compatible"); + + if (parameter_count == 1 && (in.type() == T_LONG || in.type() == T_DOUBLE)) { + ++parameter_count; // need room for return value + } + if (target->method_holder()->is_interface()) { + assem.invokespecial(target); + } else { + assem.invokevirtual(target); + } + + if (in.is_object() && in.as_symbol(THREAD) != out.as_symbol(THREAD)) { + assem.checkcast(in.as_symbol(THREAD)); + } + assem._return(in.type()); + return parameter_count; +} + +static int assemble_abstract_method_error( + BytecodeConstantPool* cp, BytecodeBuffer* buffer, Symbol* message, TRAPS) { + + Symbol* errorName = vmSymbols::java_lang_AbstractMethodError(); + Symbol* init = vmSymbols::object_initializer_name(); + Symbol* sig = vmSymbols::string_void_signature(); + + BytecodeAssembler assem(buffer, cp); + + assem._new(errorName); + assem.dup(); + assem.load_string(message); + assem.invokespecial(errorName, init, sig); + assem.athrow(); + + return 3; // max stack size: [ exception, exception, string ] +} + +static Method* new_method( + BytecodeConstantPool* cp, BytecodeBuffer* bytecodes, Symbol* name, + Symbol* sig, AccessFlags flags, int max_stack, int params, + ConstMethod::MethodType mt, TRAPS) { + + address code_start = static_cast
(bytecodes->adr_at(0)); + int code_length = bytecodes->length(); + + Method* m = Method::allocate(cp->pool_holder()->class_loader_data(), + code_length, flags, 0, 0, 0, 0, mt, CHECK_NULL); + + m->set_constants(NULL); // This will get filled in later + m->set_name_index(cp->utf8(name)); + m->set_signature_index(cp->utf8(sig)); + m->set_generic_signature_index(0); +#ifdef CC_INTERP + ResultTypeFinder rtf(sig); + m->set_result_index(rtf.type()); +#endif + m->set_size_of_parameters(params); + m->set_max_stack(max_stack); + m->set_max_locals(params); + m->constMethod()->set_stackmap_data(NULL); + m->set_code(code_start); + m->set_force_inline(true); + + return m; +} + +static void switchover_constant_pool(BytecodeConstantPool* bpool, + InstanceKlass* klass, GrowableArray* new_methods, TRAPS) { + + if (new_methods->length() > 0) { + ConstantPool* cp = bpool->create_constant_pool(CHECK); + if (cp != klass->constants()) { + klass->class_loader_data()->add_to_deallocate_list(klass->constants()); + klass->set_constants(cp); + cp->set_pool_holder(klass); + + for (int i = 0; i < new_methods->length(); ++i) { + new_methods->at(i)->set_constants(cp); + } + for (int i = 0; i < klass->methods()->length(); ++i) { + Method* mo = klass->methods()->at(i); + mo->set_constants(cp); + } + } + } +} + +// A "bridge" is a method created by javac to bridge the gap between +// an implementation and a generically-compatible, but different, signature. +// Bridges have actual bytecode implementation in classfiles. +// An "overpass", on the other hand, performs the same function as a bridge +// but does not occur in a classfile; the VM creates overpass itself, +// when it needs a path to get from a call site to an default method, and +// a bridge doesn't exist. +static void create_overpasses( + GrowableArray* slots, + InstanceKlass* klass, TRAPS) { + + GrowableArray overpasses; + BytecodeConstantPool bpool(klass->constants()); + + for (int i = 0; i < slots->length(); ++i) { + EmptyVtableSlot* slot = slots->at(i); + + if (slot->is_bound()) { + MethodFamily* method = slot->get_binding(); + int max_stack = 0; + BytecodeBuffer buffer; + +#ifndef PRODUCT + if (TraceDefaultMethods) { + tty->print("for slot: "); + slot->print_on(tty); + tty->print_cr(""); + if (method->has_target()) { + method->print_selected(tty, 1); + } else { + method->print_exception(tty, 1); + } + } +#endif // ndef PRODUCT + if (method->has_target()) { + Method* selected = method->get_selected_target(); + max_stack = assemble_redirect( + &bpool, &buffer, slot->signature(), selected, CHECK); + } else if (method->throws_exception()) { + max_stack = assemble_abstract_method_error( + &bpool, &buffer, method->get_exception_message(), CHECK); + } + AccessFlags flags = accessFlags_from( + JVM_ACC_PUBLIC | JVM_ACC_SYNTHETIC | JVM_ACC_BRIDGE); + Method* m = new_method(&bpool, &buffer, slot->name(), slot->signature(), + flags, max_stack, slot->size_of_parameters(), + ConstMethod::OVERPASS, CHECK); + if (m != NULL) { + overpasses.push(m); + } + } + } + +#ifndef PRODUCT + if (TraceDefaultMethods) { + tty->print_cr("Created %d overpass methods", overpasses.length()); + } +#endif // ndef PRODUCT + + switchover_constant_pool(&bpool, klass, &overpasses, CHECK); + merge_in_new_methods(klass, &overpasses, CHECK); +} + +static void sort_methods(GrowableArray* methods) { + // Note that this must sort using the same key as is used for sorting + // methods in InstanceKlass. + bool sorted = true; + for (int i = methods->length() - 1; i > 0; --i) { + for (int j = 0; j < i; ++j) { + Method* m1 = methods->at(j); + Method* m2 = methods->at(j + 1); + if ((uintptr_t)m1->name() > (uintptr_t)m2->name()) { + methods->at_put(j, m2); + methods->at_put(j + 1, m1); + sorted = false; + } + } + if (sorted) break; + sorted = true; + } +#ifdef ASSERT + uintptr_t prev = 0; + for (int i = 0; i < methods->length(); ++i) { + Method* mh = methods->at(i); + uintptr_t nv = (uintptr_t)mh->name(); + assert(nv >= prev, "Incorrect overpass method ordering"); + prev = nv; + } +#endif +} + +static void merge_in_new_methods(InstanceKlass* klass, + GrowableArray* new_methods, TRAPS) { + + enum { ANNOTATIONS, PARAMETERS, DEFAULTS, NUM_ARRAYS }; + + Array* original_annots[NUM_ARRAYS]; + + Array* original_methods = klass->methods(); + Annotations* annots = klass->annotations(); + original_annots[ANNOTATIONS] = annots->methods_annotations(); + original_annots[PARAMETERS] = annots->methods_parameter_annotations(); + original_annots[DEFAULTS] = annots->methods_default_annotations(); + + Array* original_ordering = klass->method_ordering(); + Array* merged_ordering = Universe::the_empty_int_array(); + + int new_size = klass->methods()->length() + new_methods->length(); + + Array* merged_annots[NUM_ARRAYS]; + + Array* merged_methods = MetadataFactory::new_array( + klass->class_loader_data(), new_size, NULL, CHECK); + for (int i = 0; i < NUM_ARRAYS; ++i) { + if (original_annots[i] != NULL) { + merged_annots[i] = MetadataFactory::new_array( + klass->class_loader_data(), new_size, CHECK); + } else { + merged_annots[i] = NULL; + } + } + if (original_ordering != NULL && original_ordering->length() > 0) { + merged_ordering = MetadataFactory::new_array( + klass->class_loader_data(), new_size, CHECK); + } + int method_order_index = klass->methods()->length(); + + sort_methods(new_methods); + + // Perform grand merge of existing methods and new methods + int orig_idx = 0; + int new_idx = 0; + + for (int i = 0; i < new_size; ++i) { + Method* orig_method = NULL; + Method* new_method = NULL; + if (orig_idx < original_methods->length()) { + orig_method = original_methods->at(orig_idx); + } + if (new_idx < new_methods->length()) { + new_method = new_methods->at(new_idx); + } + + if (orig_method != NULL && + (new_method == NULL || orig_method->name() < new_method->name())) { + merged_methods->at_put(i, orig_method); + original_methods->at_put(orig_idx, NULL); + for (int j = 0; j < NUM_ARRAYS; ++j) { + if (merged_annots[j] != NULL) { + merged_annots[j]->at_put(i, original_annots[j]->at(orig_idx)); + original_annots[j]->at_put(orig_idx, NULL); + } + } + if (merged_ordering->length() > 0) { + merged_ordering->at_put(i, original_ordering->at(orig_idx)); + } + ++orig_idx; + } else { + merged_methods->at_put(i, new_method); + if (merged_ordering->length() > 0) { + merged_ordering->at_put(i, method_order_index++); + } + ++new_idx; + } + // update idnum for new location + merged_methods->at(i)->set_method_idnum(i); + } + + // Verify correct order +#ifdef ASSERT + uintptr_t prev = 0; + for (int i = 0; i < merged_methods->length(); ++i) { + Method* mo = merged_methods->at(i); + uintptr_t nv = (uintptr_t)mo->name(); + assert(nv >= prev, "Incorrect method ordering"); + prev = nv; + } +#endif + + // Replace klass methods with new merged lists + klass->set_methods(merged_methods); + annots->set_methods_annotations(merged_annots[ANNOTATIONS]); + annots->set_methods_parameter_annotations(merged_annots[PARAMETERS]); + annots->set_methods_default_annotations(merged_annots[DEFAULTS]); + + ClassLoaderData* cld = klass->class_loader_data(); + MetadataFactory::free_array(cld, original_methods); + for (int i = 0; i < NUM_ARRAYS; ++i) { + MetadataFactory::free_array(cld, original_annots[i]); + } + if (original_ordering->length() > 0) { + klass->set_method_ordering(merged_ordering); + MetadataFactory::free_array(cld, original_ordering); + } +} + diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/classfile/defaultMethods.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/classfile/defaultMethods.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_CLASSFILE_DEFAULTMETHODS_HPP +#define SHARE_VM_CLASSFILE_DEFAULTMETHODS_HPP + +#include "runtime/handles.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/exceptions.hpp" + +class InstanceKlass; +class Symbol; +class Method; + +class DefaultMethods : AllStatic { + public: + + // Analyzes class and determines which default methods are inherited + // from interfaces (and has no other implementation). For each method + // (and each different signature the method could have), create an + // "overpass" method that is an instance method that redirects to the + // default method. Overpass methods are added to the methods lists for + // the class. + static void generate_default_methods( + InstanceKlass* klass, GrowableArray* mirandas, TRAPS); + + + // Called during linking when an invokespecial to an direct interface + // method is found. Selects and returns a method if there is a unique + // default method in the 'super_iface' part of the hierarchy which is + // also a candidate default for 'this_klass'. Otherwise throws an AME. + static Method* find_super_default( + Klass* this_klass, Klass* super_iface, + Symbol* method_name, Symbol* method_sig, TRAPS); +}; + +#endif // SHARE_VM_CLASSFILE_DEFAULTMETHODS_HPP diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/classfile/genericSignatures.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/classfile/genericSignatures.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -0,0 +1,1272 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" + +#include "classfile/genericSignatures.hpp" +#include "classfile/symbolTable.hpp" +#include "classfile/systemDictionary.hpp" +#include "memory/resourceArea.hpp" + +namespace generic { + +// Helper class for parsing the generic signature Symbol in klass and methods +class DescriptorStream : public ResourceObj { + private: + Symbol* _symbol; + int _offset; + int _mark; + const char* _parse_error; + + void set_parse_error(const char* error) { + assert(error != NULL, "Can't set NULL error string"); + _parse_error = error; + } + + public: + DescriptorStream(Symbol* sym) + : _symbol(sym), _offset(0), _mark(-1), _parse_error(NULL) {} + + const char* parse_error() const { + return _parse_error; + } + + bool at_end() { return _offset >= _symbol->utf8_length(); } + + char peek() { + if (at_end()) { + set_parse_error("Peeking past end of signature"); + return '\0'; + } else { + return _symbol->byte_at(_offset); + } + } + + char read() { + if (at_end()) { + set_parse_error("Reading past end of signature"); + return '\0'; + } else { + return _symbol->byte_at(_offset++); + } + } + + void read(char expected) { + char c = read(); + assert_char(c, expected, 0); + } + + void assert_char(char c, char expected, int pos = -1) { + if (c != expected) { + const char* fmt = "Parse error at %d: expected %c but got %c"; + size_t len = strlen(fmt) + 5; + char* buffer = NEW_RESOURCE_ARRAY(char, len); + jio_snprintf(buffer, len, fmt, _offset + pos, expected, c); + set_parse_error(buffer); + } + } + + void push(char c) { + assert(c == _symbol->byte_at(_offset - 1), "Pushing back wrong value"); + --_offset; + } + + void expect_end() { + if (!at_end()) { + set_parse_error("Unexpected data trailing signature"); + } + } + + bool has_mark() { return _mark != -1; } + + void set_mark() { + _mark = _offset; + } + + Identifier* identifier_from_mark() { + assert(has_mark(), "Mark should be set"); + if (!has_mark()) { + set_parse_error("Expected mark to be set"); + return NULL; + } else { + Identifier* id = new Identifier(_symbol, _mark, _offset - 1); + _mark = -1; + return id; + } + } +}; + + +#define CHECK_FOR_PARSE_ERROR() \ + if (STREAM->parse_error() != NULL) { \ + if (VerifyGenericSignatures) { \ + fatal(STREAM->parse_error()); \ + } \ + return NULL; \ + } 0 + +#define READ() STREAM->read(); CHECK_FOR_PARSE_ERROR() +#define PEEK() STREAM->peek(); CHECK_FOR_PARSE_ERROR() +#define PUSH(c) STREAM->push(c) +#define EXPECT(c) STREAM->read(c); CHECK_FOR_PARSE_ERROR() +#define EXPECTED(c, ch) STREAM->assert_char(c, ch); CHECK_FOR_PARSE_ERROR() +#define EXPECT_END() STREAM->expect_end(); CHECK_FOR_PARSE_ERROR() + +#define CHECK_STREAM STREAM); CHECK_FOR_PARSE_ERROR(); (0 + +#ifndef PRODUCT +void Identifier::print_on(outputStream* str) const { + for (int i = _begin; i < _end; ++i) { + str->print("%c", (char)_sym->byte_at(i)); + } +} +#endif // ndef PRODUCT + +bool Identifier::equals(Identifier* other) { + if (_sym == other->_sym && _begin == other->_begin && _end == other->_end) { + return true; + } else if (_end - _begin != other->_end - other->_begin) { + return false; + } else { + size_t len = _end - _begin; + char* addr = ((char*)_sym->bytes()) + _begin; + char* oaddr = ((char*)other->_sym->bytes()) + other->_begin; + return strncmp(addr, oaddr, len) == 0; + } +} + +bool Identifier::equals(Symbol* sym) { + Identifier id(sym, 0, sym->utf8_length()); + return equals(&id); +} + +/** + * A formal type parameter may be found in the the enclosing class, but it could + * also come from an enclosing method or outer class, in the case of inner-outer + * classes or anonymous classes. For example: + * + * class Outer { + * class Inner { + * void m(T t, V v, W w); + * } + * } + * + * In this case, the type variables in m()'s signature are not all found in the + * immediate enclosing class (Inner). class Inner has only type parameter W, + * but it's outer_class field will reference Outer's descriptor which contains + * T & V (no outer_method in this case). + * + * If you have an anonymous class, it has both an enclosing method *and* an + * enclosing class where type parameters can be declared: + * + * class MOuter { + * void bar(V v) { + * Runnable r = new Runnable() { + * public void run() {} + * public void foo(T t, V v) { ... } + * }; + * } + * } + * + * In this case, foo will be a member of some class, Runnable$1, which has no + * formal parameters itself, but has an outer_method (bar()) which provides + * type parameter V, and an outer class MOuter with type parameter T. + * + * It is also possible that the outer class is itself an inner class to some + * other class (or an anonymous class with an enclosing method), so we need to + * follow the outer_class/outer_method chain to it's end when looking for a + * type parameter. + */ +TypeParameter* Descriptor::find_type_parameter(Identifier* id, int* depth) { + + int current_depth = 0; + + MethodDescriptor* outer_method = as_method_signature(); + ClassDescriptor* outer_class = as_class_signature(); + + if (outer_class == NULL) { // 'this' is a method signature; use the holder + outer_class = outer_method->outer_class(); + } + + while (outer_method != NULL || outer_class != NULL) { + if (outer_method != NULL) { + for (int i = 0; i < outer_method->type_parameters().length(); ++i) { + TypeParameter* p = outer_method->type_parameters().at(i); + if (p->identifier()->equals(id)) { + *depth = -1; // indicates this this is a method parameter + return p; + } + } + } + if (outer_class != NULL) { + for (int i = 0; i < outer_class->type_parameters().length(); ++i) { + TypeParameter* p = outer_class->type_parameters().at(i); + if (p->identifier()->equals(id)) { + *depth = current_depth; + return p; + } + } + outer_method = outer_class->outer_method(); + outer_class = outer_class->outer_class(); + ++current_depth; + } + } + + if (VerifyGenericSignatures) { + fatal("Could not resolve identifier"); + } + + return NULL; +} + +ClassDescriptor* ClassDescriptor::parse_generic_signature(Klass* klass, TRAPS) { + return parse_generic_signature(klass, NULL, CHECK_NULL); +} + +ClassDescriptor* ClassDescriptor::parse_generic_signature( + Klass* klass, Symbol* original_name, TRAPS) { + + InstanceKlass* ik = InstanceKlass::cast(klass); + Symbol* sym = ik->generic_signature(); + + ClassDescriptor* spec; + + if (sym == NULL || (spec = ClassDescriptor::parse_generic_signature(sym)) == NULL) { + spec = ClassDescriptor::placeholder(ik); + } + + u2 outer_index = get_outer_class_index(ik, CHECK_NULL); + if (outer_index != 0) { + if (original_name == NULL) { + original_name = ik->name(); + } + Handle class_loader = Handle(THREAD, ik->class_loader()); + Handle protection_domain = Handle(THREAD, ik->protection_domain()); + + Symbol* outer_name = ik->constants()->klass_name_at(outer_index); + Klass* outer = SystemDictionary::find( + outer_name, class_loader, protection_domain, CHECK_NULL); + if (outer == NULL && !THREAD->is_Compiler_thread()) { + outer = SystemDictionary::resolve_super_or_fail(original_name, + outer_name, class_loader, protection_domain, false, CHECK_NULL); + } + + InstanceKlass* outer_ik; + ClassDescriptor* outer_spec = NULL; + if (outer == NULL) { + outer_spec = ClassDescriptor::placeholder(ik); + assert(false, "Outer class not loaded and not loadable from here"); + } else { + outer_ik = InstanceKlass::cast(outer); + outer_spec = parse_generic_signature(outer, original_name, CHECK_NULL); + } + spec->set_outer_class(outer_spec); + + u2 encl_method_idx = ik->enclosing_method_method_index(); + if (encl_method_idx != 0 && outer_ik != NULL) { + ConstantPool* cp = ik->constants(); + u2 name_index = cp->name_ref_index_at(encl_method_idx); + u2 sig_index = cp->signature_ref_index_at(encl_method_idx); + Symbol* name = cp->symbol_at(name_index); + Symbol* sig = cp->symbol_at(sig_index); + Method* m = outer_ik->find_method(name, sig); + if (m != NULL) { + Symbol* gsig = m->generic_signature(); + if (gsig != NULL) { + MethodDescriptor* gms = MethodDescriptor::parse_generic_signature(gsig, outer_spec); + spec->set_outer_method(gms); + } + } else if (VerifyGenericSignatures) { + ResourceMark rm; + stringStream ss; + ss.print("Could not find method %s %s in class %s", + name->as_C_string(), sig->as_C_string(), outer_name->as_C_string()); + fatal(ss.as_string()); + } + } + } + + spec->bind_variables_to_parameters(); + return spec; +} + +ClassDescriptor* ClassDescriptor::placeholder(InstanceKlass* klass) { + GrowableArray formals; + GrowableArray interfaces; + ClassType* super_type = NULL; + + Klass* super_klass = klass->super(); + if (super_klass != NULL) { + InstanceKlass* super = InstanceKlass::cast(super_klass); + super_type = ClassType::from_symbol(super->name()); + } + + for (int i = 0; i < klass->local_interfaces()->length(); ++i) { + InstanceKlass* iface = InstanceKlass::cast(klass->local_interfaces()->at(i)); + interfaces.append(ClassType::from_symbol(iface->name())); + } + return new ClassDescriptor(formals, super_type, interfaces); +} + +ClassDescriptor* ClassDescriptor::parse_generic_signature(Symbol* sym) { + + DescriptorStream ds(sym); + DescriptorStream* STREAM = &ds; + + GrowableArray parameters(8); + char c = READ(); + if (c == '<') { + c = READ(); + while (c != '>') { + PUSH(c); + TypeParameter* ftp = TypeParameter::parse_generic_signature(CHECK_STREAM); + parameters.append(ftp); + c = READ(); + } + } else { + PUSH(c); + } + + EXPECT('L'); + ClassType* super = ClassType::parse_generic_signature(CHECK_STREAM); + + GrowableArray signatures(2); + while (!STREAM->at_end()) { + EXPECT('L'); + ClassType* iface = ClassType::parse_generic_signature(CHECK_STREAM); + signatures.append(iface); + } + + EXPECT_END(); + + return new ClassDescriptor(parameters, super, signatures); +} + +#ifndef PRODUCT +void ClassDescriptor::print_on(outputStream* str) const { + str->indent().print_cr("ClassDescriptor {"); + { + streamIndentor si(str); + if (_type_parameters.length() > 0) { + str->indent().print_cr("Formals {"); + { + streamIndentor si(str); + for (int i = 0; i < _type_parameters.length(); ++i) { + _type_parameters.at(i)->print_on(str); + } + } + str->indent().print_cr("}"); + } + if (_super != NULL) { + str->indent().print_cr("Superclass: "); + { + streamIndentor si(str); + _super->print_on(str); + } + } + if (_interfaces.length() > 0) { + str->indent().print_cr("SuperInterfaces: {"); + { + streamIndentor si(str); + for (int i = 0; i < _interfaces.length(); ++i) { + _interfaces.at(i)->print_on(str); + } + } + str->indent().print_cr("}"); + } + if (_outer_method != NULL) { + str->indent().print_cr("Outer Method: {"); + { + streamIndentor si(str); + _outer_method->print_on(str); + } + str->indent().print_cr("}"); + } + if (_outer_class != NULL) { + str->indent().print_cr("Outer Class: {"); + { + streamIndentor si(str); + _outer_class->print_on(str); + } + str->indent().print_cr("}"); + } + } + str->indent().print_cr("}"); +} +#endif // ndef PRODUCT + +ClassType* ClassDescriptor::interface_desc(Symbol* sym) { + for (int i = 0; i < _interfaces.length(); ++i) { + if (_interfaces.at(i)->identifier()->equals(sym)) { + return _interfaces.at(i); + } + } + if (VerifyGenericSignatures) { + fatal("Did not find expected interface"); + } + return NULL; +} + +void ClassDescriptor::bind_variables_to_parameters() { + if (_outer_class != NULL) { + _outer_class->bind_variables_to_parameters(); + } + if (_outer_method != NULL) { + _outer_method->bind_variables_to_parameters(); + } + for (int i = 0; i < _type_parameters.length(); ++i) { + _type_parameters.at(i)->bind_variables_to_parameters(this, i); + } + if (_super != NULL) { + _super->bind_variables_to_parameters(this); + } + for (int i = 0; i < _interfaces.length(); ++i) { + _interfaces.at(i)->bind_variables_to_parameters(this); + } +} + +ClassDescriptor* ClassDescriptor::canonicalize(Context* ctx) { + + GrowableArray type_params(_type_parameters.length()); + for (int i = 0; i < _type_parameters.length(); ++i) { + type_params.append(_type_parameters.at(i)->canonicalize(ctx, 0)); + } + + ClassDescriptor* outer = _outer_class == NULL ? NULL : + _outer_class->canonicalize(ctx); + + ClassType* super = _super == NULL ? NULL : _super->canonicalize(ctx, 0); + + GrowableArray interfaces(_interfaces.length()); + for (int i = 0; i < _interfaces.length(); ++i) { + interfaces.append(_interfaces.at(i)->canonicalize(ctx, 0)); + } + + MethodDescriptor* md = _outer_method == NULL ? NULL : + _outer_method->canonicalize(ctx); + + return new ClassDescriptor(type_params, super, interfaces, outer, md); +} + +u2 ClassDescriptor::get_outer_class_index(InstanceKlass* klass, TRAPS) { + int inner_index = InstanceKlass::inner_class_inner_class_info_offset; + int outer_index = InstanceKlass::inner_class_outer_class_info_offset; + int name_offset = InstanceKlass::inner_class_inner_name_offset; + int next_offset = InstanceKlass::inner_class_next_offset; + + if (klass->inner_classes() == NULL || klass->inner_classes()->length() == 0) { + // No inner class info => no declaring class + return 0; + } + + Array* i_icls = klass->inner_classes(); + ConstantPool* i_cp = klass->constants(); + int i_length = i_icls->length(); + + // Find inner_klass attribute + for (int i = 0; i + next_offset < i_length; i += next_offset) { + u2 ioff = i_icls->at(i + inner_index); + u2 ooff = i_icls->at(i + outer_index); + u2 noff = i_icls->at(i + name_offset); + if (ioff != 0) { + // Check to see if the name matches the class we're looking for + // before attempting to find the class. + if (i_cp->klass_name_at_matches(klass, ioff) && ooff != 0) { + return ooff; + } + } + } + + // It may be anonymous; try for that. + u2 encl_method_class_idx = klass->enclosing_method_class_index(); + if (encl_method_class_idx != 0) { + return encl_method_class_idx; + } + + return 0; +} + +MethodDescriptor* MethodDescriptor::parse_generic_signature(Method* m, ClassDescriptor* outer) { + Symbol* generic_sig = m->generic_signature(); + MethodDescriptor* md = NULL; + if (generic_sig == NULL || (md = parse_generic_signature(generic_sig, outer)) == NULL) { + md = parse_generic_signature(m->signature(), outer); + } + assert(md != NULL, "Could not parse method signature"); + md->bind_variables_to_parameters(); + return md; +} + +MethodDescriptor* MethodDescriptor::parse_generic_signature(Symbol* sym, ClassDescriptor* outer) { + + DescriptorStream ds(sym); + DescriptorStream* STREAM = &ds; + + GrowableArray params(8); + char c = READ(); + if (c == '<') { + c = READ(); + while (c != '>') { + PUSH(c); + TypeParameter* ftp = TypeParameter::parse_generic_signature(CHECK_STREAM); + params.append(ftp); + c = READ(); + } + } else { + PUSH(c); + } + + EXPECT('('); + + GrowableArray parameters(8); + c = READ(); + while (c != ')') { + PUSH(c); + Type* arg = Type::parse_generic_signature(CHECK_STREAM); + parameters.append(arg); + c = READ(); + } + + Type* rt = Type::parse_generic_signature(CHECK_STREAM); + + GrowableArray throws; + while (!STREAM->at_end()) { + EXPECT('^'); + Type* spec = Type::parse_generic_signature(CHECK_STREAM); + throws.append(spec); + } + + return new MethodDescriptor(params, outer, parameters, rt, throws); +} + +void MethodDescriptor::bind_variables_to_parameters() { + for (int i = 0; i < _type_parameters.length(); ++i) { + _type_parameters.at(i)->bind_variables_to_parameters(this, i); + } + for (int i = 0; i < _parameters.length(); ++i) { + _parameters.at(i)->bind_variables_to_parameters(this); + } + _return_type->bind_variables_to_parameters(this); + for (int i = 0; i < _throws.length(); ++i) { + _throws.at(i)->bind_variables_to_parameters(this); + } +} + +bool MethodDescriptor::covariant_match(MethodDescriptor* other, Context* ctx) { + + if (_parameters.length() == other->_parameters.length()) { + for (int i = 0; i < _parameters.length(); ++i) { + if (!_parameters.at(i)->covariant_match(other->_parameters.at(i), ctx)) { + return false; + } + } + + if (_return_type->as_primitive() != NULL) { + return _return_type->covariant_match(other->_return_type, ctx); + } else { + // return type is a reference + return other->_return_type->as_class() != NULL || + other->_return_type->as_variable() != NULL || + other->_return_type->as_array() != NULL; + } + } else { + return false; + } +} + +MethodDescriptor* MethodDescriptor::canonicalize(Context* ctx) { + + GrowableArray type_params(_type_parameters.length()); + for (int i = 0; i < _type_parameters.length(); ++i) { + type_params.append(_type_parameters.at(i)->canonicalize(ctx, 0)); + } + + ClassDescriptor* outer = _outer_class == NULL ? NULL : + _outer_class->canonicalize(ctx); + + GrowableArray params(_parameters.length()); + for (int i = 0; i < _parameters.length(); ++i) { + params.append(_parameters.at(i)->canonicalize(ctx, 0)); + } + + Type* rt = _return_type->canonicalize(ctx, 0); + + GrowableArray throws(_throws.length()); + for (int i = 0; i < _throws.length(); ++i) { + throws.append(_throws.at(i)->canonicalize(ctx, 0)); + } + + return new MethodDescriptor(type_params, outer, params, rt, throws); +} + +#ifndef PRODUCT +TempNewSymbol MethodDescriptor::reify_signature(Context* ctx, TRAPS) { + stringStream ss(256); + + ss.print("("); + for (int i = 0; i < _parameters.length(); ++i) { + _parameters.at(i)->reify_signature(&ss, ctx); + } + ss.print(")"); + _return_type->reify_signature(&ss, ctx); + return SymbolTable::new_symbol(ss.base(), (int)ss.size(), THREAD); +} + +void MethodDescriptor::print_on(outputStream* str) const { + str->indent().print_cr("MethodDescriptor {"); + { + streamIndentor si(str); + if (_type_parameters.length() > 0) { + str->indent().print_cr("Formals: {"); + { + streamIndentor si(str); + for (int i = 0; i < _type_parameters.length(); ++i) { + _type_parameters.at(i)->print_on(str); + } + } + str->indent().print_cr("}"); + } + str->indent().print_cr("Parameters: {"); + { + streamIndentor si(str); + for (int i = 0; i < _parameters.length(); ++i) { + _parameters.at(i)->print_on(str); + } + } + str->indent().print_cr("}"); + str->indent().print_cr("Return Type: "); + { + streamIndentor si(str); + _return_type->print_on(str); + } + + if (_throws.length() > 0) { + str->indent().print_cr("Throws: {"); + { + streamIndentor si(str); + for (int i = 0; i < _throws.length(); ++i) { + _throws.at(i)->print_on(str); + } + } + str->indent().print_cr("}"); + } + } + str->indent().print_cr("}"); +} +#endif // ndef PRODUCT + +TypeParameter* TypeParameter::parse_generic_signature(DescriptorStream* STREAM) { + STREAM->set_mark(); + char c = READ(); + while (c != ':') { + c = READ(); + } + + Identifier* id = STREAM->identifier_from_mark(); + + ClassType* class_bound = NULL; + GrowableArray interface_bounds(8); + + c = READ(); + if (c != '>') { + if (c != ':') { + EXPECTED(c, 'L'); + class_bound = ClassType::parse_generic_signature(CHECK_STREAM); + c = READ(); + } + + while (c == ':') { + EXPECT('L'); + ClassType* fts = ClassType::parse_generic_signature(CHECK_STREAM); + interface_bounds.append(fts); + c = READ(); + } + } + PUSH(c); + + return new TypeParameter(id, class_bound, interface_bounds); +} + +void TypeParameter::bind_variables_to_parameters(Descriptor* sig, int position) { + if (_class_bound != NULL) { + _class_bound->bind_variables_to_parameters(sig); + } + for (int i = 0; i < _interface_bounds.length(); ++i) { + _interface_bounds.at(i)->bind_variables_to_parameters(sig); + } + _position = position; +} + +Type* TypeParameter::resolve( + Context* ctx, int inner_depth, int ctx_depth) { + + if (inner_depth == -1) { + // This indicates that the parameter is a method type parameter, which + // isn't resolveable using the class hierarchy context + return bound(); + } + + ClassType* provider = ctx->at_depth(ctx_depth); + if (provider != NULL) { + for (int i = 0; i < inner_depth && provider != NULL; ++i) { + provider = provider->outer_class(); + } + if (provider != NULL) { + TypeArgument* arg = provider->type_argument_at(_position); + if (arg != NULL) { + Type* value = arg->lower_bound(); + return value->canonicalize(ctx, ctx_depth + 1); + } + } + } + + return bound(); +} + +TypeParameter* TypeParameter::canonicalize(Context* ctx, int ctx_depth) { + ClassType* bound = _class_bound == NULL ? NULL : + _class_bound->canonicalize(ctx, ctx_depth); + + GrowableArray ifaces(_interface_bounds.length()); + for (int i = 0; i < _interface_bounds.length(); ++i) { + ifaces.append(_interface_bounds.at(i)->canonicalize(ctx, ctx_depth)); + } + + TypeParameter* ret = new TypeParameter(_identifier, bound, ifaces); + ret->_position = _position; + return ret; +} + +ClassType* TypeParameter::bound() { + if (_class_bound != NULL) { + return _class_bound; + } + + if (_interface_bounds.length() == 1) { + return _interface_bounds.at(0); + } + + return ClassType::java_lang_Object(); // TODO: investigate this case +} + +#ifndef PRODUCT +void TypeParameter::print_on(outputStream* str) const { + str->indent().print_cr("Formal: {"); + { + streamIndentor si(str); + + str->indent().print("Identifier: "); + _identifier->print_on(str); + str->print_cr(""); + if (_class_bound != NULL) { + str->indent().print_cr("Class Bound: "); + streamIndentor si(str); + _class_bound->print_on(str); + } + if (_interface_bounds.length() > 0) { + str->indent().print_cr("Interface Bounds: {"); + { + streamIndentor si(str); + for (int i = 0; i < _interface_bounds.length(); ++i) { + _interface_bounds.at(i)->print_on(str); + } + } + str->indent().print_cr("}"); + } + str->indent().print_cr("Ordinal Position: %d", _position); + } + str->indent().print_cr("}"); +} +#endif // ndef PRODUCT + +Type* Type::parse_generic_signature(DescriptorStream* STREAM) { + char c = READ(); + switch (c) { + case 'L': + return ClassType::parse_generic_signature(CHECK_STREAM); + case 'T': + return TypeVariable::parse_generic_signature(CHECK_STREAM); + case '[': + return ArrayType::parse_generic_signature(CHECK_STREAM); + default: + return new PrimitiveType(c); + } +} + +Identifier* ClassType::parse_generic_signature_simple(GrowableArray* args, + bool* has_inner, DescriptorStream* STREAM) { + STREAM->set_mark(); + + char c = READ(); + while (c != ';' && c != '.' && c != '<') { c = READ(); } + Identifier* id = STREAM->identifier_from_mark(); + + if (c == '<') { + c = READ(); + while (c != '>') { + PUSH(c); + TypeArgument* arg = TypeArgument::parse_generic_signature(CHECK_STREAM); + args->append(arg); + c = READ(); + } + c = READ(); + } + + *has_inner = (c == '.'); + if (!(*has_inner)) { + EXPECTED(c, ';'); + } + + return id; +} + +ClassType* ClassType::parse_generic_signature(DescriptorStream* STREAM) { + return parse_generic_signature(NULL, CHECK_STREAM); +} + +ClassType* ClassType::parse_generic_signature(ClassType* outer, DescriptorStream* STREAM) { + GrowableArray args; + ClassType* gct = NULL; + bool has_inner = false; + + Identifier* id = parse_generic_signature_simple(&args, &has_inner, STREAM); + if (id != NULL) { + gct = new ClassType(id, args, outer); + + if (has_inner) { + gct = parse_generic_signature(gct, CHECK_STREAM); + } + } + return gct; +} + +ClassType* ClassType::from_symbol(Symbol* sym) { + assert(sym != NULL, "Must not be null"); + GrowableArray args; + Identifier* id = new Identifier(sym, 0, sym->utf8_length()); + return new ClassType(id, args, NULL); +} + +ClassType* ClassType::java_lang_Object() { + return from_symbol(vmSymbols::java_lang_Object()); +} + +void ClassType::bind_variables_to_parameters(Descriptor* sig) { + for (int i = 0; i < _type_arguments.length(); ++i) { + _type_arguments.at(i)->bind_variables_to_parameters(sig); + } + if (_outer_class != NULL) { + _outer_class->bind_variables_to_parameters(sig); + } +} + +TypeArgument* ClassType::type_argument_at(int i) { + if (i >= 0 && i < _type_arguments.length()) { + return _type_arguments.at(i); + } else { + return NULL; + } +} + +#ifndef PRODUCT +void ClassType::reify_signature(stringStream* ss, Context* ctx) { + ss->print("L"); + _identifier->print_on(ss); + ss->print(";"); +} + +void ClassType::print_on(outputStream* str) const { + str->indent().print_cr("Class {"); + { + streamIndentor si(str); + str->indent().print("Name: "); + _identifier->print_on(str); + str->print_cr(""); + if (_type_arguments.length() != 0) { + str->indent().print_cr("Type Arguments: {"); + { + streamIndentor si(str); + for (int j = 0; j < _type_arguments.length(); ++j) { + _type_arguments.at(j)->print_on(str); + } + } + str->indent().print_cr("}"); + } + if (_outer_class != NULL) { + str->indent().print_cr("Outer Class: "); + streamIndentor sir(str); + _outer_class->print_on(str); + } + } + str->indent().print_cr("}"); +} +#endif // ndef PRODUCT + +bool ClassType::covariant_match(Type* other, Context* ctx) { + + if (other == this) { + return true; + } + + TypeVariable* variable = other->as_variable(); + if (variable != NULL) { + other = variable->resolve(ctx, 0); + } + + ClassType* outer = outer_class(); + ClassType* other_class = other->as_class(); + + if (other_class == NULL || + (outer == NULL) != (other_class->outer_class() == NULL)) { + return false; + } + + if (!_identifier->equals(other_class->_identifier)) { + return false; + } + + if (outer != NULL && !outer->covariant_match(other_class->outer_class(), ctx)) { + return false; + } + + return true; +} + +ClassType* ClassType::canonicalize(Context* ctx, int ctx_depth) { + + GrowableArray args(_type_arguments.length()); + for (int i = 0; i < _type_arguments.length(); ++i) { + args.append(_type_arguments.at(i)->canonicalize(ctx, ctx_depth)); + } + + ClassType* outer = _outer_class == NULL ? NULL : + _outer_class->canonicalize(ctx, ctx_depth); + + return new ClassType(_identifier, args, outer); +} + +TypeVariable* TypeVariable::parse_generic_signature(DescriptorStream* STREAM) { + STREAM->set_mark(); + char c = READ(); + while (c != ';') { + c = READ(); + } + Identifier* id = STREAM->identifier_from_mark(); + + return new TypeVariable(id); +} + +void TypeVariable::bind_variables_to_parameters(Descriptor* sig) { + _parameter = sig->find_type_parameter(_id, &_inner_depth); + if (VerifyGenericSignatures && _parameter == NULL) { + fatal("Could not find formal parameter"); + } +} + +Type* TypeVariable::resolve(Context* ctx, int ctx_depth) { + if (parameter() != NULL) { + return parameter()->resolve(ctx, inner_depth(), ctx_depth); + } else { + if (VerifyGenericSignatures) { + fatal("Type variable matches no parameter"); + } + return NULL; + } +} + +bool TypeVariable::covariant_match(Type* other, Context* ctx) { + + if (other == this) { + return true; + } + + Context my_context(NULL); // empty, results in erasure + Type* my_type = resolve(&my_context, 0); + if (my_type == NULL) { + return false; + } + + return my_type->covariant_match(other, ctx); +} + +Type* TypeVariable::canonicalize(Context* ctx, int ctx_depth) { + return resolve(ctx, ctx_depth); +} + +#ifndef PRODUCT +void TypeVariable::reify_signature(stringStream* ss, Context* ctx) { + Type* type = resolve(ctx, 0); + if (type != NULL) { + type->reify_signature(ss, ctx); + } +} + +void TypeVariable::print_on(outputStream* str) const { + str->indent().print_cr("Type Variable {"); + { + streamIndentor si(str); + str->indent().print("Name: "); + _id->print_on(str); + str->print_cr(""); + str->indent().print_cr("Inner depth: %d", _inner_depth); + } + str->indent().print_cr("}"); +} +#endif // ndef PRODUCT + +ArrayType* ArrayType::parse_generic_signature(DescriptorStream* STREAM) { + Type* base = Type::parse_generic_signature(CHECK_STREAM); + return new ArrayType(base); +} + +void ArrayType::bind_variables_to_parameters(Descriptor* sig) { + assert(_base != NULL, "Invalid base"); + _base->bind_variables_to_parameters(sig); +} + +bool ArrayType::covariant_match(Type* other, Context* ctx) { + assert(_base != NULL, "Invalid base"); + + if (other == this) { + return true; + } + + ArrayType* other_array = other->as_array(); + return (other_array != NULL && _base->covariant_match(other_array->_base, ctx)); +} + +ArrayType* ArrayType::canonicalize(Context* ctx, int ctx_depth) { + assert(_base != NULL, "Invalid base"); + return new ArrayType(_base->canonicalize(ctx, ctx_depth)); +} + +#ifndef PRODUCT +void ArrayType::reify_signature(stringStream* ss, Context* ctx) { + assert(_base != NULL, "Invalid base"); + ss->print("["); + _base->reify_signature(ss, ctx); +} + +void ArrayType::print_on(outputStream* str) const { + str->indent().print_cr("Array {"); + { + streamIndentor si(str); + _base->print_on(str); + } + str->indent().print_cr("}"); +} +#endif // ndef PRODUCT + +bool PrimitiveType::covariant_match(Type* other, Context* ctx) { + + PrimitiveType* other_prim = other->as_primitive(); + return (other_prim != NULL && _type == other_prim->_type); +} + +PrimitiveType* PrimitiveType::canonicalize(Context* ctx, int ctxd) { + return this; +} + +#ifndef PRODUCT +void PrimitiveType::reify_signature(stringStream* ss, Context* ctx) { + ss->print("%c", _type); +} + +void PrimitiveType::print_on(outputStream* str) const { + str->indent().print_cr("Primitive: '%c'", _type); +} +#endif // ndef PRODUCT + +void PrimitiveType::bind_variables_to_parameters(Descriptor* sig) { +} + +TypeArgument* TypeArgument::parse_generic_signature(DescriptorStream* STREAM) { + char c = READ(); + Type* type = NULL; + + switch (c) { + case '*': + return new TypeArgument(ClassType::java_lang_Object(), NULL); + break; + default: + PUSH(c); + // fall-through + case '+': + case '-': + type = Type::parse_generic_signature(CHECK_STREAM); + if (c == '+') { + return new TypeArgument(type, NULL); + } else if (c == '-') { + return new TypeArgument(ClassType::java_lang_Object(), type); + } else { + return new TypeArgument(type, type); + } + } +} + +void TypeArgument::bind_variables_to_parameters(Descriptor* sig) { + assert(_lower_bound != NULL, "Invalid lower bound"); + _lower_bound->bind_variables_to_parameters(sig); + if (_upper_bound != NULL && _upper_bound != _lower_bound) { + _upper_bound->bind_variables_to_parameters(sig); + } +} + +bool TypeArgument::covariant_match(TypeArgument* other, Context* ctx) { + assert(_lower_bound != NULL, "Invalid lower bound"); + + if (other == this) { + return true; + } + + if (!_lower_bound->covariant_match(other->lower_bound(), ctx)) { + return false; + } + return true; +} + +TypeArgument* TypeArgument::canonicalize(Context* ctx, int ctx_depth) { + assert(_lower_bound != NULL, "Invalid lower bound"); + Type* lower = _lower_bound->canonicalize(ctx, ctx_depth); + Type* upper = NULL; + + if (_upper_bound == _lower_bound) { + upper = lower; + } else if (_upper_bound != NULL) { + upper = _upper_bound->canonicalize(ctx, ctx_depth); + } + + return new TypeArgument(lower, upper); +} + +#ifndef PRODUCT +void TypeArgument::print_on(outputStream* str) const { + str->indent().print_cr("TypeArgument {"); + { + streamIndentor si(str); + if (_lower_bound != NULL) { + str->indent().print("Lower bound: "); + _lower_bound->print_on(str); + } + if (_upper_bound != NULL) { + str->indent().print("Upper bound: "); + _upper_bound->print_on(str); + } + } + str->indent().print_cr("}"); +} +#endif // ndef PRODUCT + +void Context::Mark::destroy() { + if (is_active()) { + _context->reset_to_mark(_marked_size); + } + deactivate(); +} + +void Context::apply_type_arguments( + InstanceKlass* current, InstanceKlass* super, TRAPS) { + assert(_cache != NULL, "Cannot use an empty context"); + ClassType* spec = NULL; + if (current != NULL) { + ClassDescriptor* descriptor = _cache->descriptor_for(current, CHECK); + if (super == current->super()) { + spec = descriptor->super(); + } else { + spec = descriptor->interface_desc(super->name()); + } + if (spec != NULL) { + _type_arguments.push(spec); + } + } +} + +void Context::reset_to_mark(int size) { + _type_arguments.trunc_to(size); +} + +ClassType* Context::at_depth(int i) const { + if (i < _type_arguments.length()) { + return _type_arguments.at(_type_arguments.length() - 1 - i); + } + return NULL; +} + +#ifndef PRODUCT +void Context::print_on(outputStream* str) const { + str->indent().print_cr("Context {"); + for (int i = 0; i < _type_arguments.length(); ++i) { + streamIndentor si(str); + str->indent().print("leval %d: ", i); + ClassType* ct = at_depth(i); + if (ct == NULL) { + str->print_cr(""); + continue; + } else { + str->print_cr("{"); + } + + for (int j = 0; j < ct->type_arguments_length(); ++j) { + streamIndentor si(str); + TypeArgument* ta = ct->type_argument_at(j); + Type* bound = ta->lower_bound(); + bound->print_on(str); + } + str->indent().print_cr("}"); + } + str->indent().print_cr("}"); +} +#endif // ndef PRODUCT + +ClassDescriptor* DescriptorCache::descriptor_for(InstanceKlass* ik, TRAPS) { + + ClassDescriptor** existing = _class_descriptors.get(ik); + if (existing == NULL) { + ClassDescriptor* cd = ClassDescriptor::parse_generic_signature(ik, CHECK_NULL); + _class_descriptors.put(ik, cd); + return cd; + } else { + return *existing; + } +} + +MethodDescriptor* DescriptorCache::descriptor_for( + Method* mh, ClassDescriptor* cd, TRAPS) { + assert(mh != NULL && cd != NULL, "Should not be NULL"); + MethodDescriptor** existing = _method_descriptors.get(mh); + if (existing == NULL) { + MethodDescriptor* md = MethodDescriptor::parse_generic_signature(mh, cd); + _method_descriptors.put(mh, md); + return md; + } else { + return *existing; + } +} +MethodDescriptor* DescriptorCache::descriptor_for(Method* mh, TRAPS) { + ClassDescriptor* cd = descriptor_for( + InstanceKlass::cast(mh->method_holder()), CHECK_NULL); + return descriptor_for(mh, cd, THREAD); +} + +} // namespace generic diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/classfile/genericSignatures.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/classfile/genericSignatures.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_CLASSFILE_GENERICSIGNATURES_HPP +#define SHARE_VM_CLASSFILE_GENERICSIGNATURES_HPP + +#include "classfile/symbolTable.hpp" +#include "memory/allocation.hpp" +#include "runtime/signature.hpp" +#include "utilities/growableArray.hpp" +#include "utilities/resourceHash.hpp" + +class stringStream; + +namespace generic { + +class Identifier; +class ClassDescriptor; +class MethodDescriptor; + +class TypeParameter; // a formal type parameter declared in generic signatures +class TypeArgument; // The "type value" passed to fill parameters in supertypes +class TypeVariable; // A usage of a type parameter as a value +/** + * Example: + * + * class Foo extends Bar { int m(V v) {} } + * ^^^^^^ ^^^^^^ ^^ + * type parameters type argument type variable + * + * Note that a type variable could be passed as an argument too: + * class Foo extends Bar { int m(V v) {} } + * ^^^ + * type argument's value is a type variable + */ + + +class Type; +class ClassType; +class ArrayType; +class PrimitiveType; +class Context; +class DescriptorCache; + +class DescriptorStream; + +class Identifier : public ResourceObj { + private: + Symbol* _sym; + int _begin; + int _end; + + public: + Identifier(Symbol* sym, int begin, int end) : + _sym(sym), _begin(begin), _end(end) {} + + bool equals(Identifier* other); + bool equals(Symbol* sym); + +#ifndef PRODUCT + void print_on(outputStream* str) const; +#endif // ndef PRODUCT +}; + +class Descriptor : public ResourceObj { + protected: + GrowableArray _type_parameters; + ClassDescriptor* _outer_class; + + Descriptor(GrowableArray& params, + ClassDescriptor* outer) + : _type_parameters(params), _outer_class(outer) {} + + public: + + ClassDescriptor* outer_class() { return _outer_class; } + void set_outer_class(ClassDescriptor* sig) { _outer_class = sig; } + + virtual ClassDescriptor* as_class_signature() { return NULL; } + virtual MethodDescriptor* as_method_signature() { return NULL; } + + bool is_class_signature() { return as_class_signature() != NULL; } + bool is_method_signature() { return as_method_signature() != NULL; } + + GrowableArray& type_parameters() { + return _type_parameters; + } + + TypeParameter* find_type_parameter(Identifier* id, int* param_depth); + + virtual void bind_variables_to_parameters() = 0; + +#ifndef PRODUCT + virtual void print_on(outputStream* str) const = 0; +#endif +}; + +class ClassDescriptor : public Descriptor { + private: + ClassType* _super; + GrowableArray _interfaces; + MethodDescriptor* _outer_method; + + ClassDescriptor(GrowableArray& ftp, ClassType* scs, + GrowableArray& sis, ClassDescriptor* outer_class = NULL, + MethodDescriptor* outer_method = NULL) + : Descriptor(ftp, outer_class), _super(scs), _interfaces(sis), + _outer_method(outer_method) {} + + static u2 get_outer_class_index(InstanceKlass* k, TRAPS); + static ClassDescriptor* parse_generic_signature(Klass* k, Symbol* original_name, TRAPS); + + public: + + virtual ClassDescriptor* as_class_signature() { return this; } + + MethodDescriptor* outer_method() { return _outer_method; } + void set_outer_method(MethodDescriptor* m) { _outer_method = m; } + + ClassType* super() { return _super; } + ClassType* interface_desc(Symbol* sym); + + static ClassDescriptor* parse_generic_signature(Klass* k, TRAPS); + static ClassDescriptor* parse_generic_signature(Symbol* sym); + + // For use in superclass chains in positions where this is no generic info + static ClassDescriptor* placeholder(InstanceKlass* klass); + +#ifndef PRODUCT + void print_on(outputStream* str) const; +#endif + + ClassDescriptor* canonicalize(Context* ctx); + + // Linking sets the position index in any contained TypeVariable type + // to correspond to the location of that identifier in the formal type + // parameters. + void bind_variables_to_parameters(); +}; + +class MethodDescriptor : public Descriptor { + private: + GrowableArray _parameters; + Type* _return_type; + GrowableArray _throws; + + MethodDescriptor(GrowableArray& ftp, ClassDescriptor* outer, + GrowableArray& sigs, Type* rt, GrowableArray& throws) + : Descriptor(ftp, outer), _parameters(sigs), _return_type(rt), + _throws(throws) {} + + public: + + static MethodDescriptor* parse_generic_signature(Method* m, ClassDescriptor* outer); + static MethodDescriptor* parse_generic_signature(Symbol* sym, ClassDescriptor* outer); + + MethodDescriptor* as_method_signature() { return this; } + + // Performs generic analysis on the method parameters to determine + // if both methods refer to the same argument types. + bool covariant_match(MethodDescriptor* other, Context* ctx); + + // Returns a new method descriptor with all generic variables + // removed and replaced with whatever is indicated using the Context. + MethodDescriptor* canonicalize(Context* ctx); + + void bind_variables_to_parameters(); + +#ifndef PRODUCT + TempNewSymbol reify_signature(Context* ctx, TRAPS); + void print_on(outputStream* str) const; +#endif +}; + +class TypeParameter : public ResourceObj { + private: + Identifier* _identifier; + ClassType* _class_bound; + GrowableArray _interface_bounds; + + // The position is the ordinal location of the parameter within the + // formal parameter list (excluding outer classes). It is only set for + // formal type parameters that are associated with a class -- method + // type parameters are left as -1. When resolving a generic variable to + // find the actual type, this index is used to access the generic type + // argument in the provided context object. + int _position; // Assigned during variable linking + + TypeParameter(Identifier* id, ClassType* class_bound, + GrowableArray& interface_bounds) : + _identifier(id), _class_bound(class_bound), + _interface_bounds(interface_bounds), _position(-1) {} + + public: + static TypeParameter* parse_generic_signature(DescriptorStream* str); + + ClassType* bound(); + int position() { return _position; } + + void bind_variables_to_parameters(Descriptor* sig, int position); + Identifier* identifier() { return _identifier; } + + Type* resolve(Context* ctx, int inner_depth, int ctx_depth); + TypeParameter* canonicalize(Context* ctx, int ctx_depth); + +#ifndef PRODUCT + void print_on(outputStream* str) const; +#endif +}; + +class Type : public ResourceObj { + public: + static Type* parse_generic_signature(DescriptorStream* str); + + virtual ClassType* as_class() { return NULL; } + virtual TypeVariable* as_variable() { return NULL; } + virtual ArrayType* as_array() { return NULL; } + virtual PrimitiveType* as_primitive() { return NULL; } + + virtual bool covariant_match(Type* gt, Context* ctx) = 0; + virtual Type* canonicalize(Context* ctx, int ctx_depth) = 0; + + virtual void bind_variables_to_parameters(Descriptor* sig) = 0; + +#ifndef PRODUCT + virtual void reify_signature(stringStream* ss, Context* ctx) = 0; + virtual void print_on(outputStream* str) const = 0; +#endif +}; + +class ClassType : public Type { + friend class ClassDescriptor; + protected: + Identifier* _identifier; + GrowableArray _type_arguments; + ClassType* _outer_class; + + ClassType(Identifier* identifier, + GrowableArray& args, + ClassType* outer) + : _identifier(identifier), _type_arguments(args), _outer_class(outer) {} + + // Returns true if there are inner classes to read + static Identifier* parse_generic_signature_simple( + GrowableArray* args, + bool* has_inner, DescriptorStream* str); + + static ClassType* parse_generic_signature(ClassType* outer, + DescriptorStream* str); + static ClassType* from_symbol(Symbol* sym); + + public: + ClassType* as_class() { return this; } + + static ClassType* parse_generic_signature(DescriptorStream* str); + static ClassType* java_lang_Object(); + + Identifier* identifier() { return _identifier; } + int type_arguments_length() { return _type_arguments.length(); } + TypeArgument* type_argument_at(int i); + + virtual ClassType* outer_class() { return _outer_class; } + + bool covariant_match(Type* gt, Context* ctx); + ClassType* canonicalize(Context* ctx, int context_depth); + + void bind_variables_to_parameters(Descriptor* sig); + +#ifndef PRODUCT + void reify_signature(stringStream* ss, Context* ctx); + void print_on(outputStream* str) const; +#endif +}; + +class TypeVariable : public Type { + private: + Identifier* _id; + TypeParameter* _parameter; // assigned during linking + + // how many steps "out" from inner classes, -1 if method + int _inner_depth; + + TypeVariable(Identifier* id) + : _id(id), _parameter(NULL), _inner_depth(0) {} + + public: + TypeVariable* as_variable() { return this; } + + static TypeVariable* parse_generic_signature(DescriptorStream* str); + + Identifier* identifier() { return _id; } + TypeParameter* parameter() { return _parameter; } + int inner_depth() { return _inner_depth; } + + void bind_variables_to_parameters(Descriptor* sig); + + Type* resolve(Context* ctx, int ctx_depth); + bool covariant_match(Type* gt, Context* ctx); + Type* canonicalize(Context* ctx, int ctx_depth); + +#ifndef PRODUCT + void reify_signature(stringStream* ss, Context* ctx); + void print_on(outputStream* str) const; +#endif +}; + +class ArrayType : public Type { + private: + Type* _base; + + ArrayType(Type* base) : _base(base) {} + + public: + ArrayType* as_array() { return this; } + + static ArrayType* parse_generic_signature(DescriptorStream* str); + + bool covariant_match(Type* gt, Context* ctx); + ArrayType* canonicalize(Context* ctx, int ctx_depth); + + void bind_variables_to_parameters(Descriptor* sig); + +#ifndef PRODUCT + void reify_signature(stringStream* ss, Context* ctx); + void print_on(outputStream* str) const; +#endif +}; + +class PrimitiveType : public Type { + friend class Type; + private: + char _type; // includes V for void + + PrimitiveType(char& type) : _type(type) {} + + public: + PrimitiveType* as_primitive() { return this; } + + bool covariant_match(Type* gt, Context* ctx); + PrimitiveType* canonicalize(Context* ctx, int ctx_depth); + + void bind_variables_to_parameters(Descriptor* sig); + +#ifndef PRODUCT + void reify_signature(stringStream* ss, Context* ctx); + void print_on(outputStream* str) const; +#endif +}; + +class TypeArgument : public ResourceObj { + private: + Type* _lower_bound; + Type* _upper_bound; // may be null or == _lower_bound + + TypeArgument(Type* lower_bound, Type* upper_bound) + : _lower_bound(lower_bound), _upper_bound(upper_bound) {} + + public: + + static TypeArgument* parse_generic_signature(DescriptorStream* str); + + Type* lower_bound() { return _lower_bound; } + Type* upper_bound() { return _upper_bound; } + + void bind_variables_to_parameters(Descriptor* sig); + TypeArgument* canonicalize(Context* ctx, int ctx_depth); + + bool covariant_match(TypeArgument* a, Context* ctx); + +#ifndef PRODUCT + void print_on(outputStream* str) const; +#endif +}; + + +class Context : public ResourceObj { + private: + DescriptorCache* _cache; + GrowableArray _type_arguments; + + void reset_to_mark(int size); + + public: + // When this object goes out of scope or 'destroy' is + // called, then the application of the type to the + // context is wound-back (unless it's been deactivated). + class Mark : public StackObj { + private: + mutable Context* _context; + int _marked_size; + + bool is_active() const { return _context != NULL; } + void deactivate() const { _context = NULL; } + + public: + Mark() : _context(NULL), _marked_size(0) {} + Mark(Context* ctx, int sz) : _context(ctx), _marked_size(sz) {} + Mark(const Mark& m) : _context(m._context), _marked_size(m._marked_size) { + m.deactivate(); // Ownership is transferred + } + + Mark& operator=(const Mark& cm) { + destroy(); + _context = cm._context; + _marked_size = cm._marked_size; + cm.deactivate(); + return *this; + } + + void destroy(); + ~Mark() { destroy(); } + }; + + Context(DescriptorCache* cache) : _cache(cache) {} + + Mark mark() { return Mark(this, _type_arguments.length()); } + void apply_type_arguments(InstanceKlass* current, InstanceKlass* super,TRAPS); + + ClassType* at_depth(int i) const; + +#ifndef PRODUCT + void print_on(outputStream* str) const; +#endif +}; + +/** + * Contains a cache of descriptors for classes and methods so they can be + * looked-up instead of reparsing each time they are needed. + */ +class DescriptorCache : public ResourceObj { + private: + ResourceHashtable _class_descriptors; + ResourceHashtable _method_descriptors; + + public: + ClassDescriptor* descriptor_for(InstanceKlass* ikh, TRAPS); + + MethodDescriptor* descriptor_for(Method* mh, ClassDescriptor* cd, TRAPS); + // Class descriptor derived from method holder + MethodDescriptor* descriptor_for(Method* mh, TRAPS); +}; + +} // namespace generic + +#endif // SHARE_VM_CLASSFILE_GENERICSIGNATURES_HPP + diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/classfile/systemDictionary.hpp --- a/src/share/vm/classfile/systemDictionary.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/classfile/systemDictionary.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -137,6 +137,7 @@ /* NOTE: needed too early in bootstrapping process to have checks based on JDK version */ \ /* Universe::is_gte_jdk14x_version() is not set up by this point. */ \ /* It's okay if this turns out to be NULL in non-1.4 JDKs. */ \ + do_klass(lambda_MagicLambdaImpl_klass, java_lang_invoke_MagicLambdaImpl, Opt ) \ do_klass(reflect_MagicAccessorImpl_klass, sun_reflect_MagicAccessorImpl, Opt ) \ do_klass(reflect_MethodAccessorImpl_klass, sun_reflect_MethodAccessorImpl, Opt_Only_JDK14NewRef) \ do_klass(reflect_ConstructorAccessorImpl_klass, sun_reflect_ConstructorAccessorImpl, Opt_Only_JDK14NewRef) \ diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/classfile/verifier.cpp --- a/src/share/vm/classfile/verifier.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/classfile/verifier.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -555,9 +555,10 @@ if (was_recursively_verified()) return; Method* m = methods->at(index); - if (m->is_native() || m->is_abstract()) { + if (m->is_native() || m->is_abstract() || m->is_overpass()) { // If m is native or abstract, skip it. It is checked in class file - // parser that methods do not override a final method. + // parser that methods do not override a final method. Overpass methods + // are trusted since the VM generates them. continue; } verify_method(methodHandle(THREAD, m), CHECK_VERIFY(this)); @@ -2304,11 +2305,21 @@ // Make sure the constant pool item is the right type u2 index = bcs->get_index_u2(); Bytecodes::Code opcode = bcs->raw_code(); - unsigned int types = (opcode == Bytecodes::_invokeinterface - ? 1 << JVM_CONSTANT_InterfaceMethodref - : opcode == Bytecodes::_invokedynamic - ? 1 << JVM_CONSTANT_InvokeDynamic - : 1 << JVM_CONSTANT_Methodref); + unsigned int types; + switch (opcode) { + case Bytecodes::_invokeinterface: + types = 1 << JVM_CONSTANT_InterfaceMethodref; + break; + case Bytecodes::_invokedynamic: + types = 1 << JVM_CONSTANT_InvokeDynamic; + break; + case Bytecodes::_invokespecial: + types = (1 << JVM_CONSTANT_InterfaceMethodref) | + (1 << JVM_CONSTANT_Methodref); + break; + default: + types = 1 << JVM_CONSTANT_Methodref; + } verify_cp_type(bcs->bci(), index, cp, types, CHECK_VERIFY(this)); // Get method name and signature diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/classfile/vmSymbols.hpp --- a/src/share/vm/classfile/vmSymbols.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/classfile/vmSymbols.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -110,10 +110,12 @@ template(sun_jkernel_DownloadManager, "sun/jkernel/DownloadManager") \ template(getBootClassPathEntryForClass_name, "getBootClassPathEntryForClass") \ template(sun_misc_PostVMInitHook, "sun/misc/PostVMInitHook") \ + template(sun_misc_Launcher_ExtClassLoader, "sun/misc/Launcher$ExtClassLoader") \ \ /* Java runtime version access */ \ template(sun_misc_Version, "sun/misc/Version") \ template(java_runtime_name_name, "java_runtime_name") \ + template(java_runtime_version_name, "java_runtime_version") \ \ /* class file format tags */ \ template(tag_source_file, "SourceFile") \ @@ -257,6 +259,7 @@ template(java_lang_invoke_DontInline_signature, "Ljava/lang/invoke/DontInline;") \ template(java_lang_invoke_LambdaForm_Compiled_signature, "Ljava/lang/invoke/LambdaForm$Compiled;") \ template(java_lang_invoke_LambdaForm_Hidden_signature, "Ljava/lang/invoke/LambdaForm$Hidden;") \ + template(java_lang_invoke_MagicLambdaImpl, "java/lang/invoke/MagicLambdaImpl") \ /* internal up-calls made only by the JVM, via class sun.invoke.MethodHandleNatives: */ \ template(findMethodHandleType_name, "findMethodHandleType") \ template(findMethodHandleType_signature, "(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/invoke/MethodType;") \ @@ -723,6 +726,21 @@ /* java/lang/ref/Reference */ \ do_intrinsic(_Reference_get, java_lang_ref_Reference, get_name, void_object_signature, F_R) \ \ + /* support for com.sum.crypto.provider.AESCrypt and some of its callers */ \ + do_class(com_sun_crypto_provider_aescrypt, "com/sun/crypto/provider/AESCrypt") \ + do_intrinsic(_aescrypt_encryptBlock, com_sun_crypto_provider_aescrypt, encryptBlock_name, byteArray_int_byteArray_int_signature, F_R) \ + do_intrinsic(_aescrypt_decryptBlock, com_sun_crypto_provider_aescrypt, decryptBlock_name, byteArray_int_byteArray_int_signature, F_R) \ + do_name( encryptBlock_name, "encryptBlock") \ + do_name( decryptBlock_name, "decryptBlock") \ + do_signature(byteArray_int_byteArray_int_signature, "([BI[BI)V") \ + \ + do_class(com_sun_crypto_provider_cipherBlockChaining, "com/sun/crypto/provider/CipherBlockChaining") \ + do_intrinsic(_cipherBlockChaining_encryptAESCrypt, com_sun_crypto_provider_cipherBlockChaining, encrypt_name, byteArray_int_int_byteArray_int_signature, F_R) \ + do_intrinsic(_cipherBlockChaining_decryptAESCrypt, com_sun_crypto_provider_cipherBlockChaining, decrypt_name, byteArray_int_int_byteArray_int_signature, F_R) \ + do_name( encrypt_name, "encrypt") \ + do_name( decrypt_name, "decrypt") \ + do_signature(byteArray_int_int_byteArray_int_signature, "([BII[BI)V") \ + \ /* support for sun.misc.Unsafe */ \ do_class(sun_misc_Unsafe, "sun/misc/Unsafe") \ \ diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/code/dependencies.cpp --- a/src/share/vm/code/dependencies.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/code/dependencies.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -1160,7 +1160,11 @@ // We could also return false if m does not yet appear to be // executed, if the VM version supports this distinction also. - return !m->is_abstract(); + return !m->is_abstract() && + !InstanceKlass::cast(m->method_holder())->is_interface(); + // TODO: investigate whether default methods should be + // considered as "concrete" in this situation. For now they + // are not. } diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/compiler/compilerOracle.cpp --- a/src/share/vm/compiler/compilerOracle.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/compiler/compilerOracle.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -574,7 +574,7 @@ char token[1024]; int pos = 0; int c = getc(stream); - while(c != EOF) { + while(c != EOF && pos < (int)(sizeof(token)-1)) { if (c == '\n') { token[pos++] = '\0'; parse_from_line(token); @@ -595,7 +595,7 @@ int pos = 0; const char* sp = str; int c = *sp++; - while (c != '\0') { + while (c != '\0' && pos < (int)(sizeof(token)-1)) { if (c == '\n') { token[pos++] = '\0'; parse_line(token); diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/compiler/disassembler.cpp --- a/src/share/vm/compiler/disassembler.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/compiler/disassembler.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -55,16 +55,18 @@ bool Disassembler::_tried_to_load_library = false; // This routine is in the shared library: +Disassembler::decode_func_virtual Disassembler::_decode_instructions_virtual = NULL; Disassembler::decode_func Disassembler::_decode_instructions = NULL; static const char hsdis_library_name[] = "hsdis-"HOTSPOT_LIB_ARCH; -static const char decode_instructions_name[] = "decode_instructions_virtual"; - +static const char decode_instructions_virtual_name[] = "decode_instructions_virtual"; +static const char decode_instructions_name[] = "decode_instructions"; +static bool use_new_version = true; #define COMMENT_COLUMN 40 LP64_ONLY(+8) /*could be an option*/ #define BYTES_COMMENT ";..." /* funky byte display comment */ bool Disassembler::load_library() { - if (_decode_instructions != NULL) { + if (_decode_instructions_virtual != NULL || _decode_instructions != NULL) { // Already succeeded. return true; } @@ -123,11 +125,19 @@ _library = os::dll_load(buf, ebuf, sizeof ebuf); } if (_library != NULL) { + _decode_instructions_virtual = CAST_TO_FN_PTR(Disassembler::decode_func_virtual, + os::dll_lookup(_library, decode_instructions_virtual_name)); + } + if (_decode_instructions_virtual == NULL) { + // could not spot in new version, try old version _decode_instructions = CAST_TO_FN_PTR(Disassembler::decode_func, os::dll_lookup(_library, decode_instructions_name)); + use_new_version = false; + } else { + use_new_version = true; } _tried_to_load_library = true; - if (_decode_instructions == NULL) { + if (_decode_instructions_virtual == NULL && _decode_instructions == NULL) { tty->print_cr("Could not load %s; %s; %s", buf, ((_library != NULL) ? "entry point is missing" @@ -450,17 +460,31 @@ // This is mainly for debugging the library itself. FILE* out = stdout; FILE* xmlout = (_print_raw > 1 ? out : NULL); - return (address) - (*Disassembler::_decode_instructions)((uintptr_t)start, (uintptr_t)end, - start, end - start, + return use_new_version ? + (address) + (*Disassembler::_decode_instructions_virtual)((uintptr_t)start, (uintptr_t)end, + start, end - start, + NULL, (void*) xmlout, + NULL, (void*) out, + options(), 0/*nice new line*/) + : + (address) + (*Disassembler::_decode_instructions)(start, end, NULL, (void*) xmlout, NULL, (void*) out, options()); } - return (address) - (*Disassembler::_decode_instructions)((uintptr_t)start, (uintptr_t)end, - start, end - start, + return use_new_version ? + (address) + (*Disassembler::_decode_instructions_virtual)((uintptr_t)start, (uintptr_t)end, + start, end - start, + &event_to_env, (void*) this, + &printf_to_env, (void*) this, + options(), 0/*nice new line*/) + : + (address) + (*Disassembler::_decode_instructions)(start, end, &event_to_env, (void*) this, &printf_to_env, (void*) this, options()); diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/compiler/disassembler.hpp --- a/src/share/vm/compiler/disassembler.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/compiler/disassembler.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -49,18 +49,27 @@ friend class decode_env; private: // this is the type of the dll entry point: - typedef void* (*decode_func)(uintptr_t start_va, uintptr_t end_va, + typedef void* (*decode_func_virtual)(uintptr_t start_va, uintptr_t end_va, unsigned char* buffer, uintptr_t length, void* (*event_callback)(void*, const char*, void*), void* event_stream, int (*printf_callback)(void*, const char*, ...), void* printf_stream, + const char* options, + int newline); + // this is the type of the dll entry point for old version: + typedef void* (*decode_func)(void* start_va, void* end_va, + void* (*event_callback)(void*, const char*, void*), + void* event_stream, + int (*printf_callback)(void*, const char*, ...), + void* printf_stream, const char* options); // points to the library. static void* _library; // bailout static bool _tried_to_load_library; // points to the decode function. + static decode_func_virtual _decode_instructions_virtual; static decode_func _decode_instructions; // tries to load library and return whether it succedded. static bool load_library(); @@ -85,7 +94,9 @@ public: static bool can_decode() { - return (_decode_instructions != NULL) || load_library(); + return (_decode_instructions_virtual != NULL) || + (_decode_instructions != NULL) || + load_library(); } static void decode(CodeBlob *cb, outputStream* st = NULL); static void decode(nmethod* nm, outputStream* st = NULL); diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/gc_implementation/concurrentMarkSweep/adaptiveFreeList.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/adaptiveFreeList.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "gc_implementation/concurrentMarkSweep/adaptiveFreeList.hpp" +#include "gc_implementation/concurrentMarkSweep/freeChunk.hpp" +#include "memory/freeBlockDictionary.hpp" +#include "memory/sharedHeap.hpp" +#include "runtime/globals.hpp" +#include "runtime/mutex.hpp" +#include "runtime/vmThread.hpp" + +template <> +void AdaptiveFreeList::print_on(outputStream* st, const char* c) const { + if (c != NULL) { + st->print("%16s", c); + } else { + st->print(SIZE_FORMAT_W(16), size()); + } + st->print("\t" + SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" + SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\n", + bfr_surp(), surplus(), desired(), prev_sweep(), before_sweep(), + count(), coal_births(), coal_deaths(), split_births(), split_deaths()); +} + +template +AdaptiveFreeList::AdaptiveFreeList() : FreeList(), _hint(0) { + init_statistics(); +} + +template +AdaptiveFreeList::AdaptiveFreeList(Chunk* fc) : FreeList(fc), _hint(0) { + init_statistics(); +#ifndef PRODUCT + _allocation_stats.set_returned_bytes(size() * HeapWordSize); +#endif +} + +template +void AdaptiveFreeList::initialize() { + FreeList::initialize(); + set_hint(0); + init_statistics(true /* split_birth */); +} + +template +void AdaptiveFreeList::reset(size_t hint) { + FreeList::reset(); + set_hint(hint); +} + +#ifndef PRODUCT +template +void AdaptiveFreeList::assert_proper_lock_protection_work() const { + assert(protecting_lock() != NULL, "Don't call this directly"); + assert(ParallelGCThreads > 0, "Don't call this directly"); + Thread* thr = Thread::current(); + if (thr->is_VM_thread() || thr->is_ConcurrentGC_thread()) { + // assert that we are holding the freelist lock + } else if (thr->is_GC_task_thread()) { + assert(protecting_lock()->owned_by_self(), "FreeList RACE DETECTED"); + } else if (thr->is_Java_thread()) { + assert(!SafepointSynchronize::is_at_safepoint(), "Should not be executing"); + } else { + ShouldNotReachHere(); // unaccounted thread type? + } +} +#endif +template +void AdaptiveFreeList::init_statistics(bool split_birth) { + _allocation_stats.initialize(split_birth); +} + +template +size_t AdaptiveFreeList::get_better_size() { + + // A candidate chunk has been found. If it is already under + // populated and there is a hinT, REturn the hint(). Else + // return the size of this chunk. + if (surplus() <= 0) { + if (hint() != 0) { + return hint(); + } else { + return size(); + } + } else { + // This list has a surplus so use it. + return size(); + } +} + + +template +void AdaptiveFreeList::return_chunk_at_head(Chunk* chunk) { + assert_proper_lock_protection(); + return_chunk_at_head(chunk, true); +} + +template +void AdaptiveFreeList::return_chunk_at_head(Chunk* chunk, bool record_return) { + FreeList::return_chunk_at_head(chunk, record_return); +#ifdef ASSERT + if (record_return) { + increment_returned_bytes_by(size()*HeapWordSize); + } +#endif +} + +template +void AdaptiveFreeList::return_chunk_at_tail(Chunk* chunk) { + return_chunk_at_tail(chunk, true); +} + +template +void AdaptiveFreeList::return_chunk_at_tail(Chunk* chunk, bool record_return) { + FreeList::return_chunk_at_tail(chunk, record_return); +#ifdef ASSERT + if (record_return) { + increment_returned_bytes_by(size()*HeapWordSize); + } +#endif +} + +#ifndef PRODUCT +template +void AdaptiveFreeList::verify_stats() const { + // The +1 of the LH comparand is to allow some "looseness" in + // checking: we usually call this interface when adding a block + // and we'll subsequently update the stats; we cannot update the + // stats beforehand because in the case of the large-block BT + // dictionary for example, this might be the first block and + // in that case there would be no place that we could record + // the stats (which are kept in the block itself). + assert((_allocation_stats.prev_sweep() + _allocation_stats.split_births() + + _allocation_stats.coal_births() + 1) // Total Production Stock + 1 + >= (_allocation_stats.split_deaths() + _allocation_stats.coal_deaths() + + (ssize_t)count()), // Total Current Stock + depletion + err_msg("FreeList " PTR_FORMAT " of size " SIZE_FORMAT + " violates Conservation Principle: " + "prev_sweep(" SIZE_FORMAT ")" + " + split_births(" SIZE_FORMAT ")" + " + coal_births(" SIZE_FORMAT ") + 1 >= " + " split_deaths(" SIZE_FORMAT ")" + " coal_deaths(" SIZE_FORMAT ")" + " + count(" SSIZE_FORMAT ")", + this, size(), _allocation_stats.prev_sweep(), _allocation_stats.split_births(), + _allocation_stats.split_births(), _allocation_stats.split_deaths(), + _allocation_stats.coal_deaths(), count())); +} +#endif + +// Needs to be after the definitions have been seen. +template class AdaptiveFreeList; diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/gc_implementation/concurrentMarkSweep/adaptiveFreeList.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/adaptiveFreeList.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_MEMORY_ADAPTIVEFREELIST_HPP +#define SHARE_VM_MEMORY_ADAPTIVEFREELIST_HPP + +#include "memory/freeList.hpp" +#include "gc_implementation/shared/allocationStats.hpp" + +class CompactibleFreeListSpace; + +// A class for maintaining a free list of Chunk's. The FreeList +// maintains a the structure of the list (head, tail, etc.) plus +// statistics for allocations from the list. The links between items +// are not part of FreeList. The statistics are +// used to make decisions about coalescing Chunk's when they +// are swept during collection. +// +// See the corresponding .cpp file for a description of the specifics +// for that implementation. + +class Mutex; + +template +class AdaptiveFreeList : public FreeList { + friend class CompactibleFreeListSpace; + friend class VMStructs; + // friend class PrintTreeCensusClosure; + + size_t _hint; // next larger size list with a positive surplus + + AllocationStats _allocation_stats; // allocation-related statistics + + public: + + AdaptiveFreeList(); + AdaptiveFreeList(Chunk* fc); + + using FreeList::assert_proper_lock_protection; +#ifdef ASSERT + using FreeList::protecting_lock; +#endif + using FreeList::count; + using FreeList::size; + using FreeList::verify_chunk_in_free_list; + using FreeList::getFirstNChunksFromList; + using FreeList::print_on; + void return_chunk_at_head(Chunk* fc, bool record_return); + void return_chunk_at_head(Chunk* fc); + void return_chunk_at_tail(Chunk* fc, bool record_return); + void return_chunk_at_tail(Chunk* fc); + using FreeList::return_chunk_at_tail; + using FreeList::remove_chunk; + using FreeList::prepend; + using FreeList::print_labels_on; + using FreeList::get_chunk_at_head; + + // Initialize. + void initialize(); + + // Reset the head, tail, hint, and count of a free list. + void reset(size_t hint); + + void assert_proper_lock_protection_work() const PRODUCT_RETURN; + + void print_on(outputStream* st, const char* c = NULL) const; + + size_t hint() const { + return _hint; + } + void set_hint(size_t v) { + assert_proper_lock_protection(); + assert(v == 0 || size() < v, "Bad hint"); + _hint = v; + } + + size_t get_better_size(); + + // Accessors for statistics + void init_statistics(bool split_birth = false); + + AllocationStats* allocation_stats() { + assert_proper_lock_protection(); + return &_allocation_stats; + } + + ssize_t desired() const { + return _allocation_stats.desired(); + } + void set_desired(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_desired(v); + } + void compute_desired(float inter_sweep_current, + float inter_sweep_estimate, + float intra_sweep_estimate) { + assert_proper_lock_protection(); + _allocation_stats.compute_desired(count(), + inter_sweep_current, + inter_sweep_estimate, + intra_sweep_estimate); + } + ssize_t coal_desired() const { + return _allocation_stats.coal_desired(); + } + void set_coal_desired(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_coal_desired(v); + } + + ssize_t surplus() const { + return _allocation_stats.surplus(); + } + void set_surplus(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_surplus(v); + } + void increment_surplus() { + assert_proper_lock_protection(); + _allocation_stats.increment_surplus(); + } + void decrement_surplus() { + assert_proper_lock_protection(); + _allocation_stats.decrement_surplus(); + } + + ssize_t bfr_surp() const { + return _allocation_stats.bfr_surp(); + } + void set_bfr_surp(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_bfr_surp(v); + } + ssize_t prev_sweep() const { + return _allocation_stats.prev_sweep(); + } + void set_prev_sweep(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_prev_sweep(v); + } + ssize_t before_sweep() const { + return _allocation_stats.before_sweep(); + } + void set_before_sweep(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_before_sweep(v); + } + + ssize_t coal_births() const { + return _allocation_stats.coal_births(); + } + void set_coal_births(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_coal_births(v); + } + void increment_coal_births() { + assert_proper_lock_protection(); + _allocation_stats.increment_coal_births(); + } + + ssize_t coal_deaths() const { + return _allocation_stats.coal_deaths(); + } + void set_coal_deaths(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_coal_deaths(v); + } + void increment_coal_deaths() { + assert_proper_lock_protection(); + _allocation_stats.increment_coal_deaths(); + } + + ssize_t split_births() const { + return _allocation_stats.split_births(); + } + void set_split_births(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_split_births(v); + } + void increment_split_births() { + assert_proper_lock_protection(); + _allocation_stats.increment_split_births(); + } + + ssize_t split_deaths() const { + return _allocation_stats.split_deaths(); + } + void set_split_deaths(ssize_t v) { + assert_proper_lock_protection(); + _allocation_stats.set_split_deaths(v); + } + void increment_split_deaths() { + assert_proper_lock_protection(); + _allocation_stats.increment_split_deaths(); + } + +#ifndef PRODUCT + // For debugging. The "_returned_bytes" in all the lists are summed + // and compared with the total number of bytes swept during a + // collection. + size_t returned_bytes() const { return _allocation_stats.returned_bytes(); } + void set_returned_bytes(size_t v) { _allocation_stats.set_returned_bytes(v); } + void increment_returned_bytes_by(size_t v) { + _allocation_stats.set_returned_bytes(_allocation_stats.returned_bytes() + v); + } + // Stats verification + void verify_stats() const; +#endif // NOT PRODUCT +}; + +#endif // SHARE_VM_MEMORY_ADAPTIVEFREELIST_HPP diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -91,7 +91,7 @@ _collector(NULL) { assert(sizeof(FreeChunk) / BytesPerWord <= MinChunkSize, - "FreeChunk is larger than expected"); + "FreeChunk is larger than expected"); _bt.set_space(this); initialize(mr, SpaceDecorator::Clear, SpaceDecorator::Mangle); // We have all of "mr", all of which we place in the dictionary @@ -101,14 +101,14 @@ // implementation, namely, the simple binary tree (splaying // temporarily disabled). switch (dictionaryChoice) { + case FreeBlockDictionary::dictionaryBinaryTree: + _dictionary = new BinaryTreeDictionary(mr); + break; case FreeBlockDictionary::dictionarySplayTree: case FreeBlockDictionary::dictionarySkipList: default: warning("dictionaryChoice: selected option not understood; using" " default BinaryTreeDictionary implementation instead."); - case FreeBlockDictionary::dictionaryBinaryTree: - _dictionary = new BinaryTreeDictionary(mr, use_adaptive_freelists); - break; } assert(_dictionary != NULL, "CMS dictionary initialization"); // The indexed free lists are initially all empty and are lazily @@ -453,7 +453,7 @@ reportIndexedFreeListStatistics(); gclog_or_tty->print_cr("Layout of Indexed Freelists"); gclog_or_tty->print_cr("---------------------------"); - FreeList::print_labels_on(st, "size"); + AdaptiveFreeList::print_labels_on(st, "size"); for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { _indexedFreeList[i].print_on(gclog_or_tty); for (FreeChunk* fc = _indexedFreeList[i].head(); fc != NULL; @@ -1319,7 +1319,7 @@ size_t currSize = numWords + MinChunkSize; assert(currSize % MinObjAlignment == 0, "currSize should be aligned"); for (i = currSize; i < IndexSetSize; i += IndexSetStride) { - FreeList* fl = &_indexedFreeList[i]; + AdaptiveFreeList* fl = &_indexedFreeList[i]; if (fl->head()) { ret = getFromListGreater(fl, numWords); assert(ret == NULL || ret->is_free(), "Should be returning a free chunk"); @@ -1702,7 +1702,9 @@ _dictionary->return_chunk(chunk); #ifndef PRODUCT if (CMSCollector::abstract_state() != CMSCollector::Sweeping) { - TreeChunk::as_TreeChunk(chunk)->list()->verify_stats(); + TreeChunk* tc = TreeChunk::as_TreeChunk(chunk); + TreeList* tl = tc->list(); + tl->verify_stats(); } #endif // PRODUCT } @@ -1745,7 +1747,7 @@ { MutexLockerEx x(lock, Mutex::_no_safepoint_check_flag); ec = dictionary()->find_largest_dict(); // get largest block - if (ec != NULL && ec->end() == chunk) { + if (ec != NULL && ec->end() == (uintptr_t*) chunk) { // It's a coterminal block - we can coalesce. size_t old_size = ec->size(); coalDeath(old_size); @@ -1850,11 +1852,11 @@ the excess is >= MIN_CHUNK. */ size_t start = align_object_size(numWords + MinChunkSize); if (start < IndexSetSize) { - FreeList* it = _indexedFreeList; + AdaptiveFreeList* it = _indexedFreeList; size_t hint = _indexedFreeList[start].hint(); while (hint < IndexSetSize) { assert(hint % MinObjAlignment == 0, "hint should be aligned"); - FreeList *fl = &_indexedFreeList[hint]; + AdaptiveFreeList *fl = &_indexedFreeList[hint]; if (fl->surplus() > 0 && fl->head() != NULL) { // Found a list with surplus, reset original hint // and split out a free chunk which is returned. @@ -1873,7 +1875,7 @@ } /* Requires fl->size >= numWords + MinChunkSize */ -FreeChunk* CompactibleFreeListSpace::getFromListGreater(FreeList* fl, +FreeChunk* CompactibleFreeListSpace::getFromListGreater(AdaptiveFreeList* fl, size_t numWords) { FreeChunk *curr = fl->head(); size_t oldNumWords = curr->size(); @@ -2155,7 +2157,7 @@ assert_locked(); size_t i; for (i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { - FreeList* fl = &_indexedFreeList[i]; + AdaptiveFreeList* fl = &_indexedFreeList[i]; if (PrintFLSStatistics > 1) { gclog_or_tty->print("size[%d] : ", i); } @@ -2174,7 +2176,7 @@ assert_locked(); size_t i; for (i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { - FreeList *fl = &_indexedFreeList[i]; + AdaptiveFreeList *fl = &_indexedFreeList[i]; fl->set_surplus(fl->count() - (ssize_t)((double)fl->desired() * CMSSmallSplitSurplusPercent)); } @@ -2185,7 +2187,7 @@ size_t i; size_t h = IndexSetSize; for (i = IndexSetSize - 1; i != 0; i -= IndexSetStride) { - FreeList *fl = &_indexedFreeList[i]; + AdaptiveFreeList *fl = &_indexedFreeList[i]; fl->set_hint(h); if (fl->surplus() > 0) { h = i; @@ -2197,7 +2199,7 @@ assert_locked(); size_t i; for (i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { - FreeList *fl = &_indexedFreeList[i]; + AdaptiveFreeList *fl = &_indexedFreeList[i]; fl->set_prev_sweep(fl->count()); fl->set_coal_births(0); fl->set_coal_deaths(0); @@ -2224,7 +2226,7 @@ bool CompactibleFreeListSpace::coalOverPopulated(size_t size) { if (size < SmallForDictionary) { - FreeList *fl = &_indexedFreeList[size]; + AdaptiveFreeList *fl = &_indexedFreeList[size]; return (fl->coal_desired() < 0) || ((int)fl->count() > fl->coal_desired()); } else { @@ -2234,14 +2236,14 @@ void CompactibleFreeListSpace::smallCoalBirth(size_t size) { assert(size < SmallForDictionary, "Size too large for indexed list"); - FreeList *fl = &_indexedFreeList[size]; + AdaptiveFreeList *fl = &_indexedFreeList[size]; fl->increment_coal_births(); fl->increment_surplus(); } void CompactibleFreeListSpace::smallCoalDeath(size_t size) { assert(size < SmallForDictionary, "Size too large for indexed list"); - FreeList *fl = &_indexedFreeList[size]; + AdaptiveFreeList *fl = &_indexedFreeList[size]; fl->increment_coal_deaths(); fl->decrement_surplus(); } @@ -2250,7 +2252,7 @@ if (size < SmallForDictionary) { smallCoalBirth(size); } else { - dictionary()->dict_census_udpate(size, + dictionary()->dict_census_update(size, false /* split */, true /* birth */); } @@ -2260,7 +2262,7 @@ if(size < SmallForDictionary) { smallCoalDeath(size); } else { - dictionary()->dict_census_udpate(size, + dictionary()->dict_census_update(size, false /* split */, false /* birth */); } @@ -2268,14 +2270,14 @@ void CompactibleFreeListSpace::smallSplitBirth(size_t size) { assert(size < SmallForDictionary, "Size too large for indexed list"); - FreeList *fl = &_indexedFreeList[size]; + AdaptiveFreeList *fl = &_indexedFreeList[size]; fl->increment_split_births(); fl->increment_surplus(); } void CompactibleFreeListSpace::smallSplitDeath(size_t size) { assert(size < SmallForDictionary, "Size too large for indexed list"); - FreeList *fl = &_indexedFreeList[size]; + AdaptiveFreeList *fl = &_indexedFreeList[size]; fl->increment_split_deaths(); fl->decrement_surplus(); } @@ -2284,7 +2286,7 @@ if (size < SmallForDictionary) { smallSplitBirth(size); } else { - dictionary()->dict_census_udpate(size, + dictionary()->dict_census_update(size, true /* split */, true /* birth */); } @@ -2294,7 +2296,7 @@ if (size < SmallForDictionary) { smallSplitDeath(size); } else { - dictionary()->dict_census_udpate(size, + dictionary()->dict_census_update(size, true /* split */, false /* birth */); } @@ -2517,10 +2519,10 @@ #ifndef PRODUCT void CompactibleFreeListSpace::check_free_list_consistency() const { - assert(_dictionary->min_size() <= IndexSetSize, + assert((TreeChunk::min_size() <= IndexSetSize), "Some sizes can't be allocated without recourse to" " linear allocation buffers"); - assert(BinaryTreeDictionary::min_tree_chunk_size*HeapWordSize == sizeof(TreeChunk), + assert((TreeChunk::min_size()*HeapWordSize == sizeof(TreeChunk)), "else MIN_TREE_CHUNK_SIZE is wrong"); assert(IndexSetStart != 0, "IndexSetStart not initialized"); assert(IndexSetStride != 0, "IndexSetStride not initialized"); @@ -2529,15 +2531,15 @@ void CompactibleFreeListSpace::printFLCensus(size_t sweep_count) const { assert_lock_strong(&_freelistLock); - FreeList total; + AdaptiveFreeList total; gclog_or_tty->print("end sweep# " SIZE_FORMAT "\n", sweep_count); - FreeList::print_labels_on(gclog_or_tty, "size"); + AdaptiveFreeList::print_labels_on(gclog_or_tty, "size"); size_t total_free = 0; for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { - const FreeList *fl = &_indexedFreeList[i]; + const AdaptiveFreeList *fl = &_indexedFreeList[i]; total_free += fl->count() * fl->size(); if (i % (40*IndexSetStride) == 0) { - FreeList::print_labels_on(gclog_or_tty, "size"); + AdaptiveFreeList::print_labels_on(gclog_or_tty, "size"); } fl->print_on(gclog_or_tty); total.set_bfr_surp( total.bfr_surp() + fl->bfr_surp() ); @@ -2620,7 +2622,7 @@ res = _cfls->getChunkFromDictionaryExact(word_sz); if (res == NULL) return NULL; } else { - FreeList* fl = &_indexedFreeList[word_sz]; + AdaptiveFreeList* fl = &_indexedFreeList[word_sz]; if (fl->count() == 0) { // Attempt to refill this local free list. get_from_global_pool(word_sz, fl); @@ -2640,7 +2642,7 @@ // Get a chunk of blocks of the right size and update related // book-keeping stats -void CFLS_LAB::get_from_global_pool(size_t word_sz, FreeList* fl) { +void CFLS_LAB::get_from_global_pool(size_t word_sz, AdaptiveFreeList* fl) { // Get the #blocks we want to claim size_t n_blks = (size_t)_blocks_to_claim[word_sz].average(); assert(n_blks > 0, "Error"); @@ -2722,7 +2724,7 @@ if (num_retire > 0) { _cfls->_indexedFreeList[i].prepend(&_indexedFreeList[i]); // Reset this list. - _indexedFreeList[i] = FreeList(); + _indexedFreeList[i] = AdaptiveFreeList(); _indexedFreeList[i].set_size(i); } } @@ -2736,7 +2738,7 @@ } } -void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n, FreeList* fl) { +void CompactibleFreeListSpace:: par_get_chunk_of_blocks(size_t word_sz, size_t n, AdaptiveFreeList* fl) { assert(fl->count() == 0, "Precondition."); assert(word_sz < CompactibleFreeListSpace::IndexSetSize, "Precondition"); @@ -2752,12 +2754,12 @@ (cur_sz < CompactibleFreeListSpace::IndexSetSize) && (CMSSplitIndexedFreeListBlocks || k <= 1); k++, cur_sz = k * word_sz) { - FreeList fl_for_cur_sz; // Empty. + AdaptiveFreeList fl_for_cur_sz; // Empty. fl_for_cur_sz.set_size(cur_sz); { MutexLockerEx x(_indexedFreeListParLocks[cur_sz], Mutex::_no_safepoint_check_flag); - FreeList* gfl = &_indexedFreeList[cur_sz]; + AdaptiveFreeList* gfl = &_indexedFreeList[cur_sz]; if (gfl->count() != 0) { // nn is the number of chunks of size cur_sz that // we'd need to split k-ways each, in order to create @@ -2832,12 +2834,11 @@ MutexLockerEx x(parDictionaryAllocLock(), Mutex::_no_safepoint_check_flag); while (n > 0) { - fc = dictionary()->get_chunk(MAX2(n * word_sz, - _dictionary->min_size()), + fc = dictionary()->get_chunk(MAX2(n * word_sz, _dictionary->min_size()), FreeBlockDictionary::atLeast); if (fc != NULL) { _bt.allocated((HeapWord*)fc, fc->size(), true /* reducing */); // update _unallocated_blk - dictionary()->dict_census_udpate(fc->size(), + dictionary()->dict_census_update(fc->size(), true /*split*/, false /*birth*/); break; @@ -2890,7 +2891,7 @@ fc->set_size(prefix_size); if (rem >= IndexSetSize) { returnChunkToDictionary(rem_fc); - dictionary()->dict_census_udpate(rem, true /*split*/, true /*birth*/); + dictionary()->dict_census_update(rem, true /*split*/, true /*birth*/); rem_fc = NULL; } // Otherwise, return it to the small list below. diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -25,6 +25,7 @@ #ifndef SHARE_VM_GC_IMPLEMENTATION_CONCURRENTMARKSWEEP_COMPACTIBLEFREELISTSPACE_HPP #define SHARE_VM_GC_IMPLEMENTATION_CONCURRENTMARKSWEEP_COMPACTIBLEFREELISTSPACE_HPP +#include "gc_implementation/concurrentMarkSweep/adaptiveFreeList.hpp" #include "gc_implementation/concurrentMarkSweep/promotionInfo.hpp" #include "memory/binaryTreeDictionary.hpp" #include "memory/blockOffsetTable.inline.hpp" @@ -38,6 +39,7 @@ class CompactibleFreeListSpace; class BlkClosure; class BlkClosureCareful; +class FreeChunk; class UpwardsObjectClosure; class ObjectClosureCareful; class Klass; @@ -131,7 +133,7 @@ FreeBlockDictionary::DictionaryChoice _dictionaryChoice; FreeBlockDictionary* _dictionary; // ptr to dictionary for large size blocks - FreeList _indexedFreeList[IndexSetSize]; + AdaptiveFreeList _indexedFreeList[IndexSetSize]; // indexed array for small size blocks // allocation stategy bool _fitStrategy; // Use best fit strategy. @@ -168,7 +170,7 @@ // If the count of "fl" is negative, it's absolute value indicates a // number of free chunks that had been previously "borrowed" from global // list of size "word_sz", and must now be decremented. - void par_get_chunk_of_blocks(size_t word_sz, size_t n, FreeList* fl); + void par_get_chunk_of_blocks(size_t word_sz, size_t n, AdaptiveFreeList* fl); // Allocation helper functions // Allocate using a strategy that takes from the indexed free lists @@ -214,7 +216,7 @@ // and return it. The split off remainder is returned to // the free lists. The old name for getFromListGreater // was lookInListGreater. - FreeChunk* getFromListGreater(FreeList* fl, size_t numWords); + FreeChunk* getFromListGreater(AdaptiveFreeList* fl, size_t numWords); // Get a chunk in the indexed free list or dictionary, // by considering a larger chunk and splitting it. FreeChunk* getChunkFromGreater(size_t numWords); @@ -621,7 +623,7 @@ CompactibleFreeListSpace* _cfls; // Our local free lists. - FreeList _indexedFreeList[CompactibleFreeListSpace::IndexSetSize]; + AdaptiveFreeList _indexedFreeList[CompactibleFreeListSpace::IndexSetSize]; // Initialized from a command-line arg. @@ -634,7 +636,7 @@ size_t _num_blocks [CompactibleFreeListSpace::IndexSetSize]; // Internal work method - void get_from_global_pool(size_t word_sz, FreeList* fl); + void get_from_global_pool(size_t word_sz, AdaptiveFreeList* fl); public: CFLS_LAB(CompactibleFreeListSpace* cfls); diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -9143,7 +9143,7 @@ size_t shrinkable_size_in_bytes = chunk_at_end->size(); size_t aligned_shrinkable_size_in_bytes = align_size_down(shrinkable_size_in_bytes, os::vm_page_size()); - assert(unallocated_start <= chunk_at_end->end(), + assert(unallocated_start <= (HeapWord*) chunk_at_end->end(), "Inconsistent chunk at end of space"); size_t bytes = MIN2(desired_bytes, aligned_shrinkable_size_in_bytes); size_t word_size_before = heap_word_size(_virtual_space.committed_size()); @@ -9210,7 +9210,7 @@ assert(_cmsSpace->unallocated_block() <= _cmsSpace->end(), "Inconsistency at end of space"); - assert(chunk_at_end->end() == _cmsSpace->end(), + assert(chunk_at_end->end() == (uintptr_t*) _cmsSpace->end(), "Shrinking is inconsistent"); return; } diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/freeChunk.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -133,7 +133,7 @@ } // Return the address past the end of this chunk - HeapWord* end() const { return ((HeapWord*) this) + size(); } + uintptr_t* end() const { return ((uintptr_t*) this) + size(); } // debugging void verify() const PRODUCT_RETURN; diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/gc_implementation/concurrentMarkSweep/vmStructs_cms.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/vmStructs_cms.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/vmStructs_cms.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -25,6 +25,8 @@ #ifndef SHARE_VM_GC_IMPLEMENTATION_CONCURRENTMARKSWEEP_VMSTRUCTS_CMS_HPP #define SHARE_VM_GC_IMPLEMENTATION_CONCURRENTMARKSWEEP_VMSTRUCTS_CMS_HPP +typedef BinaryTreeDictionary AFLBinaryTreeDictionary; + #define VM_STRUCTS_CMS(nonstatic_field, \ volatile_nonstatic_field, \ static_field) \ @@ -38,14 +40,8 @@ nonstatic_field(CMSCollector, _markBitMap, CMSBitMap) \ nonstatic_field(ConcurrentMarkSweepGeneration, _cmsSpace, CompactibleFreeListSpace*) \ static_field(ConcurrentMarkSweepThread, _collector, CMSCollector*) \ - volatile_nonstatic_field(FreeChunk, _size, size_t) \ - nonstatic_field(FreeChunk, _next, FreeChunk*) \ - nonstatic_field(FreeChunk, _prev, FreeChunk*) \ nonstatic_field(LinearAllocBlock, _word_size, size_t) \ - nonstatic_field(FreeList, _size, size_t) \ - nonstatic_field(FreeList, _count, ssize_t) \ - nonstatic_field(BinaryTreeDictionary,_total_size, size_t) \ - nonstatic_field(CompactibleFreeListSpace, _dictionary, FreeBlockDictionary*) \ + nonstatic_field(AFLBinaryTreeDictionary, _total_size, size_t) \ nonstatic_field(CompactibleFreeListSpace, _indexedFreeList[0], FreeList) \ nonstatic_field(CompactibleFreeListSpace, _smallLinearAllocBlock, LinearAllocBlock) @@ -60,19 +56,17 @@ declare_toplevel_type(CMSCollector) \ declare_toplevel_type(CMSBitMap) \ declare_toplevel_type(FreeChunk) \ + declare_toplevel_type(Metablock) \ declare_toplevel_type(ConcurrentMarkSweepThread*) \ declare_toplevel_type(ConcurrentMarkSweepGeneration*) \ declare_toplevel_type(SurrogateLockerThread*) \ declare_toplevel_type(CompactibleFreeListSpace*) \ declare_toplevel_type(CMSCollector*) \ - declare_toplevel_type(FreeChunk*) \ - declare_toplevel_type(BinaryTreeDictionary*) \ - declare_toplevel_type(FreeBlockDictionary*) \ - declare_toplevel_type(FreeList*) \ - declare_toplevel_type(FreeList) \ + declare_toplevel_type(AFLBinaryTreeDictionary*) \ declare_toplevel_type(LinearAllocBlock) \ declare_toplevel_type(FreeBlockDictionary) \ - declare_type(BinaryTreeDictionary, FreeBlockDictionary) + declare_type(AFLBinaryTreeDictionary, FreeBlockDictionary) \ + declare_type(AFLBinaryTreeDictionary, FreeBlockDictionary) \ #define VM_INT_CONSTANTS_CMS(declare_constant) \ declare_constant(Generation::ConcurrentMarkSweep) \ diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/gc_implementation/shared/vmGCOperations.hpp --- a/src/share/vm/gc_implementation/shared/vmGCOperations.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/gc_implementation/shared/vmGCOperations.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -191,7 +191,7 @@ class VM_CollectForMetadataAllocation: public VM_GC_Operation { private: MetaWord* _result; - size_t _size; // size of object to be allocated + size_t _size; // size of object to be allocated Metaspace::MetadataType _mdtype; ClassLoaderData* _loader_data; public: diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/interpreter/abstractInterpreter.hpp --- a/src/share/vm/interpreter/abstractInterpreter.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/interpreter/abstractInterpreter.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -320,6 +320,7 @@ void bang_stack_shadow_pages(bool native_call); void generate_all(); + void initialize_method_handle_entries(); public: AbstractInterpreterGenerator(StubQueue* _code); diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/interpreter/bytecodeInterpreter.cpp --- a/src/share/vm/interpreter/bytecodeInterpreter.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/interpreter/bytecodeInterpreter.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -235,10 +235,6 @@ #endif #endif -// JavaStack Implementation -#define MORE_STACK(count) \ - (topOfStack -= ((count) * Interpreter::stackElementWords)) - #define UPDATE_PC(opsize) {pc += opsize; } /* @@ -575,7 +571,7 @@ /* 0xE0 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, /* 0xE4 */ &&opc_default, &&opc_fast_aldc, &&opc_fast_aldc_w, &&opc_return_register_finalizer, -/* 0xE8 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, +/* 0xE8 */ &&opc_invokehandle,&&opc_default, &&opc_default, &&opc_default, /* 0xEC */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, /* 0xF0 */ &&opc_default, &&opc_default, &&opc_default, &&opc_default, @@ -1773,7 +1769,7 @@ oop obj; if ((Bytecodes::Code)opcode == Bytecodes::_getstatic) { - Klass* k = (Klass*) cache->f1(); + Klass* k = cache->f1_as_klass(); obj = k->java_mirror(); MORE_STACK(1); // Assume single slot push } else { @@ -1885,7 +1881,7 @@ --count; } if ((Bytecodes::Code)opcode == Bytecodes::_putstatic) { - Klass* k = (Klass*) cache->f1(); + Klass* k = cache->f1_as_klass(); obj = k->java_mirror(); } else { --count; @@ -2190,6 +2186,7 @@ } CASE(_invokedynamic): { + if (!EnableInvokeDynamic) { // We should not encounter this bytecode if !EnableInvokeDynamic. // The verifier will stop it. However, if we get past the verifier, @@ -2199,26 +2196,64 @@ ShouldNotReachHere(); } - int index = Bytes::get_native_u4(pc+1); + u4 index = Bytes::get_native_u4(pc+1); + ConstantPoolCacheEntry* cache = cp->constant_pool()->invokedynamic_cp_cache_entry_at(index); // We are resolved if the resolved_references field contains a non-null object (CallSite, etc.) // This kind of CP cache entry does not need to match the flags byte, because // there is a 1-1 relation between bytecode type and CP entry type. - ConstantPool* constants = METHOD->constants(); - oop result = constants->resolved_references()->obj_at(index); - if (result == NULL) { + if (! cache->is_resolved((Bytecodes::Code) opcode)) { CALL_VM(InterpreterRuntime::resolve_invokedynamic(THREAD), handle_exception); - result = THREAD->vm_result(); + cache = cp->constant_pool()->invokedynamic_cp_cache_entry_at(index); + } + + Method* method = cache->f1_as_method(); + VERIFY_OOP(method); + + if (cache->has_appendix()) { + ConstantPool* constants = METHOD->constants(); + SET_STACK_OBJECT(cache->appendix_if_resolved(constants), 0); + MORE_STACK(1); + } + + istate->set_msg(call_method); + istate->set_callee(method); + istate->set_callee_entry_point(method->from_interpreted_entry()); + istate->set_bcp_advance(5); + + UPDATE_PC_AND_RETURN(0); // I'll be back... + } + + CASE(_invokehandle): { + + if (!EnableInvokeDynamic) { + ShouldNotReachHere(); } - VERIFY_OOP(result); - oop method_handle = java_lang_invoke_CallSite::target(result); - CHECK_NULL(method_handle); - - istate->set_msg(call_method_handle); - istate->set_callee((Method*) method_handle); - istate->set_bcp_advance(5); + u2 index = Bytes::get_native_u2(pc+1); + ConstantPoolCacheEntry* cache = cp->entry_at(index); + + if (! cache->is_resolved((Bytecodes::Code) opcode)) { + CALL_VM(InterpreterRuntime::resolve_invokehandle(THREAD), + handle_exception); + cache = cp->entry_at(index); + } + + Method* method = cache->f1_as_method(); + + VERIFY_OOP(method); + + if (cache->has_appendix()) { + ConstantPool* constants = METHOD->constants(); + SET_STACK_OBJECT(cache->appendix_if_resolved(constants), 0); + MORE_STACK(1); + } + + istate->set_msg(call_method); + istate->set_callee(method); + istate->set_callee_entry_point(method->from_interpreted_entry()); + istate->set_bcp_advance(3); UPDATE_PC_AND_RETURN(0); // I'll be back... } diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/interpreter/bytecodeInterpreter.hpp --- a/src/share/vm/interpreter/bytecodeInterpreter.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/interpreter/bytecodeInterpreter.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -50,6 +50,10 @@ #ifdef CC_INTERP +// JavaStack Implementation +#define MORE_STACK(count) \ + (topOfStack -= ((count) * Interpreter::stackElementWords)) + // CVM definitions find hotspot equivalents... union VMJavaVal64 { @@ -107,7 +111,6 @@ rethrow_exception, // unwinding and throwing exception // requests to frame manager from C++ interpreter call_method, // request for new frame from interpreter, manager responds with method_entry - call_method_handle, // like the above, except the callee is a method handle return_from_method, // request from interpreter to unwind, manager responds with method_continue more_monitors, // need a new monitor throwing_exception, // unwind stack and rethrow diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/interpreter/cppInterpreter.cpp --- a/src/share/vm/interpreter/cppInterpreter.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/interpreter/cppInterpreter.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -117,7 +117,6 @@ method_entry(empty); method_entry(accessor); method_entry(abstract); - method_entry(method_handle); method_entry(java_lang_math_sin ); method_entry(java_lang_math_cos ); method_entry(java_lang_math_tan ); @@ -125,7 +124,12 @@ method_entry(java_lang_math_sqrt ); method_entry(java_lang_math_log ); method_entry(java_lang_math_log10 ); + method_entry(java_lang_math_pow ); + method_entry(java_lang_math_exp ); method_entry(java_lang_ref_reference_get); + + initialize_method_handle_entries(); + Interpreter::_native_entry_begin = Interpreter::code()->code_end(); method_entry(native); method_entry(native_synchronized); diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/interpreter/interpreter.cpp --- a/src/share/vm/interpreter/interpreter.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/interpreter/interpreter.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -464,3 +464,11 @@ } } } + +void AbstractInterpreterGenerator::initialize_method_handle_entries() { + // method handle entry kinds are generated later in MethodHandlesAdapterGenerator::generate: + for (int i = Interpreter::method_handle_invoke_FIRST; i <= Interpreter::method_handle_invoke_LAST; i++) { + Interpreter::MethodKind kind = (Interpreter::MethodKind) i; + Interpreter::_entry_table[kind] = Interpreter::_entry_table[Interpreter::abstract]; + } +} diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/interpreter/linkResolver.cpp --- a/src/share/vm/interpreter/linkResolver.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/interpreter/linkResolver.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "classfile/defaultMethods.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" #include "compiler/compileBroker.hpp" @@ -404,21 +405,13 @@ Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, bool check_access, TRAPS) { - // 1. check if klass is not interface - if (resolved_klass->is_interface()) { - ResourceMark rm(THREAD); - char buf[200]; - jio_snprintf(buf, sizeof(buf), "Found interface %s, but class was expected", Klass::cast(resolved_klass())->external_name()); - THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); - } - Handle nested_exception; - // 2. lookup method in resolved klass and its super klasses + // 1. lookup method in resolved klass and its super klasses lookup_method_in_klasses(resolved_method, resolved_klass, method_name, method_signature, CHECK); if (resolved_method.is_null()) { // not found in the class hierarchy - // 3. lookup method in all the interfaces implemented by the resolved klass + // 2. lookup method in all the interfaces implemented by the resolved klass lookup_method_in_interfaces(resolved_method, resolved_klass, method_name, method_signature, CHECK); if (resolved_method.is_null()) { @@ -432,7 +425,7 @@ } if (resolved_method.is_null()) { - // 4. method lookup failed + // 3. method lookup failed ResourceMark rm(THREAD); THROW_MSG_CAUSE(vmSymbols::java_lang_NoSuchMethodError(), Method::name_and_sig_as_C_string(Klass::cast(resolved_klass()), @@ -442,6 +435,15 @@ } } + // 4. check if klass is not interface + if (resolved_klass->is_interface() && resolved_method->is_abstract()) { + ResourceMark rm(THREAD); + char buf[200]; + jio_snprintf(buf, sizeof(buf), "Found interface %s, but class was expected", + resolved_klass()->external_name()); + THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), buf); + } + // 5. check if method is concrete if (resolved_method->is_abstract() && !resolved_klass->is_abstract()) { ResourceMark rm(THREAD); @@ -743,6 +745,27 @@ Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, bool check_access, TRAPS) { + if (resolved_klass->is_interface() && current_klass() != NULL) { + // If the target class is a direct interface, treat this as a "super" + // default call. + // + // If the current method is an overpass that happens to call a direct + // super-interface's method, then we'll end up rerunning the default method + // analysis even though we don't need to, but that's ok since it will end + // up with the same answer. + InstanceKlass* ik = InstanceKlass::cast(current_klass()); + Array* interfaces = ik->local_interfaces(); + int num_interfaces = interfaces->length(); + for (int index = 0; index < num_interfaces; index++) { + if (interfaces->at(index) == resolved_klass()) { + Method* method = DefaultMethods::find_super_default(current_klass(), + resolved_klass(), method_name, method_signature, CHECK); + resolved_method = methodHandle(THREAD, method); + return; + } + } + } + resolve_method(resolved_method, resolved_klass, method_name, method_signature, current_klass, check_access, CHECK); // check if method name is , that it is found in same klass as static type @@ -784,11 +807,17 @@ { KlassHandle method_klass = KlassHandle(THREAD, resolved_method->method_holder()); - if (check_access && + const bool direct_calling_default_method = + resolved_klass() != NULL && resolved_method() != NULL && + resolved_klass->is_interface() && !resolved_method->is_abstract(); + + if (!direct_calling_default_method && + check_access && // a) check if ACC_SUPER flag is set for the current class current_klass->is_super() && // b) check if the method class is a superclass of the current class (superclass relation is not reflexive!) - current_klass->is_subtype_of(method_klass()) && current_klass() != method_klass() && + current_klass->is_subtype_of(method_klass()) && + current_klass() != method_klass() && // c) check if the method is not resolved_method->name() != vmSymbols::object_initializer_name()) { // Lookup super method diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/interpreter/templateInterpreter.cpp --- a/src/share/vm/interpreter/templateInterpreter.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/interpreter/templateInterpreter.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -373,11 +373,7 @@ method_entry(java_lang_math_pow ) method_entry(java_lang_ref_reference_get) - // method handle entry kinds are generated later in MethodHandlesAdapterGenerator::generate: - for (int i = Interpreter::method_handle_invoke_FIRST; i <= Interpreter::method_handle_invoke_LAST; i++) { - Interpreter::MethodKind kind = (Interpreter::MethodKind) i; - Interpreter::_entry_table[kind] = Interpreter::_entry_table[Interpreter::abstract]; - } + initialize_method_handle_entries(); // all native method kinds (must be one contiguous block) Interpreter::_native_entry_begin = Interpreter::code()->code_end(); diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/memory/binaryTreeDictionary.cpp --- a/src/share/vm/memory/binaryTreeDictionary.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/memory/binaryTreeDictionary.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -25,9 +25,15 @@ #include "precompiled.hpp" #include "gc_implementation/shared/allocationStats.hpp" #include "memory/binaryTreeDictionary.hpp" +#include "memory/freeList.hpp" +#include "memory/freeBlockDictionary.hpp" +#include "memory/metablock.hpp" +#include "memory/metachunk.hpp" #include "runtime/globals.hpp" #include "utilities/ostream.hpp" #ifndef SERIALGC +#include "gc_implementation/concurrentMarkSweep/adaptiveFreeList.hpp" +#include "gc_implementation/concurrentMarkSweep/freeChunk.hpp" #include "gc_implementation/shared/spaceDecorator.hpp" #include "gc_implementation/concurrentMarkSweep/freeChunk.hpp" #endif // SERIALGC @@ -37,15 +43,18 @@ // This is currently used in the Concurrent Mark&Sweep implementation. //////////////////////////////////////////////////////////////////////////////// -template -TreeChunk* TreeChunk::as_TreeChunk(Chunk* fc) { +template class FreeList_t> +size_t TreeChunk::_min_tree_chunk_size = sizeof(TreeChunk)/HeapWordSize; + +template class FreeList_t> +TreeChunk* TreeChunk::as_TreeChunk(Chunk_t* fc) { // Do some assertion checking here. - return (TreeChunk*) fc; + return (TreeChunk*) fc; } -template -void TreeChunk::verify_tree_chunk_list() const { - TreeChunk* nextTC = (TreeChunk*)next(); +template class FreeList_t> +void TreeChunk::verify_tree_chunk_list() const { + TreeChunk* nextTC = (TreeChunk*)next(); if (prev() != NULL) { // interior list node shouldn'r have tree fields guarantee(embedded_list()->parent() == NULL && embedded_list()->left() == NULL && embedded_list()->right() == NULL, "should be clear"); @@ -57,53 +66,113 @@ } } +template class FreeList_t> +TreeList::TreeList() {} -template -TreeList* TreeList::as_TreeList(TreeChunk* tc) { +template class FreeList_t> +TreeList* +TreeList::as_TreeList(TreeChunk* tc) { // This first free chunk in the list will be the tree list. - assert(tc->size() >= BinaryTreeDictionary::min_tree_chunk_size, "Chunk is too small for a TreeChunk"); - TreeList* tl = tc->embedded_list(); + assert((tc->size() >= (TreeChunk::min_size())), + "Chunk is too small for a TreeChunk"); + TreeList* tl = tc->embedded_list(); + tl->initialize(); tc->set_list(tl); -#ifdef ASSERT - tl->set_protecting_lock(NULL); -#endif - tl->set_hint(0); tl->set_size(tc->size()); tl->link_head(tc); tl->link_tail(tc); tl->set_count(1); - tl->init_statistics(true /* split_birth */); - tl->set_parent(NULL); - tl->set_left(NULL); - tl->set_right(NULL); + + return tl; +} + + +template class FreeList_t> +TreeList* +get_chunk(size_t size, enum FreeBlockDictionary::Dither dither) { + FreeBlockDictionary::verify_par_locked(); + Chunk_t* res = get_chunk_from_tree(size, dither); + assert(res == NULL || res->is_free(), + "Should be returning a free chunk"); + assert(dither != FreeBlockDictionary::exactly || + res->size() == size, "Not correct size"); + return res; +} + +template class FreeList_t> +TreeList* +TreeList::as_TreeList(HeapWord* addr, size_t size) { + TreeChunk* tc = (TreeChunk*) addr; + assert((size >= TreeChunk::min_size()), + "Chunk is too small for a TreeChunk"); + // The space will have been mangled initially but + // is not remangled when a Chunk_t is returned to the free list + // (since it is used to maintain the chunk on the free list). + tc->assert_is_mangled(); + tc->set_size(size); + tc->link_prev(NULL); + tc->link_next(NULL); + TreeList* tl = TreeList::as_TreeList(tc); return tl; } -template -TreeList* TreeList::as_TreeList(HeapWord* addr, size_t size) { - TreeChunk* tc = (TreeChunk*) addr; - assert(size >= BinaryTreeDictionary::min_tree_chunk_size, "Chunk is too small for a TreeChunk"); - // The space in the heap will have been mangled initially but - // is not remangled when a free chunk is returned to the free list - // (since it is used to maintain the chunk on the free list). - assert((ZapUnusedHeapArea && - SpaceMangler::is_mangled((HeapWord*) tc->size_addr()) && - SpaceMangler::is_mangled((HeapWord*) tc->prev_addr()) && - SpaceMangler::is_mangled((HeapWord*) tc->next_addr())) || - (tc->size() == 0 && tc->prev() == NULL && tc->next() == NULL), - "Space should be clear or mangled"); - tc->set_size(size); - tc->link_prev(NULL); - tc->link_next(NULL); - TreeList* tl = TreeList::as_TreeList(tc); - return tl; + +#ifndef SERIALGC +// Specialize for AdaptiveFreeList which tries to avoid +// splitting a chunk of a size that is under populated in favor of +// an over populated size. The general get_better_list() just returns +// the current list. +template <> +TreeList* +TreeList::get_better_list( + BinaryTreeDictionary* dictionary) { + // A candidate chunk has been found. If it is already under + // populated, get a chunk associated with the hint for this + // chunk. + + TreeList* curTL = this; + if (surplus() <= 0) { + /* Use the hint to find a size with a surplus, and reset the hint. */ + TreeList* hintTL = this; + while (hintTL->hint() != 0) { + assert(hintTL->hint() > hintTL->size(), + "hint points in the wrong direction"); + hintTL = dictionary->find_list(hintTL->hint()); + assert(curTL != hintTL, "Infinite loop"); + if (hintTL == NULL || + hintTL == curTL /* Should not happen but protect against it */ ) { + // No useful hint. Set the hint to NULL and go on. + curTL->set_hint(0); + break; + } + assert(hintTL->size() > curTL->size(), "hint is inconsistent"); + if (hintTL->surplus() > 0) { + // The hint led to a list that has a surplus. Use it. + // Set the hint for the candidate to an overpopulated + // size. + curTL->set_hint(hintTL->size()); + // Change the candidate. + curTL = hintTL; + break; + } + } + } + return curTL; +} +#endif // SERIALGC + +template class FreeList_t> +TreeList* +TreeList::get_better_list( + BinaryTreeDictionary* dictionary) { + return this; } -template -TreeList* TreeList::remove_chunk_replace_if_needed(TreeChunk* tc) { +template class FreeList_t> +TreeList* TreeList::remove_chunk_replace_if_needed(TreeChunk* tc) { - TreeList* retTL = this; - Chunk* list = head(); + TreeList* retTL = this; + Chunk_t* list = head(); assert(!list || list != list->next(), "Chunk on list twice"); assert(tc != NULL, "Chunk being removed is NULL"); assert(parent() == NULL || this == parent()->left() || @@ -112,13 +181,13 @@ assert(head() == NULL || head()->prev() == NULL, "list invariant"); assert(tail() == NULL || tail()->next() == NULL, "list invariant"); - Chunk* prevFC = tc->prev(); - TreeChunk* nextTC = TreeChunk::as_TreeChunk(tc->next()); + Chunk_t* prevFC = tc->prev(); + TreeChunk* nextTC = TreeChunk::as_TreeChunk(tc->next()); assert(list != NULL, "should have at least the target chunk"); // Is this the first item on the list? if (tc == list) { - // The "getChunk..." functions for a TreeList will not return the + // The "getChunk..." functions for a TreeList will not return the // first chunk in the list unless it is the last chunk in the list // because the first chunk is also acting as the tree node. // When coalescing happens, however, the first chunk in the a tree @@ -127,8 +196,8 @@ // allocated when the sweeper yields (giving up the free list lock) // to allow mutator activity. If this chunk is the first in the // list and is not the last in the list, do the work to copy the - // TreeList from the first chunk to the next chunk and update all - // the TreeList pointers in the chunks in the list. + // TreeList from the first chunk to the next chunk and update all + // the TreeList pointers in the chunks in the list. if (nextTC == NULL) { assert(prevFC == NULL, "Not last chunk in the list"); set_tail(NULL); @@ -141,11 +210,11 @@ // This can be slow for a long list. Consider having // an option that does not allow the first chunk on the // list to be coalesced. - for (TreeChunk* curTC = nextTC; curTC != NULL; - curTC = TreeChunk::as_TreeChunk(curTC->next())) { + for (TreeChunk* curTC = nextTC; curTC != NULL; + curTC = TreeChunk::as_TreeChunk(curTC->next())) { curTC->set_list(retTL); } - // Fix the parent to point to the new TreeList. + // Fix the parent to point to the new TreeList. if (retTL->parent() != NULL) { if (this == retTL->parent()->left()) { retTL->parent()->set_left(retTL); @@ -176,9 +245,9 @@ prevFC->link_after(nextTC); } - // Below this point the embeded TreeList being used for the + // Below this point the embeded TreeList being used for the // tree node may have changed. Don't use "this" - // TreeList*. + // TreeList*. // chunk should still be a free chunk (bit set in _prev) assert(!retTL->head() || retTL->size() == retTL->head()->size(), "Wrong sized chunk in list"); @@ -188,7 +257,7 @@ tc->set_list(NULL); bool prev_found = false; bool next_found = false; - for (Chunk* curFC = retTL->head(); + for (Chunk_t* curFC = retTL->head(); curFC != NULL; curFC = curFC->next()) { assert(curFC != tc, "Chunk is still in list"); if (curFC == prevFC) { @@ -215,8 +284,8 @@ return retTL; } -template -void TreeList::return_chunk_at_tail(TreeChunk* chunk) { +template class FreeList_t> +void TreeList::return_chunk_at_tail(TreeChunk* chunk) { assert(chunk != NULL, "returning NULL chunk"); assert(chunk->list() == this, "list should be set for chunk"); assert(tail() != NULL, "The tree list is embedded in the first chunk"); @@ -225,12 +294,12 @@ assert(head() == NULL || head()->prev() == NULL, "list invariant"); assert(tail() == NULL || tail()->next() == NULL, "list invariant"); - Chunk* fc = tail(); + Chunk_t* fc = tail(); fc->link_after(chunk); link_tail(chunk); assert(!tail() || size() == tail()->size(), "Wrong sized chunk in list"); - increment_count(); + FreeList_t::increment_count(); debug_only(increment_returned_bytes_by(chunk->size()*sizeof(HeapWord));) assert(head() == NULL || head()->prev() == NULL, "list invariant"); assert(tail() == NULL || tail()->next() == NULL, "list invariant"); @@ -238,10 +307,10 @@ // Add this chunk at the head of the list. "At the head of the list" // is defined to be after the chunk pointer to by head(). This is -// because the TreeList is embedded in the first TreeChunk in the -// list. See the definition of TreeChunk. -template -void TreeList::return_chunk_at_head(TreeChunk* chunk) { +// because the TreeList is embedded in the first TreeChunk in the +// list. See the definition of TreeChunk. +template class FreeList_t> +void TreeList::return_chunk_at_head(TreeChunk* chunk) { assert(chunk->list() == this, "list should be set for chunk"); assert(head() != NULL, "The tree list is embedded in the first chunk"); assert(chunk != NULL, "returning NULL chunk"); @@ -249,7 +318,7 @@ assert(head() == NULL || head()->prev() == NULL, "list invariant"); assert(tail() == NULL || tail()->next() == NULL, "list invariant"); - Chunk* fc = head()->next(); + Chunk_t* fc = head()->next(); if (fc != NULL) { chunk->link_after(fc); } else { @@ -258,28 +327,38 @@ } head()->link_after(chunk); assert(!head() || size() == head()->size(), "Wrong sized chunk in list"); - increment_count(); + FreeList_t::increment_count(); debug_only(increment_returned_bytes_by(chunk->size()*sizeof(HeapWord));) assert(head() == NULL || head()->prev() == NULL, "list invariant"); assert(tail() == NULL || tail()->next() == NULL, "list invariant"); } -template -TreeChunk* TreeList::head_as_TreeChunk() { - assert(head() == NULL || TreeChunk::as_TreeChunk(head())->list() == this, - "Wrong type of chunk?"); - return TreeChunk::as_TreeChunk(head()); +template class FreeList_t> +void TreeChunk::assert_is_mangled() const { + assert((ZapUnusedHeapArea && + SpaceMangler::is_mangled((HeapWord*) Chunk_t::size_addr()) && + SpaceMangler::is_mangled((HeapWord*) Chunk_t::prev_addr()) && + SpaceMangler::is_mangled((HeapWord*) Chunk_t::next_addr())) || + (size() == 0 && prev() == NULL && next() == NULL), + "Space should be clear or mangled"); } -template -TreeChunk* TreeList::first_available() { +template class FreeList_t> +TreeChunk* TreeList::head_as_TreeChunk() { + assert(head() == NULL || (TreeChunk::as_TreeChunk(head())->list() == this), + "Wrong type of chunk?"); + return TreeChunk::as_TreeChunk(head()); +} + +template class FreeList_t> +TreeChunk* TreeList::first_available() { assert(head() != NULL, "The head of the list cannot be NULL"); - Chunk* fc = head()->next(); - TreeChunk* retTC; + Chunk_t* fc = head()->next(); + TreeChunk* retTC; if (fc == NULL) { retTC = head_as_TreeChunk(); } else { - retTC = TreeChunk::as_TreeChunk(fc); + retTC = TreeChunk::as_TreeChunk(fc); } assert(retTC->list() == this, "Wrong type of chunk."); return retTC; @@ -288,41 +367,32 @@ // Returns the block with the largest heap address amongst // those in the list for this size; potentially slow and expensive, // use with caution! -template -TreeChunk* TreeList::largest_address() { +template class FreeList_t> +TreeChunk* TreeList::largest_address() { assert(head() != NULL, "The head of the list cannot be NULL"); - Chunk* fc = head()->next(); - TreeChunk* retTC; + Chunk_t* fc = head()->next(); + TreeChunk* retTC; if (fc == NULL) { retTC = head_as_TreeChunk(); } else { // walk down the list and return the one with the highest // heap address among chunks of this size. - Chunk* last = fc; + Chunk_t* last = fc; while (fc->next() != NULL) { if ((HeapWord*)last < (HeapWord*)fc) { last = fc; } fc = fc->next(); } - retTC = TreeChunk::as_TreeChunk(last); + retTC = TreeChunk::as_TreeChunk(last); } assert(retTC->list() == this, "Wrong type of chunk."); return retTC; } -template -BinaryTreeDictionary::BinaryTreeDictionary(bool adaptive_freelists, bool splay) : - _splay(splay), _adaptive_freelists(adaptive_freelists), - _total_size(0), _total_free_blocks(0), _root(0) {} - -template -BinaryTreeDictionary::BinaryTreeDictionary(MemRegion mr, - bool adaptive_freelists, - bool splay): - _adaptive_freelists(adaptive_freelists), _splay(splay) -{ - assert(mr.word_size() >= BinaryTreeDictionary::min_tree_chunk_size, "minimum chunk size"); +template class FreeList_t> +BinaryTreeDictionary::BinaryTreeDictionary(MemRegion mr) { + assert((mr.byte_size() > min_size()), "minimum chunk size"); reset(mr); assert(root()->left() == NULL, "reset check failed"); @@ -333,52 +403,48 @@ assert(total_free_blocks() == 1, "reset check failed"); } -template -void BinaryTreeDictionary::inc_total_size(size_t inc) { +template class FreeList_t> +void BinaryTreeDictionary::inc_total_size(size_t inc) { _total_size = _total_size + inc; } -template -void BinaryTreeDictionary::dec_total_size(size_t dec) { +template class FreeList_t> +void BinaryTreeDictionary::dec_total_size(size_t dec) { _total_size = _total_size - dec; } -template -void BinaryTreeDictionary::reset(MemRegion mr) { - assert(mr.word_size() >= BinaryTreeDictionary::min_tree_chunk_size, "minimum chunk size"); - set_root(TreeList::as_TreeList(mr.start(), mr.word_size())); +template class FreeList_t> +void BinaryTreeDictionary::reset(MemRegion mr) { + assert((mr.byte_size() > min_size()), "minimum chunk size"); + set_root(TreeList::as_TreeList(mr.start(), mr.word_size())); set_total_size(mr.word_size()); set_total_free_blocks(1); } -template -void BinaryTreeDictionary::reset(HeapWord* addr, size_t byte_size) { +template class FreeList_t> +void BinaryTreeDictionary::reset(HeapWord* addr, size_t byte_size) { MemRegion mr(addr, heap_word_size(byte_size)); reset(mr); } -template -void BinaryTreeDictionary::reset() { +template class FreeList_t> +void BinaryTreeDictionary::reset() { set_root(NULL); set_total_size(0); set_total_free_blocks(0); } // Get a free block of size at least size from tree, or NULL. -// If a splay step is requested, the removal algorithm (only) incorporates -// a splay step as follows: -// . the search proceeds down the tree looking for a possible -// match. At the (closest) matching location, an appropriate splay step is applied -// (zig, zig-zig or zig-zag). A chunk of the appropriate size is then returned -// if available, and if it's the last chunk, the node is deleted. A deteleted -// node is replaced in place by its tree successor. -template -TreeChunk* -BinaryTreeDictionary::get_chunk_from_tree(size_t size, enum FreeBlockDictionary::Dither dither, bool splay) +template class FreeList_t> +TreeChunk* +BinaryTreeDictionary::get_chunk_from_tree( + size_t size, + enum FreeBlockDictionary::Dither dither) { - TreeList *curTL, *prevTL; - TreeChunk* retTC = NULL; - assert(size >= BinaryTreeDictionary::min_tree_chunk_size, "minimum chunk size"); + TreeList *curTL, *prevTL; + TreeChunk* retTC = NULL; + + assert((size >= min_size()), "minimum chunk size"); if (FLSVerifyDictionary) { verify_tree(); } @@ -398,7 +464,7 @@ } if (curTL == NULL) { // couldn't find exact match - if (dither == FreeBlockDictionary::exactly) return NULL; + if (dither == FreeBlockDictionary::exactly) return NULL; // try and find the next larger size by walking back up the search path for (curTL = prevTL; curTL != NULL;) { @@ -410,46 +476,9 @@ } if (curTL != NULL) { assert(curTL->size() >= size, "size inconsistency"); - if (adaptive_freelists()) { - // A candidate chunk has been found. If it is already under - // populated, get a chunk associated with the hint for this - // chunk. - if (curTL->surplus() <= 0) { - /* Use the hint to find a size with a surplus, and reset the hint. */ - TreeList* hintTL = curTL; - while (hintTL->hint() != 0) { - assert(hintTL->hint() == 0 || hintTL->hint() > hintTL->size(), - "hint points in the wrong direction"); - hintTL = find_list(hintTL->hint()); - assert(curTL != hintTL, "Infinite loop"); - if (hintTL == NULL || - hintTL == curTL /* Should not happen but protect against it */ ) { - // No useful hint. Set the hint to NULL and go on. - curTL->set_hint(0); - break; - } - assert(hintTL->size() > size, "hint is inconsistent"); - if (hintTL->surplus() > 0) { - // The hint led to a list that has a surplus. Use it. - // Set the hint for the candidate to an overpopulated - // size. - curTL->set_hint(hintTL->size()); - // Change the candidate. - curTL = hintTL; - break; - } - // The evm code reset the hint of the candidate as - // at an interim point. Why? Seems like this leaves - // the hint pointing to a list that didn't work. - // curTL->set_hint(hintTL->size()); - } - } - } - // don't waste time splaying if chunk's singleton - if (splay && curTL->head()->next() != NULL) { - semi_splay_step(curTL); - } + curTL = curTL->get_better_list(this); + retTC = curTL->first_available(); assert((retTC != NULL) && (curTL->count() > 0), "A list in the binary tree should not be NULL"); @@ -465,9 +494,9 @@ return retTC; } -template -TreeList* BinaryTreeDictionary::find_list(size_t size) const { - TreeList* curTL; +template class FreeList_t> +TreeList* BinaryTreeDictionary::find_list(size_t size) const { + TreeList* curTL; for (curTL = root(); curTL != NULL;) { if (curTL->size() == size) { // exact match break; @@ -484,10 +513,10 @@ } -template -bool BinaryTreeDictionary::verify_chunk_in_free_list(Chunk* tc) const { +template class FreeList_t> +bool BinaryTreeDictionary::verify_chunk_in_free_list(Chunk_t* tc) const { size_t size = tc->size(); - TreeList* tl = find_list(size); + TreeList* tl = find_list(size); if (tl == NULL) { return false; } else { @@ -495,9 +524,9 @@ } } -template -Chunk* BinaryTreeDictionary::find_largest_dict() const { - TreeList *curTL = root(); +template class FreeList_t> +Chunk_t* BinaryTreeDictionary::find_largest_dict() const { + TreeList *curTL = root(); if (curTL != NULL) { while(curTL->right() != NULL) curTL = curTL->right(); return curTL->largest_address(); @@ -510,15 +539,15 @@ // chunk in a list on a tree node, just unlink it. // If it is the last chunk in the list (the next link is NULL), // remove the node and repair the tree. -template -TreeChunk* -BinaryTreeDictionary::remove_chunk_from_tree(TreeChunk* tc) { +template class FreeList_t> +TreeChunk* +BinaryTreeDictionary::remove_chunk_from_tree(TreeChunk* tc) { assert(tc != NULL, "Should not call with a NULL chunk"); assert(tc->is_free(), "Header is not marked correctly"); - TreeList *newTL, *parentTL; - TreeChunk* retTC; - TreeList* tl = tc->list(); + TreeList *newTL, *parentTL; + TreeChunk* retTC; + TreeList* tl = tc->list(); debug_only( bool removing_only_chunk = false; if (tl == _root) { @@ -538,8 +567,8 @@ retTC = tc; // Removing this chunk can have the side effect of changing the node - // (TreeList*) in the tree. If the node is the root, update it. - TreeList* replacementTL = tl->remove_chunk_replace_if_needed(tc); + // (TreeList*) in the tree. If the node is the root, update it. + TreeList* replacementTL = tl->remove_chunk_replace_if_needed(tc); assert(tc->is_free(), "Chunk should still be free"); assert(replacementTL->parent() == NULL || replacementTL == replacementTL->parent()->left() || @@ -549,17 +578,18 @@ assert(replacementTL->parent() == NULL, "Incorrectly replacing root"); set_root(replacementTL); } - debug_only( +#ifdef ASSERT if (tl != replacementTL) { assert(replacementTL->head() != NULL, "If the tree list was replaced, it should not be a NULL list"); - TreeList* rhl = replacementTL->head_as_TreeChunk()->list(); - TreeList* rtl = TreeChunk::as_TreeChunk(replacementTL->tail())->list(); + TreeList* rhl = replacementTL->head_as_TreeChunk()->list(); + TreeList* rtl = + TreeChunk::as_TreeChunk(replacementTL->tail())->list(); assert(rhl == replacementTL, "Broken head"); assert(rtl == replacementTL, "Broken tail"); assert(replacementTL->size() == tc->size(), "Broken size"); } - ) +#endif // Does the tree need to be repaired? if (replacementTL->count() == 0) { @@ -574,7 +604,7 @@ } else if (replacementTL->right() == NULL) { // right is NULL newTL = replacementTL->left(); - debug_only(replacementTL->clearLeft();) + debug_only(replacementTL->clear_left();) } else { // we have both children, so, by patriarchal convention, // my replacement is least node in right sub-tree complicated_splice = true; @@ -623,7 +653,7 @@ newTL->set_right(replacementTL->right()); debug_only( replacementTL->clear_right(); - replacementTL->clearLeft(); + replacementTL->clear_left(); ) } assert(replacementTL->right() == NULL && @@ -644,21 +674,21 @@ verify_tree(); } assert(!removing_only_chunk || _root == NULL, "root should be NULL"); - return TreeChunk::as_TreeChunk(retTC); + return TreeChunk::as_TreeChunk(retTC); } // Remove the leftmost node (lm) in the tree and return it. // If lm has a right child, link it to the left node of // the parent of lm. -template -TreeList* BinaryTreeDictionary::remove_tree_minimum(TreeList* tl) { +template class FreeList_t> +TreeList* BinaryTreeDictionary::remove_tree_minimum(TreeList* tl) { assert(tl != NULL && tl->parent() != NULL, "really need a proper sub-tree"); // locate the subtree minimum by walking down left branches - TreeList* curTL = tl; + TreeList* curTL = tl; for (; curTL->left() != NULL; curTL = curTL->left()); // obviously curTL now has at most one child, a right child if (curTL != root()) { // Should this test just be removed? - TreeList* parentTL = curTL->parent(); + TreeList* parentTL = curTL->parent(); if (parentTL->left() == curTL) { // curTL is a left child parentTL->set_left(curTL->right()); } else { @@ -685,31 +715,14 @@ return curTL; } -// Based on a simplification of the algorithm by Sleator and Tarjan (JACM 1985). -// The simplifications are the following: -// . we splay only when we delete (not when we insert) -// . we apply a single spay step per deletion/access -// By doing such partial splaying, we reduce the amount of restructuring, -// while getting a reasonably efficient search tree (we think). -// [Measurements will be needed to (in)validate this expectation.] - -template -void BinaryTreeDictionary::semi_splay_step(TreeList* tc) { - // apply a semi-splay step at the given node: - // . if root, norting needs to be done - // . if child of root, splay once - // . else zig-zig or sig-zag depending on path from grandparent - if (root() == tc) return; - warning("*** Splaying not yet implemented; " - "tree operations may be inefficient ***"); -} - -template -void BinaryTreeDictionary::insert_chunk_in_tree(Chunk* fc) { - TreeList *curTL, *prevTL; +template class FreeList_t> +void BinaryTreeDictionary::insert_chunk_in_tree(Chunk_t* fc) { + TreeList *curTL, *prevTL; size_t size = fc->size(); - assert(size >= BinaryTreeDictionary::min_tree_chunk_size, "too small to be a TreeList"); + assert((size >= min_size()), + err_msg(SIZE_FORMAT " is too small to be a TreeChunk " SIZE_FORMAT, + size, min_size())); if (FLSVerifyDictionary) { verify_tree(); } @@ -729,9 +742,9 @@ curTL = curTL->right(); } } - TreeChunk* tc = TreeChunk::as_TreeChunk(fc); + TreeChunk* tc = TreeChunk::as_TreeChunk(fc); // This chunk is being returned to the binary tree. Its embedded - // TreeList should be unused at this point. + // TreeList should be unused at this point. tc->initialize(); if (curTL != NULL) { // exact match tc->set_list(curTL); @@ -739,8 +752,8 @@ } else { // need a new node in tree tc->clear_next(); tc->link_prev(NULL); - TreeList* newTL = TreeList::as_TreeList(tc); - assert(((TreeChunk*)tc)->list() == newTL, + TreeList* newTL = TreeList::as_TreeList(tc); + assert(((TreeChunk*)tc)->list() == newTL, "List was not initialized correctly"); if (prevTL == NULL) { // we are the only tree node assert(root() == NULL, "control point invariant"); @@ -768,30 +781,30 @@ } } -template -size_t BinaryTreeDictionary::max_chunk_size() const { - FreeBlockDictionary::verify_par_locked(); - TreeList* tc = root(); +template class FreeList_t> +size_t BinaryTreeDictionary::max_chunk_size() const { + FreeBlockDictionary::verify_par_locked(); + TreeList* tc = root(); if (tc == NULL) return 0; for (; tc->right() != NULL; tc = tc->right()); return tc->size(); } -template -size_t BinaryTreeDictionary::total_list_length(TreeList* tl) const { +template class FreeList_t> +size_t BinaryTreeDictionary::total_list_length(TreeList* tl) const { size_t res; res = tl->count(); #ifdef ASSERT size_t cnt; - Chunk* tc = tl->head(); + Chunk_t* tc = tl->head(); for (cnt = 0; tc != NULL; tc = tc->next(), cnt++); assert(res == cnt, "The count is not being maintained correctly"); #endif return res; } -template -size_t BinaryTreeDictionary::total_size_in_tree(TreeList* tl) const { +template class FreeList_t> +size_t BinaryTreeDictionary::total_size_in_tree(TreeList* tl) const { if (tl == NULL) return 0; return (tl->size() * total_list_length(tl)) + @@ -799,8 +812,8 @@ total_size_in_tree(tl->right()); } -template -double BinaryTreeDictionary::sum_of_squared_block_sizes(TreeList* const tl) const { +template class FreeList_t> +double BinaryTreeDictionary::sum_of_squared_block_sizes(TreeList* const tl) const { if (tl == NULL) { return 0.0; } @@ -811,8 +824,8 @@ return curr; } -template -size_t BinaryTreeDictionary::total_free_blocks_in_tree(TreeList* tl) const { +template class FreeList_t> +size_t BinaryTreeDictionary::total_free_blocks_in_tree(TreeList* tl) const { if (tl == NULL) return 0; return total_list_length(tl) + @@ -820,28 +833,28 @@ total_free_blocks_in_tree(tl->right()); } -template -size_t BinaryTreeDictionary::num_free_blocks() const { +template class FreeList_t> +size_t BinaryTreeDictionary::num_free_blocks() const { assert(total_free_blocks_in_tree(root()) == total_free_blocks(), "_total_free_blocks inconsistency"); return total_free_blocks(); } -template -size_t BinaryTreeDictionary::tree_height_helper(TreeList* tl) const { +template class FreeList_t> +size_t BinaryTreeDictionary::tree_height_helper(TreeList* tl) const { if (tl == NULL) return 0; return 1 + MAX2(tree_height_helper(tl->left()), tree_height_helper(tl->right())); } -template -size_t BinaryTreeDictionary::treeHeight() const { +template class FreeList_t> +size_t BinaryTreeDictionary::tree_height() const { return tree_height_helper(root()); } -template -size_t BinaryTreeDictionary::total_nodes_helper(TreeList* tl) const { +template class FreeList_t> +size_t BinaryTreeDictionary::total_nodes_helper(TreeList* tl) const { if (tl == NULL) { return 0; } @@ -849,14 +862,18 @@ total_nodes_helper(tl->right()); } -template -size_t BinaryTreeDictionary::total_nodes_in_tree(TreeList* tl) const { +template class FreeList_t> +size_t BinaryTreeDictionary::total_nodes_in_tree(TreeList* tl) const { return total_nodes_helper(root()); } -template -void BinaryTreeDictionary::dict_census_udpate(size_t size, bool split, bool birth){ - TreeList* nd = find_list(size); +template class FreeList_t> +void BinaryTreeDictionary::dict_census_update(size_t size, bool split, bool birth){} + +#ifndef SERIALGC +template <> +void BinaryTreeDictionary::dict_census_update(size_t size, bool split, bool birth){ + TreeList* nd = find_list(size); if (nd) { if (split) { if (birth) { @@ -882,16 +899,26 @@ // This is a birth associated with a LinAB. The chunk // for the LinAB is not in the dictionary. } +#endif // SERIALGC -template -bool BinaryTreeDictionary::coal_dict_over_populated(size_t size) { +template class FreeList_t> +bool BinaryTreeDictionary::coal_dict_over_populated(size_t size) { + // For the general type of freelists, encourage coalescing by + // returning true. + return true; +} + +#ifndef SERIALGC +template <> +bool BinaryTreeDictionary::coal_dict_over_populated(size_t size) { if (FLSAlwaysCoalesceLarge) return true; - TreeList* list_of_size = find_list(size); + TreeList* list_of_size = find_list(size); // None of requested size implies overpopulated. return list_of_size == NULL || list_of_size->coal_desired() <= 0 || list_of_size->count() > list_of_size->coal_desired(); } +#endif // SERIALGC // Closures for walking the binary tree. // do_list() walks the free list in a node applying the closure @@ -899,19 +926,18 @@ // do_tree() walks the nodes in the binary tree applying do_list() // to each list at each node. -template +template class FreeList_t> class TreeCensusClosure : public StackObj { protected: - virtual void do_list(FreeList* fl) = 0; + virtual void do_list(FreeList_t* fl) = 0; public: - virtual void do_tree(TreeList* tl) = 0; + virtual void do_tree(TreeList* tl) = 0; }; -template -class AscendTreeCensusClosure : public TreeCensusClosure { - using TreeCensusClosure::do_list; +template class FreeList_t> +class AscendTreeCensusClosure : public TreeCensusClosure { public: - void do_tree(TreeList* tl) { + void do_tree(TreeList* tl) { if (tl != NULL) { do_tree(tl->left()); do_list(tl); @@ -920,11 +946,10 @@ } }; -template -class DescendTreeCensusClosure : public TreeCensusClosure { - using TreeCensusClosure::do_list; +template class FreeList_t> +class DescendTreeCensusClosure : public TreeCensusClosure { public: - void do_tree(TreeList* tl) { + void do_tree(TreeList* tl) { if (tl != NULL) { do_tree(tl->right()); do_list(tl); @@ -935,8 +960,8 @@ // For each list in the tree, calculate the desired, desired // coalesce, count before sweep, and surplus before sweep. -template -class BeginSweepClosure : public AscendTreeCensusClosure { +template class FreeList_t> +class BeginSweepClosure : public AscendTreeCensusClosure { double _percentage; float _inter_sweep_current; float _inter_sweep_estimate; @@ -951,32 +976,36 @@ _inter_sweep_estimate(inter_sweep_estimate), _intra_sweep_estimate(intra_sweep_estimate) { } - void do_list(FreeList* fl) { + void do_list(FreeList* fl) {} + +#ifndef SERIALGC + void do_list(AdaptiveFreeList* fl) { double coalSurplusPercent = _percentage; fl->compute_desired(_inter_sweep_current, _inter_sweep_estimate, _intra_sweep_estimate); fl->set_coal_desired((ssize_t)((double)fl->desired() * coalSurplusPercent)); fl->set_before_sweep(fl->count()); fl->set_bfr_surp(fl->surplus()); } +#endif // SERIALGC }; // Used to search the tree until a condition is met. // Similar to TreeCensusClosure but searches the // tree and returns promptly when found. -template +template class FreeList_t> class TreeSearchClosure : public StackObj { protected: - virtual bool do_list(FreeList* fl) = 0; + virtual bool do_list(FreeList_t* fl) = 0; public: - virtual bool do_tree(TreeList* tl) = 0; + virtual bool do_tree(TreeList* tl) = 0; }; #if 0 // Don't need this yet but here for symmetry. -template -class AscendTreeSearchClosure : public TreeSearchClosure { +template class FreeList_t> +class AscendTreeSearchClosure : public TreeSearchClosure { public: - bool do_tree(TreeList* tl) { + bool do_tree(TreeList* tl) { if (tl != NULL) { if (do_tree(tl->left())) return true; if (do_list(tl)) return true; @@ -987,11 +1016,10 @@ }; #endif -template -class DescendTreeSearchClosure : public TreeSearchClosure { - using TreeSearchClosure::do_list; +template class FreeList_t> +class DescendTreeSearchClosure : public TreeSearchClosure { public: - bool do_tree(TreeList* tl) { + bool do_tree(TreeList* tl) { if (tl != NULL) { if (do_tree(tl->right())) return true; if (do_list(tl)) return true; @@ -1003,17 +1031,17 @@ // Searches the tree for a chunk that ends at the // specified address. -template -class EndTreeSearchClosure : public DescendTreeSearchClosure { +template class FreeList_t> +class EndTreeSearchClosure : public DescendTreeSearchClosure { HeapWord* _target; - Chunk* _found; + Chunk_t* _found; public: EndTreeSearchClosure(HeapWord* target) : _target(target), _found(NULL) {} - bool do_list(FreeList* fl) { - Chunk* item = fl->head(); + bool do_list(FreeList_t* fl) { + Chunk_t* item = fl->head(); while (item != NULL) { - if (item->end() == _target) { + if (item->end() == (uintptr_t*) _target) { _found = item; return true; } @@ -1021,22 +1049,22 @@ } return false; } - Chunk* found() { return _found; } + Chunk_t* found() { return _found; } }; -template -Chunk* BinaryTreeDictionary::find_chunk_ends_at(HeapWord* target) const { - EndTreeSearchClosure etsc(target); +template class FreeList_t> +Chunk_t* BinaryTreeDictionary::find_chunk_ends_at(HeapWord* target) const { + EndTreeSearchClosure etsc(target); bool found_target = etsc.do_tree(root()); assert(found_target || etsc.found() == NULL, "Consistency check"); assert(!found_target || etsc.found() != NULL, "Consistency check"); return etsc.found(); } -template -void BinaryTreeDictionary::begin_sweep_dict_census(double coalSurplusPercent, +template class FreeList_t> +void BinaryTreeDictionary::begin_sweep_dict_census(double coalSurplusPercent, float inter_sweep_current, float inter_sweep_estimate, float intra_sweep_estimate) { - BeginSweepClosure bsc(coalSurplusPercent, inter_sweep_current, + BeginSweepClosure bsc(coalSurplusPercent, inter_sweep_current, inter_sweep_estimate, intra_sweep_estimate); bsc.do_tree(root()); @@ -1045,84 +1073,91 @@ // Closures and methods for calculating total bytes returned to the // free lists in the tree. #ifndef PRODUCT -template -class InitializeDictReturnedBytesClosure : public AscendTreeCensusClosure { +template class FreeList_t> +class InitializeDictReturnedBytesClosure : public AscendTreeCensusClosure { public: - void do_list(FreeList* fl) { + void do_list(FreeList_t* fl) { fl->set_returned_bytes(0); } }; -template -void BinaryTreeDictionary::initialize_dict_returned_bytes() { - InitializeDictReturnedBytesClosure idrb; +template class FreeList_t> +void BinaryTreeDictionary::initialize_dict_returned_bytes() { + InitializeDictReturnedBytesClosure idrb; idrb.do_tree(root()); } -template -class ReturnedBytesClosure : public AscendTreeCensusClosure { +template class FreeList_t> +class ReturnedBytesClosure : public AscendTreeCensusClosure { size_t _dict_returned_bytes; public: ReturnedBytesClosure() { _dict_returned_bytes = 0; } - void do_list(FreeList* fl) { + void do_list(FreeList_t* fl) { _dict_returned_bytes += fl->returned_bytes(); } size_t dict_returned_bytes() { return _dict_returned_bytes; } }; -template -size_t BinaryTreeDictionary::sum_dict_returned_bytes() { - ReturnedBytesClosure rbc; +template class FreeList_t> +size_t BinaryTreeDictionary::sum_dict_returned_bytes() { + ReturnedBytesClosure rbc; rbc.do_tree(root()); return rbc.dict_returned_bytes(); } // Count the number of entries in the tree. -template -class treeCountClosure : public DescendTreeCensusClosure { +template class FreeList_t> +class treeCountClosure : public DescendTreeCensusClosure { public: uint count; treeCountClosure(uint c) { count = c; } - void do_list(FreeList* fl) { + void do_list(FreeList_t* fl) { count++; } }; -template -size_t BinaryTreeDictionary::total_count() { - treeCountClosure ctc(0); +template class FreeList_t> +size_t BinaryTreeDictionary::total_count() { + treeCountClosure ctc(0); ctc.do_tree(root()); return ctc.count; } #endif // PRODUCT // Calculate surpluses for the lists in the tree. -template -class setTreeSurplusClosure : public AscendTreeCensusClosure { +template class FreeList_t> +class setTreeSurplusClosure : public AscendTreeCensusClosure { double percentage; public: setTreeSurplusClosure(double v) { percentage = v; } - void do_list(FreeList* fl) { + void do_list(FreeList* fl) {} + +#ifndef SERIALGC + void do_list(AdaptiveFreeList* fl) { double splitSurplusPercent = percentage; fl->set_surplus(fl->count() - (ssize_t)((double)fl->desired() * splitSurplusPercent)); } +#endif // SERIALGC }; -template -void BinaryTreeDictionary::set_tree_surplus(double splitSurplusPercent) { - setTreeSurplusClosure sts(splitSurplusPercent); +template class FreeList_t> +void BinaryTreeDictionary::set_tree_surplus(double splitSurplusPercent) { + setTreeSurplusClosure sts(splitSurplusPercent); sts.do_tree(root()); } // Set hints for the lists in the tree. -template -class setTreeHintsClosure : public DescendTreeCensusClosure { +template class FreeList_t> +class setTreeHintsClosure : public DescendTreeCensusClosure { size_t hint; public: setTreeHintsClosure(size_t v) { hint = v; } - void do_list(FreeList* fl) { + void do_list(FreeList* fl) {} + +#ifndef SERIALGC + void do_list(AdaptiveFreeList* fl) { fl->set_hint(hint); assert(fl->hint() == 0 || fl->hint() > fl->size(), "Current hint is inconsistent"); @@ -1130,35 +1165,40 @@ hint = fl->size(); } } +#endif // SERIALGC }; -template -void BinaryTreeDictionary::set_tree_hints(void) { - setTreeHintsClosure sth(0); +template class FreeList_t> +void BinaryTreeDictionary::set_tree_hints(void) { + setTreeHintsClosure sth(0); sth.do_tree(root()); } // Save count before previous sweep and splits and coalesces. -template -class clearTreeCensusClosure : public AscendTreeCensusClosure { - void do_list(FreeList* fl) { +template class FreeList_t> +class clearTreeCensusClosure : public AscendTreeCensusClosure { + void do_list(FreeList* fl) {} + +#ifndef SERIALGC + void do_list(AdaptiveFreeList* fl) { fl->set_prev_sweep(fl->count()); fl->set_coal_births(0); fl->set_coal_deaths(0); fl->set_split_births(0); fl->set_split_deaths(0); } +#endif // SERIALGC }; -template -void BinaryTreeDictionary::clear_tree_census(void) { - clearTreeCensusClosure ctc; +template class FreeList_t> +void BinaryTreeDictionary::clear_tree_census(void) { + clearTreeCensusClosure ctc; ctc.do_tree(root()); } // Do reporting and post sweep clean up. -template -void BinaryTreeDictionary::end_sweep_dict_census(double splitSurplusPercent) { +template class FreeList_t> +void BinaryTreeDictionary::end_sweep_dict_census(double splitSurplusPercent) { // Does walking the tree 3 times hurt? set_tree_surplus(splitSurplusPercent); set_tree_hints(); @@ -1169,9 +1209,9 @@ } // Print summary statistics -template -void BinaryTreeDictionary::report_statistics() const { - FreeBlockDictionary::verify_par_locked(); +template class FreeList_t> +void BinaryTreeDictionary::report_statistics() const { + FreeBlockDictionary::verify_par_locked(); gclog_or_tty->print("Statistics for BinaryTreeDictionary:\n" "------------------------------------\n"); size_t total_size = total_chunk_size(debug_only(NULL)); @@ -1182,36 +1222,47 @@ if (free_blocks > 0) { gclog_or_tty->print("Av. Block Size: %d\n", total_size/free_blocks); } - gclog_or_tty->print("Tree Height: %d\n", treeHeight()); + gclog_or_tty->print("Tree Height: %d\n", tree_height()); } // Print census information - counts, births, deaths, etc. // for each list in the tree. Also print some summary // information. -template -class PrintTreeCensusClosure : public AscendTreeCensusClosure { +template class FreeList_t> +class PrintTreeCensusClosure : public AscendTreeCensusClosure { int _print_line; size_t _total_free; - FreeList _total; + FreeList_t _total; public: PrintTreeCensusClosure() { _print_line = 0; _total_free = 0; } - FreeList* total() { return &_total; } + FreeList_t* total() { return &_total; } size_t total_free() { return _total_free; } - void do_list(FreeList* fl) { + void do_list(FreeList* fl) { if (++_print_line >= 40) { - FreeList::print_labels_on(gclog_or_tty, "size"); + FreeList_t::print_labels_on(gclog_or_tty, "size"); _print_line = 0; } fl->print_on(gclog_or_tty); _total_free += fl->count() * fl->size() ; total()->set_count( total()->count() + fl->count() ); - total()->set_bfr_surp( total()->bfr_surp() + fl->bfr_surp() ); + } + +#ifndef SERIALGC + void do_list(AdaptiveFreeList* fl) { + if (++_print_line >= 40) { + FreeList_t::print_labels_on(gclog_or_tty, "size"); + _print_line = 0; + } + fl->print_on(gclog_or_tty); + _total_free += fl->count() * fl->size() ; + total()->set_count( total()->count() + fl->count() ); + total()->set_bfr_surp( total()->bfr_surp() + fl->bfr_surp() ); total()->set_surplus( total()->split_deaths() + fl->surplus() ); - total()->set_desired( total()->desired() + fl->desired() ); + total()->set_desired( total()->desired() + fl->desired() ); total()->set_prev_sweep( total()->prev_sweep() + fl->prev_sweep() ); total()->set_before_sweep(total()->before_sweep() + fl->before_sweep()); total()->set_coal_births( total()->coal_births() + fl->coal_births() ); @@ -1219,18 +1270,32 @@ total()->set_split_births(total()->split_births() + fl->split_births()); total()->set_split_deaths(total()->split_deaths() + fl->split_deaths()); } +#endif // SERIALGC }; -template -void BinaryTreeDictionary::print_dict_census(void) const { +template class FreeList_t> +void BinaryTreeDictionary::print_dict_census(void) const { gclog_or_tty->print("\nBinaryTree\n"); - FreeList::print_labels_on(gclog_or_tty, "size"); - PrintTreeCensusClosure ptc; + FreeList_t::print_labels_on(gclog_or_tty, "size"); + PrintTreeCensusClosure ptc; ptc.do_tree(root()); - FreeList* total = ptc.total(); - FreeList::print_labels_on(gclog_or_tty, " "); + FreeList_t* total = ptc.total(); + FreeList_t::print_labels_on(gclog_or_tty, " "); +} + +#ifndef SERIALGC +template <> +void BinaryTreeDictionary::print_dict_census(void) const { + + gclog_or_tty->print("\nBinaryTree\n"); + AdaptiveFreeList::print_labels_on(gclog_or_tty, "size"); + PrintTreeCensusClosure ptc; + ptc.do_tree(root()); + + AdaptiveFreeList* total = ptc.total(); + AdaptiveFreeList::print_labels_on(gclog_or_tty, " "); total->print_on(gclog_or_tty, "TOTAL\t"); gclog_or_tty->print( "total_free(words): " SIZE_FORMAT_W(16) @@ -1242,9 +1307,10 @@ (double)(total->desired() - total->count()) /(total->desired() != 0 ? (double)total->desired() : 1.0)); } +#endif // SERIALGC -template -class PrintFreeListsClosure : public AscendTreeCensusClosure { +template class FreeList_t> +class PrintFreeListsClosure : public AscendTreeCensusClosure { outputStream* _st; int _print_line; @@ -1253,14 +1319,14 @@ _st = st; _print_line = 0; } - void do_list(FreeList* fl) { + void do_list(FreeList_t* fl) { if (++_print_line >= 40) { - FreeList::print_labels_on(_st, "size"); + FreeList_t::print_labels_on(_st, "size"); _print_line = 0; } fl->print_on(gclog_or_tty); size_t sz = fl->size(); - for (Chunk* fc = fl->head(); fc != NULL; + for (Chunk_t* fc = fl->head(); fc != NULL; fc = fc->next()) { _st->print_cr("\t[" PTR_FORMAT "," PTR_FORMAT ") %s", fc, (HeapWord*)fc + sz, @@ -1269,11 +1335,11 @@ } }; -template -void BinaryTreeDictionary::print_free_lists(outputStream* st) const { +template class FreeList_t> +void BinaryTreeDictionary::print_free_lists(outputStream* st) const { - FreeList::print_labels_on(st, "size"); - PrintFreeListsClosure pflc(st); + FreeList_t::print_labels_on(st, "size"); + PrintFreeListsClosure pflc(st); pflc.do_tree(root()); } @@ -1281,18 +1347,18 @@ // . _root has no parent // . parent and child point to each other // . each node's key correctly related to that of its child(ren) -template -void BinaryTreeDictionary::verify_tree() const { +template class FreeList_t> +void BinaryTreeDictionary::verify_tree() const { guarantee(root() == NULL || total_free_blocks() == 0 || total_size() != 0, "_total_size should't be 0?"); guarantee(root() == NULL || root()->parent() == NULL, "_root shouldn't have parent"); verify_tree_helper(root()); } -template -size_t BinaryTreeDictionary::verify_prev_free_ptrs(TreeList* tl) { +template class FreeList_t> +size_t BinaryTreeDictionary::verify_prev_free_ptrs(TreeList* tl) { size_t ct = 0; - for (Chunk* curFC = tl->head(); curFC != NULL; curFC = curFC->next()) { + for (Chunk_t* curFC = tl->head(); curFC != NULL; curFC = curFC->next()) { ct++; assert(curFC->prev() == NULL || curFC->prev()->is_free(), "Chunk should be free"); @@ -1303,8 +1369,8 @@ // Note: this helper is recursive rather than iterative, so use with // caution on very deep trees; and watch out for stack overflow errors; // In general, to be used only for debugging. -template -void BinaryTreeDictionary::verify_tree_helper(TreeList* tl) const { +template class FreeList_t> +void BinaryTreeDictionary::verify_tree_helper(TreeList* tl) const { if (tl == NULL) return; guarantee(tl->size() != 0, "A list must has a size"); @@ -1332,15 +1398,25 @@ verify_tree_helper(tl->right()); } -template -void BinaryTreeDictionary::verify() const { +template class FreeList_t> +void BinaryTreeDictionary::verify() const { verify_tree(); guarantee(total_size() == total_size_in_tree(root()), "Total Size inconsistency"); } +template class TreeList; +template class BinaryTreeDictionary; +template class TreeChunk; + +template class TreeList; +template class BinaryTreeDictionary; +template class TreeChunk; + + #ifndef SERIALGC // Explicitly instantiate these types for FreeChunk. -template class BinaryTreeDictionary; -template class TreeChunk; -template class TreeList; +template class TreeList; +template class BinaryTreeDictionary; +template class TreeChunk; + #endif // SERIALGC diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/memory/binaryTreeDictionary.hpp --- a/src/share/vm/memory/binaryTreeDictionary.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/memory/binaryTreeDictionary.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -37,77 +37,78 @@ // A TreeList is a FreeList which can be used to maintain a // binary tree of free lists. -template class TreeChunk; -template class BinaryTreeDictionary; -template class AscendTreeCensusClosure; -template class DescendTreeCensusClosure; -template class DescendTreeSearchClosure; +template class FreeList_t> class TreeChunk; +template class FreeList_t> class BinaryTreeDictionary; +template class FreeList_t> class AscendTreeCensusClosure; +template class FreeList_t> class DescendTreeCensusClosure; +template class FreeList_t> class DescendTreeSearchClosure; -template -class TreeList: public FreeList { - friend class TreeChunk; - friend class BinaryTreeDictionary; - friend class AscendTreeCensusClosure; - friend class DescendTreeCensusClosure; - friend class DescendTreeSearchClosure; +template class FreeList_t> +class TreeList : public FreeList_t { + friend class TreeChunk; + friend class BinaryTreeDictionary; + friend class AscendTreeCensusClosure; + friend class DescendTreeCensusClosure; + friend class DescendTreeSearchClosure; - TreeList* _parent; - TreeList* _left; - TreeList* _right; + TreeList* _parent; + TreeList* _left; + TreeList* _right; protected: - TreeList* parent() const { return _parent; } - TreeList* left() const { return _left; } - TreeList* right() const { return _right; } - // Explicitly import these names into our namespace to fix name lookup with templates - using FreeList::head; - using FreeList::set_head; + TreeList* parent() const { return _parent; } + TreeList* left() const { return _left; } + TreeList* right() const { return _right; } - using FreeList::tail; - using FreeList::set_tail; - using FreeList::link_tail; + // Wrapper on call to base class, to get the template to compile. + Chunk_t* head() const { return FreeList_t::head(); } + Chunk_t* tail() const { return FreeList_t::tail(); } + void set_head(Chunk_t* head) { FreeList_t::set_head(head); } + void set_tail(Chunk_t* tail) { FreeList_t::set_tail(tail); } - using FreeList::increment_count; - NOT_PRODUCT(using FreeList::increment_returned_bytes_by;) - using FreeList::verify_chunk_in_free_list; - using FreeList::size; + size_t size() const { return FreeList_t::size(); } // Accessors for links in tree. - void set_left(TreeList* tl) { + void set_left(TreeList* tl) { _left = tl; if (tl != NULL) tl->set_parent(this); } - void set_right(TreeList* tl) { + void set_right(TreeList* tl) { _right = tl; if (tl != NULL) tl->set_parent(this); } - void set_parent(TreeList* tl) { _parent = tl; } + void set_parent(TreeList* tl) { _parent = tl; } - void clearLeft() { _left = NULL; } + void clear_left() { _left = NULL; } void clear_right() { _right = NULL; } void clear_parent() { _parent = NULL; } - void initialize() { clearLeft(); clear_right(), clear_parent(); } + void initialize() { clear_left(); clear_right(), clear_parent(); FreeList_t::initialize(); } // For constructing a TreeList from a Tree chunk or // address and size. - static TreeList* as_TreeList(TreeChunk* tc); - static TreeList* as_TreeList(HeapWord* addr, size_t size); + TreeList(); + static TreeList* + as_TreeList(TreeChunk* tc); + static TreeList* as_TreeList(HeapWord* addr, size_t size); // Returns the head of the free list as a pointer to a TreeChunk. - TreeChunk* head_as_TreeChunk(); + TreeChunk* head_as_TreeChunk(); // Returns the first available chunk in the free list as a pointer // to a TreeChunk. - TreeChunk* first_available(); + TreeChunk* first_available(); // Returns the block with the largest heap address amongst // those in the list for this size; potentially slow and expensive, // use with caution! - TreeChunk* largest_address(); + TreeChunk* largest_address(); + + TreeList* get_better_list( + BinaryTreeDictionary* dictionary); // remove_chunk_replace_if_needed() removes the given "tc" from the TreeList. // If "tc" is the first chunk in the list, it is also the @@ -115,10 +116,10 @@ // returns the possibly replaced TreeList* for the node in // the tree. It also updates the parent of the original // node to point to the new node. - TreeList* remove_chunk_replace_if_needed(TreeChunk* tc); + TreeList* remove_chunk_replace_if_needed(TreeChunk* tc); // See FreeList. - void return_chunk_at_head(TreeChunk* tc); - void return_chunk_at_tail(TreeChunk* tc); + void return_chunk_at_head(TreeChunk* tc); + void return_chunk_at_tail(TreeChunk* tc); }; // A TreeChunk is a subclass of a Chunk that additionally @@ -134,52 +135,54 @@ // on the free list for a node in the tree and is only removed if // it is the last chunk on the free list. -template -class TreeChunk : public Chunk { - friend class TreeList; - TreeList* _list; - TreeList _embedded_list; // if non-null, this chunk is on _list +template class FreeList_t> +class TreeChunk : public Chunk_t { + friend class TreeList; + TreeList* _list; + TreeList _embedded_list; // if non-null, this chunk is on _list + + static size_t _min_tree_chunk_size; + protected: - TreeList* embedded_list() const { return (TreeList*) &_embedded_list; } - void set_embedded_list(TreeList* v) { _embedded_list = *v; } + TreeList* embedded_list() const { return (TreeList*) &_embedded_list; } + void set_embedded_list(TreeList* v) { _embedded_list = *v; } public: - TreeList* list() { return _list; } - void set_list(TreeList* v) { _list = v; } - static TreeChunk* as_TreeChunk(Chunk* fc); + TreeList* list() { return _list; } + void set_list(TreeList* v) { _list = v; } + static TreeChunk* as_TreeChunk(Chunk_t* fc); // Initialize fields in a TreeChunk that should be // initialized when the TreeChunk is being added to // a free list in the tree. void initialize() { embedded_list()->initialize(); } - Chunk* next() const { return Chunk::next(); } - Chunk* prev() const { return Chunk::prev(); } - size_t size() const volatile { return Chunk::size(); } + Chunk_t* next() const { return Chunk_t::next(); } + Chunk_t* prev() const { return Chunk_t::prev(); } + size_t size() const volatile { return Chunk_t::size(); } + + static size_t min_size() { + return _min_tree_chunk_size; + } // debugging void verify_tree_chunk_list() const; + void assert_is_mangled() const; }; -template -class BinaryTreeDictionary: public FreeBlockDictionary { +template class FreeList_t> +class BinaryTreeDictionary: public FreeBlockDictionary { friend class VMStructs; - bool _splay; - bool _adaptive_freelists; size_t _total_size; size_t _total_free_blocks; - TreeList* _root; + TreeList* _root; // private accessors - bool splay() const { return _splay; } - void set_splay(bool v) { _splay = v; } void set_total_size(size_t v) { _total_size = v; } virtual void inc_total_size(size_t v); virtual void dec_total_size(size_t v); - size_t total_free_blocks() const { return _total_free_blocks; } void set_total_free_blocks(size_t v) { _total_free_blocks = v; } - TreeList* root() const { return _root; } - void set_root(TreeList* v) { _root = v; } - bool adaptive_freelists() { return _adaptive_freelists; } + TreeList* root() const { return _root; } + void set_root(TreeList* v) { _root = v; } // This field is added and can be set to point to the // the Mutex used to synchronize access to the @@ -191,54 +194,55 @@ // return it. If the chunk // is the last chunk of that size, remove the node for that size // from the tree. - TreeChunk* get_chunk_from_tree(size_t size, enum FreeBlockDictionary::Dither dither, bool splay); - // Return a list of the specified size or NULL from the tree. - // The list is not removed from the tree. - TreeList* find_list (size_t size) const; + TreeChunk* get_chunk_from_tree(size_t size, enum FreeBlockDictionary::Dither dither); // Remove this chunk from the tree. If the removal results // in an empty list in the tree, remove the empty list. - TreeChunk* remove_chunk_from_tree(TreeChunk* tc); + TreeChunk* remove_chunk_from_tree(TreeChunk* tc); // Remove the node in the trees starting at tl that has the // minimum value and return it. Repair the tree as needed. - TreeList* remove_tree_minimum(TreeList* tl); - void semi_splay_step(TreeList* tl); + TreeList* remove_tree_minimum(TreeList* tl); // Add this free chunk to the tree. - void insert_chunk_in_tree(Chunk* freeChunk); + void insert_chunk_in_tree(Chunk_t* freeChunk); public: - static const size_t min_tree_chunk_size = sizeof(TreeChunk)/HeapWordSize; + // Return a list of the specified size or NULL from the tree. + // The list is not removed from the tree. + TreeList* find_list (size_t size) const; void verify_tree() const; // verify that the given chunk is in the tree. - bool verify_chunk_in_free_list(Chunk* tc) const; + bool verify_chunk_in_free_list(Chunk_t* tc) const; private: - void verify_tree_helper(TreeList* tl) const; - static size_t verify_prev_free_ptrs(TreeList* tl); + void verify_tree_helper(TreeList* tl) const; + static size_t verify_prev_free_ptrs(TreeList* tl); // Returns the total number of chunks in the list. - size_t total_list_length(TreeList* tl) const; + size_t total_list_length(TreeList* tl) const; // Returns the total number of words in the chunks in the tree // starting at "tl". - size_t total_size_in_tree(TreeList* tl) const; + size_t total_size_in_tree(TreeList* tl) const; // Returns the sum of the square of the size of each block // in the tree starting at "tl". - double sum_of_squared_block_sizes(TreeList* const tl) const; + double sum_of_squared_block_sizes(TreeList* const tl) const; // Returns the total number of free blocks in the tree starting // at "tl". - size_t total_free_blocks_in_tree(TreeList* tl) const; - size_t num_free_blocks() const; - size_t treeHeight() const; - size_t tree_height_helper(TreeList* tl) const; - size_t total_nodes_in_tree(TreeList* tl) const; - size_t total_nodes_helper(TreeList* tl) const; + size_t total_free_blocks_in_tree(TreeList* tl) const; + size_t num_free_blocks() const; + size_t tree_height() const; + size_t tree_height_helper(TreeList* tl) const; + size_t total_nodes_in_tree(TreeList* tl) const; + size_t total_nodes_helper(TreeList* tl) const; public: // Constructor - BinaryTreeDictionary(bool adaptive_freelists, bool splay = false); - BinaryTreeDictionary(MemRegion mr, bool adaptive_freelists, bool splay = false); + BinaryTreeDictionary() : + _total_size(0), _total_free_blocks(0), _root(0) {} + + BinaryTreeDictionary(MemRegion mr); // Public accessors size_t total_size() const { return _total_size; } + size_t total_free_blocks() const { return _total_free_blocks; } // Reset the dictionary to the initial conditions with // a single free chunk. @@ -249,23 +253,24 @@ // Return a chunk of size "size" or greater from // the tree. - // want a better dynamic splay strategy for the future. - Chunk* get_chunk(size_t size, enum FreeBlockDictionary::Dither dither) { - FreeBlockDictionary::verify_par_locked(); - Chunk* res = get_chunk_from_tree(size, dither, splay()); + Chunk_t* get_chunk(size_t size, enum FreeBlockDictionary::Dither dither) { + FreeBlockDictionary::verify_par_locked(); + Chunk_t* res = get_chunk_from_tree(size, dither); assert(res == NULL || res->is_free(), "Should be returning a free chunk"); + assert(dither != FreeBlockDictionary::exactly || + res == NULL || res->size() == size, "Not correct size"); return res; } - void return_chunk(Chunk* chunk) { - FreeBlockDictionary::verify_par_locked(); + void return_chunk(Chunk_t* chunk) { + FreeBlockDictionary::verify_par_locked(); insert_chunk_in_tree(chunk); } - void remove_chunk(Chunk* chunk) { - FreeBlockDictionary::verify_par_locked(); - remove_chunk_from_tree((TreeChunk*)chunk); + void remove_chunk(Chunk_t* chunk) { + FreeBlockDictionary::verify_par_locked(); + remove_chunk_from_tree((TreeChunk*)chunk); assert(chunk->is_free(), "Should still be a free chunk"); } @@ -281,19 +286,19 @@ } size_t min_size() const { - return min_tree_chunk_size; + return TreeChunk::min_size(); } double sum_of_squared_block_sizes() const { return sum_of_squared_block_sizes(root()); } - Chunk* find_chunk_ends_at(HeapWord* target) const; + Chunk_t* find_chunk_ends_at(HeapWord* target) const; // Find the list with size "size" in the binary tree and update // the statistics in the list according to "split" (chunk was // split or coalesce) and "birth" (chunk was added or removed). - void dict_census_udpate(size_t size, bool split, bool birth); + void dict_census_update(size_t size, bool split, bool birth); // Return true if the dictionary is overpopulated (more chunks of // this size than desired) for size "size". bool coal_dict_over_populated(size_t size); @@ -307,7 +312,7 @@ // statistics for the sweep. void end_sweep_dict_census(double splitSurplusPercent); // Return the largest free chunk in the tree. - Chunk* find_largest_dict() const; + Chunk_t* find_largest_dict() const; // Accessors for statistics void set_tree_surplus(double splitSurplusPercent); void set_tree_hints(void); diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/memory/collectorPolicy.cpp --- a/src/share/vm/memory/collectorPolicy.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/memory/collectorPolicy.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -742,6 +742,8 @@ uint gc_count = 0; uint full_gc_count = 0; + assert(!Heap_lock->owned_by_self(), "Should not be holding the Heap_lock"); + do { MetaWord* result = NULL; if (GC_locker::is_active_and_needs_gc()) { @@ -756,7 +758,6 @@ } JavaThread* jthr = JavaThread::current(); if (!jthr->in_critical()) { - MutexUnlocker mul(Heap_lock); // Wait for JNI critical section to be exited GC_locker::stall_until_clear(); // The GC invoked by the last thread leaving the critical diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/memory/freeBlockDictionary.cpp --- a/src/share/vm/memory/freeBlockDictionary.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/memory/freeBlockDictionary.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -27,6 +27,8 @@ #include "gc_implementation/concurrentMarkSweep/freeChunk.hpp" #endif // SERIALGC #include "memory/freeBlockDictionary.hpp" +#include "memory/metablock.hpp" +#include "memory/metachunk.hpp" #ifdef TARGET_OS_FAMILY_linux # include "thread_linux.inline.hpp" #endif @@ -62,6 +64,9 @@ } #endif +template class FreeBlockDictionary; +template class FreeBlockDictionary; + #ifndef SERIALGC // Explicitly instantiate for FreeChunk template class FreeBlockDictionary; diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/memory/freeBlockDictionary.hpp --- a/src/share/vm/memory/freeBlockDictionary.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/memory/freeBlockDictionary.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -66,7 +66,7 @@ virtual void reset(HeapWord* addr, size_t size) = 0; virtual void reset() = 0; - virtual void dict_census_udpate(size_t size, bool split, bool birth) = 0; + virtual void dict_census_update(size_t size, bool split, bool birth) = 0; virtual bool coal_dict_over_populated(size_t size) = 0; virtual void begin_sweep_dict_census(double coalSurplusPercent, float inter_sweep_current, float inter_sweep_estimate, diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/memory/freeList.cpp --- a/src/share/vm/memory/freeList.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/memory/freeList.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -25,6 +25,8 @@ #include "precompiled.hpp" #include "memory/freeBlockDictionary.hpp" #include "memory/freeList.hpp" +#include "memory/metablock.hpp" +#include "memory/metachunk.hpp" #include "memory/sharedHeap.hpp" #include "runtime/globals.hpp" #include "runtime/mutex.hpp" @@ -49,8 +51,6 @@ { _size = 0; _count = 0; - _hint = 0; - init_statistics(); } template @@ -62,34 +62,50 @@ { _size = fc->size(); _count = 1; - _hint = 0; - init_statistics(); -#ifndef PRODUCT - _allocation_stats.set_returned_bytes(size() * HeapWordSize); -#endif } template -void FreeList::reset(size_t hint) { +void FreeList::link_head(Chunk* v) { + assert_proper_lock_protection(); + set_head(v); + // If this method is not used (just set the head instead), + // this check can be avoided. + if (v != NULL) { + v->link_prev(NULL); + } +} + + + +template +void FreeList::reset() { + // Don't set the _size to 0 because this method is + // used with a existing list that has a size but which has + // been emptied. + // Don't clear the _protecting_lock of an existing list. set_count(0); set_head(NULL); set_tail(NULL); - set_hint(hint); } template -void FreeList::init_statistics(bool split_birth) { - _allocation_stats.initialize(split_birth); +void FreeList::initialize() { +#ifdef ASSERT + // Needed early because it might be checked in other initializing code. + set_protecting_lock(NULL); +#endif + reset(); + set_size(0); } -template -Chunk* FreeList::get_chunk_at_head() { +template +Chunk_t* FreeList::get_chunk_at_head() { assert_proper_lock_protection(); assert(head() == NULL || head()->prev() == NULL, "list invariant"); assert(tail() == NULL || tail()->next() == NULL, "list invariant"); - Chunk* fc = head(); + Chunk_t* fc = head(); if (fc != NULL) { - Chunk* nextFC = fc->next(); + Chunk_t* nextFC = fc->next(); if (nextFC != NULL) { // The chunk fc being removed has a "next". Set the "next" to the // "prev" of fc. @@ -197,11 +213,6 @@ link_tail(chunk); } increment_count(); // of # of chunks in list - DEBUG_ONLY( - if (record_return) { - increment_returned_bytes_by(size()*HeapWordSize); - } - ) assert(head() == NULL || head()->prev() == NULL, "list invariant"); assert(tail() == NULL || tail()->next() == NULL, "list invariant"); assert(head() == NULL || head()->size() == size(), "wrong item on list"); @@ -233,11 +244,6 @@ } link_tail(chunk); increment_count(); // of # of chunks in list - DEBUG_ONLY( - if (record_return) { - increment_returned_bytes_by(size()*HeapWordSize); - } - ) assert(head() == NULL || head()->prev() == NULL, "list invariant"); assert(tail() == NULL || tail()->next() == NULL, "list invariant"); assert(head() == NULL || head()->size() == size(), "wrong item on list"); @@ -273,7 +279,7 @@ } } -// verify_chunk_in_free_list() is used to verify that an item is in this free list. +// verify_chunk_in_free_lists() is used to verify that an item is in this free list. // It is used as a debugging aid. template bool FreeList::verify_chunk_in_free_list(Chunk* fc) const { @@ -294,40 +300,14 @@ #ifndef PRODUCT template -void FreeList::verify_stats() const { - // The +1 of the LH comparand is to allow some "looseness" in - // checking: we usually call this interface when adding a block - // and we'll subsequently update the stats; we cannot update the - // stats beforehand because in the case of the large-block BT - // dictionary for example, this might be the first block and - // in that case there would be no place that we could record - // the stats (which are kept in the block itself). - assert((_allocation_stats.prev_sweep() + _allocation_stats.split_births() - + _allocation_stats.coal_births() + 1) // Total Production Stock + 1 - >= (_allocation_stats.split_deaths() + _allocation_stats.coal_deaths() - + (ssize_t)count()), // Total Current Stock + depletion - err_msg("FreeList " PTR_FORMAT " of size " SIZE_FORMAT - " violates Conservation Principle: " - "prev_sweep(" SIZE_FORMAT ")" - " + split_births(" SIZE_FORMAT ")" - " + coal_births(" SIZE_FORMAT ") + 1 >= " - " split_deaths(" SIZE_FORMAT ")" - " coal_deaths(" SIZE_FORMAT ")" - " + count(" SSIZE_FORMAT ")", - this, _size, _allocation_stats.prev_sweep(), _allocation_stats.split_births(), - _allocation_stats.split_births(), _allocation_stats.split_deaths(), - _allocation_stats.coal_deaths(), count())); -} - -template void FreeList::assert_proper_lock_protection_work() const { - assert(_protecting_lock != NULL, "Don't call this directly"); + assert(protecting_lock() != NULL, "Don't call this directly"); assert(ParallelGCThreads > 0, "Don't call this directly"); Thread* thr = Thread::current(); if (thr->is_VM_thread() || thr->is_ConcurrentGC_thread()) { // assert that we are holding the freelist lock } else if (thr->is_GC_task_thread()) { - assert(_protecting_lock->owned_by_self(), "FreeList RACE DETECTED"); + assert(protecting_lock()->owned_by_self(), "FreeList RACE DETECTED"); } else if (thr->is_Java_thread()) { assert(!SafepointSynchronize::is_at_safepoint(), "Should not be executing"); } else { @@ -350,21 +330,17 @@ // to the call is a non-null string, it is printed in the first column; // otherwise, if the argument is null (the default), then the size of the // (free list) block is printed in the first column. -template -void FreeList::print_on(outputStream* st, const char* c) const { +template +void FreeList::print_on(outputStream* st, const char* c) const { if (c != NULL) { st->print("%16s", c); } else { st->print(SIZE_FORMAT_W(16), size()); } - st->print("\t" - SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" - SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\t" SSIZE_FORMAT_W(14) "\n", - bfr_surp(), surplus(), desired(), prev_sweep(), before_sweep(), - count(), coal_births(), coal_deaths(), split_births(), split_deaths()); } +template class FreeList; +template class FreeList; #ifndef SERIALGC -// Needs to be after the definitions have been seen. template class FreeList; #endif // SERIALGC diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/memory/freeList.hpp --- a/src/share/vm/memory/freeList.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/memory/freeList.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -40,23 +40,19 @@ // for that implementation. class Mutex; -template class TreeList; -template class PrintTreeCensusClosure; -template +template class FreeList VALUE_OBJ_CLASS_SPEC { friend class CompactibleFreeListSpace; friend class VMStructs; - friend class PrintTreeCensusClosure; private: - Chunk* _head; // Head of list of free chunks - Chunk* _tail; // Tail of list of free chunks + Chunk_t* _head; // Head of list of free chunks + Chunk_t* _tail; // Tail of list of free chunks size_t _size; // Size in Heap words of each chunk ssize_t _count; // Number of entries in list - size_t _hint; // next larger size list with a positive surplus - AllocationStats _allocation_stats; // allocation-related statistics + protected: #ifdef ASSERT Mutex* _protecting_lock; @@ -71,10 +67,6 @@ #endif } - // Initialize the allocation statistics. - protected: - void init_statistics(bool split_birth = false); - void set_count(ssize_t v) { _count = v;} void increment_count() { _count++; } @@ -89,52 +81,48 @@ // Construct a list without any entries. FreeList(); // Construct a list with "fc" as the first (and lone) entry in the list. - FreeList(Chunk* fc); + FreeList(Chunk_t* fc); - // Reset the head, tail, hint, and count of a free list. - void reset(size_t hint); + // Do initialization + void initialize(); + + // Reset the head, tail, and count of a free list. + void reset(); // Declare the current free list to be protected by the given lock. #ifdef ASSERT - void set_protecting_lock(Mutex* protecting_lock) { - _protecting_lock = protecting_lock; + Mutex* protecting_lock() const { return _protecting_lock; } + void set_protecting_lock(Mutex* v) { + _protecting_lock = v; } #endif // Accessors. - Chunk* head() const { + Chunk_t* head() const { assert_proper_lock_protection(); return _head; } - void set_head(Chunk* v) { + void set_head(Chunk_t* v) { assert_proper_lock_protection(); _head = v; assert(!_head || _head->size() == _size, "bad chunk size"); } // Set the head of the list and set the prev field of non-null // values to NULL. - void link_head(Chunk* v) { - assert_proper_lock_protection(); - set_head(v); - // If this method is not used (just set the head instead), - // this check can be avoided. - if (v != NULL) { - v->link_prev(NULL); - } - } + void link_head(Chunk_t* v); - Chunk* tail() const { + Chunk_t* tail() const { assert_proper_lock_protection(); return _tail; } - void set_tail(Chunk* v) { + void set_tail(Chunk_t* v) { assert_proper_lock_protection(); _tail = v; assert(!_tail || _tail->size() == _size, "bad chunk size"); } // Set the tail of the list and set the next field of non-null // values to NULL. - void link_tail(Chunk* v) { + void link_tail(Chunk_t* v) { assert_proper_lock_protection(); set_tail(v); if (v != NULL) { @@ -152,174 +140,45 @@ assert_proper_lock_protection(); _size = v; } - ssize_t count() const { - return _count; - } - size_t hint() const { - return _hint; - } - void set_hint(size_t v) { - assert_proper_lock_protection(); - assert(v == 0 || _size < v, "Bad hint"); _hint = v; - } - - // Accessors for statistics - AllocationStats* allocation_stats() { - assert_proper_lock_protection(); - return &_allocation_stats; - } - - ssize_t desired() const { - return _allocation_stats.desired(); - } - void set_desired(ssize_t v) { - assert_proper_lock_protection(); - _allocation_stats.set_desired(v); - } - void compute_desired(float inter_sweep_current, - float inter_sweep_estimate, - float intra_sweep_estimate) { - assert_proper_lock_protection(); - _allocation_stats.compute_desired(_count, - inter_sweep_current, - inter_sweep_estimate, - intra_sweep_estimate); - } - ssize_t coal_desired() const { - return _allocation_stats.coal_desired(); - } - void set_coal_desired(ssize_t v) { - assert_proper_lock_protection(); - _allocation_stats.set_coal_desired(v); - } - - ssize_t surplus() const { - return _allocation_stats.surplus(); - } - void set_surplus(ssize_t v) { - assert_proper_lock_protection(); - _allocation_stats.set_surplus(v); - } - void increment_surplus() { - assert_proper_lock_protection(); - _allocation_stats.increment_surplus(); - } - void decrement_surplus() { - assert_proper_lock_protection(); - _allocation_stats.decrement_surplus(); - } + ssize_t count() const { return _count; } + void set_count(ssize_t v) { _count = v;} - ssize_t bfr_surp() const { - return _allocation_stats.bfr_surp(); - } - void set_bfr_surp(ssize_t v) { - assert_proper_lock_protection(); - _allocation_stats.set_bfr_surp(v); - } - ssize_t prev_sweep() const { - return _allocation_stats.prev_sweep(); - } - void set_prev_sweep(ssize_t v) { - assert_proper_lock_protection(); - _allocation_stats.set_prev_sweep(v); - } - ssize_t before_sweep() const { - return _allocation_stats.before_sweep(); - } - void set_before_sweep(ssize_t v) { - assert_proper_lock_protection(); - _allocation_stats.set_before_sweep(v); - } - - ssize_t coal_births() const { - return _allocation_stats.coal_births(); - } - void set_coal_births(ssize_t v) { - assert_proper_lock_protection(); - _allocation_stats.set_coal_births(v); - } - void increment_coal_births() { - assert_proper_lock_protection(); - _allocation_stats.increment_coal_births(); - } + size_t get_better_size() { return size(); } - ssize_t coal_deaths() const { - return _allocation_stats.coal_deaths(); - } - void set_coal_deaths(ssize_t v) { - assert_proper_lock_protection(); - _allocation_stats.set_coal_deaths(v); - } - void increment_coal_deaths() { - assert_proper_lock_protection(); - _allocation_stats.increment_coal_deaths(); - } - - ssize_t split_births() const { - return _allocation_stats.split_births(); - } - void set_split_births(ssize_t v) { - assert_proper_lock_protection(); - _allocation_stats.set_split_births(v); - } - void increment_split_births() { - assert_proper_lock_protection(); - _allocation_stats.increment_split_births(); - } - - ssize_t split_deaths() const { - return _allocation_stats.split_deaths(); - } - void set_split_deaths(ssize_t v) { - assert_proper_lock_protection(); - _allocation_stats.set_split_deaths(v); - } - void increment_split_deaths() { - assert_proper_lock_protection(); - _allocation_stats.increment_split_deaths(); - } - - NOT_PRODUCT( - // For debugging. The "_returned_bytes" in all the lists are summed - // and compared with the total number of bytes swept during a - // collection. - size_t returned_bytes() const { return _allocation_stats.returned_bytes(); } - void set_returned_bytes(size_t v) { _allocation_stats.set_returned_bytes(v); } - void increment_returned_bytes_by(size_t v) { - _allocation_stats.set_returned_bytes(_allocation_stats.returned_bytes() + v); - } - ) + size_t returned_bytes() const { ShouldNotReachHere(); return 0; } + void set_returned_bytes(size_t v) {} + void increment_returned_bytes_by(size_t v) {} // Unlink head of list and return it. Returns NULL if // the list is empty. - Chunk* get_chunk_at_head(); + Chunk_t* get_chunk_at_head(); // Remove the first "n" or "count", whichever is smaller, chunks from the // list, setting "fl", which is required to be empty, to point to them. - void getFirstNChunksFromList(size_t n, FreeList* fl); + void getFirstNChunksFromList(size_t n, FreeList* fl); // Unlink this chunk from it's free list - void remove_chunk(Chunk* fc); + void remove_chunk(Chunk_t* fc); // Add this chunk to this free list. - void return_chunk_at_head(Chunk* fc); - void return_chunk_at_tail(Chunk* fc); + void return_chunk_at_head(Chunk_t* fc); + void return_chunk_at_tail(Chunk_t* fc); // Similar to returnChunk* but also records some diagnostic // information. - void return_chunk_at_head(Chunk* fc, bool record_return); - void return_chunk_at_tail(Chunk* fc, bool record_return); + void return_chunk_at_head(Chunk_t* fc, bool record_return); + void return_chunk_at_tail(Chunk_t* fc, bool record_return); // Prepend "fl" (whose size is required to be the same as that of "this") // to the front of "this" list. - void prepend(FreeList* fl); + void prepend(FreeList* fl); // Verify that the chunk is in the list. // found. Return NULL if "fc" is not found. - bool verify_chunk_in_free_list(Chunk* fc) const; + bool verify_chunk_in_free_list(Chunk_t* fc) const; // Stats verification - void verify_stats() const PRODUCT_RETURN; +// void verify_stats() const { ShouldNotReachHere(); }; // Printing support static void print_labels_on(outputStream* st, const char* c); diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/memory/metablock.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/memory/metablock.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +#ifndef SHARE_VM_MEMORY_METABLOCK_HPP +#define SHARE_VM_MEMORY_METABLOCK_HPP + +// Metablock are the unit of allocation from a Chunk. It is initialized +// with the size of the requested allocation. That size is overwritten +// once the allocation returns. +// +// A Metablock may be reused by its SpaceManager but are never moved between +// SpaceManagers. There is no explicit link to the Metachunk +// from which it was allocated. Metablock may be deallocated and +// put on a freelist but the space is never freed, rather +// the Metachunk it is a part of will be deallocated when it's +// associated class loader is collected. + +class Metablock VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + private: + // Used to align the allocation (see below). + union block_t { + void* _data[3]; + struct header_t { + size_t _word_size; + Metablock* _next; + Metablock* _prev; + } _header; + } _block; + static size_t _min_block_byte_size; + static size_t _overhead; + + typedef union block_t Block; + typedef struct header_t Header; + const Block* block() const { return &_block; } + const Block::header_t* header() const { return &(block()->_header); } + public: + + static Metablock* initialize(MetaWord* p, size_t word_size); + + // This places the body of the block at a 2 word boundary + // because every block starts on a 2 word boundary. Work out + // how to make the body on a 2 word boundary if the block + // starts on a arbitrary boundary. JJJ + + size_t word_size() const { return header()->_word_size; } + void set_word_size(size_t v) { _block._header._word_size = v; } + size_t size() const volatile { return _block._header._word_size; } + void set_size(size_t v) { _block._header._word_size = v; } + Metablock* next() const { return header()->_next; } + void set_next(Metablock* v) { _block._header._next = v; } + Metablock* prev() const { return header()->_prev; } + void set_prev(Metablock* v) { _block._header._prev = v; } + + static size_t min_block_byte_size() { return _min_block_byte_size; } + static size_t overhead() { return _overhead; } + + bool is_free() { return header()->_word_size != 0; } + void clear_next() { set_next(NULL); } + void link_prev(Metablock* ptr) { set_prev(ptr); } + uintptr_t* end() { return ((uintptr_t*) this) + size(); } + bool cantCoalesce() const { return false; } + void link_next(Metablock* ptr) { set_next(ptr); } + void link_after(Metablock* ptr){ + link_next(ptr); + if (ptr != NULL) ptr->link_prev(this); + } + + // Should not be needed in a free list of Metablocks + void markNotFree() { ShouldNotReachHere(); } + + // Debug support +#ifdef ASSERT + void* prev_addr() const { return (void*)&_block._header._prev; } + void* next_addr() const { return (void*)&_block._header._next; } + void* size_addr() const { return (void*)&_block._header._word_size; } +#endif + bool verify_chunk_in_free_list(Metablock* tc) const { return true; } + bool verify_par_locked() { return true; } + + void assert_is_mangled() const {/* Don't check "\*/} +}; +#endif // SHARE_VM_MEMORY_METABLOCK_HPP diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/memory/metachunk.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/memory/metachunk.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +#ifndef SHARE_VM_MEMORY_METACHUNK_HPP +#define SHARE_VM_MEMORY_METACHUNK_HPP + +// Metachunk - Quantum of allocation from a Virtualspace +// Metachunks are reused (when freed are put on a global freelist) and +// have no permanent association to a SpaceManager. + +// +--------------+ <- end +// | | --+ ---+ +// | | | free | +// | | | | +// | | | | capacity +// | | | | +// | | <- top --+ | +// | | ---+ | +// | | | used | +// | | | | +// | | | | +// +--------------+ <- bottom ---+ ---+ + +class Metachunk VALUE_OBJ_CLASS_SPEC { + // link to support lists of chunks + Metachunk* _next; + Metachunk* _prev; + + MetaWord* _bottom; + MetaWord* _end; + MetaWord* _top; + size_t _word_size; + // Used in a guarantee() so included in the Product builds + // even through it is only for debugging. + bool _is_free; + + // Metachunks are allocated out of a MetadataVirtualSpace and + // and use some of its space to describe itself (plus alignment + // considerations). Metadata is allocated in the rest of the chunk. + // This size is the overhead of maintaining the Metachunk within + // the space. + static size_t _overhead; + + void set_bottom(MetaWord* v) { _bottom = v; } + void set_end(MetaWord* v) { _end = v; } + void set_top(MetaWord* v) { _top = v; } + void set_word_size(size_t v) { _word_size = v; } + public: +#ifdef ASSERT + Metachunk() : _bottom(NULL), _end(NULL), _top(NULL), _is_free(false) {} +#else + Metachunk() : _bottom(NULL), _end(NULL), _top(NULL) {} +#endif + + // Used to add a Metachunk to a list of Metachunks + void set_next(Metachunk* v) { _next = v; assert(v != this, "Boom");} + void set_prev(Metachunk* v) { _prev = v; assert(v != this, "Boom");} + + MetaWord* allocate(size_t word_size); + static Metachunk* initialize(MetaWord* ptr, size_t word_size); + + // Accessors + Metachunk* next() const { return _next; } + Metachunk* prev() const { return _prev; } + MetaWord* bottom() const { return _bottom; } + MetaWord* end() const { return _end; } + MetaWord* top() const { return _top; } + size_t word_size() const { return _word_size; } + size_t size() const volatile { return _word_size; } + void set_size(size_t v) { _word_size = v; } + bool is_free() { return _is_free; } + void set_is_free(bool v) { _is_free = v; } + static size_t overhead() { return _overhead; } + void clear_next() { set_next(NULL); } + void link_prev(Metachunk* ptr) { set_prev(ptr); } + uintptr_t* end() { return ((uintptr_t*) this) + size(); } + bool cantCoalesce() const { return false; } + void link_next(Metachunk* ptr) { set_next(ptr); } + void link_after(Metachunk* ptr){ + link_next(ptr); + if (ptr != NULL) ptr->link_prev(this); + } + + // Reset top to bottom so chunk can be reused. + void reset_empty() { _top = (_bottom + _overhead); } + bool is_empty() { return _top == (_bottom + _overhead); } + + // used (has been allocated) + // free (available for future allocations) + // capacity (total size of chunk) + size_t used_word_size(); + size_t free_word_size(); + size_t capacity_word_size(); + + // Debug support +#ifdef ASSERT + void* prev_addr() const { return (void*)&_prev; } + void* next_addr() const { return (void*)&_next; } + void* size_addr() const { return (void*)&_word_size; } +#endif + bool verify_chunk_in_free_list(Metachunk* tc) const { return true; } + bool verify_par_locked() { return true; } + + void assert_is_mangled() const {/* Don't check "\*/} + +#ifdef ASSERT + void mangle(); +#endif // ASSERT + + void print_on(outputStream* st) const; + void verify(); +}; +#endif // SHARE_VM_MEMORY_METACHUNK_HPP diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/memory/metaspace.cpp --- a/src/share/vm/memory/metaspace.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/memory/metaspace.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -24,9 +24,12 @@ #include "precompiled.hpp" #include "gc_interface/collectedHeap.hpp" #include "memory/binaryTreeDictionary.hpp" +#include "memory/freeList.hpp" #include "memory/collectorPolicy.hpp" #include "memory/filemap.hpp" #include "memory/freeList.hpp" +#include "memory/metablock.hpp" +#include "memory/metachunk.hpp" #include "memory/metaspace.hpp" #include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" @@ -37,15 +40,8 @@ #include "utilities/copy.hpp" #include "utilities/debug.hpp" -// Define this macro to deallocate Metablock. If not defined, -// blocks are not yet deallocated and are only mangled. -#undef DEALLOCATE_BLOCKS - -// Easily recognizable patterns -// These patterns can be the same in 32bit or 64bit since -// they only have to be easily recognizable. -const void* metaspace_allocation_leader = (void*) 0X11111111; -const void* metaspace_allocation_trailer = (void*) 0X77777777; +typedef BinaryTreeDictionary BlockTreeDictionary; +typedef BinaryTreeDictionary ChunkTreeDictionary; // Parameters for stress mode testing const uint metadata_deallocate_a_lot_block = 10; @@ -53,7 +49,6 @@ size_t const allocation_from_dictionary_limit = 64 * K; const size_t metadata_chunk_initialize = 0xf7f7f7f7; const size_t metadata_deallocate = 0xf5f5f5f5; -const size_t metadata_space_manager_allocate = 0xf3f3f3f3; MetaWord* last_allocated = 0; @@ -62,11 +57,12 @@ SmallIndex = 0, MediumIndex = 1, HumongousIndex = 2, - NumberOfFreeLists = 3 + NumberOfFreeLists = 2, + NumberOfInUseLists = 3 }; static ChunkIndex next_chunk_index(ChunkIndex i) { - assert(i < NumberOfFreeLists, "Out of bound"); + assert(i < NumberOfInUseLists, "Out of bound"); return (ChunkIndex) (i+1); } @@ -100,164 +96,13 @@ // the Chunk after the header for the Chunk) where as Metachunks // point to space in a VirtualSpace. To replace Metachunks with // Chunks, change Chunks so that they can be allocated out of a VirtualSpace. -// - -// Metablock are the unit of allocation from a Chunk. It contains -// the size of the requested allocation in a debug build. -// Also in a debug build it has a marker before and after the -// body of the block. The address of the body is the address returned -// by the allocation. -// -// Layout in a debug build. In a product build only the body is present. -// -// +-----------+-----------+------------+ +-----------+ -// | word size | leader | body | ... | trailer | -// +-----------+-----------+------------+ +-----------+ -// -// A Metablock may be reused by its SpaceManager but are never moved between -// SpaceManagers. There is no explicit link to the Metachunk -// from which it was allocated. Metablock are not deallocated, rather -// the Metachunk it is a part of will be deallocated when it's -// associated class loader is collected. -// -// When the word size of a block is passed in to the deallocation -// call the word size no longer needs to be part of a Metablock. - -class Metablock { - friend class VMStructs; - private: - // Used to align the allocation (see below) and for debugging. -#ifdef ASSERT - struct { - size_t _word_size; - void* _leader; - } _header; - void* _data[1]; -#endif - static size_t _overhead; - +size_t Metablock::_min_block_byte_size = sizeof(Metablock); #ifdef ASSERT - void set_word_size(size_t v) { _header._word_size = v; } - void* leader() { return _header._leader; } - void* trailer() { - jlong index = (jlong) _header._word_size - sizeof(_header)/BytesPerWord - 1; - assert(index > 0, err_msg("Bad indexling of trailer %d", index)); - void** ptr = &_data[index]; - return *ptr; - } - void set_leader(void* v) { _header._leader = v; } - void set_trailer(void* v) { - void** ptr = &_data[_header._word_size - sizeof(_header)/BytesPerWord - 1]; - *ptr = v; - } - public: - size_t word_size() { return _header._word_size; } -#endif - public: - - static Metablock* initialize(MetaWord* p, size_t word_size); - - // This places the body of the block at a 2 word boundary - // because every block starts on a 2 word boundary. Work out - // how to make the body on a 2 word boundary if the block - // starts on a arbitrary boundary. JJJ - -#ifdef ASSERT - MetaWord* data() { return (MetaWord*) &_data[0]; } + size_t Metablock::_overhead = + Chunk::aligned_overhead_size(sizeof(Metablock)) / BytesPerWord; #else - MetaWord* data() { return (MetaWord*) this; } -#endif - static Metablock* metablock_from_data(MetaWord* p) { -#ifdef ASSERT - size_t word_offset = offset_of(Metablock, _data)/BytesPerWord; - Metablock* result = (Metablock*) (p - word_offset); - return result; -#else - return (Metablock*) p; + size_t Metablock::_overhead = 0; #endif - } - - static size_t overhead() { return _overhead; } - void verify(); -}; - -// Metachunk - Quantum of allocation from a Virtualspace -// Metachunks are reused (when freed are put on a global freelist) and -// have no permanent association to a SpaceManager. - -// +--------------+ <- end -// | | --+ ---+ -// | | | free | -// | | | | -// | | | | capacity -// | | | | -// | | <- top --+ | -// | | ---+ | -// | | | used | -// | | | | -// | | | | -// +--------------+ <- bottom ---+ ---+ - -class Metachunk VALUE_OBJ_CLASS_SPEC { - // link to support lists of chunks - Metachunk* _next; - - MetaWord* _bottom; - MetaWord* _end; - MetaWord* _top; - size_t _word_size; - - // Metachunks are allocated out of a MetadataVirtualSpace and - // and use some of its space to describe itself (plus alignment - // considerations). Metadata is allocated in the rest of the chunk. - // This size is the overhead of maintaining the Metachunk within - // the space. - static size_t _overhead; - - void set_bottom(MetaWord* v) { _bottom = v; } - void set_end(MetaWord* v) { _end = v; } - void set_top(MetaWord* v) { _top = v; } - void set_word_size(size_t v) { _word_size = v; } - public: - - // Used to add a Metachunk to a list of Metachunks - void set_next(Metachunk* v) { _next = v; assert(v != this, "Boom");} - - Metablock* allocate(size_t word_size); - static Metachunk* initialize(MetaWord* ptr, size_t word_size); - - // Accessors - Metachunk* next() const { return _next; } - MetaWord* bottom() const { return _bottom; } - MetaWord* end() const { return _end; } - MetaWord* top() const { return _top; } - size_t word_size() const { return _word_size; } - static size_t overhead() { return _overhead; } - - // Reset top to bottom so chunk can be reused. - void reset_empty() { _top = (_bottom + _overhead); } - bool is_empty() { return _top == (_bottom + _overhead); } - - // used (has been allocated) - // free (available for future allocations) - // capacity (total size of chunk) - size_t used_word_size(); - size_t free_word_size(); - size_t capacity_word_size(); - -#ifdef ASSERT - void mangle() { - // Mangle the payload of the chunk and not the links that - // maintain list of chunks. - HeapWord* start = (HeapWord*)(bottom() + overhead()); - size_t word_size = capacity_word_size() - overhead(); - Copy::fill_to_words(start, word_size, metadata_chunk_initialize); - } -#endif // ASSERT - - void print_on(outputStream* st) const; - void verify(); -}; // Pointer to list of Metachunks. @@ -292,7 +137,10 @@ // SmallChunk // MediumChunk // HumongousChunk - ChunkList _free_chunks[3]; + ChunkList _free_chunks[NumberOfFreeLists]; + + // HumongousChunk + ChunkTreeDictionary _humongous_dictionary; // ChunkManager in all lists of this type size_t _free_chunks_total; @@ -337,7 +185,9 @@ } ChunkList* free_medium_chunks() { return &_free_chunks[1]; } ChunkList* free_small_chunks() { return &_free_chunks[0]; } - ChunkList* free_humongous_chunks() { return &_free_chunks[2]; } + ChunkTreeDictionary* humongous_dictionary() { + return &_humongous_dictionary; + } ChunkList* free_chunks(ChunkIndex index); @@ -356,41 +206,35 @@ void locked_print_free_chunks(outputStream* st); void locked_print_sum_free_chunks(outputStream* st); + + void print_on(outputStream* st); }; // Used to manage the free list of Metablocks (a block corresponds // to the allocation of a quantum of metadata). class BlockFreelist VALUE_OBJ_CLASS_SPEC { -#ifdef DEALLOCATE_BLOCKS - BinaryTreeDictionary* _dictionary; -#endif - static Metablock* initialize_free_chunk(Metablock* block, size_t word_size); - -#ifdef DEALLOCATE_BLOCKS + BlockTreeDictionary* _dictionary; + static Metablock* initialize_free_chunk(MetaWord* p, size_t word_size); + // Accessors - BinaryTreeDictionary* dictionary() const { return _dictionary; } -#endif + BlockTreeDictionary* dictionary() const { return _dictionary; } public: BlockFreelist(); ~BlockFreelist(); // Get and return a block to the free list - Metablock* get_block(size_t word_size); - void return_block(Metablock* block, size_t word_size); - - size_t totalSize() { -#ifdef DEALLOCATE_BLOCKS - if (dictionary() == NULL) { - return 0; - } else { - return dictionary()->totalSize(); - } -#else + MetaWord* get_block(size_t word_size); + void return_block(MetaWord* p, size_t word_size); + + size_t total_size() { + if (dictionary() == NULL) { return 0; -#endif + } else { + return dictionary()->total_size(); } +} void print_on(outputStream* st) const; }; @@ -600,7 +444,6 @@ }; }; - class Metadebug : AllStatic { // Debugging support for Metaspaces static int _deallocate_block_a_lot_count; @@ -655,7 +498,7 @@ // List of chunks in use by this SpaceManager. Allocations // are done from the current chunk. The list is used for deallocating // chunks when the SpaceManager is freed. - Metachunk* _chunks_in_use[NumberOfFreeLists]; + Metachunk* _chunks_in_use[NumberOfInUseLists]; Metachunk* _current_chunk; // Virtual space where allocation comes from. @@ -700,24 +543,6 @@ // Add chunk to the list of chunks in use void add_chunk(Metachunk* v, bool make_current); - // Debugging support - void verify_chunks_in_use_index(ChunkIndex index, Metachunk* v) { - switch (index) { - case 0: - assert(v->word_size() == SmallChunk, "Not a SmallChunk"); - break; - case 1: - assert(v->word_size() == MediumChunk, "Not a MediumChunk"); - break; - case 2: - assert(v->word_size() > MediumChunk, "Not a HumongousChunk"); - break; - default: - assert(false, "Wrong list."); - } - } - - protected: Mutex* lock() const { return _lock; } public: @@ -751,10 +576,10 @@ MetaWord* allocate(size_t word_size); // Helper for allocations - Metablock* allocate_work(size_t word_size); + MetaWord* allocate_work(size_t word_size); // Returns a block to the per manager freelist - void deallocate(MetaWord* p); + void deallocate(MetaWord* p, size_t word_size); // Based on the allocation size and a minimum chunk size, // returned chunk size (for expanding space for chunk allocation). @@ -763,7 +588,7 @@ // Called when an allocation from the current chunk fails. // Gets a new chunk (may require getting a new virtual space), // and allocates from that chunk. - Metablock* grow_and_allocate(size_t word_size); + MetaWord* grow_and_allocate(size_t word_size); // debugging support. @@ -780,6 +605,8 @@ uint const SpaceManager::_small_chunk_limit = 4; + + const char* SpaceManager::_expand_lock_name = "SpaceManager chunk allocation lock"; const int SpaceManager::_expand_lock_rank = Monitor::leaf - 1; @@ -788,39 +615,26 @@ SpaceManager::_expand_lock_name, Mutex::_allow_vm_block_flag); -#ifdef ASSERT -size_t Metablock::_overhead = - Chunk::aligned_overhead_size(sizeof(Metablock)) / BytesPerWord; -#else -size_t Metablock::_overhead = 0; -#endif size_t Metachunk::_overhead = Chunk::aligned_overhead_size(sizeof(Metachunk)) / BytesPerWord; // New blocks returned by the Metaspace are zero initialized. // We should fix the constructors to not assume this instead. Metablock* Metablock::initialize(MetaWord* p, size_t word_size) { + if (p == NULL) { + return NULL; + } + Metablock* result = (Metablock*) p; // Clear the memory Copy::fill_to_aligned_words((HeapWord*)result, word_size); #ifdef ASSERT result->set_word_size(word_size); - // Check after work size is set. - result->set_leader((void*) metaspace_allocation_leader); - result->set_trailer((void*) metaspace_allocation_trailer); #endif return result; } -void Metablock::verify() { -#ifdef ASSERT - assert(leader() == metaspace_allocation_leader && - trailer() == metaspace_allocation_trailer, - "block has been corrupted"); -#endif -} - // Metachunk methods Metachunk* Metachunk::initialize(MetaWord* ptr, size_t word_size) { @@ -843,18 +657,13 @@ } -Metablock* Metachunk::allocate(size_t word_size) { - Metablock* result = NULL; +MetaWord* Metachunk::allocate(size_t word_size) { + MetaWord* result = NULL; // If available, bump the pointer to allocate. if (free_word_size() >= word_size) { - result = Metablock::initialize(_top, word_size); + result = _top; _top = _top + word_size; } -#ifdef ASSERT - assert(result == NULL || - result->word_size() == word_size, - "Block size is not set correctly"); -#endif return result; } @@ -878,103 +687,85 @@ bottom(), top(), end(), word_size()); } +#ifdef ASSERT +void Metachunk::mangle() { + // Mangle the payload of the chunk and not the links that + // maintain list of chunks. + HeapWord* start = (HeapWord*)(bottom() + overhead()); + size_t word_size = capacity_word_size() - overhead(); + Copy::fill_to_words(start, word_size, metadata_chunk_initialize); +} +#endif // ASSERT void Metachunk::verify() { #ifdef ASSERT // Cannot walk through the blocks unless the blocks have // headers with sizes. - MetaWord* curr = bottom() + overhead(); - while (curr < top()) { - Metablock* block = (Metablock*) curr; - size_t word_size = block->word_size(); - block->verify(); - curr = curr + word_size; - } + assert(_bottom <= _top && + _top <= _end, + "Chunk has been smashed"); + assert(SpaceManager::is_humongous(_word_size) || + _word_size == SpaceManager::MediumChunk || + _word_size == SpaceManager::SmallChunk, + "Chunk size is wrong"); #endif return; } // BlockFreelist methods -#ifdef DEALLOCATE_BLOCKS BlockFreelist::BlockFreelist() : _dictionary(NULL) {} -#else -BlockFreelist::BlockFreelist() {} -#endif BlockFreelist::~BlockFreelist() { -#ifdef DEALLOCATE_BLOCKS if (_dictionary != NULL) { if (Verbose && TraceMetadataChunkAllocation) { _dictionary->print_free_lists(gclog_or_tty); } delete _dictionary; } -#endif } -Metablock* BlockFreelist::initialize_free_chunk(Metablock* block, size_t word_size) { -#ifdef DEALLOCATE_BLOCKS -#ifdef ASSERT - assert(word_size = block->word_size(), "Wrong chunk size"); -#endif - Metablock* result = block; - result->setSize(word_size); - result->linkPrev(NULL); - result->linkNext(NULL); - - return result; -#else - ShouldNotReachHere(); +Metablock* BlockFreelist::initialize_free_chunk(MetaWord* p, size_t word_size) { + Metablock* block = (Metablock*) p; + block->set_word_size(word_size); + block->set_prev(NULL); + block->set_next(NULL); + return block; -#endif } -void BlockFreelist::return_block(Metablock* block, size_t word_size) { -#ifdef ASSERT - assert(word_size = block->word_size(), "Block size is wrong");; -#endif - Metablock* free_chunk = initialize_free_chunk(block, word_size); -#ifdef DEALLOCATE_BLOCKS +void BlockFreelist::return_block(MetaWord* p, size_t word_size) { + Metablock* free_chunk = initialize_free_chunk(p, word_size); if (dictionary() == NULL) { - _dictionary = new BinaryTreeDictionary(false /* adaptive_freelists */); + _dictionary = new BlockTreeDictionary(); } - dictionary()->returnChunk(free_chunk); -#endif + dictionary()->return_chunk(free_chunk); } -Metablock* BlockFreelist::get_block(size_t word_size) { -#ifdef DEALLOCATE_BLOCKS +MetaWord* BlockFreelist::get_block(size_t word_size) { if (dictionary() == NULL) { return NULL; } - Metablock* free_chunk = - dictionary()->getChunk(word_size, FreeBlockDictionary::exactly); -#else - Metablock* free_chunk = NULL; -#endif - if (free_chunk == NULL) { + if (word_size < TreeChunk::min_size()) { + // Dark matter. Too small for dictionary. return NULL; } - assert(free_chunk->word_size() == word_size, "Size of chunk is incorrect"); - Metablock* block = Metablock::initialize((MetaWord*) free_chunk, word_size); -#ifdef ASSERT - assert(block->word_size() == word_size, "Block size is not set correctly"); -#endif - - return block; + + Metablock* free_block = + dictionary()->get_chunk(word_size, FreeBlockDictionary::exactly); + if (free_block == NULL) { + return NULL; + } + + return (MetaWord*) free_block; } void BlockFreelist::print_on(outputStream* st) const { -#ifdef DEALLOCATE_BLOCKS if (dictionary() == NULL) { return; } dictionary()->print_free_lists(st); -#else - return; -#endif } // VirtualSpaceNode methods @@ -1597,14 +1388,11 @@ Metadebug::deallocate_block_a_lot_count() % MetaDataDeallocateALotInterval == 0 ) { Metadebug::set_deallocate_block_a_lot_count(0); for (uint i = 0; i < metadata_deallocate_a_lot_block; i++) { - Metablock* dummy_block = sm->allocate_work(raw_word_size); + MetaWord* dummy_block = sm->allocate_work(raw_word_size); if (dummy_block == 0) { break; } -#ifdef ASSERT - assert(dummy_block->word_size() == raw_word_size, "Block size is not set correctly"); -#endif - sm->deallocate(dummy_block->data()); + sm->deallocate(dummy_block, raw_word_size); } } else { Metadebug::inc_deallocate_block_a_lot_count(); @@ -1784,8 +1572,8 @@ } void ChunkManager::locked_verify() { + locked_verify_free_chunks_count(); locked_verify_free_chunks_total(); - locked_verify_free_chunks_count(); } void ChunkManager::locked_print_free_chunks(outputStream* st) { @@ -1803,7 +1591,6 @@ return &_free_chunks[index]; } - // These methods that sum the free chunk lists are used in printing // methods that are used in product builds. size_t ChunkManager::sum_free_chunks() { @@ -1818,6 +1605,7 @@ result = result + list->sum_list_capacity(); } + result = result + humongous_dictionary()->total_size(); return result; } @@ -1831,6 +1619,7 @@ } count = count + list->sum_list_count(); } + count = count + humongous_dictionary()->total_free_blocks(); return count; } @@ -1875,23 +1664,24 @@ assert_lock_strong(SpaceManager::expand_lock()); locked_verify(); - ChunkList* free_list = find_free_chunks_list(word_size); - assert(free_list != NULL, "Sanity check"); - - Metachunk* chunk = free_list->head(); - debug_only(Metachunk* debug_head = chunk;) - - if (chunk == NULL) { - return NULL; - } - - Metachunk* prev_chunk = chunk; - if (chunk->word_size() == word_size) { - // Chunk is being removed from the chunks free list. - dec_free_chunks_total(chunk->capacity_word_size()); + + Metachunk* chunk = NULL; + if (!SpaceManager::is_humongous(word_size)) { + ChunkList* free_list = find_free_chunks_list(word_size); + assert(free_list != NULL, "Sanity check"); + + chunk = free_list->head(); + debug_only(Metachunk* debug_head = chunk;) + + if (chunk == NULL) { + return NULL; + } + // Remove the chunk as the head of the list. free_list->set_head(chunk->next()); chunk->set_next(NULL); + // Chunk has been removed from the chunks free list. + dec_free_chunks_total(chunk->capacity_word_size()); if (TraceMetadataChunkAllocation && Verbose) { tty->print_cr("ChunkManager::free_chunks_get: free_list " @@ -1899,79 +1689,24 @@ free_list, chunk, chunk->word_size()); } } else { - assert(SpaceManager::is_humongous(word_size), - "Should only need to check humongous"); - // This code to find the best fit is just for purposes of - // investigating the loss due to fragmentation on a humongous - // chunk. It will be replace by a binaryTreeDictionary for - // the humongous chunks. - uint count = 0; - Metachunk* best_fit = NULL; - Metachunk* best_fit_prev = NULL; - while (chunk != NULL) { - count++; - if (chunk->word_size() < word_size) { - prev_chunk = chunk; - chunk = chunk->next(); - } else if (chunk->word_size() == word_size) { - break; - } else { - if (best_fit == NULL || - best_fit->word_size() > chunk->word_size()) { - best_fit_prev = prev_chunk; - best_fit = chunk; - } - prev_chunk = chunk; - chunk = chunk->next(); - } - } - if (chunk == NULL) { - prev_chunk = best_fit_prev; - chunk = best_fit; + chunk = humongous_dictionary()->get_chunk( + word_size, + FreeBlockDictionary::atLeast); + + if (chunk != NULL) { + if (TraceMetadataHumongousAllocation) { + size_t waste = chunk->word_size() - word_size; + tty->print_cr("Free list allocate humongous chunk size " SIZE_FORMAT + " for requested size " SIZE_FORMAT + " waste " SIZE_FORMAT, + chunk->word_size(), word_size, waste); } - if (chunk != NULL) { - if (TraceMetadataHumongousAllocation) { - size_t waste = chunk->word_size() - word_size; - tty->print_cr("Free list allocate humongous chunk size " SIZE_FORMAT - " for requested size " SIZE_FORMAT - " waste " SIZE_FORMAT - " found at " SIZE_FORMAT " of " SIZE_FORMAT, - chunk->word_size(), word_size, waste, - count, free_list->sum_list_count()); - } - // Chunk is being removed from the chunks free list. - dec_free_chunks_total(chunk->capacity_word_size()); - // Remove the chunk if it is at the head of the list. - if (chunk == free_list->head()) { - free_list->set_head(chunk->next()); - - if (TraceMetadataHumongousAllocation) { - tty->print_cr("ChunkManager::free_chunks_get: humongous free_list " - PTR_FORMAT " chunk " PTR_FORMAT " size " SIZE_FORMAT - " new head " PTR_FORMAT, - free_list, chunk, chunk->word_size(), - free_list->head()); - } - } else { - // Remove a chunk in the interior of the list - prev_chunk->set_next(chunk->next()); - - if (TraceMetadataHumongousAllocation) { - tty->print_cr("ChunkManager::free_chunks_get: humongous free_list " - PTR_FORMAT " chunk " PTR_FORMAT " size " SIZE_FORMAT - PTR_FORMAT " prev " PTR_FORMAT " next " PTR_FORMAT, - free_list, chunk, chunk->word_size(), - prev_chunk, chunk->next()); - } - } - chunk->set_next(NULL); - } else { - if (TraceMetadataHumongousAllocation) { - tty->print_cr("ChunkManager::free_chunks_get: New humongous chunk of size " - SIZE_FORMAT, - word_size); - } - } + // Chunk is being removed from the chunks free list. + dec_free_chunks_total(chunk->capacity_word_size()); +#ifdef ASSERT + chunk->set_is_free(false); +#endif + } } locked_verify(); return chunk; @@ -2000,12 +1735,18 @@ return chunk; } +void ChunkManager::print_on(outputStream* out) { + if (PrintFLSStatistics != 0) { + humongous_dictionary()->report_statistics(); + } +} + // SpaceManager methods size_t SpaceManager::sum_free_in_chunks_in_use() const { MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); size_t free = 0; - for (ChunkIndex i = SmallIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) { + for (ChunkIndex i = SmallIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { Metachunk* chunk = chunks_in_use(i); while (chunk != NULL) { free += chunk->free_word_size(); @@ -2018,11 +1759,12 @@ size_t SpaceManager::sum_waste_in_chunks_in_use() const { MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); size_t result = 0; - for (ChunkIndex i = SmallIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) { - // Count the free space in all the chunk but not the - // current chunk from which allocations are still being done. + for (ChunkIndex i = SmallIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { + + result += sum_waste_in_chunks_in_use(i); } + return result; } @@ -2033,10 +1775,10 @@ // Count the free space in all the chunk but not the // current chunk from which allocations are still being done. if (chunk != NULL) { - while (chunk != NULL) { - if (chunk != current_chunk()) { - result += chunk->free_word_size(); - } + Metachunk* prev = chunk; + while (chunk != NULL && chunk != current_chunk()) { + result += chunk->free_word_size(); + prev = chunk; chunk = chunk->next(); count++; } @@ -2047,7 +1789,7 @@ size_t SpaceManager::sum_capacity_in_chunks_in_use() const { MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); size_t sum = 0; - for (ChunkIndex i = SmallIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) { + for (ChunkIndex i = SmallIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { Metachunk* chunk = chunks_in_use(i); while (chunk != NULL) { // Just changed this sum += chunk->capacity_word_size(); @@ -2061,9 +1803,10 @@ size_t SpaceManager::sum_count_in_chunks_in_use() { size_t count = 0; - for (ChunkIndex i = SmallIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) { + for (ChunkIndex i = SmallIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { count = count + sum_count_in_chunks_in_use(i); } + return count; } @@ -2081,7 +1824,7 @@ size_t SpaceManager::sum_used_in_chunks_in_use() const { MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); size_t used = 0; - for (ChunkIndex i = SmallIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) { + for (ChunkIndex i = SmallIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { Metachunk* chunk = chunks_in_use(i); while (chunk != NULL) { used += chunk->used_word_size(); @@ -2139,15 +1882,13 @@ gclog_or_tty->print_cr(" word_size " PTR_FORMAT, word_size); gclog_or_tty->print_cr(" chunk_word_size " PTR_FORMAT, chunk_word_size); - gclog_or_tty->print_cr(" block overhead " PTR_FORMAT - " chunk overhead " PTR_FORMAT, - Metablock::overhead(), + gclog_or_tty->print_cr(" chunk overhead " PTR_FORMAT, Metachunk::overhead()); } return chunk_word_size; } -Metablock* SpaceManager::grow_and_allocate(size_t word_size) { +MetaWord* SpaceManager::grow_and_allocate(size_t word_size) { assert(vs_list()->current_virtual_space() != NULL, "Should have been set"); assert(current_chunk() == NULL || @@ -2180,7 +1921,7 @@ void SpaceManager::print_on(outputStream* st) const { for (ChunkIndex i = SmallIndex; - i < NumberOfFreeLists ; + i < NumberOfInUseLists ; i = next_chunk_index(i) ) { st->print_cr(" chunks_in_use " PTR_FORMAT " chunk size " PTR_FORMAT, chunks_in_use(i), @@ -2191,8 +1932,11 @@ sum_waste_in_chunks_in_use(SmallIndex), sum_waste_in_chunks_in_use(MediumIndex), sum_waste_in_chunks_in_use(HumongousIndex)); - // Nothing in them yet - // block_freelists()->print_on(st); + // block free lists + if (block_freelists() != NULL) { + st->print_cr("total in block free lists " SIZE_FORMAT, + block_freelists()->total_size()); + } } SpaceManager::SpaceManager(Mutex* lock, VirtualSpaceList* vs_list) : @@ -2200,7 +1944,7 @@ _allocation_total(0), _lock(lock) { Metadebug::init_allocation_fail_alot_count(); - for (ChunkIndex i = SmallIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) { + for (ChunkIndex i = SmallIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { _chunks_in_use[i] = NULL; } _current_chunk = NULL; @@ -2262,22 +2006,24 @@ // Humongous chunks are never the current chunk. Metachunk* humongous_chunks = chunks_in_use(HumongousIndex); - if (humongous_chunks != NULL) { - chunk_manager->free_humongous_chunks()->add_at_head(humongous_chunks); - set_chunks_in_use(HumongousIndex, NULL); + while (humongous_chunks != NULL) { +#ifdef ASSERT + humongous_chunks->set_is_free(true); +#endif + Metachunk* next_humongous_chunks = humongous_chunks->next(); + chunk_manager->humongous_dictionary()->return_chunk(humongous_chunks); + humongous_chunks = next_humongous_chunks; } + set_chunks_in_use(HumongousIndex, NULL); chunk_manager->locked_verify(); } -void SpaceManager::deallocate(MetaWord* p) { +void SpaceManager::deallocate(MetaWord* p, size_t word_size) { assert_lock_strong(_lock); - ShouldNotReachHere(); // Where is this needed. -#ifdef DEALLOCATE_BLOCKS - Metablock* block = Metablock::metablock_from_data(p); - // This is expense but kept it until integration JJJ - assert(contains((address)block), "Block does not belong to this metaspace"); - block_freelists()->return_block(block, word_size); -#endif + size_t min_size = TreeChunk::min_size(); + assert(word_size >= min_size, + err_msg("Should not deallocate dark matter " SIZE_FORMAT, word_size)); + block_freelists()->return_block(p, word_size); } // Adds a chunk to the list of chunks in use. @@ -2366,50 +2112,40 @@ MetaWord* SpaceManager::allocate(size_t word_size) { MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag); - size_t block_overhead = Metablock::overhead(); // If only the dictionary is going to be used (i.e., no // indexed free list), then there is a minimum size requirement. // MinChunkSize is a placeholder for the real minimum size JJJ - size_t byte_size_with_overhead = (word_size + block_overhead) * BytesPerWord; -#ifdef DEALLOCATE_BLOCKS - size_t raw_bytes_size = MAX2(ARENA_ALIGN(byte_size_with_overhead), - MinChunkSize * BytesPerWord); -#else - size_t raw_bytes_size = ARENA_ALIGN(byte_size_with_overhead); -#endif + size_t byte_size = word_size * BytesPerWord; + + size_t byte_size_with_overhead = byte_size + Metablock::overhead(); + + size_t raw_bytes_size = MAX2(byte_size_with_overhead, + Metablock::min_block_byte_size()); + raw_bytes_size = ARENA_ALIGN(raw_bytes_size); size_t raw_word_size = raw_bytes_size / BytesPerWord; assert(raw_word_size * BytesPerWord == raw_bytes_size, "Size problem"); BlockFreelist* fl = block_freelists(); - Metablock* block = NULL; + MetaWord* p = NULL; // Allocation from the dictionary is expensive in the sense that // the dictionary has to be searched for a size. Don't allocate // from the dictionary until it starts to get fat. Is this // a reasonable policy? Maybe an skinny dictionary is fast enough // for allocations. Do some profiling. JJJ - if (fl->totalSize() > allocation_from_dictionary_limit) { - block = fl->get_block(raw_word_size); + if (fl->total_size() > allocation_from_dictionary_limit) { + p = fl->get_block(raw_word_size); } - if (block == NULL) { - block = allocate_work(raw_word_size); - if (block == NULL) { - return NULL; - } + if (p == NULL) { + p = allocate_work(raw_word_size); } Metadebug::deallocate_block_a_lot(this, raw_word_size); - // Push the allocation past the word containing the size and leader. -#ifdef ASSERT - MetaWord* result = block->data(); - return result; -#else - return (MetaWord*) block; -#endif + return p; } // Returns the address of spaced allocated for "word_size". // This methods does not know about blocks (Metablocks) -Metablock* SpaceManager::allocate_work(size_t word_size) { +MetaWord* SpaceManager::allocate_work(size_t word_size) { assert_lock_strong(_lock); #ifdef ASSERT if (Metadebug::test_metadata_failure()) { @@ -2417,7 +2153,7 @@ } #endif // Is there space in the current chunk? - Metablock* result = NULL; + MetaWord* result = NULL; // For DumpSharedSpaces, only allocate out of the current chunk which is // never null because we gave it the size we wanted. Caller reports out @@ -2436,8 +2172,8 @@ } if (result > 0) { inc_allocation_total(word_size); - assert(result != (Metablock*) chunks_in_use(MediumIndex), "Head of the list is being allocated"); - assert(result->word_size() == word_size, "Size not set correctly"); + assert(result != (MetaWord*) chunks_in_use(MediumIndex), + "Head of the list is being allocated"); } return result; @@ -2447,13 +2183,13 @@ // If there are blocks in the dictionary, then // verfication of chunks does not work since // being in the dictionary alters a chunk. - if (block_freelists()->totalSize() == 0) { + if (block_freelists()->total_size() == 0) { // Skip the small chunks because their next link points to // medium chunks. This is because the small chunk is the // current chunk (for allocations) until it is full and the // the addition of the next chunk does not NULL the next // like of the small chunk. - for (ChunkIndex i = MediumIndex; i < NumberOfFreeLists; i = next_chunk_index(i)) { + for (ChunkIndex i = MediumIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) { Metachunk* curr = chunks_in_use(i); while (curr != NULL) { curr->verify(); @@ -2492,7 +2228,7 @@ // Add up statistics for all chunks in this SpaceManager. for (ChunkIndex index = SmallIndex; - index < NumberOfFreeLists; + index < NumberOfInUseLists; index = next_chunk_index(index)) { for (Metachunk* curr = chunks_in_use(index); curr != NULL; @@ -2521,7 +2257,7 @@ #ifdef ASSERT void SpaceManager::mangle_freed_chunks() { for (ChunkIndex index = SmallIndex; - index < NumberOfFreeLists; + index < NumberOfInUseLists; index = next_chunk_index(index)) { for (Metachunk* curr = chunks_in_use(index); curr != NULL; @@ -2833,13 +2569,12 @@ } } - MetaWord* Metaspace::allocate(size_t word_size, MetadataType mdtype) { // DumpSharedSpaces doesn't use class metadata area (yet) if (mdtype == ClassType && !DumpSharedSpaces) { - return class_vsm()->allocate(word_size); + return class_vsm()->allocate(word_size); } else { - return vsm()->allocate(word_size); + return vsm()->allocate(word_size); } } @@ -2853,6 +2588,7 @@ gclog_or_tty->print_cr("Increase capacity to GC from " SIZE_FORMAT " to " SIZE_FORMAT, before_inc, MetaspaceGC::capacity_until_GC()); } + result = allocate(word_size, mdtype); return result; @@ -2889,37 +2625,39 @@ void Metaspace::deallocate(MetaWord* ptr, size_t word_size, bool is_class) { if (SafepointSynchronize::is_at_safepoint()) { assert(Thread::current()->is_VM_thread(), "should be the VM thread"); - // Don't take lock -#ifdef DEALLOCATE_BLOCKS - if (is_class) { - class_vsm()->deallocate(ptr); - } else { - vsm()->deallocate(ptr); + // Don't take Heap_lock + MutexLocker ml(vsm()->lock()); + if (word_size < TreeChunk::min_size()) { + // Dark matter. Too small for dictionary. +#ifdef ASSERT + Copy::fill_to_words((HeapWord*)ptr, word_size, 0xf5f5f5f5); +#endif + return; } -#else -#ifdef ASSERT - Copy::fill_to_words((HeapWord*)ptr, word_size, metadata_deallocate); -#endif -#endif - + if (is_class) { + class_vsm()->deallocate(ptr, word_size); + } else { + vsm()->deallocate(ptr, word_size); + } } else { MutexLocker ml(vsm()->lock()); -#ifdef DEALLOCATE_BLOCKS - if (is_class) { - class_vsm()->deallocate(ptr); - } else { - vsm()->deallocate(ptr); + if (word_size < TreeChunk::min_size()) { + // Dark matter. Too small for dictionary. +#ifdef ASSERT + Copy::fill_to_words((HeapWord*)ptr, word_size, 0xf5f5f5f5); +#endif + return; } -#else -#ifdef ASSERT - Copy::fill_to_words((HeapWord*)ptr, word_size, metadata_deallocate); -#endif -#endif + if (is_class) { + class_vsm()->deallocate(ptr, word_size); + } else { + vsm()->deallocate(ptr, word_size); + } } } -MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size, +Metablock* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size, bool read_only, MetadataType mdtype, TRAPS) { if (HAS_PENDING_EXCEPTION) { assert(false, "Should not allocate with exception pending"); @@ -2943,7 +2681,7 @@ if (result == NULL) { report_out_of_shared_space(read_only ? SharedReadOnly : SharedReadWrite); } - return result; + return Metablock::initialize(result, word_size); } result = loader_data->metaspace_non_null()->allocate(word_size, mdtype); @@ -2951,7 +2689,7 @@ if (result == NULL) { // Try to clean out some memory and retry. result = - Universe::heap()->collector_policy()->satisfy_failed_metadata_allocation( + Universe::heap()->collector_policy()->satisfy_failed_metadata_allocation( loader_data, word_size, mdtype); // If result is still null, we are out of memory. @@ -2967,7 +2705,7 @@ THROW_OOP_0(Universe::out_of_memory_error_perm_gen()); } } - return result; + return Metablock::initialize(result, word_size); } void Metaspace::print_on(outputStream* out) const { diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/memory/metaspace.hpp --- a/src/share/vm/memory/metaspace.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/memory/metaspace.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -57,12 +57,10 @@ // class ClassLoaderData; +class Metablock; class MetaWord; class Mutex; class outputStream; -class FreeChunk; -template class FreeList; -template class BinaryTreeDictionary; class SpaceManager; // Metaspaces each have a SpaceManager and allocations @@ -128,7 +126,7 @@ size_t capacity_words(MetadataType mdtype) const; size_t waste_words(MetadataType mdtype) const; - static MetaWord* allocate(ClassLoaderData* loader_data, size_t size, + static Metablock* allocate(ClassLoaderData* loader_data, size_t size, bool read_only, MetadataType mdtype, TRAPS); void deallocate(MetaWord* ptr, size_t byte_size, bool is_class); diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/oops/constMethod.cpp --- a/src/share/vm/oops/constMethod.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/oops/constMethod.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -34,29 +34,30 @@ const u2 ConstMethod::UNSET_IDNUM = 0xFFFF; ConstMethod* ConstMethod::allocate(ClassLoaderData* loader_data, - int byte_code_size, - int compressed_line_number_size, - int localvariable_table_length, - int exception_table_length, - int checked_exceptions_length, - TRAPS) { + int byte_code_size, + int compressed_line_number_size, + int localvariable_table_length, + int exception_table_length, + int checked_exceptions_length, + MethodType method_type, + TRAPS) { int size = ConstMethod::size(byte_code_size, compressed_line_number_size, localvariable_table_length, exception_table_length, checked_exceptions_length); return new (loader_data, size, true, THREAD) ConstMethod( - byte_code_size, compressed_line_number_size, - localvariable_table_length, exception_table_length, - checked_exceptions_length, size); + byte_code_size, compressed_line_number_size, localvariable_table_length, + exception_table_length, checked_exceptions_length, method_type, size); } ConstMethod::ConstMethod(int byte_code_size, - int compressed_line_number_size, - int localvariable_table_length, - int exception_table_length, - int checked_exceptions_length, - int size) { + int compressed_line_number_size, + int localvariable_table_length, + int exception_table_length, + int checked_exceptions_length, + MethodType method_type, + int size) { No_Safepoint_Verifier no_safepoint; set_interpreter_kind(Interpreter::invalid); @@ -69,6 +70,7 @@ compressed_line_number_size, localvariable_table_length, exception_table_length); + set_method_type(method_type); assert(this->size() == size, "wrong size for object"); } diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/oops/constMethod.hpp --- a/src/share/vm/oops/constMethod.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/oops/constMethod.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -108,12 +108,17 @@ class ConstMethod : public MetaspaceObj { friend class VMStructs; + +public: + typedef enum { NORMAL, OVERPASS } MethodType; + private: enum { _has_linenumber_table = 1, _has_checked_exceptions = 2, _has_localvariable_table = 4, - _has_exception_table = 8 + _has_exception_table = 8, + _is_overpass = 16 }; // Bit vector of signature @@ -145,19 +150,22 @@ // Constructor ConstMethod(int byte_code_size, - int compressed_line_number_size, - int localvariable_table_length, - int exception_table_length, - int checked_exceptions_length, - int size); + int compressed_line_number_size, + int localvariable_table_length, + int exception_table_length, + int checked_exceptions_length, + MethodType is_overpass, + int size); public: + static ConstMethod* allocate(ClassLoaderData* loader_data, - int byte_code_size, - int compressed_line_number_size, - int localvariable_table_length, - int exception_table_length, - int checked_exceptions_length, - TRAPS); + int byte_code_size, + int compressed_line_number_size, + int localvariable_table_length, + int exception_table_length, + int checked_exceptions_length, + MethodType mt, + TRAPS); bool is_constMethod() const { return true; } @@ -179,6 +187,19 @@ bool has_exception_handler() const { return (_flags & _has_exception_table) != 0; } + MethodType method_type() const { + return ((_flags & _is_overpass) == 0) ? NORMAL : OVERPASS; + } + + void set_method_type(MethodType mt) { + if (mt == NORMAL) { + _flags &= ~(_is_overpass); + } else { + _flags |= _is_overpass; + } + } + + void set_interpreter_kind(int kind) { _interpreter_kind = kind; } int interpreter_kind(void) const { return _interpreter_kind; } diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/oops/constantPool.cpp --- a/src/share/vm/oops/constantPool.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/oops/constantPool.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -1143,16 +1143,21 @@ int from_oplen = operand_array_length(from_cp->operands()); int old_oplen = operand_array_length(to_cp->operands()); if (from_oplen != 0) { + ClassLoaderData* loader_data = to_cp->pool_holder()->class_loader_data(); // append my operands to the target's operands array if (old_oplen == 0) { - to_cp->set_operands(from_cp->operands()); // reuse; do not merge + // Can't just reuse from_cp's operand list because of deallocation issues + int len = from_cp->operands()->length(); + Array* new_ops = MetadataFactory::new_array(loader_data, len, CHECK); + Copy::conjoint_memory_atomic( + from_cp->operands()->adr_at(0), new_ops->adr_at(0), len * sizeof(u2)); + to_cp->set_operands(new_ops); } else { int old_len = to_cp->operands()->length(); int from_len = from_cp->operands()->length(); int old_off = old_oplen * sizeof(u2); int from_off = from_oplen * sizeof(u2); // Use the metaspace for the destination constant pool - ClassLoaderData* loader_data = to_cp->pool_holder()->class_loader_data(); Array* new_operands = MetadataFactory::new_array(loader_data, old_len + from_len, CHECK); int fillp = 0, len = 0; // first part of dest diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/oops/instanceKlass.cpp --- a/src/share/vm/oops/instanceKlass.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/oops/instanceKlass.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -743,6 +743,35 @@ } } + if (this_oop->has_default_methods()) { + // Step 7.5: initialize any interfaces which have default methods + for (int i = 0; i < this_oop->local_interfaces()->length(); ++i) { + Klass* iface = this_oop->local_interfaces()->at(i); + InstanceKlass* ik = InstanceKlass::cast(iface); + if (ik->has_default_methods() && ik->should_be_initialized()) { + ik->initialize(THREAD); + + if (HAS_PENDING_EXCEPTION) { + Handle e(THREAD, PENDING_EXCEPTION); + CLEAR_PENDING_EXCEPTION; + { + EXCEPTION_MARK; + // Locks object, set state, and notify all waiting threads + this_oop->set_initialization_state_and_notify( + initialization_error, THREAD); + + // ignore any exception thrown, superclass initialization error is + // thrown below + CLEAR_PENDING_EXCEPTION; + } + DTRACE_CLASSINIT_PROBE_WAIT( + super__failed, InstanceKlass::cast(this_oop()), -1, wait); + THROW_OOP(e()); + } + } + } + } + // Step 8 { assert(THREAD->is_Java_thread(), "non-JavaThread in initialize_impl"); @@ -1252,11 +1281,7 @@ } #endif -Method* InstanceKlass::find_method(Symbol* name, Symbol* signature) const { - return InstanceKlass::find_method(methods(), name, signature); -} - -Method* InstanceKlass::find_method(Array* methods, Symbol* name, Symbol* signature) { +static int binary_search(Array* methods, Symbol* name) { int len = methods->length(); // methods are sorted, so do binary search int l = 0; @@ -1267,43 +1292,70 @@ assert(m->is_method(), "must be method"); int res = m->name()->fast_compare(name); if (res == 0) { - // found matching name; do linear search to find matching signature - // first, quick check for common case - if (m->signature() == signature) return m; - // search downwards through overloaded methods - int i; - for (i = mid - 1; i >= l; i--) { - Method* m = methods->at(i); - assert(m->is_method(), "must be method"); - if (m->name() != name) break; - if (m->signature() == signature) return m; - } - // search upwards - for (i = mid + 1; i <= h; i++) { - Method* m = methods->at(i); - assert(m->is_method(), "must be method"); - if (m->name() != name) break; - if (m->signature() == signature) return m; - } - // not found -#ifdef ASSERT - int index = linear_search(methods, name, signature); - assert(index == -1, err_msg("binary search should have found entry %d", index)); -#endif - return NULL; + return mid; } else if (res < 0) { l = mid + 1; } else { h = mid - 1; } } + return -1; +} + +Method* InstanceKlass::find_method(Symbol* name, Symbol* signature) const { + return InstanceKlass::find_method(methods(), name, signature); +} + +Method* InstanceKlass::find_method( + Array* methods, Symbol* name, Symbol* signature) { + int hit = binary_search(methods, name); + if (hit != -1) { + Method* m = methods->at(hit); + // Do linear search to find matching signature. First, quick check + // for common case + if (m->signature() == signature) return m; + // search downwards through overloaded methods + int i; + for (i = hit - 1; i >= 0; --i) { + Method* m = methods->at(i); + assert(m->is_method(), "must be method"); + if (m->name() != name) break; + if (m->signature() == signature) return m; + } + // search upwards + for (i = hit + 1; i < methods->length(); ++i) { + Method* m = methods->at(i); + assert(m->is_method(), "must be method"); + if (m->name() != name) break; + if (m->signature() == signature) return m; + } + // not found #ifdef ASSERT - int index = linear_search(methods, name, signature); - assert(index == -1, err_msg("binary search should have found entry %d", index)); + int index = linear_search(methods, name, signature); + assert(index == -1, err_msg("binary search should have found entry %d", index)); #endif + } return NULL; } +int InstanceKlass::find_method_by_name(Symbol* name, int* end) { + return find_method_by_name(methods(), name, end); +} + +int InstanceKlass::find_method_by_name( + Array* methods, Symbol* name, int* end_ptr) { + assert(end_ptr != NULL, "just checking"); + int start = binary_search(methods, name); + int end = start + 1; + if (start != -1) { + while (start - 1 >= 0 && (methods->at(start - 1))->name() == name) --start; + while (end < methods->length() && (methods->at(end))->name() == name) ++end; + *end_ptr = end; + return start; + } + return -1; +} + Method* InstanceKlass::uncached_lookup_method(Symbol* name, Symbol* signature) const { Klass* klass = const_cast(this); while (klass != NULL) { diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/oops/instanceKlass.hpp --- a/src/share/vm/oops/instanceKlass.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/oops/instanceKlass.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -245,6 +245,10 @@ unsigned char * _cached_class_file_bytes; // JVMTI: cached class file, before retransformable agent modified it in CFLH jint _cached_class_file_len; // JVMTI: length of above JvmtiCachedClassFieldMap* _jvmti_cached_class_field_map; // JVMTI: used during heap iteration + + // true if class, superclass, or implemented interfaces have default methods + bool _has_default_methods; + volatile u2 _idnum_allocated_count; // JNI/JVMTI: increments with the addition of methods, old ids don't change // Method array. Array* _methods; @@ -492,6 +496,13 @@ // (returns NULL if not found) Method* lookup_method_in_all_interfaces(Symbol* name, Symbol* signature) const; + // Find method indices by name. If a method with the specified name is + // found the index to the first method is returned, and 'end' is filled in + // with the index of first non-name-matching method. If no method is found + // -1 is returned. + int find_method_by_name(Symbol* name, int* end); + static int find_method_by_name(Array* methods, Symbol* name, int* end); + // constant pool ConstantPool* constants() const { return _constants; } void set_constants(ConstantPool* c) { _constants = c; } @@ -592,6 +603,9 @@ return _jvmti_cached_class_field_map; } + bool has_default_methods() const { return _has_default_methods; } + void set_has_default_methods(bool b) { _has_default_methods = b; } + // for adding methods, ConstMethod::UNSET_IDNUM means no more ids available inline u2 next_method_idnum(); void set_initial_method_idnum(u2 value) { _idnum_allocated_count = value; } @@ -728,7 +742,6 @@ GrowableArray* compute_secondary_supers(int num_extra_slots); bool compute_is_subtype_of(Klass* k); bool can_be_primary_super_slow() const; - Klass* java_super() const { return super(); } int oop_size(oop obj) const { return size_helper(); } bool oop_is_instance_slow() const { return true; } @@ -750,6 +763,10 @@ return (InstanceKlass*) k; } + InstanceKlass* java_super() const { + return (super() == NULL) ? NULL : cast(super()); + } + // Sizing (in words) static int header_size() { return align_object_offset(sizeof(InstanceKlass)/HeapWordSize); } static int size(int vtable_length, int itable_length, diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/oops/klassVtable.cpp --- a/src/share/vm/oops/klassVtable.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/oops/klassVtable.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -54,22 +54,16 @@ // the same name and signature as m), then m is a Miranda method which is // entered as a public abstract method in C's vtable. From then on it should // treated as any other public method in C for method over-ride purposes. -void klassVtable::compute_vtable_size_and_num_mirandas(int &vtable_length, - int &num_miranda_methods, - Klass* super, - Array* methods, - AccessFlags class_flags, - Handle classloader, - Symbol* classname, - Array* local_interfaces, - TRAPS - ) { - +void klassVtable::compute_vtable_size_and_num_mirandas( + int* vtable_length_ret, int* num_new_mirandas, + GrowableArray* all_mirandas, Klass* super, + Array* methods, AccessFlags class_flags, + Handle classloader, Symbol* classname, Array* local_interfaces, + TRAPS) { No_Safepoint_Verifier nsv; // set up default result values - vtable_length = 0; - num_miranda_methods = 0; + int vtable_length = 0; // start off with super's vtable length InstanceKlass* sk = (InstanceKlass*)super; @@ -86,9 +80,12 @@ } } + GrowableArray new_mirandas(20); // compute the number of mirandas methods that must be added to the end - num_miranda_methods = get_num_mirandas(super, methods, local_interfaces); - vtable_length += (num_miranda_methods * vtableEntry::size()); + get_mirandas(&new_mirandas, all_mirandas, super, methods, local_interfaces); + *num_new_mirandas = new_mirandas.length(); + + vtable_length += *num_new_mirandas * vtableEntry::size(); if (Universe::is_bootstrapping() && vtable_length == 0) { // array classes don't have their superclass set correctly during @@ -109,6 +106,8 @@ "bad vtable size for class Object"); assert(vtable_length % vtableEntry::size() == 0, "bad vtable length"); assert(vtable_length >= Universe::base_vtable_size(), "vtable too small"); + + *vtable_length_ret = vtable_length; } int klassVtable::index_of(Method* m, int len) const { @@ -191,7 +190,7 @@ } // add miranda methods; it will also update the value of initialized - fill_in_mirandas(initialized); + fill_in_mirandas(&initialized); // In class hierarchies where the accessibility is not increasing (i.e., going from private -> // package_private -> publicprotected), the vtable might actually be smaller than our initial @@ -249,6 +248,11 @@ return superk; } +// Methods that are "effectively" final don't need vtable entries. +bool method_is_effectively_final( + AccessFlags klass_flags, methodHandle target) { + return target->is_final() || klass_flags.is_final() && !target->is_overpass(); +} // Update child's copy of super vtable for overrides // OR return true if a new vtable entry is required @@ -269,7 +273,7 @@ return false; } - if (klass->is_final() || target_method()->is_final()) { + if (method_is_effectively_final(klass->access_flags(), target_method)) { // a final method never needs a new entry; final methods can be statically // resolved and they have to be present in the vtable only if they override // a super's method, in which case they re-use its entry @@ -406,7 +410,8 @@ Symbol* classname, AccessFlags class_flags, TRAPS) { - if ((class_flags.is_final() || target_method()->is_final()) || + + if (method_is_effectively_final(class_flags, target_method) || // a final method never needs a new entry; final methods can be statically // resolved and they have to be present in the vtable only if they override // a super's method, in which case they re-use its entry @@ -502,7 +507,7 @@ // miranda methods are interface methods in a class's vtable if (mhk->is_interface()) { - assert(m->is_public() && m->is_abstract(), "should be public and abstract"); + assert(m->is_public(), "should be public"); assert(ik()->implements_interface(method_holder) , "this class should implement the interface"); assert(is_miranda(m, ik()->methods(), ik()->super()), "should be a miranda_method"); return true; @@ -532,19 +537,19 @@ return false; } -void klassVtable::add_new_mirandas_to_list(GrowableArray* list_of_current_mirandas, - Array* current_interface_methods, - Array* class_methods, - Klass* super) { +void klassVtable::add_new_mirandas_to_lists( + GrowableArray* new_mirandas, GrowableArray* all_mirandas, + Array* current_interface_methods, Array* class_methods, + Klass* super) { // iterate thru the current interface's method to see if it a miranda int num_methods = current_interface_methods->length(); for (int i = 0; i < num_methods; i++) { Method* im = current_interface_methods->at(i); bool is_duplicate = false; - int num_of_current_mirandas = list_of_current_mirandas->length(); + int num_of_current_mirandas = new_mirandas->length(); // check for duplicate mirandas in different interfaces we implement for (int j = 0; j < num_of_current_mirandas; j++) { - Method* miranda = list_of_current_mirandas->at(j); + Method* miranda = new_mirandas->at(j); if ((im->name() == miranda->name()) && (im->signature() == miranda->signature())) { is_duplicate = true; @@ -557,51 +562,47 @@ InstanceKlass *sk = InstanceKlass::cast(super); // check if it is a duplicate of a super's miranda if (sk->lookup_method_in_all_interfaces(im->name(), im->signature()) == NULL) { - list_of_current_mirandas->append(im); + new_mirandas->append(im); + } + if (all_mirandas != NULL) { + all_mirandas->append(im); } } } } } -void klassVtable::get_mirandas(GrowableArray* mirandas, +void klassVtable::get_mirandas(GrowableArray* new_mirandas, + GrowableArray* all_mirandas, Klass* super, Array* class_methods, Array* local_interfaces) { - assert((mirandas->length() == 0) , "current mirandas must be 0"); + assert((new_mirandas->length() == 0) , "current mirandas must be 0"); // iterate thru the local interfaces looking for a miranda int num_local_ifs = local_interfaces->length(); for (int i = 0; i < num_local_ifs; i++) { InstanceKlass *ik = InstanceKlass::cast(local_interfaces->at(i)); - add_new_mirandas_to_list(mirandas, ik->methods(), class_methods, super); + add_new_mirandas_to_lists(new_mirandas, all_mirandas, + ik->methods(), class_methods, super); // iterate thru each local's super interfaces Array* super_ifs = ik->transitive_interfaces(); int num_super_ifs = super_ifs->length(); for (int j = 0; j < num_super_ifs; j++) { InstanceKlass *sik = InstanceKlass::cast(super_ifs->at(j)); - add_new_mirandas_to_list(mirandas, sik->methods(), class_methods, super); + add_new_mirandas_to_lists(new_mirandas, all_mirandas, + sik->methods(), class_methods, super); } } } -// get number of mirandas -int klassVtable::get_num_mirandas(Klass* super, Array* class_methods, Array* local_interfaces) { - ResourceMark rm; - GrowableArray* mirandas = new GrowableArray(20); - get_mirandas(mirandas, super, class_methods, local_interfaces); - return mirandas->length(); -} - // fill in mirandas -void klassVtable::fill_in_mirandas(int& initialized) { - ResourceMark rm; - GrowableArray* mirandas = new GrowableArray(20); - InstanceKlass *this_ik = ik(); - get_mirandas(mirandas, this_ik->super(), this_ik->methods(), this_ik->local_interfaces()); - int num_mirandas = mirandas->length(); - for (int i = 0; i < num_mirandas; i++) { - put_method_at(mirandas->at(i), initialized); - initialized++; +void klassVtable::fill_in_mirandas(int* initialized) { + GrowableArray mirandas(20); + get_mirandas(&mirandas, NULL, ik()->super(), ik()->methods(), + ik()->local_interfaces()); + for (int i = 0; i < mirandas.length(); i++) { + put_method_at(mirandas.at(i), *initialized); + ++(*initialized); } } diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/oops/klassVtable.hpp --- a/src/share/vm/oops/klassVtable.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/oops/klassVtable.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -84,11 +84,11 @@ bool is_initialized(); // computes vtable length (in words) and the number of miranda methods - static void compute_vtable_size_and_num_mirandas(int &vtable_length, int &num_miranda_methods, - Klass* super, Array* methods, - AccessFlags class_flags, Handle classloader, - Symbol* classname, Array* local_interfaces, - TRAPS); + static void compute_vtable_size_and_num_mirandas( + int* vtable_length, int* num_new_mirandas, + GrowableArray* all_mirandas, Klass* super, + Array* methods, AccessFlags class_flags, Handle classloader, + Symbol* classname, Array* local_interfaces, TRAPS); // RedefineClasses() API support: // If any entry of this vtable points to any of old_methods, @@ -125,12 +125,17 @@ // support for miranda methods bool is_miranda_entry_at(int i); - void fill_in_mirandas(int& initialized); + void fill_in_mirandas(int* initialized); static bool is_miranda(Method* m, Array* class_methods, Klass* super); - static void add_new_mirandas_to_list(GrowableArray* list_of_current_mirandas, Array* current_interface_methods, Array* class_methods, Klass* super); - static void get_mirandas(GrowableArray* mirandas, Klass* super, Array* class_methods, Array* local_interfaces); - static int get_num_mirandas(Klass* super, Array* class_methods, Array* local_interfaces); - + static void add_new_mirandas_to_lists( + GrowableArray* new_mirandas, + GrowableArray* all_mirandas, + Array* current_interface_methods, Array* class_methods, + Klass* super); + static void get_mirandas( + GrowableArray* new_mirandas, + GrowableArray* all_mirandas, Klass* super, + Array* class_methods, Array* local_interfaces); void verify_against(outputStream* st, klassVtable* vt, int index); inline InstanceKlass* ik() const; diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/oops/method.cpp --- a/src/share/vm/oops/method.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/oops/method.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -35,6 +35,7 @@ #include "memory/generation.hpp" #include "memory/metadataFactory.hpp" #include "memory/oopFactory.hpp" +#include "oops/constMethod.hpp" #include "oops/methodData.hpp" #include "oops/method.hpp" #include "oops/oop.inline.hpp" @@ -57,22 +58,24 @@ // Implementation of Method Method* Method::allocate(ClassLoaderData* loader_data, - int byte_code_size, - AccessFlags access_flags, - int compressed_line_number_size, - int localvariable_table_length, - int exception_table_length, - int checked_exceptions_length, - TRAPS) { + int byte_code_size, + AccessFlags access_flags, + int compressed_line_number_size, + int localvariable_table_length, + int exception_table_length, + int checked_exceptions_length, + ConstMethod::MethodType method_type, + TRAPS) { assert(!access_flags.is_native() || byte_code_size == 0, "native methods should not contain byte codes"); ConstMethod* cm = ConstMethod::allocate(loader_data, - byte_code_size, - compressed_line_number_size, - localvariable_table_length, - exception_table_length, - checked_exceptions_length, - CHECK_NULL); + byte_code_size, + compressed_line_number_size, + localvariable_table_length, + exception_table_length, + checked_exceptions_length, + method_type, + CHECK_NULL); int size = Method::size(access_flags.is_native()); @@ -1031,7 +1034,7 @@ methodHandle m; { Method* m_oop = Method::allocate(loader_data, 0, accessFlags_from(flags_bits), - 0, 0, 0, 0, CHECK_(empty)); + 0, 0, 0, 0, ConstMethod::NORMAL, CHECK_(empty)); m = methodHandle(THREAD, m_oop); } m->set_constants(cp()); @@ -1083,15 +1086,16 @@ int localvariable_len = m->localvariable_table_length(); int exception_table_len = m->exception_table_length(); - ClassLoaderData* loader_data = m()->method_holder()->class_loader_data(); + ClassLoaderData* loader_data = m->method_holder()->class_loader_data(); Method* newm_oop = Method::allocate(loader_data, - new_code_length, - flags, - new_compressed_linenumber_size, - localvariable_len, - exception_table_len, - checked_exceptions_len, - CHECK_(methodHandle())); + new_code_length, + flags, + new_compressed_linenumber_size, + localvariable_len, + exception_table_len, + checked_exceptions_len, + m->method_type(), + CHECK_(methodHandle())); methodHandle newm (THREAD, newm_oop); int new_method_size = newm->method_size(); @@ -1155,8 +1159,12 @@ vmSymbols::SID Method::klass_id_for_intrinsics(Klass* holder) { // if loader is not the default loader (i.e., != NULL), we can't know the intrinsics // because we are not loading from core libraries - if (InstanceKlass::cast(holder)->class_loader() != NULL) + // exception: the AES intrinsics come from lib/ext/sunjce_provider.jar + // which does not use the class default class loader so we check for its loader here + if ((InstanceKlass::cast(holder)->class_loader() != NULL) && + InstanceKlass::cast(holder)->class_loader()->klass()->name() != vmSymbols::sun_misc_Launcher_ExtClassLoader()) { return vmSymbols::NO_SID; // regardless of name, no intrinsics here + } // see if the klass name is well-known: Symbol* klass_name = InstanceKlass::cast(holder)->name(); diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/oops/method.hpp --- a/src/share/vm/oops/method.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/oops/method.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -30,7 +30,6 @@ #include "compiler/oopMap.hpp" #include "interpreter/invocationCounter.hpp" #include "oops/annotations.hpp" -#include "oops/constMethod.hpp" #include "oops/constantPool.hpp" #include "oops/instanceKlass.hpp" #include "oops/oop.hpp" @@ -104,6 +103,7 @@ class LocalVariableTableElement; class AdapterHandlerEntry; class MethodData; +class ConstMethod; class Method : public Metadata { friend class VMStructs; @@ -158,14 +158,16 @@ // Constructor Method(ConstMethod* xconst, AccessFlags access_flags, int size); public: + static Method* allocate(ClassLoaderData* loader_data, - int byte_code_size, - AccessFlags access_flags, - int compressed_line_number_size, - int localvariable_table_length, - int exception_table_length, - int checked_exceptions_length, - TRAPS); + int byte_code_size, + AccessFlags access_flags, + int compressed_line_number_size, + int localvariable_table_length, + int exception_table_length, + int checked_exceptions_length, + ConstMethod::MethodType method_type, + TRAPS); Method() { assert(DumpSharedSpaces || UseSharedSpaces, "only for CDS"); } @@ -725,6 +727,10 @@ void set_dont_inline(bool x) { _dont_inline = x; } bool is_hidden() { return _hidden; } void set_hidden(bool x) { _hidden = x; } + ConstMethod::MethodType method_type() const { + return _constMethod->method_type(); + } + bool is_overpass() const { return method_type() == ConstMethod::OVERPASS; } // On-stack replacement support bool has_osr_nmethod(int level, bool match_level) { diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/opto/c2_globals.hpp --- a/src/share/vm/opto/c2_globals.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/opto/c2_globals.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -439,6 +439,9 @@ product(bool, DoEscapeAnalysis, true, \ "Perform escape analysis") \ \ + develop(bool, ExitEscapeAnalysisOnTimeout, true, \ + "Exit or throw assert in EA when it reaches time limit") \ + \ notproduct(bool, PrintEscapeAnalysis, false, \ "Print the results of escape analysis") \ \ diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/opto/callGenerator.cpp --- a/src/share/vm/opto/callGenerator.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/opto/callGenerator.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -670,6 +670,129 @@ } +//------------------------PredictedIntrinsicGenerator------------------------------ +// Internal class which handles all predicted Intrinsic calls. +class PredictedIntrinsicGenerator : public CallGenerator { + CallGenerator* _intrinsic; + CallGenerator* _cg; + +public: + PredictedIntrinsicGenerator(CallGenerator* intrinsic, + CallGenerator* cg) + : CallGenerator(cg->method()) + { + _intrinsic = intrinsic; + _cg = cg; + } + + virtual bool is_virtual() const { return true; } + virtual bool is_inlined() const { return true; } + virtual bool is_intrinsic() const { return true; } + + virtual JVMState* generate(JVMState* jvms); +}; + + +CallGenerator* CallGenerator::for_predicted_intrinsic(CallGenerator* intrinsic, + CallGenerator* cg) { + return new PredictedIntrinsicGenerator(intrinsic, cg); +} + + +JVMState* PredictedIntrinsicGenerator::generate(JVMState* jvms) { + GraphKit kit(jvms); + PhaseGVN& gvn = kit.gvn(); + + CompileLog* log = kit.C->log(); + if (log != NULL) { + log->elem("predicted_intrinsic bci='%d' method='%d'", + jvms->bci(), log->identify(method())); + } + + Node* slow_ctl = _intrinsic->generate_predicate(kit.sync_jvms()); + if (kit.failing()) + return NULL; // might happen because of NodeCountInliningCutoff + + SafePointNode* slow_map = NULL; + JVMState* slow_jvms; + if (slow_ctl != NULL) { + PreserveJVMState pjvms(&kit); + kit.set_control(slow_ctl); + if (!kit.stopped()) { + slow_jvms = _cg->generate(kit.sync_jvms()); + if (kit.failing()) + return NULL; // might happen because of NodeCountInliningCutoff + assert(slow_jvms != NULL, "must be"); + kit.add_exception_states_from(slow_jvms); + kit.set_map(slow_jvms->map()); + if (!kit.stopped()) + slow_map = kit.stop(); + } + } + + if (kit.stopped()) { + // Predicate is always false. + kit.set_jvms(slow_jvms); + return kit.transfer_exceptions_into_jvms(); + } + + // Generate intrinsic code: + JVMState* new_jvms = _intrinsic->generate(kit.sync_jvms()); + if (new_jvms == NULL) { + // Intrinsic failed, so use slow code or make a direct call. + if (slow_map == NULL) { + CallGenerator* cg = CallGenerator::for_direct_call(method()); + new_jvms = cg->generate(kit.sync_jvms()); + } else { + kit.set_jvms(slow_jvms); + return kit.transfer_exceptions_into_jvms(); + } + } + kit.add_exception_states_from(new_jvms); + kit.set_jvms(new_jvms); + + // Need to merge slow and fast? + if (slow_map == NULL) { + // The fast path is the only path remaining. + return kit.transfer_exceptions_into_jvms(); + } + + if (kit.stopped()) { + // Intrinsic method threw an exception, so it's just the slow path after all. + kit.set_jvms(slow_jvms); + return kit.transfer_exceptions_into_jvms(); + } + + // Finish the diamond. + kit.C->set_has_split_ifs(true); // Has chance for split-if optimization + RegionNode* region = new (kit.C) RegionNode(3); + region->init_req(1, kit.control()); + region->init_req(2, slow_map->control()); + kit.set_control(gvn.transform(region)); + Node* iophi = PhiNode::make(region, kit.i_o(), Type::ABIO); + iophi->set_req(2, slow_map->i_o()); + kit.set_i_o(gvn.transform(iophi)); + kit.merge_memory(slow_map->merged_memory(), region, 2); + uint tos = kit.jvms()->stkoff() + kit.sp(); + uint limit = slow_map->req(); + for (uint i = TypeFunc::Parms; i < limit; i++) { + // Skip unused stack slots; fast forward to monoff(); + if (i == tos) { + i = kit.jvms()->monoff(); + if( i >= limit ) break; + } + Node* m = kit.map()->in(i); + Node* n = slow_map->in(i); + if (m != n) { + const Type* t = gvn.type(m)->meet(gvn.type(n)); + Node* phi = PhiNode::make(region, m, t); + phi->set_req(2, n); + kit.map()->set_req(i, gvn.transform(phi)); + } + } + return kit.transfer_exceptions_into_jvms(); +} + //-------------------------UncommonTrapCallGenerator----------------------------- // Internal class which handles all out-of-line calls checking receiver type. class UncommonTrapCallGenerator : public CallGenerator { diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/opto/callGenerator.hpp --- a/src/share/vm/opto/callGenerator.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/opto/callGenerator.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -143,6 +143,9 @@ // Registry for intrinsics: static CallGenerator* for_intrinsic(ciMethod* m); static void register_intrinsic(ciMethod* m, CallGenerator* cg); + static CallGenerator* for_predicted_intrinsic(CallGenerator* intrinsic, + CallGenerator* cg); + virtual Node* generate_predicate(JVMState* jvms) { return NULL; }; static void print_inlining(ciMethod* callee, int inline_level, int bci, const char* msg) { if (PrintInlining) diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/opto/compile.cpp --- a/src/share/vm/opto/compile.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/opto/compile.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -3047,9 +3047,9 @@ case T_LONG: case T_DOUBLE: return (_v._value.j == other._v._value.j); case T_OBJECT: - case T_METADATA: return (_v._metadata == other._v._metadata); case T_ADDRESS: return (_v._value.l == other._v._value.l); case T_VOID: return (_v._value.l == other._v._value.l); // jump-table entries + case T_METADATA: return (_v._metadata == other._v._metadata); default: ShouldNotReachHere(); } return false; diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/opto/compile.hpp --- a/src/share/vm/opto/compile.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/opto/compile.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -149,7 +149,7 @@ private: BasicType _type; union { - jvalue _value; + jvalue _value; Metadata* _metadata; } _v; int _offset; // offset of this constant (in bytes) relative to the constant table base. diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/opto/doCall.cpp --- a/src/share/vm/opto/doCall.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/opto/doCall.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -107,7 +107,17 @@ // intrinsics handle strict f.p. correctly. if (allow_inline && allow_intrinsics) { CallGenerator* cg = find_intrinsic(callee, call_is_virtual); - if (cg != NULL) return cg; + if (cg != NULL) { + if (cg->is_predicted()) { + // Code without intrinsic but, hopefully, inlined. + CallGenerator* inline_cg = this->call_generator(callee, + vtable_index, call_is_virtual, jvms, allow_inline, prof_factor, false); + if (inline_cg != NULL) { + cg = CallGenerator::for_predicted_intrinsic(cg, inline_cg); + } + } + return cg; + } } // Do method handle calls. diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/opto/escape.cpp --- a/src/share/vm/opto/escape.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/opto/escape.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -893,12 +893,16 @@ arg_has_oops && (i > TypeFunc::Parms); #ifdef ASSERT if (!(is_arraycopy || - call->as_CallLeaf()->_name != NULL && - (strcmp(call->as_CallLeaf()->_name, "g1_wb_pre") == 0 || - strcmp(call->as_CallLeaf()->_name, "g1_wb_post") == 0 )) - ) { + (call->as_CallLeaf()->_name != NULL && + (strcmp(call->as_CallLeaf()->_name, "g1_wb_pre") == 0 || + strcmp(call->as_CallLeaf()->_name, "g1_wb_post") == 0 || + strcmp(call->as_CallLeaf()->_name, "aescrypt_encryptBlock") == 0 || + strcmp(call->as_CallLeaf()->_name, "aescrypt_decryptBlock") == 0 || + strcmp(call->as_CallLeaf()->_name, "cipherBlockChaining_encryptAESCrypt") == 0 || + strcmp(call->as_CallLeaf()->_name, "cipherBlockChaining_decryptAESCrypt") == 0) + ))) { call->dump(); - assert(false, "EA: unexpected CallLeaf"); + fatal(err_msg_res("EA unexpected CallLeaf %s", call->as_CallLeaf()->_name)); } #endif // Always process arraycopy's destination object since @@ -1080,7 +1084,7 @@ C->log()->text("%s", (iterations >= CG_BUILD_ITER_LIMIT) ? "iterations" : "time"); C->log()->end_elem(" limit'"); } - assert(false, err_msg_res("infinite EA connection graph build (%f sec, %d iterations) with %d nodes and worklist size %d", + assert(ExitEscapeAnalysisOnTimeout, err_msg_res("infinite EA connection graph build (%f sec, %d iterations) with %d nodes and worklist size %d", time.seconds(), iterations, nodes_size(), ptnodes_worklist.length())); // Possible infinite build_connection_graph loop, // bailout (no changes to ideal graph were made). diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/opto/library_call.cpp --- a/src/share/vm/opto/library_call.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/opto/library_call.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -44,18 +44,22 @@ public: private: bool _is_virtual; + bool _is_predicted; vmIntrinsics::ID _intrinsic_id; public: - LibraryIntrinsic(ciMethod* m, bool is_virtual, vmIntrinsics::ID id) + LibraryIntrinsic(ciMethod* m, bool is_virtual, bool is_predicted, vmIntrinsics::ID id) : InlineCallGenerator(m), _is_virtual(is_virtual), + _is_predicted(is_predicted), _intrinsic_id(id) { } virtual bool is_intrinsic() const { return true; } virtual bool is_virtual() const { return _is_virtual; } + virtual bool is_predicted() const { return _is_predicted; } virtual JVMState* generate(JVMState* jvms); + virtual Node* generate_predicate(JVMState* jvms); vmIntrinsics::ID intrinsic_id() const { return _intrinsic_id; } }; @@ -83,6 +87,7 @@ int arg_size() const { return callee()->arg_size(); } bool try_to_inline(); + Node* try_to_predicate(); // Helper functions to inline natives void push_result(RegionNode* region, PhiNode* value); @@ -148,6 +153,7 @@ CallJavaNode* generate_method_call_virtual(vmIntrinsics::ID method_id) { return generate_method_call(method_id, true, false); } + Node * load_field_from_object(Node * fromObj, const char * fieldName, const char * fieldTypeString, bool is_exact, bool is_static); Node* make_string_method_node(int opcode, Node* str1_start, Node* cnt1, Node* str2_start, Node* cnt2); Node* make_string_method_node(int opcode, Node* str1, Node* str2); @@ -253,6 +259,10 @@ bool inline_reverseBytes(vmIntrinsics::ID id); bool inline_reference_get(); + bool inline_aescrypt_Block(vmIntrinsics::ID id); + bool inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id); + Node* inline_cipherBlockChaining_AESCrypt_predicate(bool decrypting); + Node* get_key_start_from_aescrypt_object(Node* aescrypt_object); }; @@ -306,6 +316,8 @@ } } + bool is_predicted = false; + switch (id) { case vmIntrinsics::_compareTo: if (!SpecialStringCompareTo) return NULL; @@ -413,6 +425,18 @@ break; #endif + case vmIntrinsics::_aescrypt_encryptBlock: + case vmIntrinsics::_aescrypt_decryptBlock: + if (!UseAESIntrinsics) return NULL; + break; + + case vmIntrinsics::_cipherBlockChaining_encryptAESCrypt: + case vmIntrinsics::_cipherBlockChaining_decryptAESCrypt: + if (!UseAESIntrinsics) return NULL; + // these two require the predicated logic + is_predicted = true; + break; + default: assert(id <= vmIntrinsics::LAST_COMPILER_INLINE, "caller responsibility"); assert(id != vmIntrinsics::_Object_init && id != vmIntrinsics::_invoke, "enum out of order?"); @@ -444,7 +468,7 @@ if (!InlineUnsafeOps) return NULL; } - return new LibraryIntrinsic(m, is_virtual, (vmIntrinsics::ID) id); + return new LibraryIntrinsic(m, is_virtual, is_predicted, (vmIntrinsics::ID) id); } //----------------------register_library_intrinsics----------------------- @@ -496,6 +520,47 @@ return NULL; } +Node* LibraryIntrinsic::generate_predicate(JVMState* jvms) { + LibraryCallKit kit(jvms, this); + Compile* C = kit.C; + int nodes = C->unique(); +#ifndef PRODUCT + assert(is_predicted(), "sanity"); + if ((PrintIntrinsics || PrintInlining NOT_PRODUCT( || PrintOptoInlining) ) && Verbose) { + char buf[1000]; + const char* str = vmIntrinsics::short_name_as_C_string(intrinsic_id(), buf, sizeof(buf)); + tty->print_cr("Predicate for intrinsic %s", str); + } +#endif + + Node* slow_ctl = kit.try_to_predicate(); + if (!kit.failing()) { + if (C->log()) { + C->log()->elem("predicate_intrinsic id='%s'%s nodes='%d'", + vmIntrinsics::name_at(intrinsic_id()), + (is_virtual() ? " virtual='1'" : ""), + C->unique() - nodes); + } + return slow_ctl; // Could be NULL if the check folds. + } + + // The intrinsic bailed out + if (PrintIntrinsics || PrintInlining NOT_PRODUCT( || PrintOptoInlining) ) { + if (jvms->has_method()) { + // Not a root compile. + const char* msg = "failed to generate predicate for intrinsic"; + CompileTask::print_inlining(kit.callee(), jvms->depth() - 1, kit.bci(), msg); + } else { + // Root compile + tty->print("Did not generate predicate for intrinsic %s%s at bci:%d in", + vmIntrinsics::name_at(intrinsic_id()), + (is_virtual() ? " (virtual)" : ""), kit.bci()); + } + } + C->gather_intrinsic_statistics(intrinsic_id(), is_virtual(), Compile::_intrinsic_failed); + return NULL; +} + bool LibraryCallKit::try_to_inline() { // Handle symbolic names for otherwise undistinguished boolean switches: const bool is_store = true; @@ -767,6 +832,14 @@ case vmIntrinsics::_Reference_get: return inline_reference_get(); + case vmIntrinsics::_aescrypt_encryptBlock: + case vmIntrinsics::_aescrypt_decryptBlock: + return inline_aescrypt_Block(intrinsic_id()); + + case vmIntrinsics::_cipherBlockChaining_encryptAESCrypt: + case vmIntrinsics::_cipherBlockChaining_decryptAESCrypt: + return inline_cipherBlockChaining_AESCrypt(intrinsic_id()); + default: // If you get here, it may be that someone has added a new intrinsic // to the list in vmSymbols.hpp without implementing it here. @@ -780,6 +853,36 @@ } } +Node* LibraryCallKit::try_to_predicate() { + if (!jvms()->has_method()) { + // Root JVMState has a null method. + assert(map()->memory()->Opcode() == Op_Parm, ""); + // Insert the memory aliasing node + set_all_memory(reset_memory()); + } + assert(merged_memory(), ""); + + switch (intrinsic_id()) { + case vmIntrinsics::_cipherBlockChaining_encryptAESCrypt: + return inline_cipherBlockChaining_AESCrypt_predicate(false); + case vmIntrinsics::_cipherBlockChaining_decryptAESCrypt: + return inline_cipherBlockChaining_AESCrypt_predicate(true); + + default: + // If you get here, it may be that someone has added a new intrinsic + // to the list in vmSymbols.hpp without implementing it here. +#ifndef PRODUCT + if ((PrintMiscellaneous && (Verbose || WizardMode)) || PrintOpto) { + tty->print_cr("*** Warning: Unimplemented predicate for intrinsic %s(%d)", + vmIntrinsics::name_at(intrinsic_id()), intrinsic_id()); + } +#endif + Node* slow_ctl = control(); + set_control(top()); // No fast path instrinsic + return slow_ctl; + } +} + //------------------------------push_result------------------------------ // Helper function for finishing intrinsics. void LibraryCallKit::push_result(RegionNode* region, PhiNode* value) { @@ -3830,7 +3933,7 @@ vtable_index*vtableEntry::size()) * wordSize + vtableEntry::method_offset_in_bytes(); Node* entry_addr = basic_plus_adr(obj_klass, entry_offset); - Node* target_call = make_load(NULL, entry_addr, TypeInstPtr::NOTNULL, T_OBJECT); + Node* target_call = make_load(NULL, entry_addr, TypePtr::NOTNULL, T_ADDRESS); // Compare the target method with the expected method (e.g., Object.hashCode). const TypePtr* native_call_addr = TypeMetadataPtr::make(method); @@ -5613,3 +5716,265 @@ push(result); return true; } + + +Node * LibraryCallKit::load_field_from_object(Node * fromObj, const char * fieldName, const char * fieldTypeString, + bool is_exact=true, bool is_static=false) { + + const TypeInstPtr* tinst = _gvn.type(fromObj)->isa_instptr(); + assert(tinst != NULL, "obj is null"); + assert(tinst->klass()->is_loaded(), "obj is not loaded"); + assert(!is_exact || tinst->klass_is_exact(), "klass not exact"); + + ciField* field = tinst->klass()->as_instance_klass()->get_field_by_name(ciSymbol::make(fieldName), + ciSymbol::make(fieldTypeString), + is_static); + if (field == NULL) return (Node *) NULL; + assert (field != NULL, "undefined field"); + + // Next code copied from Parse::do_get_xxx(): + + // Compute address and memory type. + int offset = field->offset_in_bytes(); + bool is_vol = field->is_volatile(); + ciType* field_klass = field->type(); + assert(field_klass->is_loaded(), "should be loaded"); + const TypePtr* adr_type = C->alias_type(field)->adr_type(); + Node *adr = basic_plus_adr(fromObj, fromObj, offset); + BasicType bt = field->layout_type(); + + // Build the resultant type of the load + const Type *type = TypeOopPtr::make_from_klass(field_klass->as_klass()); + + // Build the load. + Node* loadedField = make_load(NULL, adr, type, bt, adr_type, is_vol); + return loadedField; +} + + +//------------------------------inline_aescrypt_Block----------------------- +bool LibraryCallKit::inline_aescrypt_Block(vmIntrinsics::ID id) { + address stubAddr; + const char *stubName; + assert(UseAES, "need AES instruction support"); + + switch(id) { + case vmIntrinsics::_aescrypt_encryptBlock: + stubAddr = StubRoutines::aescrypt_encryptBlock(); + stubName = "aescrypt_encryptBlock"; + break; + case vmIntrinsics::_aescrypt_decryptBlock: + stubAddr = StubRoutines::aescrypt_decryptBlock(); + stubName = "aescrypt_decryptBlock"; + break; + } + if (stubAddr == NULL) return false; + + // Restore the stack and pop off the arguments. + int nargs = 5; // this + 2 oop/offset combos + assert(callee()->signature()->size() == nargs-1, "encryptBlock has 4 arguments"); + + Node *aescrypt_object = argument(0); + Node *src = argument(1); + Node *src_offset = argument(2); + Node *dest = argument(3); + Node *dest_offset = argument(4); + + // (1) src and dest are arrays. + const Type* src_type = src->Value(&_gvn); + const Type* dest_type = dest->Value(&_gvn); + const TypeAryPtr* top_src = src_type->isa_aryptr(); + const TypeAryPtr* top_dest = dest_type->isa_aryptr(); + assert (top_src != NULL && top_src->klass() != NULL && top_dest != NULL && top_dest->klass() != NULL, "args are strange"); + + // for the quick and dirty code we will skip all the checks. + // we are just trying to get the call to be generated. + Node* src_start = src; + Node* dest_start = dest; + if (src_offset != NULL || dest_offset != NULL) { + assert(src_offset != NULL && dest_offset != NULL, ""); + src_start = array_element_address(src, src_offset, T_BYTE); + dest_start = array_element_address(dest, dest_offset, T_BYTE); + } + + // now need to get the start of its expanded key array + // this requires a newer class file that has this array as littleEndian ints, otherwise we revert to java + Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object); + if (k_start == NULL) return false; + + // Call the stub. + make_runtime_call(RC_LEAF|RC_NO_FP, OptoRuntime::aescrypt_block_Type(), + stubAddr, stubName, TypePtr::BOTTOM, + src_start, dest_start, k_start); + + return true; +} + +//------------------------------inline_cipherBlockChaining_AESCrypt----------------------- +bool LibraryCallKit::inline_cipherBlockChaining_AESCrypt(vmIntrinsics::ID id) { + address stubAddr; + const char *stubName; + + assert(UseAES, "need AES instruction support"); + + switch(id) { + case vmIntrinsics::_cipherBlockChaining_encryptAESCrypt: + stubAddr = StubRoutines::cipherBlockChaining_encryptAESCrypt(); + stubName = "cipherBlockChaining_encryptAESCrypt"; + break; + case vmIntrinsics::_cipherBlockChaining_decryptAESCrypt: + stubAddr = StubRoutines::cipherBlockChaining_decryptAESCrypt(); + stubName = "cipherBlockChaining_decryptAESCrypt"; + break; + } + if (stubAddr == NULL) return false; + + + // Restore the stack and pop off the arguments. + int nargs = 6; // this + oop/offset + len + oop/offset + assert(callee()->signature()->size() == nargs-1, "wrong number of arguments"); + Node *cipherBlockChaining_object = argument(0); + Node *src = argument(1); + Node *src_offset = argument(2); + Node *len = argument(3); + Node *dest = argument(4); + Node *dest_offset = argument(5); + + // (1) src and dest are arrays. + const Type* src_type = src->Value(&_gvn); + const Type* dest_type = dest->Value(&_gvn); + const TypeAryPtr* top_src = src_type->isa_aryptr(); + const TypeAryPtr* top_dest = dest_type->isa_aryptr(); + assert (top_src != NULL && top_src->klass() != NULL + && top_dest != NULL && top_dest->klass() != NULL, "args are strange"); + + // checks are the responsibility of the caller + Node* src_start = src; + Node* dest_start = dest; + if (src_offset != NULL || dest_offset != NULL) { + assert(src_offset != NULL && dest_offset != NULL, ""); + src_start = array_element_address(src, src_offset, T_BYTE); + dest_start = array_element_address(dest, dest_offset, T_BYTE); + } + + // if we are in this set of code, we "know" the embeddedCipher is an AESCrypt object + // (because of the predicated logic executed earlier). + // so we cast it here safely. + // this requires a newer class file that has this array as littleEndian ints, otherwise we revert to java + + Node* embeddedCipherObj = load_field_from_object(cipherBlockChaining_object, "embeddedCipher", "Lcom/sun/crypto/provider/SymmetricCipher;", /*is_exact*/ false); + if (embeddedCipherObj == NULL) return false; + + // cast it to what we know it will be at runtime + const TypeInstPtr* tinst = _gvn.type(cipherBlockChaining_object)->isa_instptr(); + assert(tinst != NULL, "CBC obj is null"); + assert(tinst->klass()->is_loaded(), "CBC obj is not loaded"); + ciKlass* klass_AESCrypt = tinst->klass()->as_instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AESCrypt")); + if (!klass_AESCrypt->is_loaded()) return false; + + ciInstanceKlass* instklass_AESCrypt = klass_AESCrypt->as_instance_klass(); + const TypeKlassPtr* aklass = TypeKlassPtr::make(instklass_AESCrypt); + const TypeOopPtr* xtype = aklass->as_instance_type(); + Node* aescrypt_object = new(C) CheckCastPPNode(control(), embeddedCipherObj, xtype); + aescrypt_object = _gvn.transform(aescrypt_object); + + // we need to get the start of the aescrypt_object's expanded key array + Node* k_start = get_key_start_from_aescrypt_object(aescrypt_object); + if (k_start == NULL) return false; + + // similarly, get the start address of the r vector + Node* objRvec = load_field_from_object(cipherBlockChaining_object, "r", "[B", /*is_exact*/ false); + if (objRvec == NULL) return false; + Node* r_start = array_element_address(objRvec, intcon(0), T_BYTE); + + // Call the stub, passing src_start, dest_start, k_start, r_start and src_len + make_runtime_call(RC_LEAF|RC_NO_FP, + OptoRuntime::cipherBlockChaining_aescrypt_Type(), + stubAddr, stubName, TypePtr::BOTTOM, + src_start, dest_start, k_start, r_start, len); + + // return is void so no result needs to be pushed + + return true; +} + +//------------------------------get_key_start_from_aescrypt_object----------------------- +Node * LibraryCallKit::get_key_start_from_aescrypt_object(Node *aescrypt_object) { + Node* objAESCryptKey = load_field_from_object(aescrypt_object, "K", "[I", /*is_exact*/ false); + assert (objAESCryptKey != NULL, "wrong version of com.sun.crypto.provider.AESCrypt"); + if (objAESCryptKey == NULL) return (Node *) NULL; + + // now have the array, need to get the start address of the K array + Node* k_start = array_element_address(objAESCryptKey, intcon(0), T_INT); + return k_start; +} + +//----------------------------inline_cipherBlockChaining_AESCrypt_predicate---------------------------- +// Return node representing slow path of predicate check. +// the pseudo code we want to emulate with this predicate is: +// for encryption: +// if (embeddedCipherObj instanceof AESCrypt) do_intrinsic, else do_javapath +// for decryption: +// if ((embeddedCipherObj instanceof AESCrypt) && (cipher!=plain)) do_intrinsic, else do_javapath +// note cipher==plain is more conservative than the original java code but that's OK +// +Node* LibraryCallKit::inline_cipherBlockChaining_AESCrypt_predicate(bool decrypting) { + // First, check receiver for NULL since it is virtual method. + int nargs = arg_size(); + Node* objCBC = argument(0); + _sp += nargs; + objCBC = do_null_check(objCBC, T_OBJECT); + _sp -= nargs; + + if (stopped()) return NULL; // Always NULL + + // Load embeddedCipher field of CipherBlockChaining object. + Node* embeddedCipherObj = load_field_from_object(objCBC, "embeddedCipher", "Lcom/sun/crypto/provider/SymmetricCipher;", /*is_exact*/ false); + + // get AESCrypt klass for instanceOf check + // AESCrypt might not be loaded yet if some other SymmetricCipher got us to this compile point + // will have same classloader as CipherBlockChaining object + const TypeInstPtr* tinst = _gvn.type(objCBC)->isa_instptr(); + assert(tinst != NULL, "CBCobj is null"); + assert(tinst->klass()->is_loaded(), "CBCobj is not loaded"); + + // we want to do an instanceof comparison against the AESCrypt class + ciKlass* klass_AESCrypt = tinst->klass()->as_instance_klass()->find_klass(ciSymbol::make("com/sun/crypto/provider/AESCrypt")); + if (!klass_AESCrypt->is_loaded()) { + // if AESCrypt is not even loaded, we never take the intrinsic fast path + Node* ctrl = control(); + set_control(top()); // no regular fast path + return ctrl; + } + ciInstanceKlass* instklass_AESCrypt = klass_AESCrypt->as_instance_klass(); + + _sp += nargs; // gen_instanceof might do an uncommon trap + Node* instof = gen_instanceof(embeddedCipherObj, makecon(TypeKlassPtr::make(instklass_AESCrypt))); + _sp -= nargs; + Node* cmp_instof = _gvn.transform(new (C) CmpINode(instof, intcon(1))); + Node* bool_instof = _gvn.transform(new (C) BoolNode(cmp_instof, BoolTest::ne)); + + Node* instof_false = generate_guard(bool_instof, NULL, PROB_MIN); + + // for encryption, we are done + if (!decrypting) + return instof_false; // even if it is NULL + + // for decryption, we need to add a further check to avoid + // taking the intrinsic path when cipher and plain are the same + // see the original java code for why. + RegionNode* region = new(C) RegionNode(3); + region->init_req(1, instof_false); + Node* src = argument(1); + Node *dest = argument(4); + Node* cmp_src_dest = _gvn.transform(new (C) CmpPNode(src, dest)); + Node* bool_src_dest = _gvn.transform(new (C) BoolNode(cmp_src_dest, BoolTest::eq)); + Node* src_dest_conjoint = generate_guard(bool_src_dest, NULL, PROB_MIN); + region->init_req(2, src_dest_conjoint); + + record_for_igvn(region); + return _gvn.transform(region); + +} + + diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/opto/loopTransform.cpp --- a/src/share/vm/opto/loopTransform.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/opto/loopTransform.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -2715,6 +2715,8 @@ result_mem = new (C) ProjNode(call,TypeFunc::Memory); _igvn.register_new_node_with_optimizer(result_mem); +/* Disable following optimization until proper fix (add missing checks). + // If this fill is tightly coupled to an allocation and overwrites // the whole body, allow it to take over the zeroing. AllocateNode* alloc = AllocateNode::Ideal_allocation(base, this); @@ -2738,6 +2740,7 @@ #endif } } +*/ // Redirect the old control and memory edges that are outside the loop. Node* exit = head->loopexit()->proj_out(0); diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/opto/mulnode.cpp --- a/src/share/vm/opto/mulnode.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/opto/mulnode.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -479,24 +479,27 @@ return new (phase->C) AndINode(load,phase->intcon(mask&0xFFFF)); // Masking bits off of a Short? Loading a Character does some masking - if (lop == Op_LoadS && (mask & 0xFFFF0000) == 0 ) { - Node *ldus = new (phase->C) LoadUSNode(load->in(MemNode::Control), - load->in(MemNode::Memory), - load->in(MemNode::Address), - load->adr_type()); - ldus = phase->transform(ldus); - return new (phase->C) AndINode(ldus, phase->intcon(mask & 0xFFFF)); - } + if (can_reshape && + load->outcnt() == 1 && load->unique_out() == this) { + if (lop == Op_LoadS && (mask & 0xFFFF0000) == 0 ) { + Node *ldus = new (phase->C) LoadUSNode(load->in(MemNode::Control), + load->in(MemNode::Memory), + load->in(MemNode::Address), + load->adr_type()); + ldus = phase->transform(ldus); + return new (phase->C) AndINode(ldus, phase->intcon(mask & 0xFFFF)); + } - // Masking sign bits off of a Byte? Do an unsigned byte load plus - // an and. - if (lop == Op_LoadB && (mask & 0xFFFFFF00) == 0) { - Node* ldub = new (phase->C) LoadUBNode(load->in(MemNode::Control), - load->in(MemNode::Memory), - load->in(MemNode::Address), - load->adr_type()); - ldub = phase->transform(ldub); - return new (phase->C) AndINode(ldub, phase->intcon(mask)); + // Masking sign bits off of a Byte? Do an unsigned byte load plus + // an and. + if (lop == Op_LoadB && (mask & 0xFFFFFF00) == 0) { + Node* ldub = new (phase->C) LoadUBNode(load->in(MemNode::Control), + load->in(MemNode::Memory), + load->in(MemNode::Address), + load->adr_type()); + ldub = phase->transform(ldub); + return new (phase->C) AndINode(ldub, phase->intcon(mask)); + } } // Masking off sign bits? Dont make them! @@ -923,7 +926,9 @@ set_req(2, phase->intcon(0)); return this; } - else if( ld->Opcode() == Op_LoadUS ) + else if( can_reshape && + ld->Opcode() == Op_LoadUS && + ld->outcnt() == 1 && ld->unique_out() == shl) // Replace zero-extension-load with sign-extension-load return new (phase->C) LoadSNode( ld->in(MemNode::Control), ld->in(MemNode::Memory), diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/opto/runtime.cpp --- a/src/share/vm/opto/runtime.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/opto/runtime.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -811,6 +811,48 @@ return TypeFunc::make(domain, range); } +// for aescrypt encrypt/decrypt operations, just three pointers returning void (length is constant) +const TypeFunc* OptoRuntime::aescrypt_block_Type() { + // create input type (domain) + int num_args = 3; + int argcnt = num_args; + const Type** fields = TypeTuple::fields(argcnt); + int argp = TypeFunc::Parms; + fields[argp++] = TypePtr::NOTNULL; // src + fields[argp++] = TypePtr::NOTNULL; // dest + fields[argp++] = TypePtr::NOTNULL; // k array + assert(argp == TypeFunc::Parms+argcnt, "correct decoding"); + const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+argcnt, fields); + + // no result type needed + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = NULL; // void + const TypeTuple* range = TypeTuple::make(TypeFunc::Parms, fields); + return TypeFunc::make(domain, range); +} + +// for cipherBlockChaining calls of aescrypt encrypt/decrypt, four pointers and a length, returning void +const TypeFunc* OptoRuntime::cipherBlockChaining_aescrypt_Type() { + // create input type (domain) + int num_args = 5; + int argcnt = num_args; + const Type** fields = TypeTuple::fields(argcnt); + int argp = TypeFunc::Parms; + fields[argp++] = TypePtr::NOTNULL; // src + fields[argp++] = TypePtr::NOTNULL; // dest + fields[argp++] = TypePtr::NOTNULL; // k array + fields[argp++] = TypePtr::NOTNULL; // r array + fields[argp++] = TypeInt::INT; // src len + assert(argp == TypeFunc::Parms+argcnt, "correct decoding"); + const TypeTuple* domain = TypeTuple::make(TypeFunc::Parms+argcnt, fields); + + // no result type needed + fields = TypeTuple::fields(1); + fields[TypeFunc::Parms+0] = NULL; // void + const TypeTuple* range = TypeTuple::make(TypeFunc::Parms, fields); + return TypeFunc::make(domain, range); +} + //------------- Interpreter state access for on stack replacement const TypeFunc* OptoRuntime::osr_end_Type() { // create input type (domain) diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/opto/runtime.hpp --- a/src/share/vm/opto/runtime.hpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/opto/runtime.hpp Mon Nov 05 13:55:31 2012 -0800 @@ -280,6 +280,9 @@ static const TypeFunc* array_fill_Type(); + static const TypeFunc* aescrypt_block_Type(); + static const TypeFunc* cipherBlockChaining_aescrypt_Type(); + // leaf on stack replacement interpreter accessor types static const TypeFunc* osr_end_Type(); diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/opto/superword.cpp --- a/src/share/vm/opto/superword.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/opto/superword.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -1776,16 +1776,15 @@ set_velt_type(n, container_type(n)); } - // Propagate narrowed type backwards through operations + // Propagate integer narrowed type backwards through operations // that don't depend on higher order bits for (int i = _block.length() - 1; i >= 0; i--) { Node* n = _block.at(i); // Only integer types need be examined - const Type* vt = velt_type(n); - if (vt->basic_type() == T_INT) { + const Type* vtn = velt_type(n); + if (vtn->basic_type() == T_INT) { uint start, end; VectorNode::vector_operands(n, &start, &end); - const Type* vt = velt_type(n); for (uint j = start; j < end; j++) { Node* in = n->in(j); @@ -1801,6 +1800,24 @@ } } if (same_type) { + // For right shifts of small integer types (bool, byte, char, short) + // we need precise information about sign-ness. Only Load nodes have + // this information because Store nodes are the same for signed and + // unsigned values. And any arithmetic operation after a load may + // expand a value to signed Int so such right shifts can't be used + // because vector elements do not have upper bits of Int. + const Type* vt = vtn; + if (VectorNode::is_shift(in)) { + Node* load = in->in(1); + if (load->is_Load() && in_bb(load) && (velt_type(load)->basic_type() == T_INT)) { + vt = velt_type(load); + } else if (in->Opcode() != Op_LShiftI) { + // Widen type to Int to avoid creation of right shift vector + // (align + data_size(s1) check in stmts_can_pack() will fail). + // Note, left shifts work regardless type. + vt = TypeInt::INT; + } + } set_velt_type(in, vt); } } @@ -1841,7 +1858,20 @@ // Smallest type containing range of values const Type* SuperWord::container_type(Node* n) { if (n->is_Mem()) { - return Type::get_const_basic_type(n->as_Mem()->memory_type()); + BasicType bt = n->as_Mem()->memory_type(); + if (n->is_Store() && (bt == T_CHAR)) { + // Use T_SHORT type instead of T_CHAR for stored values because any + // preceding arithmetic operation extends values to signed Int. + bt = T_SHORT; + } + if (n->Opcode() == Op_LoadUB) { + // Adjust type for unsigned byte loads, it is important for right shifts. + // T_BOOLEAN is used because there is no basic type representing type + // TypeInt::UBYTE. Use of T_BOOLEAN for vectors is fine because only + // size (one byte) and sign is important. + bt = T_BOOLEAN; + } + return Type::get_const_basic_type(bt); } const Type* t = _igvn.type(n); if (t->basic_type() == T_INT) { diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/opto/type.cpp --- a/src/share/vm/opto/type.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/opto/type.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -61,7 +61,7 @@ { Bad, T_ILLEGAL, "tuple:", false, Node::NotAMachineReg, relocInfo::none }, // Tuple { Bad, T_ARRAY, "array:", false, Node::NotAMachineReg, relocInfo::none }, // Array -#if defined(IA32) || defined(AMD64) +#ifndef SPARC { Bad, T_ILLEGAL, "vectors:", false, Op_VecS, relocInfo::none }, // VectorS { Bad, T_ILLEGAL, "vectord:", false, Op_VecD, relocInfo::none }, // VectorD { Bad, T_ILLEGAL, "vectorx:", false, Op_VecX, relocInfo::none }, // VectorX diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/opto/vectornode.cpp --- a/src/share/vm/opto/vectornode.cpp Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/opto/vectornode.cpp Mon Nov 05 13:55:31 2012 -0800 @@ -29,8 +29,7 @@ //------------------------------VectorNode-------------------------------------- // Return the vector operator for the specified scalar operation -// and vector length. Also used to check if the code generator -// supports the vector operation. +// and vector length. int VectorNode::opcode(int sopc, BasicType bt) { switch (sopc) { case Op_AddI: @@ -75,7 +74,7 @@ case T_BYTE: return 0; // Unimplemented case T_CHAR: case T_SHORT: return Op_MulVS; - case T_INT: return Matcher::match_rule_supported(Op_MulVI) ? Op_MulVI : 0; // SSE4_1 + case T_INT: return Op_MulVI; } ShouldNotReachHere(); case Op_MulF: @@ -104,9 +103,9 @@ return Op_LShiftVL; case Op_RShiftI: switch (bt) { - case T_BOOLEAN: + case T_BOOLEAN:return Op_URShiftVB; // boolean is unsigned value + case T_CHAR: return Op_URShiftVS; // char is unsigned value case T_BYTE: return Op_RShiftVB; - case T_CHAR: case T_SHORT: return Op_RShiftVS; case T_INT: return Op_RShiftVI; } @@ -116,10 +115,14 @@ return Op_RShiftVL; case Op_URShiftI: switch (bt) { - case T_BOOLEAN: - case T_BYTE: return Op_URShiftVB; - case T_CHAR: - case T_SHORT: return Op_URShiftVS; + case T_BOOLEAN:return Op_URShiftVB; + case T_CHAR: return Op_URShiftVS; + case T_BYTE: + case T_SHORT: return 0; // Vector logical right shift for signed short + // values produces incorrect Java result for + // negative data because java code should convert + // a short value into int value with sign + // extension before a shift. case T_INT: return Op_URShiftVI; } ShouldNotReachHere(); @@ -157,12 +160,14 @@ return 0; // Unimplemented } +// Also used to check if the code generator +// supports the vector operation. bool VectorNode::implemented(int opc, uint vlen, BasicType bt) { if (is_java_primitive(bt) && (vlen > 1) && is_power_of_2(vlen) && Matcher::vector_size_supported(bt, vlen)) { int vopc = VectorNode::opcode(opc, bt); - return vopc > 0 && Matcher::has_match_rule(vopc); + return vopc > 0 && Matcher::match_rule_supported(vopc); } return false; } diff -r 69ad7823b1ca -r 8940ddc1036f src/share/vm/prims/jvmti.xml --- a/src/share/vm/prims/jvmti.xml Mon Nov 05 15:30:22 2012 -0500 +++ b/src/share/vm/prims/jvmti.xml Mon Nov 05 13:55:31 2012 -0800 @@ -1,7 +1,7 @@