mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-12 06:29:37 +00:00
8267532: C2: Profile and prune untaken exception handlers
8310011: Arena with try-with-resources is slower than it should be Reviewed-by: thartmann, vlivanov
This commit is contained in:
parent
464dc3dab5
commit
a5ccd3beaf
@ -3506,7 +3506,6 @@ int GraphBuilder::recursive_inline_level(ciMethod* cur_callee) const {
|
||||
return recur_level;
|
||||
}
|
||||
|
||||
|
||||
bool GraphBuilder::try_inline(ciMethod* callee, bool holder_known, bool ignore_return, Bytecodes::Code bc, Value receiver) {
|
||||
const char* msg = nullptr;
|
||||
|
||||
|
||||
@ -42,6 +42,8 @@
|
||||
ciMethodData::ciMethodData(MethodData* md)
|
||||
: ciMetadata(md),
|
||||
_data_size(0), _extra_data_size(0), _data(nullptr),
|
||||
_parameters_data_offset(0),
|
||||
_exception_handlers_data_offset(0),
|
||||
// Set an initial hint. Don't use set_hint_di() because
|
||||
// first_di() may be out of bounds if data_size is 0.
|
||||
_hint_di(first_di()),
|
||||
@ -50,8 +52,7 @@ ciMethodData::ciMethodData(MethodData* md)
|
||||
// Initialize the escape information (to "don't know.");
|
||||
_eflags(0), _arg_local(0), _arg_stack(0), _arg_returned(0),
|
||||
_invocation_counter(0),
|
||||
_orig(),
|
||||
_parameters(nullptr) {}
|
||||
_orig() {}
|
||||
|
||||
// Check for entries that reference an unloaded method
|
||||
class PrepareExtraDataClosure : public CleanExtraDataClosure {
|
||||
@ -134,8 +135,16 @@ void ciMethodData::load_remaining_extra_data() {
|
||||
|
||||
// Copy the extra data once it is prepared (i.e. cache populated, no release of extra data lock anymore)
|
||||
Copy::disjoint_words_atomic((HeapWord*) mdo->extra_data_base(),
|
||||
(HeapWord*)((address) _data + _data_size),
|
||||
(_extra_data_size - mdo->parameters_size_in_bytes()) / HeapWordSize);
|
||||
(HeapWord*) extra_data_base(),
|
||||
// copy everything from extra_data_base() up to parameters_data_base()
|
||||
pointer_delta(parameters_data_base(), extra_data_base(), HeapWordSize));
|
||||
|
||||
// skip parameter data copying. Already done in 'load_data'
|
||||
|
||||
// copy exception handler data
|
||||
Copy::disjoint_words_atomic((HeapWord*) mdo->exception_handler_data_base(),
|
||||
(HeapWord*) exception_handler_data_base(),
|
||||
exception_handler_data_size() / HeapWordSize);
|
||||
|
||||
// speculative trap entries also hold a pointer to a Method so need to be translated
|
||||
DataLayout* dp_src = mdo->extra_data_base();
|
||||
@ -195,12 +204,17 @@ bool ciMethodData::load_data() {
|
||||
// args_data_limit: ---------------------------
|
||||
// | parameter data entries |
|
||||
// | ... |
|
||||
// param_data_limit: ---------------------------
|
||||
// | ex handler data entries |
|
||||
// | ... |
|
||||
// extra_data_limit: ---------------------------
|
||||
//
|
||||
// _data_size = extra_data_base - data_base
|
||||
// _extra_data_size = extra_data_limit - extra_data_base
|
||||
// total_size = _data_size + _extra_data_size
|
||||
// args_data_limit = data_base + total_size - parameter_data_size
|
||||
// args_data_limit = param_data_base
|
||||
// param_data_limit = exception_handler_data_base
|
||||
// extra_data_limit = extra_data_limit
|
||||
|
||||
#ifndef ZERO
|
||||
// Some Zero platforms do not have expected alignment, and do not use
|
||||
@ -218,12 +232,15 @@ bool ciMethodData::load_data() {
|
||||
Copy::disjoint_words_atomic((HeapWord*) mdo->data_base(),
|
||||
(HeapWord*) _data,
|
||||
_data_size / HeapWordSize);
|
||||
// Copy offsets. This is used below
|
||||
_parameters_data_offset = mdo->parameters_type_data_di();
|
||||
_exception_handlers_data_offset = mdo->exception_handlers_data_di();
|
||||
|
||||
int parameters_data_size = mdo->parameters_size_in_bytes();
|
||||
if (parameters_data_size > 0) {
|
||||
// Snapshot the parameter data
|
||||
Copy::disjoint_words_atomic((HeapWord*) mdo->args_data_limit(),
|
||||
(HeapWord*) ((address)_data + total_size - parameters_data_size),
|
||||
Copy::disjoint_words_atomic((HeapWord*) mdo->parameters_data_base(),
|
||||
(HeapWord*) parameters_data_base(),
|
||||
parameters_data_size / HeapWordSize);
|
||||
}
|
||||
// Traverse the profile data, translating any oops into their
|
||||
@ -237,12 +254,12 @@ bool ciMethodData::load_data() {
|
||||
data = mdo->next_data(data);
|
||||
}
|
||||
if (mdo->parameters_type_data() != nullptr) {
|
||||
_parameters = data_layout_at(mdo->parameters_type_data_di());
|
||||
ciParametersTypeData* parameters = new ciParametersTypeData(_parameters);
|
||||
DataLayout* parameters_data = data_layout_at(_parameters_data_offset);
|
||||
ciParametersTypeData* parameters = new ciParametersTypeData(parameters_data);
|
||||
parameters->translate_from(mdo->parameters_type_data());
|
||||
}
|
||||
|
||||
assert((DataLayout*) ((address)_data + total_size - parameters_data_size) == args_data_limit(),
|
||||
assert((DataLayout*) ((address)_data + total_size - parameters_data_size - exception_handler_data_size()) == args_data_limit(),
|
||||
"sanity - parameter data starts after the argument data of the single ArgInfoData entry");
|
||||
load_remaining_extra_data();
|
||||
|
||||
@ -367,16 +384,24 @@ ciProfileData* ciMethodData::next_data(ciProfileData* current) {
|
||||
return next;
|
||||
}
|
||||
|
||||
DataLayout* ciMethodData::next_data_layout(DataLayout* current) {
|
||||
DataLayout* ciMethodData::next_data_layout_helper(DataLayout* current, bool extra) {
|
||||
int current_index = dp_to_di((address)current);
|
||||
int next_index = current_index + current->size_in_bytes();
|
||||
if (out_of_bounds(next_index)) {
|
||||
if (extra ? out_of_bounds_extra(next_index) : out_of_bounds(next_index)) {
|
||||
return nullptr;
|
||||
}
|
||||
DataLayout* next = data_layout_at(next_index);
|
||||
return next;
|
||||
}
|
||||
|
||||
DataLayout* ciMethodData::next_data_layout(DataLayout* current) {
|
||||
return next_data_layout_helper(current, false);
|
||||
}
|
||||
|
||||
DataLayout* ciMethodData::next_extra_data_layout(DataLayout* current) {
|
||||
return next_data_layout_helper(current, true);
|
||||
}
|
||||
|
||||
ciProfileData* ciMethodData::bci_to_extra_data(int bci, ciMethod* m, bool& two_free_slots) {
|
||||
DataLayout* dp = extra_data_base();
|
||||
DataLayout* end = args_data_limit();
|
||||
@ -438,6 +463,19 @@ ciProfileData* ciMethodData::bci_to_data(int bci, ciMethod* m) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ciBitData ciMethodData::exception_handler_bci_to_data(int bci) {
|
||||
assert(ProfileExceptionHandlers, "not profiling");
|
||||
assert(_data != nullptr, "must be initialized");
|
||||
for (DataLayout* data = exception_handler_data_base(); data < exception_handler_data_limit(); data = next_extra_data_layout(data)) {
|
||||
assert(data != nullptr, "out of bounds?");
|
||||
if (data->bci() == bci) {
|
||||
return ciBitData(data);
|
||||
}
|
||||
}
|
||||
// called with invalid bci or wrong Method/MethodData
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
||||
// Conservatively decode the trap_state of a ciProfileData.
|
||||
int ciMethodData::has_trap_at(ciProfileData* data, int reason) {
|
||||
typedef Deoptimization::DeoptReason DR_t;
|
||||
@ -612,7 +650,7 @@ uint ciMethodData::arg_modified(int arg) const {
|
||||
}
|
||||
|
||||
ciParametersTypeData* ciMethodData::parameters_type_data() const {
|
||||
return _parameters != nullptr ? new ciParametersTypeData(_parameters) : nullptr;
|
||||
return parameter_data_size() != 0 ? new ciParametersTypeData(data_layout_at(_parameters_data_offset)) : nullptr;
|
||||
}
|
||||
|
||||
ByteSize ciMethodData::offset_of_slot(ciProfileData* data, ByteSize slot_offset_in_data) {
|
||||
|
||||
@ -379,6 +379,10 @@ private:
|
||||
// Data entries
|
||||
intptr_t* _data;
|
||||
|
||||
// layout of _data
|
||||
int _parameters_data_offset;
|
||||
int _exception_handlers_data_offset;
|
||||
|
||||
// Cached hint for data_layout_before()
|
||||
int _hint_di;
|
||||
|
||||
@ -403,17 +407,13 @@ private:
|
||||
// Coherent snapshot of original header.
|
||||
MethodData::CompilerCounters _orig;
|
||||
|
||||
// Area dedicated to parameters. null if no parameter profiling for this method.
|
||||
DataLayout* _parameters;
|
||||
int parameters_size() const {
|
||||
return _parameters == nullptr ? 0 : parameters_type_data()->size_in_bytes();
|
||||
}
|
||||
|
||||
ciMethodData(MethodData* md = nullptr);
|
||||
|
||||
// Accessors
|
||||
int data_size() const { return _data_size; }
|
||||
int extra_data_size() const { return _extra_data_size; }
|
||||
int parameter_data_size() const { return _exception_handlers_data_offset - _parameters_data_offset; }
|
||||
int exception_handler_data_size() const { return dp_to_di((address) exception_handler_data_limit()) - _exception_handlers_data_offset; }
|
||||
intptr_t * data() const { return _data; }
|
||||
|
||||
MethodData* get_MethodData() const {
|
||||
@ -425,7 +425,7 @@ private:
|
||||
void print_impl(outputStream* st);
|
||||
|
||||
DataLayout* data_layout_at(int data_index) const {
|
||||
assert(data_index % sizeof(intptr_t) == 0, "unaligned");
|
||||
assert(data_index % sizeof(intptr_t) == 0, "unaligned: %d", data_index);
|
||||
return (DataLayout*) (((address)_data) + data_index);
|
||||
}
|
||||
|
||||
@ -433,6 +433,12 @@ private:
|
||||
return data_index >= data_size();
|
||||
}
|
||||
|
||||
bool out_of_bounds_extra(int data_index) {
|
||||
return data_index < data_size() || data_index >= data_size() + extra_data_size();
|
||||
}
|
||||
|
||||
DataLayout* next_data_layout_helper(DataLayout* current, bool extra);
|
||||
|
||||
// hint accessors
|
||||
int hint_di() const { return _hint_di; }
|
||||
void set_hint_di(int di) {
|
||||
@ -500,7 +506,7 @@ public:
|
||||
bool load_data();
|
||||
|
||||
// Convert a dp (data pointer) to a di (data index).
|
||||
int dp_to_di(address dp) {
|
||||
int dp_to_di(address dp) const {
|
||||
return pointer_delta_as_int(dp, ((address)_data));
|
||||
}
|
||||
|
||||
@ -511,17 +517,28 @@ public:
|
||||
ciProfileData* first_data() { return data_at(first_di()); }
|
||||
ciProfileData* next_data(ciProfileData* current);
|
||||
DataLayout* next_data_layout(DataLayout* current);
|
||||
DataLayout* next_extra_data_layout(DataLayout* current);
|
||||
bool is_valid(ciProfileData* current) { return current != nullptr; }
|
||||
bool is_valid(DataLayout* current) { return current != nullptr; }
|
||||
|
||||
// pointers to sections in _data
|
||||
// NOTE: these may be called before ciMethodData::load_data
|
||||
// this works out since everything is initialized to 0 (i.e. there will appear to be no data)
|
||||
DataLayout* extra_data_base() const { return data_layout_at(data_size()); }
|
||||
DataLayout* args_data_limit() const { return data_layout_at(data_size() + extra_data_size() -
|
||||
parameters_size()); }
|
||||
DataLayout* extra_data_limit() const { return data_layout_at(data_size() + extra_data_size()); }
|
||||
// pointers to sections in extra data
|
||||
DataLayout* args_data_limit() const { return parameters_data_base(); }
|
||||
DataLayout* parameters_data_base() const { return data_layout_at(_parameters_data_offset); }
|
||||
DataLayout* parameters_data_limit() const { return exception_handler_data_base(); }
|
||||
DataLayout* exception_handler_data_base() const { return data_layout_at(_exception_handlers_data_offset); }
|
||||
DataLayout* exception_handler_data_limit() const { return extra_data_limit(); }
|
||||
|
||||
// Get the data at an arbitrary bci, or null if there is none. If m
|
||||
// is not null look for a SpeculativeTrapData if any first.
|
||||
ciProfileData* bci_to_data(int bci, ciMethod* m = nullptr);
|
||||
|
||||
ciBitData exception_handler_bci_to_data(int bci);
|
||||
|
||||
uint overflow_trap_count() const {
|
||||
return _orig.overflow_trap_count();
|
||||
}
|
||||
|
||||
@ -460,6 +460,7 @@ JRT_END
|
||||
// bci where the exception happened. If the exception was propagated back
|
||||
// from a call, the expression stack contains the values for the bci at the
|
||||
// invoke w/o arguments (i.e., as if one were inside the call).
|
||||
// Note that the implementation of this method assumes it's only called when an exception has actually occured
|
||||
JRT_ENTRY(address, InterpreterRuntime::exception_handler_for_exception(JavaThread* current, oopDesc* exception))
|
||||
// We get here after we have unwound from a callee throwing an exception
|
||||
// into the interpreter. Any deferred stack processing is notified of
|
||||
@ -574,6 +575,7 @@ JRT_ENTRY(address, InterpreterRuntime::exception_handler_for_exception(JavaThrea
|
||||
} else {
|
||||
// handler in this method => change bci/bcp to handler bci/bcp and continue there
|
||||
handler_pc = h_method->code_base() + handler_bci;
|
||||
h_method->set_exception_handler_entered(handler_bci); // profiling
|
||||
#ifndef ZERO
|
||||
set_bcp_and_mdp(handler_pc, current);
|
||||
continuation = Interpreter::dispatch_table(vtos)[*handler_pc];
|
||||
|
||||
@ -648,6 +648,16 @@ bool Method::init_method_counters(MethodCounters* counters) {
|
||||
return Atomic::replace_if_null(&_method_counters, counters);
|
||||
}
|
||||
|
||||
void Method::set_exception_handler_entered(int handler_bci) {
|
||||
if (ProfileExceptionHandlers) {
|
||||
MethodData* mdo = method_data();
|
||||
if (mdo != nullptr) {
|
||||
BitData handler_data = mdo->exception_handler_bci_to_data(handler_bci);
|
||||
handler_data.set_exception_handler_entered();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Method::extra_stack_words() {
|
||||
// not an inline function, to avoid a header dependency on Interpreter
|
||||
return extra_stack_entries() * Interpreter::stackElementSize;
|
||||
|
||||
@ -308,6 +308,9 @@ class Method : public Metadata {
|
||||
return _method_data;
|
||||
}
|
||||
|
||||
// mark an exception handler as entered (used to prune dead catch blocks in C2)
|
||||
void set_exception_handler_entered(int handler_bci);
|
||||
|
||||
MethodCounters* method_counters() const {
|
||||
return _method_counters;
|
||||
}
|
||||
|
||||
@ -965,6 +965,12 @@ int MethodData::compute_allocation_size_in_bytes(const methodHandle& method) {
|
||||
if (args_cell > 0) {
|
||||
object_size += DataLayout::compute_size_in_bytes(args_cell);
|
||||
}
|
||||
|
||||
if (ProfileExceptionHandlers && method()->has_exception_handler()) {
|
||||
int num_exception_handlers = method()->exception_table_length();
|
||||
object_size += num_exception_handlers * single_exception_handler_data_size();
|
||||
}
|
||||
|
||||
return object_size;
|
||||
}
|
||||
|
||||
@ -1275,8 +1281,10 @@ void MethodData::initialize() {
|
||||
// for method entry so they don't fit with the framework for the
|
||||
// profiling of bytecodes). We store the offset within the MDO of
|
||||
// this area (or -1 if no parameter is profiled)
|
||||
int parm_data_size = 0;
|
||||
if (parms_cell > 0) {
|
||||
object_size += DataLayout::compute_size_in_bytes(parms_cell);
|
||||
parm_data_size = DataLayout::compute_size_in_bytes(parms_cell);
|
||||
object_size += parm_data_size;
|
||||
_parameters_type_data_di = data_size + extra_size + arg_data_size;
|
||||
DataLayout *dp = data_layout_at(data_size + extra_size + arg_data_size);
|
||||
dp->initialize(DataLayout::parameters_type_data_tag, 0, parms_cell);
|
||||
@ -1284,6 +1292,17 @@ void MethodData::initialize() {
|
||||
_parameters_type_data_di = no_parameters;
|
||||
}
|
||||
|
||||
_exception_handler_data_di = data_size + extra_size + arg_data_size + parm_data_size;
|
||||
if (ProfileExceptionHandlers && method()->has_exception_handler()) {
|
||||
int num_exception_handlers = method()->exception_table_length();
|
||||
object_size += num_exception_handlers * single_exception_handler_data_size();
|
||||
ExceptionTableElement* exception_handlers = method()->exception_table_start();
|
||||
for (int i = 0; i < num_exception_handlers; i++) {
|
||||
DataLayout *dp = exception_handler_data_at(i);
|
||||
dp->initialize(DataLayout::bit_data_tag, exception_handlers[i].handler_pc, single_exception_handler_data_cell_count());
|
||||
}
|
||||
}
|
||||
|
||||
// Set an initial hint. Don't use set_hint_di() because
|
||||
// first_di() may be out of bounds if data_size is 0.
|
||||
// In that situation, _hint_di is never used, but at
|
||||
@ -1378,6 +1397,28 @@ ProfileData* MethodData::bci_to_data(int bci) {
|
||||
return bci_to_extra_data(bci, nullptr, false);
|
||||
}
|
||||
|
||||
DataLayout* MethodData::exception_handler_bci_to_data_helper(int bci) {
|
||||
assert(ProfileExceptionHandlers, "not profiling");
|
||||
for (int i = 0; i < num_exception_handler_data(); i++) {
|
||||
DataLayout* exception_handler_data = exception_handler_data_at(i);
|
||||
if (exception_handler_data->bci() == bci) {
|
||||
return exception_handler_data;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BitData* MethodData::exception_handler_bci_to_data_or_null(int bci) {
|
||||
DataLayout* data = exception_handler_bci_to_data_helper(bci);
|
||||
return data != nullptr ? new BitData(data) : nullptr;
|
||||
}
|
||||
|
||||
BitData MethodData::exception_handler_bci_to_data(int bci) {
|
||||
DataLayout* data = exception_handler_bci_to_data_helper(bci);
|
||||
assert(data != nullptr, "invalid bci");
|
||||
return BitData(data);
|
||||
}
|
||||
|
||||
DataLayout* MethodData::next_extra(DataLayout* dp) {
|
||||
int nb_cells = 0;
|
||||
switch(dp->tag()) {
|
||||
|
||||
@ -491,10 +491,11 @@ protected:
|
||||
enum : u1 {
|
||||
// null_seen:
|
||||
// saw a null operand (cast/aastore/instanceof)
|
||||
null_seen_flag = DataLayout::first_flag + 0
|
||||
null_seen_flag = DataLayout::first_flag + 0,
|
||||
exception_handler_entered_flag = null_seen_flag + 1
|
||||
#if INCLUDE_JVMCI
|
||||
// bytecode threw any exception
|
||||
, exception_seen_flag = null_seen_flag + 1
|
||||
, exception_seen_flag = exception_handler_entered_flag + 1
|
||||
#endif
|
||||
};
|
||||
enum { bit_cell_count = 0 }; // no additional data fields needed.
|
||||
@ -525,6 +526,10 @@ public:
|
||||
void set_exception_seen() { set_flag_at(exception_seen_flag); }
|
||||
#endif
|
||||
|
||||
// true if a ex handler block at this bci was entered
|
||||
bool exception_handler_entered() { return flag_at(exception_handler_entered_flag); }
|
||||
void set_exception_handler_entered() { set_flag_at(exception_handler_entered_flag); }
|
||||
|
||||
// Code generation support
|
||||
static u1 null_seen_byte_constant() {
|
||||
return flag_number_to_constant(null_seen_flag);
|
||||
@ -2063,7 +2068,11 @@ private:
|
||||
enum { no_parameters = -2, parameters_uninitialized = -1 };
|
||||
int _parameters_type_data_di;
|
||||
|
||||
// data index of exception handler profiling data
|
||||
int _exception_handler_data_di;
|
||||
|
||||
// Beginning of the data entries
|
||||
// See comment in ciMethodData::load_data
|
||||
intptr_t _data[1];
|
||||
|
||||
// Helper for size computation
|
||||
@ -2078,6 +2087,22 @@ private:
|
||||
return (DataLayout*) (((address)_data) + data_index);
|
||||
}
|
||||
|
||||
static int single_exception_handler_data_cell_count() {
|
||||
return BitData::static_cell_count();
|
||||
}
|
||||
|
||||
static int single_exception_handler_data_size() {
|
||||
return DataLayout::compute_size_in_bytes(single_exception_handler_data_cell_count());
|
||||
}
|
||||
|
||||
DataLayout* exception_handler_data_at(int exception_handler_index) const {
|
||||
return data_layout_at(_exception_handler_data_di + (exception_handler_index * single_exception_handler_data_size()));
|
||||
}
|
||||
|
||||
int num_exception_handler_data() const {
|
||||
return exception_handlers_data_size() / single_exception_handler_data_size();
|
||||
}
|
||||
|
||||
// Initialize an individual data segment. Returns the size of
|
||||
// the segment in bytes.
|
||||
int initialize_data(BytecodeStream* stream, int data_index);
|
||||
@ -2143,6 +2168,8 @@ private:
|
||||
void clean_extra_data_helper(DataLayout* dp, int shift, bool reset = false);
|
||||
void verify_extra_data_clean(CleanExtraDataClosure* cl);
|
||||
|
||||
DataLayout* exception_handler_bci_to_data_helper(int bci);
|
||||
|
||||
public:
|
||||
void clean_extra_data(CleanExtraDataClosure* cl);
|
||||
|
||||
@ -2279,8 +2306,11 @@ public:
|
||||
}
|
||||
|
||||
int parameters_size_in_bytes() const {
|
||||
ParametersTypeData* param = parameters_type_data();
|
||||
return param == nullptr ? 0 : param->size_in_bytes();
|
||||
return pointer_delta_as_int((address) parameters_data_limit(), (address) parameters_data_base());
|
||||
}
|
||||
|
||||
int exception_handlers_data_size() const {
|
||||
return pointer_delta_as_int((address) exception_handler_data_limit(), (address) exception_handler_data_base());
|
||||
}
|
||||
|
||||
// Accessors
|
||||
@ -2333,11 +2363,26 @@ public:
|
||||
return bci_to_extra_data(bci, nullptr, true);
|
||||
}
|
||||
|
||||
BitData* exception_handler_bci_to_data_or_null(int bci);
|
||||
BitData exception_handler_bci_to_data(int bci);
|
||||
|
||||
// Add a handful of extra data records, for trap tracking.
|
||||
// Only valid after 'set_size' is called at the end of MethodData::initialize
|
||||
DataLayout* extra_data_base() const { return limit_data_position(); }
|
||||
DataLayout* extra_data_limit() const { return (DataLayout*)((address)this + size_in_bytes()); }
|
||||
DataLayout* args_data_limit() const { return (DataLayout*)((address)this + size_in_bytes() -
|
||||
parameters_size_in_bytes()); }
|
||||
// pointers to sections in extra data
|
||||
DataLayout* args_data_limit() const { return parameters_data_base(); }
|
||||
DataLayout* parameters_data_base() const {
|
||||
assert(_parameters_type_data_di != parameters_uninitialized, "called too early");
|
||||
return _parameters_type_data_di != no_parameters ? data_layout_at(_parameters_type_data_di) : parameters_data_limit();
|
||||
}
|
||||
DataLayout* parameters_data_limit() const {
|
||||
assert(_parameters_type_data_di != parameters_uninitialized, "called too early");
|
||||
return exception_handler_data_base();
|
||||
}
|
||||
DataLayout* exception_handler_data_base() const { return data_layout_at(_exception_handler_data_di); }
|
||||
DataLayout* exception_handler_data_limit() const { return extra_data_limit(); }
|
||||
|
||||
int extra_data_size() const { return (int)((address)extra_data_limit() - (address)extra_data_base()); }
|
||||
static DataLayout* next_extra(DataLayout* dp);
|
||||
|
||||
@ -2385,8 +2430,12 @@ public:
|
||||
}
|
||||
|
||||
int parameters_type_data_di() const {
|
||||
assert(_parameters_type_data_di != parameters_uninitialized && _parameters_type_data_di != no_parameters, "no args type data");
|
||||
return _parameters_type_data_di;
|
||||
assert(_parameters_type_data_di != parameters_uninitialized, "called too early");
|
||||
return _parameters_type_data_di != no_parameters ? _parameters_type_data_di : exception_handlers_data_di();
|
||||
}
|
||||
|
||||
int exception_handlers_data_di() const {
|
||||
return _exception_handler_data_di;
|
||||
}
|
||||
|
||||
// Support for code generation
|
||||
|
||||
@ -783,6 +783,8 @@
|
||||
"more than this threshold") \
|
||||
range(0, 100) \
|
||||
\
|
||||
develop(bool, StressPrunedExceptionHandlers, false, \
|
||||
"Always prune exception handlers") \
|
||||
|
||||
// end of C2_FLAGS
|
||||
|
||||
|
||||
@ -941,7 +941,7 @@ void Parse::catch_inline_exceptions(SafePointNode* ex_map) {
|
||||
|
||||
// Get the exception oop klass from its header
|
||||
Node* ex_klass_node = nullptr;
|
||||
if (has_ex_handler() && !ex_type->klass_is_exact()) {
|
||||
if (has_exception_handler() && !ex_type->klass_is_exact()) {
|
||||
Node* p = basic_plus_adr( ex_node, ex_node, oopDesc::klass_offset_in_bytes());
|
||||
ex_klass_node = _gvn.transform(LoadKlassNode::make(_gvn, nullptr, immutable_memory(), p, TypeInstPtr::KLASS, TypeInstKlassPtr::OBJECT));
|
||||
|
||||
|
||||
@ -181,9 +181,9 @@ bool GraphKit::stopped() {
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------has_ex_handler----------------------------------
|
||||
//-----------------------------has_exception_handler----------------------------------
|
||||
// Tell if this method or any caller method has exception handlers.
|
||||
bool GraphKit::has_ex_handler() {
|
||||
bool GraphKit::has_exception_handler() {
|
||||
for (JVMState* jvmsp = jvms(); jvmsp != nullptr; jvmsp = jvmsp->caller()) {
|
||||
if (jvmsp->has_method() && jvmsp->method()->has_exception_handlers()) {
|
||||
return true;
|
||||
@ -548,7 +548,7 @@ void GraphKit::builtin_throw(Deoptimization::DeoptReason reason) {
|
||||
// as hot if there has been at least one in this method.
|
||||
if (C->trap_count(reason) != 0
|
||||
&& method()->method_data()->trap_count(reason) != 0
|
||||
&& has_ex_handler()) {
|
||||
&& has_exception_handler()) {
|
||||
treat_throw_as_hot = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,7 +194,7 @@ class GraphKit : public Phase {
|
||||
bool stopped();
|
||||
|
||||
// Tell if this method or any caller method has exception handlers.
|
||||
bool has_ex_handler();
|
||||
bool has_exception_handler();
|
||||
|
||||
// Save an exception without blowing stack contents or other JVM state.
|
||||
// (The extra pointer is stuck with add_req on the map, beyond the JVMS.)
|
||||
|
||||
@ -1532,6 +1532,22 @@ void Parse::do_one_block() {
|
||||
// Set iterator to start of block.
|
||||
iter().reset_to_bci(block()->start());
|
||||
|
||||
if (ProfileExceptionHandlers && block()->is_handler()) {
|
||||
ciMethodData* methodData = method()->method_data();
|
||||
if (methodData->is_mature()) {
|
||||
ciBitData data = methodData->exception_handler_bci_to_data(block()->start());
|
||||
if (!data.exception_handler_entered() || StressPrunedExceptionHandlers) {
|
||||
// dead catch block
|
||||
// Emit an uncommon trap instead of processing the block.
|
||||
set_parse_bci(block()->start());
|
||||
uncommon_trap(Deoptimization::Reason_unreached,
|
||||
Deoptimization::Action_reinterpret,
|
||||
nullptr, "dead catch block");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CompileLog* log = C->log();
|
||||
|
||||
// Parse bytecodes
|
||||
|
||||
@ -1498,21 +1498,21 @@ static void jvmti_yield_cleanup(JavaThread* thread, ContinuationWrapper& cont) {
|
||||
#endif // INCLUDE_JVMTI
|
||||
|
||||
#ifdef ASSERT
|
||||
static bool monitors_on_stack(JavaThread* thread) {
|
||||
ContinuationEntry* ce = thread->last_continuation();
|
||||
RegisterMap map(thread,
|
||||
RegisterMap::UpdateMap::include,
|
||||
RegisterMap::ProcessFrames::include,
|
||||
RegisterMap::WalkContinuation::skip);
|
||||
map.set_include_argument_oops(false);
|
||||
for (frame f = thread->last_frame(); Continuation::is_frame_in_continuation(ce, f); f = f.sender(&map)) {
|
||||
if ((f.is_interpreted_frame() && ContinuationHelper::InterpretedFrame::is_owning_locks(f)) ||
|
||||
(f.is_compiled_frame() && ContinuationHelper::CompiledFrame::is_owning_locks(map.thread(), &map, f))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// static bool monitors_on_stack(JavaThread* thread) {
|
||||
// ContinuationEntry* ce = thread->last_continuation();
|
||||
// RegisterMap map(thread,
|
||||
// RegisterMap::UpdateMap::include,
|
||||
// RegisterMap::ProcessFrames::include,
|
||||
// RegisterMap::WalkContinuation::skip);
|
||||
// map.set_include_argument_oops(false);
|
||||
// for (frame f = thread->last_frame(); Continuation::is_frame_in_continuation(ce, f); f = f.sender(&map)) {
|
||||
// if ((f.is_interpreted_frame() && ContinuationHelper::InterpretedFrame::is_owning_locks(f)) ||
|
||||
// (f.is_compiled_frame() && ContinuationHelper::CompiledFrame::is_owning_locks(map.thread(), &map, f))) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
|
||||
bool FreezeBase::interpreted_native_or_deoptimized_on_stack() {
|
||||
ContinuationEntry* ce = _thread->last_continuation();
|
||||
@ -1575,8 +1575,8 @@ static inline int freeze_internal(JavaThread* current, intptr_t* const sp) {
|
||||
|
||||
assert(entry->is_virtual_thread() == (entry->scope(current) == java_lang_VirtualThread::vthread_scope()), "");
|
||||
|
||||
assert(monitors_on_stack(current) == ((current->held_monitor_count() - current->jni_monitor_count()) > 0),
|
||||
"Held monitor count and locks on stack invariant: " INT64_FORMAT " JNI: " INT64_FORMAT, (int64_t)current->held_monitor_count(), (int64_t)current->jni_monitor_count());
|
||||
// assert(monitors_on_stack(current) == ((current->held_monitor_count() - current->jni_monitor_count()) > 0),
|
||||
// "Held monitor count and locks on stack invariant: " INT64_FORMAT " JNI: " INT64_FORMAT, (int64_t)current->held_monitor_count(), (int64_t)current->jni_monitor_count());
|
||||
|
||||
if (entry->is_pinned() || current->held_monitor_count() > 0) {
|
||||
log_develop_debug(continuations)("PINNED due to critical section/hold monitor");
|
||||
|
||||
@ -2444,6 +2444,17 @@ JRT_ENTRY(void, Deoptimization::uncommon_trap_inner(JavaThread* current, jint tr
|
||||
nm->method()->set_not_compilable("give up compiling", CompLevel_full_optimization);
|
||||
}
|
||||
|
||||
if (ProfileExceptionHandlers && trap_mdo != nullptr) {
|
||||
BitData* exception_handler_data = trap_mdo->exception_handler_bci_to_data_or_null(trap_bci);
|
||||
if (exception_handler_data != nullptr) {
|
||||
// uncommon trap at the start of an exception handler.
|
||||
// C2 generates these for un-entered exception handlers.
|
||||
// mark the handler as entered to avoid generating
|
||||
// another uncommon trap the next time the handler is compiled
|
||||
exception_handler_data->set_exception_handler_entered();
|
||||
}
|
||||
}
|
||||
|
||||
} // Free marked resources
|
||||
|
||||
}
|
||||
|
||||
@ -1998,6 +1998,9 @@ const int ObjectAlignmentInBytes = 8;
|
||||
"more eagerly at the cost of higher overhead. A value of 0 " \
|
||||
"(default) disables native heap trimming.") \
|
||||
range(0, UINT_MAX) \
|
||||
\
|
||||
product(bool, ProfileExceptionHandlers, true, \
|
||||
"Profile exception handlers") \
|
||||
|
||||
// end of RUNTIME_FLAGS
|
||||
|
||||
|
||||
@ -677,6 +677,7 @@ JRT_END
|
||||
|
||||
// ret_pc points into caller; we are returning caller's exception handler
|
||||
// for given exception
|
||||
// Note that the implementation of this method assumes it's only called when an exception has actually occured
|
||||
address SharedRuntime::compute_compiled_exc_handler(CompiledMethod* cm, address ret_pc, Handle& exception,
|
||||
bool force_unwind, bool top_frame_only, bool& recursive_exception_occurred) {
|
||||
assert(cm != nullptr, "must exist");
|
||||
@ -779,6 +780,9 @@ address SharedRuntime::compute_compiled_exc_handler(CompiledMethod* cm, address
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (handler_bci != -1) { // did we find a handler in this method?
|
||||
sd->method()->set_exception_handler_entered(handler_bci); // profile
|
||||
}
|
||||
return nm->code_begin() + t->pco();
|
||||
}
|
||||
|
||||
|
||||
153
test/hotspot/jtreg/compiler/c2/TestExHandlerTrap.java
Normal file
153
test/hotspot/jtreg/compiler/c2/TestExHandlerTrap.java
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package compiler.c2;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/*
|
||||
* @test id=default_config
|
||||
* @bug 8267532
|
||||
* @summary Test whether trap in place of pruned exception handler block works
|
||||
*
|
||||
* @run main/othervm
|
||||
* -Xbatch
|
||||
* -Xlog:deoptimization=trace
|
||||
* -XX:CompileCommand=PrintCompilation,compiler.c2.TestExHandlerTrap::payload
|
||||
* -XX:CompileCommand=dontinline,compiler.c2.TestExHandlerTrap::payload
|
||||
* -XX:CompileCommand=dontinline,compiler.c2.TestExHandlerTrap::maybeThrow
|
||||
* compiler.c2.TestExHandlerTrap
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=no_profiling
|
||||
* @bug 8267532
|
||||
* @summary basic smoke test for disabled ex. handler profiling
|
||||
*
|
||||
* @run main/othervm
|
||||
* -Xbatch
|
||||
* -Xlog:deoptimization=trace
|
||||
* -XX:CompileCommand=PrintCompilation,compiler.c2.TestExHandlerTrap::payload
|
||||
* -XX:CompileCommand=dontinline,compiler.c2.TestExHandlerTrap::payload
|
||||
* -XX:CompileCommand=dontinline,compiler.c2.TestExHandlerTrap::maybeThrow
|
||||
* -XX:-ProfileExceptionHandlers
|
||||
* compiler.c2.TestExHandlerTrap
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test id=stress
|
||||
* @bug 8267532
|
||||
* @summary basic smoke test for stressing ex. handler pruning
|
||||
* @requires vm.debug
|
||||
*
|
||||
* @run main/othervm
|
||||
* -Xbatch
|
||||
* -Xlog:deoptimization=trace
|
||||
* -XX:CompileCommand=PrintCompilation,compiler.c2.TestExHandlerTrap::payload
|
||||
* -XX:CompileCommand=dontinline,compiler.c2.TestExHandlerTrap::payload
|
||||
* -XX:CompileCommand=dontinline,compiler.c2.TestExHandlerTrap::maybeThrow
|
||||
* -XX:+StressPrunedExceptionHandlers
|
||||
* compiler.c2.TestExHandlerTrap
|
||||
*/
|
||||
|
||||
public class TestExHandlerTrap {
|
||||
|
||||
private static final String EX_MESSAGE = "Testing trap";
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
// warmup, compile payload
|
||||
for (int i = 0; i < 20_000; i++) {
|
||||
payload(false);
|
||||
}
|
||||
|
||||
try {
|
||||
// trigger uncommon trap in pruned catch block
|
||||
payload(true);
|
||||
} catch (IllegalStateException e) {
|
||||
if (!e.getMessage().equals(EX_MESSAGE)) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// continue for a bit, to see if anything breaks
|
||||
for (int i = 0; i < 1_000; i++) {
|
||||
payload(false);
|
||||
}
|
||||
}
|
||||
|
||||
public static void payload(boolean shouldThrow) {
|
||||
doIt(shouldThrow); // mix in some inlining
|
||||
}
|
||||
|
||||
public static void doIt(boolean shouldThrow) {
|
||||
PrintStream err = System.err;
|
||||
try (ConfinedScope r = new ConfinedScope()) {
|
||||
r.addCloseAction(dummy);
|
||||
maybeThrow(shouldThrow); // out of line to prevent 'payload' from being deoptimized by unstable if
|
||||
} catch (IllegalArgumentException e) {
|
||||
// some side effects to see whether deopt is successful
|
||||
err.println("Exception message: " + e.getMessage());
|
||||
err.println("shouldThrow: " + shouldThrow);
|
||||
}
|
||||
}
|
||||
|
||||
private static void maybeThrow(boolean shouldThrow) {
|
||||
if (shouldThrow) {
|
||||
throw new IllegalStateException(EX_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
static final Runnable dummy = () -> {};
|
||||
|
||||
static class ConfinedScope implements AutoCloseable {
|
||||
final Thread owner;
|
||||
boolean closed;
|
||||
final List<Runnable> resources = new ArrayList<>();
|
||||
|
||||
private void checkState() {
|
||||
if (closed) {
|
||||
throw new AssertionError("Closed");
|
||||
} else if (owner != Thread.currentThread()) {
|
||||
throw new AssertionError("Wrong thread");
|
||||
}
|
||||
}
|
||||
|
||||
ConfinedScope() {
|
||||
this.owner = Thread.currentThread();
|
||||
}
|
||||
|
||||
void addCloseAction(Runnable runnable) {
|
||||
checkState();
|
||||
resources.add(runnable);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
checkState();
|
||||
closed = true;
|
||||
for (Runnable r : resources) {
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
202
test/hotspot/jtreg/compiler/c2/irTests/TestPrunedExHandler.java
Normal file
202
test/hotspot/jtreg/compiler/c2/irTests/TestPrunedExHandler.java
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package compiler.c2.irTests;
|
||||
|
||||
import compiler.lib.ir_framework.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8267532
|
||||
* @summary check that uncommon trap is generated for unhandled catch block
|
||||
* @library /test/lib /
|
||||
* @run driver compiler.c2.irTests.TestPrunedExHandler
|
||||
*/
|
||||
|
||||
public class TestPrunedExHandler {
|
||||
public static void main(String[] args) {
|
||||
TestFramework.runWithFlags(
|
||||
"-XX:+TieredCompilation", // we only profile in tier 3
|
||||
"-XX:CompileCommand=inline,compiler.c2.irTests.TestPrunedExHandler::inlinee",
|
||||
"-XX:CompileCommand=dontinline,compiler.c2.irTests.TestPrunedExHandler::outOfLine");
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.UNREACHED_TRAP, "1"})
|
||||
public static void testTrap() {
|
||||
try {
|
||||
outOfLine(false);
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void outOfLine(boolean shouldThrow) {
|
||||
if (shouldThrow) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.UNREACHED_TRAP, "0"})
|
||||
public static void testNoTrap(boolean shouldThrow) {
|
||||
try {
|
||||
outOfLine(shouldThrow);
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testNoTrap", mode = RunMode.STANDALONE)
|
||||
public static void runNoTrap(RunInfo info) {
|
||||
for (int i = 0; i < 2_000; i++) { // tier 3
|
||||
testNoTrap(false);
|
||||
}
|
||||
|
||||
TestFramework.assertCompiledAtLevel(info.getTest(), CompLevel.C1_FULL_PROFILE);
|
||||
|
||||
testNoTrap(true); // mark ex handler as entered
|
||||
|
||||
for (int i = 0; i < 20_000; i++) { // tier 4
|
||||
testNoTrap(false); // should have no trap
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.UNREACHED_TRAP, "0"})
|
||||
public static void testNoTrapAfterDeopt(boolean shouldThrow) {
|
||||
try {
|
||||
outOfLine(shouldThrow);
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testNoTrapAfterDeopt", mode = RunMode.STANDALONE)
|
||||
public static void runNoTrapAfterDeopt(RunInfo info) {
|
||||
for (int i = 0; i < 20_000; i++) { // tier 4
|
||||
testNoTrapAfterDeopt(false);
|
||||
}
|
||||
|
||||
TestFramework.assertCompiledByC2(info.getTest());
|
||||
|
||||
testNoTrapAfterDeopt(true); // deopt + mark ex handler as entered
|
||||
|
||||
TestFramework.assertDeoptimizedByC2(info.getTest());
|
||||
|
||||
for (int i = 0; i < 20_000; i++) { // tier 4 again
|
||||
testNoTrapAfterDeopt(false); // should have no trap
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.UNREACHED_TRAP, "0"})
|
||||
public static void testNoTrapAfterDeoptInlined(boolean shouldThrow) {
|
||||
// check that we handle exception thrown in inlinee, caught in caller.
|
||||
// C2 handles exception dispatch differently for those cases
|
||||
try {
|
||||
inlinee(shouldThrow);
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void inlinee(boolean shouldThrow) {
|
||||
outOfLine(shouldThrow);
|
||||
}
|
||||
|
||||
@Run(test = "testNoTrapAfterDeoptInlined", mode = RunMode.STANDALONE)
|
||||
public static void runNoTrapAfterDeoptInlined(RunInfo info) {
|
||||
for (int i = 0; i < 20_000; i++) { // tier 4
|
||||
testNoTrapAfterDeoptInlined(false);
|
||||
}
|
||||
|
||||
TestFramework.assertCompiledByC2(info.getTest());
|
||||
|
||||
testNoTrapAfterDeoptInlined(true); // deopt + mark ex handler as entered
|
||||
|
||||
TestFramework.assertDeoptimizedByC2(info.getTest());
|
||||
|
||||
for (int i = 0; i < 20_000; i++) { // tier 4 again
|
||||
testNoTrapAfterDeoptInlined(false); // should have no trap
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.UNREACHED_TRAP, "1"})
|
||||
public static void testThrowBeforeProfiling(boolean shouldThrow) {
|
||||
try {
|
||||
outOfLine(shouldThrow);
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testThrowBeforeProfiling", mode = RunMode.STANDALONE)
|
||||
public static void runThrowBeforeProfiling(RunInfo info) {
|
||||
testThrowBeforeProfiling(true);
|
||||
// this exception should not be profiled, as MDO has not been created yet
|
||||
|
||||
for (int i = 0; i < 20_000; i++) { // tier 4
|
||||
testThrowBeforeProfiling(false);
|
||||
}
|
||||
// should have trap
|
||||
}
|
||||
|
||||
@Test
|
||||
@IR(counts = {IRNode.UNREACHED_TRAP, "0"})
|
||||
public static void testInterpreterProfiling(boolean takeBranch, boolean shouldThrow) {
|
||||
if (takeBranch) {
|
||||
System.out.println("testInterpreterProfiling: branch taken");
|
||||
}
|
||||
|
||||
try {
|
||||
outOfLine(shouldThrow);
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "testInterpreterProfiling", mode = RunMode.STANDALONE)
|
||||
public static void runInterpreterProfiling(RunInfo info) {
|
||||
for (int i = 0; i < 20_000; i++) { // tier 4
|
||||
testInterpreterProfiling(false, false);
|
||||
}
|
||||
TestFramework.assertCompiledByC2(info.getTest());
|
||||
// should have no trap at this point
|
||||
|
||||
testInterpreterProfiling(true, false); // take branch -> deopt due to unstable if
|
||||
TestFramework.assertDeoptimizedByC2(info.getTest());
|
||||
|
||||
// continue in the interpreter:
|
||||
testInterpreterProfiling(false, false);
|
||||
// throw exception in the interpreter, test interpreter profiling:
|
||||
testInterpreterProfiling(false, true);
|
||||
|
||||
for (int i = 0; i < 20_000; i++) { // tier 4 again
|
||||
testInterpreterProfiling(false, false);
|
||||
}
|
||||
// should have no trap
|
||||
}
|
||||
|
||||
}
|
||||
@ -549,12 +549,12 @@ public class IRNode {
|
||||
// Does not work for VM builds without JVMCI like x86_32 (a rule containing this regex will be skipped without having JVMCI built).
|
||||
public static final String INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP = PREFIX + "INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP" + POSTFIX;
|
||||
static {
|
||||
trapNodes(INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP,"intrinsic_or_type_checked_inlining");
|
||||
trapNodes(INTRINSIC_OR_TYPE_CHECKED_INLINING_TRAP, "intrinsic_or_type_checked_inlining");
|
||||
}
|
||||
|
||||
public static final String INTRINSIC_TRAP = PREFIX + "INTRINSIC_TRAP" + POSTFIX;
|
||||
static {
|
||||
trapNodes(INTRINSIC_TRAP,"intrinsic");
|
||||
trapNodes(INTRINSIC_TRAP, "intrinsic");
|
||||
}
|
||||
|
||||
// Is only supported on riscv64.
|
||||
@ -1040,12 +1040,12 @@ public class IRNode {
|
||||
|
||||
public static final String NULL_ASSERT_TRAP = PREFIX + "NULL_ASSERT_TRAP" + POSTFIX;
|
||||
static {
|
||||
trapNodes(NULL_ASSERT_TRAP,"null_assert");
|
||||
trapNodes(NULL_ASSERT_TRAP, "null_assert");
|
||||
}
|
||||
|
||||
public static final String NULL_CHECK_TRAP = PREFIX + "NULL_CHECK_TRAP" + POSTFIX;
|
||||
static {
|
||||
trapNodes(NULL_CHECK_TRAP,"null_check");
|
||||
trapNodes(NULL_CHECK_TRAP, "null_check");
|
||||
}
|
||||
|
||||
public static final String OR_VB = VECTOR_PREFIX + "OR_VB" + POSTFIX;
|
||||
@ -1129,12 +1129,12 @@ public class IRNode {
|
||||
|
||||
public static final String PREDICATE_TRAP = PREFIX + "PREDICATE_TRAP" + POSTFIX;
|
||||
static {
|
||||
trapNodes(PREDICATE_TRAP,"predicate");
|
||||
trapNodes(PREDICATE_TRAP, "predicate");
|
||||
}
|
||||
|
||||
public static final String RANGE_CHECK_TRAP = PREFIX + "RANGE_CHECK_TRAP" + POSTFIX;
|
||||
static {
|
||||
trapNodes(RANGE_CHECK_TRAP,"range_check");
|
||||
trapNodes(RANGE_CHECK_TRAP, "range_check");
|
||||
}
|
||||
|
||||
public static final String REPLICATE_B = VECTOR_PREFIX + "REPLICATE_B" + POSTFIX;
|
||||
@ -1486,7 +1486,7 @@ public class IRNode {
|
||||
|
||||
public static final String TRAP = PREFIX + "TRAP" + POSTFIX;
|
||||
static {
|
||||
trapNodes(TRAP,"reason");
|
||||
trapNodes(TRAP, "reason");
|
||||
}
|
||||
|
||||
public static final String UDIV_I = PREFIX + "UDIV_I" + POSTFIX;
|
||||
@ -1521,12 +1521,17 @@ public class IRNode {
|
||||
|
||||
public static final String UNHANDLED_TRAP = PREFIX + "UNHANDLED_TRAP" + POSTFIX;
|
||||
static {
|
||||
trapNodes(UNHANDLED_TRAP,"unhandled");
|
||||
trapNodes(UNHANDLED_TRAP, "unhandled");
|
||||
}
|
||||
|
||||
public static final String UNSTABLE_IF_TRAP = PREFIX + "UNSTABLE_IF_TRAP" + POSTFIX;
|
||||
static {
|
||||
trapNodes(UNSTABLE_IF_TRAP,"unstable_if");
|
||||
trapNodes(UNSTABLE_IF_TRAP, "unstable_if");
|
||||
}
|
||||
|
||||
public static final String UNREACHED_TRAP = PREFIX + "UNREACHED_TRAP" + POSTFIX;
|
||||
static {
|
||||
trapNodes(UNREACHED_TRAP, "unreached");
|
||||
}
|
||||
|
||||
public static final String URSHIFT = PREFIX + "URSHIFT" + POSTFIX;
|
||||
|
||||
@ -63,18 +63,17 @@ public class AllocFromSliceTest extends CLayouts {
|
||||
|
||||
@Benchmark
|
||||
public MemorySegment alloc_confined() {
|
||||
Arena arena = Arena.ofConfined();
|
||||
MemorySegment segment = arena.allocate(size);
|
||||
MemorySegment.copy(arr, start, segment, C_CHAR, 0, size);
|
||||
arena.close();
|
||||
return segment;
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
MemorySegment segment = arena.allocate(size);
|
||||
MemorySegment.copy(arr, start, segment, C_CHAR, 0, size);
|
||||
return segment;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public MemorySegment alloc_confined_slice() {
|
||||
Arena arena = Arena.ofConfined();
|
||||
MemorySegment segment = arena.allocateFrom(C_CHAR, MemorySegment.ofArray(arr), C_CHAR, start, size);
|
||||
arena.close();
|
||||
return segment;
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
return arena.allocateFrom(C_CHAR, MemorySegment.ofArray(arr), C_CHAR, start, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,34 +68,30 @@ public class AllocFromTest extends CLayouts {
|
||||
|
||||
@Benchmark
|
||||
public MemorySegment alloc_confined() {
|
||||
Arena arena = Arena.ofConfined();
|
||||
MemorySegment segment = arena.allocateFrom(ValueLayout.JAVA_BYTE, arr);
|
||||
arena.close();
|
||||
return segment;
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
return arena.allocateFrom(ValueLayout.JAVA_BYTE, arr);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public MemorySegment alloc_malloc_arena() {
|
||||
MallocArena arena = new MallocArena();
|
||||
MemorySegment segment = arena.allocateFrom(ValueLayout.JAVA_BYTE, arr);
|
||||
arena.close();
|
||||
return segment;
|
||||
try (MallocArena arena = new MallocArena()) {
|
||||
return arena.allocateFrom(ValueLayout.JAVA_BYTE, arr);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public MemorySegment alloc_unsafe_arena() {
|
||||
UnsafeArena arena = new UnsafeArena();
|
||||
MemorySegment segment = arena.allocateFrom(ValueLayout.JAVA_BYTE, arr);
|
||||
arena.close();
|
||||
return segment;
|
||||
try (UnsafeArena arena = new UnsafeArena()) {
|
||||
return arena.allocateFrom(ValueLayout.JAVA_BYTE, arr);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public MemorySegment alloc_pool_arena() {
|
||||
Arena arena = pool.acquire();
|
||||
MemorySegment segment = arena.allocateFrom(ValueLayout.JAVA_BYTE, arr);
|
||||
arena.close();
|
||||
return segment;
|
||||
try (Arena arena = pool.acquire()) {
|
||||
return arena.allocateFrom(ValueLayout.JAVA_BYTE, arr);
|
||||
}
|
||||
}
|
||||
|
||||
static class SlicingPool {
|
||||
|
||||
@ -65,26 +65,23 @@ public class AllocTest extends CLayouts {
|
||||
|
||||
@Benchmark
|
||||
public MemorySegment alloc_confined() {
|
||||
Arena arena = Arena.ofConfined();
|
||||
MemorySegment segment = arena.allocate(size);
|
||||
arena.close();
|
||||
return segment;
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
return arena.allocate(size);
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public long alloc_calloc_arena() {
|
||||
CallocArena arena = new CallocArena();
|
||||
MemorySegment segment = arena.allocate(size);
|
||||
arena.close();
|
||||
return segment.address();
|
||||
try (CallocArena arena = new CallocArena()) {
|
||||
return arena.allocate(size).address();
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public long alloc_unsafe_arena() {
|
||||
UnsafeArena arena = new UnsafeArena();
|
||||
MemorySegment segment = arena.allocate(size);
|
||||
arena.close();
|
||||
return segment.address();
|
||||
try (UnsafeArena arena = new UnsafeArena()) {
|
||||
return arena.allocate(size).address();
|
||||
}
|
||||
}
|
||||
|
||||
public static class CallocArena implements Arena {
|
||||
|
||||
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package org.openjdk.bench.java.lang.foreign;
|
||||
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@State(org.openjdk.jmh.annotations.Scope.Thread)
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS)
|
||||
@Fork(value = 3)
|
||||
public class ResourceScopeCloseMin {
|
||||
|
||||
Runnable dummy = () -> {};
|
||||
|
||||
static class ConfinedScope {
|
||||
final Thread owner;
|
||||
boolean closed;
|
||||
final List<Runnable> resources = new ArrayList<>();
|
||||
|
||||
private void checkState() {
|
||||
if (closed) {
|
||||
throw new AssertionError("Closed");
|
||||
} else if (owner != Thread.currentThread()) {
|
||||
throw new AssertionError("Wrong thread");
|
||||
}
|
||||
}
|
||||
|
||||
ConfinedScope() {
|
||||
this.owner = Thread.currentThread();
|
||||
}
|
||||
|
||||
void addCloseAction(Runnable runnable) {
|
||||
checkState();
|
||||
resources.add(runnable);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
checkState();
|
||||
closed = true;
|
||||
for (Runnable r : resources) {
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void confined_close() {
|
||||
ConfinedScope scope = new ConfinedScope();
|
||||
try { // simulate TWR
|
||||
scope.addCloseAction(dummy);
|
||||
scope.close();
|
||||
} catch (RuntimeException ex) {
|
||||
scope.close();
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void confined_close_notry() {
|
||||
ConfinedScope scope = new ConfinedScope();
|
||||
scope.addCloseAction(dummy);
|
||||
scope.close();
|
||||
}
|
||||
}
|
||||
@ -90,7 +90,7 @@ public class StrLenTest extends CLayouts {
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public int panama_strlen() throws Throwable {
|
||||
public int panama_strlen_alloc() throws Throwable {
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
MemorySegment segment = arena.allocateFrom(str);
|
||||
return (int)STRLEN.invokeExact(segment);
|
||||
@ -104,10 +104,9 @@ public class StrLenTest extends CLayouts {
|
||||
|
||||
@Benchmark
|
||||
public int panama_strlen_pool() throws Throwable {
|
||||
Arena arena = pool.acquire();
|
||||
int l = (int) STRLEN.invokeExact(arena.allocateFrom(str));
|
||||
arena.close();
|
||||
return l;
|
||||
try (Arena arena = pool.acquire()) {
|
||||
return (int) STRLEN.invokeExact(arena.allocateFrom(str));
|
||||
}
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
|
||||
@ -82,10 +82,9 @@ public class ToCStringTest extends CLayouts {
|
||||
|
||||
@Benchmark
|
||||
public MemorySegment panama_writeString() throws Throwable {
|
||||
Arena arena = Arena.ofConfined();
|
||||
MemorySegment segment = arena.allocateFrom(str);
|
||||
arena.close();
|
||||
return segment;
|
||||
try (Arena arena = Arena.ofConfined()) {
|
||||
return arena.allocateFrom(str);
|
||||
}
|
||||
}
|
||||
|
||||
static native long writeString(String str);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user