Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
194 changes: 148 additions & 46 deletions src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,63 +164,138 @@ static PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntim
#endif
}

// index nodes are searched linearly.
// 16 * sizeof(uint32_t) == 64, which is a typical cache line size
// thus we expect at most one cache miss on every level of the index
#define INDEX_BRANCHING_FACTOR 16
// T_RUNTIME_FUNCTION is larger than uint32_t, so we have a smaller granularity at last level
#define FUNCTABLE_INDEX_GRANULARITY 8
#define INDEX_ALIGNMENT 64

CoffNativeCodeManager::CoffNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)
: m_moduleBase(moduleBase),
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions)
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions),
m_initializedIndices(0), m_indexCount(0), m_indices{ 0 }
{
}

CoffNativeCodeManager::~CoffNativeCodeManager()
{
for (uint32_t i = 0; i < m_indexCount; i++)
{
uint32_t* ptr = m_indices[i];
if (ptr)
{
_aligned_free(ptr);
m_indices[i] = nullptr;
}
}

m_indexCount = 0;
}

bool CoffNativeCodeManager::AllocFuncTableIndex()
{
uint32_t indexSize = (m_nRuntimeFunctionTable + FUNCTABLE_INDEX_GRANULARITY - 1) / FUNCTABLE_INDEX_GRANULARITY;
uint32_t* index = m_indices[m_indexCount] = (uint32_t*)_aligned_malloc(indexSize * sizeof(uint32_t), INDEX_ALIGNMENT);
if (!index)
return false;

memset(index, 0, indexSize * sizeof(uint32_t));
m_indexCount++;

while (indexSize > INDEX_BRANCHING_FACTOR)
{
uint32_t prevSize = indexSize;
indexSize = (indexSize + INDEX_BRANCHING_FACTOR - 1) / INDEX_BRANCHING_FACTOR;
index = m_indices[m_indexCount] = (uint32_t*)_aligned_malloc(indexSize * sizeof(uint32_t), INDEX_ALIGNMENT);
if (!index)
return false;

memset(index, 0, indexSize * sizeof(uint32_t));
m_indexCount++;
}

return true;
}

static int LookupUnwindInfoForMethod(uint32_t relativePc,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable,
int low,
int high)
NOINLINE
uint32_t** CoffNativeCodeManager::InitFuncTableIndex()
{
// Binary search the RUNTIME_FUNCTION table
// Use linear search once we get down to a small number of elements
// to avoid Binary search overhead.
while (high - low > 10)
// max offset is beyond the range of managed methods.
int maxOffset = (int)((TADDR)m_pvManagedCodeStartRange + m_cbManagedCodeRange - m_moduleBase);

// It is possible to see several threads come here at once.
// We can spin-wait for one thread to do the work or just let all threads do the initialization.
// Either way it will take roughly the same time as for the first thread to complete the work.
// Yet we can make this complete faster if threads help each other by working on different
// parts of the index.
uint32_t perThreadBias = (uint32_t)(((size_t)&perThreadBias * 11400714819323198485ul) >> 32);

// lets build the index for the runtime table. for every granule that has elements we will have an index entry
uint32_t indexSize = (m_nRuntimeFunctionTable + FUNCTABLE_INDEX_GRANULARITY - 1) / FUNCTABLE_INDEX_GRANULARITY;
uint32_t indexCount = 0;
uint32_t* index = m_indices[indexCount++];

// every index N will contain the lowest value from the granule N + 1
// when we will scan the value N in the indices and see that it is higher than the target, we will know
// that the granule N must be searched for the entry as the next granule will have higher addresses.
uint32_t start = (perThreadBias % indexSize) | 1;
for (uint32_t i = start; i < indexSize; i++)
{
int middle = low + (high - low) / 2;

PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + middle;
if (relativePc < pFunctionEntry->BeginAddress)
{
high = middle - 1;
}
else
{
low = middle;
}
if (index[i - 1] == 0)
{
_ASSERTE(i * FUNCTABLE_INDEX_GRANULARITY < m_nRuntimeFunctionTable);
index[i - 1] = m_pRuntimeFunctionTable[i * FUNCTABLE_INDEX_GRANULARITY].BeginAddress;
}
}

for (int i = low; i < high; i++)
for (uint32_t i = 1; i < start; i++)
{
PTR_RUNTIME_FUNCTION pNextFunctionEntry = pRuntimeFunctionTable + (i + 1);
if (relativePc < pNextFunctionEntry->BeginAddress)
if (index[i - 1] == 0)
{
high = i;
break;
_ASSERTE(i * FUNCTABLE_INDEX_GRANULARITY < m_nRuntimeFunctionTable);
index[i - 1] = m_pRuntimeFunctionTable[i * FUNCTABLE_INDEX_GRANULARITY].BeginAddress;
}
}

PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + high;
if (relativePc >= pFunctionEntry->BeginAddress)
// we put the maxOffset at the end of the index.
// there is no N + 1 granule to get the value from, so the last slot will contain the sentinel.
index[indexSize - 1] = maxOffset;

// Now build an N-ary tree of indices.
// Example: at branching factor 16 a program with 32K methods will have 3 sub-index levels.
uint32_t* prevIdx = index;
while (indexSize > INDEX_BRANCHING_FACTOR)
{
return high;
uint32_t prevSize = indexSize;
indexSize = (indexSize + INDEX_BRANCHING_FACTOR - 1) / INDEX_BRANCHING_FACTOR;
index = m_indices[indexCount++];

start = (perThreadBias % indexSize) | 1;
for (uint32_t i = start; i < indexSize; i++)
{
_ASSERTE(i * INDEX_BRANCHING_FACTOR < prevSize);
index[i - 1] = prevIdx[i * INDEX_BRANCHING_FACTOR];
}

for (uint32_t i = 1; i < start; i++)
{
_ASSERTE(i * INDEX_BRANCHING_FACTOR < prevSize);
index[i - 1] = prevIdx[i * INDEX_BRANCHING_FACTOR];
}

index[indexSize - 1] = maxOffset;
prevIdx = index;
}

ASSERT_UNCONDITIONALLY("Invalid code address");
return -1;
WriteRelease64((LONG64*)&m_initializedIndices, (LONG64)m_indices);
return m_initializedIndices;
}

struct CoffNativeMethodInfo
Expand All@@ -233,6 +308,39 @@ struct CoffNativeMethodInfo
// Ensure that CoffNativeMethodInfo fits into the space reserved by MethodInfo
static_assert(sizeof(CoffNativeMethodInfo) <= sizeof(MethodInfo), "CoffNativeMethodInfo too big");

FORCEINLINE
int CoffNativeCodeManager::LookupUnwindInfoIdx(uint32_t relativePc)
{
uint32_t** indices = m_initializedIndices;
if (!indices)
indices = InitFuncTableIndex();

uint32_t idx = 0;
for (int j = m_indexCount - 1; j >= 0; j--)
{
uint32_t* index = indices[j];
idx *= INDEX_BRANCHING_FACTOR;

while ((uint32_t)index[idx] < relativePc)
idx++;
}

for (idx *= FUNCTABLE_INDEX_GRANULARITY; idx < m_nRuntimeFunctionTable; idx++)
{
uint32_t curAddr = m_pRuntimeFunctionTable[idx].BeginAddress;
if (curAddr > relativePc)
return idx - 1;
}

// we can only get here if we are looking for a location inside the very last managed function.
_ASSERTE(m_pRuntimeFunctionTable[idx - 1].BeginAddress <= relativePc);
#if defined(TARGET_AMD64)
_ASSERTE(m_pRuntimeFunctionTable[idx - 1].EndAddress > relativePc);
#endif

return idx - 1;
}

bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
MethodInfo * pMethodInfoOut)
{
Expand All@@ -244,16 +352,10 @@ bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
}

CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethodInfoOut;

TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;

int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable,
0, m_nRuntimeFunctionTable - 1);
if (MethodIndex < 0)
return false;
int MethodIndex = LookupUnwindInfoIdx((uint32_t)relativePC);

PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;

pMethodInfo->runtimeFunction = pRuntimeFunction;

// The runtime function could correspond to a funclet. We need to get to the
Expand DownExpand Up@@ -965,10 +1067,7 @@ PTR_VOID CoffNativeCodeManager::GetAssociatedData(PTR_VOID ControlPC)
}

TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;

int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable, 0, m_nRuntimeFunctionTable - 1);
if (MethodIndex < 0)
return NULL;
int MethodIndex = LookupUnwindInfoIdx((uint32_t)relativePC);

PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;

Expand DownExpand Up@@ -1008,6 +1107,9 @@ bool RhRegisterOSModule(void * pModule,
if (pCoffNativeCodeManager == nullptr)
return false;

if (!pCoffNativeCodeManager->AllocFuncTableIndex())
return false;

RegisterCodeManager(pCoffNativeCodeManager, pvManagedCodeStartRange, cbManagedCodeRange);

if (!RegisterUnboxingStubs(pvUnboxingStubsStartRange, cbUnboxingStubsRange))
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,13 +44,24 @@ class CoffNativeCodeManager : public ICodeManager
PTR_PTR_VOID m_pClasslibFunctions;
uint32_t m_nClasslibFunctions;

// used to publish a reference to the index once initialized.
// if the reference is not null, the index can be accessed through it.
uint32_t** volatile m_initializedIndices;
uint32_t m_indexCount;
uint32_t* m_indices[8];

int LookupUnwindInfoIdx(uint32_t relativePc);

public:
CoffNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions);
~CoffNativeCodeManager();

bool AllocFuncTableIndex();
uint32_t** InitFuncTableIndex();

//
// Code manager methods
//
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
194 changes: 148 additions & 46 deletions src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,63 +164,138 @@ static PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntim
#endif
}

