Skip to content
Open
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
1 change: 1 addition & 0 deletions src/hotspot/share/ci/ciArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ ciConstant ciArray::element_value_impl(BasicType elembt,
// Current value of an element.
// Returns T_ILLEGAL if there is no element at the given index.
ciConstant ciArray::element_value(int index) {
assert(index >= 0, "out-of-bounds index: %d", index);
BasicType elembt = element_basic_type();
ciConstant value = check_constant_value_cache(index, elembt);
if (value.is_valid()) {
Expand Down
1 change: 1 addition & 0 deletions src/hotspot/share/ci/ciInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ ciType* ciInstance::java_mirror_type() {
// ------------------------------------------------------------------
// ciInstance::field_value_impl
ciConstant ciInstance::field_value_impl(BasicType field_btype, int offset) {
assert(offset > 0, "out-of-bounds offset: %d", offset);
ciConstant value = check_constant_value_cache(offset, field_btype);
if (value.is_valid()) {
return value;
Expand Down
24 changes: 24 additions & 0 deletions src/hotspot/share/ci/ciObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,30 @@ bool ciObject::should_be_constant() {
return handle() == nullptr;
}


// ------------------------------------------------------------------
// ciObject::identity_hash_or_no_hash
//
// The identity hash code of an object, if a hash has been computed.
jint ciObject::identity_hash_or_no_hash() {
if (!is_null_object()) {
{ // Handle identity hash as if it were a field.
ciConstant value = check_constant_value_cache(IDENTITY_HASH_OFFSET, T_INT);
if (value.is_valid()) {
return value.as_int();
}
}
VM_ENTRY_MARK;
oop obj = get_oop();
jint identity_hash = checked_cast<jint>(obj->fast_identity_hash_or_no_hash());
add_to_constant_value_cache(IDENTITY_HASH_OFFSET,
ciConstant(T_INT, identity_hash));
return identity_hash;
} else {
return markWord::no_hash;
}
}

// ------------------------------------------------------------------
// ciObject::print
//
Expand Down
3 changes: 3 additions & 0 deletions src/hotspot/share/ci/ciObject.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class ciObject : public ciBaseObject {
ciConstant value() const { return _value; }
};

const int IDENTITY_HASH_OFFSET = -1;
GrowableArray<ConstantValue>* _constant_values = nullptr;

protected:
Expand Down Expand Up @@ -183,6 +184,8 @@ class ciObject : public ciBaseObject {
return (ciTypeArray*)this;
}

jint identity_hash_or_no_hash();

// Print debugging output about this ciObject.
void print(outputStream* st);
void print() { print(tty); } // GDB cannot handle default arguments
Expand Down
3 changes: 3 additions & 0 deletions src/hotspot/share/oops/oop.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,9 @@ class oopDesc {
intptr_t slow_identity_hash();
inline bool fast_no_hash_check();

// identity hash; returns the identity hash key if it is present (doesn't compute it)
inline intptr_t fast_identity_hash_or_no_hash();

// marks are forwarded to stack when object is locked
inline bool has_displaced_mark() const;
inline markWord displaced_mark() const;
Expand Down
15 changes: 15 additions & 0 deletions src/hotspot/share/oops/oop.inline.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,21 @@ intptr_t oopDesc::identity_hash() {
}
}

// Returns the identity hash if it is present (doesn't compute it).
intptr_t oopDesc::fast_identity_hash_or_no_hash() {
// Note: The mark must be read into local variable to avoid concurrent updates.
markWord mrk = mark_acquire();
assert(!mrk.is_marked(), "should never be marked");
// Fast case: if the object is unlocked and the hash value is set, no locking is needed to read it.
// If UseObjectMonitorTable is set, the hash can simply be installed in the
// object header, since the monitor isn't in the object header.
if (UseObjectMonitorTable || !mrk.has_monitor()) {
return mrk.hash();
} else {
return markWord::no_hash;
}
}

// This checks fast simple case of whether the oop has_no_hash,
// to optimize JVMTI table lookup.
bool oopDesc::fast_no_hash_check() {
Expand Down
14 changes: 14 additions & 0 deletions src/hotspot/share/opto/library_call.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4777,6 +4777,20 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) {
generate_virtual_guard(obj_klass, slow_region);
}

// Fold identity hash after virtual dispatches are excluded.
// Constant folding in fast path checks doesn't trivially happen because
// object header is not a constant. We must check and fetch explicitly.
const TypeInstPtr* t = _gvn.type(obj)->isa_instptr();
if (t != nullptr && t->const_oop() != nullptr) {
jint hash = t->const_oop()->identity_hash_or_no_hash();
if (hash != 0) {
result_reg->init_req(_fast_path, control());
result_val->init_req(_fast_path, _gvn.intcon(hash));
set_result(result_reg, result_val);
return true;
}
}

// Get the header out of the object, use LoadMarkNode when available
Node* header_addr = basic_plus_adr(obj, oopDesc::mark_offset_in_bytes());
// The control of the load must be null. Otherwise, the load can move before
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2025, 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.
*/

package compiler.c2.irTests.constantFold;

import compiler.lib.ir_framework.IR;
import compiler.lib.ir_framework.IRNode;
import compiler.lib.ir_framework.Test;
import compiler.lib.ir_framework.TestFramework;

/*
* @test
* @bug 8372845
* @summary Verify constant folding is possible for identity hash code
* @library /test/lib /
* @requires vm.compiler2.enabled
* @run driver compiler.c2.irTests.constantFold.IdentityHashCodeFold
*/
public class IdentityHashCodeFold {

public static void main(String[] args) {
TestFramework.run();
}

static final Object a = new Object(), b = new Object();

@Test
@IR(failOn = {IRNode.ADD_I})
public int testSum() {
return a.hashCode() + System.identityHashCode(b);
}

}