Merge
This commit is contained in:
commit
10f06d081d
@ -10,3 +10,4 @@
|
||||
.igv.log
|
||||
^.hgtip
|
||||
.DS_Store
|
||||
\.class$
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
//
|
||||
|
@ -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
|
||||
|
@ -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 %{
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.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;
|
||||
void add(UncommonTrap e) {
|
||||
if (traps == null) {
|
||||
traps = new ArrayList<UncommonTrap>();
|
||||
}
|
||||
|
||||
public static void setCompat(boolean aCompat) {
|
||||
compat = aCompat;
|
||||
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 {
|
||||
|
@ -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,21 +152,33 @@ 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");
|
||||
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");
|
||||
for (CallSite site : getLateInlineCall().getCalls()) {
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
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, true);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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 {
|
||||
|
@ -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() {
|
||||
@ -122,9 +178,12 @@ public class Method implements Constants {
|
||||
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);
|
||||
return holder.equals(other.holder) && name.equals(other.name) && signature.equals(other.signature);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return holder.hashCode() ^ name.hashCode();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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,19 +56,25 @@ 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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'
|
||||
|
@ -2688,10 +2688,22 @@ bool LibraryCallKit::inline_unsafe_access(bool is_native_ptr, bool is_store, Bas
|
||||
if (need_mem_bar) insert_mem_bar(Op_MemBarCPUOrder);
|
||||
|
||||
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
|
||||
Node* p = make_load(control(), adr, value_type, type, adr_type, mo, LoadNode::Pinned, is_volatile);
|
||||
p = make_load(control(), adr, value_type, type, adr_type, mo, LoadNode::Pinned, is_volatile);
|
||||
// load value
|
||||
switch (type) {
|
||||
case T_BOOLEAN:
|
||||
@ -2717,6 +2729,7 @@ bool LibraryCallKit::inline_unsafe_access(bool is_native_ptr, bool is_store, Bas
|
||||
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
|
||||
// the end of this method. So, pushing the load onto the stack at a later
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -149,53 +149,12 @@ 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)) {
|
||||
const Type* con_type = Type::make_constant(field, obj);
|
||||
if (con_type != NULL) {
|
||||
push_node(con_type->basic_type(), makecon(con_type));
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ciType* field_klass = field->type();
|
||||
bool is_vol = field->is_volatile();
|
||||
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
@ -1675,11 +1677,11 @@ 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));
|
||||
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 {
|
||||
|
370
hotspot/test/compiler/unsafe/UnsafeGetConstantField.java
Normal file
370
hotspot/test/compiler/unsafe/UnsafeGetConstantField.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user