// index nodes are searched linearly.
// 16 * sizeof(uint32_t) == 64, which is a typical cache line size
// thus we expect at most one cache miss on every level of the index
#define INDEX_BRANCHING_FACTOR 16
// T_RUNTIME_FUNCTION is larger than uint32_t, so we have a smaller granularity at last level
#define FUNCTABLE_INDEX_GRANULARITY 8
#define INDEX_ALIGNMENT 64

CoffNativeCodeManager::CoffNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)
: m_moduleBase(moduleBase),
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions)
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions),
m_initializedIndices(0), m_indexCount(0), m_indices{ 0 }
{
}

CoffNativeCodeManager::~CoffNativeCodeManager()
{
for (uint32_t i = 0; i < m_indexCount; i++)
{
uint32_t* ptr = m_indices[i];
if (ptr)
{
_aligned_free(ptr);
m_indices[i] = nullptr;
}
}

m_indexCount = 0;
}

bool CoffNativeCodeManager::AllocFuncTableIndex()
{
uint32_t indexSize = (m_nRuntimeFunctionTable + FUNCTABLE_INDEX_GRANULARITY - 1) / FUNCTABLE_INDEX_GRANULARITY;
uint32_t* index = m_indices[m_indexCount] = (uint32_t*)_aligned_malloc(indexSize * sizeof(uint32_t), INDEX_ALIGNMENT);
if (!index)
return false;

memset(index, 0, indexSize * sizeof(uint32_t));
m_indexCount++;

while (indexSize > INDEX_BRANCHING_FACTOR)
{
uint32_t prevSize = indexSize;
indexSize = (indexSize + INDEX_BRANCHING_FACTOR - 1) / INDEX_BRANCHING_FACTOR;
index = m_indices[m_indexCount] = (uint32_t*)_aligned_malloc(indexSize * sizeof(uint32_t), INDEX_ALIGNMENT);
if (!index)
return false;

memset(index, 0, indexSize * sizeof(uint32_t));
m_indexCount++;
}

return true;
}

static int LookupUnwindInfoForMethod(uint32_t relativePc,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable,
int low,
int high)
NOINLINE
uint32_t** CoffNativeCodeManager::InitFuncTableIndex()
{
// Binary search the RUNTIME_FUNCTION table
// Use linear search once we get down to a small number of elements
// to avoid Binary search overhead.
while (high - low > 10)
// max offset is beyond the range of managed methods.
int maxOffset = (int)((TADDR)m_pvManagedCodeStartRange + m_cbManagedCodeRange - m_moduleBase);

// It is possible to see several threads come here at once.
// We can spin-wait for one thread to do the work or just let all threads do the initialization.
// Either way it will take roughly the same time as for the first thread to complete the work.
// Yet we can make this complete faster if threads help each other by working on different
// parts of the index.
uint32_t perThreadBias = (uint32_t)(((size_t)&perThreadBias * 11400714819323198485ul) >> 32);

// lets build the index for the runtime table. for every granule that has elements we will have an index entry
uint32_t indexSize = (m_nRuntimeFunctionTable + FUNCTABLE_INDEX_GRANULARITY - 1) / FUNCTABLE_INDEX_GRANULARITY;
uint32_t indexCount = 0;
uint32_t* index = m_indices[indexCount++];

// every index N will contain the lowest value from the granule N + 1
// when we will scan the value N in the indices and see that it is higher than the target, we will know
// that the granule N must be searched for the entry as the next granule will have higher addresses.
uint32_t start = (perThreadBias % indexSize) | 1;
for (uint32_t i = start; i < indexSize; i++)
{
int middle = low + (high - low) / 2;

PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + middle;
if (relativePc < pFunctionEntry->BeginAddress)
{
high = middle - 1;
}
else
{
low = middle;
}
if (index[i - 1] == 0)
{
_ASSERTE(i * FUNCTABLE_INDEX_GRANULARITY < m_nRuntimeFunctionTable);
index[i - 1] = m_pRuntimeFunctionTable[i * FUNCTABLE_INDEX_GRANULARITY].BeginAddress;
}
}

for (int i = low; i < high; i++)
for (uint32_t i = 1; i < start; i++)
{
PTR_RUNTIME_FUNCTION pNextFunctionEntry = pRuntimeFunctionTable + (i + 1);
if (relativePc < pNextFunctionEntry->BeginAddress)
if (index[i - 1] == 0)
{
high = i;
break;
_ASSERTE(i * FUNCTABLE_INDEX_GRANULARITY < m_nRuntimeFunctionTable);
index[i - 1] = m_pRuntimeFunctionTable[i * FUNCTABLE_INDEX_GRANULARITY].BeginAddress;
}
}

PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + high;
if (relativePc >= pFunctionEntry->BeginAddress)
// we put the maxOffset at the end of the index.
// there is no N + 1 granule to get the value from, so the last slot will contain the sentinel.
index[indexSize - 1] = maxOffset;

// Now build an N-ary tree of indices.
// Example: at branching factor 16 a program with 32K methods will have 3 sub-index levels.
uint32_t* prevIdx = index;
while (indexSize > INDEX_BRANCHING_FACTOR)
{
return high;
uint32_t prevSize = indexSize;
indexSize = (indexSize + INDEX_BRANCHING_FACTOR - 1) / INDEX_BRANCHING_FACTOR;
index = m_indices[indexCount++];

start = (perThreadBias % indexSize) | 1;
for (uint32_t i = start; i < indexSize; i++)
{
_ASSERTE(i * INDEX_BRANCHING_FACTOR < prevSize);
index[i - 1] = prevIdx[i * INDEX_BRANCHING_FACTOR];
}

for (uint32_t i = 1; i < start; i++)
{
_ASSERTE(i * INDEX_BRANCHING_FACTOR < prevSize);
index[i - 1] = prevIdx[i * INDEX_BRANCHING_FACTOR];
}

index[indexSize - 1] = maxOffset;
prevIdx = index;
}

ASSERT_UNCONDITIONALLY("Invalid code address");
return -1;
WriteRelease64((LONG64*)&m_initializedIndices, (LONG64)m_indices);
return m_initializedIndices;
}

struct CoffNativeMethodInfo
Expand All@@ -233,6 +308,39 @@ struct CoffNativeMethodInfo
// Ensure that CoffNativeMethodInfo fits into the space reserved by MethodInfo
static_assert(sizeof(CoffNativeMethodInfo) <= sizeof(MethodInfo), "CoffNativeMethodInfo too big");

FORCEINLINE
int CoffNativeCodeManager::LookupUnwindInfoIdx(uint32_t relativePc)
{
uint32_t** indices = m_initializedIndices;
if (!indices)
indices = InitFuncTableIndex();

uint32_t idx = 0;
for (int j = m_indexCount - 1; j >= 0; j--)
{
uint32_t* index = indices[j];
idx *= INDEX_BRANCHING_FACTOR;

while ((uint32_t)index[idx] < relativePc)
idx++;
}

for (idx *= FUNCTABLE_INDEX_GRANULARITY; idx < m_nRuntimeFunctionTable; idx++)
{
uint32_t curAddr = m_pRuntimeFunctionTable[idx].BeginAddress;
if (curAddr > relativePc)
return idx - 1;
}

// we can only get here if we are looking for a location inside the very last managed function.
_ASSERTE(m_pRuntimeFunctionTable[idx - 1].BeginAddress <= relativePc);
#if defined(TARGET_AMD64)
_ASSERTE(m_pRuntimeFunctionTable[idx - 1].EndAddress > relativePc);
#endif

return idx - 1;
}

bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
MethodInfo * pMethodInfoOut)
{
Expand All@@ -244,16 +352,10 @@ bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
}

CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethodInfoOut;

TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;

int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable,
0, m_nRuntimeFunctionTable - 1);
if (MethodIndex < 0)
return false;
int MethodIndex = LookupUnwindInfoIdx((uint32_t)relativePC);

PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;

pMethodInfo->runtimeFunction = pRuntimeFunction;

// The runtime function could correspond to a funclet. We need to get to the
Expand DownExpand Up@@ -965,10 +1067,7 @@ PTR_VOID CoffNativeCodeManager::GetAssociatedData(PTR_VOID ControlPC)
}

TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;

int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable, 0, m_nRuntimeFunctionTable - 1);
if (MethodIndex < 0)
return NULL;
int MethodIndex = LookupUnwindInfoIdx((uint32_t)relativePC);

PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;

Expand DownExpand Up@@ -1008,6 +1107,9 @@ bool RhRegisterOSModule(void * pModule,
if (pCoffNativeCodeManager == nullptr)
return false;

if (!pCoffNativeCodeManager->AllocFuncTableIndex())
return false;

RegisterCodeManager(pCoffNativeCodeManager, pvManagedCodeStartRange, cbManagedCodeRange);

if (!RegisterUnboxingStubs(pvUnboxingStubsStartRange, cbUnboxingStubsRange))
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,13 +44,24 @@ class CoffNativeCodeManager : public ICodeManager
PTR_PTR_VOID m_pClasslibFunctions;
uint32_t m_nClasslibFunctions;

// used to publish a reference to the index once initialized.
// if the reference is not null, the index can be accessed through it.
uint32_t** volatile m_initializedIndices;
uint32_t m_indexCount;
uint32_t* m_indices[8];

int LookupUnwindInfoIdx(uint32_t relativePc);

public:
CoffNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions);
~CoffNativeCodeManager();

bool AllocFuncTableIndex();
uint32_t** InitFuncTableIndex();

//
// Code manager methods
//
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
194 changes: 148 additions & 46 deletions src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,63 +164,138 @@ static PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntim
#endif
}

// index nodes are searched linearly.
// 16 * sizeof(uint32_t) == 64, which is a typical cache line size
// thus we expect at most one cache miss on every level of the index
#define INDEX_BRANCHING_FACTOR 16
// T_RUNTIME_FUNCTION is larger than uint32_t, so we have a smaller granularity at last level
#define FUNCTABLE_INDEX_GRANULARITY 8
#define INDEX_ALIGNMENT 64

