/*
* Copyright (c) 1999, 2022, 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 "asm/macroAssembler.hpp"
#include "ci/ciUtilities.inline.hpp"
#include "classfile/vmIntrinsics.hpp"
#include "compiler/compileBroker.hpp"
#include "compiler/compileLog.hpp"
#include "gc/shared/barrierSet.hpp"
#include "jfr/support/jfrIntrinsics.hpp"
#include "memory/resourceArea.hpp"
#include "oops/klass.inline.hpp"
#include "oops/objArrayKlass.hpp"
#include "opto/addnode.hpp"
#include "opto/arraycopynode.hpp"
#include "opto/c2compiler.hpp"
#include "opto/castnode.hpp"
#include "opto/cfgnode.hpp"
#include "opto/convertnode.hpp"
#include "opto/countbitsnode.hpp"
#include "opto/idealKit.hpp"
#include "opto/library_call.hpp"
#include "opto/mathexactnode.hpp"
#include "opto/mulnode.hpp"
#include "opto/narrowptrnode.hpp"
#include "opto/opaquenode.hpp"
#include "opto/parse.hpp"
#include "opto/runtime.hpp"
#include "opto/rootnode.hpp"
#include "opto/subnode.hpp"
#include "prims/unsafe.hpp"
#include "runtime/objectMonitor.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/stubRoutines.hpp"
#include "utilities/macros.hpp"
#include "utilities/powerOfTwo.hpp"
//---------------------------make_vm_intrinsic----------------------------
CallGenerator* Compile::make_vm_intrinsic(ciMethod* m, bool is_virtual) {
vmIntrinsicID id = m->intrinsic_id();
assert(id != vmIntrinsics::_none, "must be a VM intrinsic");
if (!m->is_loaded()) {
// Do not attempt to inline unloaded methods.
return NULL;
}
C2Compiler* compiler = (C2Compiler*)CompileBroker::compiler(CompLevel_full_optimization);
bool is_available = false;
{
// For calling is_intrinsic_supported and is_intrinsic_disabled_by_flag
// the compiler must transition to '_thread_in_vm' state because both
// methods access VM-internal data.
VM_ENTRY_MARK;
methodHandle mh(THREAD, m->get_Method());
is_available = compiler != NULL && compiler->is_intrinsic_supported(mh, is_virtual) &&
!C->directive()->is_intrinsic_disabled(mh) &&
!vmIntrinsics::is_disabled_by_flags(mh);
}
if (is_available) {
assert(id <= vmIntrinsics::LAST_COMPILER_INLINE, "caller responsibility");
assert(id != vmIntrinsics::_Object_init && id != vmIntrinsics::_invoke, "enum out of order?");
return new LibraryIntrinsic(m, is_virtual,
vmIntrinsics::predicates_needed(id),
vmIntrinsics::does_virtual_dispatch(id),
id);
} else {
return NULL;
}
}
JVMState* LibraryIntrinsic::generate(JVMState* jvms) {
LibraryCallKit kit(jvms, this);
Compile* C = kit.C;
int nodes = C->unique();
#ifndef PRODUCT
if ((C->print_intrinsics() || C->print_inlining()) && Verbose) {
char buf[1000];
const char* str = vmIntrinsics::short_name_as_C_string(intrinsic_id(), buf, sizeof(buf));
tty->print_cr("Intrinsic %s", str);
}
#endif
ciMethod* callee = kit.callee();
const int bci = kit.bci();
#ifdef ASSERT
Node* ctrl = kit.control();
#endif
// Try to inline the intrinsic.
if (callee->check_intrinsic_candidate() &&
kit.try_to_inline(_last_predicate)) {
const char *inline_msg = is_virtual() ? "(intrinsic, virtual)"
: "(intrinsic)";
CompileTask::print_inlining_ul(callee, jvms->depth() - 1, bci, inline_msg);
if (C->print_intrinsics() || C->print_inlining()) {
C->print_inlining(callee, jvms->depth() - 1, bci, inline_msg);
}
C->gather_intrinsic_statistics(intrinsic_id(), is_virtual(), Compile::_intrinsic_worked);
if (C->log()) {
C->log()->elem("intrinsic id='%s'%s nodes='%d'",
vmIntrinsics::name_at(intrinsic_id()),
(is_virtual() ? " virtual='1'" : ""),
C->unique() - nodes);
}
// Push the result from the inlined method onto the stack.
kit.push_result();
C->print_inlining_update(this);
return kit.transfer_exceptions_into_jvms();
}
// The intrinsic bailed out
assert(ctrl == kit.control(), "Control flow was added although the intrinsic bailed out");
if (jvms->has_method()) {
// Not a root compile.
const char* msg;
if (callee->intrinsic_candidate()) {
msg = is_virtual() ? "failed to inline (intrinsic, virtual)" : "failed to inline (intrinsic)";
} else {
msg = is_virtual() ? "failed to inline (intrinsic, virtual), method not annotated"
: "failed to inline (intrinsic), method not annotated";
}
CompileTask::print_inlining_ul(callee, jvms->depth() - 1, bci, msg);
if (C->print_intrinsics() || C->print_inlining()) {
C->print_inlining(callee, jvms->depth() - 1, bci, msg);
}
} else {
// Root compile
ResourceMark rm;
stringStream msg_stream;
msg_stream.print("Did not generate intrinsic %s%s at bci:%d in",
vmIntrinsics::name_at(intrinsic_id()),
is_virtual() ? " (virtual)" : "", bci);
const char *msg = msg_stream.freeze();
log_debug(jit, inlining)("%s", msg);
if (C->print_intrinsics() || C->print_inlining()) {
tty->print("%s", msg);
}
}
C->gather_intrinsic_statistics(intrinsic_id(), is_virtual(), Compile::_intrinsic_failed);
C->print_inlining_update(this);
return NULL;
}
Node* LibraryIntrinsic::generate_predicate(JVMState* jvms, int predicate) {
LibraryCallKit kit(jvms, this);
Compile* C = kit.C;
int nodes = C->unique();
_last_predicate = predicate;
#ifndef PRODUCT
assert(is_predicated() && predicate < predicates_count(), "sanity");
if ((C->print_intrinsics() || C->print_inlining()) && Verbose) {
char buf[1000];
const char* str = vmIntrinsics::short_name_as_C_string(intrinsic_id(), buf, sizeof(buf));
tty->print_cr("Predicate for intrinsic %s", str);
}
#endif
ciMethod* callee = kit.callee();
const int bci = kit.bci();
Node* slow_ctl = kit.try_to_predicate(predicate);
if (!kit.failing()) {
const char *inline_msg = is_virtual() ? "(intrinsic, virtual, predicate)"
: "(intrinsic, predicate)";
CompileTask::print_inlining_ul(callee, jvms->depth() - 1, bci, inline_msg);
if (C->print_intrinsics() || C->print_inlining()) {
C->print_inlining(callee, jvms->depth() - 1, bci, inline_msg);
}
C->gather_intrinsic_statistics(intrinsic_id(), is_virtual(), Compile::_intrinsic_worked);
if (C->log()) {
C->log()->elem("predicate_intrinsic id='%s'%s nodes='%d'",
vmIntrinsics::name_at(intrinsic_id()),
(is_virtual() ? " virtual='1'" : ""),
C->unique() - nodes);
}
return slow_ctl; // Could be NULL if the check folds.
}
// The intrinsic bailed out
if (jvms->has_method()) {
// Not a root compile.
const char* msg = "failed to generate predicate for intrinsic";
CompileTask::print_inlining_ul(kit.callee(), jvms->depth() - 1, bci, msg);
if (C->print_intrinsics() || C->print_inlining()) {
C->print_inlining(kit.callee(), jvms->depth() - 1, bci, msg);
}
} else {
// Root compile
ResourceMark rm;
stringStream msg_stream;
msg_stream.print("Did not generate intrinsic %s%s at bci:%d in",
vmIntrinsics::name_at(intrinsic_id()),
is_virtual() ? " (virtual)" : "", bci);
const char *msg = msg_stream.freeze();
log_debug(jit, inlining)("%s", msg);
if (C->print_intrinsics() || C->print_inlining()) {
C->print_inlining_stream()->print("%s", msg);
}
}
C->gather_intrinsic_statistics(intrinsic_id(), is_virtual(), Compile::_intrinsic_failed);
return NULL;
}
bool LibraryCallKit::try_to_inline(int predicate) {
// Handle symbolic names for otherwise undistinguished boolean switches:
const bool is_store = true;
const bool is_compress = true;
const bool is_static = true;
const bool is_volatile = true;
if (!jvms()->has_method()) {
// Root JVMState has a null method.
assert(map()->memory()->Opcode() == Op_Parm, "");
// Insert the memory aliasing node
set_all_memory(reset_memory());
}
assert(merged_memory(), "");
switch (intrinsic_id()) {
case vmIntrinsics::_hashCode: return inline_native_hashcode(intrinsic()->is_virtual(), !is_static);
case vmIntrinsics::_identityHashCode: return inline_native_hashcode(/*!virtual*/ false, is_static);
case vmIntrinsics::_getClass: return inline_native_getClass();
case vmIntrinsics::_ceil:
case vmIntrinsics::_floor:
case vmIntrinsics::_rint:
case vmIntrinsics::_dsin:
case vmIntrinsics::_dcos:
case vmIntrinsics::_dtan:
case vmIntrinsics::_dabs:
case vmIntrinsics::_fabs:
case vmIntrinsics::_iabs:
case vmIntrinsics::_labs:
case vmIntrinsics::_datan2:
case vmIntrinsics::_dsqrt:
case vmIntrinsics::_dsqrt_strict:
case vmIntrinsics::_dexp:
case vmIntrinsics::_dlog:
case vmIntrinsics::_dlog10:
case vmIntrinsics::_dpow:
case vmIntrinsics::_dcopySign:
case vmIntrinsics::_fcopySign:
case vmIntrinsics::_dsignum:
case vmIntrinsics::_roundF:
case vmIntrinsics::_roundD:
case vmIntrinsics::_fsignum: return inline_math_native(intrinsic_id());
case vmIntrinsics::_notify:
case vmIntrinsics::_notifyAll:
return inline_notify(intrinsic_id());
case vmIntrinsics::_addExactI: return inline_math_addExactI(false /* add */);
case vmIntrinsics::_addExactL: return inline_math_addExactL(false /* add */);
case vmIntrinsics::_decrementExactI: return inline_math_subtractExactI(true /* decrement */);
case vmIntrinsics::_decrementExactL: return inline_math_subtractExactL(true /* decrement */);
case vmIntrinsics::_incrementExactI: return inline_math_addExactI(true /* increment */);
case vmIntrinsics::_incrementExactL: return inline_math_addExactL(true /* increment */);
case vmIntrinsics::_multiplyExactI: return inline_math_multiplyExactI();
case vmIntrinsics::_multiplyExactL: return inline_math_multiplyExactL();
case vmIntrinsics::_multiplyHigh: return inline_math_multiplyHigh();
case vmIntrinsics::_unsignedMultiplyHigh: return inline_math_unsignedMultiplyHigh();
case vmIntrinsics::_negateExactI: return inline_math_negateExactI();
case vmIntrinsics::_negateExactL: return inline_math_negateExactL();
case vmIntrinsics::_subtractExactI: return inline_math_subtractExactI(false /* subtract */);
case vmIntrinsics::_subtractExactL: return inline_math_subtractExactL(false /* subtract */);
case vmIntrinsics::_arraycopy: return inline_arraycopy();
case vmIntrinsics::_compareToL: return inline_string_compareTo(StrIntrinsicNode::LL);
case vmIntrinsics::_compareToU: return inline_string_compareTo(StrIntrinsicNode::UU);
case vmIntrinsics::_compareToLU: return inline_string_compareTo(StrIntrinsicNode::LU);
case vmIntrinsics::_compareToUL: return inline_string_compareTo(StrIntrinsicNode::UL);
case vmIntrinsics::_indexOfL: return inline_string_indexOf(StrIntrinsicNode::LL);
case vmIntrinsics::_indexOfU: return inline_string_indexOf(StrIntrinsicNode::UU);
case vmIntrinsics::_indexOfUL: return inline_string_indexOf(StrIntrinsicNode::UL);
case vmIntrinsics::_indexOfIL: return inline_string_indexOfI(StrIntrinsicNode::LL);
case vmIntrinsics::_indexOfIU: return inline_string_indexOfI(StrIntrinsicNode::UU);
case vmIntrinsics::_indexOfIUL: return inline_string_indexOfI(StrIntrinsicNode::UL);
case vmIntrinsics::_indexOfU_char: return inline_string_indexOfChar(StrIntrinsicNode::U);
case vmIntrinsics::_indexOfL_char: return inline_string_indexOfChar(StrIntrinsicNode::L);
case vmIntrinsics::_equalsL: return inline_string_equals(StrIntrinsicNode::LL);
case vmIntrinsics::_equalsU: return inline_string_equals(StrIntrinsicNode::UU);
case vmIntrinsics::_toBytesStringU: return inline_string_toBytesU();
case vmIntrinsics::_getCharsStringU: return inline_string_getCharsU();
case vmIntrinsics::_getCharStringU: return inline_string_char_access(!is_store);
case vmIntrinsics::_putCharStringU: return inline_string_char_access( is_store);
case vmIntrinsics::_compressStringC:
case vmIntrinsics::_compressStringB: return inline_string_copy( is_compress);
case vmIntrinsics::_inflateStringC:
case vmIntrinsics::_inflateStringB: return inline_string_copy(!is_compress);
case vmIntrinsics::_getReference: return inline_unsafe_access(!is_store, T_OBJECT, Relaxed, false);
case vmIntrinsics::_getBoolean: return inline_unsafe_access(!is_store, T_BOOLEAN, Relaxed, false);
case vmIntrinsics::_getByte: return inline_unsafe_access(!is_store, T_BYTE, Relaxed, false);
case vmIntrinsics::_getShort: return inline_unsafe_access(!is_store, T_SHORT, Relaxed, false);
case vmIntrinsics::_getChar: return inline_unsafe_access(!is_store, T_CHAR, Relaxed, false);
case vmIntrinsics::_getInt: return inline_unsafe_access(!is_store, T_INT, Relaxed, false);
case vmIntrinsics::_getLong: return inline_unsafe_access(!is_store, T_LONG, Relaxed, false);
case vmIntrinsics::_getFloat: return inline_unsafe_access(!is_store, T_FLOAT, Relaxed, false);
case vmIntrinsics::_getDouble: return inline_unsafe_access(!is_store, T_DOUBLE, Relaxed, false);
case vmIntrinsics::_putReference: return inline_unsafe_access( is_store, T_OBJECT, Relaxed, false);
case vmIntrinsics::_putBoolean: return inline_unsafe_access( is_store, T_BOOLEAN, Relaxed, false);
case vmIntrinsics::_putByte: return inline_unsafe_access( is_store, T_BYTE, Relaxed, false);
case vmIntrinsics::_putShort: return inline_unsafe_access( is_store, T_SHORT, Relaxed, false);
case vmIntrinsics::_putChar: return inline_unsafe_access( is_store, T_CHAR, Relaxed, false);
case vmIntrinsics::_putInt: return inline_unsafe_access( is_store, T_INT, Relaxed, false);
case vmIntrinsics::_putLong: return inline_unsafe_access( is_store, T_LONG, Relaxed, false);
case vmIntrinsics::_putFloat: return inline_unsafe_access( is_store, T_FLOAT, Relaxed, false);
case vmIntrinsics::_putDouble: return inline_unsafe_access( is_store, T_DOUBLE, Relaxed, false);
case vmIntrinsics::_getReferenceVolatile: return inline_unsafe_access(!is_store, T_OBJECT, Volatile, false);
case vmIntrinsics::_getBooleanVolatile: return inline_unsafe_access(!is_store, T_BOOLEAN, Volatile, false);
case vmIntrinsics::_getByteVolatile: return inline_unsafe_access(!is_store, T_BYTE, Volatile, false);
case vmIntrinsics::_getShortVolatile: return inline_unsafe_access(!is_store, T_SHORT, Volatile, false);
case vmIntrinsics::_getCharVolatile: return inline_unsafe_access(!is_store, T_CHAR, Volatile, false);
case vmIntrinsics::_getIntVolatile: return inline_unsafe_access(!is_store, T_INT, Volatile, false);
case vmIntrinsics::_getLongVolatile: return inline_unsafe_access(!is_store, T_LONG, Volatile, false);
case vmIntrinsics::_getFloatVolatile: return inline_unsafe_access(!is_store, T_FLOAT, Volatile, false);
case vmIntrinsics::_getDoubleVolatile: return inline_unsafe_access(!is_store, T_DOUBLE, Volatile, false);
case vmIntrinsics::_putReferenceVolatile: return inline_unsafe_access( is_store, T_OBJECT, Volatile, false);
case vmIntrinsics::_putBooleanVolatile: return inline_unsafe_access( is_store, T_BOOLEAN, Volatile, false);
case vmIntrinsics::_putByteVolatile: return inline_unsafe_access( is_store, T_BYTE, Volatile, false);
case vmIntrinsics::_putShortVolatile: return inline_unsafe_access( is_store, T_SHORT, Volatile, false);
case vmIntrinsics::_putCharVolatile: return inline_unsafe_access( is_store, T_CHAR, Volatile, false);
case vmIntrinsics::_putIntVolatile: return inline_unsafe_access( is_store, T_INT, Volatile, false);
case vmIntrinsics::_putLongVolatile: return inline_unsafe_access( is_store, T_LONG, Volatile, false);
case vmIntrinsics::_putFloatVolatile: return inline_unsafe_access( is_store, T_FLOAT, Volatile, false);
case vmIntrinsics::_putDoubleVolatile: return inline_unsafe_access( is_store, T_DOUBLE, Volatile, false);
case vmIntrinsics::_getShortUnaligned: return inline_unsafe_access(!is_store, T_SHORT, Relaxed, true);
case vmIntrinsics::_getCharUnaligned: return inline_unsafe_access(!is_store, T_CHAR, Relaxed, true);
case vmIntrinsics::_getIntUnaligned: return inline_unsafe_access(!is_store, T_INT, Relaxed, true);
case vmIntrinsics::_getLongUnaligned: return inline_unsafe_access(!is_store, T_LONG, Relaxed, true);
case vmIntrinsics::_putShortUnaligned: return inline_unsafe_access( is_store, T_SHORT, Relaxed, true);
case vmIntrinsics::_putCharUnaligned: return inline_unsafe_access( is_store, T_CHAR, Relaxed, true);
case vmIntrinsics::_putIntUnaligned: return inline_unsafe_access( is_store, T_INT, Relaxed, true);
case vmIntrinsics::_putLongUnaligned: return inline_unsafe_access( is_store, T_LONG, Relaxed, true);
case vmIntrinsics::_getReferenceAcquire: return inline_unsafe_access(!is_store, T_OBJECT, Acquire, false);
case vmIntrinsics::_getBooleanAcquire: return inline_unsafe_access(!is_store, T_BOOLEAN, Acquire, false);
case vmIntrinsics::_getByteAcquire: return inline_unsafe_access(!is_store, T_BYTE, Acquire, false);
case vmIntrinsics::_getShortAcquire: return inline_unsafe_access(!is_store, T_SHORT, Acquire, false);
case vmIntrinsics::_getCharAcquire: return inline_unsafe_access(!is_store, T_CHAR, Acquire, false);
case vmIntrinsics::_getIntAcquire: return inline_unsafe_access(!is_store, T_INT, Acquire, false);
case vmIntrinsics::_getLongAcquire: return inline_unsafe_access(!is_store, T_LONG, Acquire, false);
case vmIntrinsics::_getFloatAcquire: return inline_unsafe_access(!is_store, T_FLOAT, Acquire, false);
case vmIntrinsics::_getDoubleAcquire: return inline_unsafe_access(!is_store, T_DOUBLE, Acquire, false);
case vmIntrinsics::_putReferenceRelease: return inline_unsafe_access( is_store, T_OBJECT, Release, false);
case vmIntrinsics::_putBooleanRelease: return inline_unsafe_access( is_store, T_BOOLEAN, Release, false);
case vmIntrinsics::_putByteRelease: return inline_unsafe_access( is_store, T_BYTE, Release, false);
case vmIntrinsics::_putShortRelease: return inline_unsafe_access( is_store, T_SHORT, Release, false);
case vmIntrinsics::_putCharRelease: return inline_unsafe_access( is_store, T_CHAR, Release, false);
case vmIntrinsics::_putIntRelease: return inline_unsafe_access( is_store, T_INT, Release, false);
case vmIntrinsics::_putLongRelease: return inline_unsafe_access( is_store, T_LONG, Release, false);
case vmIntrinsics::_putFloatRelease: return inline_unsafe_access( is_store, T_FLOAT, Release, false);
case vmIntrinsics::_putDoubleRelease: return inline_unsafe_access( is_store, T_DOUBLE, Release, false);
case vmIntrinsics::_getReferenceOpaque: return inline_unsafe_access(!is_store, T_OBJECT, Opaque, false);
case vmIntrinsics::_getBooleanOpaque: return inline_unsafe_access(!is_store, T_BOOLEAN, Opaque, false);
case vmIntrinsics::_getByteOpaque: return inline_unsafe_access(!is_store, T_BYTE, Opaque, false);
case vmIntrinsics::_getShortOpaque: return inline_unsafe_access(!is_store, T_SHORT, Opaque, false);
case vmIntrinsics::_getCharOpaque: return inline_unsafe_access(!is_store, T_CHAR, Opaque, false);
case vmIntrinsics::_getIntOpaque: return inline_unsafe_access(!is_store, T_INT, Opaque, false);
case vmIntrinsics::_getLongOpaque: return inline_unsafe_access(!is_store, T_LONG, Opaque, false);
case vmIntrinsics::_getFloatOpaque: return inline_unsafe_access(!is_store, T_FLOAT, Opaque, false);
case vmIntrinsics::_getDoubleOpaque: return inline_unsafe_access(!is_store, T_DOUBLE, Opaque, false);
case vmIntrinsics::_putReferenceOpaque: return inline_unsafe_access( is_store, T_OBJECT, Opaque, false);
case vmIntrinsics::_putBooleanOpaque: return inline_unsafe_access( is_store, T_BOOLEAN, Opaque, false);
case vmIntrinsics::_putByteOpaque: return inline_unsafe_access( is_store, T_BYTE, Opaque, false);
case vmIntrinsics::_putShortOpaque: return inline_unsafe_access( is_store, T_SHORT, Opaque, false);
case vmIntrinsics::_putCharOpaque: return inline_unsafe_access( is_store, T_CHAR, Opaque, false);
case vmIntrinsics::_putIntOpaque: return inline_unsafe_access( is_store, T_INT, Opaque, false);
case vmIntrinsics::_putLongOpaque: return inline_unsafe_access( is_store, T_LONG, Opaque, false);
case vmIntrinsics::_putFloatOpaque: return inline_unsafe_access( is_store, T_FLOAT, Opaque, false);
case vmIntrinsics::_putDoubleOpaque: return inline_unsafe_access( is_store, T_DOUBLE, Opaque, false);
case vmIntrinsics::_compareAndSetReference: return inline_unsafe_load_store(T_OBJECT, LS_cmp_swap, Volatile);
case vmIntrinsics::_compareAndSetByte: return inline_unsafe_load_store(T_BYTE, LS_cmp_swap, Volatile);
case vmIntrinsics::_compareAndSetShort: return inline_unsafe_load_store(T_SHORT, LS_cmp_swap, Volatile);
case vmIntrinsics::_compareAndSetInt: return inline_unsafe_load_store(T_INT, LS_cmp_swap, Volatile);
case vmIntrinsics::_compareAndSetLong: return inline_unsafe_load_store(T_LONG, LS_cmp_swap, Volatile);
case vmIntrinsics::_weakCompareAndSetReferencePlain: return inline_unsafe_load_store(T_OBJECT, LS_cmp_swap_weak, Relaxed);
case vmIntrinsics::_weakCompareAndSetReferenceAcquire: return inline_unsafe_load_store(T_OBJECT, LS_cmp_swap_weak, Acquire);
case vmIntrinsics::_weakCompareAndSetReferenceRelease: return inline_unsafe_load_store(T_OBJECT, LS_cmp_swap_weak, Release);
case vmIntrinsics::_weakCompareAndSetReference: return inline_unsafe_load_store(T_OBJECT, LS_cmp_swap_weak, Volatile);
case vmIntrinsics::_weakCompareAndSetBytePlain: return inline_unsafe_load_store(T_BYTE, LS_cmp_swap_weak, Relaxed);
case vmIntrinsics::_weakCompareAndSetByteAcquire: return inline_unsafe_load_store(T_BYTE, LS_cmp_swap_weak, Acquire);
case vmIntrinsics::_weakCompareAndSetByteRelease: return inline_unsafe_load_store(T_BYTE, LS_cmp_swap_weak, Release);
case vmIntrinsics::_weakCompareAndSetByte: return inline_unsafe_load_store(T_BYTE, LS_cmp_swap_weak, Volatile);
case vmIntrinsics::_weakCompareAndSetShortPlain: return inline_unsafe_load_store(T_SHORT, LS_cmp_swap_weak, Relaxed);
case vmIntrinsics::_weakCompareAndSetShortAcquire: return inline_unsafe_load_store(T_SHORT, LS_cmp_swap_weak, Acquire);
case vmIntrinsics::_weakCompareAndSetShortRelease: return inline_unsafe_load_store(T_SHORT, LS_cmp_swap_weak, Release);
case vmIntrinsics::_weakCompareAndSetShort: return inline_unsafe_load_store(T_SHORT, LS_cmp_swap_weak, Volatile);
case vmIntrinsics::_weakCompareAndSetIntPlain: return inline_unsafe_load_store(T_INT, LS_cmp_swap_weak, Relaxed);
case vmIntrinsics::_weakCompareAndSetIntAcquire: return inline_unsafe_load_store(T_INT, LS_cmp_swap_weak, Acquire);
case vmIntrinsics::_weakCompareAndSetIntRelease: return inline_unsafe_load_store(T_INT, LS_cmp_swap_weak, Release);
case vmIntrinsics::_weakCompareAndSetInt: return inline_unsafe_load_store(T_INT, LS_cmp_swap_weak, Volatile);
case vmIntrinsics::_weakCompareAndSetLongPlain: return inline_unsafe_load_store(T_LONG, LS_cmp_swap_weak, Relaxed);
case vmIntrinsics::_weakCompareAndSetLongAcquire: return inline_unsafe_load_store(T_LONG, LS_cmp_swap_weak, Acquire);
case vmIntrinsics::_weakCompareAndSetLongRelease: return inline_unsafe_load_store(T_LONG, LS_cmp_swap_weak, Release);
case vmIntrinsics::_weakCompareAndSetLong: return inline_unsafe_load_store(T_LONG, LS_cmp_swap_weak, Volatile);
case vmIntrinsics::_compareAndExchangeReference: return inline_unsafe_load_store(T_OBJECT, LS_cmp_exchange, Volatile);
case vmIntrinsics::_compareAndExchangeReferenceAcquire: return inline_unsafe_load_store(T_OBJECT, LS_cmp_exchange, Acquire);
case vmIntrinsics::_compareAndExchangeReferenceRelease: return inline_unsafe_load_store(T_OBJECT, LS_cmp_exchange, Release);
case vmIntrinsics::_compareAndExchangeByte: return inline_unsafe_load_store(T_BYTE, LS_cmp_exchange, Volatile);
case vmIntrinsics::_compareAndExchangeByteAcquire: return inline_unsafe_load_store(T_BYTE, LS_cmp_exchange, Acquire);
case vmIntrinsics::_compareAndExchangeByteRelease: return inline_unsafe_load_store(T_BYTE, LS_cmp_exchange, Release);
case vmIntrinsics::_compareAndExchangeShort: return inline_unsafe_load_store(T_SHORT, LS_cmp_exchange, Volatile);
case vmIntrinsics::_compareAndExchangeShortAcquire: return inline_unsafe_load_store(T_SHORT, LS_cmp_exchange, Acquire);
case vmIntrinsics::_compareAndExchangeShortRelease: return inline_unsafe_load_store(T_SHORT, LS_cmp_exchange, Release);
case vmIntrinsics::_compareAndExchangeInt: return inline_unsafe_load_store(T_INT, LS_cmp_exchange, Volatile);
case vmIntrinsics::_compareAndExchangeIntAcquire: return inline_unsafe_load_store(T_INT, LS_cmp_exchange, Acquire);
case vmIntrinsics::_compareAndExchangeIntRelease: return inline_unsafe_load_store(T_INT, LS_cmp_exchange, Release);
case vmIntrinsics::_compareAndExchangeLong: return inline_unsafe_load_store(T_LONG, LS_cmp_exchange, Volatile);
case vmIntrinsics::_compareAndExchangeLongAcquire: return inline_unsafe_load_store(T_LONG, LS_cmp_exchange, Acquire);
case vmIntrinsics::_compareAndExchangeLongRelease: return inline_unsafe_load_store(T_LONG, LS_cmp_exchange, Release);
case vmIntrinsics::_getAndAddByte: return inline_unsafe_load_store(T_BYTE, LS_get_add, Volatile);
case vmIntrinsics::_getAndAddShort: return inline_unsafe_load_store(T_SHORT, LS_get_add, Volatile);
case vmIntrinsics::_getAndAddInt: return inline_unsafe_load_store(T_INT, LS_get_add, Volatile);
case vmIntrinsics::_getAndAddLong: return inline_unsafe_load_store(T_LONG, LS_get_add, Volatile);
case vmIntrinsics::_getAndSetByte: return inline_unsafe_load_store(T_BYTE, LS_get_set, Volatile);
case vmIntrinsics::_getAndSetShort: return inline_unsafe_load_store(T_SHORT, LS_get_set, Volatile);
case vmIntrinsics::_getAndSetInt: return inline_unsafe_load_store(T_INT, LS_get_set, Volatile);
case vmIntrinsics::_getAndSetLong: return inline_unsafe_load_store(T_LONG, LS_get_set, Volatile);
case vmIntrinsics::_getAndSetReference: return inline_unsafe_load_store(T_OBJECT, LS_get_set, Volatile);
case vmIntrinsics::_loadFence:
case vmIntrinsics::_storeFence:
case vmIntrinsics::_storeStoreFence:
case vmIntrinsics::_fullFence: return inline_unsafe_fence(intrinsic_id());
case vmIntrinsics::_onSpinWait: return inline_onspinwait();
case vmIntrinsics::_currentCarrierThread: return inline_native_currentCarrierThread();
case vmIntrinsics::_currentThread: return inline_native_currentThread();
case vmIntrinsics::_setCurrentThread: return inline_native_setCurrentThread();
case vmIntrinsics::_scopedValueCache: return inline_native_scopedValueCache();
case vmIntrinsics::_setScopedValueCache: return inline_native_setScopedValueCache();
#ifdef JFR_HAVE_INTRINSICS
case vmIntrinsics::_counterTime: return inline_native_time_funcs(CAST_FROM_FN_PTR(address, JfrTime::time_function()), "counterTime");
case vmIntrinsics::_getEventWriter: return inline_native_getEventWriter();
#endif
case vmIntrinsics::_currentTimeMillis: return inline_native_time_funcs(CAST_FROM_FN_PTR(address, os::javaTimeMillis), "currentTimeMillis");
case vmIntrinsics::_nanoTime: return inline_native_time_funcs(CAST_FROM_FN_PTR(address, os::javaTimeNanos), "nanoTime");
case vmIntrinsics::_writeback0: return inline_unsafe_writeback0();
case vmIntrinsics::_writebackPreSync0: return inline_unsafe_writebackSync0(true);
case vmIntrinsics::_writebackPostSync0: return inline_unsafe_writebackSync0(false);
case vmIntrinsics::_allocateInstance: return inline_unsafe_allocate();
case vmIntrinsics::_copyMemory: return inline_unsafe_copyMemory();
case vmIntrinsics::_getLength: return inline_native_getLength();
case vmIntrinsics::_copyOf: return inline_array_copyOf(false);
case vmIntrinsics::_copyOfRange: return inline_array_copyOf(true);
case vmIntrinsics::_equalsB: return inline_array_equals(StrIntrinsicNode::LL);
case vmIntrinsics::_equalsC: return inline_array_equals(StrIntrinsicNode::UU);
case vmIntrinsics::_Preconditions_checkIndex: return inline_preconditions_checkIndex(T_INT);
case vmIntrinsics::_Preconditions_checkLongIndex: return inline_preconditions_checkIndex(T_LONG);
case vmIntrinsics::_clone: return inline_native_clone(intrinsic()->is_virtual());
case vmIntrinsics::_allocateUninitializedArray: return inline_unsafe_newArray(true);
case vmIntrinsics::_newArray: return inline_unsafe_newArray(false);
case vmIntrinsics::_isAssignableFrom: return inline_native_subtype_check();
case vmIntrinsics::_isInstance:
case vmIntrinsics::_getModifiers:
case vmIntrinsics::_isInterface:
case vmIntrinsics::_isArray:
case vmIntrinsics::_isPrimitive:
case vmIntrinsics::_isHidden:
case vmIntrinsics::_getSuperclass:
case vmIntrinsics::_getClassAccessFlags: return inline_native_Class_query(intrinsic_id());
case vmIntrinsics::_floatToRawIntBits:
case vmIntrinsics::_floatToIntBits:
case vmIntrinsics::_intBitsToFloat:
case vmIntrinsics::_doubleToRawLongBits:
case vmIntrinsics::_doubleToLongBits:
case vmIntrinsics::_longBitsToDouble:
case vmIntrinsics::_floatToFloat16:
case vmIntrinsics::_float16ToFloat: return inline_fp_conversions(intrinsic_id());
case vmIntrinsics::_floatIsFinite:
case vmIntrinsics::_floatIsInfinite:
case vmIntrinsics::_doubleIsFinite:
case vmIntrinsics::_doubleIsInfinite: return inline_fp_range_check(intrinsic_id());
case vmIntrinsics::_numberOfLeadingZeros_i:
case vmIntrinsics::_numberOfLeadingZeros_l:
case vmIntrinsics::_numberOfTrailingZeros_i:
case vmIntrinsics::_numberOfTrailingZeros_l:
case vmIntrinsics::_bitCount_i:
case vmIntrinsics::_bitCount_l:
case vmIntrinsics::_reverse_i:
case vmIntrinsics::_reverse_l:
case vmIntrinsics::_reverseBytes_i:
case vmIntrinsics::_reverseBytes_l:
case vmIntrinsics::_reverseBytes_s:
case vmIntrinsics::_reverseBytes_c: return inline_number_methods(intrinsic_id());
case vmIntrinsics::_compress_i:
case vmIntrinsics::_compress_l:
case vmIntrinsics::_expand_i:
case vmIntrinsics::_expand_l: return inline_bitshuffle_methods(intrinsic_id());
case vmIntrinsics::_compareUnsigned_i:
case vmIntrinsics::_compareUnsigned_l: return inline_compare_unsigned(intrinsic_id());
case vmIntrinsics::_divideUnsigned_i:
case vmIntrinsics::_divideUnsigned_l:
case vmIntrinsics::_remainderUnsigned_i:
case vmIntrinsics::_remainderUnsigned_l: return inline_divmod_methods(intrinsic_id());
case vmIntrinsics::_getCallerClass: return inline_native_Reflection_getCallerClass();
case vmIntrinsics::_Reference_get: return inline_reference_get();
case vmIntrinsics::_Reference_refersTo0: return inline_reference_refersTo0(false);
case vmIntrinsics::_PhantomReference_refersTo0: return inline_reference_refersTo0(true);
case vmIntrinsics::_Class_cast: return inline_Class_cast();
case vmIntrinsics::_aescrypt_encryptBlock:
case vmIntrinsics::_aescrypt_decryptBlock: return inline_aescrypt_Block(intrinsic_id());
case vmIntrinsics::_cipherBlockChaining_encryptAESCrypt:
case vmIntrinsics::_cipherBlockChaining_decryptAESCrypt:
return inline_cipherBlockChaining_AESCrypt(intrinsic_id());
case vmIntrinsics::_electronicCodeBook_encryptAESCrypt:
case vmIntrinsics::_electronicCodeBook_decryptAESCrypt:
return inline_electronicCodeBook_AESCrypt(intrinsic_id());
case vmIntrinsics::_counterMode_AESCrypt:
return inline_counterMode_AESCrypt(intrinsic_id());
case vmIntrinsics::_galoisCounterMode_AESCrypt:
return inline_galoisCounterMode_AESCrypt();
case vmIntrinsics::_md5_implCompress:
case vmIntrinsics::_sha_implCompress:
case vmIntrinsics::_sha2_implCompress:
case vmIntrinsics::_sha5_implCompress:
case vmIntrinsics::_sha3_implCompress:
return inline_digestBase_implCompress(intrinsic_id());
case vmIntrinsics::_digestBase_implCompressMB:
return inline_digestBase_implCompressMB(predicate);
case vmIntrinsics::_multiplyToLen:
return inline_multiplyToLen();
case vmIntrinsics::_squareToLen:
return inline_squareToLen();
case vmIntrinsics::_mulAdd:
return inline_mulAdd();
case vmIntrinsics::_montgomeryMultiply:
return inline_montgomeryMultiply();
case vmIntrinsics::_montgomerySquare:
return inline_montgomerySquare();
case vmIntrinsics::_bigIntegerRightShiftWorker:
return inline_bigIntegerShift(true);
case vmIntrinsics::_bigIntegerLeftShiftWorker:
return inline_bigIntegerShift(false);
case vmIntrinsics::_vectorizedMismatch:
return inline_vectorizedMismatch();
case vmIntrinsics::_ghash_processBlocks:
return inline_ghash_processBlocks();
case vmIntrinsics::_chacha20Block:
return inline_chacha20Block();
case vmIntrinsics::_base64_encodeBlock:
return inline_base64_encodeBlock();
case vmIntrinsics::_base64_decodeBlock:
return inline_base64_decodeBlock();
case vmIntrinsics::_poly1305_processBlocks:
return inline_poly1305_processBlocks();
case vmIntrinsics::_encodeISOArray:
case vmIntrinsics::_encodeByteISOArray:
return inline_encodeISOArray(false);
case vmIntrinsics::_encodeAsciiArray:
return inline_encodeISOArray(true);
case vmIntrinsics::_updateCRC32:
return inline_updateCRC32();
case vmIntrinsics::_updateBytesCRC32:
return inline_updateBytesCRC32();
case vmIntrinsics::_updateByteBufferCRC32:
return inline_updateByteBufferCRC32();
case vmIntrinsics::_updateBytesCRC32C:
return inline_updateBytesCRC32C();
case vmIntrinsics::_updateDirectByteBufferCRC32C:
return inline_updateDirectByteBufferCRC32C();
case vmIntrinsics::_updateBytesAdler32:
return inline_updateBytesAdler32();
case vmIntrinsics::_updateByteBufferAdler32:
return inline_updateByteBufferAdler32();
case vmIntrinsics::_profileBoolean:
return inline_profileBoolean();
case vmIntrinsics::_isCompileConstant:
return inline_isCompileConstant();
case vmIntrinsics::_countPositives:
return inline_countPositives();
case vmIntrinsics::_fmaD:
case vmIntrinsics::_fmaF:
return inline_fma(intrinsic_id());
case vmIntrinsics::_isDigit:
case vmIntrinsics::_isLowerCase:
case vmIntrinsics::_isUpperCase:
case vmIntrinsics::_isWhitespace:
return inline_character_compare(intrinsic_id());
case vmIntrinsics::_min:
case vmIntrinsics::_max:
case vmIntrinsics::_min_strict:
case vmIntrinsics::_max_strict:
return inline_min_max(intrinsic_id());
case vmIntrinsics::_maxF:
case vmIntrinsics::_minF:
case vmIntrinsics::_maxD:
case vmIntrinsics::_minD:
case vmIntrinsics::_maxF_strict:
case vmIntrinsics::_minF_strict:
case vmIntrinsics::_maxD_strict:
case vmIntrinsics::_minD_strict:
return inline_fp_min_max(intrinsic_id());
case vmIntrinsics::_VectorUnaryOp:
return inline_vector_nary_operation(1);
case vmIntrinsics::_VectorBinaryOp:
return inline_vector_nary_operation(2);
case vmIntrinsics::_VectorTernaryOp:
return inline_vector_nary_operation(3);
case vmIntrinsics::_VectorFromBitsCoerced:
return inline_vector_frombits_coerced();
case vmIntrinsics::_VectorShuffleIota:
return inline_vector_shuffle_iota();
case vmIntrinsics::_VectorMaskOp:
return inline_vector_mask_operation();
case vmIntrinsics::_VectorShuffleToVector:
return inline_vector_shuffle_to_vector();
case vmIntrinsics::_VectorLoadOp:
return inline_vector_mem_operation(/*is_store=*/false);
case vmIntrinsics::_VectorLoadMaskedOp:
return inline_vector_mem_masked_operation(/*is_store*/false);
case vmIntrinsics::_VectorStoreOp:
return inline_vector_mem_operation(/*is_store=*/true);
case vmIntrinsics::_VectorStoreMaskedOp:
return inline_vector_mem_masked_operation(/*is_store=*/true);
case vmIntrinsics::_VectorGatherOp:
return inline_vector_gather_scatter(/*is_scatter*/ false);
case vmIntrinsics::_VectorScatterOp:
return inline_vector_gather_scatter(/*is_scatter*/ true);
case vmIntrinsics::_VectorReductionCoerced:
return inline_vector_reduction();
case vmIntrinsics::_VectorTest:
return inline_vector_test();
case vmIntrinsics::_VectorBlend:
return inline_vector_blend();
case vmIntrinsics::_VectorRearrange:
return inline_vector_rearrange();
case vmIntrinsics::_VectorCompare:
return inline_vector_compare();
case vmIntrinsics::_VectorBroadcastInt:
return inline_vector_broadcast_int();
case vmIntrinsics::_VectorConvert:
return inline_vector_convert();
case vmIntrinsics::_VectorInsert:
return inline_vector_insert();
case vmIntrinsics::_VectorExtract:
return inline_vector_extract();
case vmIntrinsics::_VectorCompressExpand:
return inline_vector_compress_expand();
case vmIntrinsics::_IndexVector:
return inline_index_vector();
case vmIntrinsics::_getObjectSize:
return inline_getObjectSize();
case vmIntrinsics::_blackhole:
return inline_blackhole();
default:
// If you get here, it may be that someone has added a new intrinsic
// to the list in vmIntrinsics.hpp without implementing it here.
#ifndef PRODUCT
if ((PrintMiscellaneous && (Verbose || WizardMode)) || PrintOpto) {
tty->print_cr("*** Warning: Unimplemented intrinsic %s(%d)",
vmIntrinsics::name_at(intrinsic_id()), vmIntrinsics::as_int(intrinsic_id()));
}
#endif
return false;
}
}
Node* LibraryCallKit::try_to_predicate(int predicate) {
if (!jvms()->has_method()) {
// Root JVMState has a null method.
assert(map()->memory()->Opcode() == Op_Parm, "");
// Insert the memory aliasing node
set_all_memory(reset_memory());
}
assert(merged_memory(), "");
switch (intrinsic_id()) {
case vmIntrinsics::_cipherBlockChaining_encryptAESCrypt:
return inline_cipherBlockChaining_AESCrypt_predicate(false);
case vmIntrinsics::_cipherBlockChaining_decryptAESCrypt:
return inline_cipherBlockChaining_AESCrypt_predicate(true);
case vmIntrinsics::_electronicCodeBook_encryptAESCrypt:
return inline_electronicCodeBook_AESCrypt_predicate(false);
case vmIntrinsics::_electronicCodeBook_decryptAESCrypt:
return inline_electronicCodeBook_AESCrypt_predicate(true);
case vmIntrinsics::_counterMode_AESCrypt:
return inline_counterMode_AESCrypt_predicate();
case vmIntrinsics::_digestBase_implCompressMB:
return inline_digestBase_implCompressMB_predicate(predicate);
case vmIntrinsics::_galoisCounterMode_AESCrypt:
return inline_galoisCounterMode_AESCrypt_predicate();
default:
// If you get here, it may be that someone has added a new intrinsic
// to the list in vmIntrinsics.hpp without implementing it here.
#ifndef PRODUCT
if ((PrintMiscellaneous && (Verbose || WizardMode)) || PrintOpto) {
tty->print_cr("*** Warning: Unimplemented predicate for intrinsic %s(%d)",
vmIntrinsics::name_at(intrinsic_id()), vmIntrinsics::as_int(intrinsic_id()));
}
#endif
Node* slow_ctl = control();
set_control(top()); // No fast path intrinsic
return slow_ctl;
}
}
//------------------------------set_result-------------------------------
// Helper function for finishing intrinsics.
void LibraryCallKit::set_result(RegionNode* region, PhiNode* value) {
record_for_igvn(region);
set_control(_gvn.transform(region));
set_result( _gvn.transform(value));
assert(value->type()->basic_type() == result()->bottom_type()->basic_type(), "sanity");
}
//------------------------------generate_guard---------------------------
// Helper function for generating guarded fast-slow graph structures.
// The given 'test', if true, guards a slow path. If the test fails
// then a fast path can be taken. (We generally hope it fails.)
// In all cases, GraphKit::control() is updated to the fast path.
// The returned value represents the control for the slow path.
// The return value is never 'top'; it is either a valid control
// or NULL if it is obvious that the slow path can never be taken.
// Also, if region and the slow control are not NULL, the slow edge
// is appended to the region.
Node* LibraryCallKit::generate_guard(Node* test, RegionNode* region, float true_prob) {
if (stopped()) {
// Already short circuited.
return NULL;
}
// Build an if node and its projections.
// If test is true we take the slow path, which we assume is uncommon.
if (_gvn.type(test) == TypeInt::ZERO) {
// The slow branch is never taken. No need to build this guard.
return NULL;
}
IfNode* iff = create_and_map_if(control(), test, true_prob, COUNT_UNKNOWN);
Node* if_slow = _gvn.transform(new IfTrueNode(iff));
if (if_slow == top()) {
// The slow branch is never taken. No need to build this guard.
return NULL;
}
if (region != NULL)
region->add_req(if_slow);
Node* if_fast = _gvn.transform(new IfFalseNode(iff));
set_control(if_fast);
return if_slow;
}
inline Node* LibraryCallKit::generate_slow_guard(Node* test, RegionNode* region) {
return generate_guard(test, region, PROB_UNLIKELY_MAG(3));
}
inline Node* LibraryCallKit::generate_fair_guard(Node* test, RegionNode* region) {
return generate_guard(test, region, PROB_FAIR);
}
inline Node* LibraryCallKit::generate_negative_guard(Node* index, RegionNode* region,
Node* *pos_index) {
if (stopped())
return NULL; // already stopped
if (_gvn.type(index)->higher_equal(TypeInt::POS)) // [0,maxint]
return NULL; // index is already adequately typed
Node* cmp_lt = _gvn.transform(new CmpINode(index, intcon(0)));
Node* bol_lt = _gvn.transform(new BoolNode(cmp_lt, BoolTest::lt));
Node* is_neg = generate_guard(bol_lt, region, PROB_MIN);
if (is_neg != NULL && pos_index != NULL) {
// Emulate effect of Parse::adjust_map_after_if.
Node* ccast = new CastIINode(index, TypeInt::POS);
ccast->set_req(0, control());
(*pos_index) = _gvn.transform(ccast);
}
return is_neg;
}
// Make sure that 'position' is a valid limit index, in [0..length].
// There are two equivalent plans for checking this:
// A. (offset + copyLength) unsigned<= arrayLength
// B. offset <= (arrayLength - copyLength)
// We require that all of the values above, except for the sum and
// difference, are already known to be non-negative.
// Plan A is robust in the face of overflow, if offset and copyLength
// are both hugely positive.
//
// Plan B is less direct and intuitive, but it does not overflow at
// all, since the difference of two non-negatives is always
// representable. Whenever Java methods must perform the equivalent
// check they generally use Plan B instead of Plan A.
// For the moment we use Plan A.
inline Node* LibraryCallKit::generate_limit_guard(Node* offset,
Node* subseq_length,
Node* array_length,
RegionNode* region) {
if (stopped())
return NULL; // already stopped
bool zero_offset = _gvn.type(offset) == TypeInt::ZERO;
if (zero_offset && subseq_length->eqv_uncast(array_length))
return NULL; // common case of whole-array copy
Node* last = subseq_length;
if (!zero_offset) // last += offset
last = _gvn.transform(new AddINode(last, offset));
Node* cmp_lt = _gvn.transform(new CmpUNode(array_length, last));
Node* bol_lt = _gvn.transform(new BoolNode(cmp_lt, BoolTest::lt));
Node* is_over = generate_guard(bol_lt, region, PROB_MIN);
return is_over;
}
// Emit range checks for the given String.value byte array
void LibraryCallKit::generate_string_range_check(Node* array, Node* offset, Node* count, bool char_count) {
if (stopped()) {
return; // already stopped
}
RegionNode* bailout = new RegionNode(1);
record_for_igvn(bailout);
if (char_count) {
// Convert char count to byte count
count = _gvn.transform(new LShiftINode(count, intcon(1)));
}
// Offset and count must not be negative
generate_negative_guard(offset, bailout);
generate_negative_guard(count, bailout);
// Offset + count must not exceed length of array
generate_limit_guard(offset, count, load_array_length(array), bailout);
if (bailout->req() > 1) {
PreserveJVMState pjvms(this);
set_control(_gvn.transform(bailout));
uncommon_trap(Deoptimization::Reason_intrinsic,
Deoptimization::Action_maybe_recompile);
}
}
Node* LibraryCallKit::current_thread_helper(Node*& tls_output, ByteSize handle_offset,
bool is_immutable) {
ciKlass* thread_klass = env()->Thread_klass();
const Type* thread_type
= TypeOopPtr::make_from_klass(thread_klass)->cast_to_ptr_type(TypePtr::NotNull);
Node* thread = _gvn.transform(new ThreadLocalNode());
Node* p = basic_plus_adr(top()/*!oop*/, thread, in_bytes(handle_offset));
tls_output = thread;
Node* thread_obj_handle
= (is_immutable
? LoadNode::make(_gvn, NULL, immutable_memory(), p, p->bottom_type()->is_ptr(),
TypeRawPtr::NOTNULL, T_ADDRESS, MemNode::unordered)
: make_load(NULL, p, p->bottom_type()->is_ptr(), T_ADDRESS, MemNode::unordered));
thread_obj_handle = _gvn.transform(thread_obj_handle);
DecoratorSet decorators = IN_NATIVE;
if (is_immutable) {
decorators |= C2_IMMUTABLE_MEMORY;
}
return access_load(thread_obj_handle, thread_type, T_OBJECT, decorators);
}
//--------------------------generate_current_thread--------------------
Node* LibraryCallKit::generate_current_thread(Node* &tls_output) {
return current_thread_helper(tls_output, JavaThread::threadObj_offset(),
/*is_immutable*/false);
}
//--------------------------generate_virtual_thread--------------------
Node* LibraryCallKit::generate_virtual_thread(Node* tls_output) {
return current_thread_helper(tls_output, JavaThread::vthread_offset(),
!C->method()->changes_current_thread());
}
//------------------------------make_string_method_node------------------------
// Helper method for String intrinsic functions. This version is called with
// str1 and str2 pointing to byte[] nodes containing Latin1 or UTF16 encoded
// characters (depending on 'is_byte'). cnt1 and cnt2 are pointing to Int nodes
// containing the lengths of str1 and str2.
Node* LibraryCallKit::make_string_method_node(int opcode, Node* str1_start, Node* cnt1, Node* str2_start, Node* cnt2, StrIntrinsicNode::ArgEnc ae) {
Node* result = NULL;
switch (opcode) {
case Op_StrIndexOf:
result = new StrIndexOfNode(control(), memory(TypeAryPtr::BYTES),
str1_start, cnt1, str2_start, cnt2, ae);
break;
case Op_StrComp:
result = new StrCompNode(control(), memory(TypeAryPtr::BYTES),
str1_start, cnt1, str2_start, cnt2, ae);
break;
case Op_StrEquals:
// We already know that cnt1 == cnt2 here (checked in 'inline_string_equals').
// Use the constant length if there is one because optimized match rule may exist.
result = new StrEqualsNode(control(), memory(TypeAryPtr::BYTES),
str1_start, str2_start, cnt2->is_Con() ? cnt2 : cnt1, ae);
break;
default:
ShouldNotReachHere();
return NULL;
}
// All these intrinsics have checks.
C->set_has_split_ifs(true); // Has chance for split-if optimization
clear_upper_avx();
return _gvn.transform(result);
}
//------------------------------inline_string_compareTo------------------------
bool LibraryCallKit::inline_string_compareTo(StrIntrinsicNode::ArgEnc ae) {
Node* arg1 = argument(0);
Node* arg2 = argument(1);
arg1 = must_be_not_null(arg1, true);
arg2 = must_be_not_null(arg2, true);
// Get start addr and length of first argument
Node* arg1_start = array_element_address(arg1, intcon(0), T_BYTE);
Node* arg1_cnt = load_array_length(arg1);
// Get start addr and length of second argument
Node* arg2_start = array_element_address(arg2, intcon(0), T_BYTE);
Node* arg2_cnt = load_array_length(arg2);
Node* result = make_string_method_node(Op_StrComp, arg1_start, arg1_cnt, arg2_start, arg2_cnt, ae);
set_result(result);
return true;
}
//------------------------------inline_string_equals------------------------
bool LibraryCallKit::inline_string_equals(StrIntrinsicNode::ArgEnc ae) {
Node* arg1 = argument(0);
Node* arg2 = argument(1);
// paths (plus control) merge
RegionNode* region = new RegionNode(3);
Node* phi = new PhiNode(region, TypeInt::BOOL);
if (!stopped()) {
arg1 = must_be_not_null(arg1, true);
arg2 = must_be_not_null(arg2, true);
// Get start addr and length of first argument
Node* arg1_start = array_element_address(arg1, intcon(0), T_BYTE);
Node* arg1_cnt = load_array_length(arg1);
// Get start addr and length of second argument
Node* arg2_start = array_element_address(arg2, intcon(0), T_BYTE);
Node* arg2_cnt = load_array_length(arg2);
// Check for arg1_cnt != arg2_cnt
Node* cmp = _gvn.transform(new CmpINode(arg1_cnt, arg2_cnt));
Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::ne));
Node* if_ne = generate_slow_guard(bol, NULL);
if (if_ne != NULL) {
phi->init_req(2, intcon(0));
region->init_req(2, if_ne);
}
// Check for count == 0 is done by assembler code for StrEquals.
if (!stopped()) {
Node* equals = make_string_method_node(Op_StrEquals, arg1_start, arg1_cnt, arg2_start, arg2_cnt, ae);
phi->init_req(1, equals);
region->init_req(1, control());
}
}
// post merge
set_control(_gvn.transform(region));
record_for_igvn(region);
set_result(_gvn.transform(phi));
return true;
}
//------------------------------inline_array_equals----------------------------
bool LibraryCallKit::inline_array_equals(StrIntrinsicNode::ArgEnc ae) {
assert(ae == StrIntrinsicNode::UU || ae == StrIntrinsicNode::LL, "unsupported array types");
Node* arg1 = argument(0);
Node* arg2 = argument(1);
const TypeAryPtr* mtype = (ae == StrIntrinsicNode::UU) ? TypeAryPtr::CHARS : TypeAryPtr::BYTES;
set_result(_gvn.transform(new AryEqNode(control(), memory(mtype), arg1, arg2, ae)));
clear_upper_avx();
return true;
}
//------------------------------inline_countPositives------------------------------
bool LibraryCallKit::inline_countPositives() {
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
return false;
}
assert(callee()->signature()->size() == 3, "countPositives has 3 parameters");
// no receiver since it is static method
Node* ba = argument(0);
Node* offset = argument(1);
Node* len = argument(2);
ba = must_be_not_null(ba, true);
// Range checks
generate_string_range_check(ba, offset, len, false);
if (stopped()) {
return true;
}
Node* ba_start = array_element_address(ba, offset, T_BYTE);
Node* result = new CountPositivesNode(control(), memory(TypeAryPtr::BYTES), ba_start, len);
set_result(_gvn.transform(result));
return true;
}
bool LibraryCallKit::inline_preconditions_checkIndex(BasicType bt) {
Node* index = argument(0);
Node* length = bt == T_INT ? argument(1) : argument(2);
if (too_many_traps(Deoptimization::Reason_intrinsic) || too_many_traps(Deoptimization::Reason_range_check)) {
return false;
}
// check that length is positive
Node* len_pos_cmp = _gvn.transform(CmpNode::make(length, integercon(0, bt), bt));
Node* len_pos_bol = _gvn.transform(new BoolNode(len_pos_cmp, BoolTest::ge));
{
BuildCutout unless(this, len_pos_bol, PROB_MAX);
uncommon_trap(Deoptimization::Reason_intrinsic,
Deoptimization::Action_make_not_entrant);
}
if (stopped()) {
// Length is known to be always negative during compilation and the IR graph so far constructed is good so return success
return true;
}
// length is now known positive, add a cast node to make this explicit
jlong upper_bound = _gvn.type(length)->is_integer(bt)->hi_as_long();
Node* casted_length = ConstraintCastNode::make(control(), length, TypeInteger::make(0, upper_bound, Type::WidenMax, bt), ConstraintCastNode::RegularDependency, bt);
casted_length = _gvn.transform(casted_length);
replace_in_map(length, casted_length);
length = casted_length;
// Use an unsigned comparison for the range check itself
Node* rc_cmp = _gvn.transform(CmpNode::make(index, length, bt, true));
BoolTest::mask btest = BoolTest::lt;
Node* rc_bool = _gvn.transform(new BoolNode(rc_cmp, btest));
RangeCheckNode* rc = new RangeCheckNode(control(), rc_bool, PROB_MAX, COUNT_UNKNOWN);
_gvn.set_type(rc, rc->Value(&_gvn));
if (!rc_bool->is_Con()) {
record_for_igvn(rc);
}
set_control(_gvn.transform(new IfTrueNode(rc)));
{
PreserveJVMState pjvms(this);
set_control(_gvn.transform(new IfFalseNode(rc)));
uncommon_trap(Deoptimization::Reason_range_check,
Deoptimization::Action_make_not_entrant);
}
if (stopped()) {
// Range check is known to always fail during compilation and the IR graph so far constructed is good so return success
return true;
}
// index is now known to be >= 0 and < length, cast it
Node* result = ConstraintCastNode::make(control(), index, TypeInteger::make(0, upper_bound, Type::WidenMax, bt), ConstraintCastNode::RegularDependency, bt);
result = _gvn.transform(result);
set_result(result);
replace_in_map(index, result);
return true;
}
//------------------------------inline_string_indexOf------------------------
bool LibraryCallKit::inline_string_indexOf(StrIntrinsicNode::ArgEnc ae) {
if (!Matcher::match_rule_supported(Op_StrIndexOf)) {
return false;
}
Node* src = argument(0);
Node* tgt = argument(1);
// Make the merge point
RegionNode* result_rgn = new RegionNode(4);
Node* result_phi = new PhiNode(result_rgn, TypeInt::INT);
src = must_be_not_null(src, true);
tgt = must_be_not_null(tgt, true);
// Get start addr and length of source string
Node* src_start = array_element_address(src, intcon(0), T_BYTE);
Node* src_count = load_array_length(src);
// Get start addr and length of substring
Node* tgt_start = array_element_address(tgt, intcon(0), T_BYTE);
Node* tgt_count = load_array_length(tgt);
if (ae == StrIntrinsicNode::UU || ae == StrIntrinsicNode::UL) {
// Divide src size by 2 if String is UTF16 encoded
src_count = _gvn.transform(new RShiftINode(src_count, intcon(1)));
}
if (ae == StrIntrinsicNode::UU) {
// Divide substring size by 2 if String is UTF16 encoded
tgt_count = _gvn.transform(new RShiftINode(tgt_count, intcon(1)));
}
Node* result = make_indexOf_node(src_start, src_count, tgt_start, tgt_count, result_rgn, result_phi, ae);
if (result != NULL) {
result_phi->init_req(3, result);
result_rgn->init_req(3, control());
}
set_control(_gvn.transform(result_rgn));
record_for_igvn(result_rgn);
set_result(_gvn.transform(result_phi));
return true;
}
//-----------------------------inline_string_indexOf-----------------------
bool LibraryCallKit::inline_string_indexOfI(StrIntrinsicNode::ArgEnc ae) {
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
return false;
}
if (!Matcher::match_rule_supported(Op_StrIndexOf)) {
return false;
}
assert(callee()->signature()->size() == 5, "String.indexOf() has 5 arguments");
Node* src = argument(0); // byte[]
Node* src_count = argument(1); // char count
Node* tgt = argument(2); // byte[]
Node* tgt_count = argument(3); // char count
Node* from_index = argument(4); // char index
src = must_be_not_null(src, true);
tgt = must_be_not_null(tgt, true);
// Multiply byte array index by 2 if String is UTF16 encoded
Node* src_offset = (ae == StrIntrinsicNode::LL) ? from_index : _gvn.transform(new LShiftINode(from_index, intcon(1)));
src_count = _gvn.transform(new SubINode(src_count, from_index));
Node* src_start = array_element_address(src, src_offset, T_BYTE);
Node* tgt_start = array_element_address(tgt, intcon(0), T_BYTE);
// Range checks
generate_string_range_check(src, src_offset, src_count, ae != StrIntrinsicNode::LL);
generate_string_range_check(tgt, intcon(0), tgt_count, ae == StrIntrinsicNode::UU);
if (stopped()) {
return true;
}
RegionNode* region = new RegionNode(5);
Node* phi = new PhiNode(region, TypeInt::INT);
Node* result = make_indexOf_node(src_start, src_count, tgt_start, tgt_count, region, phi, ae);
if (result != NULL) {
// The result is index relative to from_index if substring was found, -1 otherwise.
// Generate code which will fold into cmove.
Node* cmp = _gvn.transform(new CmpINode(result, intcon(0)));
Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::lt));
Node* if_lt = generate_slow_guard(bol, NULL);
if (if_lt != NULL) {
// result == -1
phi->init_req(3, result);
region->init_req(3, if_lt);
}
if (!stopped()) {
result = _gvn.transform(new AddINode(result, from_index));
phi->init_req(4, result);
region->init_req(4, control());
}
}
set_control(_gvn.transform(region));
record_for_igvn(region);
set_result(_gvn.transform(phi));
clear_upper_avx();
return true;
}
// Create StrIndexOfNode with fast path checks
Node* LibraryCallKit::make_indexOf_node(Node* src_start, Node* src_count, Node* tgt_start, Node* tgt_count,
RegionNode* region, Node* phi, StrIntrinsicNode::ArgEnc ae) {
// Check for substr count > string count
Node* cmp = _gvn.transform(new CmpINode(tgt_count, src_count));
Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::gt));
Node* if_gt = generate_slow_guard(bol, NULL);
if (if_gt != NULL) {
phi->init_req(1, intcon(-1));
region->init_req(1, if_gt);
}
if (!stopped()) {
// Check for substr count == 0
cmp = _gvn.transform(new CmpINode(tgt_count, intcon(0)));
bol = _gvn.transform(new BoolNode(cmp, BoolTest::eq));
Node* if_zero = generate_slow_guard(bol, NULL);
if (if_zero != NULL) {
phi->init_req(2, intcon(0));
region->init_req(2, if_zero);
}
}
if (!stopped()) {
return make_string_method_node(Op_StrIndexOf, src_start, src_count, tgt_start, tgt_count, ae);
}
return NULL;
}
//-----------------------------inline_string_indexOfChar-----------------------
bool LibraryCallKit::inline_string_indexOfChar(StrIntrinsicNode::ArgEnc ae) {
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
return false;
}
if (!Matcher::match_rule_supported(Op_StrIndexOfChar)) {
return false;
}
assert(callee()->signature()->size() == 4, "String.indexOfChar() has 4 arguments");
Node* src = argument(0); // byte[]
Node* tgt = argument(1); // tgt is int ch
Node* from_index = argument(2);
Node* max = argument(3);
src = must_be_not_null(src, true);
Node* src_offset = ae == StrIntrinsicNode::L ? from_index : _gvn.transform(new LShiftINode(from_index, intcon(1)));
Node* src_start = array_element_address(src, src_offset, T_BYTE);
Node* src_count = _gvn.transform(new SubINode(max, from_index));
// Range checks
generate_string_range_check(src, src_offset, src_count, ae == StrIntrinsicNode::U);
if (stopped()) {
return true;
}
RegionNode* region = new RegionNode(3);
Node* phi = new PhiNode(region, TypeInt::INT);
Node* result = new StrIndexOfCharNode(control(), memory(TypeAryPtr::BYTES), src_start, src_count, tgt, ae);
C->set_has_split_ifs(true); // Has chance for split-if optimization
_gvn.transform(result);
Node* cmp = _gvn.transform(new CmpINode(result, intcon(0)));
Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::lt));
Node* if_lt = generate_slow_guard(bol, NULL);
if (if_lt != NULL) {
// result == -1
phi->init_req(2, result);
region->init_req(2, if_lt);
}
if (!stopped()) {
result = _gvn.transform(new AddINode(result, from_index));
phi->init_req(1, result);
region->init_req(1, control());
}
set_control(_gvn.transform(region));
record_for_igvn(region);
set_result(_gvn.transform(phi));
return true;
}
//---------------------------inline_string_copy---------------------
// compressIt == true --> generate a compressed copy operation (compress char[]/byte[] to byte[])
// int StringUTF16.compress(char[] src, int srcOff, byte[] dst, int dstOff, int len)
// int StringUTF16.compress(byte[] src, int srcOff, byte[] dst, int dstOff, int len)
// compressIt == false --> generate an inflated copy operation (inflate byte[] to char[]/byte[])
// void StringLatin1.inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len)
// void StringLatin1.inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len)
bool LibraryCallKit::inline_string_copy(bool compress) {
if (too_many_traps(Deoptimization::Reason_intrinsic)) {
return false;
}
int nargs = 5; // 2 oops, 3 ints
assert(callee()->signature()->size() == nargs, "string copy has 5 arguments");
Node* src = argument(0);
Node* src_offset = argument(1);
Node* dst = argument(2);
Node* dst_offset = argument(3);
Node* length = argument(4);
// Check for allocation before we add nodes that would confuse
// tightly_coupled_allocation()
AllocateArrayNode* alloc = tightly_coupled_allocation(dst);
// Figure out the size and type of the elements we will be copying.
const Type* src_type = src->Value(&_gvn);
const Type* dst_type = dst->Value(&_gvn);
BasicType src_elem = src_type->isa_aryptr()->elem()->array_element_basic_type();
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.30 Sekunden
(vorverarbeitet)
¤
|
Haftungshinweis
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.
|