use crate::util::gc_byte;
use crate::util::{constants, Address, ObjectReference};
use crate::vm::ObjectModel;
use std::sync::atomic::{AtomicUsize, Ordering};
use crate::plan::{AllocationSemantics, CopyContext};
use crate::vm::VMBinding;
const FORWARDING_NOT_TRIGGERED_YET: u8 = 0;
const BEING_FORWARDED: u8 = 2;
const FORWARDED: u8 = 3;
const FORWARDING_MASK: u8 = 3;
#[allow(unused)]
const FORWARDING_BITS: usize = 2;
pub fn attempt_to_forward<VM: VMBinding>(object: ObjectReference) -> u8 {
let mut old_value = gc_byte::read_gc_byte::<VM>(object);
if old_value & FORWARDING_MASK != FORWARDING_NOT_TRIGGERED_YET {
return old_value;
}
while !gc_byte::compare_exchange_gc_byte::<VM>(object, old_value, old_value | BEING_FORWARDED) {
old_value = gc_byte::read_gc_byte::<VM>(object);
if old_value & FORWARDING_MASK != FORWARDING_NOT_TRIGGERED_YET {
return old_value;
}
}
old_value
}
pub fn spin_and_get_forwarded_object<VM: VMBinding>(
object: ObjectReference,
gc_byte: u8,
) -> ObjectReference {
let mut gc_byte = gc_byte;
while gc_byte & FORWARDING_MASK == BEING_FORWARDED {
gc_byte = gc_byte::read_gc_byte::<VM>(object);
}
if gc_byte & FORWARDING_MASK == FORWARDED {
let status_word = read_forwarding_word::<VM>(object);
unsafe {
match gc_byte_offset_in_forwarding_word::<VM>() {
Some(fw_offset) => {
Address::from_usize(
status_word
& !((FORWARDING_MASK as usize)
<< (-fw_offset * constants::BITS_IN_BYTE as isize)),
)
.to_object_reference()
}
None => Address::from_usize(status_word).to_object_reference(),
}
}
} else {
panic!(
"Invalid header value 0x{:x} 0x{:x} for object{}",
gc_byte,
read_forwarding_word::<VM>(object),
object
)
}
}
pub fn forward_object<VM: VMBinding, CC: CopyContext>(
object: ObjectReference,
semantics: AllocationSemantics,
copy_context: &mut CC,
) -> ObjectReference {
let new_object = VM::VMObjectModel::copy(object, semantics, copy_context);
match gc_byte_offset_in_forwarding_word::<VM>() {
Some(fw_offset) => {
write_forwarding_word::<VM>(
object,
new_object.to_address().as_usize()
| (FORWARDED as usize) << (-fw_offset * constants::BITS_IN_BYTE as isize),
);
}
None => {
write_forwarding_word::<VM>(object, new_object.to_address().as_usize());
gc_byte::write_gc_byte::<VM>(object, FORWARDED);
}
};
new_object
}
pub fn set_forwarding_pointer<VM: VMBinding>(object: ObjectReference, ptr: ObjectReference) {
match gc_byte_offset_in_forwarding_word::<VM>() {
Some(fw_offset) => {
write_forwarding_word::<VM>(
object,
ptr.to_address().as_usize()
| (FORWARDED as usize) << (-fw_offset * constants::BITS_IN_BYTE as isize),
);
}
None => {
write_forwarding_word::<VM>(object, ptr.to_address().as_usize());
gc_byte::write_gc_byte::<VM>(object, FORWARDED);
}
}
}
pub fn is_forwarded<VM: VMBinding>(object: ObjectReference) -> bool {
gc_byte::read_gc_byte::<VM>(object) & FORWARDING_MASK == FORWARDED
}
pub fn is_forwarded_or_being_forwarded<VM: VMBinding>(object: ObjectReference) -> bool {
gc_byte::read_gc_byte::<VM>(object) & FORWARDING_MASK != 0
}
pub fn state_is_forwarded_or_being_forwarded(gc_byte: u8) -> bool {
gc_byte & FORWARDING_MASK != 0
}
pub fn state_is_being_forwarded(gc_byte: u8) -> bool {
gc_byte & FORWARDING_MASK == BEING_FORWARDED
}
pub fn clear_forwarding_bits<VM: VMBinding>(object: ObjectReference) {
let mut old_val = gc_byte::read_gc_byte::<VM>(object);
while !gc_byte::compare_exchange_gc_byte::<VM>(object, old_val, old_val & !FORWARDING_MASK) {
old_val = gc_byte::read_gc_byte::<VM>(object);
}
}
fn get_forwarding_word_address<VM: VMBinding>(object: ObjectReference) -> Address {
match gc_byte_offset_in_forwarding_word::<VM>() {
Some(fw_offset) => object.to_address() + VM::VMObjectModel::GC_BYTE_OFFSET + fw_offset,
None => {
let obj_lowest_addr = VM::VMObjectModel::object_start_ref(object);
#[cfg(not(feature = "side_gc_header"))]
{
let abs_gc_byte_offset = (object.to_address() - obj_lowest_addr) as isize
+ VM::VMObjectModel::GC_BYTE_OFFSET;
if abs_gc_byte_offset >= constants::BYTES_IN_ADDRESS as isize {
obj_lowest_addr } else {
obj_lowest_addr + constants::BYTES_IN_ADDRESS }
}
#[cfg(feature = "side_gc_header")]
{
obj_lowest_addr }
}
}
}
pub fn read_forwarding_word<VM: VMBinding>(object: ObjectReference) -> usize {
unsafe {
get_forwarding_word_address::<VM>(object).atomic_load::<AtomicUsize>(Ordering::SeqCst)
}
}
pub fn write_forwarding_word<VM: VMBinding>(object: ObjectReference, val: usize) {
trace!("GCForwardingWord::write({:#?}, {:x})\n", object, val);
unsafe {
get_forwarding_word_address::<VM>(object).atomic_store::<AtomicUsize>(val, Ordering::SeqCst)
}
}
pub fn compare_exchange_forwarding_word<VM: VMBinding>(
object: ObjectReference,
old: usize,
new: usize,
) -> bool {
let res = unsafe {
get_forwarding_word_address::<VM>(object)
.compare_exchange::<AtomicUsize>(old, new, Ordering::SeqCst, Ordering::SeqCst)
.is_ok()
};
trace!(
"\nGCForwardingWord::compare_exchange({:#?}, old = {:x}, new = {:x}) -> {}\n",
object,
old,
new,
res
);
res
}
#[cfg(target_endian = "little")]
pub(super) fn gc_byte_offset_in_forwarding_word<VM: VMBinding>() -> Option<isize> {
#[cfg(not(feature = "side_gc_header"))]
{
let gcbyte_lshift = VM::VMObjectModel::GC_BYTE_OFFSET % constants::BYTES_IN_WORD as isize;
if gcbyte_lshift == 0 {
Some(0)
} else if gcbyte_lshift == (constants::BYTES_IN_WORD - 1) as isize {
Some(1 - constants::BYTES_IN_WORD as isize)
} else {
None
}
}
#[cfg(feature = "side_gc_header")]
{
None
}
}
#[cfg(target_endian = "big")]
pub(super) fn gc_byte_offset_in_forwarding_word<VM: VMBinding>() -> Option<isize> {
unimplemented!()
}
#[cfg(debug_assertions)]
pub(crate) fn check_alloc_size<VM: VMBinding>(size: usize) {
debug_assert!(
if cfg!(feature = "side_gc_header") || gc_byte_offset_in_forwarding_word::<VM>().is_some() {
size >= constants::BYTES_IN_WORD
} else {
size >= 2 * constants::BYTES_IN_WORD
},
"allocation size (0x{:x}) is too small!",
size
);
}