CoffNativeCodeManager::CoffNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)
: m_moduleBase(moduleBase),
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions)
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions),
m_initializedIndices(0), m_indexCount(0), m_indices{ 0 }
{
}

CoffNativeCodeManager::~CoffNativeCodeManager()
{
for (uint32_t i = 0; i < m_indexCount; i++)
{
uint32_t* ptr = m_indices[i];
if (ptr)
{
_aligned_free(ptr);
m_indices[i] = nullptr;
}
}

m_indexCount = 0;
}

bool CoffNativeCodeManager::AllocFuncTableIndex()
{
uint32_t indexSize = (m_nRuntimeFunctionTable + FUNCTABLE_INDEX_GRANULARITY - 1) / FUNCTABLE_INDEX_GRANULARITY;
uint32_t* index = m_indices[m_indexCount] = (uint32_t*)_aligned_malloc(indexSize * sizeof(uint32_t), INDEX_ALIGNMENT);
if (!index)
return false;

memset(index, 0, indexSize * sizeof(uint32_t));
m_indexCount++;

while (indexSize > INDEX_BRANCHING_FACTOR)
{
uint32_t prevSize = indexSize;
indexSize = (indexSize + INDEX_BRANCHING_FACTOR - 1) / INDEX_BRANCHING_FACTOR;
index = m_indices[m_indexCount] = (uint32_t*)_aligned_malloc(indexSize * sizeof(uint32_t), INDEX_ALIGNMENT);
if (!index)
return false;

memset(index, 0, indexSize * sizeof(uint32_t));
m_indexCount++;
}

return true;
}

static int LookupUnwindInfoForMethod(uint32_t relativePc,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable,
int low,
int high)
NOINLINE
uint32_t** CoffNativeCodeManager::InitFuncTableIndex()
{
// Binary search the RUNTIME_FUNCTION table
// Use linear search once we get down to a small number of elements
// to avoid Binary search overhead.
while (high - low > 10)
// max offset is beyond the range of managed methods.
int maxOffset = (int)((TADDR)m_pvManagedCodeStartRange + m_cbManagedCodeRange - m_moduleBase);

// It is possible to see several threads come here at once.
// We can spin-wait for one thread to do the work or just let all threads do the initialization.
// Either way it will take roughly the same time as for the first thread to complete the work.
// Yet we can make this complete faster if threads help each other by working on different
// parts of the index.
uint32_t perThreadBias = (uint32_t)(((size_t)&perThreadBias * 11400714819323198485ul) >> 32);

// lets build the index for the runtime table. for every granule that has elements we will have an index entry
uint32_t indexSize = (m_nRuntimeFunctionTable + FUNCTABLE_INDEX_GRANULARITY - 1) / FUNCTABLE_INDEX_GRANULARITY;
uint32_t indexCount = 0;
uint32_t* index = m_indices[indexCount++];

// every index N will contain the lowest value from the granule N + 1
// when we will scan the value N in the indices and see that it is higher than the target, we will know
// that the granule N must be searched for the entry as the next granule will have higher addresses.
uint32_t start = (perThreadBias % indexSize) | 1;
for (uint32_t i = start; i < indexSize; i++)
{
int middle = low + (high - low) / 2;

PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + middle;
if (relativePc < pFunctionEntry->BeginAddress)
{
high = middle - 1;
}
else
{
low = middle;
}
if (index[i - 1] == 0)
{
_ASSERTE(i * FUNCTABLE_INDEX_GRANULARITY < m_nRuntimeFunctionTable);
index[i - 1] = m_pRuntimeFunctionTable[i * FUNCTABLE_INDEX_GRANULARITY].BeginAddress;
}
}

for (int i = low; i < high; i++)
for (uint32_t i = 1; i < start; i++)
{
PTR_RUNTIME_FUNCTION pNextFunctionEntry = pRuntimeFunctionTable + (i + 1);
if (relativePc < pNextFunctionEntry->BeginAddress)
if (index[i - 1] == 0)
{
high = i;
break;
_ASSERTE(i * FUNCTABLE_INDEX_GRANULARITY < m_nRuntimeFunctionTable);
index[i - 1] = m_pRuntimeFunctionTable[i * FUNCTABLE_INDEX_GRANULARITY].BeginAddress;
}
}

PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + high;
if (relativePc >= pFunctionEntry->BeginAddress)
// we put the maxOffset at the end of the index.
// there is no N + 1 granule to get the value from, so the last slot will contain the sentinel.
index[indexSize - 1] = maxOffset;

// Now build an N-ary tree of indices.
// Example: at branching factor 16 a program with 32K methods will have 3 sub-index levels.
uint32_t* prevIdx = index;
while (indexSize > INDEX_BRANCHING_FACTOR)
{
return high;
uint32_t prevSize = indexSize;
indexSize = (indexSize + INDEX_BRANCHING_FACTOR - 1) / INDEX_BRANCHING_FACTOR;
index = m_indices[indexCount++];

start = (perThreadBias % indexSize) | 1;
for (uint32_t i = start; i < indexSize; i++)
{
_ASSERTE(i * INDEX_BRANCHING_FACTOR < prevSize);
index[i - 1] = prevIdx[i * INDEX_BRANCHING_FACTOR];
}

for (uint32_t i = 1; i < start; i++)
{
_ASSERTE(i * INDEX_BRANCHING_FACTOR < prevSize);
index[i - 1] = prevIdx[i * INDEX_BRANCHING_FACTOR];
}

index[indexSize - 1] = maxOffset;
prevIdx = index;
}

ASSERT_UNCONDITIONALLY("Invalid code address");
return -1;
WriteRelease64((LONG64*)&m_initializedIndices, (LONG64)m_indices);
return m_initializedIndices;
}

struct CoffNativeMethodInfo
Expand All@@ -233,6 +308,39 @@ struct CoffNativeMethodInfo
// Ensure that CoffNativeMethodInfo fits into the space reserved by MethodInfo
static_assert(sizeof(CoffNativeMethodInfo) <= sizeof(MethodInfo), "CoffNativeMethodInfo too big");

FORCEINLINE
int CoffNativeCodeManager::LookupUnwindInfoIdx(uint32_t relativePc)
{
uint32_t** indices = m_initializedIndices;
if (!indices)
indices = InitFuncTableIndex();

uint32_t idx = 0;
for (int j = m_indexCount - 1; j >= 0; j--)
{
uint32_t* index = indices[j];
idx *= INDEX_BRANCHING_FACTOR;

while ((uint32_t)index[idx] < relativePc)
idx++;
}

for (idx *= FUNCTABLE_INDEX_GRANULARITY; idx < m_nRuntimeFunctionTable; idx++)
{
uint32_t curAddr = m_pRuntimeFunctionTable[idx].BeginAddress;
if (curAddr > relativePc)
return idx - 1;
}

// we can only get here if we are looking for a location inside the very last managed function.
_ASSERTE(m_pRuntimeFunctionTable[idx - 1].BeginAddress <= relativePc);
#if defined(TARGET_AMD64)
_ASSERTE(m_pRuntimeFunctionTable[idx - 1].EndAddress > relativePc);
#endif

return idx - 1;
}

bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
MethodInfo * pMethodInfoOut)
{
Expand All@@ -244,16 +352,10 @@ bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
}

CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethodInfoOut;

TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;

int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable,
0, m_nRuntimeFunctionTable - 1);
if (MethodIndex < 0)
return false;
int MethodIndex = LookupUnwindInfoIdx((uint32_t)relativePC);

PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;

pMethodInfo->runtimeFunction = pRuntimeFunction;

// The runtime function could correspond to a funclet. We need to get to the
Expand DownExpand Up@@ -965,10 +1067,7 @@ PTR_VOID CoffNativeCodeManager::GetAssociatedData(PTR_VOID ControlPC)
}

TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;

int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable, 0, m_nRuntimeFunctionTable - 1);
if (MethodIndex < 0)
return NULL;
int MethodIndex = LookupUnwindInfoIdx((uint32_t)relativePC);

PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;

Expand DownExpand Up@@ -1008,6 +1107,9 @@ bool RhRegisterOSModule(void * pModule,
if (pCoffNativeCodeManager == nullptr)
return false;

if (!pCoffNativeCodeManager->AllocFuncTableIndex())
return false;

RegisterCodeManager(pCoffNativeCodeManager, pvManagedCodeStartRange, cbManagedCodeRange);

if (!RegisterUnboxingStubs(pvUnboxingStubsStartRange, cbUnboxingStubsRange))
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,13 +44,24 @@ class CoffNativeCodeManager : public ICodeManager
PTR_PTR_VOID m_pClasslibFunctions;
uint32_t m_nClasslibFunctions;

// used to publish a reference to the index once initialized.
// if the reference is not null, the index can be accessed through it.
uint32_t** volatile m_initializedIndices;
uint32_t m_indexCount;
uint32_t* m_indices[8];

int LookupUnwindInfoIdx(uint32_t relativePc);

public:
CoffNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions);
~CoffNativeCodeManager();

bool AllocFuncTableIndex();
uint32_t** InitFuncTableIndex();

//
// Code manager methods
//
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
194 changes: 148 additions & 46 deletions src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,63 +164,138 @@ static PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntim
#endif
}

// index nodes are searched linearly.
// 16 * sizeof(uint32_t) == 64, which is a typical cache line size
// thus we expect at most one cache miss on every level of the index
#define INDEX_BRANCHING_FACTOR 16
// T_RUNTIME_FUNCTION is larger than uint32_t, so we have a smaller granularity at last level
#define FUNCTABLE_INDEX_GRANULARITY 8
#define INDEX_ALIGNMENT 64

