Skip to content

Commit e727fba

Browse files
author
Aman Khalid
committed
Fix unwind info when fake-splitting
Fake-splitting currently breaks stack walks by not generating unwind info for cold code. This commit implements unwind info on x86/64 when fake-splitting by generating unwind info for the combined hot/cold section just once, rather than generating unwind info for the separate sections. For reasons to be investigated, this implementation does not work when the code sections are separated by an arbitrary buffer (such as the 4KB buffer previously used). Thus, the buffer has been removed from the fake-splitting implementation: Now, the hot and cold sections are placed contiguously in memory, but the JIT continues to behave as if they are arbitrarily far away (for example, by using long branches between sections). Following this fix, fake-splitting no longer requires the GC to be suppressed by setting `COMPlus_GCgen0size=1000000`. A test job has been added to `runtime-jit-experimental` to ensure fake/stress-splitting does not regress.
1 parent 1f303a1 commit e727fba

File tree

8 files changed

+112
-42
lines changed

8 files changed

+112
-42
lines changed

eng/pipelines/common/templates/runtimes/run-test-job.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,7 @@ jobs:
572572
- jitosr_stress
573573
- jitosr_pgo
574574
- jitosr_stress_random
575+
- jit_stress_splitting
575576
- jitpartialcompilation
576577
- jitpartialcompilation_osr
577578
- jitpartialcompilation_osr_pgo

src/coreclr/jit/compiler.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7966,6 +7966,10 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
79667966
void unwindReserveFuncHelper(FuncInfoDsc* func, bool isHotCode);
79677967
void unwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode, void* pColdCode, bool isHotCode);
79687968

7969+
#ifdef DEBUG
7970+
void fakeUnwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode);
7971+
#endif // DEBUG
7972+
79697973
#endif // TARGET_AMD64 || (TARGET_X86 && FEATURE_EH_FUNCLETS)
79707974

79717975
UNATIVE_OFFSET unwindGetCurrentOffset(FuncInfoDsc* func);

