Skip to content

Arm64: Implement region write barriers #111636

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

Merged
merged 52 commits into from
May 17, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
db6c2cf
Arm64: Implement region write barriers
a74nh Jan 16, 2025
5de7e0f
Fix byte region barriers
a74nh Jan 21, 2025
9315aa1
Fix bit region barriers
a74nh Jan 21, 2025
d0e46f0
test instead of cmp for bitwise write barriers
a74nh Jan 22, 2025
cb83f53
use LSE to atomically update bitwise write barriers
a74nh Jan 22, 2025
c615772
move atomics check into gcenv.ee.cpp
a74nh Jan 23, 2025
1c865f1
Skip ephemeral checks for regionless server GC, and refactor checks
a74nh Jan 23, 2025
15dde1b
Move ephemeral checks back
a74nh Jan 24, 2025
b5b28ce
Add GC-write-barriers.md
a74nh Jan 27, 2025
c27d516
More variables for the pseudo code
a74nh Jan 27, 2025
d014417
WRITE_BARRIER_CHECK instead of TARGET_ARM64
a74nh Jan 27, 2025
6068fb0
Add JIT_CheckedWriteBarrier and fixups to doc
a74nh Jan 28, 2025
fe0ab46
Merge main
a74nh Mar 4, 2025
a416437
Use writebarriermanager for arm64.
a74nh Jan 13, 2025
86c2576
Add the different write barrier functions
a74nh Feb 25, 2025
38bc2a1
Don't clear ephemeral values
a74nh Mar 4, 2025
5eed451
Only use offsets for inline variables
a74nh Mar 4, 2025
6b29574
Remove jitinterfaceamd64.cpp
a74nh Mar 4, 2025
e3bbf96
Don't use m_pRegionShrSrc on Arm64
a74nh Mar 4, 2025
091e265
Remove commented code
a74nh Mar 4, 2025
91eb26f
make writebarriermanager closer to original code
a74nh Mar 4, 2025
b947119
undo patchedcode.asm changes
a74nh Mar 4, 2025
580c16f
Add writebarriermanager.h
a74nh Mar 4, 2025
7993687
Update doc
a74nh Mar 5, 2025
90d889b
Replace JIT_WriteBarrier with zeroed memory
a74nh Mar 5, 2025
da7b5c0
Fix writebarrier macro labels
a74nh Mar 5, 2025
dc6d0a7
Fix JIT_WriteBarrier_PreGrow64 checks
a74nh Mar 5, 2025
6e66842
remove unused label
a74nh Mar 5, 2025
729cb77
Remove JIT_UpdateWriteBarrierState
a74nh Mar 5, 2025
e0759b8
first attempt at windows .asm
a74nh Mar 5, 2025
e026a06
remove wbs_begin
a74nh Mar 5, 2025
678f8e1
fix ifdefs for windows
a74nh Mar 7, 2025
5a156d3
Remove _TEXT
a74nh Mar 7, 2025
b318113
Fix macro arg passing on OSX
a74nh Mar 7, 2025
9312f3c
Fix patching arithmetic to work on MacOS
a74nh Mar 12, 2025
13bd03c
merge main
a74nh Mar 12, 2025
0f21673
Fixup comments in doc and assembly
a74nh Mar 13, 2025
48620bb
fix location casts for windows
a74nh Mar 13, 2025
d97e4c1
copy patching changes to windows
a74nh Mar 13, 2025
8f73468
fix location casts again
a74nh Mar 13, 2025
b5cbb9e
remove extra labels
a74nh Mar 14, 2025
4dc3f63
fix windows build
a74nh Mar 14, 2025
6ea99c0
fix patched constants on windows
a74nh Mar 14, 2025
75d2443
Fix RegionShr size on windows
a74nh Mar 19, 2025
08c3635
Merge main
a74nh Mar 19, 2025
1c80ab8
Merge main
a74nh Apr 7, 2025
aefc2ef
Add WRITE_BARRIER_CHECK and LSE atomics to the doc
a74nh Apr 15, 2025
8e0dbe4
merge main
a74nh Apr 17, 2025
8d164ef
doc typo fixes
a74nh Apr 22, 2025
8b02ab4
Merge main
a74nh Apr 22, 2025
328024e
Merge main
a74nh May 16, 2025
801b9cc
Merge main
a74nh May 16, 2025
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
33 changes: 18 additions & 15 deletions src/coreclr/vm/arm64/asmhelpers.S
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ PATCH_LABEL ThePreStubPatchLabel
ret lr
LEAF_END ThePreStubPatch, _TEXT

