Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8305898: Alternative self-forwarding mechanism #13779

Closed
wants to merge 35 commits into from
Closed
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
619e3fa
8305898: Alternative self-forwarding mechanism
rkennke May 3, 2023
7dca17a
Replace uses of decode_pointer() with forwardee()
rkennke May 3, 2023
d6c0bd0
Merge branch 'JDK-8305896' into JDK-8305898
rkennke May 3, 2023
0d066ea
Merge branch 'JDK-8305896' into JDK-8305898
May 4, 2023
efb08a6
Use forwardee() in forward_to_atomic() method
May 4, 2023
909a810
Merge branch 'JDK-8305896' into JDK-8305898
rkennke May 4, 2023
b9c8ca0
Merge branch 'JDK-8305896' into JDK-8305898
rkennke May 4, 2023
ab4aea0
Merge branch 'JDK-8305896' into JDK-8305898
rkennke May 8, 2023
15a8626
Merge branch 'JDK-8305896' into JDK-8305898
rkennke May 8, 2023
1313a45
Update src/hotspot/share/oops/oop.inline.hpp
rkennke May 9, 2023
06e555d
Update src/hotspot/share/oops/oop.inline.hpp
rkennke May 9, 2023
1d3e833
Update src/hotspot/share/oops/oop.inline.hpp
rkennke May 9, 2023
a559e8d
Update src/hotspot/share/oops/oop.inline.hpp
rkennke May 9, 2023
94e3d07
@shipilev suggestions
May 9, 2023
a142343
Merge branch 'JDK-8305896' into JDK-8305898
May 9, 2023
915c20b
Fix assert
May 9, 2023
6d39d57
Fix asserts (again)
May 9, 2023
eb04cd7
Update src/hotspot/share/oops/oop.inline.hpp
rkennke May 10, 2023
40c1b0b
Update src/hotspot/share/oops/oop.inline.hpp
rkennke May 10, 2023
4d9713c
Rename self-forwarded -> forward-failed
rkennke May 10, 2023
39c3372
Merge remote-tracking branch 'origin/JDK-8305898' into JDK-8305898
rkennke May 10, 2023
0229792
Merge branch 'JDK-8305896' into JDK-8305898
rkennke May 10, 2023
866771c
wqRevert "Rename self-forwarded -> forward-failed"
rkennke May 11, 2023
95341f0
Merge branch 'JDK-8305896' into JDK-8305898
rkennke May 11, 2023
880d564
Merge branch 'JDK-8305896' into JDK-8305898
May 12, 2023
d35cfb4
Fix tests on 32bit builds
rkennke May 13, 2023
9e934ba
Merge branch 'JDK-8305896' into JDK-8305898
rkennke May 17, 2023
4895ad8
Update comment about mark-word layout
rkennke May 17, 2023
3519da7
Merge branch 'JDK-8305896' into JDK-8305898
May 18, 2023
3838ac0
Merge branch 'JDK-8305896' into JDK-8305898
Jun 16, 2023
cd5f237
Merge branch 'JDK-8305896' into JDK-8305898
rkennke Aug 11, 2023
2a00d30
Merge branch 'JDK-8305896' into JDK-8305898
rkennke Aug 25, 2023
dbb74fb
Merge branch 'JDK-8305896' into JDK-8305898
rkennke Sep 29, 2023
eee8ab5
Merge branch 'JDK-8305896' into JDK-8305898
rkennke Nov 9, 2023
1a4dda1
Merge branch 'JDK-8305896' into JDK-8305898
rkennke Dec 11, 2023
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
2 changes: 1 addition & 1 deletion src/hotspot/share/gc/g1/g1OopClosures.inline.hpp
Original file line number Diff line number Diff line change
@@ -228,7 +228,7 @@ void G1ParCopyClosure<barrier, should_mark>::do_oop_work(T* p) {
oop forwardee;
markWord m = obj->mark();
if (m.is_marked()) {
forwardee = cast_to_oop(m.decode_pointer());
forwardee = obj->forwardee(m);
} else {
forwardee = _par_scan_state->copy_to_survivor_space(state, obj, m);
}
4 changes: 2 additions & 2 deletions src/hotspot/share/gc/g1/g1ParScanThreadState.cpp
Original file line number Diff line number Diff line change
@@ -212,7 +212,7 @@ void G1ParScanThreadState::do_oop_evac(T* p) {

markWord m = obj->mark();
if (m.is_marked()) {
obj = cast_to_oop(m.decode_pointer());
obj = obj->forwardee(m);
Copy link

@mmyxym mmyxym Jul 24, 2023

Choose a reason for hiding this comment

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

Shall we have a method "oop::forwardee_not_self" which guarantee to be not self fowarded? So we can remove the self-forward if-check in GC critical path.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are there any paths that don't need to handle self-forwarded state?

Also, it seems to me that the path would be dominated by the load of the mark-word. Testing-and-branching for the self-forwarded bit seems like the minor problem there. It would be nice if we could tell the C++ compiler that the branch is expected to be uncommon, so it could shape the emitted code accordingly, but, afaik, we can't.

If we were to micro-optimize the forwarding-decoding, then it would be more useful to optimize the common pattern:

if (o->is_forwarded()) { // Loads and tests the mark-word
  oop fwd = o->forwardee(); // Loads mark-word again, and decode forwardee.
 ...
}

To something like:

oop fwd = o->forwardee(); // Return nullptr when not forwarded
if (fwd != nullptr) {
  ...
}

There's a way to improve further on this, as I proposed back in the early versions of #5955, which also avoids the decoding altogether if not forwarded, but it's a little more clunky:

OopForwarding fwd(obj);
if (fwd.is_forwarded()) {
  oop forwardee = fwd.forwardee();
  ...
}

where the scoped OopForwarding object would encapsulate the markWord and the testing and decoding of the fwd-ptr, but it has been rejected back then. But it would certainly help more than an oopDesc::forwardee_not_self() approach (if that were even possible, which I think it is not).

Copy link

Choose a reason for hiding this comment

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

Sorry. It's my misunderstanding. G1ParScanThreadState::do_oop_evac still needs to handle self forward.

} else {
obj = do_copy_to_survivor_space(region_attr, obj, m);
}
@@ -632,7 +632,7 @@ NOINLINE
oop G1ParScanThreadState::handle_evacuation_failure_par(oop old, markWord m, size_t word_sz, bool cause_pinned) {
assert(_g1h->is_in_cset(old), "Object " PTR_FORMAT " should be in the CSet", p2i(old));

oop forward_ptr = old->forward_to_atomic(old, m, memory_order_relaxed);
oop forward_ptr = old->forward_to_self_atomic(m, memory_order_relaxed);
if (forward_ptr == nullptr) {
// Forward-to-self succeeded. We are the "owner" of the object.
HeapRegion* r = _g1h->heap_region_containing(old);
2 changes: 1 addition & 1 deletion src/hotspot/share/gc/parallel/psPromotionManager.cpp
Original file line number Diff line number Diff line change
@@ -325,7 +325,7 @@ oop PSPromotionManager::oop_promotion_failed(oop obj, markWord obj_mark) {
// this started. If it is the same (i.e., no forwarding
// pointer has been installed), then this thread owns
// it.
if (obj->forward_to_atomic(obj, obj_mark) == nullptr) {
if (obj->forward_to_self_atomic(obj_mark) == nullptr) {
// We won any races, we "own" this object.
assert(obj == obj->forwardee(), "Sanity");

Original file line number Diff line number Diff line change
@@ -152,7 +152,7 @@ inline oop PSPromotionManager::copy_to_survivor_space(oop o) {
// other thread.
OrderAccess::acquire();
// Return the already installed forwardee.
return cast_to_oop(m.decode_pointer());
return o->forwardee(m);
}
}

3 changes: 1 addition & 2 deletions src/hotspot/share/gc/serial/defNewGeneration.cpp
Original file line number Diff line number Diff line change
@@ -898,8 +898,7 @@ void DefNewGeneration::handle_promotion_failure(oop old) {

ContinuationGCSupport::transform_stack_chunk(old);

// forward to self
old->forward_to(old);
old->forward_to_self();

_promo_failure_scan_stack.push(old);

31 changes: 26 additions & 5 deletions src/hotspot/share/oops/markWord.hpp
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@
#ifndef SHARE_OOPS_MARKWORD_HPP
#define SHARE_OOPS_MARKWORD_HPP

#include "gc/shared/gc_globals.hpp"
#include "metaprogramming/primitiveConversions.hpp"
#include "oops/oopsHierarchy.hpp"
#include "runtime/globals.hpp"
@@ -43,6 +44,10 @@
// --------
// unused:25 hash:31 -->| unused_gap:1 age:4 unused_gap:1 lock:2 (normal object)
//
// 64 bits (alternative GC forwarding):
// ------------------------------------
// unused:25 hash:31 -->| unused_gap:1 age:4 self-fwd:1 lock:2 (normal object)
//
// - hash contains the identity hash value: largest value is
// 31 bits, see os::random(). Also, 64-bit vm's require
// a hash value no bigger than 32 bits because they will not
@@ -103,17 +108,20 @@ class markWord {
// Constants
static const int age_bits = 4;
static const int lock_bits = 2;
static const int first_unused_gap_bits = 1;
static const int max_hash_bits = BitsPerWord - age_bits - lock_bits - first_unused_gap_bits;
static const int self_forwarded_bits = 1;
static const int max_hash_bits = BitsPerWord - age_bits - lock_bits - self_forwarded_bits;
static const int hash_bits = max_hash_bits > 31 ? 31 : max_hash_bits;
static const int second_unused_gap_bits = LP64_ONLY(1) NOT_LP64(0);
static const int unused_gap_bits = LP64_ONLY(1) NOT_LP64(0);

static const int lock_shift = 0;
static const int age_shift = lock_bits + first_unused_gap_bits;
static const int hash_shift = age_shift + age_bits + second_unused_gap_bits;
static const int self_forwarded_shift = lock_shift + lock_bits;
static const int age_shift = self_forwarded_shift + self_forwarded_bits;
static const int hash_shift = age_shift + age_bits + unused_gap_bits;

static const uintptr_t lock_mask = right_n_bits(lock_bits);
static const uintptr_t lock_mask_in_place = lock_mask << lock_shift;
static const uintptr_t self_forwarded_mask = right_n_bits(self_forwarded_bits);
static const uintptr_t self_forwarded_mask_in_place = self_forwarded_mask << self_forwarded_shift;
static const uintptr_t age_mask = right_n_bits(age_bits);
static const uintptr_t age_mask_in_place = age_mask << age_shift;
static const uintptr_t hash_mask = right_n_bits(hash_bits);
@@ -260,6 +268,19 @@ class markWord {

// Recover address of oop from encoded form used in mark
inline void* decode_pointer() { return (void*)clear_lock_bits().value(); }

#ifdef _LP64
inline bool self_forwarded() const {
bool self_fwd = mask_bits(value(), self_forwarded_mask_in_place) != 0;
assert(!self_fwd || UseAltGCForwarding, "Only set self-fwd bit when using alt GC forwarding");
return self_fwd;
}

inline markWord set_self_forwarded() const {
assert(UseAltGCForwarding, "Only call this with alt GC forwarding");
return markWord(value() | self_forwarded_mask_in_place | marked_value);
}
#endif
};

// Support atomic operations.
3 changes: 3 additions & 0 deletions src/hotspot/share/oops/oop.hpp
Original file line number Diff line number Diff line change
@@ -260,14 +260,17 @@ class oopDesc {
inline bool is_forwarded() const;

inline void forward_to(oop p);
inline void forward_to_self();

// Like "forward_to", but inserts the forwarding pointer atomically.
// Exactly one thread succeeds in inserting the forwarding pointer, and
// this call returns null for that thread; any other thread has the
// value of the forwarding pointer returned and does not modify "this".
inline oop forward_to_atomic(oop p, markWord compare, atomic_memory_order order = memory_order_conservative);
inline oop forward_to_self_atomic(markWord compare, atomic_memory_order order = memory_order_conservative);

inline oop forwardee() const;
inline oop forwardee(markWord header) const;

// Age of object during scavenge
inline uint age() const;
68 changes: 64 additions & 4 deletions src/hotspot/share/oops/oop.inline.hpp
Original file line number Diff line number Diff line change
@@ -38,6 +38,7 @@
#include "oops/oopsHierarchy.hpp"
#include "runtime/atomic.hpp"
#include "runtime/globals.hpp"
#include "runtime/safepoint.hpp"
#include "utilities/align.hpp"
#include "utilities/debug.hpp"
#include "utilities/macros.hpp"
@@ -270,28 +271,87 @@ bool oopDesc::is_forwarded() const {

// Used by scavengers
void oopDesc::forward_to(oop p) {
assert(p != cast_to_oop(this) || !UseAltGCForwarding, "Must not be called with self-forwarding");
markWord m = markWord::encode_pointer_as_mark(p);
assert(m.decode_pointer() == p, "encoding must be reversible");
assert(forwardee(m) == p, "encoding must be reversible");
set_mark(m);
}

void oopDesc::forward_to_self() {
#ifdef _LP64
if (UseAltGCForwarding) {
markWord m = mark();
// If mark is displaced, we need to preserve the real header during GC.
// It will be restored to the displaced header after GC.
assert(SafepointSynchronize::is_at_safepoint(), "we can only safely fetch the displaced header at safepoint");
if (m.has_displaced_mark_helper()) {
m = m.displaced_mark_helper();
}
m = m.set_self_forwarded();
assert(forwardee(m) == cast_to_oop(this), "encoding must be reversible");
set_mark(m);
Comment on lines +283 to +292
Copy link
Member

Choose a reason for hiding this comment

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

Could you explain why we need to restore the displaced mark word here? I wonder why it matters what the old mark word is and why this code can't be changed to something like this:

    markWord m = markWord::prototype().set_self_forwarded();
    set_mark(m);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The main reason why we need to implement the self-forwarding is to preserve the upper bits of the mark-word (specifically, the Klass* that we want to place there with JEP 450). If the header is displaced, we would install the self-forwarded bit in the tagged native-pointer that is in the header, meaning we loose the ability to get to the actual header to fetch the Klass*. We do restore the header later, but GC needs access to the Klass* meanwhile.

Copy link
Member

Choose a reason for hiding this comment

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

Thanks. While reviewing this patch I was thinking about the current JDK code, which as far as I understand doesn't need to restore the displaced header here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right. It might be cleaner to move that part out of here and into #13961, WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

I think that would be good.

} else
#endif
{
forward_to(oop(this));
}
}

oop oopDesc::forward_to_atomic(oop p, markWord compare, atomic_memory_order order) {
assert(p != cast_to_oop(this) || !UseAltGCForwarding, "Must not be called with self-forwarding");
markWord m = markWord::encode_pointer_as_mark(p);
assert(m.decode_pointer() == p, "encoding must be reversible");
markWord old_mark = cas_set_mark(m, compare, order);
if (old_mark == compare) {
return nullptr;
} else {
return cast_to_oop(old_mark.decode_pointer());
return forwardee(old_mark);
}
}

oop oopDesc::forward_to_self_atomic(markWord compare, atomic_memory_order order) {
#ifdef _LP64
if (UseAltGCForwarding) {
markWord m = compare;
// If mark is displaced, we need to preserve the real header during GC.
// It will be restored to the displaced header after GC.
assert(SafepointSynchronize::is_at_safepoint(), "we can only safely fetch the displaced header at safepoint");
if (m.has_displaced_mark_helper()) {
m = m.displaced_mark_helper();
}
m = m.set_self_forwarded();
assert(forwardee(m) == cast_to_oop(this), "encoding must be reversible");
markWord old_mark = cas_set_mark(m, compare, order);
if (old_mark == compare) {
return nullptr;
} else {
assert(old_mark.is_marked(), "must be marked here");
return forwardee(old_mark);
}
} else
#endif
{
return forward_to_atomic(cast_to_oop(this), compare, order);
}
}

oop oopDesc::forwardee(markWord header) const {
assert(header.is_marked(), "only decode when actually forwarded");
#ifdef _LP64
if (header.self_forwarded()) {
return cast_to_oop(this);
} else
#endif
{
return cast_to_oop(header.decode_pointer());
}
}

// Note that the forwardee is not the same thing as the displaced_mark.
// The forwardee is used when copying during scavenge and mark-sweep.
// It does need to clear the low two locking- and GC-related bits.
oop oopDesc::forwardee() const {
assert(is_forwarded(), "only decode when actually forwarded");
return cast_to_oop(mark().decode_pointer());
return forwardee(mark());
}

// The following method needs to be MT safe.