diff options
author | dotnet-bot <dotnet-bot@microsoft.com> | 2017-08-08 01:26:23 +0300 |
---|---|---|
committer | dotnet-bot <dotnet-bot@microsoft.com> | 2017-08-08 01:26:23 +0300 |
commit | 1bc2b144bd978d441c6afff399e33081edf41f18 (patch) | |
tree | 1d5dbd674d725956edfb7843e54c279991001925 /src/Native/Runtime/threadstore.cpp | |
parent | 980f02121ea435fd5610deafd6248376f32bd246 (diff) |
ProjectX: Loop Hijack for GC poll
This change enables hijacking loops for GC poll. A loop needs a GC poll if it's not proved to have other GC hijack points, such as function calls.
Unlike the current table-based approach adopted in ProjectN, a piece of hijack code is injected to the loop to lead the control flow to the runtime for GC. For example, a loop usually has a conditional or unconditional branch at each of its back edge.
$LN10:
// loop body
test eax, eax
jne $LN10,
With this change the branch code will be changed to
cmp LoopHijackFlag, 0
jne $LNStub
$LNsource:
test eax, eax
jne $LN10
LNStub: (at the end of function)
call RhpLoopHijackX
jmp $LNsource;
A compare-and-jump is injected directly to the loop body while the hijacking code is placed at the end of the function since it's considered "cold code" assuming GC does not happened very often.
The global variable LoopHijackFlag is per module since we don't want to occupy or trash a register for it inside the loop.
The runtime helper RhpLoopHijackX is designed the preserve every register including callee-save registers and scratch registers so that no register used in the loop is trashed.
The transform happens very late in UTC in order not to affect the register allocation and to catch lower-introduced loops.
The change only works for Od mode for now. In Ox mode, a liveness analysis of up-exposed CC flag register use is needed so that the LoopHijackFlag check doesn't trash the CC register.
Testing:
The Nutc\BugFixes\BusyWait.proj test which is specifically designed to test this feature passed
No regression to other ToF tests
[tfs-changeset: 1669432]
Diffstat (limited to 'src/Native/Runtime/threadstore.cpp')
-rw-r--r-- | src/Native/Runtime/threadstore.cpp | 7 |
1 files changed, 7 insertions, 0 deletions
diff --git a/src/Native/Runtime/threadstore.cpp b/src/Native/Runtime/threadstore.cpp index 3bbd16b89..3342b7e42 100644 --- a/src/Native/Runtime/threadstore.cpp +++ b/src/Native/Runtime/threadstore.cpp @@ -229,6 +229,9 @@ void ThreadStore::SuspendAllThreads(CLREventStatic* pCompletionEvent) // set the global trap for pinvoke leave and return RhpTrapThreads |= (UInt32)TrapThreadsFlags::TrapThreads; + // Set each module's loop hijack flag + GetRuntimeInstance()->SetLoopHijackFlags(RhpTrapThreads); + // Our lock-free algorithm depends on flushing write buffers of all processors running RH code. The // reason for this is that we essentially implement Dekker's algorithm, which requires write ordering. PalFlushProcessWriteBuffers(); @@ -292,6 +295,10 @@ void ThreadStore::ResumeAllThreads(CLREventStatic* pCompletionEvent) END_FOREACH_THREAD RhpTrapThreads &= ~(UInt32)TrapThreadsFlags::TrapThreads; + + // Reset module's hijackLoops flag + GetRuntimeInstance()->SetLoopHijackFlags(0); + RhpSuspendingThread = NULL; pCompletionEvent->Set(); UnlockThreadStore(); |