/* * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. * Copyright 2008, 2009, 2010, 2011 Red Hat, Inc. * 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 "ci/ciEnv.hpp" #include "ci/ciMethod.hpp" #include "code/debugInfoRec.hpp" #include "code/dependencies.hpp" #include "code/exceptionHandlerTable.hpp" #include "code/oopRecorder.hpp" #include "compiler/abstractCompiler.hpp" #include "compiler/oopMap.hpp" #include "memory/resourceArea.hpp" #include "shark/llvmHeaders.hpp" #include "shark/sharkBuilder.hpp" #include "shark/sharkCodeBuffer.hpp" #include "shark/sharkCompiler.hpp" #include "shark/sharkContext.hpp" #include "shark/sharkEntry.hpp" #include "shark/sharkFunction.hpp" #include "shark/sharkMemoryManager.hpp" #include "shark/sharkNativeWrapper.hpp" #include "shark/shark_globals.hpp" #include "utilities/debug.hpp" #include using namespace llvm; namespace { cl::opt MCPU("mcpu"); cl::list MAttrs("mattr", cl::CommaSeparated); } SharkCompiler::SharkCompiler() : AbstractCompiler(shark) { // Create the lock to protect the memory manager and execution engine _execution_engine_lock = new Monitor(Mutex::leaf, "SharkExecutionEngineLock"); MutexLocker locker(execution_engine_lock()); // Make LLVM safe for multithreading if (!llvm_start_multithreaded()) fatal("llvm_start_multithreaded() failed"); // Initialize the native target InitializeNativeTarget(); // MCJIT require a native AsmPrinter InitializeNativeTargetAsmPrinter(); // Create the two contexts which we'll use _normal_context = new SharkContext("normal"); _native_context = new SharkContext("native"); // Create the memory manager _memory_manager = new SharkMemoryManager(); // Finetune LLVM for the current host CPU. StringMap Features; bool gotCpuFeatures = llvm::sys::getHostCPUFeatures(Features); std::string cpu("-mcpu=" + llvm::sys::getHostCPUName()); std::vector args; args.push_back(""); // program name args.push_back(cpu.c_str()); std::string mattr("-mattr="); if(gotCpuFeatures){ for(StringMap::iterator I = Features.begin(), E = Features.end(); I != E; ++I){ if(I->second){ std::string attr(I->first()); mattr+="+"+attr+","; } } args.push_back(mattr.c_str()); } args.push_back(0); // terminator cl::ParseCommandLineOptions(args.size() - 1, (char **) &args[0]); // Create the JIT std::string ErrorMsg; EngineBuilder builder(_normal_context->module()); builder.setMCPU(MCPU); builder.setMAttrs(MAttrs); builder.setJITMemoryManager(memory_manager()); builder.setEngineKind(EngineKind::JIT); builder.setErrorStr(&ErrorMsg); if (! fnmatch(SharkOptimizationLevel, "None", 0)) { tty->print_cr("Shark optimization level set to: None"); builder.setOptLevel(llvm::CodeGenOpt::None); } else if (! fnmatch(SharkOptimizationLevel, "Less", 0)) { tty->print_cr("Shark optimization level set to: Less"); builder.setOptLevel(llvm::CodeGenOpt::Less); } else if (! fnmatch(SharkOptimizationLevel, "Aggressive", 0)) { tty->print_cr("Shark optimization level set to: Aggressive"); builder.setOptLevel(llvm::CodeGenOpt::Aggressive); } // else Default is selected by, well, default :-) _execution_engine = builder.create(); if (!execution_engine()) { if (!ErrorMsg.empty()) printf("Error while creating Shark JIT: %s\n",ErrorMsg.c_str()); else printf("Unknown error while creating Shark JIT\n"); exit(1); } execution_engine()->addModule(_native_context->module()); // All done set_state(initialized); } void SharkCompiler::initialize() { ShouldNotCallThis(); } void SharkCompiler::compile_method(ciEnv* env, ciMethod* target, int entry_bci, DirectiveSet* directive) { assert(is_initialized(), "should be"); ResourceMark rm; const char *name = methodname( target->holder()->name()->as_utf8(), target->name()->as_utf8()); // Do the typeflow analysis ciTypeFlow *flow; if (entry_bci == InvocationEntryBci) flow = target->get_flow_analysis(); else flow = target->get_osr_flow_analysis(entry_bci); if (flow->failing()) return; if (SharkPrintTypeflowOf != NULL) { if (!fnmatch(SharkPrintTypeflowOf, name, 0)) flow->print_on(tty); } // Create the recorders Arena arena; env->set_oop_recorder(new OopRecorder(&arena)); OopMapSet oopmaps; env->set_debug_info(new DebugInformationRecorder(env->oop_recorder())); env->debug_info()->set_oopmaps(&oopmaps); env->set_dependencies(new Dependencies(env)); // Create the code buffer and builder CodeBuffer hscb("Shark", 256 * K, 64 * K); hscb.initialize_oop_recorder(env->oop_recorder()); MacroAssembler *masm = new MacroAssembler(&hscb); SharkCodeBuffer cb(masm); SharkBuilder builder(&cb); // Emit the entry point SharkEntry *entry = (SharkEntry *) cb.malloc(sizeof(SharkEntry)); // Build the LLVM IR for the method Function *function = SharkFunction::build(env, &builder, flow, name); if (env->failing()) { return; } // Generate native code. It's unpleasant that we have to drop into // the VM to do this -- it blocks safepoints -- but I can't see any // other way to handle the locking. { ThreadInVMfromNative tiv(JavaThread::current()); generate_native_code(entry, function, name); } // Install the method into the VM CodeOffsets offsets; offsets.set_value(CodeOffsets::Deopt, 0); offsets.set_value(CodeOffsets::Exceptions, 0); offsets.set_value(CodeOffsets::Verified_Entry, target->is_static() ? 0 : wordSize); ExceptionHandlerTable handler_table; ImplicitExceptionTable inc_table; env->register_method(target, entry_bci, &offsets, 0, &hscb, 0, &oopmaps, &handler_table, &inc_table, this, false, directive(), false); } nmethod* SharkCompiler::generate_native_wrapper(MacroAssembler* masm, const methodHandle& target, int compile_id, BasicType* arg_types, BasicType return_type) { assert(is_initialized(), "should be"); ResourceMark rm; const char *name = methodname( target->klass_name()->as_utf8(), target->name()->as_utf8()); // Create the code buffer and builder SharkCodeBuffer cb(masm); SharkBuilder builder(&cb); // Emit the entry point SharkEntry *entry = (SharkEntry *) cb.malloc(sizeof(SharkEntry)); // Build the LLVM IR for the method SharkNativeWrapper *wrapper = SharkNativeWrapper::build( &builder, target, name, arg_types, return_type); // Generate native code generate_native_code(entry, wrapper->function(), name); // Return the nmethod for installation in the VM return nmethod::new_native_nmethod(target, compile_id, masm->code(), 0, 0, wrapper->frame_size(), wrapper->receiver_offset(), wrapper->lock_offset(), wrapper->oop_maps()); } void SharkCompiler::generate_native_code(SharkEntry* entry, Function* function, const char* name) { // Print the LLVM bitcode, if requested if (SharkPrintBitcodeOf != NULL) { if (!fnmatch(SharkPrintBitcodeOf, name, 0)) function->dump(); } if (SharkVerifyFunction != NULL) { if (!fnmatch(SharkVerifyFunction, name, 0)) { verifyFunction(*function); } } // Compile to native code address code = NULL; context()->add_function(function); { MutexLocker locker(execution_engine_lock()); free_queued_methods(); #ifndef NDEBUG #if SHARK_LLVM_VERSION <= 31 #define setCurrentDebugType SetCurrentDebugType #endif if (SharkPrintAsmOf != NULL) { if (!fnmatch(SharkPrintAsmOf, name, 0)) { llvm::setCurrentDebugType(X86_ONLY("x86-emitter") NOT_X86("jit")); llvm::DebugFlag = true; } else { llvm::setCurrentDebugType(""); llvm::DebugFlag = false; } } #ifdef setCurrentDebugType #undef setCurrentDebugType #endif #endif // !NDEBUG memory_manager()->set_entry_for_function(function, entry); code = (address) execution_engine()->getPointerToFunction(function); } assert(code != NULL, "code must be != NULL"); entry->set_entry_point(code); entry->set_function(function); entry->set_context(context()); address code_start = entry->code_start(); address code_limit = entry->code_limit(); // Register generated code for profiling, etc if (JvmtiExport::should_post_dynamic_code_generated()) JvmtiExport::post_dynamic_code_generated(name, code_start, code_limit); // Print debug information, if requested if (SharkTraceInstalls) { tty->print_cr( " [%p-%p): %s (%d bytes code)", code_start, code_limit, name, code_limit - code_start); } } void SharkCompiler::free_compiled_method(address code) { // This method may only be called when the VM is at a safepoint. // All _thread_in_vm threads will be waiting for the safepoint to // finish with the exception of the VM thread, so we can consider // ourself the owner of the execution engine lock even though we // can't actually acquire it at this time. assert(Thread::current()->is_Compiler_thread(), "must be called by compiler thread"); assert_locked_or_safepoint(CodeCache_lock); SharkEntry *entry = (SharkEntry *) code; entry->context()->push_to_free_queue(entry->function()); } void SharkCompiler::free_queued_methods() { // The free queue is protected by the execution engine lock assert(execution_engine_lock()->owned_by_self(), "should be"); while (true) { Function *function = context()->pop_from_free_queue(); if (function == NULL) break; execution_engine()->freeMachineCodeForFunction(function); function->eraseFromParent(); } } const char* SharkCompiler::methodname(const char* klass, const char* method) { char *buf = NEW_RESOURCE_ARRAY(char, strlen(klass) + 2 + strlen(method) + 1); char *dst = buf; for (const char *c = klass; *c; c++) { if (*c == '/') *(dst++) = '.'; else *(dst++) = *c; } *(dst++) = ':'; *(dst++) = ':'; for (const char *c = method; *c; c++) { *(dst++) = *c; } *(dst++) = '\0'; return buf; } void SharkCompiler::print_timers() { // do nothing }