src/coreclr/jit/ee_il_dll.cpp

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,14 +1125,13 @@ void Compiler::eeDispLineInfos()
11251125
void Compiler::eeAllocMem(AllocMemArgs* args)
11261126
{
11271127
#ifdef DEBUG
1128-
// Fake splitting implementation: hot section = hot code + 4K buffer + cold code
1129-
const UNATIVE_OFFSET hotSizeRequest = args->hotCodeSize;
1130-
const UNATIVE_OFFSET coldSizeRequest = args->coldCodeSize;
1131-
const UNATIVE_OFFSET fakeSplittingBuffer = 4096;
1128+
const UNATIVE_OFFSET hotSizeRequest = args->hotCodeSize;
1129+
const UNATIVE_OFFSET coldSizeRequest = args->coldCodeSize;
11321130

1131+
// Fake splitting implementation: place hot/cold code in contiguous section
11331132
if (JitConfig.JitFakeProcedureSplitting() && (coldSizeRequest > 0))
11341133
{
1135-
args->hotCodeSize = hotSizeRequest + fakeSplittingBuffer + coldSizeRequest;
1134+
args->hotCodeSize = hotSizeRequest + coldSizeRequest;
11361135
args->coldCodeSize = 0;
11371136
}
11381137
#endif
@@ -1143,8 +1142,8 @@ void Compiler::eeAllocMem(AllocMemArgs* args)
11431142
if (JitConfig.JitFakeProcedureSplitting() && (coldSizeRequest > 0))
11441143
{
11451144
// Fix up hot/cold code pointers
1146-
args->coldCodeBlock = ((BYTE*)args->hotCodeBlock) + hotSizeRequest + fakeSplittingBuffer;
1147-
args->coldCodeBlockRW = ((BYTE*)args->hotCodeBlockRW) + hotSizeRequest + fakeSplittingBuffer;
1145+
args->coldCodeBlock = ((BYTE*)args->hotCodeBlock) + hotSizeRequest;
1146+
args->coldCodeBlockRW = ((BYTE*)args->hotCodeBlockRW) + hotSizeRequest;
11481147

11491148
// Reset args' hot/cold code sizes in case caller reads them later
11501149
args->hotCodeSize = hotSizeRequest;
@@ -1161,13 +1160,6 @@ void Compiler::eeReserveUnwindInfo(bool isFunclet, bool isColdCode, ULONG unwind
11611160
printf("reserveUnwindInfo(isFunclet=%s, isColdCode=%s, unwindSize=0x%x)\n", isFunclet ? "true" : "false",
11621161
isColdCode ? "true" : "false", unwindSize);
11631162
}
1164-
1165-
// Fake splitting currently does not handle unwind info for cold code
1166-
if (isColdCode && JitConfig.JitFakeProcedureSplitting())
1167-
{
1168-
JITDUMP("reserveUnwindInfo for cold code with JitFakeProcedureSplitting enabled: ignoring cold unwind info\n");
1169-
return;
1170-
}
11711163
#endif // DEBUG
11721164

11731165
if (info.compMatchedVM)
@@ -1207,13 +1199,6 @@ void Compiler::eeAllocUnwindInfo(BYTE* pHotCode,
12071199
}
12081200
printf(")\n");
12091201
}
1210-
1211-
// Fake splitting currently does not handle unwind info for cold code
1212-
if (pColdCode && JitConfig.JitFakeProcedureSplitting())
1213-
{
1214-
JITDUMP("allocUnwindInfo for cold code with JitFakeProcedureSplitting enabled: ignoring cold unwind info\n");
1215-
return;
1216-
}
12171202
#endif // DEBUG
12181203

12191204
if (info.compMatchedVM)

src/coreclr/jit/emit.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6637,10 +6637,13 @@ unsigned emitter::emitEndCodeGen(Compiler* comp,
66376637
{
66386638
// The allocated chunk is bigger than used, fill in unused space in it.
66396639
unsigned unusedSize = allocatedHotCodeSize - emitCurCodeOffs(cp);
6640+
BYTE* cpRW = cp + writeableOffset;
66406641
for (unsigned i = 0; i < unusedSize; ++i)
66416642
{
6642-
*cp++ = DEFAULT_CODE_BUFFER_INIT;
6643+
*cpRW++ = DEFAULT_CODE_BUFFER_INIT;
66436644
}
6645+
6646+
cp = cpRW - writeableOffset;
66446647
assert(allocatedHotCodeSize == emitCurCodeOffs(cp));
66456648
}
66466649
}

src/coreclr/jit/jitconfigvalues.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,6 @@ CONFIG_INTEGER(JitDumpInlinePhases, W("JitDumpInlinePhases"), 1) // Dump inline
196196
CONFIG_METHODSET(JitEHDump, W("JitEHDump")) // Dump the EH table for the method, as reported to the VM
197197
CONFIG_METHODSET(JitExclude, W("JitExclude"))
198198
CONFIG_INTEGER(JitFakeProcedureSplitting, W("JitFakeProcedureSplitting"), 0) // Do code splitting independent of VM.
199-
// For now, this disables unwind info for
200-
// cold sections, breaking stack walks.
201-
// Set COMPlus_GCgen0size=1000000 to avoid
202-
// running the GC, which requires
203-
// stack-walking.
204199
CONFIG_METHODSET(JitForceProcedureSplitting, W("JitForceProcedureSplitting"))
205200
CONFIG_METHODSET(JitGCDump, W("JitGCDump"))
206201
CONFIG_METHODSET(JitDebugDump, W("JitDebugDump"))

src/coreclr/jit/unwindamd64.cpp

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -656,11 +656,21 @@ void Compiler::unwindReserve()
656656
//
657657
void Compiler::unwindReserveFunc(FuncInfoDsc* func)
658658
{
659-
unwindReserveFuncHelper(func, true);
660-
661-
if (fgFirstColdBlock != nullptr)
659+
#ifdef DEBUG
660+
if (JitConfig.JitFakeProcedureSplitting() && (fgFirstColdBlock != nullptr))
662661
{
663-
unwindReserveFuncHelper(func, false);
662+
assert(func->funKind == FUNC_ROOT); // No fake-splitting of funclets.
663+
unwindReserveFuncHelper(func, true);
664+
}
665+
else
666+
#endif // DEBUG
667+
{
668+
unwindReserveFuncHelper(func, true);
669+
670+
if (fgFirstColdBlock != nullptr)
671+
{
672+
unwindReserveFuncHelper(func, false);
673+
}
664674
}
665675
}
666676

@@ -880,12 +890,43 @@ void Compiler::unwindEmitFunc(FuncInfoDsc* func, void* pHotCode, void* pColdCode
880890
static_assert_no_msg(FUNC_HANDLER == (FuncKind)CORJIT_FUNC_HANDLER);
881891
static_assert_no_msg(FUNC_FILTER == (FuncKind)CORJIT_FUNC_FILTER);
882892

883-
unwindEmitFuncHelper(func, pHotCode, pColdCode, true);
893+
#ifdef DEBUG
894+
if (JitConfig.JitFakeProcedureSplitting() && (pColdCode != nullptr))
895+
{
896+
fakeUnwindEmitFuncHelper(func, pHotCode);
897+
}
898+
else
899+
#endif // DEBUG
900+
{
901+
unwindEmitFuncHelper(func, pHotCode, pColdCode, true);
902+
903+
if (pColdCode != nullptr)
904+
{
905+
unwindEmitFuncHelper(func, pHotCode, pColdCode, false);
906+
}
907+
}
908+
}
909+
910+
#ifdef DEBUG
911+
void Compiler::fakeUnwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode)
912+
{
913+
assert(fgFirstColdBlock != nullptr);
914+
assert(func->funKind == FUNC_ROOT); // No fake-splitting of funclets.
884915

885-
if (pColdCode != nullptr)
916+
const UNATIVE_OFFSET startOffset = 0;
917+
const UNATIVE_OFFSET endOffset = info.compNativeCodeSize;
918+
const DWORD unwindCodeBytes = sizeof(func->unwindCodes) - func->unwindCodeSlot;
919+
BYTE* pUnwindBlock = &func->unwindCodes[func->unwindCodeSlot];
920+
921+
if (opts.dspUnwind)
886922
{
887-
unwindEmitFuncHelper(func, pHotCode, pColdCode, false);
923+
DumpUnwindInfo(true, startOffset, endOffset, (const UNWIND_INFO* const)pUnwindBlock);
888924
}
925+
926+
// Pass pColdCode = nullptr; VM allocs unwind info for combined hot/cold section
927+
eeAllocUnwindInfo((BYTE*)pHotCode, nullptr, startOffset, endOffset, unwindCodeBytes, pUnwindBlock,
928+
(CorJitFuncKind)func->funKind);
889929
}
930+
#endif // DEBUG
890931