// void JIT_UpdateWriteBarrierState(bool skipEphemeralCheck, size_t writeableOffset)
// void JIT_UpdateWriteBarrierState(size_t writeableOffset)
//
// Update shadow copies of the various state info required for barrier
//
Expand All @@ -194,12 +194,11 @@ LEAF_END ThePreStubPatch, _TEXT
LEAF_ENTRY JIT_UpdateWriteBarrierState, _TEXT
PROLOG_SAVE_REG_PAIR_INDEXED fp, lr, -16

// x0-x7, x10 will contain intended new state
// x8 will preserve skipEphemeralCheck
// x0-x7, x10-x11, x13-x14 will contain intended new state
// x9 will preserve writeableOffset
// x12 will be used for pointers

mov x8, x0
mov x9, x1
mov x9, x0

PREPARE_EXTERNAL_VAR g_card_table, x12
ldr x0, [x12]
Expand All @@ -220,23 +219,27 @@ LEAF_ENTRY JIT_UpdateWriteBarrierState, _TEXT
PREPARE_EXTERNAL_VAR g_ephemeral_high, x12
ldr x4, [x12]

cbz x8, LOCAL_LABEL(EphemeralCheckEnabled)
movz x3, #0
movn x4, #0
LOCAL_LABEL(EphemeralCheckEnabled):

PREPARE_EXTERNAL_VAR g_lowest_address, x12
ldr x5, [x12]

PREPARE_EXTERNAL_VAR g_highest_address, x12
ldr x6, [x12]

PREPARE_EXTERNAL_VAR g_region_to_generation_table, x12
ldr x7, [x12]

PREPARE_EXTERNAL_VAR g_region_shr, x12
ldr w10, [x12]

PREPARE_EXTERNAL_VAR g_region_use_bitwise_write_barrier, x12
ldr w11, [x12]

#ifdef WRITE_BARRIER_CHECK
PREPARE_EXTERNAL_VAR g_GCShadow, x12
ldr x7, [x12]
ldr x13, [x12]

PREPARE_EXTERNAL_VAR g_GCShadowEnd, x12
ldr x10, [x12]
ldr x14, [x12]
#endif

// Update wbs state
Expand All @@ -247,12 +250,12 @@ LOCAL_LABEL(EphemeralCheckEnabled):
stp x0, x1, [x12], 16
stp x2, x3, [x12], 16
stp x4, x5, [x12], 16
str x6, [x12], 8
stp x6, x7, [x12], 16
stp w10, w11, [x12], 8
#ifdef WRITE_BARRIER_CHECK
stp x7, x10, [x12], 16
stp x13, x14, [x12], 16
#endif


EPILOG_RESTORE_REG_PAIR_INDEXED fp, lr, 16
EPILOG_RETURN
LEAF_END JIT_UpdateWriteBarrierState
Expand Down
62 changes: 57 additions & 5 deletions src/coreclr/vm/arm64/patchedcode.S
Original file line number Diff line number Diff line change
Expand Up @@ -142,33 +142,79 @@ LOCAL_LABEL(ShadowUpdateEnd):
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
// Update the write watch table if necessary
ldr x12, LOCAL_LABEL(wbs_sw_ww_table)
cbz x12, LOCAL_LABEL(CheckCardTable)
cbz x12, LOCAL_LABEL(CheckCardTableBounds)
add x12, x12, x14, lsr #0xc // SoftwareWriteWatch::AddressToTableByteIndexShift
ldrb w17, [x12]
cbnz x17, LOCAL_LABEL(CheckCardTable)
cbnz x17, LOCAL_LABEL(CheckCardTableBounds)
mov w17, #0xFF
strb w17, [x12]
#endif

LOCAL_LABEL(CheckCardTable):
// Branch to Exit if the reference is not in the Gen0 heap
LOCAL_LABEL(CheckCardTableBounds):
// Branch to Exit if the reference is not in the heap
ldr x12, LOCAL_LABEL(wbs_ephemeral_low)
ldr x17, LOCAL_LABEL(wbs_ephemeral_high)
cmp x15, x12
ccmp x15, x17, #0x2, hs
bhs LOCAL_LABEL(Exit)

// Region Checks

// Check if using regions
ldr x17, LOCAL_LABEL(wbs_region_to_generation_table)
cbz x17, LOCAL_LABEL(CheckCardTable)

