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

Support VO bit #76

Merged
merged 4 commits into from
Dec 3, 2024
Merged
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
9 changes: 8 additions & 1 deletion src/gc-interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,14 @@ JL_DLLEXPORT void *jl_gc_perm_alloc(size_t sz, int zero, unsigned align,
// immortal region that is never swept. The second parameter specifies the type of the
// object being allocated and will be used to set the object header.
struct _jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT;

// permanently allocates a symbol (jl_sym_t). The object needs to be word aligned,
// and tagged with jl_sym_tag.
// FIXME: Ideally we should merge this with jl_gc_permobj, as symbol is an object.
// Currently there are a few differences between the two functions, and refactoring is needed.
// 1. sz for this function includes the object header, and sz for jl_gc_permobj excludes the header size.
// 2. align for this function is word align, and align for jl_gc_permobj depends on the allocation size.
// 3. ty for this function is jl_symbol_tag << 4, and ty for jl_gc_permobj is a datatype pointer.
struct _jl_value_t *jl_gc_permsymbol(size_t sz) JL_NOTSAFEPOINT;
// This function notifies the GC about memory addresses that are set when loading the boot image.
// The GC may use that information to, for instance, determine that all objects in that chunk of memory should
// be treated as marked and belonged to the old generation in nursery collections.
Expand Down
47 changes: 31 additions & 16 deletions src/gc-mmtk.c
Original file line number Diff line number Diff line change
Expand Up @@ -860,18 +860,28 @@ inline void* bump_alloc_fast(MMTkMutatorContext* mutator, uintptr_t* cursor, uin
}
}

inline void mmtk_set_side_metadata(const void* side_metadata_base, void* obj) {
intptr_t addr = (intptr_t) obj;
uint8_t* meta_addr = (uint8_t*) side_metadata_base + (addr >> 6);
intptr_t shift = (addr >> 3) & 0b111;
while(1) {
uint8_t old_val = *meta_addr;
uint8_t new_val = old_val | (1 << shift);
if (jl_atomic_cmpswap((_Atomic(uint8_t)*)meta_addr, &old_val, new_val)) {
break;
}
}
}

inline void* mmtk_immix_alloc_fast(MMTkMutatorContext* mutator, size_t size, size_t align, size_t offset) {
ImmixAllocator* allocator = &mutator->allocators.immix[MMTK_DEFAULT_IMMIX_ALLOCATOR];
return bump_alloc_fast(mutator, (uintptr_t*)&allocator->cursor, (intptr_t)allocator->limit, size, align, offset, 0);
}

inline void mmtk_immix_post_alloc_slow(MMTkMutatorContext* mutator, void* obj, size_t size) {
Copy link

Choose a reason for hiding this comment

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

Wouldn't be useful to keep this for debugging purposes? That was the only reason I left it there --- I think you had something like that for the write barriers too.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. It would be useful in some cases. It allows us to do post alloc in Rust, and it is independent from alloc (e.g. inlined alloc with a post alloc call). However, I am not sure if this is really useful. Without it, we can still disable in lined alloc, and do alloc call which includes post alloc.

Besides, if we would like to do this 'post_alloc_slow' call, we will need to make it visible to the codegen. Julia does not differentiate alloc and post alloc in its own GC (post alloc is just a part of the alloc call). In the GC interface, there is no separate post alloc and we do post alloc in those alloc functions as well. So while we can use the term post alloc in MMTk code, I feel it a bit confusing to introduce the term 'post alloc' to Julia.

So it seems it is unclear whether it would be useful to allow post alloc calls for debugging, and it would be confusing to the code base to have it implemented. I think it might be a good idea to just remove it.

mmtk_post_alloc(mutator, obj, size, 0);
}

inline void mmtk_immix_post_alloc_fast(MMTkMutatorContext* mutator, void* obj, size_t size) {
// FIXME: for now, we do nothing
// but when supporting moving, this is where we set the valid object (VO) bit
if (MMTK_NEEDS_VO_BIT) {
mmtk_set_side_metadata(MMTK_SIDE_VO_BIT_BASE_ADDRESS, obj);
}
}

inline void* mmtk_immortal_alloc_fast(MMTkMutatorContext* mutator, size_t size, size_t align, size_t offset) {
Expand All @@ -881,16 +891,11 @@ inline void* mmtk_immortal_alloc_fast(MMTkMutatorContext* mutator, size_t size,

inline void mmtk_immortal_post_alloc_fast(MMTkMutatorContext* mutator, void* obj, size_t size) {
if (MMTK_NEEDS_WRITE_BARRIER == MMTK_OBJECT_BARRIER) {
intptr_t addr = (intptr_t) obj;
uint8_t* meta_addr = (uint8_t*) (MMTK_SIDE_LOG_BIT_BASE_ADDRESS) + (addr >> 6);
intptr_t shift = (addr >> 3) & 0b111;
while(1) {
uint8_t old_val = *meta_addr;
uint8_t new_val = old_val | (1 << shift);
if (jl_atomic_cmpswap((_Atomic(uint8_t)*)meta_addr, &old_val, new_val)) {
break;
}
}
mmtk_set_side_metadata(MMTK_SIDE_LOG_BIT_BASE_ADDRESS, obj);
}

if (MMTK_NEEDS_VO_BIT) {
mmtk_set_side_metadata(MMTK_SIDE_VO_BIT_BASE_ADDRESS, obj);
}
}

Expand Down Expand Up @@ -1069,6 +1074,16 @@ jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT
return jl_valueof(o);
}

jl_value_t *jl_gc_permsymbol(size_t sz) JL_NOTSAFEPOINT
{
jl_taggedvalue_t *tag = (jl_taggedvalue_t*)jl_gc_perm_alloc(sz, 0, sizeof(void*), 0);
jl_value_t *sym = jl_valueof(tag);
jl_ptls_t ptls = jl_current_task->ptls;
jl_set_typetagof(sym, jl_symbol_tag, 0); // We need to set symbol tag. The GC tag doesnt matter.
mmtk_immortal_post_alloc_fast(&ptls->gc_tls.mmtk_mutator, sym, sz);
return sym;
}

JL_DLLEXPORT void *jl_gc_managed_malloc(size_t sz)
{
jl_ptls_t ptls = jl_current_task->ptls;
Expand Down
9 changes: 9 additions & 0 deletions src/gc-stock.c
Original file line number Diff line number Diff line change
Expand Up @@ -3836,6 +3836,15 @@ jl_value_t *jl_gc_permobj(size_t sz, void *ty) JL_NOTSAFEPOINT
return jl_valueof(o);
}

jl_value_t *jl_gc_permsymbol(size_t sz) JL_NOTSAFEPOINT
{
jl_taggedvalue_t *tag = (jl_taggedvalue_t*)jl_gc_perm_alloc(sz, 0, sizeof(void*), 0);
jl_value_t *sym = jl_valueof(tag);
// set to old marked so that we won't look at it in the GC or write barrier.
jl_set_typetagof(sym, jl_symbol_tag, GC_OLD_MARKED);
return sym;
}

JL_DLLEXPORT int jl_gc_enable_conservative_gc_support(void)
{
if (jl_is_initialized()) {
Expand Down
6 changes: 6 additions & 0 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -2748,7 +2748,13 @@ extern void mmtk_object_reference_write_slow(void* mutator, const void* parent,
#define MMTK_DEFAULT_IMMIX_ALLOCATOR (0)
#define MMTK_IMMORTAL_BUMP_ALLOCATOR (0)

// VO bit is required to support conservative stack scanning and moving.
#define MMTK_NEEDS_VO_BIT (1)

void mmtk_immortal_post_alloc_fast(MMTkMutatorContext* mutator, void* obj, size_t size);

extern const void* MMTK_SIDE_LOG_BIT_BASE_ADDRESS;
extern const void* MMTK_SIDE_VO_BIT_BASE_ADDRESS;

// Directly call into MMTk for write barrier (debugging only)
STATIC_INLINE void mmtk_gc_wb_full(const void *parent, const void *ptr) JL_NOTSAFEPOINT
Expand Down
31 changes: 31 additions & 0 deletions src/llvm-late-gc-lowering-mmtk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,37 @@ Value* LateLowerGCFrame::lowerGCAllocBytesLate(CallInst *target, Function &F)

auto v_raw = builder.CreateNSWAdd(result, ConstantInt::get(Type::getInt64Ty(target->getContext()), sizeof(jl_taggedvalue_t)));
auto v_as_ptr = builder.CreateIntToPtr(v_raw, smallAllocFunc->getReturnType());

// Post alloc
if (MMTK_NEEDS_VO_BIT) {
auto intptr_ty = Type::getInt64Ty(target->getContext());
auto i8_ty = Type::getInt8Ty(F.getContext());
intptr_t metadata_base_address = reinterpret_cast<intptr_t>(MMTK_SIDE_VO_BIT_BASE_ADDRESS);
auto metadata_base_val = ConstantInt::get(intptr_ty, metadata_base_address);
auto metadata_base_ptr = ConstantExpr::getIntToPtr(metadata_base_val, PointerType::get(i8_ty, 0));

// intptr_t addr = (intptr_t) v;
auto addr = v_raw;

// uint8_t* vo_meta_addr = (uint8_t*) (MMTK_SIDE_VO_BIT_BASE_ADDRESS) + (addr >> 6);
auto shr = builder.CreateLShr(addr, ConstantInt::get(intptr_ty, 6));
auto metadata_ptr = builder.CreateGEP(i8_ty, metadata_base_ptr, shr);

// intptr_t shift = (addr >> 3) & 0b111;
auto shift = builder.CreateAnd(builder.CreateLShr(addr, ConstantInt::get(intptr_ty, 3)), ConstantInt::get(intptr_ty, 7));

// uint8_t byte_val = *vo_meta_addr;
auto byte_val = builder.CreateAlignedLoad(i8_ty, metadata_ptr, Align());

// uint8_t new_val = byte_val | (1 << shift);
auto shifted_val = builder.CreateShl(ConstantInt::get(intptr_ty, 1), shift);
auto shifted_val_i8 = builder.CreateTruncOrBitCast(shifted_val, i8_ty);
auto new_val = builder.CreateOr(byte_val, shifted_val_i8);

// (*vo_meta_addr) = new_val;
builder.CreateStore(new_val, metadata_ptr);
}

builder.CreateBr(next_instr->getParent());

phiNode->addIncoming(new_call, slowpath);
Expand Down
7 changes: 2 additions & 5 deletions src/symbol.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "julia.h"
#include "julia_internal.h"
#include "julia_assert.h"
#include "gc-interface.h"

#ifdef __cplusplus
extern "C" {
Expand All @@ -34,12 +35,8 @@ static size_t symbol_nbytes(size_t len) JL_NOTSAFEPOINT

static jl_sym_t *mk_symbol(const char *str, size_t len) JL_NOTSAFEPOINT
{
jl_sym_t *sym;
size_t nb = symbol_nbytes(len);
jl_taggedvalue_t *tag = (jl_taggedvalue_t*)jl_gc_perm_alloc(nb, 0, sizeof(void*), 0);
sym = (jl_sym_t*)jl_valueof(tag);
// set to old marked so that we won't look at it in the GC or write barrier.
jl_set_typetagof(sym, jl_symbol_tag, GC_OLD_MARKED);
jl_sym_t *sym = (jl_sym_t*)jl_gc_permsymbol(nb);
jl_atomic_store_relaxed(&sym->left, NULL);
jl_atomic_store_relaxed(&sym->right, NULL);
sym->hash = hash_symbol(str, len);
Expand Down
Loading