jdk-24/hotspot/src/share/vm/c1/c1_Compilation.cpp
Igor Veresov ab725dba1d 7003554: (tiered) assert(is_null_object() || handle() != NULL) failed: cannot embed null pointer
C1 with profiling doesn't check whether the MDO has been really allocated, which can silently fail if the perm gen is full. The solution is to check if the allocation failed and bailout out of inlining or compilation.

Reviewed-by: kvn, never
2010-12-02 17:21:12 -08:00

577 lines
17 KiB
C++

/*
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "precompiled.hpp"
#include "c1/c1_CFGPrinter.hpp"
#include "c1/c1_Compilation.hpp"
#include "c1/c1_IR.hpp"
#include "c1/c1_LIRAssembler.hpp"
#include "c1/c1_LinearScan.hpp"
#include "c1/c1_MacroAssembler.hpp"
#include "c1/c1_ValueMap.hpp"
#include "c1/c1_ValueStack.hpp"
#include "code/debugInfoRec.hpp"
typedef enum {
_t_compile,
_t_setup,
_t_optimizeIR,
_t_buildIR,
_t_emit_lir,
_t_linearScan,
_t_lirGeneration,
_t_lir_schedule,
_t_codeemit,
_t_codeinstall,
max_phase_timers
} TimerName;
static const char * timer_name[] = {
"compile",
"setup",
"optimizeIR",
"buildIR",
"emit_lir",
"linearScan",
"lirGeneration",
"lir_schedule",
"codeemit",
"codeinstall"
};
static elapsedTimer timers[max_phase_timers];
static int totalInstructionNodes = 0;
class PhaseTraceTime: public TraceTime {
private:
JavaThread* _thread;
public:
PhaseTraceTime(TimerName timer):
TraceTime("", &timers[timer], CITime || CITimeEach, Verbose) {
}
};
// Implementation of Compilation
#ifndef PRODUCT
void Compilation::maybe_print_current_instruction() {
if (_current_instruction != NULL && _last_instruction_printed != _current_instruction) {
_last_instruction_printed = _current_instruction;
_current_instruction->print_line();
}
}
#endif // PRODUCT
DebugInformationRecorder* Compilation::debug_info_recorder() const {
return _env->debug_info();
}
Dependencies* Compilation::dependency_recorder() const {
return _env->dependencies();
}
void Compilation::initialize() {
// Use an oop recorder bound to the CI environment.
// (The default oop recorder is ignorant of the CI.)
OopRecorder* ooprec = new OopRecorder(_env->arena());
_env->set_oop_recorder(ooprec);
_env->set_debug_info(new DebugInformationRecorder(ooprec));
debug_info_recorder()->set_oopmaps(new OopMapSet());
_env->set_dependencies(new Dependencies(_env));
}
void Compilation::build_hir() {
CHECK_BAILOUT();
// setup ir
_hir = new IR(this, method(), osr_bci());
if (!_hir->is_valid()) {
bailout("invalid parsing");
return;
}
#ifndef PRODUCT
if (PrintCFGToFile) {
CFGPrinter::print_cfg(_hir, "After Generation of HIR", true, false);
}
#endif
#ifndef PRODUCT
if (PrintCFG || PrintCFG0) { tty->print_cr("CFG after parsing"); _hir->print(true); }
if (PrintIR || PrintIR0 ) { tty->print_cr("IR after parsing"); _hir->print(false); }
#endif
_hir->verify();
if (UseC1Optimizations) {
NEEDS_CLEANUP
// optimization
PhaseTraceTime timeit(_t_optimizeIR);
_hir->optimize();
}
_hir->verify();
_hir->split_critical_edges();
#ifndef PRODUCT
if (PrintCFG || PrintCFG1) { tty->print_cr("CFG after optimizations"); _hir->print(true); }
if (PrintIR || PrintIR1 ) { tty->print_cr("IR after optimizations"); _hir->print(false); }
#endif
_hir->verify();
// compute block ordering for code generation
// the control flow must not be changed from here on
_hir->compute_code();
if (UseGlobalValueNumbering) {
ResourceMark rm;
int instructions = Instruction::number_of_instructions();
GlobalValueNumbering gvn(_hir);
assert(instructions == Instruction::number_of_instructions(),
"shouldn't have created an instructions");
}
// compute use counts after global value numbering
_hir->compute_use_counts();
#ifndef PRODUCT
if (PrintCFG || PrintCFG2) { tty->print_cr("CFG before code generation"); _hir->code()->print(true); }
if (PrintIR || PrintIR2 ) { tty->print_cr("IR before code generation"); _hir->code()->print(false, true); }
#endif
_hir->verify();
}
void Compilation::emit_lir() {
CHECK_BAILOUT();
LIRGenerator gen(this, method());
{
PhaseTraceTime timeit(_t_lirGeneration);
hir()->iterate_linear_scan_order(&gen);
}
CHECK_BAILOUT();
{
PhaseTraceTime timeit(_t_linearScan);
LinearScan* allocator = new LinearScan(hir(), &gen, frame_map());
set_allocator(allocator);
// Assign physical registers to LIR operands using a linear scan algorithm.
allocator->do_linear_scan();
CHECK_BAILOUT();
_max_spills = allocator->max_spills();
}
if (BailoutAfterLIR) {
if (PrintLIR && !bailed_out()) {
print_LIR(hir()->code());
}
bailout("Bailing out because of -XX:+BailoutAfterLIR");
}
}
void Compilation::emit_code_epilog(LIR_Assembler* assembler) {
CHECK_BAILOUT();
CodeOffsets* code_offsets = assembler->offsets();
// generate code or slow cases
assembler->emit_slow_case_stubs();
CHECK_BAILOUT();
// generate exception adapters
assembler->emit_exception_entries(exception_info_list());
CHECK_BAILOUT();
// Generate code for exception handler.
code_offsets->set_value(CodeOffsets::Exceptions, assembler->emit_exception_handler());
CHECK_BAILOUT();
// Generate code for deopt handler.
code_offsets->set_value(CodeOffsets::Deopt, assembler->emit_deopt_handler());
CHECK_BAILOUT();
// Emit the MethodHandle deopt handler code (if required).
if (has_method_handle_invokes()) {
// We can use the same code as for the normal deopt handler, we
// just need a different entry point address.
code_offsets->set_value(CodeOffsets::DeoptMH, assembler->emit_deopt_handler());
CHECK_BAILOUT();
}
// Emit the handler to remove the activation from the stack and
// dispatch to the caller.
offsets()->set_value(CodeOffsets::UnwindHandler, assembler->emit_unwind_handler());
// done
masm()->flush();
}
void Compilation::setup_code_buffer(CodeBuffer* code, int call_stub_estimate) {
// Preinitialize the consts section to some large size:
int locs_buffer_size = 20 * (relocInfo::length_limit + sizeof(relocInfo));
char* locs_buffer = NEW_RESOURCE_ARRAY(char, locs_buffer_size);
code->insts()->initialize_shared_locs((relocInfo*)locs_buffer,
locs_buffer_size / sizeof(relocInfo));
code->initialize_consts_size(Compilation::desired_max_constant_size());
// Call stubs + two deopt handlers (regular and MH) + exception handler
code->initialize_stubs_size((call_stub_estimate * LIR_Assembler::call_stub_size) +
LIR_Assembler::exception_handler_size +
2 * LIR_Assembler::deopt_handler_size);
}
int Compilation::emit_code_body() {
// emit code
setup_code_buffer(code(), allocator()->num_calls());
code()->initialize_oop_recorder(env()->oop_recorder());
_masm = new C1_MacroAssembler(code());
_masm->set_oop_recorder(env()->oop_recorder());
LIR_Assembler lir_asm(this);
lir_asm.emit_code(hir()->code());
CHECK_BAILOUT_(0);
emit_code_epilog(&lir_asm);
CHECK_BAILOUT_(0);
generate_exception_handler_table();
#ifndef PRODUCT
if (PrintExceptionHandlers && Verbose) {
exception_handler_table()->print();
}
#endif /* PRODUCT */
return frame_map()->framesize();
}
int Compilation::compile_java_method() {
assert(!method()->is_native(), "should not reach here");
if (BailoutOnExceptionHandlers) {
if (method()->has_exception_handlers()) {
bailout("linear scan can't handle exception handlers");
}
}
CHECK_BAILOUT_(no_frame_size);
if (is_profiling() && !method()->ensure_method_data()) {
BAILOUT_("mdo allocation failed", no_frame_size);
}
{
PhaseTraceTime timeit(_t_buildIR);
build_hir();
}
if (BailoutAfterHIR) {
BAILOUT_("Bailing out because of -XX:+BailoutAfterHIR", no_frame_size);
}
{
PhaseTraceTime timeit(_t_emit_lir);
_frame_map = new FrameMap(method(), hir()->number_of_locks(), MAX2(4, hir()->max_stack()));
emit_lir();
}
CHECK_BAILOUT_(no_frame_size);
{
PhaseTraceTime timeit(_t_codeemit);
return emit_code_body();
}
}
void Compilation::install_code(int frame_size) {
// frame_size is in 32-bit words so adjust it intptr_t words
assert(frame_size == frame_map()->framesize(), "must match");
assert(in_bytes(frame_map()->framesize_in_bytes()) % sizeof(intptr_t) == 0, "must be at least pointer aligned");
_env->register_method(
method(),
osr_bci(),
&_offsets,
in_bytes(_frame_map->sp_offset_for_orig_pc()),
code(),
in_bytes(frame_map()->framesize_in_bytes()) / sizeof(intptr_t),
debug_info_recorder()->_oopmaps,
exception_handler_table(),
implicit_exception_table(),
compiler(),
_env->comp_level(),
true,
has_unsafe_access()
);
}
void Compilation::compile_method() {
// setup compilation
initialize();
if (!method()->can_be_compiled()) {
// Prevent race condition 6328518.
// This can happen if the method is obsolete or breakpointed.
bailout("Bailing out because method is not compilable");
return;
}
if (_env->jvmti_can_hotswap_or_post_breakpoint()) {
// We can assert evol_method because method->can_be_compiled is true.
dependency_recorder()->assert_evol_method(method());
}
if (method()->break_at_execute()) {
BREAKPOINT;
}
#ifndef PRODUCT
if (PrintCFGToFile) {
CFGPrinter::print_compilation(this);
}
#endif
// compile method
int frame_size = compile_java_method();
// bailout if method couldn't be compiled
// Note: make sure we mark the method as not compilable!
CHECK_BAILOUT();
if (InstallMethods) {
// install code
PhaseTraceTime timeit(_t_codeinstall);
install_code(frame_size);
}
totalInstructionNodes += Instruction::number_of_instructions();
}
void Compilation::generate_exception_handler_table() {
// Generate an ExceptionHandlerTable from the exception handler
// information accumulated during the compilation.
ExceptionInfoList* info_list = exception_info_list();
if (info_list->length() == 0) {
return;
}
// allocate some arrays for use by the collection code.
const int num_handlers = 5;
GrowableArray<intptr_t>* bcis = new GrowableArray<intptr_t>(num_handlers);
GrowableArray<intptr_t>* scope_depths = new GrowableArray<intptr_t>(num_handlers);
GrowableArray<intptr_t>* pcos = new GrowableArray<intptr_t>(num_handlers);
for (int i = 0; i < info_list->length(); i++) {
ExceptionInfo* info = info_list->at(i);
XHandlers* handlers = info->exception_handlers();
// empty the arrays
bcis->trunc_to(0);
scope_depths->trunc_to(0);
pcos->trunc_to(0);
for (int i = 0; i < handlers->length(); i++) {
XHandler* handler = handlers->handler_at(i);
assert(handler->entry_pco() != -1, "must have been generated");
int e = bcis->find(handler->handler_bci());
if (e >= 0 && scope_depths->at(e) == handler->scope_count()) {
// two different handlers are declared to dispatch to the same
// catch bci. During parsing we created edges for each
// handler but we really only need one. The exception handler
// table will also get unhappy if we try to declare both since
// it's nonsensical. Just skip this handler.
continue;
}
bcis->append(handler->handler_bci());
if (handler->handler_bci() == -1) {
// insert a wildcard handler at scope depth 0 so that the
// exception lookup logic with find it.
scope_depths->append(0);
} else {
scope_depths->append(handler->scope_count());
}
pcos->append(handler->entry_pco());
// stop processing once we hit a catch any
if (handler->is_catch_all()) {
assert(i == handlers->length() - 1, "catch all must be last handler");
}
}
exception_handler_table()->add_subtable(info->pco(), bcis, scope_depths, pcos);
}
}
Compilation::Compilation(AbstractCompiler* compiler, ciEnv* env, ciMethod* method,
int osr_bci, BufferBlob* buffer_blob)
: _compiler(compiler)
, _env(env)
, _method(method)
, _osr_bci(osr_bci)
, _hir(NULL)
, _max_spills(-1)
, _frame_map(NULL)
, _masm(NULL)
, _has_exception_handlers(false)
, _has_fpu_code(true) // pessimistic assumption
, _would_profile(false)
, _has_unsafe_access(false)
, _has_method_handle_invokes(false)
, _bailout_msg(NULL)
, _exception_info_list(NULL)
, _allocator(NULL)
, _next_id(0)
, _next_block_id(0)
, _code(buffer_blob)
, _current_instruction(NULL)
#ifndef PRODUCT
, _last_instruction_printed(NULL)
#endif // PRODUCT
{
PhaseTraceTime timeit(_t_compile);
_arena = Thread::current()->resource_area();
_env->set_compiler_data(this);
_exception_info_list = new ExceptionInfoList();
_implicit_exception_table.set_size(0);
compile_method();
if (bailed_out()) {
_env->record_method_not_compilable(bailout_msg(), !TieredCompilation);
if (is_profiling()) {
// Compilation failed, create MDO, which would signal the interpreter
// to start profiling on its own.
_method->ensure_method_data();
}
} else if (is_profiling() && _would_profile) {
ciMethodData *md = method->method_data_or_null();
assert(md != NULL, "Sanity");
md->set_would_profile(_would_profile);
}
}
Compilation::~Compilation() {
_env->set_compiler_data(NULL);
}
void Compilation::add_exception_handlers_for_pco(int pco, XHandlers* exception_handlers) {
#ifndef PRODUCT
if (PrintExceptionHandlers && Verbose) {
tty->print_cr(" added exception scope for pco %d", pco);
}
#endif
// Note: we do not have program counters for these exception handlers yet
exception_info_list()->push(new ExceptionInfo(pco, exception_handlers));
}
void Compilation::notice_inlined_method(ciMethod* method) {
_env->notice_inlined_method(method);
}
void Compilation::bailout(const char* msg) {
assert(msg != NULL, "bailout message must exist");
if (!bailed_out()) {
// keep first bailout message
if (PrintBailouts) tty->print_cr("compilation bailout: %s", msg);
_bailout_msg = msg;
}
}
void Compilation::print_timers() {
// tty->print_cr(" Native methods : %6.3f s, Average : %2.3f", CompileBroker::_t_native_compilation.seconds(), CompileBroker::_t_native_compilation.seconds() / CompileBroker::_total_native_compile_count);
float total = timers[_t_setup].seconds() + timers[_t_buildIR].seconds() + timers[_t_emit_lir].seconds() + timers[_t_lir_schedule].seconds() + timers[_t_codeemit].seconds() + timers[_t_codeinstall].seconds();
tty->print_cr(" Detailed C1 Timings");
tty->print_cr(" Setup time: %6.3f s (%4.1f%%)", timers[_t_setup].seconds(), (timers[_t_setup].seconds() / total) * 100.0);
tty->print_cr(" Build IR: %6.3f s (%4.1f%%)", timers[_t_buildIR].seconds(), (timers[_t_buildIR].seconds() / total) * 100.0);
tty->print_cr(" Optimize: %6.3f s (%4.1f%%)", timers[_t_optimizeIR].seconds(), (timers[_t_optimizeIR].seconds() / total) * 100.0);
tty->print_cr(" Emit LIR: %6.3f s (%4.1f%%)", timers[_t_emit_lir].seconds(), (timers[_t_emit_lir].seconds() / total) * 100.0);
tty->print_cr(" LIR Gen: %6.3f s (%4.1f%%)", timers[_t_lirGeneration].seconds(), (timers[_t_lirGeneration].seconds() / total) * 100.0);
tty->print_cr(" Linear Scan: %6.3f s (%4.1f%%)", timers[_t_linearScan].seconds(), (timers[_t_linearScan].seconds() / total) * 100.0);
NOT_PRODUCT(LinearScan::print_timers(timers[_t_linearScan].seconds()));
tty->print_cr(" LIR Schedule: %6.3f s (%4.1f%%)", timers[_t_lir_schedule].seconds(), (timers[_t_lir_schedule].seconds() / total) * 100.0);
tty->print_cr(" Code Emission: %6.3f s (%4.1f%%)", timers[_t_codeemit].seconds(), (timers[_t_codeemit].seconds() / total) * 100.0);
tty->print_cr(" Code Installation: %6.3f s (%4.1f%%)", timers[_t_codeinstall].seconds(), (timers[_t_codeinstall].seconds() / total) * 100.0);
tty->print_cr(" Instruction Nodes: %6d nodes", totalInstructionNodes);
NOT_PRODUCT(LinearScan::print_statistics());
}
#ifndef PRODUCT
void Compilation::compile_only_this_method() {
ResourceMark rm;
fileStream stream(fopen("c1_compile_only", "wt"));
stream.print_cr("# c1 compile only directives");
compile_only_this_scope(&stream, hir()->top_scope());
}
void Compilation::compile_only_this_scope(outputStream* st, IRScope* scope) {
st->print("CompileOnly=");
scope->method()->holder()->name()->print_symbol_on(st);
st->print(".");
scope->method()->name()->print_symbol_on(st);
st->cr();
}
void Compilation::exclude_this_method() {
fileStream stream(fopen(".hotspot_compiler", "at"));
stream.print("exclude ");
method()->holder()->name()->print_symbol_on(&stream);
stream.print(" ");
method()->name()->print_symbol_on(&stream);
stream.cr();
stream.cr();
}
#endif