// Calculate region locations
ldr w12, LOCAL_LABEL(wbs_region_shr)
lsr x15, x15, x12
add x15, x15, x17 // x15 = (RHS >> wbs_region_shr) + wbs_region_to_generation_table
lsr x12, x14, x12
add x12, x12, x17 // x12 = (LHS >> wbs_region_shr) + wbs_region_to_generation_table

// Check whether the region we're storing into is gen 0 - nothing to do in this case
ldrb w12, [x12]
cbz w12, LOCAL_LABEL(Exit)

// Check this is going from old to young
ldrb w15, [x15]
cmp w15, w12
bhs LOCAL_LABEL(Exit)

// Bitwise write barriers only
ldr w17, LOCAL_LABEL(wbs_region_use_bitwise_write_barrier)
cbz w17, LOCAL_LABEL(CheckCardTable)

// Check if we need to update the card table
lsr w17, w14, 8
and w17, w17, 7
movz w15, 1
lsl w17, w15, w17 // w17 = 1 << (RHS >> 8 && 7)
ldr x12, LOCAL_LABEL(wbs_card_table)
add x15, x12, x14, lsr #11
ldrb w12, [x15]
ldrb w12, [x15] // w12 = [(RHS >> 11) + g_card_table]
tst w12, w17
bne LOCAL_LABEL(Exit)

// Atomically update the card table
// Requires LSE, but the code is only compiled for 8.0
.word 0x383131FF // stsetb w17, [x15]
b LOCAL_LABEL(CheckCardBundleTable)

// End of Region Checks

LOCAL_LABEL(CheckCardTable):
// Check if we need to update the card table
ldr x12, LOCAL_LABEL(wbs_card_table)
add x15, x12, x14, lsr #11
ldrb w12, [x15] // w12 = [(RHS >> 11) + g_card_table]
cmp x12, 0xFF
beq LOCAL_LABEL(Exit)

// Update the card table
mov x12, 0xFF
strb w12, [x15]

LOCAL_LABEL(CheckCardBundleTable):
#ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
// Check if we need to update the card bundle table
ldr x12, LOCAL_LABEL(wbs_card_bundle_table)
Expand Down Expand Up @@ -208,6 +254,12 @@ LOCAL_LABEL(wbs_lowest_address):
.quad 0
LOCAL_LABEL(wbs_highest_address):
.quad 0
LOCAL_LABEL(wbs_region_to_generation_table):
.quad 0
LOCAL_LABEL(wbs_region_shr):
.word 0
LOCAL_LABEL(wbs_region_use_bitwise_write_barrier):
.word 0
#ifdef WRITE_BARRIER_CHECK
LOCAL_LABEL(wbs_GCShadow):
.quad 0
Expand Down
19 changes: 10 additions & 9 deletions src/coreclr/vm/arm64/stubs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -865,12 +865,12 @@ void JIT_TailCall()
}

#if !defined(DACCESS_COMPILE)
EXTERN_C void JIT_UpdateWriteBarrierState(bool skipEphemeralCheck, size_t writeableOffset);
EXTERN_C void JIT_UpdateWriteBarrierState(size_t writeableOffset);

extern "C" void STDCALL JIT_PatchedCodeStart();
extern "C" void STDCALL JIT_PatchedCodeLast();

static void UpdateWriteBarrierState(bool skipEphemeralCheck)
static void UpdateWriteBarrierState()
{
BYTE *writeBarrierCodeStart = GetWriteBarrierCodeLocation((void*)JIT_PatchedCodeStart);
BYTE *writeBarrierCodeStartRW = writeBarrierCodeStart;
Expand All @@ -880,7 +880,8 @@ static void UpdateWriteBarrierState(bool skipEphemeralCheck)
writeBarrierWriterHolder.AssignExecutableWriterHolder(writeBarrierCodeStart, (BYTE*)JIT_PatchedCodeLast - (BYTE*)JIT_PatchedCodeStart);
writeBarrierCodeStartRW = writeBarrierWriterHolder.GetRW();
}
JIT_UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap(), writeBarrierCodeStartRW - writeBarrierCodeStart);

JIT_UpdateWriteBarrierState(writeBarrierCodeStartRW - writeBarrierCodeStart);
}

void InitJITHelpers1()
Expand Down Expand Up @@ -909,12 +910,12 @@ void InitJITHelpers1()
}
}

UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
UpdateWriteBarrierState();
}


