This commit is contained in:
Vladimir Kozlov 2015-07-14 18:58:38 +02:00
commit 10f06d081d
32 changed files with 2770 additions and 478 deletions

View File

@ -10,3 +10,4 @@
.igv.log
^.hgtip
.DS_Store
\.class$

View File

@ -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 @@ inline BasicObjectLock* frame::interpreter_frame_monitor_begin() const {
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);
}

View File

@ -3433,6 +3433,376 @@ void MacroAssembler::char_arrays_equalsImm(Register str1_reg, Register str2_reg,
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

View File

@ -677,6 +677,31 @@ class MacroAssembler: public Assembler {
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
//

View File

@ -423,6 +423,13 @@ inline void MacroAssembler::trap_range_check_ge(Register a, int si16) {
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

View File

@ -10930,7 +10930,7 @@ instruct partialSubtypeCheck(iRegPdst result, iRegP_N2P subklass, iRegP_N2P supe
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 %{

View File

@ -2053,6 +2053,79 @@ class StubGenerator: public StubCodeGenerator {
__ 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 @@ class StubGenerator: public StubCodeGenerator {
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:

View File

@ -198,6 +198,10 @@ void VM_Version::initialize() {
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 @@ void VM_Version::initialize() {
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.

View File

@ -308,7 +308,7 @@ void VM_Version::initialize() {
}
} 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);
}

View File

@ -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 @@ all: logc.jar
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

View File

@ -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 @@ package com.sun.hotspot.tools.compiler;
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 @@ public abstract class BasicLogEvent implements LogEvent {
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);
}

View File

@ -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.ArrayDeque;
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<CallSite> 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<UncommonTrap> 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<CallSite>());
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 class CallSite {
}
public void print(PrintStream stream) {
print(stream, 0);
print(stream, 0, true, false);
}
void emit(PrintStream stream, int indent) {
@ -92,21 +170,14 @@ public class CallSite {
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 @@ public class CallSite {
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 @@ public class CallSite {
return calls;
}
public void setCalls(List<CallSite> calls) {
this.calls = calls;
public List<UncommonTrap> 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<UncommonTrap>();
}
traps.add(e);
}
void setEndNodes(int n) {
@ -216,21 +289,30 @@ public class CallSite {
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<CallSite> 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 @@ public class CallSite {
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<CallSite> findCallSite2(CallSite site) {
if (calls == null) {
return null;
@ -260,7 +347,7 @@ public class CallSite {
for (CallSite c : calls) {
if (c.matches(site)) {
ArrayDeque<CallSite> stack = new ArrayDeque<CallSite>();
ArrayDeque<CallSite> stack = new ArrayDeque<>();
stack.push(c);
return stack;
} else {

View File

@ -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 @@ package com.sun.hotspot.tools.compiler;
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<Phase> phases = new ArrayList<Phase>(4);
/**
* The phases through which this compilation goes.
*/
private ArrayList<Phase> phases = new ArrayList<>(4);
/**
* In case this compilation fails, the reason for that.
*/
private String failureReason;
Compilation(int id) {
@ -52,9 +124,17 @@ public class Compilation implements LogEvent {
void reset() {
call = new CallSite();
lateInlineCall = new CallSite();
phases = new ArrayList<Phase>(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 @@ public class Compilation implements LogEvent {
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 @@ public class Compilation implements LogEvent {
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 @@ public class Compilation implements LogEvent {
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 @@ public class Compilation implements LogEvent {
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;
}

View File

@ -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 @@ import java.util.regex.*;
* 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 {
private Reader reader;
private char[] buffer = new char[4096];
@ -55,32 +54,38 @@ class LogCleanupReader extends Reader {
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;
}

View File

@ -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<LogEvent> defaultSort = LogParser.sortByStart;
Comparator<LogEvent> 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 (args[index].equals("-n")) {
defaultSort = LogParser.sortByNameAndStart;
} else if (a.equals("-n")) {
sort = LogParser.sortByNameAndStart;
index++;
} else if (args[index].equals("-s")) {
defaultSort = LogParser.sortByStart;
} else if (a.equals("-s")) {
sort = LogParser.sortByStart;
index++;
} else if (args[index].equals("-c")) {
} else if (a.equals("-t")) {
printTimeStamps = true;
index++;
} 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 @@ public class LogCompilation extends DefaultHandler implements ErrorHandler, Cons
usage(1);
}
if (compare) {
compareLogs(index, args);
return;
}
while (index < args.length) {
ArrayList<LogEvent> events = LogParser.parse(args[index], cleanup);
ArrayList<LogEvent> 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 @@ public class LogCompilation extends DefaultHandler implements ErrorHandler, Cons
}
}
/**
* Print extensive statistics from parsed log files.
*/
public static void printStatistics(ArrayList<LogEvent> 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<String, Double> phaseTime = new LinkedHashMap<String, Double>(7);
LinkedHashMap<String, Integer> phaseNodes = new LinkedHashMap<String, Integer>(7);
// track time spent in compiler phases
LinkedHashMap<String, Double> phaseTime = new LinkedHashMap<>(7);
// track nodes created per phase
LinkedHashMap<String, Integer> phaseNodes = new LinkedHashMap<>(7);
double elapsed = 0;
for (LogEvent e : events) {
@ -137,18 +208,17 @@ public class LogCompilation extends DefaultHandler implements ErrorHandler, Cons
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 @@ public class LogCompilation extends DefaultHandler implements ErrorHandler, Cons
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 @@ public class LogCompilation extends DefaultHandler implements ErrorHandler, Cons
}
}
}
/**
* 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<MethodBCIPair,MethodBCIPair> methods = new HashMap<>();
ArrayList<HashMap<MethodBCIPair,Object>> 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<LogEvent> events = LogParser.parse(args[index], false);
HashMap<MethodBCIPair,Object> 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<Object> l = new LinkedList<>();
l.add(other);
l.add(comp);
compiles.put(key, l);
} else {
List<Object> l = (List<Object>) 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<MethodBCIPair,Object> 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<Object>) e).size();
lastc = (Compilation) ((List<Object>) 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<LogEvent> events, PrintStream out) {
// map method names to a list of log events
LinkedHashMap<String, ArrayList<LogEvent>> traps = new LinkedHashMap<>();
// map compilation IDs to compilations
HashMap<Integer, Compilation> 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<LogEvent> 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<LogEvent> elist = traps.get(name);
if (elist == null) {
elist = new ArrayList<LogEvent>();
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<LogEvent> 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();
}
}
}

View File

@ -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);
}

View File

@ -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 @@ class MakeNotEntrantEvent extends BasicLogEvent {
return nmethod;
}
public void print(PrintStream stream) {
public void print(PrintStream stream, boolean printID) {
if (isZombie()) {
stream.printf("%s make_zombie\n", getId());
} else {

View File

@ -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 @@ package com.sun.hotspot.tools.compiler;
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 @@ public class Method implements Constants {
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 @@ public class Method implements Constants {
return getHolder() + "::" + getName() + " (" + getBytes() + " bytes)";
}
public String getFullName() {
return getHolder().replace('/', '.') + "." + getName() + signature;
}
public String getHolder() {
return holder;
}
@ -86,12 +138,16 @@ public class Method implements Constants {
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 @@ public class Method implements Constants {
@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();
}
}

View File

@ -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 @@ package com.sun.hotspot.tools.compiler;
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 @@ public class NMethod extends BasicLogEvent {
size = sz;
}
public void print(PrintStream out) {
public void print(PrintStream out, boolean printID) {
// XXX Currently we do nothing
// throw new InternalError();
}

View File

@ -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 @@ package com.sun.hotspot.tools.compiler;
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 class Phase extends BasicLogEvent {
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 @@ public class Phase extends BasicLogEvent {
}
@Override
public void print(PrintStream stream) {
public void print(PrintStream stream, boolean printID) {
throw new UnsupportedOperationException("Not supported yet.");
}
}

View File

@ -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();
}
}

View File

@ -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<String> jvmsMethods = new ArrayList<>();
private List<Integer> jvmsBCIs = new ArrayList<>();
UncommonTrapEvent(double s, String i, String r, String a, int c) {
super(s, i);
@ -40,20 +56,26 @@ class UncommonTrapEvent extends BasicLogEvent {
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 @@ class UncommonTrapEvent extends BasicLogEvent {
this.count = count;
}
public String getJvms() {
return jvms;
}
public void setJvms(String jvms) {
this.jvms = 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) {
this.compilation = compilation;
super.setCompilation(compilation);
// Attempt to associate a bytecode with with this trap
CallSite site = compilation.getCall();
int i = 0;
try {
List<UncommonTrap> 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 = "<unknown>";
}
}
public void addMethodAndBCI(String method, int bci) {
jvmsMethods.add(0, method);
jvmsBCIs.add(0, bci);
}
}

View File

@ -181,6 +181,17 @@ public:
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);

View File

@ -501,8 +501,8 @@ void CompileTask::log_task(xmlStream* log) {
methodHandle method(thread, this->method());
ResourceMark rm(thread);
// <task id='9' method='M' osr_bci='X' level='1' blocking='1' stamp='1.234'>
log->print(" compile_id='%d'", _compile_id);
// <task compiler='Cx' id='9' method='M' osr_bci='X' level='1' blocking='1' stamp='1.234'>
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'

View File

@ -2687,35 +2687,48 @@ bool LibraryCallKit::inline_unsafe_access(bool is_native_ptr, bool is_store, Bas
// 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);
}
}
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;
}
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

View File

@ -539,10 +539,6 @@ class Parse : public GraphKit {
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();

View File

@ -1478,8 +1478,10 @@ void Parse::do_one_bytecode() {
}
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;

View File

@ -149,51 +149,10 @@ void Parse::do_get_xxx(Node* obj, ciField* field, bool is_field) {
// 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 @@ void Parse::do_put_xxx(Node* obj, ciField* field, bool is_field) {
}
}
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;

View File

@ -200,8 +200,7 @@ const Type* Type::get_typeflow_type(ciType* type) {
//-----------------------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,15 +221,58 @@ const Type* Type::make_from_constant(ciConstant constant,
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);
}
}
}
return con_type;
}
//------------------------------make-------------------------------------------
// Create a simple Type, with default empty symbol sets. Then hashcons it
// and look for an existing copy in the type dictionary.
@ -3009,9 +3051,7 @@ const TypeOopPtr* TypeOopPtr::make_from_klass_common(ciKlass *klass, bool klass_
//------------------------------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 @@ const TypeOopPtr* TypeOopPtr::make_from_constant(ciObject* o,
// 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 @@ const TypeOopPtr* TypeOopPtr::make_from_constant(ciObject* o,
} 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 @@ const TypeAryPtr* TypeAryPtr::cast_to_size(const TypeInt* new_size) const {
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 @@ int TypeAryPtr::stable_dimension() const {
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 @@ int TypeMetadataPtr::hash(void) const {
// 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);
}

View File

@ -412,8 +412,9 @@ public:
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 @@ public:
// 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 @@ public:
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 TypeKlassPtr *Type::is_klassptr() const {
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 TypeNarrowOop* Type::make_narrowoop() const {
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 {

View File

@ -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
{ // <init>
MethodVisitor mv = cw.visitMethod(0, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()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();
}
{ // <clinit>
MethodVisitor mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()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, "<init>", "()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", "<init>", "()V", false);
break;
}
default: {
mv.visitLdcInsn(type.value);
break;
}
}
mv.visitFieldInsn((isStatic() ? PUTSTATIC : PUTFIELD), className, FIELD_NAME, fieldDesc);
}
}
}