CoffNativeCodeManager::CoffNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)
: m_moduleBase(moduleBase),
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions)
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions),
m_initializedIndices(0), m_indexCount(0), m_indices{ 0 }
{
}

CoffNativeCodeManager::~CoffNativeCodeManager()
{
for (uint32_t i = 0; i < m_indexCount; i++)
{
uint32_t* ptr = m_indices[i];
if (ptr)
{
_aligned_free(ptr);
m_indices[i] = nullptr;
}
}

m_indexCount = 0;
}

bool CoffNativeCodeManager::AllocFuncTableIndex()
{
uint32_t indexSize = (m_nRuntimeFunctionTable + FUNCTABLE_INDEX_GRANULARITY - 1) / FUNCTABLE_INDEX_GRANULARITY;
uint32_t* index = m_indices[m_indexCount] = (uint32_t*)_aligned_malloc(indexSize * sizeof(uint32_t), INDEX_ALIGNMENT);
if (!index)
return false;

memset(index, 0, indexSize * sizeof(uint32_t));
m_indexCount++;

while (indexSize > INDEX_BRANCHING_FACTOR)
{
uint32_t prevSize = indexSize;
indexSize = (indexSize + INDEX_BRANCHING_FACTOR - 1) / INDEX_BRANCHING_FACTOR;
index = m_indices[m_indexCount] = (uint32_t*)_aligned_malloc(indexSize * sizeof(uint32_t), INDEX_ALIGNMENT);
if (!index)
return false;

memset(index, 0, indexSize * sizeof(uint32_t));
m_indexCount++;
}

return true;
}

static int LookupUnwindInfoForMethod(uint32_t relativePc,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable,
int low,
int high)
NOINLINE
uint32_t** CoffNativeCodeManager::InitFuncTableIndex()
{
// Binary search the RUNTIME_FUNCTION table
// Use linear search once we get down to a small number of elements
// to avoid Binary search overhead.
while (high - low > 10)
// max offset is beyond the range of managed methods.
int maxOffset = (int)((TADDR)m_pvManagedCodeStartRange + m_cbManagedCodeRange - m_moduleBase);

// It is possible to see several threads come here at once.
// We can spin-wait for one thread to do the work or just let all threads do the initialization.
// Either way it will take roughly the same time as for the first thread to complete the work.
// Yet we can make this complete faster if threads help each other by working on different
// parts of the index.
uint32_t perThreadBias = (uint32_t)(((size_t)&perThreadBias * 11400714819323198485ul) >> 32);

// lets build the index for the runtime table. for every granule that has elements we will have an index entry
uint32_t indexSize = (m_nRuntimeFunctionTable + FUNCTABLE_INDEX_GRANULARITY - 1) / FUNCTABLE_INDEX_GRANULARITY;
uint32_t indexCount = 0;
uint32_t* index = m_indices[indexCount++];

// every index N will contain the lowest value from the granule N + 1
// when we will scan the value N in the indices and see that it is higher than the target, we will know
// that the granule N must be searched for the entry as the next granule will have higher addresses.
uint32_t start = (perThreadBias % indexSize) | 1;
for (uint32_t i = start; i < indexSize; i++)
{
int middle = low + (high - low) / 2;

PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + middle;
if (relativePc < pFunctionEntry->BeginAddress)
{
high = middle - 1;
}
else
{
low = middle;
}
if (index[i - 1] == 0)
{
_ASSERTE(i * FUNCTABLE_INDEX_GRANULARITY < m_nRuntimeFunctionTable);
index[i - 1] = m_pRuntimeFunctionTable[i * FUNCTABLE_INDEX_GRANULARITY].BeginAddress;
}
}

for (int i = low; i < high; i++)
for (uint32_t i = 1; i < start; i++)
{
PTR_RUNTIME_FUNCTION pNextFunctionEntry = pRuntimeFunctionTable + (i + 1);
if (relativePc < pNextFunctionEntry->BeginAddress)
if (index[i - 1] == 0)
{
high = i;
break;
_ASSERTE(i * FUNCTABLE_INDEX_GRANULARITY < m_nRuntimeFunctionTable);
index[i - 1] = m_pRuntimeFunctionTable[i * FUNCTABLE_INDEX_GRANULARITY].BeginAddress;
}
}

PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + high;
if (relativePc >= pFunctionEntry->BeginAddress)
// we put the maxOffset at the end of the index.
// there is no N + 1 granule to get the value from, so the last slot will contain the sentinel.
index[indexSize - 1] = maxOffset;

// Now build an N-ary tree of indices.
// Example: at branching factor 16 a program with 32K methods will have 3 sub-index levels.
uint32_t* prevIdx = index;
while (indexSize > INDEX_BRANCHING_FACTOR)
{
return high;
uint32_t prevSize = indexSize;
indexSize = (indexSize + INDEX_BRANCHING_FACTOR - 1) / INDEX_BRANCHING_FACTOR;
index = m_indices[indexCount++];

start = (perThreadBias % indexSize) | 1;
for (uint32_t i = start; i < indexSize; i++)
{
_ASSERTE(i * INDEX_BRANCHING_FACTOR < prevSize);
index[i - 1] = prevIdx[i * INDEX_BRANCHING_FACTOR];
}

for (uint32_t i = 1; i < start; i++)
{
_ASSERTE(i * INDEX_BRANCHING_FACTOR < prevSize);
index[i - 1] = prevIdx[i * INDEX_BRANCHING_FACTOR];
}

index[indexSize - 1] = maxOffset;
prevIdx = index;
}

ASSERT_UNCONDITIONALLY("Invalid code address");
return -1;
WriteRelease64((LONG64*)&m_initializedIndices, (LONG64)m_indices);
return m_initializedIndices;
}

struct CoffNativeMethodInfo
Expand All@@ -233,6 +308,39 @@ struct CoffNativeMethodInfo
// Ensure that CoffNativeMethodInfo fits into the space reserved by MethodInfo
static_assert(sizeof(CoffNativeMethodInfo) <= sizeof(MethodInfo), "CoffNativeMethodInfo too big");

FORCEINLINE
int CoffNativeCodeManager::LookupUnwindInfoIdx(uint32_t relativePc)
{
uint32_t** indices = m_initializedIndices;
if (!indices)
indices = InitFuncTableIndex();

uint32_t idx = 0;
for (int j = m_indexCount - 1; j >= 0; j--)
{
uint32_t* index = indices[j];
idx *= INDEX_BRANCHING_FACTOR;

while ((uint32_t)index[idx] < relativePc)
idx++;
}

for (idx *= FUNCTABLE_INDEX_GRANULARITY; idx < m_nRuntimeFunctionTable; idx++)
{
uint32_t curAddr = m_pRuntimeFunctionTable[idx].BeginAddress;
if (curAddr > relativePc)
return idx - 1;
}

// we can only get here if we are looking for a location inside the very last managed function.
_ASSERTE(m_pRuntimeFunctionTable[idx - 1].BeginAddress <= relativePc);
#if defined(TARGET_AMD64)
_ASSERTE(m_pRuntimeFunctionTable[idx - 1].EndAddress > relativePc);
#endif

return idx - 1;
}

bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
MethodInfo * pMethodInfoOut)
{
Expand All@@ -244,16 +352,10 @@ bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
}

CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethodInfoOut;

TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;

int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable,
0, m_nRuntimeFunctionTable - 1);
if (MethodIndex < 0)
return false;
int MethodIndex = LookupUnwindInfoIdx((uint32_t)relativePC);

PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;

pMethodInfo->runtimeFunction = pRuntimeFunction;

// The runtime function could correspond to a funclet. We need to get to the
Expand DownExpand Up@@ -965,10 +1067,7 @@ PTR_VOID CoffNativeCodeManager::GetAssociatedData(PTR_VOID ControlPC)
}

TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;

int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable, 0, m_nRuntimeFunctionTable - 1);
if (MethodIndex < 0)
return NULL;
int MethodIndex = LookupUnwindInfoIdx((uint32_t)relativePC);

PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;

Expand DownExpand Up@@ -1008,6 +1107,9 @@ bool RhRegisterOSModule(void * pModule,
if (pCoffNativeCodeManager == nullptr)
return false;

if (!pCoffNativeCodeManager->AllocFuncTableIndex())
return false;

RegisterCodeManager(pCoffNativeCodeManager, pvManagedCodeStartRange, cbManagedCodeRange);

if (!RegisterUnboxingStubs(pvUnboxingStubsStartRange, cbUnboxingStubsRange))
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,13 +44,24 @@ class CoffNativeCodeManager : public ICodeManager
PTR_PTR_VOID m_pClasslibFunctions;
uint32_t m_nClasslibFunctions;

// used to publish a reference to the index once initialized.
// if the reference is not null, the index can be accessed through it.
uint32_t** volatile m_initializedIndices;
uint32_t m_indexCount;
uint32_t* m_indices[8];

int LookupUnwindInfoIdx(uint32_t relativePc);

public:
CoffNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions);
~CoffNativeCodeManager();

bool AllocFuncTableIndex();
uint32_t** InitFuncTableIndex();

//
// Code manager methods
//
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
194 changes: 148 additions & 46 deletions src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,63 +164,138 @@ static PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntim
#endif
}

// index nodes are searched linearly.
// 16 * sizeof(uint32_t) == 64, which is a typical cache line size
// thus we expect at most one cache miss on every level of the index
#define INDEX_BRANCHING_FACTOR 16
// T_RUNTIME_FUNCTION is larger than uint32_t, so we have a smaller granularity at last level
#define FUNCTABLE_INDEX_GRANULARITY 8
#define INDEX_ALIGNMENT 64

CoffNativeCodeManager::CoffNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)
: m_moduleBase(moduleBase),
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions)
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions),
m_initializedIndices(0), m_indexCount(0), m_indices{ 0 }
{
}