#else
void UpdateWriteBarrierState(bool) {}
void UpdateWriteBarrierState() {}
#endif // !defined(DACCESS_COMPILE)

PTR_CONTEXT GetCONTEXTFromRedirectedStubStackFrame(T_DISPATCHER_CONTEXT * pDispatcherContext)
Expand Down Expand Up @@ -1076,26 +1077,26 @@ void FlushWriteBarrierInstructionCache()

int StompWriteBarrierEphemeral(bool isRuntimeSuspended)
{
UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
UpdateWriteBarrierState();
return SWB_PASS;
}

int StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck)
{
UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
UpdateWriteBarrierState();
return SWB_PASS;
}

#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
int SwitchToWriteWatchBarrier(bool isRuntimeSuspended)
{
UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
UpdateWriteBarrierState();
return SWB_PASS;
}

int SwitchToNonWriteWatchBarrier(bool isRuntimeSuspended)
{
UpdateWriteBarrierState(GCHeapUtilities::IsServerHeap());
UpdateWriteBarrierState();
return SWB_PASS;
}
#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
Expand Down
37 changes: 35 additions & 2 deletions src/coreclr/vm/gcenv.ee.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args)
ThreadSuspend::RestartEE(FALSE, TRUE);
}
return; // unlike other branches we have already done cleanup so bailing out here

case WriteBarrierOp::StompEphemeral:
assert(args->is_runtime_suspended && "the runtime must be suspended here!");
// StompEphemeral requires a new ephemeral low and a new ephemeral high
Expand All @@ -1063,8 +1064,22 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args)
g_region_to_generation_table = args->region_to_generation_table;
g_region_shr = args->region_shr;
g_region_use_bitwise_write_barrier = args->region_use_bitwise_write_barrier;
#if defined(HOST_ARM64)
// Only allow bitwise write barriers if LSE atomics are present
if (!g_arm64_atomics_present)
{
g_region_use_bitwise_write_barrier = false;
}
// Skip ephemeral checks for regionless server GC
if (GCHeapUtilities::IsServerHeap() && g_region_to_generation_table == nullptr)
{
g_ephemeral_low = 0;
g_ephemeral_high = 0;
}
#endif
stompWBCompleteActions |= ::StompWriteBarrierEphemeral(args->is_runtime_suspended);
break;

case WriteBarrierOp::Initialize:
assert(args->is_runtime_suspended && "the runtime must be suspended here!");
// This operation should only be invoked once, upon initialization.
Expand All @@ -1090,16 +1105,32 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args)
g_region_to_generation_table = args->region_to_generation_table;
g_region_shr = args->region_shr;
g_region_use_bitwise_write_barrier = args->region_use_bitwise_write_barrier;
g_ephemeral_low = args->ephemeral_low;
g_ephemeral_high = args->ephemeral_high;
#if defined(HOST_ARM64)
// Only allow bitwise write barriers if LSE atomics are present
if (!g_arm64_atomics_present)
{
g_region_use_bitwise_write_barrier = false;
}
// Skip ephemeral checks for regionless server GC
if (GCHeapUtilities::IsServerHeap() && g_region_to_generation_table == nullptr)
{
g_ephemeral_low = 0;
g_ephemeral_high = 0;
}
#endif
stompWBCompleteActions |= ::StompWriteBarrierResize(true, false);

#if !defined(HOST_ARM64)
// StompWriteBarrierResize does not necessarily bash g_ephemeral_low
// usages, so we must do so here. This is particularly true on x86,
// where StompWriteBarrierResize will not bash g_ephemeral_low when
// called with the parameters (true, false), as it is above.
g_ephemeral_low = args->ephemeral_low;
g_ephemeral_high = args->ephemeral_high;
stompWBCompleteActions |= ::StompWriteBarrierEphemeral(true);
#endif
break;

case WriteBarrierOp::SwitchToWriteWatch:
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
assert(args->is_runtime_suspended && "the runtime must be suspended here!");
Expand All @@ -1111,6 +1142,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args)
assert(!"should never be called without FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP");
#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
break;

case WriteBarrierOp::SwitchToNonWriteWatch:
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
assert(args->is_runtime_suspended && "the runtime must be suspended here!");
Expand All @@ -1121,6 +1153,7 @@ void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args)
assert(!"should never be called without FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP");
#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
break;

default:
assert(!"unknown WriteBarrierOp enum");
}
Expand Down
Loading