# HG changeset patch # User kvn # Date 1436893118 -7200 # Node ID 7c15975834cd3a3fea3d4f5b4a9a1918c6b5ac71 # Parent 13420c0a3ad5cd137be27e61a16ec33f86aca008# Parent adbf29d9ca435129ca174e345f899c85e500fdd9 Merge diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/.hgignore --- a/hotspot/.hgignore Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/.hgignore Tue Jul 14 18:58:38 2015 +0200 @@ -10,3 +10,4 @@ .igv.log ^.hgtip .DS_Store +\.class$ diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/cpu/ppc/vm/frame_ppc.inline.hpp --- a/hotspot/src/cpu/ppc/vm/frame_ppc.inline.hpp Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/cpu/ppc/vm/frame_ppc.inline.hpp Tue Jul 14 18:58:38 2015 +0200 @@ -1,6 +1,6 @@ /* - * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. - * Copyright 2012, 2014 SAP AG. All rights reserved. + * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012, 2015 SAP AG. 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 @@ -225,7 +225,7 @@ return (BasicObjectLock *) get_ijava_state(); } -// SAPJVM ASc 2012-11-21. Return register stack slot addr at which currently interpreted method is found +// Return register stack slot addr at which currently interpreted method is found. inline Method** frame::interpreter_frame_method_addr() const { return (Method**) &(get_ijava_state()->method); } diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/cpu/ppc/vm/macroAssembler_ppc.cpp --- a/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.cpp Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.cpp Tue Jul 14 18:58:38 2015 +0200 @@ -3433,6 +3433,376 @@ bind(Ldone_false); } +// dest_lo += src1 + src2 +// dest_hi += carry1 + carry2 +void MacroAssembler::add2_with_carry(Register dest_hi, + Register dest_lo, + Register src1, Register src2) { + li(R0, 0); + addc(dest_lo, dest_lo, src1); + adde(dest_hi, dest_hi, R0); + addc(dest_lo, dest_lo, src2); + adde(dest_hi, dest_hi, R0); +} + +// Multiply 64 bit by 64 bit first loop. +void MacroAssembler::multiply_64_x_64_loop(Register x, Register xstart, + Register x_xstart, + Register y, Register y_idx, + Register z, + Register carry, + Register product_high, Register product, + Register idx, Register kdx, + Register tmp) { + // jlong carry, x[], y[], z[]; + // for (int idx=ystart, kdx=ystart+1+xstart; idx >= 0; idx--, kdx--) { + // huge_128 product = y[idx] * x[xstart] + carry; + // z[kdx] = (jlong)product; + // carry = (jlong)(product >>> 64); + // } + // z[xstart] = carry; + + Label L_first_loop, L_first_loop_exit; + Label L_one_x, L_one_y, L_multiply; + + addic_(xstart, xstart, -1); + blt(CCR0, L_one_x); // Special case: length of x is 1. + + // Load next two integers of x. + sldi(tmp, xstart, LogBytesPerInt); + ldx(x_xstart, x, tmp); +#ifdef VM_LITTLE_ENDIAN + rldicl(x_xstart, x_xstart, 32, 0); +#endif + + align(32, 16); + bind(L_first_loop); + + cmpdi(CCR0, idx, 1); + blt(CCR0, L_first_loop_exit); + addi(idx, idx, -2); + beq(CCR0, L_one_y); + + // Load next two integers of y. + sldi(tmp, idx, LogBytesPerInt); + ldx(y_idx, y, tmp); +#ifdef VM_LITTLE_ENDIAN + rldicl(y_idx, y_idx, 32, 0); +#endif + + + bind(L_multiply); + multiply64(product_high, product, x_xstart, y_idx); + + li(tmp, 0); + addc(product, product, carry); // Add carry to result. + adde(product_high, product_high, tmp); // Add carry of the last addition. + addi(kdx, kdx, -2); + + // Store result. +#ifdef VM_LITTLE_ENDIAN + rldicl(product, product, 32, 0); +#endif + sldi(tmp, kdx, LogBytesPerInt); + stdx(product, z, tmp); + mr_if_needed(carry, product_high); + b(L_first_loop); + + + bind(L_one_y); // Load one 32 bit portion of y as (0,value). + + lwz(y_idx, 0, y); + b(L_multiply); + + + bind( L_one_x ); // Load one 32 bit portion of x as (0,value). + + lwz(x_xstart, 0, x); + b(L_first_loop); + + bind(L_first_loop_exit); +} + +// Multiply 64 bit by 64 bit and add 128 bit. +void MacroAssembler::multiply_add_128_x_128(Register x_xstart, Register y, + Register z, Register yz_idx, + Register idx, Register carry, + Register product_high, Register product, + Register tmp, int offset) { + + // huge_128 product = (y[idx] * x_xstart) + z[kdx] + carry; + // z[kdx] = (jlong)product; + + sldi(tmp, idx, LogBytesPerInt); + if ( offset ) { + addi(tmp, tmp, offset); + } + ldx(yz_idx, y, tmp); +#ifdef VM_LITTLE_ENDIAN + rldicl(yz_idx, yz_idx, 32, 0); +#endif + + multiply64(product_high, product, x_xstart, yz_idx); + ldx(yz_idx, z, tmp); +#ifdef VM_LITTLE_ENDIAN + rldicl(yz_idx, yz_idx, 32, 0); +#endif + + add2_with_carry(product_high, product, carry, yz_idx); + + sldi(tmp, idx, LogBytesPerInt); + if ( offset ) { + addi(tmp, tmp, offset); + } +#ifdef VM_LITTLE_ENDIAN + rldicl(product, product, 32, 0); +#endif + stdx(product, z, tmp); +} + +// Multiply 128 bit by 128 bit. Unrolled inner loop. +void MacroAssembler::multiply_128_x_128_loop(Register x_xstart, + Register y, Register z, + Register yz_idx, Register idx, Register carry, + Register product_high, Register product, + Register carry2, Register tmp) { + + // jlong carry, x[], y[], z[]; + // int kdx = ystart+1; + // for (int idx=ystart-2; idx >= 0; idx -= 2) { // Third loop + // huge_128 product = (y[idx+1] * x_xstart) + z[kdx+idx+1] + carry; + // z[kdx+idx+1] = (jlong)product; + // jlong carry2 = (jlong)(product >>> 64); + // product = (y[idx] * x_xstart) + z[kdx+idx] + carry2; + // z[kdx+idx] = (jlong)product; + // carry = (jlong)(product >>> 64); + // } + // idx += 2; + // if (idx > 0) { + // product = (y[idx] * x_xstart) + z[kdx+idx] + carry; + // z[kdx+idx] = (jlong)product; + // carry = (jlong)(product >>> 64); + // } + + Label L_third_loop, L_third_loop_exit, L_post_third_loop_done; + const Register jdx = R0; + + // Scale the index. + srdi_(jdx, idx, 2); + beq(CCR0, L_third_loop_exit); + mtctr(jdx); + + align(32, 16); + bind(L_third_loop); + + addi(idx, idx, -4); + + multiply_add_128_x_128(x_xstart, y, z, yz_idx, idx, carry, product_high, product, tmp, 8); + mr_if_needed(carry2, product_high); + + multiply_add_128_x_128(x_xstart, y, z, yz_idx, idx, carry2, product_high, product, tmp, 0); + mr_if_needed(carry, product_high); + bdnz(L_third_loop); + + bind(L_third_loop_exit); // Handle any left-over operand parts. + + andi_(idx, idx, 0x3); + beq(CCR0, L_post_third_loop_done); + + Label L_check_1; + + addic_(idx, idx, -2); + blt(CCR0, L_check_1); + + multiply_add_128_x_128(x_xstart, y, z, yz_idx, idx, carry, product_high, product, tmp, 0); + mr_if_needed(carry, product_high); + + bind(L_check_1); + + addi(idx, idx, 0x2); + andi_(idx, idx, 0x1) ; + addic_(idx, idx, -1); + blt(CCR0, L_post_third_loop_done); + + sldi(tmp, idx, LogBytesPerInt); + lwzx(yz_idx, y, tmp); + multiply64(product_high, product, x_xstart, yz_idx); + lwzx(yz_idx, z, tmp); + + add2_with_carry(product_high, product, yz_idx, carry); + + sldi(tmp, idx, LogBytesPerInt); + stwx(product, z, tmp); + srdi(product, product, 32); + + sldi(product_high, product_high, 32); + orr(product, product, product_high); + mr_if_needed(carry, product); + + bind(L_post_third_loop_done); +} // multiply_128_x_128_loop + +void MacroAssembler::multiply_to_len(Register x, Register xlen, + Register y, Register ylen, + Register z, Register zlen, + Register tmp1, Register tmp2, + Register tmp3, Register tmp4, + Register tmp5, Register tmp6, + Register tmp7, Register tmp8, + Register tmp9, Register tmp10, + Register tmp11, Register tmp12, + Register tmp13) { + + ShortBranchVerifier sbv(this); + + assert_different_registers(x, xlen, y, ylen, z, zlen, + tmp1, tmp2, tmp3, tmp4, tmp5, tmp6); + assert_different_registers(x, xlen, y, ylen, z, zlen, + tmp1, tmp2, tmp3, tmp4, tmp5, tmp7); + assert_different_registers(x, xlen, y, ylen, z, zlen, + tmp1, tmp2, tmp3, tmp4, tmp5, tmp8); + + const Register idx = tmp1; + const Register kdx = tmp2; + const Register xstart = tmp3; + + const Register y_idx = tmp4; + const Register carry = tmp5; + const Register product = tmp6; + const Register product_high = tmp7; + const Register x_xstart = tmp8; + const Register tmp = tmp9; + + // First Loop. + // + // final static long LONG_MASK = 0xffffffffL; + // int xstart = xlen - 1; + // int ystart = ylen - 1; + // long carry = 0; + // for (int idx=ystart, kdx=ystart+1+xstart; idx >= 0; idx-, kdx--) { + // long product = (y[idx] & LONG_MASK) * (x[xstart] & LONG_MASK) + carry; + // z[kdx] = (int)product; + // carry = product >>> 32; + // } + // z[xstart] = (int)carry; + + mr_if_needed(idx, ylen); // idx = ylen + mr_if_needed(kdx, zlen); // kdx = xlen + ylen + li(carry, 0); // carry = 0 + + Label L_done; + + addic_(xstart, xlen, -1); + blt(CCR0, L_done); + + multiply_64_x_64_loop(x, xstart, x_xstart, y, y_idx, z, + carry, product_high, product, idx, kdx, tmp); + + Label L_second_loop; + + cmpdi(CCR0, kdx, 0); + beq(CCR0, L_second_loop); + + Label L_carry; + + addic_(kdx, kdx, -1); + beq(CCR0, L_carry); + + // Store lower 32 bits of carry. + sldi(tmp, kdx, LogBytesPerInt); + stwx(carry, z, tmp); + srdi(carry, carry, 32); + addi(kdx, kdx, -1); + + + bind(L_carry); + + // Store upper 32 bits of carry. + sldi(tmp, kdx, LogBytesPerInt); + stwx(carry, z, tmp); + + // Second and third (nested) loops. + // + // for (int i = xstart-1; i >= 0; i--) { // Second loop + // carry = 0; + // for (int jdx=ystart, k=ystart+1+i; jdx >= 0; jdx--, k--) { // Third loop + // long product = (y[jdx] & LONG_MASK) * (x[i] & LONG_MASK) + + // (z[k] & LONG_MASK) + carry; + // z[k] = (int)product; + // carry = product >>> 32; + // } + // z[i] = (int)carry; + // } + // + // i = xlen, j = tmp1, k = tmp2, carry = tmp5, x[i] = rdx + + bind(L_second_loop); + + li(carry, 0); // carry = 0; + + addic_(xstart, xstart, -1); // i = xstart-1; + blt(CCR0, L_done); + + Register zsave = tmp10; + + mr(zsave, z); + + + Label L_last_x; + + sldi(tmp, xstart, LogBytesPerInt); + add(z, z, tmp); // z = z + k - j + addi(z, z, 4); + addic_(xstart, xstart, -1); // i = xstart-1; + blt(CCR0, L_last_x); + + sldi(tmp, xstart, LogBytesPerInt); + ldx(x_xstart, x, tmp); +#ifdef VM_LITTLE_ENDIAN + rldicl(x_xstart, x_xstart, 32, 0); +#endif + + + Label L_third_loop_prologue; + + bind(L_third_loop_prologue); + + Register xsave = tmp11; + Register xlensave = tmp12; + Register ylensave = tmp13; + + mr(xsave, x); + mr(xlensave, xstart); + mr(ylensave, ylen); + + + multiply_128_x_128_loop(x_xstart, y, z, y_idx, ylen, + carry, product_high, product, x, tmp); + + mr(z, zsave); + mr(x, xsave); + mr(xlen, xlensave); // This is the decrement of the loop counter! + mr(ylen, ylensave); + + addi(tmp3, xlen, 1); + sldi(tmp, tmp3, LogBytesPerInt); + stwx(carry, z, tmp); + addic_(tmp3, tmp3, -1); + blt(CCR0, L_done); + + srdi(carry, carry, 32); + sldi(tmp, tmp3, LogBytesPerInt); + stwx(carry, z, tmp); + b(L_second_loop); + + // Next infrequent code is moved outside loops. + bind(L_last_x); + + lwz(x_xstart, 0, x); + b(L_third_loop_prologue); + + bind(L_done); +} // multiply_to_len void MacroAssembler::asm_assert(bool check_equal, const char *msg, int id) { #ifdef ASSERT diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/cpu/ppc/vm/macroAssembler_ppc.hpp --- a/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.hpp Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.hpp Tue Jul 14 18:58:38 2015 +0200 @@ -677,6 +677,31 @@ void char_arrays_equalsImm(Register str1_reg, Register str2_reg, int cntval, Register result_reg, Register tmp1_reg, Register tmp2_reg); + // Emitters for BigInteger.multiplyToLen intrinsic. + inline void multiply64(Register dest_hi, Register dest_lo, + Register x, Register y); + void add2_with_carry(Register dest_hi, Register dest_lo, + Register src1, Register src2); + void multiply_64_x_64_loop(Register x, Register xstart, Register x_xstart, + Register y, Register y_idx, Register z, + Register carry, Register product_high, Register product, + Register idx, Register kdx, Register tmp); + void multiply_add_128_x_128(Register x_xstart, Register y, Register z, + Register yz_idx, Register idx, Register carry, + Register product_high, Register product, Register tmp, + int offset); + void multiply_128_x_128_loop(Register x_xstart, + Register y, Register z, + Register yz_idx, Register idx, Register carry, + Register product_high, Register product, + Register carry2, Register tmp); + void multiply_to_len(Register x, Register xlen, + Register y, Register ylen, + Register z, Register zlen, + Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5, + Register tmp6, Register tmp7, Register tmp8, Register tmp9, Register tmp10, + Register tmp11, Register tmp12, Register tmp13); + // // Debugging // diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/cpu/ppc/vm/macroAssembler_ppc.inline.hpp --- a/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.inline.hpp Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/cpu/ppc/vm/macroAssembler_ppc.inline.hpp Tue Jul 14 18:58:38 2015 +0200 @@ -423,6 +423,13 @@ twi(traptoEqual | traptoGreaterThanUnsigned, a/*reg a*/, si16); } +// unsigned integer multiplication 64*64 -> 128 bits +inline void MacroAssembler::multiply64(Register dest_hi, Register dest_lo, + Register x, Register y) { + mulld(dest_lo, x, y); + mulhdu(dest_hi, x, y); +} + #if defined(ABI_ELFv2) inline address MacroAssembler::function_entry() { return pc(); } #else diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/cpu/ppc/vm/ppc.ad --- a/hotspot/src/cpu/ppc/vm/ppc.ad Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/cpu/ppc/vm/ppc.ad Tue Jul 14 18:58:38 2015 +0200 @@ -10930,7 +10930,7 @@ instruct cmpFastLock(flagsReg crx, iRegPdst oop, iRegPdst box, iRegPdst tmp1, iRegPdst tmp2, iRegPdst tmp3) %{ match(Set crx (FastLock oop box)); effect(TEMP tmp1, TEMP tmp2, TEMP tmp3); - predicate(/*(!UseNewFastLockPPC64 || UseBiasedLocking) &&*/ !Compile::current()->use_rtm()); + predicate(!Compile::current()->use_rtm()); format %{ "FASTLOCK $oop, $box, $tmp1, $tmp2, $tmp3" %} ins_encode %{ diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/cpu/ppc/vm/stubGenerator_ppc.cpp --- a/hotspot/src/cpu/ppc/vm/stubGenerator_ppc.cpp Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/cpu/ppc/vm/stubGenerator_ppc.cpp Tue Jul 14 18:58:38 2015 +0200 @@ -2053,6 +2053,79 @@ __ blr(); } + // Stub for BigInteger::multiplyToLen() + // + // Arguments: + // + // Input: + // R3 - x address + // R4 - x length + // R5 - y address + // R6 - y length + // R7 - z address + // R8 - z length + // + address generate_multiplyToLen() { + + StubCodeMark mark(this, "StubRoutines", "multiplyToLen"); + + address start = __ function_entry(); + + const Register x = R3; + const Register xlen = R4; + const Register y = R5; + const Register ylen = R6; + const Register z = R7; + const Register zlen = R8; + + const Register tmp1 = R2; // TOC not used. + const Register tmp2 = R9; + const Register tmp3 = R10; + const Register tmp4 = R11; + const Register tmp5 = R12; + + // non-volatile regs + const Register tmp6 = R31; + const Register tmp7 = R30; + const Register tmp8 = R29; + const Register tmp9 = R28; + const Register tmp10 = R27; + const Register tmp11 = R26; + const Register tmp12 = R25; + const Register tmp13 = R24; + + BLOCK_COMMENT("Entry:"); + + // Save non-volatile regs (frameless). + int current_offs = 8; + __ std(R24, -current_offs, R1_SP); current_offs += 8; + __ std(R25, -current_offs, R1_SP); current_offs += 8; + __ std(R26, -current_offs, R1_SP); current_offs += 8; + __ std(R27, -current_offs, R1_SP); current_offs += 8; + __ std(R28, -current_offs, R1_SP); current_offs += 8; + __ std(R29, -current_offs, R1_SP); current_offs += 8; + __ std(R30, -current_offs, R1_SP); current_offs += 8; + __ std(R31, -current_offs, R1_SP); + + __ multiply_to_len(x, xlen, y, ylen, z, zlen, tmp1, tmp2, tmp3, tmp4, tmp5, + tmp6, tmp7, tmp8, tmp9, tmp10, tmp11, tmp12, tmp13); + + // Restore non-volatile regs. + current_offs = 8; + __ ld(R24, -current_offs, R1_SP); current_offs += 8; + __ ld(R25, -current_offs, R1_SP); current_offs += 8; + __ ld(R26, -current_offs, R1_SP); current_offs += 8; + __ ld(R27, -current_offs, R1_SP); current_offs += 8; + __ ld(R28, -current_offs, R1_SP); current_offs += 8; + __ ld(R29, -current_offs, R1_SP); current_offs += 8; + __ ld(R30, -current_offs, R1_SP); current_offs += 8; + __ ld(R31, -current_offs, R1_SP); + + __ blr(); // Return to caller. + + return start; + } + // Initialization void generate_initial() { // Generates all stubs and initializes the entry points @@ -2102,6 +2175,12 @@ generate_safefetch("SafeFetchN", sizeof(intptr_t), &StubRoutines::_safefetchN_entry, &StubRoutines::_safefetchN_fault_pc, &StubRoutines::_safefetchN_continuation_pc); + +#ifdef COMPILER2 + if (UseMultiplyToLenIntrinsic) { + StubRoutines::_multiplyToLen = generate_multiplyToLen(); + } +#endif } public: diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp --- a/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/cpu/ppc/vm/vm_version_ppc.cpp Tue Jul 14 18:58:38 2015 +0200 @@ -198,6 +198,10 @@ FLAG_SET_DEFAULT(UseCRC32CIntrinsics, false); } + if (FLAG_IS_DEFAULT(UseMultiplyToLenIntrinsic)) { + UseMultiplyToLenIntrinsic = true; + } + // Adjust RTM (Restricted Transactional Memory) flags. if (!has_tcheck() && UseRTMLocking) { // Can't continue because UseRTMLocking affects UseBiasedLocking flag @@ -228,7 +232,6 @@ warning("RTMAbortRatio must be in the range 0 to 100, resetting it to 50"); FLAG_SET_DEFAULT(RTMAbortRatio, 50); } - FLAG_SET_ERGO(bool, UseNewFastLockPPC64, false); // Does not implement TM. guarantee(RTMSpinLoopCount > 0, "unsupported"); #else // Only C2 does RTM locking optimization. diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp --- a/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/cpu/sparc/vm/vm_version_sparc.cpp Tue Jul 14 18:58:38 2015 +0200 @@ -308,7 +308,7 @@ } } else if (UseGHASHIntrinsics) { if (!FLAG_IS_DEFAULT(UseGHASHIntrinsics)) - warning("GHASH intrinsics require VIS3 insructions support. Intriniscs will be disabled"); + warning("GHASH intrinsics require VIS3 instruction support. Intrinsics will be disabled"); FLAG_SET_DEFAULT(UseGHASHIntrinsics, false); } diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/tools/LogCompilation/Makefile --- a/hotspot/src/share/tools/LogCompilation/Makefile Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/tools/LogCompilation/Makefile Tue Jul 14 18:58:38 2015 +0200 @@ -1,5 +1,5 @@ # -# Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2009, 2015, 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 @@ -62,7 +62,7 @@ logc.jar: filelist manifest.mf @mkdir -p $(OUTPUT_DIR) - $(JAVAC) -source 1.5 -deprecation -sourcepath $(SRC_DIR) -d $(OUTPUT_DIR) @filelist + $(JAVAC) -deprecation -sourcepath $(SRC_DIR) -d $(OUTPUT_DIR) @filelist $(JAR) cvfm logc.jar manifest.mf -C $(OUTPUT_DIR) com .PHONY: filelist diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/BasicLogEvent.java --- a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/BasicLogEvent.java Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/BasicLogEvent.java Tue Jul 14 18:58:38 2015 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2015, 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 @@ -27,14 +27,29 @@ import java.io.PrintStream; /** - * - * @author never + * Provide basic data structures and behaviour for {@link LogEvent}s. */ public abstract class BasicLogEvent implements LogEvent { + /** + * The event's ID. This is a number; we represent it as a string for + * convenience. + */ protected final String id; + + /** + * The event's start time. + */ protected final double start; + + /** + * The event's end time. + */ protected double end; + + /** + * The compilation during which this event was signalled. + */ protected Compilation compilation; BasicLogEvent(double start, String id) { @@ -43,33 +58,37 @@ this.id = id; } - public double getStart() { + public final double getStart() { return start; } - public double getEnd() { + public final double getEnd() { return end; } - public void setEnd(double end) { + public final void setEnd(double end) { this.end = end; } - public double getElapsedTime() { + public final double getElapsedTime() { return ((int) ((getEnd() - getStart()) * 1000)) / 1000.0; } - public String getId() { + public final String getId() { return id; } - public Compilation getCompilation() { + public final Compilation getCompilation() { return compilation; } + /** + * Set the compilation for this event. This is not a {@code final} method + * as it is overridden in {@link UncommonTrapEvent}. + */ public void setCompilation(Compilation compilation) { this.compilation = compilation; } - abstract public void print(PrintStream stream); + abstract public void print(PrintStream stream, boolean printID); } diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/CallSite.java --- a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/CallSite.java Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/CallSite.java Tue Jul 14 18:58:38 2015 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2015, 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 @@ -29,41 +29,119 @@ import java.util.ArrayList; import java.util.List; +/** + * Representation of a compilation scope in a compilation log. This class is a + * hybrid: its instances can represent original scopes of methods being + * compiled, but are also used to represent call sites in given methods. + */ public class CallSite { + /** + * The index of the call in the caller. This will be 0 if this instance + * represents a compilation root. + */ private int bci; + + /** + * The method that is called at this call site. This will be {@code null} + * if this instance represents a compilation root. + */ private Method method; + + /** + * The invocation count for this call site. + */ private int count; + + /** + * The receiver type of the call represented by this instance, if known. + */ private String receiver; + + /** + * In case the {@linkplain receiver receiver type} of the call represented + * by this instance is known, this is how often the type was encountered. + */ private int receiver_count; + + /** + * The reason for a success or failure of an inlining operation at this + * call site. + */ private String reason; + + /** + * A list of all calls in this compilation scope. + */ private List calls; + + /** + * Number of nodes in the graph at the end of parsing this compilation + * scope. + */ private int endNodes; + + /** + * Number of live nodes in the graph at the end of parsing this compilation + * scope. + */ private int endLiveNodes; + + /** + * Time in seconds since VM startup at which parsing this compilation scope + * ended. + */ private double timeStamp; + + /** + * The inline ID in case the call represented by this instance is inlined, + * 0 otherwise. + */ private long inlineId; - CallSite() { - } + /** + * List of uncommon traps in this compilation scope. + */ + private List traps; + /** + * Default constructor: used to create an instance that represents the top + * scope of a compilation. + */ + CallSite() {} + + /** + * Constructor to create an instance that represents an actual method call. + */ CallSite(int bci, Method m) { this.bci = bci; this.method = m; } + /** + * Add a call site to the compilation scope represented by this instance. + */ void add(CallSite site) { if (getCalls() == null) { - setCalls(new ArrayList()); + calls = new ArrayList<>(); } getCalls().add(site); } + /** + * Return the last of the {@linkplain #getCalls() call sites} in this + * compilation scope. + */ CallSite last() { - return last(-1); + return getCalls().get(getCalls().size() - 1); } - CallSite last(int fromEnd) { - return getCalls().get(getCalls().size() + fromEnd); + /** + * Return the last-but-one of the {@linkplain #getCalls() call sites} in + * this compilation scope. + */ + CallSite lastButOne() { + return getCalls().get(getCalls().size() - 2); } public String toString() { @@ -84,7 +162,7 @@ } public void print(PrintStream stream) { - print(stream, 0); + print(stream, 0, true, false); } void emit(PrintStream stream, int indent) { @@ -92,21 +170,14 @@ stream.print(' '); } } - private static boolean compat = true; - public void print(PrintStream stream, int indent) { + public void print(PrintStream stream, int indent, boolean printInlining, boolean printUncommonTraps) { emit(stream, indent); String m = getMethod().getHolder() + "::" + getMethod().getName(); if (getReason() == null) { stream.print(" @ " + getBci() + " " + m + " (" + getMethod().getBytes() + " bytes)"); - } else { - if (isCompat()) { - stream.print(" @ " + getBci() + " " + m + " " + getReason()); - } else { - stream.print("- @ " + getBci() + " " + m + - " (" + getMethod().getBytes() + " bytes) " + getReason()); - } + stream.print(" @ " + getBci() + " " + m + " " + getReason()); } stream.printf(" (end time: %6.4f", getTimeStamp()); if (getEndNodes() > 0) { @@ -116,13 +187,16 @@ if (getReceiver() != null) { emit(stream, indent + 4); - // stream.println("type profile " + method.holder + " -> " + receiver + " (" + - // receiver_count + "/" + count + "," + (receiver_count * 100 / count) + "%)"); stream.println("type profile " + getMethod().getHolder() + " -> " + getReceiver() + " (" + (getReceiverCount() * 100 / getCount()) + "%)"); } - if (getCalls() != null) { + if (printInlining && getCalls() != null) { for (CallSite site : getCalls()) { + site.print(stream, indent + 2, printInlining, printUncommonTraps); + } + } + if (printUncommonTraps && getTraps() != null) { + for (UncommonTrap site : getTraps()) { site.print(stream, indent + 2); } } @@ -180,16 +254,15 @@ return calls; } - public void setCalls(List calls) { - this.calls = calls; + public List getTraps() { + return traps; } - public static boolean isCompat() { - return compat; - } - - public static void setCompat(boolean aCompat) { - compat = aCompat; + void add(UncommonTrap e) { + if (traps == null) { + traps = new ArrayList(); + } + traps.add(e); } void setEndNodes(int n) { @@ -216,21 +289,30 @@ return timeStamp; } + /** + * Check whether this call site matches another. Every late inline call + * site has a unique inline ID. If the call site we're looking for has one, + * then use it; otherwise rely on method name and byte code index. + */ private boolean matches(CallSite other) { - // Every late inline call site has a unique inline id. If the - // call site we're looking for has one then use it other rely - // on method name and bci. if (other.inlineId != 0) { return inlineId == other.inlineId; } return method.equals(other.method) && bci == other.bci; } + /** + * Locate a late inline call site: find, in this instance's + * {@linkplain #calls call sites}, the one furthest down the given call + * stack. + * + * Multiple chains of identical call sites with the same method name / bci + * combination are possible, so we have to try them all until we find the + * late inline call site that has a matching inline ID. + * + * @return a matching call site, or {@code null} if none was found. + */ public CallSite findCallSite(ArrayDeque sites) { - // Locate a late inline call site. Multiple chains of - // identical call sites with the same method name/bci are - // possible so we have to try them all until we find the late - // inline call site that has a matching inline id. if (calls == null) { return null; } @@ -253,6 +335,11 @@ return null; } + /** + * Locate a late inline call site in the tree spanned by all this instance's + * {@linkplain #calls call sites}, and return the sequence of call sites + * (scopes) leading to that late inline call site. + */ public ArrayDeque findCallSite2(CallSite site) { if (calls == null) { return null; @@ -260,7 +347,7 @@ for (CallSite c : calls) { if (c.matches(site)) { - ArrayDeque stack = new ArrayDeque(); + ArrayDeque stack = new ArrayDeque<>(); stack.push(c); return stack; } else { diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/Compilation.java --- a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/Compilation.java Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/Compilation.java Tue Jul 14 18:58:38 2015 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2015, 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 @@ -27,22 +27,94 @@ import java.io.PrintStream; import java.util.ArrayList; +/** + * One particular compilation, represented in the compilation log file as a + * {@code task} element. + */ public class Compilation implements LogEvent { + /** + * The compilation ID. + */ private int id; + + /** + * Whether this is a compilation for on-stack replacement (OSR). + */ private boolean osr; + + /** + * The method being compiled. + */ private Method method; + + /** + * The {@linkplain CallSite scope} of this compilation. This is created as + * an empty {@link CallSite} instance, to be filled with data (and + * meaning) later on. + */ private CallSite call = new CallSite(); + + /** + * In case a {@code late_inline} event occurs during the compilation, this + * field holds the information about it. + */ private CallSite lateInlineCall = new CallSite(); - private int osrBci; + + /** + * The bytecode instruction index for on-stack replacement compilations; -1 + * if this is not an OSR compilation. + */ + private int bci; + + /** + * The method under compilation's invocation count. + */ private String icount; + + /** + * The method under compilation's backedge count. + */ private String bcount; + + /** + * Additional information for special compilations (e.g., adapters). + */ private String special; + + /** + * The name of the compiler performing this compilation. + */ + private String compiler; + + /** + * Start time stamp. + */ private double start; + + /** + * End time stamp. + */ private double end; + + /** + * Trip count of the register allocator. + */ private int attempts; + + /** + * The compilation result (a native method). + */ private NMethod nmethod; - private ArrayList phases = new ArrayList(4); + + /** + * The phases through which this compilation goes. + */ + private ArrayList phases = new ArrayList<>(4); + + /** + * In case this compilation fails, the reason for that. + */ private String failureReason; Compilation(int id) { @@ -52,9 +124,17 @@ void reset() { call = new CallSite(); lateInlineCall = new CallSite(); - phases = new ArrayList(4); + phases = new ArrayList<>(4); } + /** + * Get a compilation phase by name, or {@code null}. + * + * @param s the name of the phase to retrieve in this compilation. + * + * @return a compilation phase, or {@code null} if no phase with the given + * name is found. + */ Phase getPhase(String s) { for (Phase p : getPhases()) { if (p.getName().equals(s)) { @@ -72,20 +152,32 @@ return start; } + public void setCompiler(String compiler) { + this.compiler = compiler; + } + + public String getCompiler() { + return compiler; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getId()); sb.append(" "); + sb.append(getCompiler()); + sb.append(" "); sb.append(getMethod()); sb.append(" "); sb.append(getIcount()); sb.append("+"); sb.append(getBcount()); sb.append("\n"); - for (CallSite site : getCall().getCalls()) { - sb.append(site); - sb.append("\n"); + if (getCall() != null && getCall().getCalls() != null) { + for (CallSite site : getCall().getCalls()) { + sb.append(site); + sb.append("\n"); + } } if (getLateInlineCall().getCalls() != null) { sb.append("late inline:\n"); @@ -101,38 +193,50 @@ if (getMethod() == null) { stream.println(getSpecial()); } else { - int bc = isOsr() ? getOsr_bci() : -1; - stream.print(getId() + getMethod().decodeFlags(bc) + getMethod().format(bc)); + int bc = isOsr() ? getBCI() : -1; + stream.print(getId() + getMethod().decodeFlags(bc) + " " + compiler + " " + getMethod().format(bc)); } } - public void print(PrintStream stream) { - print(stream, 0, false); + public void print(PrintStream stream, boolean printID) { + print(stream, 0, printID, true, false); } - public void print(PrintStream stream, boolean printInlining) { - print(stream, 0, printInlining); + public void print(PrintStream stream, boolean printID, boolean printInlining) { + print(stream, 0, printID, printInlining, false); } - public void print(PrintStream stream, int indent, boolean printInlining) { + public void print(PrintStream stream, boolean printID, boolean printInlining, boolean printUncommonTraps) { + print(stream, 0, printID, printInlining, printUncommonTraps); + } + + public void print(PrintStream stream, int indent, boolean printID, boolean printInlining, boolean printUncommonTraps) { if (getMethod() == null) { stream.println(getSpecial()); } else { - int bc = isOsr() ? getOsr_bci() : -1; - stream.print(getId() + getMethod().decodeFlags(bc) + getMethod().format(bc)); + if (printID) { + stream.print(getId()); + } + int bc = isOsr() ? getBCI() : -1; + stream.print(getMethod().decodeFlags(bc) + " " + compiler + " " + getMethod().format(bc)); stream.println(); if (getFailureReason() != null) { - stream.println("COMPILE FAILED " + getFailureReason()); + stream.println("COMPILE SKIPPED: " + getFailureReason() + " (not retryable)"); } if (printInlining && call.getCalls() != null) { for (CallSite site : call.getCalls()) { + site.print(stream, indent + 2, printInlining, printUncommonTraps); + } + } + if (printUncommonTraps && call.getTraps() != null) { + for (UncommonTrap site : call.getTraps()) { site.print(stream, indent + 2); } } if (printInlining && lateInlineCall.getCalls() != null) { stream.println("late inline:"); for (CallSite site : lateInlineCall.getCalls()) { - site.print(stream, indent + 2); + site.print(stream, indent + 2, printInlining, printUncommonTraps); } } } @@ -154,12 +258,12 @@ this.osr = osr; } - public int getOsr_bci() { - return osrBci; + public int getBCI() { + return bci; } - public void setOsr_bci(int osrBci) { - this.osrBci = osrBci; + public void setBCI(int osrBci) { + this.bci = osrBci; } public String getIcount() { @@ -230,9 +334,13 @@ return method; } + /** + * Set the method under compilation. If it is already set, ignore the + * argument to avoid changing the method by post-parse inlining info. + * + * @param method the method under compilation. May be ignored. + */ public void setMethod(Method method) { - // Don't change method if it is already set to avoid changing - // it by post parse inlining info. if (getMethod() == null) { this.method = method; } diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/LogCleanupReader.java --- a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/LogCleanupReader.java Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/LogCleanupReader.java Tue Jul 14 18:58:38 2015 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2015, 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 @@ -31,10 +31,9 @@ * This class is a filter class to deal with malformed XML that used * to be produced by the JVM when generating LogCompilation. In 1.6 * and later releases it shouldn't be required. - * @author never */ +class LogCleanupReader extends Reader { -class LogCleanupReader extends Reader { private Reader reader; private char[] buffer = new char[4096]; @@ -55,32 +54,38 @@ reader = r; } - static final private Matcher pattern = Pattern.compile(".+ compile_id='[0-9]+'.*( compile_id='[0-9]+)").matcher(""); - static final private Matcher pattern2 = Pattern.compile("' (C[12]) compile_id=").matcher(""); - static final private Matcher pattern3 = Pattern.compile("'(destroy_vm)/").matcher(""); + static final private Matcher duplicateCompileID = Pattern.compile(".+ compile_id='[0-9]+'.*( compile_id='[0-9]+)").matcher(""); + static final private Matcher compilerName = Pattern.compile("' (C[12]) compile_id=").matcher(""); + static final private Matcher destroyVM = Pattern.compile("'(destroy_vm)/").matcher(""); + /** + * The log cleanup takes place in this method. If any of the three patterns + * ({@link #duplicateCompileID}, {@link #compilerName}, {@link #destroyVM}) + * match, that indicates a problem in the log. The cleanup is performed by + * correcting the input line and writing it back into the {@link #line} + * buffer. + */ private void fill() throws IOException { rawFill(); if (length != -1) { boolean changed = false; String s = new String(line, 0, length); - String orig = s; - pattern2.reset(s); - if (pattern2.find()) { - s = s.substring(0, pattern2.start(1)) + s.substring(pattern2.end(1) + 1); + compilerName.reset(s); + if (compilerName.find()) { + s = s.substring(0, compilerName.start(1)) + s.substring(compilerName.end(1) + 1); changed = true; } - pattern.reset(s); - if (pattern.lookingAt()) { - s = s.substring(0, pattern.start(1)) + s.substring(pattern.end(1) + 1); + duplicateCompileID.reset(s); + if (duplicateCompileID.lookingAt()) { + s = s.substring(0, duplicateCompileID.start(1)) + s.substring(duplicateCompileID.end(1) + 1); changed = true; } - pattern3.reset(s); - if (pattern3.find()) { - s = s.substring(0, pattern3.start(1)) + s.substring(pattern3.end(1)); + destroyVM.reset(s); + if (destroyVM.find()) { + s = s.substring(0, destroyVM.start(1)) + s.substring(destroyVM.end(1)); changed = true; } diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/LogCompilation.java --- a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/LogCompilation.java Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/LogCompilation.java Tue Jul 14 18:58:38 2015 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2015, 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 @@ -22,60 +22,102 @@ * */ -/** - * The main command line driver of a parser for LogCompilation output. - * @author never - */ - package com.sun.hotspot.tools.compiler; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.PrintStream; import java.util.*; + import org.xml.sax.*; import org.xml.sax.helpers.*; -public class LogCompilation extends DefaultHandler implements ErrorHandler, Constants { +/** + * The LogCompilation tool parses log files generated by HotSpot using the + * {@code -XX:+LogCompilation} command line flag, and outputs the data + * collected therein in a nicely formatted way. There are various sorting + * options available, as well as options that select specific compilation + * events (such as inlining decisions) for inclusion in the output. + * + * The tool is also capable of fixing broken compilation logs as sometimes + * generated by Java 1.5 JVMs. + */ +public class LogCompilation extends DefaultHandler implements ErrorHandler { + /** + * Print usage information and terminate with a given exit code. + */ public static void usage(int exitcode) { System.out.println("Usage: LogCompilation [ -v ] [ -c ] [ -s ] [ -e | -n ] file1 ..."); + System.out.println("By default, the tool will print the logged compilations ordered by start time."); System.out.println(" -c: clean up malformed 1.5 xml"); System.out.println(" -i: print inlining decisions"); System.out.println(" -S: print compilation statistics"); - System.out.println(" -s: sort events by start time"); + System.out.println(" -U: print uncommon trap statistics"); + System.out.println(" -t: print with time stamps"); + System.out.println(" -s: sort events by start time (default)"); System.out.println(" -e: sort events by elapsed time"); System.out.println(" -n: sort events by name and start"); + System.out.println(" -C: compare logs (give files to compare on command line)"); + System.out.println(" -d: do not print compilation IDs"); System.exit(exitcode); } + /** + * Process command line arguments, parse log files and trigger desired + * functionality. + */ public static void main(String[] args) throws Exception { - Comparator defaultSort = LogParser.sortByStart; + Comparator sort = LogParser.sortByStart; boolean statistics = false; boolean printInlining = false; boolean cleanup = false; + boolean trapHistory = false; + boolean printTimeStamps = false; + boolean compare = false; + boolean printID = true; int index = 0; while (args.length > index) { - if (args[index].equals("-e")) { - defaultSort = LogParser.sortByElapsed; + String a = args[index]; + if (a.equals("-e")) { + sort = LogParser.sortByElapsed; + index++; + } else if (a.equals("-n")) { + sort = LogParser.sortByNameAndStart; index++; - } else if (args[index].equals("-n")) { - defaultSort = LogParser.sortByNameAndStart; + } else if (a.equals("-s")) { + sort = LogParser.sortByStart; index++; - } else if (args[index].equals("-s")) { - defaultSort = LogParser.sortByStart; + } else if (a.equals("-t")) { + printTimeStamps = true; index++; - } else if (args[index].equals("-c")) { + } else if (a.equals("-c")) { cleanup = true; index++; - } else if (args[index].equals("-S")) { + } else if (a.equals("-S")) { statistics = true; index++; - } else if (args[index].equals("-h")) { + } else if (a.equals("-U")) { + trapHistory = true; + index++; + } else if (a.equals("-h")) { usage(0); - } else if (args[index].equals("-i")) { + } else if (a.equals("-i")) { printInlining = true; index++; + } else if (a.equals("-C")) { + compare = true; + index++; + } else if (a.equals("-d")) { + printID = false; + index++; } else { + if (a.charAt(0) == '-') { + System.out.println("Unknown option '" + a + "', assuming file name."); + } break; } } @@ -84,19 +126,40 @@ usage(1); } + if (compare) { + compareLogs(index, args); + return; + } + while (index < args.length) { - ArrayList events = LogParser.parse(args[index], cleanup); + ArrayList events = null; + try { + events = LogParser.parse(args[index], cleanup); + } catch (FileNotFoundException fnfe) { + System.out.println("File not found: " + args[index]); + System.exit(1); + } + + Collections.sort(events, sort); if (statistics) { printStatistics(events, System.out); + } else if (trapHistory) { + printTrapHistory(events, System.out); } else { - Collections.sort(events, defaultSort); for (LogEvent c : events) { - if (printInlining && c instanceof Compilation) { - Compilation comp = (Compilation)c; - comp.print(System.out, true); + if (c instanceof NMethod) { + // skip these + continue; + } + if (printTimeStamps) { + System.out.print(c.getStart() + ": "); + } + if (c instanceof Compilation) { + Compilation comp = (Compilation) c; + comp.print(System.out, printID, printInlining); } else { - c.print(System.out); + c.print(System.out, printID); } } } @@ -104,17 +167,25 @@ } } + /** + * Print extensive statistics from parsed log files. + */ public static void printStatistics(ArrayList events, PrintStream out) { + // track code cache size long cacheSize = 0; long maxCacheSize = 0; + // track number of nmethods int nmethodsCreated = 0; int nmethodsLive = 0; + // track how many compilations were attempted multiple times + // (indexed by attempts, mapping to number of compilations) int[] attempts = new int[32]; - double regallocTime = 0; int maxattempts = 0; - LinkedHashMap phaseTime = new LinkedHashMap(7); - LinkedHashMap phaseNodes = new LinkedHashMap(7); + // track time spent in compiler phases + LinkedHashMap phaseTime = new LinkedHashMap<>(7); + // track nodes created per phase + LinkedHashMap phaseNodes = new LinkedHashMap<>(7); double elapsed = 0; for (LogEvent e : events) { @@ -137,18 +208,17 @@ v2 = Integer.valueOf(0); } phaseNodes.put(phase.getName(), Integer.valueOf(v2.intValue() + phase.getNodes())); - /* Print phase name, elapsed time, nodes at the start of the phase, - nodes created in the phase, live nodes at the start of the phase, - live nodes added in the phase. - */ - out.printf("\t%s %6.4f %d %d %d %d\n", phase.getName(), phase.getElapsedTime(), phase.getStartNodes(), phase.getNodes(), phase.getStartLiveNodes(), phase.getLiveNodes()); + // Print phase name, elapsed time, nodes at the start of + // the phase, nodes created in the phase, live nodes at the + // start of the phase, live nodes added in the phase. + out.printf("\t%s %6.4f %d %d %d %d\n", phase.getName(), phase.getElapsedTime(), phase.getStartNodes(), phase.getNodes(), phase.getStartLiveNodes(), phase.getAddedLiveNodes()); } } else if (e instanceof MakeNotEntrantEvent) { MakeNotEntrantEvent mne = (MakeNotEntrantEvent) e; NMethod nm = mne.getNMethod(); if (mne.isZombie()) { if (nm == null) { - System.err.println(mne.getId()); + System.err.println("zombie make not entrant event without nmethod: " + mne.getId()); } cacheSize -= nm.getSize(); nmethodsLive--; @@ -161,8 +231,7 @@ maxCacheSize = Math.max(cacheSize, maxCacheSize); } } - out.printf("NMethods: %d created %d live %d bytes (%d peak) in the code cache\n", - nmethodsCreated, nmethodsLive, cacheSize, maxCacheSize); + out.printf("NMethods: %d created %d live %d bytes (%d peak) in the code cache\n", nmethodsCreated, nmethodsLive, cacheSize, maxCacheSize); out.println("Phase times:"); for (String name : phaseTime.keySet()) { Double v = phaseTime.get(name); @@ -178,4 +247,265 @@ } } } + + /** + * Container class for a pair of a method and a bytecode instruction index + * used by a compiler. This is used in + * {@linkplain #compareLogs() comparing logs}. + */ + static class MethodBCIPair { + public MethodBCIPair(Method m, int b, String c) { + method = m; + bci = b; + compiler = c; + } + + Method method; + int bci; + String compiler; + + public boolean equals(Object other) { + if (!(other instanceof MethodBCIPair)) { + return false; + } + MethodBCIPair otherp = (MethodBCIPair)other; + return (otherp.bci == bci && + otherp.method.equals(method) && + otherp.compiler.equals(compiler)); + } + + public int hashCode() { + return method.hashCode() + bci; + } + + public String toString() { + if (bci != -1) { + return method + "@" + bci + " (" + compiler + ")"; + } else { + return method + " (" + compiler + ")"; + } + } + } + + /** + * Compare a number of compilation log files. Each of the logs is parsed, + * and all compilations found therein are written to a sorted file (prefix + * {@code sorted-}. A summary is written to a new file {@code summary.txt}. + * + * @param index the index in the command line arguments at which to start + * looking for files to compare. + * @param args the command line arguments with which {@link LogCompilation} + * was originally invoked. + * + * @throws Exception in case any exceptions are thrown in the called + * methods. + */ + @SuppressWarnings("unchecked") + static void compareLogs(int index, String[] args) throws Exception { + HashMap methods = new HashMap<>(); + ArrayList> logs = new ArrayList<>(); + PrintStream[] outs = new PrintStream[args.length - index]; + PrintStream summary = new PrintStream(new FileOutputStream("summary.txt")); + int o = 0; + // Process all logs given on the command line: collect compilation + // data; in particular, method/bci pairs. + while (index < args.length) { + String basename = new File(args[index]).getName(); + String outname = "sorted-" + basename; + System.out.println("Sorting " + basename + " to " + outname); + outs[o] = new PrintStream(new FileOutputStream(outname)); + o++; + System.out.println("Parsing " + args[index]); + ArrayList events = LogParser.parse(args[index], false); + HashMap compiles = new HashMap<>(); + logs.add(compiles); + for (LogEvent c : events) { + if (c instanceof Compilation) { + Compilation comp = (Compilation) c; + MethodBCIPair key = new MethodBCIPair(comp.getMethod(), comp.getBCI(), + comp.getCompiler()); + MethodBCIPair e = methods.get(key); + if (e == null) { + methods.put(key, key); + } else { + key = e; + } + Object other = compiles.get(key); + if (other == null) { + compiles.put(key, comp); + } else { + if (!(other instanceof List)) { + List l = new LinkedList<>(); + l.add(other); + l.add(comp); + compiles.put(key, l); + } else { + List l = (List) other; + l.add(comp); + } + } + } + } + index++; + } + + // Process the collected method/bci pairs and write the output. + for (MethodBCIPair pair : methods.keySet()) { + summary.print(pair + " "); + int base = -1; + String first = null; + boolean mismatch = false; + boolean different = false; + String[] output = new String[outs.length]; + o = 0; + for (HashMap set : logs) { + Object e = set.get(pair); + String thisone = null; + Compilation lastc = null; + int n; + if (e == null) { + n = 0; + } else if (e instanceof Compilation) { + n = 1; + lastc = (Compilation) e; + } else { + // Compare the last compilation that was done for this method + n = ((List) e).size(); + lastc = (Compilation) ((List) e).get(n - 1); + } + if (lastc != null) { + n = 1; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintStream ps = new PrintStream(baos); + lastc.print(ps, false); + ps.close(); + thisone = new String(baos.toByteArray()); + } + if (base == -1) { + base = n; + } else if (base != n) { + mismatch = true; + } + output[o++] = thisone; + if (thisone != null) { + if (first == null) { + first = thisone; + } else { + if (!first.equals(thisone)) { + different = true; + } + } + } + if (different) { + summary.print(n + "d "); + } else { + summary.print(n + " "); + } + } + if (mismatch) { + summary.print("mismatch"); + } + summary.println(); + if (different) { + for (int i = 0; i < outs.length; i++) { + if (output[i] != null) { + outs[i].println(output[i]); + } + } + } + } + for (int i = 0; i < outs.length; i++) { + outs[i].close(); + } + if (summary != System.out) { + summary.close(); + } + } + + /** + * Print the history of uncommon trap events. + */ + public static void printTrapHistory(ArrayList events, PrintStream out) { + // map method names to a list of log events + LinkedHashMap> traps = new LinkedHashMap<>(); + // map compilation IDs to compilations + HashMap comps = new HashMap<>(); + + // First, iterate over all logged events, collecting data about + // uncommon trap events. + for (LogEvent e : events) { + if (e instanceof NMethod) { + // skip these + continue; + } + if (e instanceof Compilation) { + Compilation c = (Compilation) e; + String name = c.getMethod().getFullName(); + ArrayList elist = traps.get(name); + if (elist != null && comps.get(c.getId()) == null) { + comps.put(c.getId(), c); + // If there were previous events for the method + // then keep track of later compiles too. + elist.add(c); + } + continue; + } + if (e instanceof BasicLogEvent) { + BasicLogEvent ble = (BasicLogEvent) e; + Compilation c = ble.getCompilation(); + if (c == null) { + if (!(ble instanceof NMethod)) { + throw new InternalError("only nmethods should have a null compilation; here's a " + ble.getClass()); + } + continue; + } + String name = c.getMethod().getFullName(); + ArrayList elist = traps.get(name); + if (elist == null) { + elist = new ArrayList(); + traps.put(name, elist); + } + int bleId = Integer.parseInt(ble.getId()); + if (comps.get(bleId) == null) { + comps.put(bleId, c); + // Add the associated compile to the list. It + // will likely go at the end but we need to search + // backwards for the proper insertion point. + double start = c.getStart(); + int ipoint = 0; + while (ipoint < elist.size() && elist.get(ipoint).getStart() < start) { + ipoint++; + } + if (ipoint == elist.size()) { + elist.add(c); + } else { + elist.add(ipoint, c); + } + } + elist.add(ble); + } + } + + // Second, iterate over collected traps and output information. + for (String c: traps.keySet()) { + ArrayList elist = traps.get(c); + String name = ((Compilation) elist.get(0)).getMethod().getFullName(); + System.out.println(name); + double start = 0; + for (LogEvent e: elist) { + if (start > e.getStart() && e.getStart() != 0) { + throw new InternalError("wrong sorting order for traps"); + } + start = e.getStart(); + out.print(e.getStart() + ": "); + if (e instanceof Compilation) { + ((Compilation) e).print(out, true, true, true); + } else { + e.print(out, true); + } + } + out.println(); + } + } + } diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/LogEvent.java --- a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/LogEvent.java Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/LogEvent.java Tue Jul 14 18:58:38 2015 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2015, 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 @@ -25,14 +25,31 @@ package com.sun.hotspot.tools.compiler; import java.io.PrintStream; -import java.util.*; +/** + * The interface of an event from a HotSpot compilation log. Events can have a + * duration, e.g., a compiler {@link Phase} is an event, and so is an entire + * {@link Compilation}. + */ public interface LogEvent { + + /** + * The event's start time. + */ public double getStart(); + /** + * The event's duration in milliseconds. + */ public double getElapsedTime(); + /** + * The compilation during which this event was signalled. + */ public Compilation getCompilation(); - public void print(PrintStream stream); + /** + * Print the event to the given stream. + */ + public void print(PrintStream stream, boolean printID); } diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/LogParser.java --- a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/LogParser.java Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/LogParser.java Tue Jul 14 18:58:38 2015 +0200 @@ -33,30 +33,269 @@ import java.io.Reader; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; +import java.util.Deque; import java.util.HashMap; import java.util.LinkedHashMap; -import java.util.Stack; +import java.util.regex.Pattern; + import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; + import org.xml.sax.Attributes; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; +import org.xml.sax.Locator; import org.xml.sax.helpers.DefaultHandler; -public class LogParser extends DefaultHandler implements ErrorHandler, Constants { +/** + * A SAX parser for HotSpot compilation logs. The bulk of the parsing and event + * maintenance work is done in the {@link #startElement(String,String,String,Attributes)} + * and {@link #endElement(String,String,String)} methods. + */ +public class LogParser extends DefaultHandler implements ErrorHandler { + + static final Pattern spacePattern = Pattern.compile(" "); - static final HashMap typeMap; + /** + * Map internal array type descriptors to Java names. + */ + static final HashMap type2printableMap; + + /** + * Map Java primitive type names to internal type descriptors. + */ + static final HashMap type2vmtypeMap; + static { - typeMap = new HashMap(); - typeMap.put("[I", "int[]"); - typeMap.put("[C", "char[]"); - typeMap.put("[Z", "boolean[]"); - typeMap.put("[L", "Object[]"); - typeMap.put("[B", "byte[]"); + type2printableMap = new HashMap<>(); + type2printableMap.put("[I", "int[]"); + type2printableMap.put("[C", "char[]"); + type2printableMap.put("[Z", "boolean[]"); + type2printableMap.put("[L", "Object[]"); + type2printableMap.put("[B", "byte[]"); + + type2vmtypeMap = new HashMap<>(); + type2vmtypeMap.put("void", "V"); + type2vmtypeMap.put("boolean", "Z"); + type2vmtypeMap.put("byte", "B"); + type2vmtypeMap.put("char", "C"); + type2vmtypeMap.put("short", "S"); + type2vmtypeMap.put("int", "I"); + type2vmtypeMap.put("long", "J"); + type2vmtypeMap.put("float", "F"); + type2vmtypeMap.put("double", "D"); } + static String[] bytecodes = new String[] { + "nop", + "aconst_null", + "iconst_m1", + "iconst_0", + "iconst_1", + "iconst_2", + "iconst_3", + "iconst_4", + "iconst_5", + "lconst_0", + "lconst_1", + "fconst_0", + "fconst_1", + "fconst_2", + "dconst_0", + "dconst_1", + "bipush", + "sipush", + "ldc", + "ldc_w", + "ldc2_w", + "iload", + "lload", + "fload", + "dload", + "aload", + "iload_0", + "iload_1", + "iload_2", + "iload_3", + "lload_0", + "lload_1", + "lload_2", + "lload_3", + "fload_0", + "fload_1", + "fload_2", + "fload_3", + "dload_0", + "dload_1", + "dload_2", + "dload_3", + "aload_0", + "aload_1", + "aload_2", + "aload_3", + "iaload", + "laload", + "faload", + "daload", + "aaload", + "baload", + "caload", + "saload", + "istore", + "lstore", + "fstore", + "dstore", + "astore", + "istore_0", + "istore_1", + "istore_2", + "istore_3", + "lstore_0", + "lstore_1", + "lstore_2", + "lstore_3", + "fstore_0", + "fstore_1", + "fstore_2", + "fstore_3", + "dstore_0", + "dstore_1", + "dstore_2", + "dstore_3", + "astore_0", + "astore_1", + "astore_2", + "astore_3", + "iastore", + "lastore", + "fastore", + "dastore", + "aastore", + "bastore", + "castore", + "sastore", + "pop", + "pop2", + "dup", + "dup_x1", + "dup_x2", + "dup2", + "dup2_x1", + "dup2_x2", + "swap", + "iadd", + "ladd", + "fadd", + "dadd", + "isub", + "lsub", + "fsub", + "dsub", + "imul", + "lmul", + "fmul", + "dmul", + "idiv", + "ldiv", + "fdiv", + "ddiv", + "irem", + "lrem", + "frem", + "drem", + "ineg", + "lneg", + "fneg", + "dneg", + "ishl", + "lshl", + "ishr", + "lshr", + "iushr", + "lushr", + "iand", + "land", + "ior", + "lor", + "ixor", + "lxor", + "iinc", + "i2l", + "i2f", + "i2d", + "l2i", + "l2f", + "l2d", + "f2i", + "f2l", + "f2d", + "d2i", + "d2l", + "d2f", + "i2b", + "i2c", + "i2s", + "lcmp", + "fcmpl", + "fcmpg", + "dcmpl", + "dcmpg", + "ifeq", + "ifne", + "iflt", + "ifge", + "ifgt", + "ifle", + "if_icmpeq", + "if_icmpne", + "if_icmplt", + "if_icmpge", + "if_icmpgt", + "if_icmple", + "if_acmpeq", + "if_acmpne", + "goto", + "jsr", + "ret", + "tableswitch", + "lookupswitch", + "ireturn", + "lreturn", + "freturn", + "dreturn", + "areturn", + "return", + "getstatic", + "putstatic", + "getfield", + "putfield", + "invokevirtual", + "invokespecial", + "invokestatic", + "invokeinterface", + "invokedynamic", + "new", + "newarray", + "anewarray", + "arraylength", + "athrow", + "checkcast", + "instanceof", + "monitorenter", + "monitorexit", + "wide", + "multianewarray", + "ifnull", + "ifnonnull", + "goto_w", + "jsr_w", + "breakpoint" + }; + + /** + * Sort log events by start time. + */ static Comparator sortByStart = new Comparator() { public int compare(LogEvent a, LogEvent b) { @@ -80,25 +319,29 @@ return 7; } }; + + /** + * Sort log events first by the name of the compiled method, then by start + * time. In case one of the events has no associated compilation (or the + * associated compilation has no method name), the event with a compilation + * and/or name is considered the larger one. + */ static Comparator sortByNameAndStart = new Comparator() { public int compare(LogEvent a, LogEvent b) { Compilation c1 = a.getCompilation(); Compilation c2 = b.getCompilation(); - if (c1 != null && c2 != null) { + if (c1 != null && c1.getMethod() != null && c2 != null && c2.getMethod() != null) { int result = c1.getMethod().toString().compareTo(c2.getMethod().toString()); if (result != 0) { return result; } - } - double difference = (a.getStart() - b.getStart()); - if (difference < 0) { + } else if ((c1 == null || c1.getMethod() == null) && c2 != null && c2.getMethod() != null) { return -1; - } - if (difference > 0) { + } else if ((c2 == null || c2.getMethod() == null) && c1 != null && c1.getMethod() != null) { return 1; } - return 0; + return Double.compare(a.getStart(), b.getStart()); } public boolean equals(Object other) { @@ -110,6 +353,10 @@ return 7; } }; + + /** + * Sort log events by duration. + */ static Comparator sortByElapsed = new Comparator() { public int compare(LogEvent a, LogEvent b) { @@ -134,6 +381,10 @@ } }; + /** + * Shrink-wrapped representation of a JVMState (tailored to meet this + * tool's needs). It only records a method and bytecode instruction index. + */ class Jvms { Jvms(Method method, int bci) { this.method = method; @@ -146,12 +397,33 @@ } } + /** + * Representation of a lock elimination. Locks, corresponding to + * synchronized blocks and method calls, may be eliminated if the object in + * question is guaranteed to be used thread-locally. + */ class LockElimination extends BasicLogEvent { - ArrayList jvms = new ArrayList(1); + /** + * Track all locations from which this lock was eliminated. + */ + ArrayList jvms = new ArrayList<>(1); + + /** + * The kind of lock (coarsened, nested, non-escaping, unknown). + */ final String kind; + + /** + * The lock class (unlock, lock, unknown). + */ final String classId; + + /** + * The precise type of lock. + */ final String tagName; + LockElimination(String tagName, double start, String id, String kind, String classId) { super(start, id); this.kind = kind; @@ -160,8 +432,11 @@ } @Override - public void print(PrintStream stream) { - stream.printf("%s %s %s %s %.3f ", getId(), tagName, kind, classId, getStart()); + public void print(PrintStream stream, boolean printID) { + if (printID) { + stream.printf("%s ", getId()); + } + stream.printf("%s %s %s %.3f ", tagName, kind, classId, getStart()); stream.print(jvms.toString()); stream.print("\n"); } @@ -172,25 +447,154 @@ } - private ArrayList events = new ArrayList(); + /** + * A list of log events. This is populated with the events found in the + * compilation log file during parsing. + */ + private ArrayList events = new ArrayList<>(); + + /** + * Map compilation log IDs to type names. + */ + private HashMap types = new HashMap<>(); - private HashMap types = new HashMap(); - private HashMap methods = new HashMap(); - private LinkedHashMap nmethods = new LinkedHashMap(); - private HashMap compiles = new HashMap(); + /** + * Map compilation log IDs to methods. + */ + private HashMap methods = new HashMap<>(); + + /** + * Map compilation IDs ({@see #makeId()}) to newly created nmethods. + */ + private LinkedHashMap nmethods = new LinkedHashMap<>(); + + /** + * Map compilation task IDs {@see #makeId()}) to {@link Compilation} + * objects. + */ + private HashMap compiles = new HashMap<>(); + + /** + * Track compilation failure reasons. + */ private String failureReason; - private int bci; - private Stack scopes = new Stack(); + + /** + * The current bytecode instruction index. + */ + private int current_bci; + + /** + * The current bytecode instruction. + */ + private int current_bytecode; + + /** + * A sequence of {@link CallSite}s representing a call stack. A scope + * typically holds several {@link CallSite}s that represent calls + * originating from that scope. + * + * New scopes are typically pushed when parse log events are encountered + * ({@see #startElement()}) and popped when parsing of a given Java method + * is done ({@see #endElement()}). Parsing events can be nested. Several + * other events add information to scopes ({@see #startElement()}). + */ + private Deque scopes = new ArrayDeque<>(); + + /** + * The current compilation. + */ private Compilation compile; + + /** + * The {@linkplain CallSite compilation scope} currently in focus. + */ private CallSite site; + + /** + * The {@linkplain CallSite method handle call site} currently under + * observation. + */ private CallSite methodHandleSite; - private Stack phaseStack = new Stack(); + + /** + * Keep track of potentially nested compiler {@linkplain Phase phases}. + */ + private Deque phaseStack = new ArrayDeque<>(); + + /** + * The {@linkplain LockElimination lock elimination event} currently being + * processed. + */ private LockElimination currentLockElimination; + + /** + * The {@linkplain UncommonTrapEvent uncommon trap event} currently being + * processed. + */ private UncommonTrapEvent currentTrap; - private Stack lateInlineScope; + + /** + * During the processing of a late inline event, this stack holds the + * {@link CallSite}s that represent the inlining event's call stack. + */ + private Deque lateInlineScope; + + /** + * Denote whether a late inlining event is currently being processed. + */ private boolean lateInlining; + /** + * A document locator to provide better error messages: this allows the + * tool to display in which line of the log file the problem occurred. + */ + private Locator locator; + /** + * Callback for the SAX framework to set the document locator. + */ + @Override + public void setDocumentLocator(Locator locator) { + this.locator = locator; + } + + /** + * Report an internal error explicitly raised, i.e., not derived from an + * exception. + * + * @param msg The error message to report. + */ + private void reportInternalError(String msg) { + reportInternalError(msg, null); + } + + /** + * Report an internal error derived from an exception. + * + * @param msg The beginning of the error message to report. The message + * from the exception will be appended to this. + * @param e The exception that led to the internal error. + */ + private void reportInternalError(String msg, Exception e) { + if (locator != null) { + msg += " at " + locator.getLineNumber() + ":" + locator.getColumnNumber(); + if (e != null) { + msg += " - " + e.getMessage(); + } + } + if (e != null) { + throw new Error(msg, e); + } else { + throw new Error(msg); + } + } + + /** + * Parse a long hexadecimal address into a {@code long} value. As Java only + * supports positive {@code long} values, extra error handling and parsing + * logic is provided. + */ long parseLong(String l) { try { return Long.decode(l).longValue(); @@ -207,16 +611,29 @@ System.out.println(v1); System.out.println(v2); System.out.println(Long.toHexString(v1 + v2)); - throw new InternalError("bad conversion"); + reportInternalError("bad conversion"); } return v1 + v2; } } + /** + * Entry point for log file parsing with a file name. + * + * @param file The name of the log file to parse. + * @param cleanup Whether to perform bad XML cleanup during parsing (this + * is relevant for some log files generated by the 1.5 JVM). + * @return a list of {@link LogEvent} instances describing the events found + * in the log file. + */ public static ArrayList parse(String file, boolean cleanup) throws Exception { return parse(new FileReader(file), cleanup); } + /** + * Entry point for log file parsing with a file reader. + * {@see #parse(String,boolean)} + */ public static ArrayList parse(Reader reader, boolean cleanup) throws Exception { // Create the XML input factory SAXParserFactory factory = SAXParserFactory.newInstance(); @@ -238,31 +655,58 @@ // Carry on with what we've got... } - // Associate compilations with their NMethods - for (NMethod nm : log.nmethods.values()) { - Compilation c = log.compiles.get(nm.getId()); - nm.setCompilation(c); - // Native wrappers for methods don't have a compilation - if (c != null) { - c.setNMethod(nm); + // Associate compilations with their NMethods and other kinds of events + for (LogEvent e : log.events) { + if (e instanceof BasicLogEvent) { + BasicLogEvent ble = (BasicLogEvent) e; + Compilation c = log.compiles.get(ble.getId()); + if (c == null) { + if (!(ble instanceof NMethod)) { + throw new InternalError("only nmethods should have a null compilation, here's a " + ble.getClass()); + } + continue; + } + ble.setCompilation(c); + if (ble instanceof NMethod) { + c.setNMethod((NMethod) ble); + } } } - // Initially we want the LogEvent log sorted by timestamp - Collections.sort(log.events, sortByStart); - return log.events; } + /** + * Retrieve a given attribute's value from a collection of XML tag + * attributes. Report an error if the requested attribute is not found. + * + * @param attr A collection of XML tag attributes. + * @param name The name of the attribute the value of which is to be found. + * @return The value of the requested attribute, or {@code null} if it was + * not found. + */ String search(Attributes attr, String name) { String result = attr.getValue(name); if (result != null) { return result; } else { - throw new InternalError("can't find " + name); + reportInternalError("can't find " + name); + return null; } } + /** + * Retrieve a given attribute's value from a collection of XML tag + * attributes. Return a default value if the requested attribute is not + * found. + * + * @param attr A collection of XML tag attributes. + * @param name The name of the attribute the value of which is to be found. + * @param defaultValue The default value to return if the attribute is not + * found. + * @return The value of the requested attribute, or the default value if it + * was not found. + */ String search(Attributes attr, String name, String defaultValue) { String result = attr.getValue(name); if (result != null) { @@ -270,33 +714,70 @@ } return defaultValue; } - int indent = 0; + /** + * Map a type ID from the compilation log to an actual type name. In case + * the type represents an internal array type descriptor, return a + * Java-level name. If the type ID cannot be mapped to a name, raise an + * error. + */ String type(String id) { String result = types.get(id); if (result == null) { - throw new InternalError(id); + reportInternalError(id); } - String remapped = typeMap.get(result); + String remapped = type2printableMap.get(result); if (remapped != null) { return remapped; } return result; } + /** + * Register a mapping from log file type ID to type name. + */ void type(String id, String name) { assert type(id) == null; types.put(id, name); } + /** + * Map a log file type ID to an internal type declarator. + */ + String sigtype(String id) { + String result = types.get(id); + String remapped = type2vmtypeMap.get(result); + if (remapped != null) { + return remapped; + } + if (result == null) { + reportInternalError(id); + } + if (result.charAt(0) == '[') { + return result; + } + return "L" + result + ";"; + } + + /** + * Retrieve a method based on the log file ID it was registered under. + * Raise an error if the ID does not map to a method. + */ Method method(String id) { Method result = methods.get(id); if (result == null) { - throw new InternalError(id); + reportInternalError(id); } return result; } + /** + * From a compilation ID and kind, assemble a compilation ID for inclusion + * in the output. + * + * @param atts A collection of XML attributes from which the required + * attributes are retrieved. + */ public String makeId(Attributes atts) { String id = atts.getValue("compile_id"); String kind = atts.getValue("kind"); @@ -306,11 +787,60 @@ return id; } + /** + * Process the start of a compilation log XML element.
    + *
  • phase: record the beginning of a compilation phase, pushing + * it on the {@linkplain #phaseStack phase stack} and collecting + * information about the compiler graph.
  • + *
  • phase_done: record the end of a compilation phase, popping it + * off the {@linkplain #phaseStack phase stack} and collecting information + * about the compiler graph (number of nodes and live nodes).
  • + *
  • task: register the start of a new compilation.
  • + *
  • type: register a type.
  • + *
  • bc: note the current bytecode index and instruction name, + * updating {@link #current_bci} and {@link #current_bytecode}.
  • + *
  • klass: register a type (class).
  • + *
  • method: register a Java method.
  • + *
  • call: process a call, populating {@link #site} with the + * appropriate data.
  • + *
  • regalloc: record the register allocator's trip count in the + * {@linkplain #compile current compilation}.
  • + *
  • inline_fail: record the reason for a failed inline + * operation.
  • + *
  • inline_success: record a successful inlining operation, + * noting the success reason in the {@linkplain #site call site}.
  • + *
  • failure: note a compilation failure, storing the reason + * description in {@link #failureReason}.
  • + *
  • task_done: register the end of a compilation, recording time + * stamp and success information.
  • + *
  • make_not_entrant: deal with making a native method + * non-callable (e.g., during an OSR compilation, if there are still + * activations) or a zombie (when the method can be deleted).
  • + *
  • uncommon_trap: process an uncommon trap, setting the + * {@link #currentTrap} field.
  • + *
  • eliminate_lock: record the start of a lock elimination, + * setting the {@link #currentLockElimination} event.
  • + *
  • late_inline: start processing a late inline decision: + * initialize the {@linkplain #lateInlineScope inline scope stack}, create + * an {@linkplain #site initial scope} with a bogus bytecode index and the + * right inline ID, and push the scope with the inline ID attached. Note + * that most of late inlining processing happens in + * {@link #endElement()}.
  • + *
  • jvms: record a {@linkplain Jvms JVMState}. Depending on the + * context in which this event is encountered, this can mean adding + * information to the currently being processed trap, lock elimination, or + * inlining operation.
  • + *
  • inline_id: set the inline ID in the + * {@linkplain #site current call site}.
  • + *
  • nmethod: record the creation of a new {@link NMethod} and + * store it in the {@link #nmethods} map.
  • + *
  • parse: begin parsing a Java method's bytecode and + * transforming it into an initial compiler IR graph.
  • + *
  • parse_done: finish parsing a Java method's bytecode.
  • + *
+ */ @Override - public void startElement(String uri, - String localName, - String qname, - Attributes atts) { + public void startElement(String uri, String localName, String qname, Attributes atts) { if (qname.equals("phase")) { Phase p = new Phase(search(atts, "name"), Double.parseDouble(search(atts, "stamp")), @@ -322,45 +852,62 @@ String phaseName = search(atts, "name", null); if (phaseName != null && !p.getId().equals(phaseName)) { System.out.println("phase: " + p.getId()); - throw new InternalError("phase name mismatch"); + reportInternalError("phase name mismatch"); } p.setEnd(Double.parseDouble(search(atts, "stamp"))); p.setEndNodes(Integer.parseInt(search(atts, "nodes", "0"))); p.setEndLiveNodes(Integer.parseInt(search(atts, "live", "0"))); compile.getPhases().add(p); } else if (qname.equals("task")) { + String id = makeId(atts); + + // Create the new Compilation instance and populate it with readily + // available data. compile = new Compilation(Integer.parseInt(search(atts, "compile_id", "-1"))); compile.setStart(Double.parseDouble(search(atts, "stamp"))); compile.setICount(search(atts, "count", "0")); compile.setBCount(search(atts, "backedge_count", "0")); - - String method = atts.getValue("method"); - int space = method.indexOf(' '); - method = method.substring(0, space) + "::" + - method.substring(space + 1, method.indexOf(' ', space + 1) + 1); + compile.setBCI(Integer.parseInt(search(atts, "osr_bci", "-1"))); String compiler = atts.getValue("compiler"); if (compiler == null) { compiler = ""; } + compile.setCompiler(compiler); + + // Extract the name of the compiled method. + String[] parts = spacePattern.split(atts.getValue("method")); + String methodName = parts[0] + "::" + parts[1]; + + // Continue collecting compilation meta-data. String kind = atts.getValue("compile_kind"); if (kind == null) { kind = "normal"; } if (kind.equals("osr")) { compile.setOsr(true); - compile.setOsr_bci(Integer.parseInt(search(atts, "osr_bci"))); } else if (kind.equals("c2i")) { - compile.setSpecial("--- adapter " + method); + compile.setSpecial("--- adapter " + methodName); } else { - compile.setSpecial(compile.getId() + " " + method + " (0 bytes)"); + compile.setSpecial(compile.getId() + " " + methodName + " (0 bytes)"); } + + // Build a dummy method to stuff in the Compilation at the + // beginning. + Method m = new Method(); + m.setHolder(parts[0]); + m.setName(parts[1]); + m.setSignature(parts[2]); + m.setFlags("0"); + m.setBytes("unknown"); + compile.setMethod(m); events.add(compile); - compiles.put(makeId(atts), compile); + compiles.put(id, compile); site = compile.getCall(); } else if (qname.equals("type")) { type(search(atts, "id"), search(atts, "name")); } else if (qname.equals("bc")) { - bci = Integer.parseInt(search(atts, "bci")); + current_bci = Integer.parseInt(search(atts, "bci")); + current_bytecode = Integer.parseInt(search(atts, "code")); } else if (qname.equals("klass")) { type(search(atts, "id"), search(atts, "name")); } else if (qname.equals("method")) { @@ -369,7 +916,19 @@ m.setHolder(type(search(atts, "holder"))); m.setName(search(atts, "name")); m.setReturnType(type(search(atts, "return"))); - m.setArguments(search(atts, "arguments", "void")); + String arguments = atts.getValue("arguments");; + if (arguments == null) { + m.setSignature("()" + sigtype(atts.getValue("return"))); + } else { + String[] args = spacePattern.split(arguments); + StringBuilder sb = new StringBuilder("("); + for (int i = 0; i < args.length; i++) { + sb.append(sigtype(args[i])); + } + sb.append(")"); + sb.append(sigtype(atts.getValue("return"))); + m.setSignature(sb.toString()); + } if (search(atts, "unloaded", "0").equals("0")) { m.setBytes(search(atts, "bytes")); @@ -385,15 +944,17 @@ if (lateInlining && scopes.size() == 0) { // re-attempting already seen call site (late inlining for MH invokes) if (m != site.getMethod()) { - if (bci != site.getBci()) { - System.out.println(m + " bci: " + bci); - System.out.println(site.getMethod() + " bci: " + site.getBci()); - throw new InternalError("bci mismatch after late inlining"); + if (current_bci != site.getBci()) { + System.err.println(m + " bci: " + current_bci); + System.err.println(site.getMethod() + " bci: " + site.getBci()); + reportInternalError("bci mismatch after late inlining"); } site.setMethod(m); } } else { - site = new CallSite(bci, m); + // We're dealing with a new call site; the called method is + // likely to be parsed next. + site = new CallSite(current_bci, m); } site.setCount(Integer.parseInt(search(atts, "count", "0"))); String receiver = atts.getValue("receiver"); @@ -403,7 +964,8 @@ } int methodHandle = Integer.parseInt(search(atts, "method_handle_intrinsic", "0")); if (lateInlining && scopes.size() == 0) { - // The call was added before this round of late inlining + // The call was already added before this round of late + // inlining. Ignore. } else if (methodHandle == 0) { scopes.peek().add(site); } else { @@ -421,18 +983,16 @@ methodHandleSite = null; } if (lateInlining && scopes.size() == 0) { - site.setReason(search(atts, "reason")); + site.setReason("fail: " + search(atts, "reason")); lateInlining = false; } else { - scopes.peek().last().setReason(search(atts, "reason")); + scopes.peek().last().setReason("fail: " + search(atts, "reason")); } } else if (qname.equals("inline_success")) { if (methodHandleSite != null) { - throw new InternalError("method handle site should have been replaced"); + reportInternalError("method handle site should have been replaced"); } - if (lateInlining && scopes.size() == 0) { - site.setReason(null); - } + site.setReason("succeed: " + search(atts, "reason")); } else if (qname.equals("failure")) { failureReason = search(atts, "reason"); } else if (qname.equals("task_done")) { @@ -444,7 +1004,7 @@ } else if (qname.equals("make_not_entrant")) { String id = makeId(atts); NMethod nm = nmethods.get(id); - if (nm == null) throw new InternalError(); + if (nm == null) reportInternalError("nm == null"); LogEvent e = new MakeNotEntrantEvent(Double.parseDouble(search(atts, "stamp")), id, atts.getValue("zombie") != null, nm); events.add(e); @@ -459,8 +1019,22 @@ Integer.parseInt(search(atts, "count", "0"))); events.add(currentTrap); } else { - // uncommon trap inserted during parsing. - // ignore for now + if (atts.getValue("method") != null) { + // These are messages from ciTypeFlow that don't + // actually correspond to generated code. + return; + } + try { + if (scopes.size() == 0) { + reportInternalError("scope underflow"); + } + scopes.peek().add(new UncommonTrap(Integer.parseInt(search(atts, "bci")), + search(atts, "reason"), + search(atts, "action"), + bytecodes[current_bytecode])); + } catch (Error e) { + e.printStackTrace(); + } } } else if (qname.startsWith("eliminate_lock")) { String id = atts.getValue("compile_id"); @@ -474,24 +1048,27 @@ } else if (qname.equals("late_inline")) { long inlineId = 0; try { - Long.parseLong(search(atts, "inline_id")); + inlineId = Long.parseLong(search(atts, "inline_id")); } catch (InternalError ex) { // Log files from older hotspots may lack inline_id, // and zero is an acceptable substitute that allows processing to continue. } - lateInlineScope = new Stack(); - site = new CallSite(-999, method(search(atts, "method"))); + lateInlineScope = new ArrayDeque<>(); + Method m = method(search(atts, "method")); + site = new CallSite(-999, m); site.setInlineId(inlineId); lateInlineScope.push(site); } else if (qname.equals("jvms")) { // if (currentTrap != null) { - currentTrap.addJVMS(atts.getValue("method"), Integer.parseInt(atts.getValue("bci"))); + String[] parts = spacePattern.split(atts.getValue("method")); + currentTrap.addMethodAndBCI(parts[0].replace('/', '.') + '.' + parts[1] + parts[2], Integer.parseInt(atts.getValue("bci"))); } else if (currentLockElimination != null) { currentLockElimination.addJVMS(method(atts.getValue("method")), Integer.parseInt(atts.getValue("bci"))); } else if (lateInlineScope != null) { - bci = Integer.parseInt(search(atts, "bci")); - site = new CallSite(bci, method(search(atts, "method"))); + current_bci = Integer.parseInt(search(atts, "bci")); + Method m = method(search(atts, "method")); + site = new CallSite(current_bci, m); lateInlineScope.push(site); } else { // Ignore , @@ -499,7 +1076,7 @@ } } else if (qname.equals("inline_id")) { if (methodHandleSite != null) { - throw new InternalError("method handle site should have been replaced"); + reportInternalError("method handle site should have been replaced"); } long id = Long.parseLong(search(atts, "id")); site.setInlineId(id); @@ -513,33 +1090,53 @@ events.add(nm); } else if (qname.equals("parse")) { if (failureReason != null && scopes.size() == 0 && !lateInlining) { + // A compilation just failed, and we're back at a top + // compilation scope. failureReason = null; compile.reset(); site = compile.getCall(); } + // Error checking. if (methodHandleSite != null) { - throw new InternalError("method handle site should have been replaced"); + reportInternalError("method handle site should have been replaced"); } - Method m = method(search(atts, "method")); + Method m = method(search(atts, "method")); // this is the method being parsed if (lateInlining && scopes.size() == 0) { if (site.getMethod() != m) { - System.out.println(site.getMethod()); - System.out.println(m); - throw new InternalError("Unexpected method mismatch during late inlining"); + reportInternalError("Unexpected method mismatch during late inlining (method at call site: " + + site.getMethod() + ", method being parsed: " + m + ")"); } } + if (scopes.size() == 0 && !lateInlining) { + // The method being parsed is actually the method being + // compiled; i.e., we're dealing with a compilation top scope, + // which we must consequently push to the scopes stack. compile.setMethod(m); scopes.push(site); } else { + // The method being parsed is *not* the current compilation's + // top scope; i.e., we're dealing with an actual call site + // in the top scope or somewhere further down a call stack. if (site.getMethod() == m) { + // We're dealing with monomorphic inlining that didn't have + // to be narrowed down, because the receiver was known + // beforehand. scopes.push(site); - } else if (scopes.peek().getCalls().size() > 2 && m == scopes.peek().last(-2).getMethod()) { - scopes.push(scopes.peek().last(-2)); + } else if (scopes.peek().getCalls().size() > 2 && m == scopes.peek().lastButOne().getMethod()) { + // We're dealing with an at least bimorphic call site, and + // the compiler has now decided to parse the last-but-one + // method. The last one may already have been parsed for + // inlining. + scopes.push(scopes.peek().lastButOne()); } else { - // C1 prints multiple method tags during inlining when it narrows method being inlinied. - // Example: + // The method has been narrowed down to the one we're now + // going to parse, which is inlined here. It's monomorphic + // inlining, but was not immediately clear as such. + // + // C1 prints multiple method tags during inlining when it + // narrows the method being inlined. Example: // ... // // @@ -552,100 +1149,132 @@ } } } else if (qname.equals("parse_done")) { - CallSite call = scopes.pop(); + // Attach collected information about IR nodes to the current + // parsing scope before it's popped off the stack in endElement() + // (see where the parse tag is handled). + CallSite call = scopes.peek(); call.setEndNodes(Integer.parseInt(search(atts, "nodes", "0"))); call.setEndLiveNodes(Integer.parseInt(search(atts, "live", "0"))); call.setTimeStamp(Double.parseDouble(search(atts, "stamp"))); - scopes.push(call); } } + /** + * Process the end of a compilation log XML element.
    + *
  • parse: finish transforming a Java method's bytecode + * instructions to an initial compiler IR graph.
  • + *
  • uncommon_trap: record the end of processing an uncommon trap, + * resetting {@link #currentTrap}.
  • + *
  • eliminate_lock: record the end of a lock elimination, + * resetting {@link #currentLockElimination}.
  • + *
  • late_inline: the closing tag for late_inline does not denote + * the end of a late inlining operation, but the end of the descriptive log + * data given at its beginning. That is, we're now in the position to + * assemble details about the inlining chain (bytecode instruction index in + * caller, called method). The {@link #lateInlining} flag is set to + * {@code true} here. (It will be reset when parsing the inlined methods is + * done; this happens for the successful case in this method as well, when + * {@code parse} elements are processed; and for inlining failures, in + * {@link #startElement()}, when {@code inline_fail} elements are + * processed.)
  • + *
  • task: perform cleanup at the end of a compilation. Note that + * the explicit {@code task_done} event is handled in + * {@link #startElement()}.
  • + *
+ */ @Override - public void endElement(String uri, - String localName, - String qname) { - if (qname.equals("parse")) { - indent -= 2; - scopes.pop(); - if (scopes.size() == 0) { - lateInlining = false; - } - } else if (qname.equals("uncommon_trap")) { - currentTrap = null; - } else if (qname.startsWith("eliminate_lock")) { - currentLockElimination = null; - } else if (qname.equals("late_inline")) { - // Populate late inlining info. - if (scopes.size() != 0) { - throw new InternalError("scopes should be empty for late inline"); - } - // late inline scopes are specified in reverse order: - // compiled method should be on top of stack. - CallSite caller = lateInlineScope.pop(); - Method m = compile.getMethod(); - if (m != caller.getMethod()) { - System.err.println(m); - System.err.println(caller.getMethod() + " bci: " + bci); - throw new InternalError("call site and late_inline info don't match"); - } + public void endElement(String uri, String localName, String qname) { + try { + if (qname.equals("parse")) { + // Finish dealing with the current call scope. If no more are + // left, no late inlining can be going on. + scopes.pop(); + if (scopes.size() == 0) { + lateInlining = false; + } + } else if (qname.equals("uncommon_trap")) { + currentTrap = null; + } else if (qname.startsWith("eliminate_lock")) { + currentLockElimination = null; + } else if (qname.equals("late_inline")) { + // Populate late inlining info. + if (scopes.size() != 0) { + reportInternalError("scopes should be empty for late inline"); + } + // late inline scopes are specified in reverse order: + // compiled method should be on top of stack. + CallSite caller = lateInlineScope.pop(); + Method m = compile.getMethod(); + if (!m.equals(caller.getMethod())) { + reportInternalError(String.format("call site and late_inline info don't match:\n method %s\n caller method %s, bci %d", m, caller.getMethod(), current_bci)); + } - // late_inline contains caller+bci info, convert it - // to bci+callee info used by LogCompilation. - CallSite lateInlineSite = compile.getLateInlineCall(); - ArrayDeque thisCallScopes = new ArrayDeque(); - do { - bci = caller.getBci(); - // Next inlined call. - caller = lateInlineScope.pop(); - CallSite callee = new CallSite(bci, caller.getMethod()); - callee.setInlineId(caller.getInlineId()); - thisCallScopes.addLast(callee); - lateInlineSite.add(callee); - lateInlineSite = callee; - } while (!lateInlineScope.empty()); + // Walk down the inlining chain and assemble bci+callee info. + // This needs to be converted from caller+bci info contained in + // the late_inline data. + CallSite lateInlineSite = compile.getLateInlineCall(); + ArrayDeque thisCallScopes = new ArrayDeque<>(); + do { + current_bci = caller.getBci(); + // Next inlined call. + caller = lateInlineScope.pop(); + CallSite callee = new CallSite(current_bci, caller.getMethod()); + callee.setInlineId(caller.getInlineId()); + thisCallScopes.addLast(callee); + lateInlineSite.add(callee); + lateInlineSite = callee; + } while (!lateInlineScope.isEmpty()); - site = compile.getCall().findCallSite(thisCallScopes); - if (site == null) { - System.out.println("call scopes:"); - for (CallSite c : thisCallScopes) { - System.out.println(c.getMethod() + " " + c.getBci() + " " + c.getInlineId()); + site = compile.getCall().findCallSite(thisCallScopes); + if (site == null) { + // Call site could not be found - report the problem in detail. + System.err.println("call scopes:"); + for (CallSite c : thisCallScopes) { + System.err.println(c.getMethod() + " " + c.getBci() + " " + c.getInlineId()); + } + CallSite c = thisCallScopes.getLast(); + if (c.getInlineId() != 0) { + System.err.println("Looking for call site in entire tree:"); + ArrayDeque stack = compile.getCall().findCallSite2(c); + for (CallSite c2 : stack) { + System.err.println(c2.getMethod() + " " + c2.getBci() + " " + c2.getInlineId()); + } + } + System.err.println(caller.getMethod() + " bci: " + current_bci); + reportInternalError("couldn't find call site"); } - CallSite c = thisCallScopes.getLast(); - if (c.getInlineId() != 0) { - System.out.println("Looking for call site in entire tree:"); - ArrayDeque stack = compile.getCall().findCallSite2(c); - for (CallSite c2 : stack) { - System.out.println(c2.getMethod() + " " + c2.getBci() + " " + c2.getInlineId()); + lateInlining = true; + + if (caller.getBci() != -999) { + System.out.println(caller.getMethod()); + reportInternalError("broken late_inline info"); + } + if (site.getMethod() != caller.getMethod()) { + if (site.getInlineId() == caller.getInlineId()) { + site.setMethod(caller.getMethod()); + } else { + System.out.println(site.getMethod()); + System.out.println(caller.getMethod()); + reportInternalError("call site and late_inline info don't match"); } } - System.out.println(caller.getMethod() + " bci: " + bci); - throw new InternalError("couldn't find call site"); - } - lateInlining = true; - - if (caller.getBci() != -999) { - System.out.println(caller.getMethod()); - throw new InternalError("broken late_inline info"); + // late_inline is followed by parse with scopes.size() == 0, + // 'site' will be pushed to scopes. + lateInlineScope = null; + } else if (qname.equals("task")) { + types.clear(); + methods.clear(); + site = null; } - if (site.getMethod() != caller.getMethod()) { - if (site.getInlineId() == caller.getInlineId()) { - site.setMethod(caller.getMethod()); - } else { - System.out.println(site.getMethod()); - System.out.println(caller.getMethod()); - throw new InternalError("call site and late_inline info don't match"); - } - } - // late_inline is followed by parse with scopes.size() == 0, - // 'site' will be pushed to scopes. - lateInlineScope = null; - } else if (qname.equals("task")) { - types.clear(); - methods.clear(); - site = null; + } catch (Exception e) { + reportInternalError("exception while processing end element", e); } } + // + // Handlers for problems that occur in XML parsing itself. + // + @Override public void warning(org.xml.sax.SAXParseException e) { System.err.println(e.getMessage() + " at line " + e.getLineNumber() + ", column " + e.getColumnNumber()); diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/MakeNotEntrantEvent.java --- a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/MakeNotEntrantEvent.java Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/MakeNotEntrantEvent.java Tue Jul 14 18:58:38 2015 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2015, 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 @@ -21,14 +21,25 @@ * questions. * */ - package com.sun.hotspot.tools.compiler; import java.io.PrintStream; +/** + * In a compilation log, represent the event of making a given compiled method + * not-entrant, e.g., during an OSR compilation. + */ class MakeNotEntrantEvent extends BasicLogEvent { + + /** + * Denote whether the method is marked as a zombie, i.e., no further + * activations exist. + */ private final boolean zombie; + /** + * The method in question. + */ private NMethod nmethod; MakeNotEntrantEvent(double s, String i, boolean z, NMethod nm) { @@ -41,7 +52,7 @@ return nmethod; } - public void print(PrintStream stream) { + public void print(PrintStream stream, boolean printID) { if (isZombie()) { stream.printf("%s make_zombie\n", getId()); } else { diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/Method.java --- a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/Method.java Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/Method.java Tue Jul 14 18:58:38 2015 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2015, 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 @@ -26,16 +26,58 @@ import java.util.Arrays; -public class Method implements Constants { +import static com.sun.hotspot.tools.compiler.Constants.*; + +/** + * Representation of a Java method in a compilation log. + */ +public class Method { + /** + * The name of the class holding the method. + */ private String holder; + + /** + * The method's name. + */ private String name; + + /** + * The return type of the method, as a fully qualified (source-level) class + * or primitive type name. + */ private String returnType; - private String arguments; + + /** + * The method's signature, in internal form. + */ + private String signature; + + /** + * The length of the method's byte code. + */ private String bytes; + + /** + * The number of times this method was invoked in the interpreter. + */ private String iicount; + + /** + * The method's flags, in the form of a {@code String} representing the + * {@code int} encoding them. + */ private String flags; + /** + * Decode the {@link flags} numerical string to a format for console + * output. The result does not honour all possible flags but includes + * information about OSR compilation. + * + * @param osr_bci the byte code index at which an OSR compilation takes + * place, or -1 if the compilation is not an OSR one. + */ String decodeFlags(int osr_bci) { int f = Integer.parseInt(getFlags()); char[] c = new char[4]; @@ -49,6 +91,12 @@ return new String(c); } + /** + * Format this method for console output. + * + * @param osr_bci the byte code index at which OSR takes place, or -1 if no + * OSR compilation is going on. + */ String format(int osr_bci) { if (osr_bci >= 0) { return getHolder() + "::" + getName() + " @ " + osr_bci + " (" + getBytes() + " bytes)"; @@ -62,6 +110,10 @@ return getHolder() + "::" + getName() + " (" + getBytes() + " bytes)"; } + public String getFullName() { + return getHolder().replace('/', '.') + "." + getName() + signature; + } + public String getHolder() { return holder; } @@ -86,12 +138,16 @@ this.returnType = returnType; } - public String getArguments() { - return arguments; + public String getSignature() { + return signature; } - public void setArguments(String arguments) { - this.arguments = arguments; + public void setSignature(String signature) { + this.signature = signature.replace('/', '.'); + } + + public String getArguments() { + return signature.substring(0, signature.indexOf(')') + 1); } public String getBytes() { @@ -121,10 +177,13 @@ @Override public boolean equals(Object o) { if (o instanceof Method) { - Method other = (Method)o; - return holder.equals(other.holder) && name.equals(other.name) && - arguments.equals(other.arguments) && returnType.equals(other.returnType); + Method other = (Method) o; + return holder.equals(other.holder) && name.equals(other.name) && signature.equals(other.signature); } return false; } + + public int hashCode() { + return holder.hashCode() ^ name.hashCode(); + } } diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/NMethod.java --- a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/NMethod.java Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/NMethod.java Tue Jul 14 18:58:38 2015 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2015, 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 @@ -26,9 +26,20 @@ import java.io.PrintStream; +/** + * A compilation log event that is signalled whenever a new nmethod (a native + * method, a compilation result) is created. + */ public class NMethod extends BasicLogEvent { + /** + * The nmethod's starting address in memory. + */ private long address; + + /** + * The nmethod's size in bytes. + */ private long size; NMethod(double s, String i, long a, long sz) { @@ -37,7 +48,7 @@ size = sz; } - public void print(PrintStream out) { + public void print(PrintStream out, boolean printID) { // XXX Currently we do nothing // throw new InternalError(); } diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/Phase.java --- a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/Phase.java Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/Phase.java Tue Jul 14 18:58:38 2015 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2015, 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 @@ -26,11 +26,30 @@ import java.io.PrintStream; +/** + * Representation of a compilation phase as a log event. + */ public class Phase extends BasicLogEvent { + /** + * The number of nodes in the compilation at the beginning of this phase. + */ private final int startNodes; + + /** + * The number of nodes in the compilation at the end of this phase. + */ private int endNodes; + + /** + * The number of live nodes in the compilation at the beginning of this + * phase. + */ private final int startLiveNodes; + + /** + * The number of live nodes in the compilation at the end of this phase. + */ private int endLiveNodes; Phase(String n, double s, int nodes, int live) { @@ -58,8 +77,11 @@ public int getEndNodes() { return endNodes; } - /* Number of live nodes added by the phase */ - int getLiveNodes() { + + /** + * The number of live nodes added by this phase. + */ + int getAddedLiveNodes() { return getEndLiveNodes() - getStartLiveNodes(); } @@ -76,7 +98,7 @@ } @Override - public void print(PrintStream stream) { + public void print(PrintStream stream, boolean printID) { throw new UnsupportedOperationException("Not supported yet."); } } diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrap.java Tue Jul 14 18:58:38 2015 +0200 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2009, 2015, 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ +package com.sun.hotspot.tools.compiler; + +import java.io.PrintStream; + +/** + * An instance of this class represents an uncommon trap associated with a + * given bytecode instruction. An uncommon trap is described in terms of its + * reason and action to be taken. An instance of this class is always relative + * to a specific method and only contains the relevant bytecode instruction + * index. + */ +class UncommonTrap { + + private int bci; + private String reason; + private String action; + private String bytecode; + + public UncommonTrap(int b, String r, String a, String bc) { + bci = b; + reason = r; + action = a; + bytecode = bc; + } + + public int getBCI() { + return bci; + } + + public String getReason() { + return reason; + } + + public String getAction() { + return action; + } + + public String getBytecode() { + return bytecode; + } + + void emit(PrintStream stream, int indent) { + for (int i = 0; i < indent; i++) { + stream.print(' '); + } + } + + public void print(PrintStream stream, int indent) { + emit(stream, indent); + stream.println(this); + } + + public String toString() { + return "@ " + bci + " " + getBytecode() + " uncommon trap " + getReason() + " " + getAction(); + } +} diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrapEvent.java --- a/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrapEvent.java Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/tools/LogCompilation/src/com/sun/hotspot/tools/compiler/UncommonTrapEvent.java Tue Jul 14 18:58:38 2015 +0200 @@ -21,17 +21,33 @@ * questions. * */ - package com.sun.hotspot.tools.compiler; import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; +/** + * Represents an uncommon trap encountered during a compilation. + */ class UncommonTrapEvent extends BasicLogEvent { private final String reason; private final String action; + + /** + * Denote how many times this trap has been encountered. + */ private int count; - private String jvms = ""; + + /** + * The name of the bytecode instruction at which the trap occurred. + */ + private String bytecode; + + private List jvmsMethods = new ArrayList<>(); + + private List jvmsBCIs = new ArrayList<>(); UncommonTrapEvent(double s, String i, String r, String a, int c) { super(s, i); @@ -40,20 +56,26 @@ count = c; } - - public void addJVMS(String method, int bci) { - setJvms(getJvms() + " @" + bci + " " + method + "\n"); - } - public void updateCount(UncommonTrapEvent trap) { setCount(Math.max(getCount(), trap.getCount())); } - public void print(PrintStream stream) { - stream.printf("%s uncommon trap %.3f %s %s\n", getId(), getStart(), getReason(), getAction()); - stream.print(getJvms()); + public void print(PrintStream stream, boolean printID) { + if (printID) { + stream.print(getId() + " "); + } + stream.printf("uncommon trap %s %s %s\n", bytecode, getReason(), getAction()); + int indent = 2; + for (int j = 0; j < jvmsMethods.size(); j++) { + for (int i = 0; i < indent; i++) { + stream.print(' '); + } + stream.println("@ " + jvmsBCIs.get(j) + " " + jvmsMethods.get(j)); + indent += 2; + } } + public String getReason() { return reason; } @@ -70,15 +92,56 @@ this.count = count; } - public String getJvms() { - return jvms; + /** + * Set the compilation for this event. This involves identifying the call + * site to which this uncommon trap event belongs. In addition to setting + * the {@link #compilation} link, this method will consequently also set + * the {@link #bytecode} field. + */ + public void setCompilation(Compilation compilation) { + super.setCompilation(compilation); + // Attempt to associate a bytecode with with this trap + CallSite site = compilation.getCall(); + int i = 0; + try { + List traps = site.getTraps(); + while (i + 1 < jvmsMethods.size()) { + if (!jvmsMethods.get(i).equals(site.getMethod().getFullName())) { + throw new InternalError(jvmsMethods.get(i) + " != " + site.getMethod().getFullName()); + } + CallSite result = null; + for (CallSite call : site.getCalls()) { + if (call.getBci() == jvmsBCIs.get(i) && + call.getMethod().getFullName().equals(jvmsMethods.get(i + 1)) && + call.getReceiver() == null) { + result = call; + i++; + break; + } + } + if (result == null) { + throw new InternalError("couldn't find call site"); + } + site = result; + traps = site.getTraps(); + } + for (UncommonTrap trap : traps) { + if (trap.getBCI() == jvmsBCIs.get(i) && + trap.getReason().equals(getReason()) && + trap.getAction().equals(getAction())) { + bytecode = trap.getBytecode(); + return; + } + } + throw new InternalError("couldn't find bytecode"); + } catch (Exception e) { + bytecode = ""; + } } - public void setJvms(String jvms) { - this.jvms = jvms; + public void addMethodAndBCI(String method, int bci) { + jvmsMethods.add(0, method); + jvmsBCIs.add(0, bci); } - public void setCompilation(Compilation compilation) { - this.compilation = compilation; - } } diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/vm/ci/ciField.hpp --- a/hotspot/src/share/vm/ci/ciField.hpp Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/vm/ci/ciField.hpp Tue Jul 14 18:58:38 2015 +0200 @@ -181,6 +181,17 @@ return (holder()->is_subclass_of(callsite_klass) && (name() == ciSymbol::target_name())); } + bool is_autobox_cache() { + ciSymbol* klass_name = holder()->name(); + return (name() == ciSymbol::cache_field_name() && + holder()->uses_default_loader() && + (klass_name == ciSymbol::java_lang_Character_CharacterCache() || + klass_name == ciSymbol::java_lang_Byte_ByteCache() || + klass_name == ciSymbol::java_lang_Short_ShortCache() || + klass_name == ciSymbol::java_lang_Integer_IntegerCache() || + klass_name == ciSymbol::java_lang_Long_LongCache())); + } + // Debugging output void print(); void print_name_on(outputStream* st); diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/vm/compiler/compileBroker.cpp --- a/hotspot/src/share/vm/compiler/compileBroker.cpp Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/vm/compiler/compileBroker.cpp Tue Jul 14 18:58:38 2015 +0200 @@ -501,8 +501,8 @@ methodHandle method(thread, this->method()); ResourceMark rm(thread); - // - log->print(" compile_id='%d'", _compile_id); + // + log->print(" compiler='%s' compile_id='%d'", _comp_level <= CompLevel_full_profile ? "C1" : "C2", _compile_id); if (_osr_bci != CompileBroker::standard_entry_bci) { log->print(" compile_kind='osr'"); // same as nmethod::compile_kind } // else compile_kind='c2c' diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/vm/opto/library_call.cpp --- a/hotspot/src/share/vm/opto/library_call.cpp Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/vm/opto/library_call.cpp Tue Jul 14 18:58:38 2015 +0200 @@ -2687,35 +2687,48 @@ // of safe & unsafe memory. if (need_mem_bar) insert_mem_bar(Op_MemBarCPUOrder); - if (!is_store) { - MemNode::MemOrd mo = is_volatile ? MemNode::acquire : MemNode::unordered; - // To be valid, unsafe loads may depend on other conditions than - // the one that guards them: pin the Load node - Node* p = make_load(control(), adr, value_type, type, adr_type, mo, LoadNode::Pinned, is_volatile); - // load value - switch (type) { - case T_BOOLEAN: - case T_CHAR: - case T_BYTE: - case T_SHORT: - case T_INT: - case T_LONG: - case T_FLOAT: - case T_DOUBLE: - break; - case T_OBJECT: - if (need_read_barrier) { - insert_pre_barrier(heap_base_oop, offset, p, !(is_volatile || need_mem_bar)); + if (!is_store) { + Node* p = NULL; + // Try to constant fold a load from a constant field + ciField* field = alias_type->field(); + if (heap_base_oop != top() && + field != NULL && field->is_constant() && field->layout_type() == type) { + // final or stable field + const Type* con_type = Type::make_constant(alias_type->field(), heap_base_oop); + if (con_type != NULL) { + p = makecon(con_type); } - break; - case T_ADDRESS: - // Cast to an int type. - p = _gvn.transform(new CastP2XNode(NULL, p)); - p = ConvX2UL(p); - break; - default: - fatal(err_msg_res("unexpected type %d: %s", type, type2name(type))); - break; + } + if (p == NULL) { + MemNode::MemOrd mo = is_volatile ? MemNode::acquire : MemNode::unordered; + // To be valid, unsafe loads may depend on other conditions than + // the one that guards them: pin the Load node + p = make_load(control(), adr, value_type, type, adr_type, mo, LoadNode::Pinned, is_volatile); + // load value + switch (type) { + case T_BOOLEAN: + case T_CHAR: + case T_BYTE: + case T_SHORT: + case T_INT: + case T_LONG: + case T_FLOAT: + case T_DOUBLE: + break; + case T_OBJECT: + if (need_read_barrier) { + insert_pre_barrier(heap_base_oop, offset, p, !(is_volatile || need_mem_bar)); + } + break; + case T_ADDRESS: + // Cast to an int type. + p = _gvn.transform(new CastP2XNode(NULL, p)); + p = ConvX2UL(p); + break; + default: + fatal(err_msg_res("unexpected type %d: %s", type, type2name(type))); + break; + } } // The load node has the control of the preceding MemBarCPUOrder. All // following nodes will have the control of the MemBarCPUOrder inserted at diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/vm/opto/parse.hpp --- a/hotspot/src/share/vm/opto/parse.hpp Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/vm/opto/parse.hpp Tue Jul 14 18:58:38 2015 +0200 @@ -539,10 +539,6 @@ void do_get_xxx(Node* obj, ciField* field, bool is_field); void do_put_xxx(Node* obj, ciField* field, bool is_field); - // loading from a constant field or the constant pool - // returns false if push failed (non-perm field constants only, not ldcs) - bool push_constant(ciConstant con, bool require_constant = false, bool is_autobox_cache = false, const Type* basic_type = NULL); - // implementation of object creation bytecodes void emit_guard_for_new(ciInstanceKlass* klass); void do_new(); diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/vm/opto/parse2.cpp --- a/hotspot/src/share/vm/opto/parse2.cpp Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/vm/opto/parse2.cpp Tue Jul 14 18:58:38 2015 +0200 @@ -1478,8 +1478,10 @@ } assert(constant.basic_type() != T_OBJECT || constant.as_object()->is_instance(), "must be java_mirror of klass"); - bool pushed = push_constant(constant, true); - guarantee(pushed, "must be possible to push this constant"); + const Type* con_type = Type::make_from_constant(constant); + if (con_type != NULL) { + push_node(con_type->basic_type(), makecon(con_type)); + } } break; diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/vm/opto/parse3.cpp --- a/hotspot/src/share/vm/opto/parse3.cpp Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/vm/opto/parse3.cpp Tue Jul 14 18:58:38 2015 +0200 @@ -149,51 +149,10 @@ // Does this field have a constant value? If so, just push the value. if (field->is_constant()) { // final or stable field - const Type* stable_type = NULL; - if (FoldStableValues && field->is_stable()) { - stable_type = Type::get_const_type(field->type()); - if (field->type()->is_array_klass()) { - int stable_dimension = field->type()->as_array_klass()->dimension(); - stable_type = stable_type->is_aryptr()->cast_to_stable(true, stable_dimension); - } - } - if (field->is_static()) { - // final static field - if (C->eliminate_boxing()) { - // The pointers in the autobox arrays are always non-null. - ciSymbol* klass_name = field->holder()->name(); - if (field->name() == ciSymbol::cache_field_name() && - field->holder()->uses_default_loader() && - (klass_name == ciSymbol::java_lang_Character_CharacterCache() || - klass_name == ciSymbol::java_lang_Byte_ByteCache() || - klass_name == ciSymbol::java_lang_Short_ShortCache() || - klass_name == ciSymbol::java_lang_Integer_IntegerCache() || - klass_name == ciSymbol::java_lang_Long_LongCache())) { - bool require_const = true; - bool autobox_cache = true; - if (push_constant(field->constant_value(), require_const, autobox_cache)) { - return; - } - } - } - if (push_constant(field->constant_value(), false, false, stable_type)) - return; - } else { - // final or stable non-static field - // Treat final non-static fields of trusted classes (classes in - // java.lang.invoke and sun.invoke packages and subpackages) as - // compile time constants. - if (obj->is_Con()) { - const TypeOopPtr* oop_ptr = obj->bottom_type()->isa_oopptr(); - ciObject* constant_oop = oop_ptr->const_oop(); - ciConstant constant = field->constant_value_of(constant_oop); - if (FoldStableValues && field->is_stable() && constant.is_null_or_zero()) { - // fall through to field load; the field is not yet initialized - } else { - if (push_constant(constant, true, false, stable_type)) - return; - } - } + const Type* con_type = Type::make_constant(field, obj); + if (con_type != NULL) { + push_node(con_type->basic_type(), makecon(con_type)); + return; } } @@ -362,39 +321,6 @@ } } - - -bool Parse::push_constant(ciConstant constant, bool require_constant, bool is_autobox_cache, const Type* stable_type) { - const Type* con_type = Type::make_from_constant(constant, require_constant, is_autobox_cache); - switch (constant.basic_type()) { - case T_ARRAY: - case T_OBJECT: - // cases: - // can_be_constant = (oop not scavengable || ScavengeRootsInCode != 0) - // should_be_constant = (oop not scavengable || ScavengeRootsInCode >= 2) - // An oop is not scavengable if it is in the perm gen. - if (stable_type != NULL && con_type != NULL && con_type->isa_oopptr()) - con_type = con_type->join_speculative(stable_type); - break; - - case T_ILLEGAL: - // Invalid ciConstant returned due to OutOfMemoryError in the CI - assert(C->env()->failing(), "otherwise should not see this"); - // These always occur because of object types; we are going to - // bail out anyway, so make the stack depths match up - push( zerocon(T_OBJECT) ); - return false; - } - - if (con_type == NULL) - // we cannot inline the oop, but we can use it later to narrow a type - return false; - - push_node(constant.basic_type(), makecon(con_type)); - return true; -} - - //============================================================================= void Parse::do_anewarray() { bool will_link; diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/vm/opto/type.cpp --- a/hotspot/src/share/vm/opto/type.cpp Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/vm/opto/type.cpp Tue Jul 14 18:58:38 2015 +0200 @@ -200,8 +200,7 @@ //-----------------------make_from_constant------------------------------------ -const Type* Type::make_from_constant(ciConstant constant, - bool require_constant, bool is_autobox_cache) { +const Type* Type::make_from_constant(ciConstant constant, bool require_constant) { switch (constant.basic_type()) { case T_BOOLEAN: return TypeInt::make(constant.as_boolean()); case T_CHAR: return TypeInt::make(constant.as_char()); @@ -222,14 +221,57 @@ if (oop_constant->is_null_object()) { return Type::get_zero_type(T_OBJECT); } else if (require_constant || oop_constant->should_be_constant()) { - return TypeOopPtr::make_from_constant(oop_constant, require_constant, is_autobox_cache); + return TypeOopPtr::make_from_constant(oop_constant, require_constant); + } + } + case T_ILLEGAL: + // Invalid ciConstant returned due to OutOfMemoryError in the CI + assert(Compile::current()->env()->failing(), "otherwise should not see this"); + return NULL; + } + // Fall through to failure + return NULL; +} + + +const Type* Type::make_constant(ciField* field, Node* obj) { + if (!field->is_constant()) return NULL; + + const Type* con_type = NULL; + if (field->is_static()) { + // final static field + con_type = Type::make_from_constant(field->constant_value(), /*require_const=*/true); + if (Compile::current()->eliminate_boxing() && field->is_autobox_cache() && con_type != NULL) { + con_type = con_type->is_aryptr()->cast_to_autobox_cache(true); + } + } else { + // final or stable non-static field + // Treat final non-static fields of trusted classes (classes in + // java.lang.invoke and sun.invoke packages and subpackages) as + // compile time constants. + if (obj->is_Con()) { + const TypeOopPtr* oop_ptr = obj->bottom_type()->isa_oopptr(); + ciObject* constant_oop = oop_ptr->const_oop(); + ciConstant constant = field->constant_value_of(constant_oop); + con_type = Type::make_from_constant(constant, /*require_const=*/true); + } + } + if (FoldStableValues && field->is_stable() && con_type != NULL) { + if (con_type->is_zero_type()) { + return NULL; // the field hasn't been initialized yet + } else if (con_type->isa_oopptr()) { + const Type* stable_type = Type::get_const_type(field->type()); + if (field->type()->is_array_klass()) { + int stable_dimension = field->type()->as_array_klass()->dimension(); + stable_type = stable_type->is_aryptr()->cast_to_stable(true, stable_dimension); + } + if (stable_type != NULL) { + con_type = con_type->join_speculative(stable_type); } } } - // Fall through to failure - return NULL; -} - + return con_type; +} //------------------------------make------------------------------------------- // Create a simple Type, with default empty symbol sets. Then hashcons it @@ -3009,9 +3051,7 @@ //------------------------------make_from_constant----------------------------- // Make a java pointer from an oop constant -const TypeOopPtr* TypeOopPtr::make_from_constant(ciObject* o, - bool require_constant, - bool is_autobox_cache) { +const TypeOopPtr* TypeOopPtr::make_from_constant(ciObject* o, bool require_constant) { assert(!o->is_null_object(), "null object not yet handled here."); ciKlass* klass = o->klass(); if (klass->is_instance_klass()) { @@ -3026,10 +3066,6 @@ // Element is an object array. Recursively call ourself. const TypeOopPtr *etype = TypeOopPtr::make_from_klass_raw(klass->as_obj_array_klass()->element_klass()); - if (is_autobox_cache) { - // The pointers in the autobox arrays are always non-null. - etype = etype->cast_to_ptr_type(TypePtr::NotNull)->is_oopptr(); - } const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length())); // We used to pass NotNull in here, asserting that the sub-arrays // are all not-null. This is not true in generally, as code can @@ -3039,7 +3075,7 @@ } else if (!o->should_be_constant()) { return TypeAryPtr::make(TypePtr::NotNull, arr0, klass, true, 0); } - const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0, InstanceBot, NULL, InlineDepthBottom, is_autobox_cache); + const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0); return arr; } else if (klass->is_type_array_klass()) { // Element is an typeArray @@ -3940,7 +3976,6 @@ return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _instance_id, _speculative, _inline_depth); } - //------------------------------cast_to_stable--------------------------------- const TypeAryPtr* TypeAryPtr::cast_to_stable(bool stable, int stable_dimension) const { if (stable_dimension <= 0 || (stable_dimension == 1 && stable == this->is_stable())) @@ -3969,6 +4004,18 @@ return dim; } +//----------------------cast_to_autobox_cache----------------------------------- +const TypeAryPtr* TypeAryPtr::cast_to_autobox_cache(bool cache) const { + if (is_autobox_cache() == cache) return this; + const TypeOopPtr* etype = elem()->make_oopptr(); + if (etype == NULL) return this; + // The pointers in the autobox arrays are always non-null. + TypePtr::PTR ptr_type = cache ? TypePtr::NotNull : TypePtr::AnyNull; + etype = etype->cast_to_ptr_type(TypePtr::NotNull)->is_oopptr(); + const TypeAry* new_ary = TypeAry::make(etype, size(), is_stable()); + return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _instance_id, _speculative, _inline_depth, cache); +} + //------------------------------eq--------------------------------------------- // Structural equality check for Type representations bool TypeAryPtr::eq( const Type *t ) const { @@ -4455,7 +4502,7 @@ // TRUE if Type is a singleton type, FALSE otherwise. Singletons are simple // constants bool TypeMetadataPtr::singleton(void) const { - // detune optimizer to not generate constant metadta + constant offset as a constant! + // detune optimizer to not generate constant metadata + constant offset as a constant! // TopPTR, Null, AnyNull, Constant are all singletons return (_offset == 0) && !below_centerline(_ptr); } diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/src/share/vm/opto/type.hpp --- a/hotspot/src/share/vm/opto/type.hpp Tue Jul 14 09:55:22 2015 -0700 +++ b/hotspot/src/share/vm/opto/type.hpp Tue Jul 14 18:58:38 2015 +0200 @@ -412,8 +412,9 @@ static const Type* get_typeflow_type(ciType* type); static const Type* make_from_constant(ciConstant constant, - bool require_constant = false, - bool is_autobox_cache = false); + bool require_constant = false); + + static const Type* make_constant(ciField* field, Node* obj); // Speculative type helper methods. See TypePtr. virtual const TypePtr* speculative() const { return NULL; } @@ -973,8 +974,7 @@ // may return a non-singleton type. // If require_constant, produce a NULL if a singleton is not possible. static const TypeOopPtr* make_from_constant(ciObject* o, - bool require_constant = false, - bool not_null_elements = false); + bool require_constant = false); // Make a generic (unclassed) pointer to an oop. static const TypeOopPtr* make(PTR ptr, int offset, int instance_id, @@ -1184,6 +1184,8 @@ const TypeAryPtr* cast_to_stable(bool stable, int stable_dimension = 1) const; int stable_dimension() const; + const TypeAryPtr* cast_to_autobox_cache(bool cache) const; + // Convenience common pre-built types. static const TypeAryPtr *RANGE; static const TypeAryPtr *OOPS; @@ -1674,12 +1676,12 @@ inline const TypePtr* Type::make_ptr() const { return (_base == NarrowOop) ? is_narrowoop()->get_ptrtype() : - ((_base == NarrowKlass) ? is_narrowklass()->get_ptrtype() : - (isa_ptr() ? is_ptr() : NULL)); + ((_base == NarrowKlass) ? is_narrowklass()->get_ptrtype() : + isa_ptr()); } inline const TypeOopPtr* Type::make_oopptr() const { - return (_base == NarrowOop) ? is_narrowoop()->get_ptrtype()->is_oopptr() : is_oopptr(); + return (_base == NarrowOop) ? is_narrowoop()->get_ptrtype()->isa_oopptr() : isa_oopptr(); } inline const TypeNarrowOop* Type::make_narrowoop() const { @@ -1689,7 +1691,7 @@ inline const TypeNarrowKlass* Type::make_narrowklass() const { return (_base == NarrowKlass) ? is_narrowklass() : - (isa_ptr() ? TypeNarrowKlass::make(is_ptr()) : NULL); + (isa_ptr() ? TypeNarrowKlass::make(is_ptr()) : NULL); } inline bool Type::is_floatingpoint() const { diff -r 13420c0a3ad5 -r 7c15975834cd hotspot/test/compiler/unsafe/UnsafeGetConstantField.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/test/compiler/unsafe/UnsafeGetConstantField.java Tue Jul 14 18:58:38 2015 +0200 @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary tests on constant folding of unsafe get operations + * @library /testlibrary /../../test/lib + * @run main/bootclasspath -XX:+UnlockDiagnosticVMOptions + * -Xbatch -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:+UseUnalignedAccesses + * java.lang.invoke.UnsafeGetConstantField + * @run main/bootclasspath -XX:+UnlockDiagnosticVMOptions + * -Xbatch -XX:-TieredCompilation + * -XX:+FoldStableValues + * -XX:-UseUnalignedAccesses + * java.lang.invoke.UnsafeGetConstantField + */ +package java.lang.invoke; + +import jdk.internal.org.objectweb.asm.*; +import jdk.test.lib.Asserts; +import jdk.test.lib.Utils; +import sun.misc.Unsafe; +import static jdk.internal.org.objectweb.asm.Opcodes.*; + +public class UnsafeGetConstantField { + static final Class THIS_CLASS = UnsafeGetConstantField.class; + + static final Unsafe U = Utils.getUnsafe(); + + public static void main(String[] args) { + testUnsafeGetAddress(); + testUnsafeGetField(); + testUnsafeGetFieldUnaligned(); + System.out.println("TEST PASSED"); + } + + static final long nativeAddr = U.allocateMemory(16); + static void testUnsafeGetAddress() { + long cookie = 0x12345678L; + U.putAddress(nativeAddr, cookie); + for (int i = 0; i < 20_000; i++) { + Asserts.assertEquals(checkGetAddress(), cookie); + } + } + @DontInline + static long checkGetAddress() { + return U.getAddress(nativeAddr); + } + + static void testUnsafeGetField() { + int[] testedFlags = new int[] { 0, ACC_STATIC, ACC_FINAL, (ACC_STATIC | ACC_FINAL) }; + boolean[] boolValues = new boolean[] { false, true }; + String[] modes = new String[] { "", "Volatile" }; + + for (JavaType t : JavaType.values()) { + for (int flags : testedFlags) { + for (boolean stable : boolValues) { + for (boolean hasDefaultValue : boolValues) { + for (String suffix : modes) { + runTest(t, flags, stable, hasDefaultValue, suffix); + } + } + } + } + } + } + + static void testUnsafeGetFieldUnaligned() { + JavaType[] types = new JavaType[] { JavaType.S, JavaType.C, JavaType.I, JavaType.J }; + int[] testedFlags = new int[] { 0, ACC_STATIC, ACC_FINAL, (ACC_STATIC | ACC_FINAL) }; + boolean[] boolValues = new boolean[] { false, true }; + + for (JavaType t : types) { + for (int flags : testedFlags) { + for (boolean stable : boolValues) { + for (boolean hasDefaultValue : boolValues) { + runTest(t, flags, stable, hasDefaultValue, "Unaligned"); + } + } + } + } + } + + static void runTest(JavaType t, int flags, boolean stable, boolean hasDefaultValue, String postfix) { + Generator g = new Generator(t, flags, stable, hasDefaultValue, postfix); + Test test = g.generate(); + System.out.printf("type=%s flags=%d stable=%b default=%b post=%s\n", + t.typeName, flags, stable, hasDefaultValue, postfix); + // Trigger compilation + for (int i = 0; i < 20_000; i++) { + Asserts.assertEQ(test.testDirect(), test.testUnsafe()); + } + } + + interface Test { + Object testDirect(); + Object testUnsafe(); + } + + enum JavaType { + Z("Boolean", true), + B("Byte", new Byte((byte)-1)), + S("Short", new Short((short)-1)), + C("Char", Character.MAX_VALUE), + I("Int", -1), + J("Long", -1L), + F("Float", -1F), + D("Double", -1D), + L("Object", new Object()); + + String typeName; + Object value; + String wrapper; + JavaType(String name, Object value) { + this.typeName = name; + this.value = value; + this.wrapper = internalName(value.getClass()); + } + + String desc() { + if (this == JavaType.L) { + return "Ljava/lang/Object;"; + } else { + return toString(); + } + } + } + + static String internalName(Class cls) { + return cls.getName().replace('.', '/'); + } + static String descriptor(Class cls) { + return String.format("L%s;", internalName(cls)); + } + + /** + * Sample generated class: + * static class T1 implements Test { + * final int f = -1; + * static final long FIELD_OFFSET; + * static final T1 t = new T1(); + * static { + * FIELD_OFFSET = U.objectFieldOffset(T1.class.getDeclaredField("f")); + * } + * public Object testDirect() { return t.f; } + * public Object testUnsafe() { return U.getInt(t, FIELD_OFFSET); } + * } + */ + static class Generator { + static final String FIELD_NAME = "f"; + static final String UNSAFE_NAME = internalName(Unsafe.class); + static final String UNSAFE_DESC = descriptor(Unsafe.class); + + final JavaType type; + final int flags; + final boolean stable; + final boolean hasDefaultValue; + final String nameSuffix; + + final String className; + final String classDesc; + final String fieldDesc; + + Generator(JavaType t, int flags, boolean stable, boolean hasDefaultValue, String suffix) { + this.type = t; + this.flags = flags; + this.stable = stable; + this.hasDefaultValue = hasDefaultValue; + this.nameSuffix = suffix; + + fieldDesc = type.desc(); + className = String.format("%s$Test%s%s__f=%d__s=%b__d=%b", internalName(THIS_CLASS), type.typeName, + suffix, flags, stable, hasDefaultValue); + classDesc = String.format("L%s;", className); + } + + byte[] generateClassFile() { + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); + cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER, className, null, "java/lang/Object", + new String[]{ internalName(Test.class) }); + + // Declare fields + cw.visitField(ACC_FINAL | ACC_STATIC, "t", classDesc, null, null).visitEnd(); + cw.visitField(ACC_FINAL | ACC_STATIC, "FIELD_OFFSET", "J", null, null).visitEnd(); + cw.visitField(ACC_FINAL | ACC_STATIC, "U", UNSAFE_DESC, null, null).visitEnd(); + if (isStatic()) { + cw.visitField(ACC_FINAL | ACC_STATIC, "STATIC_BASE", "Ljava/lang/Object;", null, null).visitEnd(); + } + + FieldVisitor fv = cw.visitField(flags, FIELD_NAME, fieldDesc, null, null); + if (stable) { + fv.visitAnnotation(descriptor(Stable.class), true); + } + fv.visitEnd(); + + // Methods + { // + MethodVisitor mv = cw.visitMethod(0, "", "()V", null, null); + mv.visitCode(); + + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + if (!isStatic()) { + initField(mv); + } + mv.visitInsn(RETURN); + + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + { // public Object testDirect() { return t.f; } + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "testDirect", "()Ljava/lang/Object;", null, null); + mv.visitCode(); + + getFieldValue(mv); + wrapResult(mv); + mv.visitInsn(ARETURN); + + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + { // public Object testUnsafe() { return U.getInt(t, FIELD_OFFSET); } + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "testUnsafe", "()Ljava/lang/Object;", null, null); + mv.visitCode(); + + getFieldValueUnsafe(mv); + wrapResult(mv); + mv.visitInsn(ARETURN); + + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + { // + MethodVisitor mv = cw.visitMethod(ACC_STATIC, "", "()V", null, null); + mv.visitCode(); + + // Cache Unsafe instance + mv.visitMethodInsn(INVOKESTATIC, UNSAFE_NAME, "getUnsafe", "()"+UNSAFE_DESC, false); + mv.visitFieldInsn(PUTSTATIC, className, "U", UNSAFE_DESC); + + // Create test object instance + mv.visitTypeInsn(NEW, className); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, className, "", "()V", false); + mv.visitFieldInsn(PUTSTATIC, className, "t", classDesc); + + // Compute field offset + getUnsafe(mv); + getField(mv); + mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, (isStatic() ? "staticFieldOffset" : "objectFieldOffset"), + "(Ljava/lang/reflect/Field;)J", false); + mv.visitFieldInsn(PUTSTATIC, className, "FIELD_OFFSET", "J"); + + // Compute base offset for static field + if (isStatic()) { + getUnsafe(mv); + getField(mv); + mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, "staticFieldBase", "(Ljava/lang/reflect/Field;)Ljava/lang/Object;", false); + mv.visitFieldInsn(PUTSTATIC, className, "STATIC_BASE", "Ljava/lang/Object;"); + initField(mv); + } + + mv.visitInsn(RETURN); + mv.visitMaxs(0, 0); + mv.visitEnd(); + } + + return cw.toByteArray(); + } + + Test generate() { + byte[] classFile = generateClassFile(); + Class c = U.defineClass(className, classFile, 0, classFile.length, THIS_CLASS.getClassLoader(), null); + try { + return (Test) c.newInstance(); + } catch(Exception e) { + throw new Error(e); + } + } + + boolean isStatic() { + return (flags & ACC_STATIC) > 0; + } + boolean isFinal() { + return (flags & ACC_FINAL) > 0; + } + void getUnsafe(MethodVisitor mv) { + mv.visitFieldInsn(GETSTATIC, className, "U", UNSAFE_DESC); + } + void getField(MethodVisitor mv) { + mv.visitLdcInsn(Type.getType(classDesc)); + mv.visitLdcInsn(FIELD_NAME); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getDeclaredField", "(Ljava/lang/String;)Ljava/lang/reflect/Field;", false); + } + void getFieldValue(MethodVisitor mv) { + if (isStatic()) { + mv.visitFieldInsn(GETSTATIC, className, FIELD_NAME, fieldDesc); + } else { + mv.visitFieldInsn(GETSTATIC, className, "t", classDesc); + mv.visitFieldInsn(GETFIELD, className, FIELD_NAME, fieldDesc); + } + } + void getFieldValueUnsafe(MethodVisitor mv) { + getUnsafe(mv); + if (isStatic()) { + mv.visitFieldInsn(GETSTATIC, className, "STATIC_BASE", "Ljava/lang/Object;"); + } else { + mv.visitFieldInsn(GETSTATIC, className, "t", classDesc); + } + mv.visitFieldInsn(GETSTATIC, className, "FIELD_OFFSET", "J"); + String name = "get" + type.typeName + nameSuffix; + mv.visitMethodInsn(INVOKEVIRTUAL, UNSAFE_NAME, name, "(Ljava/lang/Object;J)" + type.desc(), false); + } + void wrapResult(MethodVisitor mv) { + if (type != JavaType.L) { + String desc = String.format("(%s)L%s;", type.desc(), type.wrapper); + mv.visitMethodInsn(INVOKESTATIC, type.wrapper, "valueOf", desc, false); + } + } + void initField(MethodVisitor mv) { + if (hasDefaultValue) { + return; // Nothing to do + } + if (!isStatic()) { + mv.visitVarInsn(ALOAD, 0); + } + switch (type) { + case L: { + mv.visitTypeInsn(NEW, "java/lang/Object"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + + break; + } + default: { + mv.visitLdcInsn(type.value); + break; + } + } + mv.visitFieldInsn((isStatic() ? PUTSTATIC : PUTFIELD), className, FIELD_NAME, fieldDesc); + } + } +}