CoffNativeCodeManager::~CoffNativeCodeManager()
{
for (uint32_t i = 0; i < m_indexCount; i++)
{
uint32_t* ptr = m_indices[i];
if (ptr)
{
_aligned_free(ptr);
m_indices[i] = nullptr;
}
}

m_indexCount = 0;
}

bool CoffNativeCodeManager::AllocFuncTableIndex()
{
uint32_t indexSize = (m_nRuntimeFunctionTable + FUNCTABLE_INDEX_GRANULARITY - 1) / FUNCTABLE_INDEX_GRANULARITY;
uint32_t* index = m_indices[m_indexCount] = (uint32_t*)_aligned_malloc(indexSize * sizeof(uint32_t), INDEX_ALIGNMENT);
if (!index)
return false;

memset(index, 0, indexSize * sizeof(uint32_t));
m_indexCount++;

while (indexSize > INDEX_BRANCHING_FACTOR)
{
uint32_t prevSize = indexSize;
indexSize = (indexSize + INDEX_BRANCHING_FACTOR - 1) / INDEX_BRANCHING_FACTOR;
index = m_indices[m_indexCount] = (uint32_t*)_aligned_malloc(indexSize * sizeof(uint32_t), INDEX_ALIGNMENT);
if (!index)
return false;

memset(index, 0, indexSize * sizeof(uint32_t));
m_indexCount++;
}

return true;
}

static int LookupUnwindInfoForMethod(uint32_t relativePc,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable,
int low,
int high)
NOINLINE
uint32_t** CoffNativeCodeManager::InitFuncTableIndex()
{
// Binary search the RUNTIME_FUNCTION table
// Use linear search once we get down to a small number of elements
// to avoid Binary search overhead.
while (high - low > 10)
// max offset is beyond the range of managed methods.
int maxOffset = (int)((TADDR)m_pvManagedCodeStartRange + m_cbManagedCodeRange - m_moduleBase);

// It is possible to see several threads come here at once.
// We can spin-wait for one thread to do the work or just let all threads do the initialization.
// Either way it will take roughly the same time as for the first thread to complete the work.
// Yet we can make this complete faster if threads help each other by working on different
// parts of the index.
uint32_t perThreadBias = (uint32_t)(((size_t)&perThreadBias * 11400714819323198485ul) >> 32);

// lets build the index for the runtime table. for every granule that has elements we will have an index entry
uint32_t indexSize = (m_nRuntimeFunctionTable + FUNCTABLE_INDEX_GRANULARITY - 1) / FUNCTABLE_INDEX_GRANULARITY;
uint32_t indexCount = 0;
uint32_t* index = m_indices[indexCount++];

// every index N will contain the lowest value from the granule N + 1
// when we will scan the value N in the indices and see that it is higher than the target, we will know
// that the granule N must be searched for the entry as the next granule will have higher addresses.
uint32_t start = (perThreadBias % indexSize) | 1;
for (uint32_t i = start; i < indexSize; i++)
{
int middle = low + (high - low) / 2;

PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + middle;
if (relativePc < pFunctionEntry->BeginAddress)
{
high = middle - 1;
}
else
{
low = middle;
}
if (index[i - 1] == 0)
{
_ASSERTE(i * FUNCTABLE_INDEX_GRANULARITY < m_nRuntimeFunctionTable);
index[i - 1] = m_pRuntimeFunctionTable[i * FUNCTABLE_INDEX_GRANULARITY].BeginAddress;
}
}

for (int i = low; i < high; i++)
for (uint32_t i = 1; i < start; i++)
{
PTR_RUNTIME_FUNCTION pNextFunctionEntry = pRuntimeFunctionTable + (i + 1);
if (relativePc < pNextFunctionEntry->BeginAddress)
if (index[i - 1] == 0)
{
high = i;
break;
_ASSERTE(i * FUNCTABLE_INDEX_GRANULARITY < m_nRuntimeFunctionTable);
index[i - 1] = m_pRuntimeFunctionTable[i * FUNCTABLE_INDEX_GRANULARITY].BeginAddress;
}
}

PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + high;
if (relativePc >= pFunctionEntry->BeginAddress)
// we put the maxOffset at the end of the index.
// there is no N + 1 granule to get the value from, so the last slot will contain the sentinel.
index[indexSize - 1] = maxOffset;

// Now build an N-ary tree of indices.
// Example: at branching factor 16 a program with 32K methods will have 3 sub-index levels.
uint32_t* prevIdx = index;
while (indexSize > INDEX_BRANCHING_FACTOR)
{
return high;
uint32_t prevSize = indexSize;
indexSize = (indexSize + INDEX_BRANCHING_FACTOR - 1) / INDEX_BRANCHING_FACTOR;
index = m_indices[indexCount++];

start = (perThreadBias % indexSize) | 1;
for (uint32_t i = start; i < indexSize; i++)
{
_ASSERTE(i * INDEX_BRANCHING_FACTOR < prevSize);
index[i - 1] = prevIdx[i * INDEX_BRANCHING_FACTOR];
}

for (uint32_t i = 1; i < start; i++)
{
_ASSERTE(i * INDEX_BRANCHING_FACTOR < prevSize);
index[i - 1] = prevIdx[i * INDEX_BRANCHING_FACTOR];
}

index[indexSize - 1] = maxOffset;
prevIdx = index;
}

ASSERT_UNCONDITIONALLY("Invalid code address");
return -1;
WriteRelease64((LONG64*)&m_initializedIndices, (LONG64)m_indices);
return m_initializedIndices;
}

struct CoffNativeMethodInfo
Expand All@@ -233,6 +308,39 @@ struct CoffNativeMethodInfo
// Ensure that CoffNativeMethodInfo fits into the space reserved by MethodInfo
static_assert(sizeof(CoffNativeMethodInfo) <= sizeof(MethodInfo), "CoffNativeMethodInfo too big");

FORCEINLINE
int CoffNativeCodeManager::LookupUnwindInfoIdx(uint32_t relativePc)
{
uint32_t** indices = m_initializedIndices;
if (!indices)
indices = InitFuncTableIndex();

uint32_t idx = 0;
for (int j = m_indexCount - 1; j >= 0; j--)
{
uint32_t* index = indices[j];
idx *= INDEX_BRANCHING_FACTOR;

while ((uint32_t)index[idx] < relativePc)
idx++;
}

for (idx *= FUNCTABLE_INDEX_GRANULARITY; idx < m_nRuntimeFunctionTable; idx++)
{
uint32_t curAddr = m_pRuntimeFunctionTable[idx].BeginAddress;
if (curAddr > relativePc)
return idx - 1;
}

// we can only get here if we are looking for a location inside the very last managed function.
_ASSERTE(m_pRuntimeFunctionTable[idx - 1].BeginAddress <= relativePc);
#if defined(TARGET_AMD64)
_ASSERTE(m_pRuntimeFunctionTable[idx - 1].EndAddress > relativePc);
#endif

return idx - 1;
}

bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
MethodInfo * pMethodInfoOut)
{
Expand All@@ -244,16 +352,10 @@ bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
}

CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethodInfoOut;

TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;

int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable,
0, m_nRuntimeFunctionTable - 1);
if (MethodIndex < 0)
return false;
int MethodIndex = LookupUnwindInfoIdx((uint32_t)relativePC);

PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;

pMethodInfo->runtimeFunction = pRuntimeFunction;

// The runtime function could correspond to a funclet. We need to get to the
Expand DownExpand Up@@ -965,10 +1067,7 @@ PTR_VOID CoffNativeCodeManager::GetAssociatedData(PTR_VOID ControlPC)
}

TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;

int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable, 0, m_nRuntimeFunctionTable - 1);
if (MethodIndex < 0)
return NULL;
int MethodIndex = LookupUnwindInfoIdx((uint32_t)relativePC);

PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;

Expand DownExpand Up@@ -1008,6 +1107,9 @@ bool RhRegisterOSModule(void * pModule,
if (pCoffNativeCodeManager == nullptr)
return false;

if (!pCoffNativeCodeManager->AllocFuncTableIndex())
return false;

RegisterCodeManager(pCoffNativeCodeManager, pvManagedCodeStartRange, cbManagedCodeRange);

if (!RegisterUnboxingStubs(pvUnboxingStubsStartRange, cbUnboxingStubsRange))
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,13 +44,24 @@ class CoffNativeCodeManager : public ICodeManager
PTR_PTR_VOID m_pClasslibFunctions;
uint32_t m_nClasslibFunctions;

// used to publish a reference to the index once initialized.
// if the reference is not null, the index can be accessed through it.
uint32_t** volatile m_initializedIndices;
uint32_t m_indexCount;
uint32_t* m_indices[8];

int LookupUnwindInfoIdx(uint32_t relativePc);

public:
CoffNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions);
~CoffNativeCodeManager();

bool AllocFuncTableIndex();
uint32_t** InitFuncTableIndex();

//
// Code manager methods
//
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
194 changes: 148 additions & 46 deletions src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,63 +164,138 @@ static PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntim
#endif
}

// index nodes are searched linearly.
// 16 * sizeof(uint32_t) == 64, which is a typical cache line size
// thus we expect at most one cache miss on every level of the index
#define INDEX_BRANCHING_FACTOR 16
// T_RUNTIME_FUNCTION is larger than uint32_t, so we have a smaller granularity at last level
#define FUNCTABLE_INDEX_GRANULARITY 8
#define INDEX_ALIGNMENT 64

CoffNativeCodeManager::CoffNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)
: m_moduleBase(moduleBase),
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions)
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions),
m_initializedIndices(0), m_indexCount(0), m_indices{ 0 }
{
}

CoffNativeCodeManager::~CoffNativeCodeManager()
{
for (uint32_t i = 0; i < m_indexCount; i++)
{
uint32_t* ptr = m_indices[i];
if (ptr)
{
_aligned_free(ptr);
m_indices[i] = nullptr;
}
}

m_indexCount = 0;
}

bool CoffNativeCodeManager::AllocFuncTableIndex()
{
uint32_t indexSize = (m_nRuntimeFunctionTable + FUNCTABLE_INDEX_GRANULARITY - 1) / FUNCTABLE_INDEX_GRANULARITY;
uint32_t* index = m_indices[m_indexCount] = (uint32_t*)_aligned_malloc(indexSize * sizeof(uint32_t), INDEX_ALIGNMENT);
if (!index)
return false;

memset(index, 0, indexSize * sizeof(uint32_t));
m_indexCount++;

while (indexSize > INDEX_BRANCHING_FACTOR)
{
uint32_t prevSize = indexSize;
indexSize = (indexSize + INDEX_BRANCHING_FACTOR - 1) / INDEX_BRANCHING_FACTOR;
index = m_indices[m_indexCount] = (uint32_t*)_aligned_malloc(indexSize * sizeof(uint32_t), INDEX_ALIGNMENT);
if (!index)
return false;

memset(index, 0, indexSize * sizeof(uint32_t));
m_indexCount++;
}

return true;
}

static int LookupUnwindInfoForMethod(uint32_t relativePc,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable,
int low,
int high)
NOINLINE
uint32_t** CoffNativeCodeManager::InitFuncTableIndex()
{
// Binary search the RUNTIME_FUNCTION table
// Use linear search once we get down to a small number of elements
// to avoid Binary search overhead.
while (high - low > 10)
// max offset is beyond the range of managed methods.
int maxOffset = (int)((TADDR)m_pvManagedCodeStartRange + m_cbManagedCodeRange - m_moduleBase);

// It is possible to see several threads come here at once.
// We can spin-wait for one thread to do the work or just let all threads do the initialization.
// Either way it will take roughly the same time as for the first thread to complete the work.
// Yet we can make this complete faster if threads help each other by working on different
// parts of the index.
uint32_t perThreadBias = (uint32_t)(((size_t)&perThreadBias * 11400714819323198485ul) >> 32);

// lets build the index for the runtime table. for every granule that has elements we will have an index entry
uint32_t indexSize = (m_nRuntimeFunctionTable + FUNCTABLE_INDEX_GRANULARITY - 1) / FUNCTABLE_INDEX_GRANULARITY;
uint32_t indexCount = 0;
uint32_t* index = m_indices[indexCount++];

// every index N will contain the lowest value from the granule N + 1
// when we will scan the value N in the indices and see that it is higher than the target, we will know
// that the granule N must be searched for the entry as the next granule will have higher addresses.
uint32_t start = (perThreadBias % indexSize) | 1;
for (uint32_t i = start; i < indexSize; i++)
{
int middle = low + (high - low) / 2;

PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + middle;
if (relativePc < pFunctionEntry->BeginAddress)
{
high = middle - 1;
}
else
{
low = middle;
}
if (index[i - 1] == 0)
{
_ASSERTE(i * FUNCTABLE_INDEX_GRANULARITY < m_nRuntimeFunctionTable);
index[i - 1] = m_pRuntimeFunctionTable[i * FUNCTABLE_INDEX_GRANULARITY].BeginAddress;
}
}

for (int i = low; i < high; i++)
for (uint32_t i = 1; i < start; i++)
{
PTR_RUNTIME_FUNCTION pNextFunctionEntry = pRuntimeFunctionTable + (i + 1);
if (relativePc < pNextFunctionEntry->BeginAddress)
if (index[i - 1] == 0)
{
high = i;
break;
_ASSERTE(i * FUNCTABLE_INDEX_GRANULARITY < m_nRuntimeFunctionTable);
index[i - 1] = m_pRuntimeFunctionTable[i * FUNCTABLE_INDEX_GRANULARITY].BeginAddress;
}
}

PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + high;
if (relativePc >= pFunctionEntry->BeginAddress)
// we put the maxOffset at the end of the index.
// there is no N + 1 granule to get the value from, so the last slot will contain the sentinel.
index[indexSize - 1] = maxOffset;

// Now build an N-ary tree of indices.
// Example: at branching factor 16 a program with 32K methods will have 3 sub-index levels.
uint32_t* prevIdx = index;
while (indexSize > INDEX_BRANCHING_FACTOR)
{
return high;
uint32_t prevSize = indexSize;
indexSize = (indexSize + INDEX_BRANCHING_FACTOR - 1) / INDEX_BRANCHING_FACTOR;
index = m_indices[indexCount++];

start = (perThreadBias % indexSize) | 1;
for (uint32_t i = start; i < indexSize; i++)
{
_ASSERTE(i * INDEX_BRANCHING_FACTOR < prevSize);
index[i - 1] = prevIdx[i * INDEX_BRANCHING_FACTOR];
}

for (uint32_t i = 1; i < start; i++)
{
_ASSERTE(i * INDEX_BRANCHING_FACTOR < prevSize);
index[i - 1] = prevIdx[i * INDEX_BRANCHING_FACTOR];
}

index[indexSize - 1] = maxOffset;
prevIdx = index;
}

ASSERT_UNCONDITIONALLY("Invalid code address");
return -1;
WriteRelease64((LONG64*)&m_initializedIndices, (LONG64)m_indices);
return m_initializedIndices;
}

struct CoffNativeMethodInfo
Expand All@@ -233,6 +308,39 @@ struct CoffNativeMethodInfo
// Ensure that CoffNativeMethodInfo fits into the space reserved by MethodInfo
static_assert(sizeof(CoffNativeMethodInfo) <= sizeof(MethodInfo), "CoffNativeMethodInfo too big");

FORCEINLINE
int CoffNativeCodeManager::LookupUnwindInfoIdx(uint32_t relativePc)
{
uint32_t** indices = m_initializedIndices;
if (!indices)
indices = InitFuncTableIndex();

uint32_t idx = 0;
for (int j = m_indexCount - 1; j >= 0; j--)
{
uint32_t* index = indices[j];
idx *= INDEX_BRANCHING_FACTOR;

while ((uint32_t)index[idx] < relativePc)
idx++;
}

for (idx *= FUNCTABLE_INDEX_GRANULARITY; idx < m_nRuntimeFunctionTable; idx++)
{
uint32_t curAddr = m_pRuntimeFunctionTable[idx].BeginAddress;
if (curAddr > relativePc)
return idx - 1;
}

// we can only get here if we are looking for a location inside the very last managed function.
_ASSERTE(m_pRuntimeFunctionTable[idx - 1].BeginAddress <= relativePc);
#if defined(TARGET_AMD64)
_ASSERTE(m_pRuntimeFunctionTable[idx - 1].EndAddress > relativePc);
#endif

return idx - 1;
}

bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
MethodInfo * pMethodInfoOut)
{
Expand All@@ -244,16 +352,10 @@ bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
}

CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethodInfoOut;

TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;

int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable,
0, m_nRuntimeFunctionTable - 1);
if (MethodIndex < 0)
return false;
int MethodIndex = LookupUnwindInfoIdx((uint32_t)relativePC);

PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;

pMethodInfo->runtimeFunction = pRuntimeFunction;

// The runtime function could correspond to a funclet. We need to get to the
Expand DownExpand Up@@ -965,10 +1067,7 @@ PTR_VOID CoffNativeCodeManager::GetAssociatedData(PTR_VOID ControlPC)
}

TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;

int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable, 0, m_nRuntimeFunctionTable - 1);
if (MethodIndex < 0)
return NULL;
int MethodIndex = LookupUnwindInfoIdx((uint32_t)relativePC);

PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;

Expand DownExpand Up@@ -1008,6 +1107,9 @@ bool RhRegisterOSModule(void * pModule,
if (pCoffNativeCodeManager == nullptr)
return false;

if (!pCoffNativeCodeManager->AllocFuncTableIndex())
return false;

RegisterCodeManager(pCoffNativeCodeManager, pvManagedCodeStartRange, cbManagedCodeRange);

if (!RegisterUnboxingStubs(pvUnboxingStubsStartRange, cbUnboxingStubsRange))
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,13 +44,24 @@ class CoffNativeCodeManager : public ICodeManager
PTR_PTR_VOID m_pClasslibFunctions;
uint32_t m_nClasslibFunctions;

// used to publish a reference to the index once initialized.
// if the reference is not null, the index can be accessed through it.
uint32_t** volatile m_initializedIndices;
uint32_t m_indexCount;
uint32_t* m_indices[8];

int LookupUnwindInfoIdx(uint32_t relativePc);

public:
CoffNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions);
~CoffNativeCodeManager();

bool AllocFuncTableIndex();
uint32_t** InitFuncTableIndex();

//
// Code manager methods
//
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
194 changes: 148 additions & 46 deletions src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,63 +164,138 @@ static PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntim
#endif
}

// index nodes are searched linearly.
// 16 * sizeof(uint32_t) == 64, which is a typical cache line size
// thus we expect at most one cache miss on every level of the index
#define INDEX_BRANCHING_FACTOR 16
// T_RUNTIME_FUNCTION is larger than uint32_t, so we have a smaller granularity at last level
#define FUNCTABLE_INDEX_GRANULARITY 8
#define INDEX_ALIGNMENT 64

CoffNativeCodeManager::CoffNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)
: m_moduleBase(moduleBase),
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions)
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions),
m_initializedIndices(0), m_indexCount(0), m_indices{ 0 }
{
}

CoffNativeCodeManager::~CoffNativeCodeManager()
{
for (uint32_t i = 0; i < m_indexCount; i++)
{
uint32_t* ptr = m_indices[i];
if (ptr)
{
_aligned_free(ptr);
m_indices[i] = nullptr;
}
}

m_indexCount = 0;
}

