Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fe5bb85
wip
pavelsavara Aug 25, 2026
ab76425
Define ep_rt_session_stopping no-op for NativeAOT eventpipe
pavelsavara Aug 25, 2026
1d4dd47
Flush PGO data before taking the EventPipe lock on session stop
pavelsavara Aug 25, 2026
47d4eab
enable BlazorEventPipeTestWithCpuSamples
pavelsavara Aug 25, 2026
b7df191
WBT test for PGO trace
pavelsavara Aug 26, 2026
38ef900
feedback
pavelsavara Aug 26, 2026
0b4176a
fix event pipe early collection
pavelsavara Aug 26, 2026
62594a2
Fix WBT dotnet-pgo copy: pre-create nested output dirs (amd64/arm64)
pavelsavara Aug 26, 2026
9e738b6
fix WBT linux
pavelsavara Sep 9, 2026
3f302aa
Deploy dotnet-pgo into WBT Helix payload via CopyToOutputDirectory
pavelsavara Sep 10, 2026
6c453c9
Restore dotnet-pgo for WBT via Private=false ProjectReference
pavelsavara Sep 10, 2026
9c215f5
Fix dotnet-pgo payload glob dropping runtimeconfig.json
pavelsavara Sep 11, 2026
1abefcb
Address PR review feedback for interpreter PGO
pavelsavara Sep 11, 2026
a983705
Correct ep_rt_session_stopping lock-contract comment
pavelsavara Sep 11, 2026
194b6a8
Address follow-up PR review on interpreter PGO
pavelsavara Sep 11, 2026
b3ec395
Scope interpreter PGO flush to the stopping session
pavelsavara Sep 11, 2026
97d7a19
Scope PGO block instrumentation to header IL range; assert on MT WASM…
pavelsavara Sep 16, 2026
c9b1e4b
Route PGO flush to the stopping session via rundown thread
pavelsavara Sep 16, 2026
3f7c7cd
Scope interpreter PGO to single-threaded WASM; mask-gate MT assert
pavelsavara Sep 16, 2026
c79ea57
Pass session mask to session-stopping hook; add pgo-trace to rollup s…
pavelsavara Sep 16, 2026
0426f3d
Drop 'desktop' from PGO keyword comment
pavelsavara Sep 16, 2026
3c52a09
Merge branch 'main' into wasm_collect_PGO
pavelsavara Sep 16, 2026
9727640
Split EventPipe PGO emission from text-file export
pavelsavara Sep 17, 2026
6055010
Make browser PGO trace collection one-shot per process
pavelsavara Sep 17, 2026
a47e597
Merge branch 'main' into wasm_collect_PGO
pavelsavara Sep 17, 2026
dd1081a
cleanup
pavelsavara Sep 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/coreclr/clrfeatures.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ if (FEATURE_DYNAMIC_CODE_COMPILED)
set(FEATURE_PGO 1)
endif()

if (NOT DEFINED FEATURE_PGO AND CLR_CMAKE_TARGET_ARCH_WASM)
set(FEATURE_PGO 1)
endif()

# On desktop, if dynamic code compiled is false, we still enable static linking so we don't have to add platform manifest entries
# for interpreter library, which is required for the packs build
if (CLR_CMAKE_TARGET_ARCH_WASM OR CLR_CMAKE_TARGET_APPLE_MOBILE OR NOT FEATURE_DYNAMIC_CODE_COMPILED)
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/inc/clrconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ CONFIG_DWORD_INFO(INTERNAL_OSR_HighId, W("OSR_HighId"), 10000000, "High end of e
RETAIL_CONFIG_STRING_INFO(INTERNAL_PGODataPath, W("PGODataPath"), "Read/Write PGO data from/to the indicated file.")
RETAIL_CONFIG_DWORD_INFO(INTERNAL_ReadPGOData, W("ReadPGOData"), 0, "Read PGO data")
RETAIL_CONFIG_DWORD_INFO(INTERNAL_WritePGOData, W("WritePGOData"), 0, "Write PGO data")
RETAIL_CONFIG_DWORD_INFO(INTERNAL_InterpPGO, W("InterpPGO"), 0, "Instrument interpreted methods with block counters and make the profile available for offline PGO (e.g. dotnet-pgo).")
RETAIL_CONFIG_DWORD_INFO(EXTERNAL_TieredPGO, W("TieredPGO"), 1, "Instrument Tier0 code and make counts available to Tier1")

// TieredPGO_InstrumentOnlyHotCode values:
Expand Down
95 changes: 95 additions & 0 deletions src/coreclr/interpreter/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ bool InterpCompiler::s_samplingProfilerEnabled = false;
bool InterpCompiler::s_browserProfilerEnabled = false;
#endif
#endif // PERFTRACING_DISABLE_THREADS
#if (defined(TARGET_BROWSER) || defined(TARGET_WASI)) && defined(PERFTRACING_DISABLE_THREADS)
bool InterpCompiler::s_interpPgoEnabled = false;
#endif // (TARGET_BROWSER || TARGET_WASI) && PERFTRACING_DISABLE_THREADS

#if MEASURE_MEM_ALLOC
#include <minipal/mutex.h>
Expand Down Expand Up @@ -2246,6 +2249,12 @@ InterpCompiler::InterpCompiler(COMP_HANDLE compHnd,
#endif
#endif // PERFTRACING_DISABLE_THREADS

#if (defined(TARGET_BROWSER) || defined(TARGET_WASI)) && defined(PERFTRACING_DISABLE_THREADS)
m_emitInterpPGO = s_interpPgoEnabled
&& (InterpConfig.InterpPGOMethods().isEmpty()
|| InterpConfig.InterpPGOMethods().contains(compHnd, m_methodHnd, m_classHnd, &m_methodInfo->args));
#endif // (TARGET_BROWSER || TARGET_WASI) && PERFTRACING_DISABLE_THREADS

#ifdef DEBUG
m_methodName = ::PrintMethodName(compHnd, m_classHnd, m_methodHnd, &m_methodInfo->args,
/* includeAssembly */ false,
Expand Down Expand Up @@ -2348,6 +2357,11 @@ bool InterpCompiler::CompileMethod()
}
#endif

#if (defined(TARGET_BROWSER) || defined(TARGET_WASI)) && defined(PERFTRACING_DISABLE_THREADS)
if (m_emitInterpPGO)
InstrumentBlockCounts();
#endif // (TARGET_BROWSER || TARGET_WASI) && PERFTRACING_DISABLE_THREADS

AllocOffsets();
PatchInitLocals(m_methodInfo);

Expand Down Expand Up @@ -8668,6 +8682,87 @@ void InterpCompiler::CreateSynchronizedRetValVar()
INTERP_DUMP("Created ret val var V%d\n", m_synchronizedOrAsyncRetValVarIndex);
}

#if (defined(TARGET_BROWSER) || defined(TARGET_WASI)) && defined(PERFTRACING_DISABLE_THREADS)
// Instrument each basic block with a block-count PGO probe. The counters are allocated by
// allocPgoInstrumentationBySchema (native PgoManager memory), so they persist independently of
// EventPipe session lifetime; the accumulated profile is flushed to the trace as
// JitInstrumentationDataVerbose events, which dotnet-pgo consumes to build an .mibc.
void InterpCompiler::InstrumentBlockCounts()
{

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really need all this logic from here ? Can we simply set a bit on the basic block to signal that it is the target of a backwards branch (as we already check here for safepoint purposes). Then we could simply emit the INTOP_PGO_COUNT as separate simple pass or when doing final code emit for the bblock in question (in EmitCode). I thought this profiling was meant for loops but we seem to generate the tracking opcode for all branch targets.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we instrument all branch/switch/leave targets, not just loop heads ?

🤖 "The real reason is weighted flow-graph reconstruction"
https://gist.github.com/pavelsavara/c53070aff3e2f46d5a6e840f13d34607

I don't know how relevant that is to WASM. @AndyAyersMS @davidwrighton, should we simplify the trace ?

@BrzVlad if we don't need all targets, then we can re-use the INTOP_PROF_SAMPLEPOINT place.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review pointed out below that maybe we need even more blocks to be instrumented.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well... it is plausible that information from all branches is valuable, but without building and measuring, it's hard to say how valuable. If you can predict non-looping branches you can predict hot vs cold paths, which could possibly inform details for branch hinting(https://github.com/WebAssembly/branch-hinting/blob/master/proposals/branch-hinting/Overview.md) or adjust optimization decisions (should the optimizer make size/speed tradeoffs). Historically we've seen some value from that level of precision, but I wouldn't say that its perfect. The mibc format and this data collection pipeline is capable of capturing edge/block level data and which could be fed from the interpreter into the jit. So, my preference would be to err on the side of capturing too much data. Unfortunately, we currently don't have what I would consider to be a clean set of performance data in any form, so the improvements from branch level adjustments is likely impossible to see right now.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we only collect hits from method entry and end of the loop, would it still be a valid .mibc ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well... it is plausible that information from all branches is valuable, but without building and measuring, it's hard to say how valuable. If you can predict non-looping branches you can predict hot vs cold paths, which could possibly inform details for branch hinting(https://github.com/WebAssembly/branch-hinting/blob/master/proposals/branch-hinting/Overview.md) or adjust optimization decisions (should the optimizer make size/speed tradeoffs). Historically we've seen some value from that level of precision, but I wouldn't say that its perfect. The mibc format and this data collection pipeline is capable of capturing edge/block level data and which could be fed from the interpreter into the jit. So, my preference would be to err on the side of capturing too much data. Unfortunately, we currently don't have what I would consider to be a clean set of performance data in any form, so the improvements from branch level adjustments is likely impossible to see right now.

#133795

// Mark blocks that are the target of a branch or switch (loop and branch heads). Together with
// the method entry, these are the block heads whose execution count can't be inferred from a
// single predecessor, so only they are worth a counter; the precompiler reconstructs the rest of
// the flow graph from them. This deliberately avoids the JIT's edge/spanning-tree scheme (#130517).
bool *isBranchTarget = getAllocator(IMK_BasicBlock).allocateZeroed<bool>(m_BBCount);
for (InterpBasicBlock *bb = m_pEntryBB; bb != NULL; bb = bb->pNextBB)
{
for (InterpInst *ins = bb->pFirstIns; ins != NULL; ins = ins->pNext)
{
if (ins->opcode == INTOP_SWITCH)
{
int32_t n = ins->data[0];
for (int32_t i = 0; i < n; i++)
isBranchTarget[ins->info.ppTargetBBTable[i]->index] = true;
}
else if (InterpOpIsUncondBranch(ins->opcode) || InterpOpIsCondBranch(ins->opcode) ||
ins->opcode == INTOP_LEAVE_CATCH || ins->opcode == INTOP_CALL_FINALLY)
{
isBranchTarget[ins->info.pTargetBB->index] = true;
}
}
}

// Collect the canonical block for each real IL offset that is the method entry (IL offset 0) or
// a branch/loop target. Clones (funclet / leave-chain islands) and blocks removed by optimization
// are skipped, so every schema entry carries a unique IL offset, matching what
// getPgoInstrumentationResults and dotnet-pgo expect.
TArray<InterpBasicBlock*, MemPoolAllocator> blocks(GetMemPoolAllocator(IMK_DataItem));
for (InterpBasicBlock *bb = m_pEntryBB; bb != NULL; bb = bb->pNextBB)
{
if (bb->ilOffset < 0 || bb->ilOffset >= m_ILCodeSizeFromILHeader || m_ppOffsetToBB[bb->ilOffset] != bb)
continue;
if (bb->ilOffset == 0 || isBranchTarget[bb->index])
blocks.Add(bb);
Comment on lines +8724 to +8725

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is flip side of Vlad's question above.

}

int32_t numBlocks = blocks.GetSize();
if (numBlocks == 0)
return;

// One 4-byte block counter per block.
TArray<ICorJitInfo::PgoInstrumentationSchema, MemPoolAllocator> schema(GetMemPoolAllocator(IMK_DataItem));
for (int32_t i = 0; i < numBlocks; i++)
{
ICorJitInfo::PgoInstrumentationSchema schemaElem;
schemaElem.Offset = 0;
schemaElem.InstrumentationKind = ICorJitInfo::PgoInstrumentationKind::BasicBlockIntCount;
schemaElem.ILOffset = blocks.Get(i)->ilOffset;
schemaElem.Count = 1;
schemaElem.Other = 0;
schema.Add(schemaElem);
}

ICorJitInfo::PgoInstrumentationSchema *pSchema = schema.GetUnderlyingArray();
uint8_t *pInstrumentationData = NULL;
HRESULT hr = m_compHnd->allocPgoInstrumentationBySchema(m_methodHnd, pSchema, (uint32_t)numBlocks, &pInstrumentationData);
if (FAILED(hr) || pInstrumentationData == NULL)
{
INTERP_DUMP("InstrumentBlockCounts: allocPgoInstrumentationBySchema failed (hr=0x%08x)\n", hr);
return;
}

// Insert an INTOP_PGO_COUNT probe at the start of each block, pointing at its counter.
for (int32_t i = 0; i < numBlocks; i++)
{
uint32_t *pCounter = (uint32_t*)(pInstrumentationData + pSchema[i].Offset);
InterpInst *ins = InsertInsBB(blocks.Get(i), NULL, INTOP_PGO_COUNT);
// Probe is a pure counter increment with no IL mapping; keep it out of the debug maps.
ins->ilOffset = -1;
ins->data[0] = GetDataItemIndex((void*)pCounter);
Comment thread
pavelsavara marked this conversation as resolved.
}
}
#endif // (TARGET_BROWSER || TARGET_WASI) && PERFTRACING_DISABLE_THREADS

void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo)
{
bool readonly = false;
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/interpreter/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,10 @@ class InterpCompiler
#endif
#endif // PERFTRACING_DISABLE_THREADS

#if (defined(TARGET_BROWSER) || defined(TARGET_WASI)) && defined(PERFTRACING_DISABLE_THREADS)
bool m_emitInterpPGO;
#endif // (TARGET_BROWSER || TARGET_WASI) && PERFTRACING_DISABLE_THREADS

void DeclarePointerIsClass(CORINFO_CLASS_HANDLE clsHnd)
{
#ifdef DEBUG
Expand Down Expand Up @@ -767,6 +771,10 @@ class InterpCompiler
void CreateSynchronizedRetValVar();

void GenerateCode(CORINFO_METHOD_INFO* methodInfo);

#if (defined(TARGET_BROWSER) || defined(TARGET_WASI)) && defined(PERFTRACING_DISABLE_THREADS)
void InstrumentBlockCounts();
#endif // (TARGET_BROWSER || TARGET_WASI) && PERFTRACING_DISABLE_THREADS
InterpBasicBlock* GenerateCodeForLeaveChainIslands(InterpBasicBlock *pNewBB, InterpBasicBlock *pPrevBB);
void PatchInitLocals(CORINFO_METHOD_INFO* methodInfo);

Expand Down Expand Up @@ -1150,6 +1158,9 @@ class InterpCompiler
static bool s_browserProfilerEnabled;
#endif
#endif // PERFTRACING_DISABLE_THREADS
#if (defined(TARGET_BROWSER) || defined(TARGET_WASI)) && defined(PERFTRACING_DISABLE_THREADS)
static bool s_interpPgoEnabled;
#endif // (TARGET_BROWSER || TARGET_WASI) && PERFTRACING_DISABLE_THREADS

#if MEASURE_MEM_ALLOC
// Memory statistics for profiling.
Expand Down
7 changes: 7 additions & 0 deletions src/coreclr/interpreter/eeinterp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ extern "C" INTERP_API void jitStartup(ICorJitHost* jitHost)
#endif // PERFTRACING_DISABLE_THREADS
}

if (InterpConfig.InterpPGO() != 0)
{
#if (defined(TARGET_BROWSER) || defined(TARGET_WASI)) && defined(PERFTRACING_DISABLE_THREADS)
InterpCompiler::s_interpPgoEnabled = true;
#endif // (TARGET_BROWSER || TARGET_WASI) && PERFTRACING_DISABLE_THREADS
}

g_interpInitialized = true;
}
/*****************************************************************************/
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/interpreter/inc/intops.def
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ OPDEF(INTOP_PROF_ENTER, "prof.enter", 2, 0, 0, InterpOpMethodHandle)
OPDEF(INTOP_PROF_LEAVE, "prof.leave", 1, 0, 0, InterpOpNoArgs)
#endif

#if (defined(TARGET_BROWSER) || defined(TARGET_WASI)) && defined(PERFTRACING_DISABLE_THREADS)
OPDEF(INTOP_PGO_COUNT, "pgo.count", 2, 0, 0, InterpOpLdPtr)
#endif

OPDEF(INTOP_BR, "br", 2, 0, 0, InterpOpBranch)

OPDEF(INTOP_BRFALSE_I4, "brfalse.i4", 3, 0, 1, InterpOpBranch)
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/interpreter/interpconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ RELEASE_CONFIG_INTEGER(InterpMode, "InterpMode", 0); // Interpreter mode, one of

RELEASE_CONFIG_INTEGER(DisplayMemStats, "JitMemStats", 0); // Display interpreter memory usage statistics (0=off, 1=summary, 2=detailed per-method)
RELEASE_CONFIG_METHODSET(WasmPerformanceInstrumentation, "WasmPerformanceInstrumentation") // Method filter for WASM performance instrumentation profiler. Uses standard MethodSet pattern format.
RELEASE_CONFIG_INTEGER(InterpPGO, "InterpPGO", 0); // Instrument interpreted methods with block counters and make the profile available for offline PGO (e.g. dotnet-pgo).
RELEASE_CONFIG_METHODSET(InterpPGOMethods, "InterpPGOMethods") // Optional method filter scoping InterpPGO instrumentation (empty = all methods). Uses standard MethodSet pattern format.

#undef CONFIG_STRING
#undef RELEASE_CONFIG_STRING
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/nativeaot/Runtime/eventpipe/ep-rt-aot.h
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,16 @@ ep_rt_notify_profiler_provider_created (EventPipeProvider *provider)
// Following mono's path of no-op
}

static
inline
void
ep_rt_session_stopping (EventPipeSessionID session_id, uint64_t session_mask)
{
// Following mono's path of no-op
(void)session_id;
(void)session_mask;
}

/*
* Arrays.
*/
Expand Down
48 changes: 48 additions & 0 deletions src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@
#ifdef ENABLE_PERFTRACING
#include <eventpipe/ep-types.h>
#include <eventpipe/ep.h>
#include <eventpipe/ep-event.h>
#include <eventpipe/ep-session.h>
#include <eventpipe/ep-stack-contents.h>
#include <eventpipe/ep-rt.h>
#include "threadsuspend.h"
#ifdef FEATURE_PGO
#include "pgo.h"
#endif

ep_rt_lock_handle_t _ep_rt_coreclr_config_lock_handle;
CrstStatic _ep_rt_coreclr_config_lock;
Expand Down Expand Up @@ -169,4 +174,47 @@ ep_rt_coreclr_sample_profiler_write_sampling_event_for_threads (
return;
}

void
ep_rt_coreclr_session_stopping (EventPipeSessionID session_id, uint64_t session_mask)
{
STATIC_CONTRACT_NOTHROW;
#if defined(FEATURE_PGO) && defined(PERFTRACING_DISABLE_THREADS)
// Flush block-count PGO only into the session that enabled the JitInstrumentationData events
extern EventPipeEvent *EventPipeEventJitInstrumentationDataVerbose;
if (EventPipeEventJitInstrumentationDataVerbose != NULL &&
ep_event_is_enabled_by_mask (EventPipeEventJitInstrumentationDataVerbose, session_mask))
{
// Mark this thread as a rundown thread bound to the stopping session so the events emitted by the
// flush are routed to that single session (ep_session_write_event) instead of broadcast to every
// enabled session; the marker is cleared after the flush, including on exception. Dereferencing the
// session is safe here: this path is single-threaded (PERFTRACING_DISABLE_THREADS), so nothing frees
// it before section2 disables it.
EventPipeSession *session = reinterpret_cast<EventPipeSession *>(static_cast<uintptr_t>(session_id));
EventPipeThread *thread = ep_thread_get_or_create ();
if (thread != NULL)
{
Comment thread
pavelsavara marked this conversation as resolved.
ep_thread_set_as_rundown_thread (thread, session);
EX_TRY
{
PgoManager::EmitInstrumentationDataToEventPipe ();
}
EX_CATCH { }
EX_END_CATCH
ep_thread_set_as_rundown_thread (thread, NULL);
}
}
Comment thread
pavelsavara marked this conversation as resolved.
#elif defined(FEATURE_PGO) && (defined(TARGET_BROWSER) || defined(TARGET_WASI))
extern EventPipeEvent *EventPipeEventJitInstrumentationDataVerbose;
if (EventPipeEventJitInstrumentationDataVerbose != NULL &&
ep_event_is_enabled_by_mask (EventPipeEventJitInstrumentationDataVerbose, session_mask))
{
PORTABILITY_ASSERT ("Interpreter block-count PGO flush is not implemented for multithreaded WASM (requires PERFTRACING_DISABLE_THREADS).");
}
(void)session_id;
#else
(void)session_id;
(void)session_mask;
#endif // FEATURE_PGO && PERFTRACING_DISABLE_THREADS
}

