diff options
author | Koundinya Veluri <kouvel@users.noreply.github.com> | 2016-04-12 21:19:01 -0700 |
---|---|---|
committer | Koundinya Veluri <kouvel@users.noreply.github.com> | 2016-04-12 21:19:01 -0700 |
commit | afdce3a592e5f6f2047bed057d121225be91743d (patch) | |
tree | 7ab5637597f3daae58dbf1929abd363cbfe0b619 /src/vm/amd64 | |
parent | 5ef243f597b8198efce1add587f16aae59b1f568 (diff) | |
parent | c235ae17cd3a87f8032948bdcb838641d8e6c055 (diff) | |
download | coreclr-afdce3a592e5f6f2047bed057d121225be91743d.tar.gz coreclr-afdce3a592e5f6f2047bed057d121225be91743d.tar.bz2 coreclr-afdce3a592e5f6f2047bed057d121225be91743d.zip |
Merge pull request #4074 from kouvel/SoftwareWriteWatch
Implement software write watch and make concurrent GC functional outs…
Diffstat (limited to 'src/vm/amd64')
-rw-r--r-- | src/vm/amd64/JitHelpers_Fast.asm | 84 | ||||
-rw-r--r-- | src/vm/amd64/JitHelpers_FastWriteBarriers.asm | 228 | ||||
-rw-r--r-- | src/vm/amd64/JitHelpers_Slow.asm | 18 | ||||
-rw-r--r-- | src/vm/amd64/jithelpers_fast.S | 160 | ||||
-rw-r--r-- | src/vm/amd64/jithelpers_fastwritebarriers.S | 247 | ||||
-rw-r--r-- | src/vm/amd64/jithelpers_slow.S | 15 | ||||
-rw-r--r-- | src/vm/amd64/jitinterfaceamd64.cpp | 455 |
7 files changed, 817 insertions, 390 deletions
diff --git a/src/vm/amd64/JitHelpers_Fast.asm b/src/vm/amd64/JitHelpers_Fast.asm index 8e39a6d39f..90185205af 100644 --- a/src/vm/amd64/JitHelpers_Fast.asm +++ b/src/vm/amd64/JitHelpers_Fast.asm @@ -27,6 +27,11 @@ EXTERN g_lowest_address:QWORD EXTERN g_highest_address:QWORD EXTERN g_card_table:QWORD +ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +EXTERN g_sw_ww_table:QWORD +EXTERN g_sw_ww_enabled_for_gc_heap:BYTE +endif + ifdef WRITE_BARRIER_CHECK ; Those global variables are always defined, but should be 0 for Server GC g_GCShadow TEXTEQU <?g_GCShadow@@3PEAEEA> @@ -466,6 +471,67 @@ ifdef _DEBUG jmp JIT_WriteBarrier_Debug endif +ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + ; JIT_WriteBarrier_WriteWatch_PostGrow64 + + ; Regarding patchable constants: + ; - 64-bit constants have to be loaded into a register + ; - The constants have to be aligned to 8 bytes so that they can be patched easily + ; - The constant loads have been located to minimize NOP padding required to align the constants + ; - Using different registers for successive constant loads helps pipeline better. Should we decide to use a special + ; non-volatile calling convention, this should be changed to use just one register. + + ; Do the move into the GC . It is correct to take an AV here, the EH code + ; figures out that this came from a WriteBarrier and correctly maps it back + ; to the managed method which called the WriteBarrier (see setup in + ; InitializeExceptionHandling, vm\exceptionhandling.cpp). + mov [rcx], rdx + + ; Update the write watch table if necessary + mov rax, rcx + mov r8, 0F0F0F0F0F0F0F0F0h + shr rax, 0Ch ; SoftwareWriteWatch::AddressToTableByteIndexShift + NOP_2_BYTE ; padding for alignment of constant + mov r9, 0F0F0F0F0F0F0F0F0h + add rax, r8 + cmp byte ptr [rax], 0h + jne CheckCardTable + mov byte ptr [rax], 0FFh + + NOP_3_BYTE ; padding for alignment of constant + + ; Check the lower and upper ephemeral region bounds + CheckCardTable: + cmp rdx, r9 + jb Exit + + NOP_3_BYTE ; padding for alignment of constant + + mov r8, 0F0F0F0F0F0F0F0F0h + + cmp rdx, r8 + jae Exit + + nop ; padding for alignment of constant + + mov rax, 0F0F0F0F0F0F0F0F0h + + ; Touch the card table entry, if not already dirty. + shr rcx, 0Bh + cmp byte ptr [rcx + rax], 0FFh + jne UpdateCardTable + REPRET + + UpdateCardTable: + mov byte ptr [rcx + rax], 0FFh + ret + + align 16 + Exit: + REPRET +else + ; JIT_WriteBarrier_PostGrow64 + ; Do the move into the GC . It is correct to take an AV here, the EH code ; figures out that this came from a WriteBarrier and correctly maps it back ; to the managed method which called the WriteBarrier (see setup in @@ -510,6 +576,8 @@ endif align 16 Exit: REPRET +endif + ; make sure this guy is bigger than any of the other guys align 16 nop @@ -577,7 +645,8 @@ LEAF_END JIT_PatchedCodeLast, _TEXT ; Entry: ; RDI - address of ref-field (assigned to) ; RSI - address of the data (source) -; RCX can be trashed +; RCX is trashed +; RAX is trashed when FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP is defined ; Exit: ; RDI, RSI are incremented by SIZEOF(LPVOID) LEAF_ENTRY JIT_ByRefWriteBarrier, _TEXT @@ -653,7 +722,20 @@ ifdef WRITE_BARRIER_CHECK pop r10 endif +ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + ; Update the write watch table if necessary + cmp byte ptr [g_sw_ww_enabled_for_gc_heap], 0h + je CheckCardTable + mov rax, rdi + shr rax, 0Ch ; SoftwareWriteWatch::AddressToTableByteIndexShift + add rax, qword ptr [g_sw_ww_table] + cmp byte ptr [rax], 0h + jne CheckCardTable + mov byte ptr [rax], 0FFh +endif + ; See if we can just quick out + CheckCardTable: cmp rcx, [g_ephemeral_low] jb Exit cmp rcx, [g_ephemeral_high] diff --git a/src/vm/amd64/JitHelpers_FastWriteBarriers.asm b/src/vm/amd64/JitHelpers_FastWriteBarriers.asm index 17730142ed..07e985f94f 100644 --- a/src/vm/amd64/JitHelpers_FastWriteBarriers.asm +++ b/src/vm/amd64/JitHelpers_FastWriteBarriers.asm @@ -41,39 +41,6 @@ include asmconstants.inc ; (card table, ephemeral region ranges, etc) are naturally aligned since ; there are codepaths that will overwrite these values while the EE is running. ; -LEAF_ENTRY JIT_WriteBarrier_PreGrow32, _TEXT - align 4 - ; Do the move into the GC . It is correct to take an AV here, the EH code - ; figures out that this came from a WriteBarrier and correctly maps it back - ; to the managed method which called the WriteBarrier (see setup in - ; InitializeExceptionHandling, vm\exceptionhandling.cpp). - mov [rcx], rdx - - NOP_2_BYTE ; padding for alignment of constant - -PATCH_LABEL JIT_WriteBarrier_PreGrow32_PatchLabel_Lower - cmp rdx, 0F0F0F0F0h - jb Exit - - shr rcx, 0Bh -PATCH_LABEL JIT_WriteBarrier_PreGrow32_PatchLabel_CardTable_Check - cmp byte ptr [rcx + 0F0F0F0F0h], 0FFh - jne UpdateCardTable - REPRET - - nop ; padding for alignment of constant - -PATCH_LABEL JIT_WriteBarrier_PreGrow32_PatchLabel_CardTable_Update - UpdateCardTable: - mov byte ptr [rcx + 0F0F0F0F0h], 0FFh - ret - - align 16 - Exit: - REPRET -LEAF_END_MARKED JIT_WriteBarrier_PreGrow32, _TEXT - - LEAF_ENTRY JIT_WriteBarrier_PreGrow64, _TEXT align 8 ; Do the move into the GC . It is correct to take an AV here, the EH code @@ -165,57 +132,107 @@ PATCH_LABEL JIT_WriteBarrier_PostGrow64_Patch_Label_CardTable REPRET LEAF_END_MARKED JIT_WriteBarrier_PostGrow64, _TEXT -LEAF_ENTRY JIT_WriteBarrier_PostGrow32, _TEXT - align 4 + +ifdef FEATURE_SVR_GC + +LEAF_ENTRY JIT_WriteBarrier_SVR64, _TEXT + align 8 + ; + ; SVR GC has multiple heaps, so it cannot provide one single + ; ephemeral region to bounds check against, so we just skip the + ; bounds checking all together and do our card table update + ; unconditionally. + ; + ; Do the move into the GC . It is correct to take an AV here, the EH code ; figures out that this came from a WriteBarrier and correctly maps it back ; to the managed method which called the WriteBarrier (see setup in ; InitializeExceptionHandling, vm\exceptionhandling.cpp). mov [rcx], rdx - NOP_2_BYTE ; padding for alignment of constant + NOP_3_BYTE ; padding for alignment of constant - ; Check the lower and upper ephemeral region bounds +PATCH_LABEL JIT_WriteBarrier_SVR64_PatchLabel_CardTable + mov rax, 0F0F0F0F0F0F0F0F0h -PATCH_LABEL JIT_WriteBarrier_PostGrow32_PatchLabel_Lower - cmp rdx, 0F0F0F0F0h - jb Exit + shr rcx, 0Bh - NOP_3_BYTE ; padding for alignment of constant + cmp byte ptr [rcx + rax], 0FFh + jne UpdateCardTable + REPRET -PATCH_LABEL JIT_WriteBarrier_PostGrow32_PatchLabel_Upper - cmp rdx, 0F0F0F0F0h - jae Exit + UpdateCardTable: + mov byte ptr [rcx + rax], 0FFh + ret +LEAF_END_MARKED JIT_WriteBarrier_SVR64, _TEXT + +endif + + +ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + +LEAF_ENTRY JIT_WriteBarrier_WriteWatch_PreGrow64, _TEXT + align 8 + + ; Regarding patchable constants: + ; - 64-bit constants have to be loaded into a register + ; - The constants have to be aligned to 8 bytes so that they can be patched easily + ; - The constant loads have been located to minimize NOP padding required to align the constants + ; - Using different registers for successive constant loads helps pipeline better. Should we decide to use a special + ; non-volatile calling convention, this should be changed to use just one register. + + ; Do the move into the GC . It is correct to take an AV here, the EH code + ; figures out that this came from a WriteBarrier and correctly maps it back + ; to the managed method which called the WriteBarrier (see setup in + ; InitializeExceptionHandling, vm\exceptionhandling.cpp). + mov [rcx], rdx + + ; Update the write watch table if necessary + mov rax, rcx +PATCH_LABEL JIT_WriteBarrier_WriteWatch_PreGrow64_Patch_Label_WriteWatchTable + mov r8, 0F0F0F0F0F0F0F0F0h + shr rax, 0Ch ; SoftwareWriteWatch::AddressToTableByteIndexShift + NOP_2_BYTE ; padding for alignment of constant +PATCH_LABEL JIT_WriteBarrier_WriteWatch_PreGrow64_Patch_Label_Lower + mov r9, 0F0F0F0F0F0F0F0F0h + add rax, r8 + cmp byte ptr [rax], 0h + jne CheckCardTable + mov byte ptr [rax], 0FFh + + ; Check the lower ephemeral region bound. + CheckCardTable: + cmp rdx, r9 + jb Exit ; Touch the card table entry, if not already dirty. shr rcx, 0Bh - -PATCH_LABEL JIT_WriteBarrier_PostGrow32_PatchLabel_CheckCardTable - cmp byte ptr [rcx + 0F0F0F0F0h], 0FFh + NOP_2_BYTE ; padding for alignment of constant +PATCH_LABEL JIT_WriteBarrier_WriteWatch_PreGrow64_Patch_Label_CardTable + mov rax, 0F0F0F0F0F0F0F0F0h + cmp byte ptr [rcx + rax], 0FFh jne UpdateCardTable REPRET - nop ; padding for alignment of constant - -PATCH_LABEL JIT_WriteBarrier_PostGrow32_PatchLabel_UpdateCardTable UpdateCardTable: - mov byte ptr [rcx + 0F0F0F0F0h], 0FFh + mov byte ptr [rcx + rax], 0FFh ret align 16 Exit: REPRET -LEAF_END_MARKED JIT_WriteBarrier_PostGrow32, _TEXT +LEAF_END_MARKED JIT_WriteBarrier_WriteWatch_PreGrow64, _TEXT -LEAF_ENTRY JIT_WriteBarrier_SVR32, _TEXT - align 4 - ; - ; SVR GC has multiple heaps, so it cannot provide one single - ; ephemeral region to bounds check against, so we just skip the - ; bounds checking all together and do our card table update - ; unconditionally. - ; +LEAF_ENTRY JIT_WriteBarrier_WriteWatch_PostGrow64, _TEXT + align 8 + + ; Regarding patchable constants: + ; - 64-bit constants have to be loaded into a register + ; - The constants have to be aligned to 8 bytes so that they can be patched easily + ; - The constant loads have been located to minimize NOP padding required to align the constants + ; - Using different registers for successive constant loads helps pipeline better. Should we decide to use a special + ; non-volatile calling convention, this should be changed to use just one register. ; Do the move into the GC . It is correct to take an AV here, the EH code ; figures out that this came from a WriteBarrier and correctly maps it back @@ -223,25 +240,67 @@ LEAF_ENTRY JIT_WriteBarrier_SVR32, _TEXT ; InitializeExceptionHandling, vm\exceptionhandling.cpp). mov [rcx], rdx - shr rcx, 0Bh + ; Update the write watch table if necessary + mov rax, rcx +PATCH_LABEL JIT_WriteBarrier_WriteWatch_PostGrow64_Patch_Label_WriteWatchTable + mov r8, 0F0F0F0F0F0F0F0F0h + shr rax, 0Ch ; SoftwareWriteWatch::AddressToTableByteIndexShift + NOP_2_BYTE ; padding for alignment of constant +PATCH_LABEL JIT_WriteBarrier_WriteWatch_PostGrow64_Patch_Label_Lower + mov r9, 0F0F0F0F0F0F0F0F0h + add rax, r8 + cmp byte ptr [rax], 0h + jne CheckCardTable + mov byte ptr [rax], 0FFh NOP_3_BYTE ; padding for alignment of constant -PATCH_LABEL JIT_WriteBarrier_SVR32_PatchLabel_CheckCardTable - cmp byte ptr [rcx + 0F0F0F0F0h], 0FFh - jne UpdateCardTable - REPRET + ; Check the lower and upper ephemeral region bounds + CheckCardTable: + cmp rdx, r9 + jb Exit + + NOP_3_BYTE ; padding for alignment of constant + +PATCH_LABEL JIT_WriteBarrier_WriteWatch_PostGrow64_Patch_Label_Upper + mov r8, 0F0F0F0F0F0F0F0F0h + + cmp rdx, r8 + jae Exit nop ; padding for alignment of constant -PATCH_LABEL JIT_WriteBarrier_SVR32_PatchLabel_UpdateCardTable +PATCH_LABEL JIT_WriteBarrier_WriteWatch_PostGrow64_Patch_Label_CardTable + mov rax, 0F0F0F0F0F0F0F0F0h + + ; Touch the card table entry, if not already dirty. + shr rcx, 0Bh + cmp byte ptr [rcx + rax], 0FFh + jne UpdateCardTable + REPRET + UpdateCardTable: - mov byte ptr [rcx + 0F0F0F0F0h], 0FFh + mov byte ptr [rcx + rax], 0FFh ret -LEAF_END_MARKED JIT_WriteBarrier_SVR32, _TEXT -LEAF_ENTRY JIT_WriteBarrier_SVR64, _TEXT + align 16 + Exit: + REPRET +LEAF_END_MARKED JIT_WriteBarrier_WriteWatch_PostGrow64, _TEXT + + +ifdef FEATURE_SVR_GC + +LEAF_ENTRY JIT_WriteBarrier_WriteWatch_SVR64, _TEXT align 8 + + ; Regarding patchable constants: + ; - 64-bit constants have to be loaded into a register + ; - The constants have to be aligned to 8 bytes so that they can be patched easily + ; - The constant loads have been located to minimize NOP padding required to align the constants + ; - Using different registers for successive constant loads helps pipeline better. Should we decide to use a special + ; non-volatile calling convention, this should be changed to use just one register. + ; ; SVR GC has multiple heaps, so it cannot provide one single ; ephemeral region to bounds check against, so we just skip the @@ -255,21 +314,32 @@ LEAF_ENTRY JIT_WriteBarrier_SVR64, _TEXT ; InitializeExceptionHandling, vm\exceptionhandling.cpp). mov [rcx], rdx - NOP_3_BYTE ; padding for alignment of constant - -PATCH_LABEL JIT_WriteBarrier_SVR64_PatchLabel_CardTable - mov rax, 0F0F0F0F0F0F0F0F0h - + ; Update the write watch table if necessary + mov rax, rcx +PATCH_LABEL JIT_WriteBarrier_WriteWatch_SVR64_PatchLabel_WriteWatchTable + mov r8, 0F0F0F0F0F0F0F0F0h + shr rax, 0Ch ; SoftwareWriteWatch::AddressToTableByteIndexShift + NOP_2_BYTE ; padding for alignment of constant +PATCH_LABEL JIT_WriteBarrier_WriteWatch_SVR64_PatchLabel_CardTable + mov r9, 0F0F0F0F0F0F0F0F0h + add rax, r8 + cmp byte ptr [rax], 0h + jne CheckCardTable + mov byte ptr [rax], 0FFh + + CheckCardTable: shr rcx, 0Bh - - cmp byte ptr [rcx + rax], 0FFh + cmp byte ptr [rcx + r9], 0FFh jne UpdateCardTable REPRET UpdateCardTable: - mov byte ptr [rcx + rax], 0FFh + mov byte ptr [rcx + r9], 0FFh ret -LEAF_END_MARKED JIT_WriteBarrier_SVR64, _TEXT +LEAF_END_MARKED JIT_WriteBarrier_WriteWatch_SVR64, _TEXT - end +endif +endif + + end diff --git a/src/vm/amd64/JitHelpers_Slow.asm b/src/vm/amd64/JitHelpers_Slow.asm index 51829cad42..64b9a82e61 100644 --- a/src/vm/amd64/JitHelpers_Slow.asm +++ b/src/vm/amd64/JitHelpers_Slow.asm @@ -28,6 +28,11 @@ EXTERN g_lowest_address:QWORD EXTERN g_highest_address:QWORD EXTERN g_card_table:QWORD +ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +EXTERN g_sw_ww_table:QWORD +EXTERN g_sw_ww_enabled_for_gc_heap:BYTE +endif + ifdef WRITE_BARRIER_CHECK ; Those global variables are always defined, but should be 0 for Server GC g_GCShadow TEXTEQU <?g_GCShadow@@3PEAEEA> @@ -118,6 +123,19 @@ ifdef WRITE_BARRIER_CHECK DoneShadow: endif +ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + ; Update the write watch table if necessary + cmp byte ptr [g_sw_ww_enabled_for_gc_heap], 0h + je CheckCardTable + mov r10, rcx + shr r10, 0Ch ; SoftwareWriteWatch::AddressToTableByteIndexShift + add r10, qword ptr [g_sw_ww_table] + cmp byte ptr [r10], 0h + jne CheckCardTable + mov byte ptr [r10], 0FFh +endif + + CheckCardTable: ; See if we can just quick out cmp rax, [g_ephemeral_low] jb Exit diff --git a/src/vm/amd64/jithelpers_fast.S b/src/vm/amd64/jithelpers_fast.S index 22f21bb8de..a0650759f6 100644 --- a/src/vm/amd64/jithelpers_fast.S +++ b/src/vm/amd64/jithelpers_fast.S @@ -10,6 +10,45 @@ LEAF_ENTRY JIT_PatchedCodeStart, _TEXT ret LEAF_END JIT_PatchedCodeStart, _TEXT + +// There is an even more optimized version of these helpers possible which takes +// advantage of knowledge of which way the ephemeral heap is growing to only do 1/2 +// that check (this is more significant in the JIT_WriteBarrier case). +// +// Additionally we can look into providing helpers which will take the src/dest from +// specific registers (like x86) which _could_ (??) make for easier register allocation +// for the JIT64, however it might lead to having to have some nasty code that treats +// these guys really special like... :(. +// +// Version that does the move, checks whether or not it's in the GC and whether or not +// it needs to have it's card updated +// +// void JIT_CheckedWriteBarrier(Object** dst, Object* src) +LEAF_ENTRY JIT_CheckedWriteBarrier, _TEXT + + // When WRITE_BARRIER_CHECK is defined _NotInHeap will write the reference + // but if it isn't then it will just return. + // + // See if this is in GCHeap + PREPARE_EXTERNAL_VAR g_lowest_address, rax + cmp rdi, [rax] + // jb NotInHeap + .byte 0x72, 0x0e + PREPARE_EXTERNAL_VAR g_highest_address, rax + cmp rdi, [rax] + // jnb NotInHeap + .byte 0x73, 0x02 + + // call C_FUNC(JIT_WriteBarrier) + .byte 0xeb, 0x05 + + NotInHeap: + // See comment above about possible AV + mov [rdi], rsi + ret +LEAF_END_MARKED JIT_CheckedWriteBarrier, _TEXT + + // This is used by the mechanism to hold either the JIT_WriteBarrier_PreGrow // or JIT_WriteBarrier_PostGrow code (depending on the state of the GC). It _WILL_ // change at runtime as the GC changes. Initially it should simply be a copy of the @@ -22,6 +61,71 @@ LEAF_ENTRY JIT_WriteBarrier, _TEXT jmp C_FUNC(JIT_WriteBarrier_Debug) #endif +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + // JIT_WriteBarrier_WriteWatch_PostGrow64 + + // Regarding patchable constants: + // - 64-bit constants have to be loaded into a register + // - The constants have to be aligned to 8 bytes so that they can be patched easily + // - The constant loads have been located to minimize NOP padding required to align the constants + // - Using different registers for successive constant loads helps pipeline better. Should we decide to use a special + // non-volatile calling convention, this should be changed to use just one register. + + // Do the move into the GC . It is correct to take an AV here, the EH code + // figures out that this came from a WriteBarrier and correctly maps it back + // to the managed method which called the WriteBarrier (see setup in + // InitializeExceptionHandling, vm\exceptionhandling.cpp). + mov [rdi], rsi + + // Update the write watch table if necessary + mov rax, rdi + movabs r10, 0xF0F0F0F0F0F0F0F0 + shr rax, 0Ch // SoftwareWriteWatch::AddressToTableByteIndexShift + NOP_2_BYTE // padding for alignment of constant + movabs r11, 0xF0F0F0F0F0F0F0F0 + add rax, r10 + cmp byte ptr [rax], 0h + .byte 0x75, 0x06 + // jne CheckCardTable + mov byte ptr [rax], 0FFh + + NOP_3_BYTE // padding for alignment of constant + + // Check the lower and upper ephemeral region bounds + CheckCardTable: + cmp rsi, r11 + .byte 0x72,0x3D + // jb Exit + + NOP_3_BYTE // padding for alignment of constant + + movabs r10, 0xF0F0F0F0F0F0F0F0 + + cmp rsi, r10 + .byte 0x73,0x2B + // jae Exit + + nop // padding for alignment of constant + + movabs rax, 0xF0F0F0F0F0F0F0F0 + + // Touch the card table entry, if not already dirty. + shr rdi, 0Bh + cmp byte ptr [rdi + rax], 0FFh + .byte 0x75, 0x02 + // jne UpdateCardTable + REPRET + + UpdateCardTable: + mov byte ptr [rdi + rax], 0FFh + ret + + .balign 16 + Exit: + REPRET +#else + // JIT_WriteBarrier_PostGrow64 + // Do the move into the GC . It is correct to take an AV here, the EH code // figures out that this came from a WriteBarrier and correctly maps it back // to the managed method which called the WriteBarrier (see setup in @@ -69,6 +173,8 @@ LEAF_ENTRY JIT_WriteBarrier, _TEXT .balign 16 Exit: REPRET +#endif + // make sure this guy is bigger than any of the other guys .balign 16 nop @@ -79,43 +185,6 @@ LEAF_ENTRY JIT_PatchedCodeLast, _TEXT ret LEAF_END JIT_PatchedCodeLast, _TEXT -// There is an even more optimized version of these helpers possible which takes -// advantage of knowledge of which way the ephemeral heap is growing to only do 1/2 -// that check (this is more significant in the JIT_WriteBarrier case). -// -// Additionally we can look into providing helpers which will take the src/dest from -// specific registers (like x86) which _could_ (??) make for easier register allocation -// for the JIT64, however it might lead to having to have some nasty code that treats -// these guys really special like... :(. -// -// Version that does the move, checks whether or not it's in the GC and whether or not -// it needs to have it's card updated -// -// void JIT_CheckedWriteBarrier(Object** dst, Object* src) -LEAF_ENTRY JIT_CheckedWriteBarrier, _TEXT - - // When WRITE_BARRIER_CHECK is defined _NotInHeap will write the reference - // but if it isn't then it will just return. - // - // See if this is in GCHeap - PREPARE_EXTERNAL_VAR g_lowest_address, rax - cmp rdi, [rax] - // jb NotInHeap - .byte 0x72, 0x0e - PREPARE_EXTERNAL_VAR g_highest_address, rax - cmp rdi, [rax] - // jnb NotInHeap - .byte 0x73, 0x02 - - // call C_FUNC(JIT_WriteBarrier) - .byte 0xeb, 0x84 - - NotInHeap: - // See comment above about possible AV - mov [rdi], rsi - ret -LEAF_END_MARKED JIT_CheckedWriteBarrier, _TEXT - // JIT_ByRefWriteBarrier has weird symantics, see usage in StubLinkerX86.cpp // // Entry: @@ -128,7 +197,7 @@ LEAF_END_MARKED JIT_CheckedWriteBarrier, _TEXT // // RCX is trashed // RAX is trashed -// R10 is trashed on Debug build +// R10 is trashed // R11 is trashed on Debug build // Exit: // RDI, RSI are incremented by SIZEOF(LPVOID) @@ -202,6 +271,21 @@ LEAF_ENTRY JIT_ByRefWriteBarrier, _TEXT DoneShadow_ByRefWriteBarrier: #endif +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + // Update the write watch table if necessary + PREPARE_EXTERNAL_VAR g_sw_ww_enabled_for_gc_heap, rax + cmp byte ptr [rax], 0h + je CheckCardTable_ByRefWriteBarrier + mov rax, rdi + shr rax, 0Ch // SoftwareWriteWatch::AddressToTableByteIndexShift + PREPARE_EXTERNAL_VAR g_sw_ww_table, r10 + add rax, qword ptr [r10] + cmp byte ptr [rax], 0h + jne CheckCardTable_ByRefWriteBarrier + mov byte ptr [rax], 0FFh +#endif + + CheckCardTable_ByRefWriteBarrier: // See if we can just quick out PREPARE_EXTERNAL_VAR g_ephemeral_low, rax cmp rcx, [rax] diff --git a/src/vm/amd64/jithelpers_fastwritebarriers.S b/src/vm/amd64/jithelpers_fastwritebarriers.S index f8d41cb88d..085f85bc8b 100644 --- a/src/vm/amd64/jithelpers_fastwritebarriers.S +++ b/src/vm/amd64/jithelpers_fastwritebarriers.S @@ -5,39 +5,6 @@ .intel_syntax noprefix #include "unixasmmacros.inc" - .balign 4 -LEAF_ENTRY JIT_WriteBarrier_PreGrow32, _TEXT - // Do the move into the GC . It is correct to take an AV here, the EH code - // figures out that this came from a WriteBarrier and correctly maps it back - // to the managed method which called the WriteBarrier (see setup in - // InitializeExceptionHandling, vm\exceptionhandling.cpp). - mov [rdi], rsi - - NOP_2_BYTE // padding for alignment of constant - -PATCH_LABEL JIT_WriteBarrier_PreGrow32_PatchLabel_Lower - cmp rsi, -0F0F0F10h // 0F0F0F0F0h - .byte 0x72, 0x22 - // jb Exit_PreGrow32 - - shr rdi, 0Bh -PATCH_LABEL JIT_WriteBarrier_PreGrow32_PatchLabel_CardTable_Check - cmp byte ptr [rdi + 0F0F0F0F0h], 0FFh - .byte 0x75, 0x03 - // jne UpdateCardTable_PreGrow32 - REPRET - - nop // padding for alignment of constant - -PATCH_LABEL JIT_WriteBarrier_PreGrow32_PatchLabel_CardTable_Update - UpdateCardTable_PreGrow32: - mov byte ptr [rdi + 0F0F0F0F0h], 0FFh - ret - - .balign 16 - Exit_PreGrow32: - REPRET -LEAF_END_MARKED JIT_WriteBarrier_PreGrow32, _TEXT .balign 8 LEAF_ENTRY JIT_WriteBarrier_PreGrow64, _TEXT @@ -80,6 +47,7 @@ PATCH_LABEL JIT_WriteBarrier_PreGrow64_Patch_Label_CardTable REPRET LEAF_END_MARKED JIT_WriteBarrier_PreGrow64, _TEXT + .balign 8 // See comments for JIT_WriteBarrier_PreGrow (above). LEAF_ENTRY JIT_WriteBarrier_PostGrow64, _TEXT @@ -134,60 +102,109 @@ PATCH_LABEL JIT_WriteBarrier_PostGrow64_Patch_Label_CardTable REPRET LEAF_END_MARKED JIT_WriteBarrier_PostGrow64, _TEXT - .balign 4 -LEAF_ENTRY JIT_WriteBarrier_PostGrow32, _TEXT + +#ifdef FEATURE_SVR_GC + + .balign 8 +LEAF_ENTRY JIT_WriteBarrier_SVR64, _TEXT + // + // SVR GC has multiple heaps, so it cannot provide one single + // ephemeral region to bounds check against, so we just skip the + // bounds checking all together and do our card table update + // unconditionally. + // + // Do the move into the GC . It is correct to take an AV here, the EH code // figures out that this came from a WriteBarrier and correctly maps it back // to the managed method which called the WriteBarrier (see setup in // InitializeExceptionHandling, vm\exceptionhandling.cpp). mov [rdi], rsi - NOP_2_BYTE // padding for alignment of constant + NOP_3_BYTE // padding for alignment of constant - // Check the lower and upper ephemeral region bounds +PATCH_LABEL JIT_WriteBarrier_SVR64_PatchLabel_CardTable + movabs rax, 0xF0F0F0F0F0F0F0F0 -PATCH_LABEL JIT_WriteBarrier_PostGrow32_PatchLabel_Lower - cmp rsi, -0F0F0F10h // 0F0F0F0F0h - .byte 0x72, 0x2e - // jb Exit_PostGrow32 + shr rdi, 0Bh - NOP_3_BYTE // padding for alignment of constant + cmp byte ptr [rdi + rax], 0FFh + .byte 0x75, 0x02 + // jne UpdateCardTable_SVR64 + REPRET -PATCH_LABEL JIT_WriteBarrier_PostGrow32_PatchLabel_Upper - cmp rsi, -0F0F0F10h // 0F0F0F0F0h - .byte 0x73, 0x22 - // jae Exit_PostGrow32 + UpdateCardTable_SVR64: + mov byte ptr [rdi + rax], 0FFh + ret +LEAF_END_MARKED JIT_WriteBarrier_SVR64, _TEXT - // Touch the card table entry, if not already dirty. - shr rdi, 0Bh +#endif + + +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + + .balign 8 +LEAF_ENTRY JIT_WriteBarrier_WriteWatch_PreGrow64, _TEXT + // Regarding patchable constants: + // - 64-bit constants have to be loaded into a register + // - The constants have to be aligned to 8 bytes so that they can be patched easily + // - The constant loads have been located to minimize NOP padding required to align the constants + // - Using different registers for successive constant loads helps pipeline better. Should we decide to use a special + // non-volatile calling convention, this should be changed to use just one register. + + // Do the move into the GC . It is correct to take an AV here, the EH code + // figures out that this came from a WriteBarrier and correctly maps it back + // to the managed method which called the WriteBarrier (see setup in + // InitializeExceptionHandling, vm\exceptionhandling.cpp). + mov [rdi], rsi -PATCH_LABEL JIT_WriteBarrier_PostGrow32_PatchLabel_CheckCardTable - cmp byte ptr [rdi + 0F0F0F0F0h], 0FFh + // Update the write watch table if necessary + mov rax, rdi +PATCH_LABEL JIT_WriteBarrier_WriteWatch_PreGrow64_Patch_Label_WriteWatchTable + movabs r10, 0xF0F0F0F0F0F0F0F0 + shr rax, 0Ch // SoftwareWriteWatch::AddressToTableByteIndexShift + NOP_2_BYTE // padding for alignment of constant +PATCH_LABEL JIT_WriteBarrier_WriteWatch_PreGrow64_Patch_Label_Lower + movabs r11, 0xF0F0F0F0F0F0F0F0 + add rax, r10 + cmp byte ptr [rax], 0h .byte 0x75, 0x03 - // jne UpdateCardTable_PostGrow32 - REPRET + // jne CheckCardTable_WriteWatch_PreGrow64 + mov byte ptr [rax], 0FFh - nop // padding for alignment of constant + CheckCardTable_WriteWatch_PreGrow64: + // Check the lower ephemeral region bound. + cmp rsi, r11 + .byte 0x72, 0x20 + // jb Exit_WriteWatch_PreGrow64 -PATCH_LABEL JIT_WriteBarrier_PostGrow32_PatchLabel_UpdateCardTable - UpdateCardTable_PostGrow32: - mov byte ptr [rdi + 0F0F0F0F0h], 0FFh + // Touch the card table entry, if not already dirty. + shr rdi, 0Bh + NOP_2_BYTE // padding for alignment of constant +PATCH_LABEL JIT_WriteBarrier_WriteWatch_PreGrow64_Patch_Label_CardTable + movabs rax, 0xF0F0F0F0F0F0F0F0 + cmp byte ptr [rdi + rax], 0FFh + .byte 0x75, 0x02 + // jne UpdateCardTable_WriteWatch_PreGrow64 + REPRET + + UpdateCardTable_WriteWatch_PreGrow64: + mov byte ptr [rdi + rax], 0FFh ret .balign 16 - Exit_PostGrow32: + Exit_WriteWatch_PreGrow64: REPRET -LEAF_END_MARKED JIT_WriteBarrier_PostGrow32, _TEXT +LEAF_END_MARKED JIT_WriteBarrier_WriteWatch_PreGrow64, _TEXT - .balign 4 -LEAF_ENTRY JIT_WriteBarrier_SVR32, _TEXT - // - // SVR GC has multiple heaps, so it cannot provide one single - // ephemeral region to bounds check against, so we just skip the - // bounds checking all together and do our card table update - // unconditionally. - // + .balign 8 +LEAF_ENTRY JIT_WriteBarrier_WriteWatch_PostGrow64, _TEXT + // Regarding patchable constants: + // - 64-bit constants have to be loaded into a register + // - The constants have to be aligned to 8 bytes so that they can be patched easily + // - The constant loads have been located to minimize NOP padding required to align the constants + // - Using different registers for successive constant loads helps pipeline better. Should we decide to use a special + // non-volatile calling convention, this should be changed to use just one register. // Do the move into the GC . It is correct to take an AV here, the EH code // figures out that this came from a WriteBarrier and correctly maps it back @@ -195,26 +212,70 @@ LEAF_ENTRY JIT_WriteBarrier_SVR32, _TEXT // InitializeExceptionHandling, vm\exceptionhandling.cpp). mov [rdi], rsi - shr rdi, 0Bh + // Update the write watch table if necessary + mov rax, rdi +PATCH_LABEL JIT_WriteBarrier_WriteWatch_PostGrow64_Patch_Label_WriteWatchTable + movabs r10, 0xF0F0F0F0F0F0F0F0 + shr rax, 0Ch // SoftwareWriteWatch::AddressToTableByteIndexShift + NOP_2_BYTE // padding for alignment of constant +PATCH_LABEL JIT_WriteBarrier_WriteWatch_PostGrow64_Patch_Label_Lower + movabs r11, 0xF0F0F0F0F0F0F0F0 + add rax, r10 + cmp byte ptr [rax], 0h + .byte 0x75, 0x06 + // jne CheckCardTable_WriteWatch_PostGrow64 + mov byte ptr [rax], 0FFh NOP_3_BYTE // padding for alignment of constant -PATCH_LABEL JIT_WriteBarrier_SVR32_PatchLabel_CheckCardTable - cmp byte ptr [rdi + 0F0F0F0F0h], 0FFh - .byte 0x75, 0x03 - // jne UpdateCardTable_SVR32 - REPRET + // Check the lower and upper ephemeral region bounds + CheckCardTable_WriteWatch_PostGrow64: + cmp rsi, r11 + .byte 0x72, 0x3d + // jb Exit_WriteWatch_PostGrow64 + + NOP_3_BYTE // padding for alignment of constant + +PATCH_LABEL JIT_WriteBarrier_WriteWatch_PostGrow64_Patch_Label_Upper + movabs r10, 0xF0F0F0F0F0F0F0F0 + + cmp rsi, r10 + .byte 0x73, 0x2b + // jae Exit_WriteWatch_PostGrow64 nop // padding for alignment of constant -PATCH_LABEL JIT_WriteBarrier_SVR32_PatchLabel_UpdateCardTable - UpdateCardTable_SVR32: - mov byte ptr [rdi + 0F0F0F0F0h], 0FFh +PATCH_LABEL JIT_WriteBarrier_WriteWatch_PostGrow64_Patch_Label_CardTable + movabs rax, 0xF0F0F0F0F0F0F0F0 + + // Touch the card table entry, if not already dirty. + shr rdi, 0Bh + cmp byte ptr [rdi + rax], 0FFh + .byte 0x75, 0x02 + // jne UpdateCardTable_WriteWatch_PostGrow64 + REPRET + + UpdateCardTable_WriteWatch_PostGrow64: + mov byte ptr [rdi + rax], 0FFh ret -LEAF_END_MARKED JIT_WriteBarrier_SVR32, _TEXT + + .balign 16 + Exit_WriteWatch_PostGrow64: + REPRET +LEAF_END_MARKED JIT_WriteBarrier_WriteWatch_PostGrow64, _TEXT + + +#ifdef FEATURE_SVR_GC .balign 8 -LEAF_ENTRY JIT_WriteBarrier_SVR64, _TEXT +LEAF_ENTRY JIT_WriteBarrier_WriteWatch_SVR64, _TEXT + // Regarding patchable constants: + // - 64-bit constants have to be loaded into a register + // - The constants have to be aligned to 8 bytes so that they can be patched easily + // - The constant loads have been located to minimize NOP padding required to align the constants + // - Using different registers for successive constant loads helps pipeline better. Should we decide to use a special + // non-volatile calling convention, this should be changed to use just one register. + // // SVR GC has multiple heaps, so it cannot provide one single // ephemeral region to bounds check against, so we just skip the @@ -228,19 +289,31 @@ LEAF_ENTRY JIT_WriteBarrier_SVR64, _TEXT // InitializeExceptionHandling, vm\exceptionhandling.cpp). mov [rdi], rsi - NOP_3_BYTE // padding for alignment of constant - -PATCH_LABEL JIT_WriteBarrier_SVR64_PatchLabel_CardTable - movabs rax, 0xF0F0F0F0F0F0F0F0 + // Update the write watch table if necessary + mov rax, rdi +PATCH_LABEL JIT_WriteBarrier_WriteWatch_SVR64_PatchLabel_WriteWatchTable + movabs r10, 0xF0F0F0F0F0F0F0F0 + shr rax, 0Ch // SoftwareWriteWatch::AddressToTableByteIndexShift + NOP_2_BYTE // padding for alignment of constant +PATCH_LABEL JIT_WriteBarrier_WriteWatch_SVR64_PatchLabel_CardTable + movabs r11, 0xF0F0F0F0F0F0F0F0 + add rax, r10 + cmp byte ptr [rax], 0h + .byte 0x75, 0x03 + // jne CheckCardTable_WriteWatch_SVR64 + mov byte ptr [rax], 0FFh + CheckCardTable_WriteWatch_SVR64: shr rdi, 0Bh - - cmp byte ptr [rdi + rax], 0FFh + cmp byte ptr [rdi + r11], 0FFh .byte 0x75, 0x02 - // jne UpdateCardTable_SVR64 + // jne UpdateCardTable_WriteWatch_SVR64 REPRET - UpdateCardTable_SVR64: - mov byte ptr [rdi + rax], 0FFh + UpdateCardTable_WriteWatch_SVR64: + mov byte ptr [rdi + r11], 0FFh ret -LEAF_END_MARKED JIT_WriteBarrier_SVR64, _TEXT +LEAF_END_MARKED JIT_WriteBarrier_WriteWatch_SVR64, _TEXT + +#endif +#endif diff --git a/src/vm/amd64/jithelpers_slow.S b/src/vm/amd64/jithelpers_slow.S index 4d18e4356c..6c8d9077b8 100644 --- a/src/vm/amd64/jithelpers_slow.S +++ b/src/vm/amd64/jithelpers_slow.S @@ -68,6 +68,21 @@ LEAF_ENTRY JIT_WriteBarrier_Debug, _TEXT DoneShadow: #endif +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + // Update the write watch table if necessary + PREPARE_EXTERNAL_VAR g_sw_ww_enabled_for_gc_heap, r10 + cmp byte ptr [r10], 0h + je CheckCardTable_Debug + mov r10, rdi + shr r10, 0Ch // SoftwareWriteWatch::AddressToTableByteIndexShift + PREPARE_EXTERNAL_VAR g_sw_ww_table, r11 + add r10, qword ptr [r11] + cmp byte ptr [r10], 0h + jne CheckCardTable_Debug + mov byte ptr [r10], 0FFh +#endif + + CheckCardTable_Debug: // See if we can just quick out PREPARE_EXTERNAL_VAR g_ephemeral_low, r10 cmp rax, [r10] diff --git a/src/vm/amd64/jitinterfaceamd64.cpp b/src/vm/amd64/jitinterfaceamd64.cpp index cfcca1d372..39c2e05c2f 100644 --- a/src/vm/amd64/jitinterfaceamd64.cpp +++ b/src/vm/amd64/jitinterfaceamd64.cpp @@ -16,6 +16,7 @@ #include "eeconfig.h" #include "excep.h" #include "threadsuspend.h" +#include "../../gc/softwarewritewatch.h" extern uint8_t* g_ephemeral_low; extern uint8_t* g_ephemeral_high; @@ -24,24 +25,11 @@ extern uint32_t* g_card_table; // Patch Labels for the various write barriers EXTERN_C void JIT_WriteBarrier_End(); -EXTERN_C void JIT_WriteBarrier_PreGrow32(Object **dst, Object *ref); -EXTERN_C void JIT_WriteBarrier_PreGrow32_PatchLabel_Lower(); -EXTERN_C void JIT_WriteBarrier_PreGrow32_PatchLabel_CardTable_Check(); -EXTERN_C void JIT_WriteBarrier_PreGrow32_PatchLabel_CardTable_Update(); -EXTERN_C void JIT_WriteBarrier_PreGrow32_End(); - EXTERN_C void JIT_WriteBarrier_PreGrow64(Object **dst, Object *ref); EXTERN_C void JIT_WriteBarrier_PreGrow64_Patch_Label_Lower(); EXTERN_C void JIT_WriteBarrier_PreGrow64_Patch_Label_CardTable(); EXTERN_C void JIT_WriteBarrier_PreGrow64_End(); -EXTERN_C void JIT_WriteBarrier_PostGrow32(Object **dst, Object *ref); -EXTERN_C void JIT_WriteBarrier_PostGrow32_PatchLabel_Lower(); -EXTERN_C void JIT_WriteBarrier_PostGrow32_PatchLabel_Upper(); -EXTERN_C void JIT_WriteBarrier_PostGrow32_PatchLabel_CheckCardTable(); -EXTERN_C void JIT_WriteBarrier_PostGrow32_PatchLabel_UpdateCardTable(); -EXTERN_C void JIT_WriteBarrier_PostGrow32_End(); - EXTERN_C void JIT_WriteBarrier_PostGrow64(Object **dst, Object *ref); EXTERN_C void JIT_WriteBarrier_PostGrow64_Patch_Label_Lower(); EXTERN_C void JIT_WriteBarrier_PostGrow64_Patch_Label_Upper(); @@ -49,15 +37,32 @@ EXTERN_C void JIT_WriteBarrier_PostGrow64_Patch_Label_CardTable(); EXTERN_C void JIT_WriteBarrier_PostGrow64_End(); #ifdef FEATURE_SVR_GC -EXTERN_C void JIT_WriteBarrier_SVR32(Object **dst, Object *ref); -EXTERN_C void JIT_WriteBarrier_SVR32_PatchLabel_CheckCardTable(); -EXTERN_C void JIT_WriteBarrier_SVR32_PatchLabel_UpdateCardTable(); -EXTERN_C void JIT_WriteBarrier_SVR32_End(); - EXTERN_C void JIT_WriteBarrier_SVR64(Object **dst, Object *ref); EXTERN_C void JIT_WriteBarrier_SVR64_PatchLabel_CardTable(); EXTERN_C void JIT_WriteBarrier_SVR64_End(); -#endif +#endif // FEATURE_SVR_GC + +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +EXTERN_C void JIT_WriteBarrier_WriteWatch_PreGrow64(Object **dst, Object *ref); +EXTERN_C void JIT_WriteBarrier_WriteWatch_PreGrow64_Patch_Label_WriteWatchTable(); +EXTERN_C void JIT_WriteBarrier_WriteWatch_PreGrow64_Patch_Label_Lower(); +EXTERN_C void JIT_WriteBarrier_WriteWatch_PreGrow64_Patch_Label_CardTable(); +EXTERN_C void JIT_WriteBarrier_WriteWatch_PreGrow64_End(); + +EXTERN_C void JIT_WriteBarrier_WriteWatch_PostGrow64(Object **dst, Object *ref); +EXTERN_C void JIT_WriteBarrier_WriteWatch_PostGrow64_Patch_Label_WriteWatchTable(); +EXTERN_C void JIT_WriteBarrier_WriteWatch_PostGrow64_Patch_Label_Lower(); +EXTERN_C void JIT_WriteBarrier_WriteWatch_PostGrow64_Patch_Label_Upper(); +EXTERN_C void JIT_WriteBarrier_WriteWatch_PostGrow64_Patch_Label_CardTable(); +EXTERN_C void JIT_WriteBarrier_WriteWatch_PostGrow64_End(); + +#ifdef FEATURE_SVR_GC +EXTERN_C void JIT_WriteBarrier_WriteWatch_SVR64(Object **dst, Object *ref); +EXTERN_C void JIT_WriteBarrier_WriteWatch_SVR64_PatchLabel_WriteWatchTable(); +EXTERN_C void JIT_WriteBarrier_WriteWatch_SVR64_PatchLabel_CardTable(); +EXTERN_C void JIT_WriteBarrier_WriteWatch_SVR64_End(); +#endif // FEATURE_SVR_GC +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP WriteBarrierManager g_WriteBarrierManager; @@ -90,28 +95,13 @@ void WriteBarrierManager::Validate() // are places where these values are updated while the EE is running // NOTE: we can't call this from the ctor since our infrastructure isn't ready for assert dialogs - PBYTE pLowerBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PreGrow32, PatchLabel_Lower, 3); - PBYTE pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PreGrow32, PatchLabel_CardTable_Check, 2); - PBYTE pCardTableImmediate2 = CALC_PATCH_LOCATION(JIT_WriteBarrier_PreGrow32, PatchLabel_CardTable_Update, 2); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pLowerBoundImmediate) & 0x3) == 0); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pCardTableImmediate) & 0x3) == 0); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pCardTableImmediate2) & 0x3) == 0); + PBYTE pLowerBoundImmediate, pUpperBoundImmediate, pCardTableImmediate; pLowerBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PreGrow64, Patch_Label_Lower, 2); pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PreGrow64, Patch_Label_CardTable, 2); _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pLowerBoundImmediate) & 0x7) == 0); _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pCardTableImmediate) & 0x7) == 0); - PBYTE pUpperBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PostGrow32, PatchLabel_Upper, 3); - pLowerBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PostGrow32, PatchLabel_Lower, 3); - pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PostGrow32, PatchLabel_CheckCardTable, 2); - pCardTableImmediate2 = CALC_PATCH_LOCATION(JIT_WriteBarrier_PostGrow32, PatchLabel_UpdateCardTable, 2); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pUpperBoundImmediate) & 0x3) == 0); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pLowerBoundImmediate) & 0x3) == 0); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pCardTableImmediate) & 0x3) == 0); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pCardTableImmediate2) & 0x3) == 0); - - pLowerBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PostGrow64, Patch_Label_Lower, 2); pUpperBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PostGrow64, Patch_Label_Upper, 2); pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PostGrow64, Patch_Label_CardTable, 2); @@ -120,14 +110,36 @@ void WriteBarrierManager::Validate() _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pCardTableImmediate) & 0x7) == 0); #ifdef FEATURE_SVR_GC - pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_SVR32, PatchLabel_CheckCardTable, 2); - pCardTableImmediate2 = CALC_PATCH_LOCATION(JIT_WriteBarrier_SVR32, PatchLabel_UpdateCardTable, 2); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pCardTableImmediate) & 0x3) == 0); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pCardTableImmediate2) & 0x3) == 0); - pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_SVR64, PatchLabel_CardTable, 2); _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pCardTableImmediate) & 0x7) == 0); -#endif +#endif // FEATURE_SVR_GC + +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + PBYTE pWriteWatchTableImmediate; + + pWriteWatchTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_PreGrow64, Patch_Label_WriteWatchTable, 2); + pLowerBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_PreGrow64, Patch_Label_Lower, 2); + pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_PreGrow64, Patch_Label_CardTable, 2); + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pWriteWatchTableImmediate) & 0x7) == 0); + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pLowerBoundImmediate) & 0x7) == 0); + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pCardTableImmediate) & 0x7) == 0); + + pWriteWatchTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_PostGrow64, Patch_Label_WriteWatchTable, 2); + pLowerBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_PostGrow64, Patch_Label_Lower, 2); + pUpperBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_PostGrow64, Patch_Label_Upper, 2); + pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_PostGrow64, Patch_Label_CardTable, 2); + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pWriteWatchTableImmediate) & 0x7) == 0); + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pLowerBoundImmediate) & 0x7) == 0); + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pUpperBoundImmediate) & 0x7) == 0); + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pCardTableImmediate) & 0x7) == 0); + +#ifdef FEATURE_SVR_GC + pWriteWatchTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_SVR64, PatchLabel_WriteWatchTable, 2); + pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_SVR64, PatchLabel_CardTable, 2); + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pWriteWatchTableImmediate) & 0x7) == 0); + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", (reinterpret_cast<UINT64>(pCardTableImmediate) & 0x7) == 0); +#endif // FEATURE_SVR_GC +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP } #endif // CODECOVERAGE @@ -139,20 +151,24 @@ PCODE WriteBarrierManager::GetCurrentWriteBarrierCode() switch (m_currentWriteBarrier) { - case WRITE_BARRIER_PREGROW32: - return GetEEFuncEntryPoint(JIT_WriteBarrier_PreGrow32); case WRITE_BARRIER_PREGROW64: return GetEEFuncEntryPoint(JIT_WriteBarrier_PreGrow64); - case WRITE_BARRIER_POSTGROW32: - return GetEEFuncEntryPoint(JIT_WriteBarrier_PostGrow32); case WRITE_BARRIER_POSTGROW64: return GetEEFuncEntryPoint(JIT_WriteBarrier_PostGrow64); #ifdef FEATURE_SVR_GC - case WRITE_BARRIER_SVR32: - return GetEEFuncEntryPoint(JIT_WriteBarrier_SVR32); case WRITE_BARRIER_SVR64: return GetEEFuncEntryPoint(JIT_WriteBarrier_SVR64); -#endif +#endif // FEATURE_SVR_GC +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + case WRITE_BARRIER_WRITE_WATCH_PREGROW64: + return GetEEFuncEntryPoint(JIT_WriteBarrier_WriteWatch_PreGrow64); + case WRITE_BARRIER_WRITE_WATCH_POSTGROW64: + return GetEEFuncEntryPoint(JIT_WriteBarrier_WriteWatch_PostGrow64); +#ifdef FEATURE_SVR_GC + case WRITE_BARRIER_WRITE_WATCH_SVR64: + return GetEEFuncEntryPoint(JIT_WriteBarrier_WriteWatch_SVR64); +#endif // FEATURE_SVR_GC +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP default: UNREACHABLE_MSG("unexpected m_currentWriteBarrier!"); }; @@ -167,20 +183,24 @@ size_t WriteBarrierManager::GetSpecificWriteBarrierSize(WriteBarrierType writeBa switch (writeBarrier) { - case WRITE_BARRIER_PREGROW32: - return MARKED_FUNCTION_SIZE(JIT_WriteBarrier_PreGrow32); case WRITE_BARRIER_PREGROW64: return MARKED_FUNCTION_SIZE(JIT_WriteBarrier_PreGrow64); - case WRITE_BARRIER_POSTGROW32: - return MARKED_FUNCTION_SIZE(JIT_WriteBarrier_PostGrow32); case WRITE_BARRIER_POSTGROW64: return MARKED_FUNCTION_SIZE(JIT_WriteBarrier_PostGrow64); #ifdef FEATURE_SVR_GC - case WRITE_BARRIER_SVR32: - return MARKED_FUNCTION_SIZE(JIT_WriteBarrier_SVR32); case WRITE_BARRIER_SVR64: return MARKED_FUNCTION_SIZE(JIT_WriteBarrier_SVR64); -#endif +#endif // FEATURE_SVR_GC +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + case WRITE_BARRIER_WRITE_WATCH_PREGROW64: + return MARKED_FUNCTION_SIZE(JIT_WriteBarrier_WriteWatch_PreGrow64); + case WRITE_BARRIER_WRITE_WATCH_POSTGROW64: + return MARKED_FUNCTION_SIZE(JIT_WriteBarrier_WriteWatch_PostGrow64); +#ifdef FEATURE_SVR_GC + case WRITE_BARRIER_WRITE_WATCH_SVR64: + return MARKED_FUNCTION_SIZE(JIT_WriteBarrier_WriteWatch_SVR64); +#endif // FEATURE_SVR_GC +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP case WRITE_BARRIER_BUFFER: return MARKED_FUNCTION_SIZE(JIT_WriteBarrier); default: @@ -202,38 +222,25 @@ PBYTE WriteBarrierManager::CalculatePatchLocation(LPVOID base, LPVOID label, int return ((LPBYTE)GetEEFuncEntryPoint(JIT_WriteBarrier) + ((LPBYTE)GetEEFuncEntryPoint(label) - (LPBYTE)GetEEFuncEntryPoint(base) + offset)); } -void WriteBarrierManager::ChangeWriteBarrierTo(WriteBarrierType newWriteBarrier) -{ - GCX_MAYBE_COOP_NO_THREAD_BROKEN((GetThread() != NULL)); - BOOL bEESuspended = FALSE; - if(m_currentWriteBarrier != WRITE_BARRIER_UNINITIALIZED && !IsGCThread()) +void WriteBarrierManager::ChangeWriteBarrierTo(WriteBarrierType newWriteBarrier, bool isRuntimeSuspended) +{ + GCX_MAYBE_COOP_NO_THREAD_BROKEN((!isRuntimeSuspended && GetThread() != NULL)); + BOOL bEESuspendedHere = FALSE; + if(!isRuntimeSuspended && m_currentWriteBarrier != WRITE_BARRIER_UNINITIALIZED) { ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_FOR_GC_PREP); - bEESuspended = TRUE; - } - + bEESuspendedHere = TRUE; + } + _ASSERTE(m_currentWriteBarrier != newWriteBarrier); m_currentWriteBarrier = newWriteBarrier; - + // the memcpy must come before the switch statment because the asserts inside the switch // are actually looking into the JIT_WriteBarrier buffer memcpy((PVOID)JIT_WriteBarrier, (LPVOID)GetCurrentWriteBarrierCode(), GetCurrentWriteBarrierSize()); switch (newWriteBarrier) { - case WRITE_BARRIER_PREGROW32: - { - m_pLowerBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PreGrow32, PatchLabel_Lower, 3); - m_pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PreGrow32, PatchLabel_CardTable_Check, 2); - m_pCardTableImmediate2 = CALC_PATCH_LOCATION(JIT_WriteBarrier_PreGrow32, PatchLabel_CardTable_Update, 2); - - // Make sure that we will be bashing the right places (immediates should be hardcoded to 0x0f0f0f0f0f0f0f0f0). - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0 == *(DWORD*)m_pLowerBoundImmediate); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0 == *(DWORD*)m_pCardTableImmediate); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0 == *(DWORD*)m_pCardTableImmediate2); - break; - } - case WRITE_BARRIER_PREGROW64: { m_pLowerBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PreGrow64, Patch_Label_Lower, 2); @@ -244,22 +251,7 @@ void WriteBarrierManager::ChangeWriteBarrierTo(WriteBarrierType newWriteBarrier) _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0f0f0f0f0 == *(UINT64*)m_pCardTableImmediate); break; } - - case WRITE_BARRIER_POSTGROW32: - { - m_pUpperBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PostGrow32, PatchLabel_Upper, 3); - m_pLowerBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PostGrow32, PatchLabel_Lower, 3); - m_pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PostGrow32, PatchLabel_CheckCardTable, 2); - m_pCardTableImmediate2 = CALC_PATCH_LOCATION(JIT_WriteBarrier_PostGrow32, PatchLabel_UpdateCardTable, 2); - // Make sure that we will be bashing the right places (immediates should be hardcoded to 0x0f0f0f0f0f0f0f0f0). - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0 == *(DWORD*)m_pUpperBoundImmediate); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0 == *(DWORD*)m_pLowerBoundImmediate); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0 == *(DWORD*)m_pCardTableImmediate); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0 == *(DWORD*)m_pCardTableImmediate2); - break; - } - case WRITE_BARRIER_POSTGROW64: { m_pLowerBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_PostGrow64, Patch_Label_Lower, 2); @@ -274,35 +266,67 @@ void WriteBarrierManager::ChangeWriteBarrierTo(WriteBarrierType newWriteBarrier) } #ifdef FEATURE_SVR_GC - case WRITE_BARRIER_SVR32: + case WRITE_BARRIER_SVR64: { - m_pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_SVR32, PatchLabel_CheckCardTable, 2); - m_pCardTableImmediate2 = CALC_PATCH_LOCATION(JIT_WriteBarrier_SVR32, PatchLabel_UpdateCardTable, 2); + m_pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_SVR64, PatchLabel_CardTable, 2); // Make sure that we will be bashing the right places (immediates should be hardcoded to 0x0f0f0f0f0f0f0f0f0). - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0 == *(DWORD*)m_pCardTableImmediate); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0 == *(DWORD*)m_pCardTableImmediate2); + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0f0f0f0f0 == *(UINT64*)m_pCardTableImmediate); + break; + } +#endif // FEATURE_SVR_GC + +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + case WRITE_BARRIER_WRITE_WATCH_PREGROW64: + { + m_pWriteWatchTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_PreGrow64, Patch_Label_WriteWatchTable, 2); + m_pLowerBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_PreGrow64, Patch_Label_Lower, 2); + m_pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_PreGrow64, Patch_Label_CardTable, 2); + + // Make sure that we will be bashing the right places (immediates should be hardcoded to 0x0f0f0f0f0f0f0f0f0). + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0f0f0f0f0 == *(UINT64*)m_pWriteWatchTableImmediate); + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0f0f0f0f0 == *(UINT64*)m_pLowerBoundImmediate); + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0f0f0f0f0 == *(UINT64*)m_pCardTableImmediate); break; } - case WRITE_BARRIER_SVR64: + case WRITE_BARRIER_WRITE_WATCH_POSTGROW64: { - m_pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_SVR64, PatchLabel_CardTable, 2); + m_pWriteWatchTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_PostGrow64, Patch_Label_WriteWatchTable, 2); + m_pLowerBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_PostGrow64, Patch_Label_Lower, 2); + m_pUpperBoundImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_PostGrow64, Patch_Label_Upper, 2); + m_pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_PostGrow64, Patch_Label_CardTable, 2); // Make sure that we will be bashing the right places (immediates should be hardcoded to 0x0f0f0f0f0f0f0f0f0). + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0f0f0f0f0 == *(UINT64*)m_pWriteWatchTableImmediate); + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0f0f0f0f0 == *(UINT64*)m_pLowerBoundImmediate); _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0f0f0f0f0 == *(UINT64*)m_pCardTableImmediate); - break; + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0f0f0f0f0 == *(UINT64*)m_pUpperBoundImmediate); + break; } -#endif + +#ifdef FEATURE_SVR_GC + case WRITE_BARRIER_WRITE_WATCH_SVR64: + { + m_pWriteWatchTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_SVR64, PatchLabel_WriteWatchTable, 2); + m_pCardTableImmediate = CALC_PATCH_LOCATION(JIT_WriteBarrier_WriteWatch_SVR64, PatchLabel_CardTable, 2); + + // Make sure that we will be bashing the right places (immediates should be hardcoded to 0x0f0f0f0f0f0f0f0f0). + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0f0f0f0f0 == *(UINT64*)m_pWriteWatchTableImmediate); + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", 0xf0f0f0f0f0f0f0f0 == *(UINT64*)m_pCardTableImmediate); + break; + } +#endif // FEATURE_SVR_GC +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP default: UNREACHABLE_MSG("unexpected write barrier type!"); } - UpdateEphemeralBounds(); - UpdateCardTableLocation(FALSE); + UpdateEphemeralBounds(true); + UpdateWriteWatchAndCardTableLocations(true, false); - if(bEESuspended) + if(bEESuspendedHere) { ThreadSuspend::RestartEE(FALSE, TRUE); } @@ -325,21 +349,25 @@ void WriteBarrierManager::Initialize() // write barrier implementations. size_t cbWriteBarrierBuffer = GetSpecificWriteBarrierSize(WRITE_BARRIER_BUFFER); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", cbWriteBarrierBuffer >= GetSpecificWriteBarrierSize(WRITE_BARRIER_PREGROW32)); _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", cbWriteBarrierBuffer >= GetSpecificWriteBarrierSize(WRITE_BARRIER_PREGROW64)); - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", cbWriteBarrierBuffer >= GetSpecificWriteBarrierSize(WRITE_BARRIER_POSTGROW32)); _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", cbWriteBarrierBuffer >= GetSpecificWriteBarrierSize(WRITE_BARRIER_POSTGROW64)); #ifdef FEATURE_SVR_GC - _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", cbWriteBarrierBuffer >= GetSpecificWriteBarrierSize(WRITE_BARRIER_SVR32)); _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", cbWriteBarrierBuffer >= GetSpecificWriteBarrierSize(WRITE_BARRIER_SVR64)); -#endif +#endif // FEATURE_SVR_GC +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", cbWriteBarrierBuffer >= GetSpecificWriteBarrierSize(WRITE_BARRIER_WRITE_WATCH_PREGROW64)); + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", cbWriteBarrierBuffer >= GetSpecificWriteBarrierSize(WRITE_BARRIER_WRITE_WATCH_POSTGROW64)); +#ifdef FEATURE_SVR_GC + _ASSERTE_ALL_BUILDS("clr/src/VM/AMD64/JITinterfaceAMD64.cpp", cbWriteBarrierBuffer >= GetSpecificWriteBarrierSize(WRITE_BARRIER_WRITE_WATCH_SVR64)); +#endif // FEATURE_SVR_GC +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP #if !defined(CODECOVERAGE) Validate(); #endif } -bool WriteBarrierManager::NeedDifferentWriteBarrier(BOOL bReqUpperBoundsCheck, WriteBarrierType* pNewWriteBarrierType) +bool WriteBarrierManager::NeedDifferentWriteBarrier(bool bReqUpperBoundsCheck, WriteBarrierType* pNewWriteBarrierType) { // Init code for the JIT_WriteBarrier assembly routine. Since it will be bashed everytime the GC Heap // changes size, we want to do most of the work just once. @@ -362,22 +390,9 @@ bool WriteBarrierManager::NeedDifferentWriteBarrier(BOOL bReqUpperBoundsCheck, W } #endif - writeBarrierType = GCHeap::IsServerHeap() ? WRITE_BARRIER_SVR32 : WRITE_BARRIER_PREGROW32; + writeBarrierType = GCHeap::IsServerHeap() ? WRITE_BARRIER_SVR64 : WRITE_BARRIER_PREGROW64; continue; - case WRITE_BARRIER_PREGROW32: - if (bReqUpperBoundsCheck) - { - writeBarrierType = WRITE_BARRIER_POSTGROW32; - continue; - } - - if (!FitsInI4((size_t)g_card_table) || !FitsInI4((size_t)g_ephemeral_low)) - { - writeBarrierType = WRITE_BARRIER_PREGROW64; - } - break; - case WRITE_BARRIER_PREGROW64: if (bReqUpperBoundsCheck) { @@ -385,27 +400,30 @@ bool WriteBarrierManager::NeedDifferentWriteBarrier(BOOL bReqUpperBoundsCheck, W } break; - case WRITE_BARRIER_POSTGROW32: - if (!FitsInI4((size_t)g_card_table) || !FitsInI4((size_t)g_ephemeral_low) || !FitsInI4((size_t)g_ephemeral_high)) - { - writeBarrierType = WRITE_BARRIER_POSTGROW64; - } - break; - case WRITE_BARRIER_POSTGROW64: break; #ifdef FEATURE_SVR_GC - case WRITE_BARRIER_SVR32: - if (!FitsInI4((size_t)g_card_table)) + case WRITE_BARRIER_SVR64: + break; +#endif // FEATURE_SVR_GC + +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + case WRITE_BARRIER_WRITE_WATCH_PREGROW64: + if (bReqUpperBoundsCheck) { - writeBarrierType = WRITE_BARRIER_SVR64; + writeBarrierType = WRITE_BARRIER_WRITE_WATCH_POSTGROW64; } break; - case WRITE_BARRIER_SVR64: + case WRITE_BARRIER_WRITE_WATCH_POSTGROW64: break; -#endif + +#ifdef FEATURE_SVR_GC + case WRITE_BARRIER_WRITE_WATCH_SVR64: + break; +#endif // FEATURE_SVR_GC +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP default: UNREACHABLE_MSG("unexpected write barrier type!"); @@ -417,14 +435,14 @@ bool WriteBarrierManager::NeedDifferentWriteBarrier(BOOL bReqUpperBoundsCheck, W return m_currentWriteBarrier != writeBarrierType; } -void WriteBarrierManager::UpdateEphemeralBounds() +void WriteBarrierManager::UpdateEphemeralBounds(bool isRuntimeSuspended) { bool needToFlushCache = false; WriteBarrierType newType; - if (NeedDifferentWriteBarrier(FALSE, &newType)) + if (NeedDifferentWriteBarrier(false, &newType)) { - ChangeWriteBarrierTo(newType); + ChangeWriteBarrierTo(newType, isRuntimeSuspended); return; } @@ -436,31 +454,10 @@ void WriteBarrierManager::UpdateEphemeralBounds() switch (m_currentWriteBarrier) { - - case WRITE_BARRIER_POSTGROW32: - { - // Change immediate if different from new g_ephermeral_high. - if (*(INT32*)m_pUpperBoundImmediate != (INT32)(size_t)g_ephemeral_high) - { - *(INT32*)m_pUpperBoundImmediate = (INT32)(size_t)g_ephemeral_high; - needToFlushCache = true; - } - } - // - // INTENTIONAL FALL-THROUGH! - // - case WRITE_BARRIER_PREGROW32: - { - // Change immediate if different from new g_ephermeral_low. - if (*(INT32*)m_pLowerBoundImmediate != (INT32)(size_t)g_ephemeral_low) - { - *(INT32*)m_pLowerBoundImmediate = (INT32)(size_t)g_ephemeral_low; - needToFlushCache = true; - } - break; - } - case WRITE_BARRIER_POSTGROW64: +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + case WRITE_BARRIER_WRITE_WATCH_POSTGROW64: +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP { // Change immediate if different from new g_ephermeral_high. if (*(UINT64*)m_pUpperBoundImmediate != (size_t)g_ephemeral_high) @@ -473,6 +470,9 @@ void WriteBarrierManager::UpdateEphemeralBounds() // INTENTIONAL FALL-THROUGH! // case WRITE_BARRIER_PREGROW64: +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + case WRITE_BARRIER_WRITE_WATCH_PREGROW64: +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP { // Change immediate if different from new g_ephermeral_low. if (*(UINT64*)m_pLowerBoundImmediate != (size_t)g_ephemeral_low) @@ -484,12 +484,14 @@ void WriteBarrierManager::UpdateEphemeralBounds() } #ifdef FEATURE_SVR_GC - case WRITE_BARRIER_SVR32: case WRITE_BARRIER_SVR64: +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + case WRITE_BARRIER_WRITE_WATCH_SVR64: +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP { break; } -#endif +#endif // FEATURE_SVR_GC default: UNREACHABLE_MSG("unexpected m_currentWriteBarrier in UpdateEphemeralBounds"); @@ -501,7 +503,7 @@ void WriteBarrierManager::UpdateEphemeralBounds() } } -void WriteBarrierManager::UpdateCardTableLocation(BOOL bReqUpperBoundsCheck) +void WriteBarrierManager::UpdateWriteWatchAndCardTableLocations(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) { // If we are told that we require an upper bounds check (GC did some heap // reshuffling), we need to switch to the WriteBarrier_PostGrow function for @@ -510,7 +512,7 @@ void WriteBarrierManager::UpdateCardTableLocation(BOOL bReqUpperBoundsCheck) WriteBarrierType newType; if (NeedDifferentWriteBarrier(bReqUpperBoundsCheck, &newType)) { - ChangeWriteBarrierTo(newType); + ChangeWriteBarrierTo(newType, isRuntimeSuspended); return; } @@ -522,24 +524,30 @@ void WriteBarrierManager::UpdateCardTableLocation(BOOL bReqUpperBoundsCheck) bool fFlushCache = false; - if (m_currentWriteBarrier == WRITE_BARRIER_PREGROW32 || - m_currentWriteBarrier == WRITE_BARRIER_POSTGROW32 || - m_currentWriteBarrier == WRITE_BARRIER_SVR32) +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + switch (m_currentWriteBarrier) { - if (*(INT32*)m_pCardTableImmediate != (INT32)(size_t)g_card_table) - { - *(INT32*)m_pCardTableImmediate = (INT32)(size_t)g_card_table; - *(INT32*)m_pCardTableImmediate2 = (INT32)(size_t)g_card_table; - fFlushCache = true; - } + case WRITE_BARRIER_WRITE_WATCH_PREGROW64: + case WRITE_BARRIER_WRITE_WATCH_POSTGROW64: +#ifdef FEATURE_SVR_GC + case WRITE_BARRIER_WRITE_WATCH_SVR64: +#endif // FEATURE_SVR_GC + if (*(UINT64*)m_pWriteWatchTableImmediate != (size_t)SoftwareWriteWatch::GetTable()) + { + *(UINT64*)m_pWriteWatchTableImmediate = (size_t)SoftwareWriteWatch::GetTable(); + fFlushCache = true; + } + break; + + default: + break; // clang seems to require all enum values to be covered for some reason } - else +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP + + if (*(UINT64*)m_pCardTableImmediate != (size_t)g_card_table) { - if (*(UINT64*)m_pCardTableImmediate != (size_t)g_card_table) - { - *(UINT64*)m_pCardTableImmediate = (size_t)g_card_table; - fFlushCache = true; - } + *(UINT64*)m_pCardTableImmediate = (size_t)g_card_table; + fFlushCache = true; } if (fFlushCache) @@ -548,23 +556,100 @@ void WriteBarrierManager::UpdateCardTableLocation(BOOL bReqUpperBoundsCheck) } } +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +void WriteBarrierManager::SwitchToWriteWatchBarrier(bool isRuntimeSuspended) +{ + WriteBarrierType newWriteBarrierType; + switch (m_currentWriteBarrier) + { + case WRITE_BARRIER_UNINITIALIZED: + // Using the debug-only write barrier + return; + + case WRITE_BARRIER_PREGROW64: + newWriteBarrierType = WRITE_BARRIER_WRITE_WATCH_PREGROW64; + break; + + case WRITE_BARRIER_POSTGROW64: + newWriteBarrierType = WRITE_BARRIER_WRITE_WATCH_POSTGROW64; + break; + +#ifdef FEATURE_SVR_GC + case WRITE_BARRIER_SVR64: + newWriteBarrierType = WRITE_BARRIER_WRITE_WATCH_SVR64; + break; +#endif // FEATURE_SVR_GC + + default: + UNREACHABLE(); + } + + ChangeWriteBarrierTo(newWriteBarrierType, isRuntimeSuspended); +} + +void WriteBarrierManager::SwitchToNonWriteWatchBarrier(bool isRuntimeSuspended) +{ + WriteBarrierType newWriteBarrierType; + switch (m_currentWriteBarrier) + { + case WRITE_BARRIER_UNINITIALIZED: + // Using the debug-only write barrier + return; + + case WRITE_BARRIER_WRITE_WATCH_PREGROW64: + newWriteBarrierType = WRITE_BARRIER_PREGROW64; + break; + + case WRITE_BARRIER_WRITE_WATCH_POSTGROW64: + newWriteBarrierType = WRITE_BARRIER_POSTGROW64; + break; + +#ifdef FEATURE_SVR_GC + case WRITE_BARRIER_WRITE_WATCH_SVR64: + newWriteBarrierType = WRITE_BARRIER_SVR64; + break; +#endif // FEATURE_SVR_GC + + default: + UNREACHABLE(); + } + + ChangeWriteBarrierTo(newWriteBarrierType, isRuntimeSuspended); +} +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP // This function bashes the super fast amd64 version of the JIT_WriteBarrier // helper. It should be called by the GC whenever the ephermeral region // bounds get changed, but still remain on the top of the GC Heap. -void StompWriteBarrierEphemeral() +void StompWriteBarrierEphemeral(bool isRuntimeSuspended) { WRAPPER_NO_CONTRACT; - g_WriteBarrierManager.UpdateEphemeralBounds(); + g_WriteBarrierManager.UpdateEphemeralBounds(isRuntimeSuspended); } // This function bashes the super fast amd64 versions of the JIT_WriteBarrier // helpers. It should be called by the GC whenever the ephermeral region gets moved // from being at the top of the GC Heap, and/or when the cards table gets moved. -void StompWriteBarrierResize(BOOL bReqUpperBoundsCheck) +void StompWriteBarrierResize(bool isRuntimeSuspended, bool bReqUpperBoundsCheck) +{ + WRAPPER_NO_CONTRACT; + + g_WriteBarrierManager.UpdateWriteWatchAndCardTableLocations(isRuntimeSuspended, bReqUpperBoundsCheck); +} + +#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP +void SwitchToWriteWatchBarrier(bool isRuntimeSuspended) +{ + WRAPPER_NO_CONTRACT; + + g_WriteBarrierManager.SwitchToWriteWatchBarrier(isRuntimeSuspended); +} + +void SwitchToNonWriteWatchBarrier(bool isRuntimeSuspended) { WRAPPER_NO_CONTRACT; - g_WriteBarrierManager.UpdateCardTableLocation(bReqUpperBoundsCheck); + g_WriteBarrierManager.SwitchToNonWriteWatchBarrier(isRuntimeSuspended); } +#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP |