Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/hotspot/share/classfile/javaClasses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2453,7 +2453,7 @@ void java_lang_Throwable::print_stack_trace(Handle throwable, outputStream* st)
BacktraceElement bte = iter.next(THREAD);
print_stack_element_to_stream(st, bte._mirror, bte._method_id, bte._version, bte._bci, bte._name);
}
{
if (THREAD->can_call_java()) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows java_lang_Throwable::print_stack_trace to be used in contexts where Java calls cannot be made.

// Call getCause() which doesn't necessarily return the _cause field.
ExceptionMark em(THREAD);
JavaValue cause(T_OBJECT);
Expand All @@ -2475,6 +2475,9 @@ void java_lang_Throwable::print_stack_trace(Handle throwable, outputStream* st)
st->cr();
}
}
} else {
st->print_raw_cr("<<cannot call Java to get cause>>");
return;
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/hotspot/share/classfile/systemDictionary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ InstanceKlass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
InstanceKlass* loaded_class = nullptr;
SymbolHandle superclassname; // Keep alive while loading in parallel thread.

assert(THREAD->can_call_java(),
guarantee(THREAD->can_call_java(),
"can not load classes with compiler thread: class=%s, classloader=%s",
name->as_C_string(),
class_loader.is_null() ? "null" : class_loader->klass()->name()->as_C_string());
Expand Down Expand Up @@ -2056,7 +2056,7 @@ Method* SystemDictionary::find_method_handle_invoker(Klass* klass,
Klass* accessing_klass,
Handle* appendix_result,
TRAPS) {
assert(THREAD->can_call_java() ,"");
guarantee(THREAD->can_call_java(), "");
Handle method_type =
SystemDictionary::find_method_handle_type(signature, accessing_klass, CHECK_NULL);

Expand Down
11 changes: 7 additions & 4 deletions src/hotspot/share/compiler/compilerThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ CompilerThread::CompilerThread(CompileQueue* queue,
_queue = queue;
_counters = counters;
_buffer_blob = nullptr;
_can_call_java = false;
_compiler = nullptr;
_arena_stat = CompilationMemoryStatistic::enabled() ? new ArenaStatCounter : nullptr;

Expand All @@ -56,15 +57,17 @@ CompilerThread::~CompilerThread() {
delete _arena_stat;
}

void CompilerThread::set_compiler(AbstractCompiler* c) {
// Only jvmci compiler threads can call Java
_can_call_java = c != nullptr && c->is_jvmci();
_compiler = c;
}

void CompilerThread::thread_entry(JavaThread* thread, TRAPS) {
assert(thread->is_Compiler_thread(), "must be compiler thread");
CompileBroker::compiler_thread_loop();
}

bool CompilerThread::can_call_java() const {
return _compiler != nullptr && _compiler->is_jvmci();
}

// Hide native compiler threads from external view.
bool CompilerThread::is_hidden_from_external_view() const {
return _compiler == nullptr || _compiler->is_hidden_from_external_view();
Expand Down
10 changes: 5 additions & 5 deletions src/hotspot/share/compiler/compilerThread.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,17 @@ class AbstractCompiler;
class ArenaStatCounter;
class BufferBlob;
class ciEnv;
class CompileThread;
class CompilerThread;
class CompileLog;
class CompileTask;
class CompileQueue;
class CompilerCounters;
class IdealGraphPrinter;
class JVMCIEnv;
class JVMCIPrimitiveArray;

// A thread used for Compilation.
class CompilerThread : public JavaThread {
friend class VMStructs;
JVMCI_ONLY(friend class CompilerThreadCanCallJava;)
private:
CompilerCounters* _counters;

Expand All @@ -51,6 +50,7 @@ class CompilerThread : public JavaThread {
CompileTask* volatile _task; // print_threads_compiling can read this concurrently.
CompileQueue* _queue;
BufferBlob* _buffer_blob;
bool _can_call_java;

AbstractCompiler* _compiler;
TimeStamp _idle_time;
Expand All @@ -73,13 +73,13 @@ class CompilerThread : public JavaThread {

bool is_Compiler_thread() const { return true; }

virtual bool can_call_java() const;
virtual bool can_call_java() const { return _can_call_java; }

// Returns true if this CompilerThread is hidden from JVMTI and FlightRecorder. C1 and C2 are
// always hidden but JVMCI compiler threads might be hidden.
virtual bool is_hidden_from_external_view() const;

void set_compiler(AbstractCompiler* c) { _compiler = c; }
void set_compiler(AbstractCompiler* c);
AbstractCompiler* compiler() const { return _compiler; }

CompileQueue* queue() const { return _queue; }
Expand Down
28 changes: 28 additions & 0 deletions src/hotspot/share/jvmci/jvmci.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include "precompiled.hpp"
#include "classfile/systemDictionary.hpp"
#include "compiler/abstractCompiler.hpp"
#include "compiler/compileTask.hpp"
#include "compiler/compilerThread.hpp"
#include "gc/shared/collectedHeap.hpp"
Expand Down Expand Up @@ -53,6 +54,29 @@ volatile intx JVMCI::_fatal_log_init_thread = -1;
volatile int JVMCI::_fatal_log_fd = -1;
const char* JVMCI::_fatal_log_filename = nullptr;

CompilerThreadCanCallJava::CompilerThreadCanCallJava(JavaThread* current, bool new_state) {
_current = nullptr;
if (current->is_Compiler_thread()) {
CompilerThread* ct = CompilerThread::cast(current);
if (ct->_can_call_java != new_state &&
ct->_compiler != nullptr &&
ct->_compiler->is_jvmci())
{
// Only enter a new context if the ability of the
// current thread to call Java actually changes
_reset_state = ct->_can_call_java;
ct->_can_call_java = new_state;
_current = ct;
}
}
}

CompilerThreadCanCallJava::~CompilerThreadCanCallJava() {
if (_current != nullptr) {
_current->_can_call_java = _reset_state;
}
}

void jvmci_vmStructs_init() NOT_DEBUG_RETURN;

bool JVMCI::can_initialize_JVMCI() {
Expand Down Expand Up @@ -176,6 +200,10 @@ void JVMCI::ensure_box_caches_initialized(TRAPS) {
java_lang_Long_LongCache::symbol()
};

// Class resolution and initialization below
// requires calling into Java
CompilerThreadCanCallJava ccj(THREAD, true);

for (unsigned i = 0; i < sizeof(box_classes) / sizeof(Symbol*); i++) {
Klass* k = SystemDictionary::resolve_or_fail(box_classes[i], true, CHECK);
InstanceKlass* ik = InstanceKlass::cast(k);
Expand Down
29 changes: 29 additions & 0 deletions src/hotspot/share/jvmci/jvmci.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "utilities/exceptions.hpp"

class BoolObjectClosure;
class CompilerThread;
class constantPoolHandle;
class JavaThread;
class JVMCIEnv;
Expand All @@ -46,6 +47,34 @@ typedef FormatStringEventLog<256> StringEventLog;
struct _jmetadata;
typedef struct _jmetadata *jmetadata;

// A stack object that manages a scope in which the current thread, if
// it's a CompilerThread, can have its CompilerThread::_can_call_java
// field changed. This allows restricting libjvmci better in terms
// of when it can make Java calls. If a Java call on a CompilerThread
// reaches a clinit, there's a risk of dead-lock when async compilation
// is disabled (e.g. -Xbatch or -Xcomp) as the non-CompilerThread thread
// waiting for the blocking compilation may hold the clinit lock.
//
// This scope is primarily used to disable Java calls when libjvmci enters
// the VM via a C2V (i.e. CompilerToVM) native method.
class CompilerThreadCanCallJava : StackObj {
private:
CompilerThread* _current; // Only non-null if state of thread changed
bool _reset_state; // Value prior to state change, undefined
// if no state change.
public:
// Enters a scope in which the ability of the current CompilerThread
// to call Java is specified by `new_state`. This call only makes a
// change if the current thread is a CompilerThread associated with
// a JVMCI compiler whose CompilerThread::_can_call_java is not
// currently `new_state`.
CompilerThreadCanCallJava(JavaThread* current, bool new_state);

// Resets CompilerThread::_can_call_java of the current thread if the
// constructor changed it.
~CompilerThreadCanCallJava();
};

class JVMCI : public AllStatic {
friend class JVMCIRuntime;
friend class JVMCIEnv;
Expand Down
42 changes: 32 additions & 10 deletions src/hotspot/share/jvmci/jvmciCompilerToVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,19 @@ Handle JavaArgumentUnboxer::next_arg(BasicType expectedType) {
MACOS_AARCH64_ONLY(ThreadWXEnable __wx(WXWrite, thread)); \
ThreadInVMfromNative __tiv(thread); \
HandleMarkCleaner __hm(thread); \
JavaThread* THREAD = thread; \
JavaThread* THREAD = thread; \
debug_only(VMNativeEntryWrapper __vew;)

// Native method block that transitions current thread to '_thread_in_vm'.
#define C2V_BLOCK(result_type, name, signature) \
JVMCI_VM_ENTRY_MARK; \
ResourceMark rm; \
JVMCIENV_FROM_JNI(JVMCI::compilation_tick(thread), env);
// Note: CompilerThreadCanCallJava must precede JVMCIENV_FROM_JNI so that
// the translation of an uncaught exception in the JVMCIEnv does not make
// a Java call when __is_hotspot == false.
#define C2V_BLOCK(result_type, name, signature) \
JVMCI_VM_ENTRY_MARK; \
ResourceMark rm; \
bool __is_hotspot = env == thread->jni_environment(); \
CompilerThreadCanCallJava ccj(thread, __is_hotspot); \
JVMCIENV_FROM_JNI(JVMCI::compilation_tick(thread), env); \

static JavaThread* get_current_thread(bool allow_null=true) {
Thread* thread = Thread::current_or_null_safe();
Expand All @@ -188,7 +193,7 @@ static JavaThread* get_current_thread(bool allow_null=true) {
#define C2V_VMENTRY(result_type, name, signature) \
JNIEXPORT result_type JNICALL c2v_ ## name signature { \
JavaThread* thread = get_current_thread(); \
if (thread == nullptr) { \
if (thread == nullptr) { \
env->ThrowNew(JNIJVMCI::InternalError::clazz(), \
err_msg("Cannot call into HotSpot from JVMCI shared library without attaching current thread")); \
return; \
Expand All @@ -199,7 +204,7 @@ static JavaThread* get_current_thread(bool allow_null=true) {
#define C2V_VMENTRY_(result_type, name, signature, result) \
JNIEXPORT result_type JNICALL c2v_ ## name signature { \
JavaThread* thread = get_current_thread(); \
if (thread == nullptr) { \
if (thread == nullptr) { \
env->ThrowNew(JNIJVMCI::InternalError::clazz(), \
err_msg("Cannot call into HotSpot from JVMCI shared library without attaching current thread")); \
return result; \
Expand All @@ -221,15 +226,15 @@ static JavaThread* get_current_thread(bool allow_null=true) {
#define JNI_THROW(caller, name, msg) do { \
jint __throw_res = env->ThrowNew(JNIJVMCI::name::clazz(), msg); \
if (__throw_res != JNI_OK) { \
tty->print_cr("Throwing " #name " in " caller " returned %d", __throw_res); \
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The VM should prefer event logging over printing to the console.

JVMCI_event_1("Throwing " #name " in " caller " returned %d", __throw_res); \
} \
return; \
} while (0);

#define JNI_THROW_(caller, name, msg, result) do { \
jint __throw_res = env->ThrowNew(JNIJVMCI::name::clazz(), msg); \
if (__throw_res != JNI_OK) { \
tty->print_cr("Throwing " #name " in " caller " returned %d", __throw_res); \
JVMCI_event_1("Throwing " #name " in " caller " returned %d", __throw_res); \
} \
return result; \
} while (0)
Expand Down Expand Up @@ -579,6 +584,7 @@ C2V_VMENTRY_0(jboolean, shouldInlineMethod,(JNIEnv* env, jobject, ARGUMENT_PAIR(
C2V_END

C2V_VMENTRY_NULL(jobject, lookupType, (JNIEnv* env, jobject, jstring jname, ARGUMENT_PAIR(accessing_klass), jint accessing_klass_loader, jboolean resolve))
CompilerThreadCanCallJava canCallJava(thread, resolve); // Resolution requires Java calls
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is currently required by libgraal - it may be fixable in future.

JVMCIObject name = JVMCIENV->wrap(jname);
const char* str = JVMCIENV->as_utf8_string(name);
TempNewSymbol class_name = SymbolTable::new_symbol(str);
Expand All @@ -592,7 +598,7 @@ C2V_VMENTRY_NULL(jobject, lookupType, (JNIEnv* env, jobject, jstring jname, ARGU
if (val != nullptr) {
if (strstr(val, "<trace>") != nullptr) {
tty->print_cr("CompilerToVM.lookupType: %s", str);
} else if (strstr(val, str) != nullptr) {
} else if (strstr(str, val) != nullptr) {
Copy link
Member Author

@dougxc dougxc Oct 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes an existing bug: the test is meant to be whether val is a substring of str, not the other way around.

THROW_MSG_0(vmSymbols::java_lang_Exception(),
err_msg("lookupTypeException: %s", str));
}
Expand Down Expand Up @@ -938,6 +944,17 @@ C2V_VMENTRY_NULL(jobject, resolveFieldInPool, (JNIEnv* env, jobject, ARGUMENT_PA
Bytecodes::Code code = (Bytecodes::Code)(((int) opcode) & 0xFF);
fieldDescriptor fd;
methodHandle mh(THREAD, UNPACK_PAIR(Method, method));

Bytecodes::Code bc = (Bytecodes::Code) (((int) opcode) & 0xFF);
int holder_index = cp->klass_ref_index_at(index, bc);
if (!cp->tag_at(holder_index).is_klass() && !THREAD->can_call_java()) {
// If the holder is not resolved in the constant pool and the current
// thread cannot call Java, return null. This avoids a Java call
// in LinkInfo to load the holder.
Symbol* klass_name = cp->klass_ref_at_noresolve(index, bc);
return nullptr;
}

LinkInfo link_info(cp, index, mh, code, CHECK_NULL);
LinkResolver::resolve_field(fd, link_info, Bytecodes::java_code(code), false, CHECK_NULL);
JVMCIPrimitiveArray info = JVMCIENV->wrap(info_handle);
Expand Down Expand Up @@ -2726,6 +2743,7 @@ C2V_VMENTRY_0(jlong, translate, (JNIEnv* env, jobject, jobject obj_handle, jbool
return 0L;
}
PEER_JVMCIENV_FROM_THREAD(THREAD, !JVMCIENV->is_hotspot());
CompilerThreadCanCallJava canCallJava(thread, PEER_JVMCIENV->is_hotspot());
PEER_JVMCIENV->check_init(JVMCI_CHECK_0);

JVMCIEnv* thisEnv = JVMCIENV;
Expand Down Expand Up @@ -2945,18 +2963,21 @@ static jbyteArray get_encoded_annotation_data(InstanceKlass* holder, AnnotationA

C2V_VMENTRY_NULL(jbyteArray, getEncodedClassAnnotationData, (JNIEnv* env, jobject, ARGUMENT_PAIR(klass),
jobject filter, jint filter_length, jlong filter_klass_pointers))
CompilerThreadCanCallJava canCallJava(thread, true); // Requires Java support
InstanceKlass* holder = InstanceKlass::cast(UNPACK_PAIR(Klass, klass));
return get_encoded_annotation_data(holder, holder->class_annotations(), true, filter_length, filter_klass_pointers, THREAD, JVMCIENV);
C2V_END

C2V_VMENTRY_NULL(jbyteArray, getEncodedExecutableAnnotationData, (JNIEnv* env, jobject, ARGUMENT_PAIR(method),
jobject filter, jint filter_length, jlong filter_klass_pointers))
CompilerThreadCanCallJava canCallJava(thread, true); // Requires Java support
methodHandle method(THREAD, UNPACK_PAIR(Method, method));
return get_encoded_annotation_data(method->method_holder(), method->annotations(), false, filter_length, filter_klass_pointers, THREAD, JVMCIENV);
C2V_END

C2V_VMENTRY_NULL(jbyteArray, getEncodedFieldAnnotationData, (JNIEnv* env, jobject, ARGUMENT_PAIR(klass), jint index,
jobject filter, jint filter_length, jlong filter_klass_pointers))
CompilerThreadCanCallJava canCallJava(thread, true); // Requires Java support
InstanceKlass* holder = check_field(InstanceKlass::cast(UNPACK_PAIR(Klass, klass)), index, JVMCIENV);
fieldDescriptor fd(holder, index);
return get_encoded_annotation_data(holder, fd.annotations(), false, filter_length, filter_klass_pointers, THREAD, JVMCIENV);
Expand Down Expand Up @@ -3013,6 +3034,7 @@ C2V_VMENTRY_0(jboolean, addFailedSpeculation, (JNIEnv* env, jobject, jlong faile
C2V_END

C2V_VMENTRY(void, callSystemExit, (JNIEnv* env, jobject, jint status))
CompilerThreadCanCallJava canCallJava(thread, true);
JavaValue result(T_VOID);
JavaCallArguments jargs(1);
jargs.push_int(status);
Expand Down
23 changes: 18 additions & 5 deletions src/hotspot/share/jvmci/jvmciEnv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,15 @@ class HotSpotToSharedLibraryExceptionTranslation : public ExceptionTranslation {
private:
const Handle& _throwable;

char* print_throwable_to_buffer(Handle throwable, jlong buffer, int buffer_size) {
char* char_buffer = (char*) buffer + 4;
stringStream st(char_buffer, (size_t) buffer_size - 4);
java_lang_Throwable::print_stack_trace(throwable, &st);
u4 len = (u4) st.size();
*((u4*) buffer) = len;
return char_buffer;
}

bool handle_pending_exception(JavaThread* THREAD, jlong buffer, int buffer_size) {
if (HAS_PENDING_EXCEPTION) {
Handle throwable = Handle(THREAD, PENDING_EXCEPTION);
Expand All @@ -457,11 +466,7 @@ class HotSpotToSharedLibraryExceptionTranslation : public ExceptionTranslation {
JVMCI_event_1("error translating exception: OutOfMemoryError");
decode(THREAD, _encode_oome_fail, 0L);
} else {
char* char_buffer = (char*) buffer + 4;
stringStream st(char_buffer, (size_t) buffer_size - 4);
java_lang_Throwable::print_stack_trace(throwable, &st);
u4 len = (u4) st.size();
*((u4*) buffer) = len;
char* char_buffer = print_throwable_to_buffer(throwable, buffer, buffer_size);
JVMCI_event_1("error translating exception: %s", char_buffer);
decode(THREAD, _encode_fail, buffer);
}
Expand All @@ -471,6 +476,13 @@ class HotSpotToSharedLibraryExceptionTranslation : public ExceptionTranslation {
}

int encode(JavaThread* THREAD, jlong buffer, int buffer_size) {
if (!THREAD->can_call_java()) {
char* char_buffer = print_throwable_to_buffer(_throwable, buffer, buffer_size);
const char* detail = log_is_enabled(Info, exceptions) ? "" : " (-Xlog:exceptions may give more detail)";
JVMCI_event_1("cannot call Java to translate exception%s: %s", detail, char_buffer);
decode(THREAD, _encode_fail, buffer);
return 0;
}
Klass* vmSupport = SystemDictionary::resolve_or_fail(vmSymbols::jdk_internal_vm_VMSupport(), true, THREAD);
if (handle_pending_exception(THREAD, buffer, buffer_size)) {
return 0;
Expand Down Expand Up @@ -1311,6 +1323,7 @@ JVMCIObject JVMCIEnv::get_jvmci_type(const JVMCIKlassHandle& klass, JVMCI_TRAPS)
JavaThread* THREAD = JVMCI::compilation_tick(JavaThread::current()); // For exception macros.
jboolean exception = false;
if (is_hotspot()) {
CompilerThreadCanCallJava ccj(THREAD, true);
JavaValue result(T_OBJECT);
JavaCallArguments args;
args.push_long(pointer);
Expand Down
Loading