8265518: C1: Intrinsic support for Preconditions.checkIndex

Reviewed-by: dfuchs, iveresov
This commit is contained in:
Yi Yang 2021-06-12 01:03:50 +00:00
parent a466b49e71
commit 5cee23a9ed
11 changed files with 270 additions and 98 deletions

View File

@ -207,7 +207,8 @@ bool Compiler::is_intrinsic_supported(const methodHandle& method) {
case vmIntrinsics::_putCharUnaligned:
case vmIntrinsics::_putIntUnaligned:
case vmIntrinsics::_putLongUnaligned:
case vmIntrinsics::_checkIndex:
case vmIntrinsics::_Preconditions_checkIndex:
case vmIntrinsics::_Preconditions_checkLongIndex:
case vmIntrinsics::_updateCRC32:
case vmIntrinsics::_updateBytesCRC32:
case vmIntrinsics::_updateByteBufferCRC32:

View File

@ -485,22 +485,6 @@ void LIRGenerator::array_range_check(LIR_Opr array, LIR_Opr index,
}
}
void LIRGenerator::nio_range_check(LIR_Opr buffer, LIR_Opr index, LIR_Opr result, CodeEmitInfo* info) {
CodeStub* stub = new RangeCheckStub(info, index);
if (index->is_constant()) {
cmp_mem_int(lir_cond_belowEqual, buffer, java_nio_Buffer::limit_offset(), index->as_jint(), info);
__ branch(lir_cond_belowEqual, stub); // forward branch
} else {
cmp_reg_mem(lir_cond_aboveEqual, index, buffer,
java_nio_Buffer::limit_offset(), T_INT, info);
__ branch(lir_cond_aboveEqual, stub); // forward branch
}
__ move(index, result);
}
void LIRGenerator::arithmetic_op(Bytecodes::Code code, LIR_Opr result, LIR_Opr left, LIR_Opr right, LIR_Opr tmp_op, CodeEmitInfo* info) {
LIR_Opr result_op = result;
LIR_Opr left_op = left;
@ -1859,39 +1843,70 @@ void LIRGenerator::do_LoadField(LoadField* x) {
info ? new CodeEmitInfo(info) : NULL, info);
}
// int/long jdk.internal.util.Preconditions.checkIndex
void LIRGenerator::do_PreconditionsCheckIndex(Intrinsic* x, BasicType type) {
assert(x->number_of_arguments() == 3, "wrong type");
LIRItem index(x->argument_at(0), this);
LIRItem length(x->argument_at(1), this);
LIRItem oobef(x->argument_at(2), this);
//------------------------java.nio.Buffer.checkIndex------------------------
// int java.nio.Buffer.checkIndex(int)
void LIRGenerator::do_NIOCheckIndex(Intrinsic* x) {
// NOTE: by the time we are in checkIndex() we are guaranteed that
// the buffer is non-null (because checkIndex is package-private and
// only called from within other methods in the buffer).
assert(x->number_of_arguments() == 2, "wrong type");
LIRItem buf (x->argument_at(0), this);
LIRItem index(x->argument_at(1), this);
buf.load_item();
index.load_item();
length.load_item();
oobef.load_item();
LIR_Opr result = rlock_result(x);
if (GenerateRangeChecks) {
CodeEmitInfo* info = state_for(x);
CodeStub* stub = new RangeCheckStub(info, index.result());
if (index.result()->is_constant()) {
cmp_mem_int(lir_cond_belowEqual, buf.result(), java_nio_Buffer::limit_offset(), index.result()->as_jint(), info);
__ branch(lir_cond_belowEqual, stub);
} else {
cmp_reg_mem(lir_cond_aboveEqual, index.result(), buf.result(),
java_nio_Buffer::limit_offset(), T_INT, info);
__ branch(lir_cond_aboveEqual, stub);
}
__ move(index.result(), result);
} else {
// Just load the index into the result register
__ move(index.result(), result);
// x->state() is created from copy_state_for_exception, it does not contains arguments
// we should prepare them before entering into interpreter mode due to deoptimization.
ValueStack* state = x->state();
for (int i = 0; i < x->number_of_arguments(); i++) {
Value arg = x->argument_at(i);
state->push(arg->type(), arg);
}
}
CodeEmitInfo* info = state_for(x, state);
LIR_Opr len = length.result();
LIR_Opr zero = NULL;
if (type == T_INT) {
zero = LIR_OprFact::intConst(0);
if (length.result()->is_constant()){
len = LIR_OprFact::intConst(length.result()->as_jint());
}
} else {
assert(type == T_LONG, "sanity check");
zero = LIR_OprFact::longConst(0);
if (length.result()->is_constant()){
len = LIR_OprFact::longConst(length.result()->as_jlong());
}
}
// C1 can not handle the case that comparing index with constant value while condition
// is neither lir_cond_equal nor lir_cond_notEqual, see LIR_Assembler::comp_op.
LIR_Opr zero_reg = new_register(type);
__ move(zero, zero_reg);
#if defined(X86) && !defined(_LP64)
// BEWARE! On 32-bit x86 cmp clobbers its left argument so we need a temp copy.
LIR_Opr index_copy = new_register(index.type());
// index >= 0
__ move(index.result(), index_copy);
__ cmp(lir_cond_less, index_copy, zero_reg);
__ branch(lir_cond_less, new DeoptimizeStub(info, Deoptimization::Reason_range_check,
Deoptimization::Action_make_not_entrant));
// index < length
__ move(index.result(), index_copy);
__ cmp(lir_cond_greaterEqual, index_copy, len);
__ branch(lir_cond_greaterEqual, new DeoptimizeStub(info, Deoptimization::Reason_range_check,
Deoptimization::Action_make_not_entrant));
#else
// index >= 0
__ cmp(lir_cond_less, index.result(), zero_reg);
__ branch(lir_cond_less, new DeoptimizeStub(info, Deoptimization::Reason_range_check,
Deoptimization::Action_make_not_entrant));
// index < length
__ cmp(lir_cond_greaterEqual, index.result(), len);
__ branch(lir_cond_greaterEqual, new DeoptimizeStub(info, Deoptimization::Reason_range_check,
Deoptimization::Action_make_not_entrant));
#endif
__ move(index.result(), result);
}
//------------------------array access--------------------------------------
@ -3115,8 +3130,12 @@ void LIRGenerator::do_Intrinsic(Intrinsic* x) {
case vmIntrinsics::_fmaD: do_FmaIntrinsic(x); break;
case vmIntrinsics::_fmaF: do_FmaIntrinsic(x); break;
// java.nio.Buffer.checkIndex
case vmIntrinsics::_checkIndex: do_NIOCheckIndex(x); break;
case vmIntrinsics::_Preconditions_checkIndex:
do_PreconditionsCheckIndex(x, T_INT);
break;
case vmIntrinsics::_Preconditions_checkLongIndex:
do_PreconditionsCheckIndex(x, T_LONG);
break;
case vmIntrinsics::_compareAndSetReference:
do_CompareAndSwap(x, objectType);

View File

@ -260,7 +260,7 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure {
void do_LibmIntrinsic(Intrinsic* x);
void do_ArrayCopy(Intrinsic* x);
void do_CompareAndSwap(Intrinsic* x, ValueType* type);
void do_NIOCheckIndex(Intrinsic* x);
void do_PreconditionsCheckIndex(Intrinsic* x, BasicType type);
void do_FPIntrinsics(Intrinsic* x);
void do_Reference_get(Intrinsic* x);
void do_update_CRC32(Intrinsic* x);
@ -348,8 +348,6 @@ class LIRGenerator: public InstructionVisitor, public BlockClosure {
// this loads the length and compares against the index
void array_range_check (LIR_Opr array, LIR_Opr index, CodeEmitInfo* null_check_info, CodeEmitInfo* range_check_info);
// For java.nio.Buffer.checkIndex
void nio_range_check (LIR_Opr buffer, LIR_Opr index, LIR_Opr result, CodeEmitInfo* info);
void arithmetic_op_int (Bytecodes::Code code, LIR_Opr result, LIR_Opr left, LIR_Opr right, LIR_Opr tmp);
void arithmetic_op_long (Bytecodes::Code code, LIR_Opr result, LIR_Opr left, LIR_Opr right, CodeEmitInfo* info = NULL);

View File

@ -153,9 +153,6 @@
product(bool, InlineSynchronizedMethods, true, \
"Inline synchronized methods") \
\
product(bool, InlineNIOCheckIndex, true, DIAGNOSTIC, \
"Intrinsify java.nio.Buffer.checkIndex") \
\
develop(bool, CanonicalizeNodes, true, \
"Canonicalize graph nodes") \
\

View File

@ -4647,26 +4647,6 @@ void java_lang_AssertionStatusDirectives::set_deflt(oop o, bool val) {
o->bool_field_put(_deflt_offset, val);
}
// Support for intrinsification of java.nio.Buffer.checkIndex
int java_nio_Buffer::_limit_offset;
#define BUFFER_FIELDS_DO(macro) \
macro(_limit_offset, k, "limit", int_signature, false)
void java_nio_Buffer::compute_offsets() {
InstanceKlass* k = vmClasses::nio_Buffer_klass();
assert(k != NULL, "must be loaded in 1.4+");
BUFFER_FIELDS_DO(FIELD_COMPUTE_OFFSET);
}
#if INCLUDE_CDS
void java_nio_Buffer::serialize_offsets(SerializeClosure* f) {
BUFFER_FIELDS_DO(FIELD_SERIALIZE_OFFSET);
}
#endif
int java_util_concurrent_locks_AbstractOwnableSynchronizer::_owner_offset;
#define AOS_FIELDS_DO(macro) \

View File

@ -66,7 +66,6 @@ class RecordComponent;
f(java_lang_reflect_Constructor) \
f(java_lang_reflect_Field) \
f(java_lang_reflect_RecordComponent) \
f(java_nio_Buffer) \
f(reflect_ConstantPool) \
f(reflect_UnsafeStaticFieldAccessorImpl) \
f(java_lang_reflect_Parameter) \
@ -1584,16 +1583,6 @@ class java_lang_AssertionStatusDirectives: AllStatic {
};
class java_nio_Buffer: AllStatic {
private:
static int _limit_offset;
public:
static int limit_offset() { CHECK_INIT(_limit_offset); }
static void compute_offsets();
static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN;
};
class java_util_concurrent_locks_AbstractOwnableSynchronizer : AllStatic {
private:
static int _owner_offset;

View File

@ -88,7 +88,8 @@ bool vmIntrinsics::preserves_state(vmIntrinsics::ID id) {
case vmIntrinsics::_dlog10:
case vmIntrinsics::_dexp:
case vmIntrinsics::_dpow:
case vmIntrinsics::_checkIndex:
case vmIntrinsics::_Preconditions_checkIndex:
case vmIntrinsics::_Preconditions_checkLongIndex:
case vmIntrinsics::_Reference_get:
case vmIntrinsics::_updateCRC32:
case vmIntrinsics::_updateBytesCRC32:
@ -467,11 +468,6 @@ bool vmIntrinsics::disabled_by_jvm_flags(vmIntrinsics::ID id) {
case vmIntrinsics::_copyMemory:
if (!InlineArrayCopy || !InlineUnsafeOps) return true;
break;
#ifdef COMPILER1
case vmIntrinsics::_checkIndex:
if (!InlineNIOCheckIndex) return true;
break;
#endif // COMPILER1
#ifdef COMPILER2
case vmIntrinsics::_clone:
case vmIntrinsics::_copyOf:

View File

@ -341,10 +341,6 @@ class methodHandle;
do_intrinsic(_Preconditions_checkLongIndex, jdk_internal_util_Preconditions, checkIndex_name, Preconditions_checkLongIndex_signature, F_S) \
do_signature(Preconditions_checkLongIndex_signature, "(JJLjava/util/function/BiFunction;)J") \
\
do_class(java_nio_Buffer, "java/nio/Buffer") \
do_intrinsic(_checkIndex, java_nio_Buffer, checkIndex_name, int_int_signature, F_R) \
do_name( checkIndex_name, "checkIndex") \
\
do_class(java_lang_StringCoding, "java/lang/StringCoding") \
do_intrinsic(_hasNegatives, java_lang_StringCoding, hasNegatives_name, hasNegatives_signature, F_S) \
do_name( hasNegatives_name, "hasNegatives") \

View File

@ -124,6 +124,7 @@
template(java_io_FileInputStream, "java/io/FileInputStream") \
template(java_io_ByteArrayInputStream, "java/io/ByteArrayInputStream") \
template(java_io_Serializable, "java/io/Serializable") \
template(java_nio_Buffer, "java/nio/Buffer") \
template(java_util_Arrays, "java/util/Arrays") \
template(java_util_Objects, "java/util/Objects") \
template(java_util_Properties, "java/util/Properties") \
@ -482,6 +483,7 @@
template(use_unaligned_access_name, "UNALIGNED_ACCESS") \
template(data_cache_line_flush_size_name, "DATA_CACHE_LINE_FLUSH_SIZE") \
template(during_unsafe_access_name, "during_unsafe_access") \
template(checkIndex_name, "checkIndex") \
\
/* name symbols needed by intrinsics */ \
VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, VM_SYMBOL_IGNORE, template, VM_SYMBOL_IGNORE, VM_ALIAS_IGNORE) \

View File

@ -34,9 +34,9 @@ import jdk.internal.misc.ScopedMemoryAccess.Scope;
import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM.BufferPool;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.IntrinsicCandidate;
import java.io.FileDescriptor;
import java.util.Objects;
import java.util.Spliterator;
/**
@ -737,11 +737,8 @@ public abstract class Buffer {
* IndexOutOfBoundsException} if it is not smaller than the limit
* or is smaller than zero.
*/
@IntrinsicCandidate
final int checkIndex(int i) { // package-private
if ((i < 0) || (i >= limit))
throw new IndexOutOfBoundsException();
return i;
return Objects.checkIndex(i, limit);
}
final int checkIndex(int i, int nb) { // package-private

View File

@ -0,0 +1,197 @@
/*
* Copyright (c) 2021, Alibaba Group Holding Limited. 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.
*
*/
/*
* @test
* @author Yi Yang
* @bug 8265518
* @summary C1 intrinsic support for jdk.internal.util.Preconditions.checkIndex
* @requires vm.compiler1.enabled
* @library /test/lib
* @modules java.base/jdk.internal.util
* @run main/othervm -XX:TieredStopAtLevel=1 -Xbatch
* -XX:CompileCommand=dontinline,*TestCheckIndexC1Intrinsic.check*
* -XX:CompileCommand=compileonly,*TestCheckIndexC1Intrinsic.check*
* compiler.c1.TestCheckIndexC1Intrinsic
*/
package compiler.c1;
import jdk.test.lib.Asserts;
import jdk.internal.util.Preconditions;
public class TestCheckIndexC1Intrinsic {
static int limit = 100;
private static class MyException extends RuntimeException {
public MyException(String msg) {
super(msg);
}
}
static void check0() {
long res = Preconditions.checkIndex(0, 1, null);
Asserts.assertEquals((int)res, 0);
try {
Preconditions.checkIndex(1, 1, null);
throw new AssertionError("Expected IndexOutOfBoundsException not thrown");
} catch (IndexOutOfBoundsException e) {
// got it!
}
try {
Preconditions.checkIndex(Integer.MIN_VALUE, -1, null);
throw new AssertionError("Expected IndexOutOfBoundsException not thrown");
} catch (IndexOutOfBoundsException e) {
// got it!
}
try {
Preconditions.checkIndex(Integer.MIN_VALUE, Integer.MIN_VALUE, null);
throw new AssertionError("Expected IndexOutOfBoundsException not thrown");
} catch (IndexOutOfBoundsException e) {
// got it!
}
try {
Preconditions.checkIndex(Long.MAX_VALUE, Long.MAX_VALUE, null);
throw new AssertionError("Expected IndexOutOfBoundsException not thrown");
} catch (IndexOutOfBoundsException e) {
// got it!
}
res = Preconditions.checkIndex(Long.MAX_VALUE - 1, Long.MAX_VALUE, null);
Asserts.assertEquals(res, Long.MAX_VALUE - 1);
try {
// read fields
Preconditions.checkIndex(limit + 1, limit, (s, integers) -> new MyException("Reason:" + s + "::" + integers));
throw new AssertionError("Expected IndexOutOfBoundsException not thrown");
} catch(MyException e){
// got it!
}
}
static void check1(int i) {
boolean trigger = false;
try {
Preconditions.checkIndex(i, 9999, (s, integers) -> new RuntimeException("ex"));
} catch (RuntimeException e) {
Asserts.assertTrue("ex".equals(e.getMessage()));
trigger = true;
} finally {
if (trigger) {
Asserts.assertTrue(i == 9999L);
} else {
Asserts.assertTrue(i != 9999L);
}
}
}
static void check2(long i) {
boolean trigger = false;
try {
Preconditions.checkIndex(i, 9999L, (s, integers) -> new RuntimeException("ex"));
} catch (RuntimeException e) {
Asserts.assertTrue("ex".equals(e.getMessage()));
trigger = true;
} finally {
if (trigger) {
Asserts.assertTrue(i == 9999L);
} else {
Asserts.assertTrue(i != 9999L);
}
}
}
static void check3(int i) {
boolean trigger = false;
try {
Preconditions.checkIndex(i, 9999, null);
} catch (IndexOutOfBoundsException e) {
Asserts.assertTrue(i == 9999);
trigger = true;
} finally {
if (trigger) {
Asserts.assertTrue(i == 9999L);
} else {
Asserts.assertTrue(i != 9999L);
}
}
}
static void check4(long i) {
boolean trigger = false;
try {
Preconditions.checkIndex(i, 9999L, null);
} catch (IndexOutOfBoundsException e) {
trigger = true;
} finally {
if (trigger) {
Asserts.assertTrue(i == 9999L);
} else {
Asserts.assertTrue(i != 9999L);
}
}
}
static void check5(int i) {
Preconditions.checkIndex(i, 99999, (s, integers) -> new RuntimeException("ex"));
}
static void check6(long i) {
Preconditions.checkIndex(i, 99999L, (s, integers) -> new RuntimeException("ex"));
}
static void check7(int i) {
Preconditions.checkIndex(i, 99999, null);
}
static void check8(long i) {
Preconditions.checkIndex(i, 99999L, null);
}
static void check9(int i) {
Preconditions.checkIndex(i, i + 1, null);
}
static void check10(long i) {
Preconditions.checkIndex(i, i + 1L, null);
}
public static void main(String... args) {
for (int i = 0; i < 10_000; i++) {
check0();
check1(i);
check2((long) i);
check3(i);
check4((long) i);
check5(i);
check6((long) i);
check7(i);
check8((long) i);
check9(i);
check10((long)i);
}
}
}