#endif /* ENABLE_PERFTRACING */
19 changes: 17 additions & 2 deletions src/coreclr/vm/eventing/eventpipe/ep-rt-coreclr.h
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,16 @@ ep_rt_notify_profiler_provider_created (EventPipeProvider *provider)
#endif // !DACCESS_COMPILE && PROFILING_SUPPORTED
}

static
inline
void
ep_rt_session_stopping (EventPipeSessionID session_id, uint64_t session_mask)
{
STATIC_CONTRACT_NOTHROW;
extern void ep_rt_coreclr_session_stopping (EventPipeSessionID session_id, uint64_t session_mask);
ep_rt_coreclr_session_stopping (session_id, session_mask);
}

/*
* Arrays.
*/
Expand Down Expand Up @@ -1093,8 +1103,13 @@ ep_rt_queue_job (
void *params)
{
#ifdef HOST_BROWSER
// In single-threaded mode the job runs on the browser event loop
SystemJS_DiagnosticServerQueueJob ((ep_rt_job_cb_t)job_func, params);
// In single-threaded mode the job runs on the browser event loop. Run the callback inline the
// first time so the diagnostic server makes progress synchronously (e.g. it can connect and
// resume during startup suspension) and only defer a re-schedule if it isn't done yet. Mirrors
// the Mono ep_rt_queue_job in ep-rt-mono.h.
ep_rt_job_cb_t cb = (ep_rt_job_cb_t)job_func;
if (!cb (params))
SystemJS_DiagnosticServerQueueJob (cb, params);
return true;
#else
EP_UNREACHABLE ("Not implemented on this platform");
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/vm/interpexec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2088,6 +2088,14 @@ void InterpExecMethod(InterpreterFrame *pInterpreterFrame, InterpMethodContextFr
INTOP_NEXT;
#endif // TARGET_BROWSER && PERFTRACING_DISABLE_THREADS

#if (defined(TARGET_BROWSER) || defined(TARGET_WASI)) && defined(PERFTRACING_DISABLE_THREADS)
INTOP_CASE(INTOP_PGO_COUNT)
// Increment the block-count PGO counter whose address is stored as a data item.
(*(uint32_t*)pMethod->pDataItems[ip[1]])++;
ip += 2;
INTOP_NEXT;
#endif // (TARGET_BROWSER || TARGET_WASI) && PERFTRACING_DISABLE_THREADS

INTOP_CASE(INTOP_BR)
ip += ip[1];
INTOP_NEXT;
Expand Down
27 changes: 20 additions & 7 deletions src/coreclr/vm/jitinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13061,7 +13061,19 @@ CORJIT_FLAGS* CEECodeGenInfo::getJitFlagsInternal()
}

/*********************************************************************/
HRESULT CEEJitInfo::allocPgoInstrumentationBySchema(
#ifdef FEATURE_PGO
static bool InterpreterPgoInstrumentationEnabled()
{
#if defined(TARGET_BROWSER) || defined(TARGET_WASI)
static ConfigDWORD s_interpPgo;
return s_interpPgo.val(CLRConfig::INTERNAL_InterpPGO) != 0;
#else
return false;
#endif
}
#endif // FEATURE_PGO

HRESULT CEECodeGenInfo::allocPgoInstrumentationBySchema(
CORINFO_METHOD_HANDLE ftnHnd, /* IN */
PgoInstrumentationSchema* pSchema, /* IN/OUT */
uint32_t countSchemaItems, /* IN */
Expand All @@ -13080,9 +13092,10 @@ HRESULT CEEJitInfo::allocPgoInstrumentationBySchema(

#ifdef FEATURE_PGO

// Only try instrumenting tiering-eligible methods
// Only try instrumenting tiering-eligible methods, unless interpreter PGO is enabled, in
// which case we instrument every method for offline profile collection.
MethodDesc* pMD = (MethodDesc*)ftnHnd;
if (pMD->IsEligibleForTieredCompilation())
if (pMD->IsEligibleForTieredCompilation() || InterpreterPgoInstrumentationEnabled())
{
Comment thread
pavelsavara marked this conversation as resolved.
hr = PgoManager::allocPgoInstrumentationBySchema(pMD, m_ILHeader, pSchema, countSchemaItems, pInstrumentationData);
}
Expand All @@ -13091,7 +13104,7 @@ HRESULT CEEJitInfo::allocPgoInstrumentationBySchema(
hr = E_NOTIMPL;
}
#else
_ASSERTE(!"allocMethodBlockCounts not implemented on CEEJitInfo!");
_ASSERTE(!"allocMethodBlockCounts not implemented on CEECodeGenInfo!");
hr = E_NOTIMPL;
#endif // !FEATURE_PGO

Expand All @@ -13100,9 +13113,9 @@ HRESULT CEEJitInfo::allocPgoInstrumentationBySchema(
return hr;
}

// Consider implementing getBBProfileData on CEEJitInfo. This will allow us
// Consider implementing getBBProfileData on CEECodeGenInfo. This will allow us
// to use profile info in codegen for non zapped images.
HRESULT CEEJitInfo::getPgoInstrumentationResults(
HRESULT CEECodeGenInfo::getPgoInstrumentationResults(
CORINFO_METHOD_HANDLE ftnHnd,
PgoInstrumentationSchema **pSchema, // pointer to the schema table which describes the instrumentation results (pointer will not remain valid after jit completes)
uint32_t * pCountSchemaItems, // pointer to the count schema items
Expand Down Expand Up @@ -13163,7 +13176,7 @@ HRESULT CEEJitInfo::getPgoInstrumentationResults(
*pPgoSource = pDataCur->m_pgoSource;
hr = pDataCur->m_hr;
#else
_ASSERTE(!"getPgoInstrumentationResults not implemented on CEEJitInfo!");
_ASSERTE(!"getPgoInstrumentationResults not implemented on CEECodeGenInfo!");
hr = E_NOTIMPL;
#endif

Expand Down
Loading
Loading