bool CoffNativeCodeManager::AllocFuncTableIndex()
{
uint32_t indexSize = (m_nRuntimeFunctionTable + FUNCTABLE_INDEX_GRANULARITY - 1) / FUNCTABLE_INDEX_GRANULARITY;
uint32_t* index = m_indices[m_indexCount] = (uint32_t*)_aligned_malloc(indexSize * sizeof(uint32_t), INDEX_ALIGNMENT);
if (!index)
return false;

memset(index, 0, indexSize * sizeof(uint32_t));
m_indexCount++;

while (indexSize > INDEX_BRANCHING_FACTOR)
{
uint32_t prevSize = indexSize;
indexSize = (indexSize + INDEX_BRANCHING_FACTOR - 1) / INDEX_BRANCHING_FACTOR;
index = m_indices[m_indexCount] = (uint32_t*)_aligned_malloc(indexSize * sizeof(uint32_t), INDEX_ALIGNMENT);
if (!index)
return false;

memset(index, 0, indexSize * sizeof(uint32_t));
m_indexCount++;
}

return true;
}

static int LookupUnwindInfoForMethod(uint32_t relativePc,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable,
int low,
int high)
NOINLINE
uint32_t** CoffNativeCodeManager::InitFuncTableIndex()
{
// Binary search the RUNTIME_FUNCTION table
// Use linear search once we get down to a small number of elements
// to avoid Binary search overhead.
while (high - low > 10)
// max offset is beyond the range of managed methods.
int maxOffset = (int)((TADDR)m_pvManagedCodeStartRange + m_cbManagedCodeRange - m_moduleBase);

// It is possible to see several threads come here at once.
// We can spin-wait for one thread to do the work or just let all threads do the initialization.
// Either way it will take roughly the same time as for the first thread to complete the work.
// Yet we can make this complete faster if threads help each other by working on different
// parts of the index.
uint32_t perThreadBias = (uint32_t)(((size_t)&perThreadBias * 11400714819323198485ul) >> 32);

// lets build the index for the runtime table. for every granule that has elements we will have an index entry
uint32_t indexSize = (m_nRuntimeFunctionTable + FUNCTABLE_INDEX_GRANULARITY - 1) / FUNCTABLE_INDEX_GRANULARITY;
uint32_t indexCount = 0;
uint32_t* index = m_indices[indexCount++];

// every index N will contain the lowest value from the granule N + 1
// when we will scan the value N in the indices and see that it is higher than the target, we will know
// that the granule N must be searched for the entry as the next granule will have higher addresses.
uint32_t start = (perThreadBias % indexSize) | 1;
for (uint32_t i = start; i < indexSize; i++)
{
int middle = low + (high - low) / 2;

PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + middle;
if (relativePc < pFunctionEntry->BeginAddress)
{
high = middle - 1;
}
else
{
low = middle;
}
if (index[i - 1] == 0)
{
_ASSERTE(i * FUNCTABLE_INDEX_GRANULARITY < m_nRuntimeFunctionTable);
index[i - 1] = m_pRuntimeFunctionTable[i * FUNCTABLE_INDEX_GRANULARITY].BeginAddress;
}
}

for (int i = low; i < high; i++)
for (uint32_t i = 1; i < start; i++)
{
PTR_RUNTIME_FUNCTION pNextFunctionEntry = pRuntimeFunctionTable + (i + 1);
if (relativePc < pNextFunctionEntry->BeginAddress)
if (index[i - 1] == 0)
{
high = i;
break;
_ASSERTE(i * FUNCTABLE_INDEX_GRANULARITY < m_nRuntimeFunctionTable);
index[i - 1] = m_pRuntimeFunctionTable[i * FUNCTABLE_INDEX_GRANULARITY].BeginAddress;
}
}

PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + high;
if (relativePc >= pFunctionEntry->BeginAddress)
// we put the maxOffset at the end of the index.
// there is no N + 1 granule to get the value from, so the last slot will contain the sentinel.
index[indexSize - 1] = maxOffset;

// Now build an N-ary tree of indices.
// Example: at branching factor 16 a program with 32K methods will have 3 sub-index levels.
uint32_t* prevIdx = index;
while (indexSize > INDEX_BRANCHING_FACTOR)
{
return high;
uint32_t prevSize = indexSize;
indexSize = (indexSize + INDEX_BRANCHING_FACTOR - 1) / INDEX_BRANCHING_FACTOR;
index = m_indices[indexCount++];

start = (perThreadBias % indexSize) | 1;
for (uint32_t i = start; i < indexSize; i++)
{
_ASSERTE(i * INDEX_BRANCHING_FACTOR < prevSize);
index[i - 1] = prevIdx[i * INDEX_BRANCHING_FACTOR];
}

for (uint32_t i = 1; i < start; i++)
{
_ASSERTE(i * INDEX_BRANCHING_FACTOR < prevSize);
index[i - 1] = prevIdx[i * INDEX_BRANCHING_FACTOR];
}

index[indexSize - 1] = maxOffset;
prevIdx = index;
}

ASSERT_UNCONDITIONALLY("Invalid code address");
return -1;
WriteRelease64((LONG64*)&m_initializedIndices, (LONG64)m_indices);
return m_initializedIndices;
}

struct CoffNativeMethodInfo
Expand All@@ -233,6 +308,39 @@ struct CoffNativeMethodInfo
// Ensure that CoffNativeMethodInfo fits into the space reserved by MethodInfo
static_assert(sizeof(CoffNativeMethodInfo) <= sizeof(MethodInfo), "CoffNativeMethodInfo too big");

FORCEINLINE
int CoffNativeCodeManager::LookupUnwindInfoIdx(uint32_t relativePc)
{
uint32_t** indices = m_initializedIndices;
if (!indices)
indices = InitFuncTableIndex();

uint32_t idx = 0;
for (int j = m_indexCount - 1; j >= 0; j--)
{
uint32_t* index = indices[j];
idx *= INDEX_BRANCHING_FACTOR;

while ((uint32_t)index[idx] < relativePc)
idx++;
}

for (idx *= FUNCTABLE_INDEX_GRANULARITY; idx < m_nRuntimeFunctionTable; idx++)
{
uint32_t curAddr = m_pRuntimeFunctionTable[idx].BeginAddress;
if (curAddr > relativePc)
return idx - 1;
}

// we can only get here if we are looking for a location inside the very last managed function.
_ASSERTE(m_pRuntimeFunctionTable[idx - 1].BeginAddress <= relativePc);
#if defined(TARGET_AMD64)
_ASSERTE(m_pRuntimeFunctionTable[idx - 1].EndAddress > relativePc);
#endif

return idx - 1;
}

bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
MethodInfo * pMethodInfoOut)
{
Expand All@@ -244,16 +352,10 @@ bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
}

CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethodInfoOut;

TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;

int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable,
0, m_nRuntimeFunctionTable - 1);
if (MethodIndex < 0)
return false;
int MethodIndex = LookupUnwindInfoIdx((uint32_t)relativePC);

PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;

pMethodInfo->runtimeFunction = pRuntimeFunction;

// The runtime function could correspond to a funclet. We need to get to the
Expand DownExpand Up@@ -965,10 +1067,7 @@ PTR_VOID CoffNativeCodeManager::GetAssociatedData(PTR_VOID ControlPC)
}

TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;

int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable, 0, m_nRuntimeFunctionTable - 1);
if (MethodIndex < 0)
return NULL;
int MethodIndex = LookupUnwindInfoIdx((uint32_t)relativePC);

PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;

Expand DownExpand Up@@ -1008,6 +1107,9 @@ bool RhRegisterOSModule(void * pModule,
if (pCoffNativeCodeManager == nullptr)
return false;

if (!pCoffNativeCodeManager->AllocFuncTableIndex())
return false;

RegisterCodeManager(pCoffNativeCodeManager, pvManagedCodeStartRange, cbManagedCodeRange);

if (!RegisterUnboxingStubs(pvUnboxingStubsStartRange, cbUnboxingStubsRange))
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,13 +44,24 @@ class CoffNativeCodeManager : public ICodeManager
PTR_PTR_VOID m_pClasslibFunctions;
uint32_t m_nClasslibFunctions;

// used to publish a reference to the index once initialized.
// if the reference is not null, the index can be accessed through it.
uint32_t** volatile m_initializedIndices;
uint32_t m_indexCount;
uint32_t* m_indices[8];

int LookupUnwindInfoIdx(uint32_t relativePc);

public:
CoffNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions);
~CoffNativeCodeManager();

bool AllocFuncTableIndex();
uint32_t** InitFuncTableIndex();

//
// Code manager methods
//
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
194 changes: 148 additions & 46 deletions src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -164,63 +164,138 @@ static PTR_VOID GetUnwindDataBlob(TADDR moduleBase, PTR_RUNTIME_FUNCTION pRuntim
#endif
}

// index nodes are searched linearly.
// 16 * sizeof(uint32_t) == 64, which is a typical cache line size
// thus we expect at most one cache miss on every level of the index
#define INDEX_BRANCHING_FACTOR 16
// T_RUNTIME_FUNCTION is larger than uint32_t, so we have a smaller granularity at last level
#define FUNCTABLE_INDEX_GRANULARITY 8
#define INDEX_ALIGNMENT 64

CoffNativeCodeManager::CoffNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions)
: m_moduleBase(moduleBase),
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions)
m_pvManagedCodeStartRange(pvManagedCodeStartRange), m_cbManagedCodeRange(cbManagedCodeRange),
m_pRuntimeFunctionTable(pRuntimeFunctionTable), m_nRuntimeFunctionTable(nRuntimeFunctionTable),
m_pClasslibFunctions(pClasslibFunctions), m_nClasslibFunctions(nClasslibFunctions),
m_initializedIndices(0), m_indexCount(0), m_indices{ 0 }
{
}

