summaryrefslogtreecommitdiff
path: root/src/vm/amd64
diff options
context:
space:
mode:
authorKoundinya Veluri <kouvel@users.noreply.github.com>2016-04-12 21:19:01 -0700
committerKoundinya Veluri <kouvel@users.noreply.github.com>2016-04-12 21:19:01 -0700
commitafdce3a592e5f6f2047bed057d121225be91743d (patch)
tree7ab5637597f3daae58dbf1929abd363cbfe0b619 /src/vm/amd64
parent5ef243f597b8198efce1add587f16aae59b1f568 (diff)
parentc235ae17cd3a87f8032948bdcb838641d8e6c055 (diff)
downloadcoreclr-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.asm84
-rw-r--r--src/vm/amd64/JitHelpers_FastWriteBarriers.asm228
-rw-r--r--src/vm/amd64/JitHelpers_Slow.asm18
-rw-r--r--src/vm/amd64/jithelpers_fast.S160
-rw-r--r--src/vm/amd64/jithelpers_fastwritebarriers.S247
-rw-r--r--src/vm/amd64/jithelpers_slow.S15
-rw-r--r--src/vm/amd64/jitinterfaceamd64.cpp455
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