891932
#endif // TARGET_AMD64

src/coreclr/jit/unwindx86.cpp

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,21 @@ void Compiler::unwindEmit(void* pHotCode, void* pColdCode)
113113
//
114114
void Compiler::unwindReserveFunc(FuncInfoDsc* func)
115115
{
116-
unwindReserveFuncHelper(func, true);
117-
118-
if (fgFirstColdBlock != nullptr)
116+
#ifdef DEBUG
117+
if (JitConfig.JitFakeProcedureSplitting() && (fgFirstColdBlock != nullptr))
119118
{
120-
unwindReserveFuncHelper(func, false);
119+
assert(func->funKind == FUNC_ROOT); // No fake-splitting of funclets.
120+
unwindReserveFuncHelper(func, true);
121+
}
122+
else
123+
#endif // DEBUG
124+
{
125+
unwindReserveFuncHelper(func, true);
126+
127+
if (fgFirstColdBlock != nullptr)
128+
{
129+
unwindReserveFuncHelper(func, false);
130+
}
121131
}
122132
}
123133

@@ -154,11 +164,20 @@ void Compiler::unwindEmitFunc(FuncInfoDsc* func, void* pHotCode, void* pColdCode
154164
static_assert_no_msg(FUNC_HANDLER == (FuncKind)CORJIT_FUNC_HANDLER);
155165
static_assert_no_msg(FUNC_FILTER == (FuncKind)CORJIT_FUNC_FILTER);
156166

157-
unwindEmitFuncHelper(func, pHotCode, pColdCode, true);
158-
159-
if (pColdCode != nullptr)
167+
#ifdef DEBUG
168+
if (JitConfig.JitFakeProcedureSplitting() && (pColdCode != nullptr))
169+
{
170+
fakeUnwindEmitFuncHelper(func, pHotCode);
171+
}
172+
else
173+
#endif // DEBUG
160174
{
161-
unwindEmitFuncHelper(func, pHotCode, pColdCode, false);
175+
unwindEmitFuncHelper(func, pHotCode, pColdCode, true);
176+
177+
if (pColdCode != nullptr)
178+
{
179+
unwindEmitFuncHelper(func, pHotCode, pColdCode, false);
180+
}
162181
}
163182
}
164183