CoffNativeCodeManager::~CoffNativeCodeManager()
{
for (uint32_t i = 0; i < m_indexCount; i++)
{
uint32_t* ptr = m_indices[i];
if (ptr)
{
_aligned_free(ptr);
m_indices[i] = nullptr;
}
}

m_indexCount = 0;
}

bool CoffNativeCodeManager::AllocFuncTableIndex()
{
uint32_t indexSize = (m_nRuntimeFunctionTable + FUNCTABLE_INDEX_GRANULARITY - 1) / FUNCTABLE_INDEX_GRANULARITY;
uint32_t* index = m_indices[m_indexCount] = (uint32_t*)_aligned_malloc(indexSize * sizeof(uint32_t), INDEX_ALIGNMENT);
if (!index)
return false;

memset(index, 0, indexSize * sizeof(uint32_t));
m_indexCount++;

while (indexSize > INDEX_BRANCHING_FACTOR)
{
uint32_t prevSize = indexSize;
indexSize = (indexSize + INDEX_BRANCHING_FACTOR - 1) / INDEX_BRANCHING_FACTOR;
index = m_indices[m_indexCount] = (uint32_t*)_aligned_malloc(indexSize * sizeof(uint32_t), INDEX_ALIGNMENT);
if (!index)
return false;

memset(index, 0, indexSize * sizeof(uint32_t));
m_indexCount++;
}

return true;
}

static int LookupUnwindInfoForMethod(uint32_t relativePc,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable,
int low,
int high)
NOINLINE
uint32_t** CoffNativeCodeManager::InitFuncTableIndex()
{
// Binary search the RUNTIME_FUNCTION table
// Use linear search once we get down to a small number of elements
// to avoid Binary search overhead.
while (high - low > 10)
// max offset is beyond the range of managed methods.
int maxOffset = (int)((TADDR)m_pvManagedCodeStartRange + m_cbManagedCodeRange - m_moduleBase);

// It is possible to see several threads come here at once.
// We can spin-wait for one thread to do the work or just let all threads do the initialization.
// Either way it will take roughly the same time as for the first thread to complete the work.
// Yet we can make this complete faster if threads help each other by working on different
// parts of the index.
uint32_t perThreadBias = (uint32_t)(((size_t)&perThreadBias * 11400714819323198485ul) >> 32);

// lets build the index for the runtime table. for every granule that has elements we will have an index entry
uint32_t indexSize = (m_nRuntimeFunctionTable + FUNCTABLE_INDEX_GRANULARITY - 1) / FUNCTABLE_INDEX_GRANULARITY;
uint32_t indexCount = 0;
uint32_t* index = m_indices[indexCount++];

// every index N will contain the lowest value from the granule N + 1
// when we will scan the value N in the indices and see that it is higher than the target, we will know
// that the granule N must be searched for the entry as the next granule will have higher addresses.
uint32_t start = (perThreadBias % indexSize) | 1;
for (uint32_t i = start; i < indexSize; i++)
{
int middle = low + (high - low) / 2;

PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + middle;
if (relativePc < pFunctionEntry->BeginAddress)
{
high = middle - 1;
}
else
{
low = middle;
}
if (index[i - 1] == 0)
{
_ASSERTE(i * FUNCTABLE_INDEX_GRANULARITY < m_nRuntimeFunctionTable);
index[i - 1] = m_pRuntimeFunctionTable[i * FUNCTABLE_INDEX_GRANULARITY].BeginAddress;
}
}

for (int i = low; i < high; i++)
for (uint32_t i = 1; i < start; i++)
{
PTR_RUNTIME_FUNCTION pNextFunctionEntry = pRuntimeFunctionTable + (i + 1);
if (relativePc < pNextFunctionEntry->BeginAddress)
if (index[i - 1] == 0)
{
high = i;
break;
_ASSERTE(i * FUNCTABLE_INDEX_GRANULARITY < m_nRuntimeFunctionTable);
index[i - 1] = m_pRuntimeFunctionTable[i * FUNCTABLE_INDEX_GRANULARITY].BeginAddress;
}
}

PTR_RUNTIME_FUNCTION pFunctionEntry = pRuntimeFunctionTable + high;
if (relativePc >= pFunctionEntry->BeginAddress)
// we put the maxOffset at the end of the index.
// there is no N + 1 granule to get the value from, so the last slot will contain the sentinel.
index[indexSize - 1] = maxOffset;

// Now build an N-ary tree of indices.
// Example: at branching factor 16 a program with 32K methods will have 3 sub-index levels.
uint32_t* prevIdx = index;
while (indexSize > INDEX_BRANCHING_FACTOR)
{
return high;
uint32_t prevSize = indexSize;
indexSize = (indexSize + INDEX_BRANCHING_FACTOR - 1) / INDEX_BRANCHING_FACTOR;
index = m_indices[indexCount++];

start = (perThreadBias % indexSize) | 1;
for (uint32_t i = start; i < indexSize; i++)
{
_ASSERTE(i * INDEX_BRANCHING_FACTOR < prevSize);
index[i - 1] = prevIdx[i * INDEX_BRANCHING_FACTOR];
}

for (uint32_t i = 1; i < start; i++)
{
_ASSERTE(i * INDEX_BRANCHING_FACTOR < prevSize);
index[i - 1] = prevIdx[i * INDEX_BRANCHING_FACTOR];
}

index[indexSize - 1] = maxOffset;
prevIdx = index;
}

ASSERT_UNCONDITIONALLY("Invalid code address");
return -1;
WriteRelease64((LONG64*)&m_initializedIndices, (LONG64)m_indices);
return m_initializedIndices;
}

struct CoffNativeMethodInfo
Expand All@@ -233,6 +308,39 @@ struct CoffNativeMethodInfo
// Ensure that CoffNativeMethodInfo fits into the space reserved by MethodInfo
static_assert(sizeof(CoffNativeMethodInfo) <= sizeof(MethodInfo), "CoffNativeMethodInfo too big");

FORCEINLINE
int CoffNativeCodeManager::LookupUnwindInfoIdx(uint32_t relativePc)
{
uint32_t** indices = m_initializedIndices;
if (!indices)
indices = InitFuncTableIndex();

uint32_t idx = 0;
for (int j = m_indexCount - 1; j >= 0; j--)
{
uint32_t* index = indices[j];
idx *= INDEX_BRANCHING_FACTOR;

while ((uint32_t)index[idx] < relativePc)
idx++;
}

for (idx *= FUNCTABLE_INDEX_GRANULARITY; idx < m_nRuntimeFunctionTable; idx++)
{
uint32_t curAddr = m_pRuntimeFunctionTable[idx].BeginAddress;
if (curAddr > relativePc)
return idx - 1;
}

// we can only get here if we are looking for a location inside the very last managed function.
_ASSERTE(m_pRuntimeFunctionTable[idx - 1].BeginAddress <= relativePc);
#if defined(TARGET_AMD64)
_ASSERTE(m_pRuntimeFunctionTable[idx - 1].EndAddress > relativePc);
#endif

return idx - 1;
}

bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
MethodInfo * pMethodInfoOut)
{
Expand All@@ -244,16 +352,10 @@ bool CoffNativeCodeManager::FindMethodInfo(PTR_VOID ControlPC,
}

CoffNativeMethodInfo * pMethodInfo = (CoffNativeMethodInfo *)pMethodInfoOut;

TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;

int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable,
0, m_nRuntimeFunctionTable - 1);
if (MethodIndex < 0)
return false;
int MethodIndex = LookupUnwindInfoIdx((uint32_t)relativePC);

PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;

pMethodInfo->runtimeFunction = pRuntimeFunction;

// The runtime function could correspond to a funclet. We need to get to the
Expand DownExpand Up@@ -965,10 +1067,7 @@ PTR_VOID CoffNativeCodeManager::GetAssociatedData(PTR_VOID ControlPC)
}

TADDR relativePC = dac_cast<TADDR>(ControlPC) - m_moduleBase;

int MethodIndex = LookupUnwindInfoForMethod((uint32_t)relativePC, m_pRuntimeFunctionTable, 0, m_nRuntimeFunctionTable - 1);
if (MethodIndex < 0)
return NULL;
int MethodIndex = LookupUnwindInfoIdx((uint32_t)relativePC);

PTR_RUNTIME_FUNCTION pRuntimeFunction = m_pRuntimeFunctionTable + MethodIndex;

Expand DownExpand Up@@ -1008,6 +1107,9 @@ bool RhRegisterOSModule(void * pModule,
if (pCoffNativeCodeManager == nullptr)
return false;

if (!pCoffNativeCodeManager->AllocFuncTableIndex())
return false;

RegisterCodeManager(pCoffNativeCodeManager, pvManagedCodeStartRange, cbManagedCodeRange);

if (!RegisterUnboxingStubs(pvUnboxingStubsStartRange, cbUnboxingStubsRange))
Expand Down
11 changes: 11 additions & 0 deletions src/coreclr/nativeaot/Runtime/windows/CoffNativeCodeManager.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -44,13 +44,24 @@ class CoffNativeCodeManager : public ICodeManager
PTR_PTR_VOID m_pClasslibFunctions;
uint32_t m_nClasslibFunctions;

// used to publish a reference to the index once initialized.
// if the reference is not null, the index can be accessed through it.
uint32_t** volatile m_initializedIndices;
uint32_t m_indexCount;
uint32_t* m_indices[8];

int LookupUnwindInfoIdx(uint32_t relativePc);

public:
CoffNativeCodeManager(TADDR moduleBase,
PTR_VOID pvManagedCodeStartRange, uint32_t cbManagedCodeRange,
PTR_RUNTIME_FUNCTION pRuntimeFunctionTable, uint32_t nRuntimeFunctionTable,
PTR_PTR_VOID pClasslibFunctions, uint32_t nClasslibFunctions);
~CoffNativeCodeManager();

bool AllocFuncTableIndex();
uint32_t** InitFuncTableIndex();

//
// Code manager methods
//
Expand Down