@@ -256,4 +275,23 @@ void Compiler::unwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode, void* pCo
256275
eeAllocUnwindInfo((BYTE*)pHotCode, (BYTE*)pColdCode, startOffset, endOffset, sizeof(UNWIND_INFO),
257276
(BYTE*)&unwindInfo, (CorJitFuncKind)func->funKind);
258277
}
278+
279+
#ifdef DEBUG
280+
void Compiler::fakeUnwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode)
281+
{
282+
assert(fgFirstColdBlock != nullptr);
283+
assert(func->funKind == FUNC_ROOT); // No fake-splitting of funclets.
284+
285+
const UNATIVE_OFFSET startOffset = 0;
286+
const UNATIVE_OFFSET endOffset = info.compNativeCodeSize;
287+
288+
UNWIND_INFO unwindInfo;
289+
unwindInfo.FunctionLength = (ULONG)(endOffset);
290+
291+
// Pass pColdCode = nullptr; VM allocs unwind info for combined hot/cold section
292+
eeAllocUnwindInfo((BYTE*)pHotCode, nullptr, startOffset, endOffset, sizeof(UNWIND_INFO), (BYTE*)&unwindInfo,
293+
(CorJitFuncKind)func->funKind);
294+
}
295+
#endif // DEBUG
296+
259297
#endif // FEATURE_EH_FUNCLETS

src/tests/Common/testenvironment.proj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@
4242
COMPlus_HeapVerify;
4343
COMPlus_JITMinOpts;
4444
COMPlus_JitELTHookEnabled;
45+
COMPlus_JitFakeProcedureSplitting;
4546
COMPlus_JitStress;
47+
COMPlus_JitStressProcedureSplitting;
4648
COMPlus_JitStressRegs;
4749
COMPlus_TailcallStress;
4850
COMPlus_ReadyToRun;
@@ -188,6 +190,7 @@
188190
<TestEnvironment Include="jitosr" TC_OnStackReplacement="1" TC_QuickJitForLoops="1" TieredCompilation="1" />
189191
<TestEnvironment Include="jitosr_stress" TC_OnStackReplacement="1" TC_QuickJitForLoops="1" TC_OnStackReplacement_InitialCounter="1" OSR_HitLimit="1" TieredCompilation="1" />
190192
<TestEnvironment Include="jitosr_stress_random" TC_OnStackReplacement="1" TC_QuickJitForLoops="1" TC_OnStackReplacement_InitialCounter="1" OSR_HitLimit="2" TieredCompilation="1" JitRandomOnStackReplacement="15"/>
193+
<TestEnvironment Include="jit_stress_splitting" JitFakeProcedureSplitting="1" JitStressProcedureSplitting="1" />
191194
<TestEnvironment Include="jitosr_pgo" TC_OnStackReplacement="1" TC_QuickJitForLoops="1" TieredCompilation="1" TieredPGO="1" />
192195
<TestEnvironment Include="jitpartialcompilation" TC_PartialCompilation="1" TC_QuickJitForLoops="1" TieredCompilation="1" />
193196
<TestEnvironment Include="jitpartialcompilation_pgo" TC_PartialCompilation="1" TC_QuickJitForLoops="1" TieredCompilation="1" TieredPGO="1" />

0 commit comments

Comments
 (0)