Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/coreclr/jit/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -476,6 +476,7 @@ set( JIT_WASM_HEADERS
emitfmtswasm.h
instrswasm.h
registerwasm.h
fgwasm.h
)

convert_to_absolute_path(JIT_SOURCES ${JIT_SOURCES})
Expand Down
20 changes: 16 additions & 4 deletions src/coreclr/jit/compiler.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -1798,13 +1798,17 @@ class FlowGraphDfsTree
// Whether the DFS that produced the tree used edge likelihoods to influence successor visitation order.
bool m_profileAware;

// Whether the DFS reflects Wasm control flow rules.
bool m_forWasm;
Comment thread
AndyAyersMS marked this conversation as resolved.

public:
FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware)
FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware, bool forWasm = false)
: m_comp(comp)
, m_postOrder(postOrder)
, m_postOrderCount(postOrderCount)
, m_hasCycle(hasCycle)
, m_profileAware(profileAware)
, m_forWasm(forWasm)
{
}

Expand DownExpand Up@@ -1844,6 +1848,11 @@ class FlowGraphDfsTree
return m_profileAware;
}

bool IsForWasm() const
{
return m_forWasm;
}

#ifdef DEBUG
void Dump() const;
#endif // DEBUG
Expand DownExpand Up@@ -2137,7 +2146,7 @@ class FlowGraphNaturalLoops

FlowGraphNaturalLoops(const FlowGraphDfsTree* dfs);

static bool FindNaturalLoopBlocks(FlowGraphNaturalLoop* loop, ArrayStack<BasicBlock*>& worklist);
bool FindNaturalLoopBlocks(FlowGraphNaturalLoop* loop, ArrayStack<BasicBlock*>& worklist);
static bool IsLoopCanonicalizable(FlowGraphNaturalLoop* loop);

public:
Expand All@@ -2157,6 +2166,8 @@ class FlowGraphNaturalLoops
bool IsLoopBackEdge(FlowEdge* edge);
bool IsLoopExitEdge(FlowEdge* edge);

bool IsForWasm() { return m_dfsTree->IsForWasm(); }

class LoopsPostOrderIter
{
jitstd::vector<FlowGraphNaturalLoop*>* m_loops;
Expand DownExpand Up@@ -6237,6 +6248,7 @@ class Compiler

PhaseStatus fgFindOperOrder();

FlowGraphDfsTree* fgWasmDfs();
PhaseStatus fgWasmControlFlow();

// method that returns if you should split here
Expand All@@ -6245,8 +6257,8 @@ class Compiler
PhaseStatus fgSetBlockOrder();
bool fgHasCycleWithoutGCSafePoint();

template <typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile = false>
unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge);
template <typename SuccessorEnumerator, typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile = false>
unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge, jitstd::vector<BasicBlock*>& entries);

template <const bool useProfile = false>
FlowGraphDfsTree* fgComputeDfs();
Expand Down
44 changes: 22 additions & 22 deletions src/coreclr/jit/compiler.hpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -5105,30 +5105,39 @@ inline bool Compiler::compCanHavePatchpoints(const char** reason)
// fgRunDfs: Run DFS over the flow graph.
//
// Type parameters:
// VisitPreorder - Functor type that takes a BasicBlock* and its preorder number
// VisitPostorder - Functor type that takes a BasicBlock* and its postorder number
// VisitEdge - Functor type that takes two BasicBlock*.
// useProfile - If true, determines order of successors visited using profile data
// SuccessorEnumerator - Functor type to enumerate successors of a block
// VisitPreorder - Functor type that takes a BasicBlock* and its preorder number
// VisitPostorder - Functor type that takes a BasicBlock* and its postorder number
// VisitEdge - Functor type that takes two BasicBlock*.
// useProfile - If true, determines order of successors visited using profile data
//
// Parameters:
// visitPreorder - Functor to visit block in its preorder
// visitPostorder - Functor to visit block in its postorder
// visitEdge - Functor to visit an edge. Called after visitPreorder (if
// this is the first time the successor is seen).
// entryBlocks - vector of entry blocks for the flow graph
//
// Returns:
// Number of blocks visited.
//
template <typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile /* = false */>
unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPostorder, VisitEdge visitEdge)
template <typename SuccessorEnumerator,
typename VisitPreorder,
typename VisitPostorder,
typename VisitEdge,
const bool useProfile /* = false */>
unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder,
VisitPostorder visitPostorder,
VisitEdge visitEdge,
jitstd::vector<BasicBlock*>& entryBlocks)
{
BitVecTraits traits(fgBBNumMax + 1, this);
BitVec visited(BitVecOps::MakeEmpty(&traits));

unsigned preOrderIndex = 0;
unsigned postOrderIndex = 0;

ArrayStack<AllSuccessorEnumerator> blocks(getAllocator(CMK_DepthFirstSearch));
ArrayStack<SuccessorEnumerator> blocks(getAllocator(CMK_DepthFirstSearch));

auto dfsFrom = [&](BasicBlock* firstBB) {
BitVecOps::AddElemD(&traits, visited, firstBB->bbNum);
Expand DownExpand Up@@ -5158,23 +5167,14 @@ unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPos
}
};

dfsFrom(fgFirstBB);
assert(!entryBlocks.empty());

if ((fgEntryBB != nullptr) && !BitVecOps::IsMember(&traits, visited, fgEntryBB->bbNum))
for (BasicBlock* const entryBlock : entryBlocks)
{
// OSR methods will early on create flow that looks like it goes to the
// patchpoint, but during morph we may transform to something that
// requires the original entry (fgEntryBB).
assert(opts.IsOSR());
dfsFrom(fgEntryBB);
}

if ((genReturnBB != nullptr) && !BitVecOps::IsMember(&traits, visited, genReturnBB->bbNum))
{
assert(!fgGlobalMorphDone);
// We introduce the merged return BB before morph and will redirect
// other returns to it as part of morph; keep it reachable.
dfsFrom(genReturnBB);
if (!BitVecOps::IsMember(&traits, visited, entryBlock->bbNum))
{
dfsFrom(entryBlock);
}
}

assert(preOrderIndex == postOrderIndex);
Expand Down
30 changes: 24 additions & 6 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -4739,18 +4739,36 @@ void Compiler::fgDebugCheckFlowGraphAnnotations()

auto visitEdge = [](BasicBlock* block, BasicBlock* succ) {};

jitstd::vector<BasicBlock*> entryBlocks(getAllocator(CMK_DepthFirstSearch));

entryBlocks.push_back(fgFirstBB);

if (fgEntryBB != nullptr)
{
// OSR methods will early on create flow that looks like it goes to the
// patchpoint, but during morph we may transform to something that
// requires the original entry (fgEntryBB).
assert(opts.IsOSR());
entryBlocks.push_back(fgEntryBB);
}

if ((genReturnBB != nullptr) && !fgGlobalMorphDone)
{
// We introduce the merged return BB before morph and will redirect
// other returns to it as part of morph; keep it reachable.
entryBlocks.push_back(genReturnBB);
}

unsigned count;
if (m_dfsTree->IsProfileAware())
{
count = fgRunDfs<decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge), true>(visitPreorder,
visitPostorder,
visitEdge);
count = fgRunDfs<AllSuccessorEnumerator, decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge),
true>(visitPreorder, visitPostorder, visitEdge, entryBlocks);
}
else
{
count = fgRunDfs<decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge), false>(visitPreorder,
visitPostorder,
visitEdge);
count = fgRunDfs<AllSuccessorEnumerator, decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge),
false>(visitPreorder, visitPostorder, visitEdge, entryBlocks);
}

assert(m_dfsTree->GetPostOrderCount() == count);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
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;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
[Wasm RyuJIT] Implement Wasm DFS and Loop Finding by AndyAyersMS · Pull Request #121457 · dotnet/runtime · GitHub
Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/coreclr/jit/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -476,6 +476,7 @@ set( JIT_WASM_HEADERS
emitfmtswasm.h
instrswasm.h
registerwasm.h
fgwasm.h
)

convert_to_absolute_path(JIT_SOURCES ${JIT_SOURCES})
Expand Down
20 changes: 16 additions & 4 deletions src/coreclr/jit/compiler.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -1798,13 +1798,17 @@ class FlowGraphDfsTree
// Whether the DFS that produced the tree used edge likelihoods to influence successor visitation order.
bool m_profileAware;

// Whether the DFS reflects Wasm control flow rules.
bool m_forWasm;
Comment thread
AndyAyersMS marked this conversation as resolved.

public:
FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware)
FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware, bool forWasm = false)
: m_comp(comp)
, m_postOrder(postOrder)
, m_postOrderCount(postOrderCount)
, m_hasCycle(hasCycle)
, m_profileAware(profileAware)
, m_forWasm(forWasm)
{
}

Expand DownExpand Up@@ -1844,6 +1848,11 @@ class FlowGraphDfsTree
return m_profileAware;
}

bool IsForWasm() const
{
return m_forWasm;
}

#ifdef DEBUG
void Dump() const;
#endif // DEBUG
Expand DownExpand Up@@ -2137,7 +2146,7 @@ class FlowGraphNaturalLoops

FlowGraphNaturalLoops(const FlowGraphDfsTree* dfs);

static bool FindNaturalLoopBlocks(FlowGraphNaturalLoop* loop, ArrayStack<BasicBlock*>& worklist);
bool FindNaturalLoopBlocks(FlowGraphNaturalLoop* loop, ArrayStack<BasicBlock*>& worklist);
static bool IsLoopCanonicalizable(FlowGraphNaturalLoop* loop);

public:
Expand All@@ -2157,6 +2166,8 @@ class FlowGraphNaturalLoops
bool IsLoopBackEdge(FlowEdge* edge);
bool IsLoopExitEdge(FlowEdge* edge);

bool IsForWasm() { return m_dfsTree->IsForWasm(); }

class LoopsPostOrderIter
{
jitstd::vector<FlowGraphNaturalLoop*>* m_loops;
Expand DownExpand Up@@ -6237,6 +6248,7 @@ class Compiler

PhaseStatus fgFindOperOrder();

FlowGraphDfsTree* fgWasmDfs();
PhaseStatus fgWasmControlFlow();

// method that returns if you should split here
Expand All@@ -6245,8 +6257,8 @@ class Compiler
PhaseStatus fgSetBlockOrder();
bool fgHasCycleWithoutGCSafePoint();

template <typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile = false>
unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge);
template <typename SuccessorEnumerator, typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile = false>
unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge, jitstd::vector<BasicBlock*>& entries);

template <const bool useProfile = false>
FlowGraphDfsTree* fgComputeDfs();
Expand Down
44 changes: 22 additions & 22 deletions src/coreclr/jit/compiler.hpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -5105,30 +5105,39 @@ inline bool Compiler::compCanHavePatchpoints(const char** reason)
// fgRunDfs: Run DFS over the flow graph.
//
// Type parameters:
// VisitPreorder - Functor type that takes a BasicBlock* and its preorder number
// VisitPostorder - Functor type that takes a BasicBlock* and its postorder number
// VisitEdge - Functor type that takes two BasicBlock*.
// useProfile - If true, determines order of successors visited using profile data
// SuccessorEnumerator - Functor type to enumerate successors of a block
// VisitPreorder - Functor type that takes a BasicBlock* and its preorder number
// VisitPostorder - Functor type that takes a BasicBlock* and its postorder number
// VisitEdge - Functor type that takes two BasicBlock*.
// useProfile - If true, determines order of successors visited using profile data
//
// Parameters:
// visitPreorder - Functor to visit block in its preorder
// visitPostorder - Functor to visit block in its postorder
// visitEdge - Functor to visit an edge. Called after visitPreorder (if
// this is the first time the successor is seen).
// entryBlocks - vector of entry blocks for the flow graph
//
// Returns:
// Number of blocks visited.
//
template <typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile /* = false */>
unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPostorder, VisitEdge visitEdge)
template <typename SuccessorEnumerator,
typename VisitPreorder,
typename VisitPostorder,
typename VisitEdge,
const bool useProfile /* = false */>
unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder,
VisitPostorder visitPostorder,
VisitEdge visitEdge,
jitstd::vector<BasicBlock*>& entryBlocks)
{
BitVecTraits traits(fgBBNumMax + 1, this);
BitVec visited(BitVecOps::MakeEmpty(&traits));

unsigned preOrderIndex = 0;
unsigned postOrderIndex = 0;

ArrayStack<AllSuccessorEnumerator> blocks(getAllocator(CMK_DepthFirstSearch));
ArrayStack<SuccessorEnumerator> blocks(getAllocator(CMK_DepthFirstSearch));

auto dfsFrom = [&](BasicBlock* firstBB) {
BitVecOps::AddElemD(&traits, visited, firstBB->bbNum);
Expand DownExpand Up@@ -5158,23 +5167,14 @@ unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPos
}
};

dfsFrom(fgFirstBB);
assert(!entryBlocks.empty());

if ((fgEntryBB != nullptr) && !BitVecOps::IsMember(&traits, visited, fgEntryBB->bbNum))
for (BasicBlock* const entryBlock : entryBlocks)
{
// OSR methods will early on create flow that looks like it goes to the
// patchpoint, but during morph we may transform to something that
// requires the original entry (fgEntryBB).
assert(opts.IsOSR());
dfsFrom(fgEntryBB);
}

if ((genReturnBB != nullptr) && !BitVecOps::IsMember(&traits, visited, genReturnBB->bbNum))
{
assert(!fgGlobalMorphDone);
// We introduce the merged return BB before morph and will redirect
// other returns to it as part of morph; keep it reachable.
dfsFrom(genReturnBB);
if (!BitVecOps::IsMember(&traits, visited, entryBlock->bbNum))
{
dfsFrom(entryBlock);
}
}

assert(preOrderIndex == postOrderIndex);
Expand Down
30 changes: 24 additions & 6 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -4739,18 +4739,36 @@ void Compiler::fgDebugCheckFlowGraphAnnotations()

auto visitEdge = [](BasicBlock* block, BasicBlock* succ) {};

jitstd::vector<BasicBlock*> entryBlocks(getAllocator(CMK_DepthFirstSearch));

entryBlocks.push_back(fgFirstBB);

if (fgEntryBB != nullptr)
{
// OSR methods will early on create flow that looks like it goes to the
// patchpoint, but during morph we may transform to something that
// requires the original entry (fgEntryBB).
assert(opts.IsOSR());
entryBlocks.push_back(fgEntryBB);
}

if ((genReturnBB != nullptr) && !fgGlobalMorphDone)
{
// We introduce the merged return BB before morph and will redirect
// other returns to it as part of morph; keep it reachable.
entryBlocks.push_back(genReturnBB);
}

unsigned count;
if (m_dfsTree->IsProfileAware())
{
count = fgRunDfs<decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge), true>(visitPreorder,
visitPostorder,
visitEdge);
count = fgRunDfs<AllSuccessorEnumerator, decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge),
true>(visitPreorder, visitPostorder, visitEdge, entryBlocks);
}
else
{
count = fgRunDfs<decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge), false>(visitPreorder,
visitPostorder,
visitEdge);
count = fgRunDfs<AllSuccessorEnumerator, decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge),
false>(visitPreorder, visitPostorder, visitEdge, entryBlocks);
}

assert(m_dfsTree->GetPostOrderCount() == count);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [Wasm RyuJIT] Implement Wasm DFS and Loop Finding by AndyAyersMS · Pull Request #121457 · dotnet/runtime · GitHub
Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/coreclr/jit/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -476,6 +476,7 @@ set( JIT_WASM_HEADERS
emitfmtswasm.h
instrswasm.h
registerwasm.h
fgwasm.h
)

convert_to_absolute_path(JIT_SOURCES ${JIT_SOURCES})
Expand Down
20 changes: 16 additions & 4 deletions src/coreclr/jit/compiler.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -1798,13 +1798,17 @@ class FlowGraphDfsTree
// Whether the DFS that produced the tree used edge likelihoods to influence successor visitation order.
bool m_profileAware;

// Whether the DFS reflects Wasm control flow rules.
bool m_forWasm;
Comment thread
AndyAyersMS marked this conversation as resolved.

public:
FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware)
FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware, bool forWasm = false)
: m_comp(comp)
, m_postOrder(postOrder)
, m_postOrderCount(postOrderCount)
, m_hasCycle(hasCycle)
, m_profileAware(profileAware)
, m_forWasm(forWasm)
{
}

Expand DownExpand Up@@ -1844,6 +1848,11 @@ class FlowGraphDfsTree
return m_profileAware;
}

bool IsForWasm() const
{
return m_forWasm;
}

#ifdef DEBUG
void Dump() const;
#endif // DEBUG
Expand DownExpand Up@@ -2137,7 +2146,7 @@ class FlowGraphNaturalLoops

FlowGraphNaturalLoops(const FlowGraphDfsTree* dfs);

static bool FindNaturalLoopBlocks(FlowGraphNaturalLoop* loop, ArrayStack<BasicBlock*>& worklist);
bool FindNaturalLoopBlocks(FlowGraphNaturalLoop* loop, ArrayStack<BasicBlock*>& worklist);
static bool IsLoopCanonicalizable(FlowGraphNaturalLoop* loop);

public:
Expand All@@ -2157,6 +2166,8 @@ class FlowGraphNaturalLoops
bool IsLoopBackEdge(FlowEdge* edge);
bool IsLoopExitEdge(FlowEdge* edge);

bool IsForWasm() { return m_dfsTree->IsForWasm(); }

class LoopsPostOrderIter
{
jitstd::vector<FlowGraphNaturalLoop*>* m_loops;
Expand DownExpand Up@@ -6237,6 +6248,7 @@ class Compiler

PhaseStatus fgFindOperOrder();

FlowGraphDfsTree* fgWasmDfs();
PhaseStatus fgWasmControlFlow();

// method that returns if you should split here
Expand All@@ -6245,8 +6257,8 @@ class Compiler
PhaseStatus fgSetBlockOrder();
bool fgHasCycleWithoutGCSafePoint();

template <typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile = false>
unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge);
template <typename SuccessorEnumerator, typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile = false>
unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge, jitstd::vector<BasicBlock*>& entries);

template <const bool useProfile = false>
FlowGraphDfsTree* fgComputeDfs();
Expand Down
44 changes: 22 additions & 22 deletions src/coreclr/jit/compiler.hpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -5105,30 +5105,39 @@ inline bool Compiler::compCanHavePatchpoints(const char** reason)
// fgRunDfs: Run DFS over the flow graph.
//
// Type parameters:
// VisitPreorder - Functor type that takes a BasicBlock* and its preorder number
// VisitPostorder - Functor type that takes a BasicBlock* and its postorder number
// VisitEdge - Functor type that takes two BasicBlock*.
// useProfile - If true, determines order of successors visited using profile data
// SuccessorEnumerator - Functor type to enumerate successors of a block
// VisitPreorder - Functor type that takes a BasicBlock* and its preorder number
// VisitPostorder - Functor type that takes a BasicBlock* and its postorder number
// VisitEdge - Functor type that takes two BasicBlock*.
// useProfile - If true, determines order of successors visited using profile data
//
// Parameters:
// visitPreorder - Functor to visit block in its preorder
// visitPostorder - Functor to visit block in its postorder
// visitEdge - Functor to visit an edge. Called after visitPreorder (if
// this is the first time the successor is seen).
// entryBlocks - vector of entry blocks for the flow graph
//
// Returns:
// Number of blocks visited.
//
template <typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile /* = false */>
unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPostorder, VisitEdge visitEdge)
template <typename SuccessorEnumerator,
typename VisitPreorder,
typename VisitPostorder,
typename VisitEdge,
const bool useProfile /* = false */>
unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder,
VisitPostorder visitPostorder,
VisitEdge visitEdge,
jitstd::vector<BasicBlock*>& entryBlocks)
{
BitVecTraits traits(fgBBNumMax + 1, this);
BitVec visited(BitVecOps::MakeEmpty(&traits));

unsigned preOrderIndex = 0;
unsigned postOrderIndex = 0;

ArrayStack<AllSuccessorEnumerator> blocks(getAllocator(CMK_DepthFirstSearch));
ArrayStack<SuccessorEnumerator> blocks(getAllocator(CMK_DepthFirstSearch));

auto dfsFrom = [&](BasicBlock* firstBB) {
BitVecOps::AddElemD(&traits, visited, firstBB->bbNum);
Expand DownExpand Up@@ -5158,23 +5167,14 @@ unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPos
}
};

dfsFrom(fgFirstBB);
assert(!entryBlocks.empty());

if ((fgEntryBB != nullptr) && !BitVecOps::IsMember(&traits, visited, fgEntryBB->bbNum))
for (BasicBlock* const entryBlock : entryBlocks)
{
// OSR methods will early on create flow that looks like it goes to the
// patchpoint, but during morph we may transform to something that
// requires the original entry (fgEntryBB).
assert(opts.IsOSR());
dfsFrom(fgEntryBB);
}

if ((genReturnBB != nullptr) && !BitVecOps::IsMember(&traits, visited, genReturnBB->bbNum))
{
assert(!fgGlobalMorphDone);
// We introduce the merged return BB before morph and will redirect
// other returns to it as part of morph; keep it reachable.
dfsFrom(genReturnBB);
if (!BitVecOps::IsMember(&traits, visited, entryBlock->bbNum))
{
dfsFrom(entryBlock);
}
}

assert(preOrderIndex == postOrderIndex);
Expand Down
30 changes: 24 additions & 6 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -4739,18 +4739,36 @@ void Compiler::fgDebugCheckFlowGraphAnnotations()

auto visitEdge = [](BasicBlock* block, BasicBlock* succ) {};

jitstd::vector<BasicBlock*> entryBlocks(getAllocator(CMK_DepthFirstSearch));

entryBlocks.push_back(fgFirstBB);

if (fgEntryBB != nullptr)
{
// OSR methods will early on create flow that looks like it goes to the
// patchpoint, but during morph we may transform to something that
// requires the original entry (fgEntryBB).
assert(opts.IsOSR());
entryBlocks.push_back(fgEntryBB);
}

if ((genReturnBB != nullptr) && !fgGlobalMorphDone)
{
// We introduce the merged return BB before morph and will redirect
// other returns to it as part of morph; keep it reachable.
entryBlocks.push_back(genReturnBB);
}

unsigned count;
if (m_dfsTree->IsProfileAware())
{
count = fgRunDfs<decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge), true>(visitPreorder,
visitPostorder,
visitEdge);
count = fgRunDfs<AllSuccessorEnumerator, decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge),
true>(visitPreorder, visitPostorder, visitEdge, entryBlocks);
}
else
{
count = fgRunDfs<decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge), false>(visitPreorder,
visitPostorder,
visitEdge);
count = fgRunDfs<AllSuccessorEnumerator, decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge),
false>(visitPreorder, visitPostorder, visitEdge, entryBlocks);
}

assert(m_dfsTree->GetPostOrderCount() == count);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [Wasm RyuJIT] Implement Wasm DFS and Loop Finding by AndyAyersMS · Pull Request #121457 · dotnet/runtime · GitHub
Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/coreclr/jit/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -476,6 +476,7 @@ set( JIT_WASM_HEADERS
emitfmtswasm.h
instrswasm.h
registerwasm.h
fgwasm.h
)

convert_to_absolute_path(JIT_SOURCES ${JIT_SOURCES})
Expand Down
20 changes: 16 additions & 4 deletions src/coreclr/jit/compiler.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -1798,13 +1798,17 @@ class FlowGraphDfsTree
// Whether the DFS that produced the tree used edge likelihoods to influence successor visitation order.
bool m_profileAware;

// Whether the DFS reflects Wasm control flow rules.
bool m_forWasm;
Comment thread
AndyAyersMS marked this conversation as resolved.

public:
FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware)
FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware, bool forWasm = false)
: m_comp(comp)
, m_postOrder(postOrder)
, m_postOrderCount(postOrderCount)
, m_hasCycle(hasCycle)
, m_profileAware(profileAware)
, m_forWasm(forWasm)
{
}

Expand DownExpand Up@@ -1844,6 +1848,11 @@ class FlowGraphDfsTree
return m_profileAware;
}

bool IsForWasm() const
{
return m_forWasm;
}

#ifdef DEBUG
void Dump() const;
#endif // DEBUG
Expand DownExpand Up@@ -2137,7 +2146,7 @@ class FlowGraphNaturalLoops

FlowGraphNaturalLoops(const FlowGraphDfsTree* dfs);

static bool FindNaturalLoopBlocks(FlowGraphNaturalLoop* loop, ArrayStack<BasicBlock*>& worklist);
bool FindNaturalLoopBlocks(FlowGraphNaturalLoop* loop, ArrayStack<BasicBlock*>& worklist);
static bool IsLoopCanonicalizable(FlowGraphNaturalLoop* loop);

public:
Expand All@@ -2157,6 +2166,8 @@ class FlowGraphNaturalLoops
bool IsLoopBackEdge(FlowEdge* edge);
bool IsLoopExitEdge(FlowEdge* edge);

bool IsForWasm() { return m_dfsTree->IsForWasm(); }

class LoopsPostOrderIter
{
jitstd::vector<FlowGraphNaturalLoop*>* m_loops;
Expand DownExpand Up@@ -6237,6 +6248,7 @@ class Compiler

PhaseStatus fgFindOperOrder();

FlowGraphDfsTree* fgWasmDfs();
PhaseStatus fgWasmControlFlow();

// method that returns if you should split here
Expand All@@ -6245,8 +6257,8 @@ class Compiler
PhaseStatus fgSetBlockOrder();
bool fgHasCycleWithoutGCSafePoint();

template <typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile = false>
unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge);
template <typename SuccessorEnumerator, typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile = false>
unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge, jitstd::vector<BasicBlock*>& entries);

template <const bool useProfile = false>
FlowGraphDfsTree* fgComputeDfs();
Expand Down
44 changes: 22 additions & 22 deletions src/coreclr/jit/compiler.hpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -5105,30 +5105,39 @@ inline bool Compiler::compCanHavePatchpoints(const char** reason)
// fgRunDfs: Run DFS over the flow graph.
//
// Type parameters:
// VisitPreorder - Functor type that takes a BasicBlock* and its preorder number
// VisitPostorder - Functor type that takes a BasicBlock* and its postorder number
// VisitEdge - Functor type that takes two BasicBlock*.
// useProfile - If true, determines order of successors visited using profile data
// SuccessorEnumerator - Functor type to enumerate successors of a block
// VisitPreorder - Functor type that takes a BasicBlock* and its preorder number
// VisitPostorder - Functor type that takes a BasicBlock* and its postorder number
// VisitEdge - Functor type that takes two BasicBlock*.
// useProfile - If true, determines order of successors visited using profile data
//
// Parameters:
// visitPreorder - Functor to visit block in its preorder
// visitPostorder - Functor to visit block in its postorder
// visitEdge - Functor to visit an edge. Called after visitPreorder (if
// this is the first time the successor is seen).
// entryBlocks - vector of entry blocks for the flow graph
//
// Returns:
// Number of blocks visited.
//
template <typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile /* = false */>
unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPostorder, VisitEdge visitEdge)
template <typename SuccessorEnumerator,
typename VisitPreorder,
typename VisitPostorder,
typename VisitEdge,
const bool useProfile /* = false */>
unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder,
VisitPostorder visitPostorder,
VisitEdge visitEdge,
jitstd::vector<BasicBlock*>& entryBlocks)
{
BitVecTraits traits(fgBBNumMax + 1, this);
BitVec visited(BitVecOps::MakeEmpty(&traits));

unsigned preOrderIndex = 0;
unsigned postOrderIndex = 0;

ArrayStack<AllSuccessorEnumerator> blocks(getAllocator(CMK_DepthFirstSearch));
ArrayStack<SuccessorEnumerator> blocks(getAllocator(CMK_DepthFirstSearch));

auto dfsFrom = [&](BasicBlock* firstBB) {
BitVecOps::AddElemD(&traits, visited, firstBB->bbNum);
Expand DownExpand Up@@ -5158,23 +5167,14 @@ unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPos
}
};

dfsFrom(fgFirstBB);
assert(!entryBlocks.empty());

if ((fgEntryBB != nullptr) && !BitVecOps::IsMember(&traits, visited, fgEntryBB->bbNum))
for (BasicBlock* const entryBlock : entryBlocks)
{
// OSR methods will early on create flow that looks like it goes to the
// patchpoint, but during morph we may transform to something that
// requires the original entry (fgEntryBB).
assert(opts.IsOSR());
dfsFrom(fgEntryBB);
}

if ((genReturnBB != nullptr) && !BitVecOps::IsMember(&traits, visited, genReturnBB->bbNum))
{
assert(!fgGlobalMorphDone);
// We introduce the merged return BB before morph and will redirect
// other returns to it as part of morph; keep it reachable.
dfsFrom(genReturnBB);
if (!BitVecOps::IsMember(&traits, visited, entryBlock->bbNum))
{
dfsFrom(entryBlock);
}
}

assert(preOrderIndex == postOrderIndex);
Expand Down
30 changes: 24 additions & 6 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -4739,18 +4739,36 @@ void Compiler::fgDebugCheckFlowGraphAnnotations()

auto visitEdge = [](BasicBlock* block, BasicBlock* succ) {};

jitstd::vector<BasicBlock*> entryBlocks(getAllocator(CMK_DepthFirstSearch));

entryBlocks.push_back(fgFirstBB);

if (fgEntryBB != nullptr)
{
// OSR methods will early on create flow that looks like it goes to the
// patchpoint, but during morph we may transform to something that
// requires the original entry (fgEntryBB).
assert(opts.IsOSR());
entryBlocks.push_back(fgEntryBB);
}

if ((genReturnBB != nullptr) && !fgGlobalMorphDone)
{
// We introduce the merged return BB before morph and will redirect
// other returns to it as part of morph; keep it reachable.
entryBlocks.push_back(genReturnBB);
}

unsigned count;
if (m_dfsTree->IsProfileAware())
{
count = fgRunDfs<decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge), true>(visitPreorder,
visitPostorder,
visitEdge);
count = fgRunDfs<AllSuccessorEnumerator, decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge),
true>(visitPreorder, visitPostorder, visitEdge, entryBlocks);
}
else
{
count = fgRunDfs<decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge), false>(visitPreorder,
visitPostorder,
visitEdge);
count = fgRunDfs<AllSuccessorEnumerator, decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge),
false>(visitPreorder, visitPostorder, visitEdge, entryBlocks);
}

assert(m_dfsTree->GetPostOrderCount() == count);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' [Wasm RyuJIT] Implement Wasm DFS and Loop Finding by AndyAyersMS · Pull Request #121457 · dotnet/runtime · GitHub
Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/coreclr/jit/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -476,6 +476,7 @@ set( JIT_WASM_HEADERS
emitfmtswasm.h
instrswasm.h
registerwasm.h
fgwasm.h
)

convert_to_absolute_path(JIT_SOURCES ${JIT_SOURCES})
Expand Down
20 changes: 16 additions & 4 deletions src/coreclr/jit/compiler.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -1798,13 +1798,17 @@ class FlowGraphDfsTree
// Whether the DFS that produced the tree used edge likelihoods to influence successor visitation order.
bool m_profileAware;

// Whether the DFS reflects Wasm control flow rules.
bool m_forWasm;
Comment thread
AndyAyersMS marked this conversation as resolved.

public:
FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware)
FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware, bool forWasm = false)
: m_comp(comp)
, m_postOrder(postOrder)
, m_postOrderCount(postOrderCount)
, m_hasCycle(hasCycle)
, m_profileAware(profileAware)
, m_forWasm(forWasm)
{
}

Expand DownExpand Up@@ -1844,6 +1848,11 @@ class FlowGraphDfsTree
return m_profileAware;
}

bool IsForWasm() const
{
return m_forWasm;
}

#ifdef DEBUG
void Dump() const;
#endif // DEBUG
Expand DownExpand Up@@ -2137,7 +2146,7 @@ class FlowGraphNaturalLoops

FlowGraphNaturalLoops(const FlowGraphDfsTree* dfs);

static bool FindNaturalLoopBlocks(FlowGraphNaturalLoop* loop, ArrayStack<BasicBlock*>& worklist);
bool FindNaturalLoopBlocks(FlowGraphNaturalLoop* loop, ArrayStack<BasicBlock*>& worklist);
static bool IsLoopCanonicalizable(FlowGraphNaturalLoop* loop);

public:
Expand All@@ -2157,6 +2166,8 @@ class FlowGraphNaturalLoops
bool IsLoopBackEdge(FlowEdge* edge);
bool IsLoopExitEdge(FlowEdge* edge);

bool IsForWasm() { return m_dfsTree->IsForWasm(); }

class LoopsPostOrderIter
{
jitstd::vector<FlowGraphNaturalLoop*>* m_loops;
Expand DownExpand Up@@ -6237,6 +6248,7 @@ class Compiler

PhaseStatus fgFindOperOrder();

FlowGraphDfsTree* fgWasmDfs();
PhaseStatus fgWasmControlFlow();

// method that returns if you should split here
Expand All@@ -6245,8 +6257,8 @@ class Compiler
PhaseStatus fgSetBlockOrder();
bool fgHasCycleWithoutGCSafePoint();

template <typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile = false>
unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge);
template <typename SuccessorEnumerator, typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile = false>
unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge, jitstd::vector<BasicBlock*>& entries);

template <const bool useProfile = false>
FlowGraphDfsTree* fgComputeDfs();
Expand Down
44 changes: 22 additions & 22 deletions src/coreclr/jit/compiler.hpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -5105,30 +5105,39 @@ inline bool Compiler::compCanHavePatchpoints(const char** reason)
// fgRunDfs: Run DFS over the flow graph.
//
// Type parameters:
// VisitPreorder - Functor type that takes a BasicBlock* and its preorder number
// VisitPostorder - Functor type that takes a BasicBlock* and its postorder number
// VisitEdge - Functor type that takes two BasicBlock*.
// useProfile - If true, determines order of successors visited using profile data
// SuccessorEnumerator - Functor type to enumerate successors of a block
// VisitPreorder - Functor type that takes a BasicBlock* and its preorder number
// VisitPostorder - Functor type that takes a BasicBlock* and its postorder number
// VisitEdge - Functor type that takes two BasicBlock*.
// useProfile - If true, determines order of successors visited using profile data
//
// Parameters:
// visitPreorder - Functor to visit block in its preorder
// visitPostorder - Functor to visit block in its postorder
// visitEdge - Functor to visit an edge. Called after visitPreorder (if
// this is the first time the successor is seen).
// entryBlocks - vector of entry blocks for the flow graph
//
// Returns:
// Number of blocks visited.
//
template <typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile /* = false */>
unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPostorder, VisitEdge visitEdge)
template <typename SuccessorEnumerator,
typename VisitPreorder,
typename VisitPostorder,
typename VisitEdge,
const bool useProfile /* = false */>
unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder,
VisitPostorder visitPostorder,
VisitEdge visitEdge,
jitstd::vector<BasicBlock*>& entryBlocks)
{
BitVecTraits traits(fgBBNumMax + 1, this);
BitVec visited(BitVecOps::MakeEmpty(&traits));

unsigned preOrderIndex = 0;
unsigned postOrderIndex = 0;

ArrayStack<AllSuccessorEnumerator> blocks(getAllocator(CMK_DepthFirstSearch));
ArrayStack<SuccessorEnumerator> blocks(getAllocator(CMK_DepthFirstSearch));

auto dfsFrom = [&](BasicBlock* firstBB) {
BitVecOps::AddElemD(&traits, visited, firstBB->bbNum);
Expand DownExpand Up@@ -5158,23 +5167,14 @@ unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPos
}
};

dfsFrom(fgFirstBB);
assert(!entryBlocks.empty());

if ((fgEntryBB != nullptr) && !BitVecOps::IsMember(&traits, visited, fgEntryBB->bbNum))
for (BasicBlock* const entryBlock : entryBlocks)
{
// OSR methods will early on create flow that looks like it goes to the
// patchpoint, but during morph we may transform to something that
// requires the original entry (fgEntryBB).
assert(opts.IsOSR());
dfsFrom(fgEntryBB);
}

if ((genReturnBB != nullptr) && !BitVecOps::IsMember(&traits, visited, genReturnBB->bbNum))
{
assert(!fgGlobalMorphDone);
// We introduce the merged return BB before morph and will redirect
// other returns to it as part of morph; keep it reachable.
dfsFrom(genReturnBB);
if (!BitVecOps::IsMember(&traits, visited, entryBlock->bbNum))
{
dfsFrom(entryBlock);
}
}

assert(preOrderIndex == postOrderIndex);
Expand Down
30 changes: 24 additions & 6 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -4739,18 +4739,36 @@ void Compiler::fgDebugCheckFlowGraphAnnotations()

auto visitEdge = [](BasicBlock* block, BasicBlock* succ) {};

jitstd::vector<BasicBlock*> entryBlocks(getAllocator(CMK_DepthFirstSearch));

entryBlocks.push_back(fgFirstBB);

if (fgEntryBB != nullptr)
{
// OSR methods will early on create flow that looks like it goes to the
// patchpoint, but during morph we may transform to something that
// requires the original entry (fgEntryBB).
assert(opts.IsOSR());
entryBlocks.push_back(fgEntryBB);
}

if ((genReturnBB != nullptr) && !fgGlobalMorphDone)
{
// We introduce the merged return BB before morph and will redirect
// other returns to it as part of morph; keep it reachable.
entryBlocks.push_back(genReturnBB);
}

unsigned count;
if (m_dfsTree->IsProfileAware())
{
count = fgRunDfs<decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge), true>(visitPreorder,
visitPostorder,
visitEdge);
count = fgRunDfs<AllSuccessorEnumerator, decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge),
true>(visitPreorder, visitPostorder, visitEdge, entryBlocks);
}
else
{
count = fgRunDfs<decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge), false>(visitPreorder,
visitPostorder,
visitEdge);
count = fgRunDfs<AllSuccessorEnumerator, decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge),
false>(visitPreorder, visitPostorder, visitEdge, entryBlocks);
}

assert(m_dfsTree->GetPostOrderCount() == count);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [Wasm RyuJIT] Implement Wasm DFS and Loop Finding by AndyAyersMS · Pull Request #121457 · dotnet/runtime · GitHub
Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/coreclr/jit/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -476,6 +476,7 @@ set( JIT_WASM_HEADERS
emitfmtswasm.h
instrswasm.h
registerwasm.h
fgwasm.h
)

convert_to_absolute_path(JIT_SOURCES ${JIT_SOURCES})
Expand Down
20 changes: 16 additions & 4 deletions src/coreclr/jit/compiler.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -1798,13 +1798,17 @@ class FlowGraphDfsTree
// Whether the DFS that produced the tree used edge likelihoods to influence successor visitation order.
bool m_profileAware;

// Whether the DFS reflects Wasm control flow rules.
bool m_forWasm;
Comment thread
AndyAyersMS marked this conversation as resolved.

public:
FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware)
FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware, bool forWasm = false)
: m_comp(comp)
, m_postOrder(postOrder)
, m_postOrderCount(postOrderCount)
, m_hasCycle(hasCycle)
, m_profileAware(profileAware)
, m_forWasm(forWasm)
{
}

Expand DownExpand Up@@ -1844,6 +1848,11 @@ class FlowGraphDfsTree
return m_profileAware;
}

bool IsForWasm() const
{
return m_forWasm;
}

#ifdef DEBUG
void Dump() const;
#endif // DEBUG
Expand DownExpand Up@@ -2137,7 +2146,7 @@ class FlowGraphNaturalLoops

FlowGraphNaturalLoops(const FlowGraphDfsTree* dfs);

static bool FindNaturalLoopBlocks(FlowGraphNaturalLoop* loop, ArrayStack<BasicBlock*>& worklist);
bool FindNaturalLoopBlocks(FlowGraphNaturalLoop* loop, ArrayStack<BasicBlock*>& worklist);
static bool IsLoopCanonicalizable(FlowGraphNaturalLoop* loop);

public:
Expand All@@ -2157,6 +2166,8 @@ class FlowGraphNaturalLoops
bool IsLoopBackEdge(FlowEdge* edge);
bool IsLoopExitEdge(FlowEdge* edge);

bool IsForWasm() { return m_dfsTree->IsForWasm(); }

class LoopsPostOrderIter
{
jitstd::vector<FlowGraphNaturalLoop*>* m_loops;
Expand DownExpand Up@@ -6237,6 +6248,7 @@ class Compiler

PhaseStatus fgFindOperOrder();

FlowGraphDfsTree* fgWasmDfs();
PhaseStatus fgWasmControlFlow();

// method that returns if you should split here
Expand All@@ -6245,8 +6257,8 @@ class Compiler
PhaseStatus fgSetBlockOrder();
bool fgHasCycleWithoutGCSafePoint();

template <typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile = false>
unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge);
template <typename SuccessorEnumerator, typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile = false>
unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge, jitstd::vector<BasicBlock*>& entries);

template <const bool useProfile = false>
FlowGraphDfsTree* fgComputeDfs();
Expand Down
44 changes: 22 additions & 22 deletions src/coreclr/jit/compiler.hpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -5105,30 +5105,39 @@ inline bool Compiler::compCanHavePatchpoints(const char** reason)
// fgRunDfs: Run DFS over the flow graph.
//
// Type parameters:
// VisitPreorder - Functor type that takes a BasicBlock* and its preorder number
// VisitPostorder - Functor type that takes a BasicBlock* and its postorder number
// VisitEdge - Functor type that takes two BasicBlock*.
// useProfile - If true, determines order of successors visited using profile data
// SuccessorEnumerator - Functor type to enumerate successors of a block
// VisitPreorder - Functor type that takes a BasicBlock* and its preorder number
// VisitPostorder - Functor type that takes a BasicBlock* and its postorder number
// VisitEdge - Functor type that takes two BasicBlock*.
// useProfile - If true, determines order of successors visited using profile data
//
// Parameters:
// visitPreorder - Functor to visit block in its preorder
// visitPostorder - Functor to visit block in its postorder
// visitEdge - Functor to visit an edge. Called after visitPreorder (if
// this is the first time the successor is seen).
// entryBlocks - vector of entry blocks for the flow graph
//
// Returns:
// Number of blocks visited.
//
template <typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile /* = false */>
unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPostorder, VisitEdge visitEdge)
template <typename SuccessorEnumerator,
typename VisitPreorder,
typename VisitPostorder,
typename VisitEdge,
const bool useProfile /* = false */>
unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder,
VisitPostorder visitPostorder,
VisitEdge visitEdge,
jitstd::vector<BasicBlock*>& entryBlocks)
{
BitVecTraits traits(fgBBNumMax + 1, this);
BitVec visited(BitVecOps::MakeEmpty(&traits));

unsigned preOrderIndex = 0;
unsigned postOrderIndex = 0;

ArrayStack<AllSuccessorEnumerator> blocks(getAllocator(CMK_DepthFirstSearch));
ArrayStack<SuccessorEnumerator> blocks(getAllocator(CMK_DepthFirstSearch));

auto dfsFrom = [&](BasicBlock* firstBB) {
BitVecOps::AddElemD(&traits, visited, firstBB->bbNum);
Expand DownExpand Up@@ -5158,23 +5167,14 @@ unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPos
}
};

dfsFrom(fgFirstBB);
assert(!entryBlocks.empty());

if ((fgEntryBB != nullptr) && !BitVecOps::IsMember(&traits, visited, fgEntryBB->bbNum))
for (BasicBlock* const entryBlock : entryBlocks)
{
// OSR methods will early on create flow that looks like it goes to the
// patchpoint, but during morph we may transform to something that
// requires the original entry (fgEntryBB).
assert(opts.IsOSR());
dfsFrom(fgEntryBB);
}

if ((genReturnBB != nullptr) && !BitVecOps::IsMember(&traits, visited, genReturnBB->bbNum))
{
assert(!fgGlobalMorphDone);
// We introduce the merged return BB before morph and will redirect
// other returns to it as part of morph; keep it reachable.
dfsFrom(genReturnBB);
if (!BitVecOps::IsMember(&traits, visited, entryBlock->bbNum))
{
dfsFrom(entryBlock);
}
}

assert(preOrderIndex == postOrderIndex);
Expand Down
30 changes: 24 additions & 6 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -4739,18 +4739,36 @@ void Compiler::fgDebugCheckFlowGraphAnnotations()

auto visitEdge = [](BasicBlock* block, BasicBlock* succ) {};

jitstd::vector<BasicBlock*> entryBlocks(getAllocator(CMK_DepthFirstSearch));

entryBlocks.push_back(fgFirstBB);

if (fgEntryBB != nullptr)
{
// OSR methods will early on create flow that looks like it goes to the
// patchpoint, but during morph we may transform to something that
// requires the original entry (fgEntryBB).
assert(opts.IsOSR());
entryBlocks.push_back(fgEntryBB);
}

if ((genReturnBB != nullptr) && !fgGlobalMorphDone)
{
// We introduce the merged return BB before morph and will redirect
// other returns to it as part of morph; keep it reachable.
entryBlocks.push_back(genReturnBB);
}

unsigned count;
if (m_dfsTree->IsProfileAware())
{
count = fgRunDfs<decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge), true>(visitPreorder,
visitPostorder,
visitEdge);
count = fgRunDfs<AllSuccessorEnumerator, decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge),
true>(visitPreorder, visitPostorder, visitEdge, entryBlocks);
}
else
{
count = fgRunDfs<decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge), false>(visitPreorder,
visitPostorder,
visitEdge);
count = fgRunDfs<AllSuccessorEnumerator, decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge),
false>(visitPreorder, visitPostorder, visitEdge, entryBlocks);
}

assert(m_dfsTree->GetPostOrderCount() == count);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [Wasm RyuJIT] Implement Wasm DFS and Loop Finding by AndyAyersMS · Pull Request #121457 · dotnet/runtime · GitHub
Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/coreclr/jit/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -476,6 +476,7 @@ set( JIT_WASM_HEADERS
emitfmtswasm.h
instrswasm.h
registerwasm.h
fgwasm.h
)

convert_to_absolute_path(JIT_SOURCES ${JIT_SOURCES})
Expand Down
20 changes: 16 additions & 4 deletions src/coreclr/jit/compiler.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -1798,13 +1798,17 @@ class FlowGraphDfsTree
// Whether the DFS that produced the tree used edge likelihoods to influence successor visitation order.
bool m_profileAware;

// Whether the DFS reflects Wasm control flow rules.
bool m_forWasm;
Comment thread
AndyAyersMS marked this conversation as resolved.

public:
FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware)
FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware, bool forWasm = false)
: m_comp(comp)
, m_postOrder(postOrder)
, m_postOrderCount(postOrderCount)
, m_hasCycle(hasCycle)
, m_profileAware(profileAware)
, m_forWasm(forWasm)
{
}

Expand DownExpand Up@@ -1844,6 +1848,11 @@ class FlowGraphDfsTree
return m_profileAware;
}

bool IsForWasm() const
{
return m_forWasm;
}

#ifdef DEBUG
void Dump() const;
#endif // DEBUG
Expand DownExpand Up@@ -2137,7 +2146,7 @@ class FlowGraphNaturalLoops

FlowGraphNaturalLoops(const FlowGraphDfsTree* dfs);

static bool FindNaturalLoopBlocks(FlowGraphNaturalLoop* loop, ArrayStack<BasicBlock*>& worklist);
bool FindNaturalLoopBlocks(FlowGraphNaturalLoop* loop, ArrayStack<BasicBlock*>& worklist);
static bool IsLoopCanonicalizable(FlowGraphNaturalLoop* loop);

public:
Expand All@@ -2157,6 +2166,8 @@ class FlowGraphNaturalLoops
bool IsLoopBackEdge(FlowEdge* edge);
bool IsLoopExitEdge(FlowEdge* edge);

bool IsForWasm() { return m_dfsTree->IsForWasm(); }

class LoopsPostOrderIter
{
jitstd::vector<FlowGraphNaturalLoop*>* m_loops;
Expand DownExpand Up@@ -6237,6 +6248,7 @@ class Compiler

PhaseStatus fgFindOperOrder();

FlowGraphDfsTree* fgWasmDfs();
PhaseStatus fgWasmControlFlow();

// method that returns if you should split here
Expand All@@ -6245,8 +6257,8 @@ class Compiler
PhaseStatus fgSetBlockOrder();
bool fgHasCycleWithoutGCSafePoint();

template <typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile = false>
unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge);
template <typename SuccessorEnumerator, typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile = false>
unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge, jitstd::vector<BasicBlock*>& entries);

template <const bool useProfile = false>
FlowGraphDfsTree* fgComputeDfs();
Expand Down
44 changes: 22 additions & 22 deletions src/coreclr/jit/compiler.hpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -5105,30 +5105,39 @@ inline bool Compiler::compCanHavePatchpoints(const char** reason)
// fgRunDfs: Run DFS over the flow graph.
//
// Type parameters:
// VisitPreorder - Functor type that takes a BasicBlock* and its preorder number
// VisitPostorder - Functor type that takes a BasicBlock* and its postorder number
// VisitEdge - Functor type that takes two BasicBlock*.
// useProfile - If true, determines order of successors visited using profile data
// SuccessorEnumerator - Functor type to enumerate successors of a block
// VisitPreorder - Functor type that takes a BasicBlock* and its preorder number
// VisitPostorder - Functor type that takes a BasicBlock* and its postorder number
// VisitEdge - Functor type that takes two BasicBlock*.
// useProfile - If true, determines order of successors visited using profile data
//
// Parameters:
// visitPreorder - Functor to visit block in its preorder
// visitPostorder - Functor to visit block in its postorder
// visitEdge - Functor to visit an edge. Called after visitPreorder (if
// this is the first time the successor is seen).
// entryBlocks - vector of entry blocks for the flow graph
//
// Returns:
// Number of blocks visited.
//
template <typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile /* = false */>
unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPostorder, VisitEdge visitEdge)
template <typename SuccessorEnumerator,
typename VisitPreorder,
typename VisitPostorder,
typename VisitEdge,
const bool useProfile /* = false */>
unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder,
VisitPostorder visitPostorder,
VisitEdge visitEdge,
jitstd::vector<BasicBlock*>& entryBlocks)
{
BitVecTraits traits(fgBBNumMax + 1, this);
BitVec visited(BitVecOps::MakeEmpty(&traits));

unsigned preOrderIndex = 0;
unsigned postOrderIndex = 0;

ArrayStack<AllSuccessorEnumerator> blocks(getAllocator(CMK_DepthFirstSearch));
ArrayStack<SuccessorEnumerator> blocks(getAllocator(CMK_DepthFirstSearch));

auto dfsFrom = [&](BasicBlock* firstBB) {
BitVecOps::AddElemD(&traits, visited, firstBB->bbNum);
Expand DownExpand Up@@ -5158,23 +5167,14 @@ unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPos
}
};

dfsFrom(fgFirstBB);
assert(!entryBlocks.empty());

if ((fgEntryBB != nullptr) && !BitVecOps::IsMember(&traits, visited, fgEntryBB->bbNum))
for (BasicBlock* const entryBlock : entryBlocks)
{
// OSR methods will early on create flow that looks like it goes to the
// patchpoint, but during morph we may transform to something that
// requires the original entry (fgEntryBB).
assert(opts.IsOSR());
dfsFrom(fgEntryBB);
}

if ((genReturnBB != nullptr) && !BitVecOps::IsMember(&traits, visited, genReturnBB->bbNum))
{
assert(!fgGlobalMorphDone);
// We introduce the merged return BB before morph and will redirect
// other returns to it as part of morph; keep it reachable.
dfsFrom(genReturnBB);
if (!BitVecOps::IsMember(&traits, visited, entryBlock->bbNum))
{
dfsFrom(entryBlock);
}
}

assert(preOrderIndex == postOrderIndex);
Expand Down
30 changes: 24 additions & 6 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -4739,18 +4739,36 @@ void Compiler::fgDebugCheckFlowGraphAnnotations()

auto visitEdge = [](BasicBlock* block, BasicBlock* succ) {};

jitstd::vector<BasicBlock*> entryBlocks(getAllocator(CMK_DepthFirstSearch));

entryBlocks.push_back(fgFirstBB);

if (fgEntryBB != nullptr)
{
// OSR methods will early on create flow that looks like it goes to the
// patchpoint, but during morph we may transform to something that
// requires the original entry (fgEntryBB).
assert(opts.IsOSR());
entryBlocks.push_back(fgEntryBB);
}

if ((genReturnBB != nullptr) && !fgGlobalMorphDone)
{
// We introduce the merged return BB before morph and will redirect
// other returns to it as part of morph; keep it reachable.
entryBlocks.push_back(genReturnBB);
}

unsigned count;
if (m_dfsTree->IsProfileAware())
{
count = fgRunDfs<decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge), true>(visitPreorder,
visitPostorder,
visitEdge);
count = fgRunDfs<AllSuccessorEnumerator, decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge),
true>(visitPreorder, visitPostorder, visitEdge, entryBlocks);
}
else
{
count = fgRunDfs<decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge), false>(visitPreorder,
visitPostorder,
visitEdge);
count = fgRunDfs<AllSuccessorEnumerator, decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge),
false>(visitPreorder, visitPostorder, visitEdge, entryBlocks);
}

assert(m_dfsTree->GetPostOrderCount() == count);
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); [Wasm RyuJIT] Implement Wasm DFS and Loop Finding by AndyAyersMS · Pull Request #121457 · dotnet/runtime · GitHub
Skip to content
Merged
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
1 change: 1 addition & 0 deletions src/coreclr/jit/CMakeLists.txt
Original file line numberDiff line numberDiff line change
Expand Up@@ -476,6 +476,7 @@ set( JIT_WASM_HEADERS
emitfmtswasm.h
instrswasm.h
registerwasm.h
fgwasm.h
)

convert_to_absolute_path(JIT_SOURCES ${JIT_SOURCES})
Expand Down
20 changes: 16 additions & 4 deletions src/coreclr/jit/compiler.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -1798,13 +1798,17 @@ class FlowGraphDfsTree
// Whether the DFS that produced the tree used edge likelihoods to influence successor visitation order.
bool m_profileAware;

// Whether the DFS reflects Wasm control flow rules.
bool m_forWasm;
Comment thread
AndyAyersMS marked this conversation as resolved.

public:
FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware)
FlowGraphDfsTree(Compiler* comp, BasicBlock** postOrder, unsigned postOrderCount, bool hasCycle, bool profileAware, bool forWasm = false)
: m_comp(comp)
, m_postOrder(postOrder)
, m_postOrderCount(postOrderCount)
, m_hasCycle(hasCycle)
, m_profileAware(profileAware)
, m_forWasm(forWasm)
{
}

Expand DownExpand Up@@ -1844,6 +1848,11 @@ class FlowGraphDfsTree
return m_profileAware;
}

bool IsForWasm() const
{
return m_forWasm;
}

#ifdef DEBUG
void Dump() const;
#endif // DEBUG
Expand DownExpand Up@@ -2137,7 +2146,7 @@ class FlowGraphNaturalLoops

FlowGraphNaturalLoops(const FlowGraphDfsTree* dfs);

static bool FindNaturalLoopBlocks(FlowGraphNaturalLoop* loop, ArrayStack<BasicBlock*>& worklist);
bool FindNaturalLoopBlocks(FlowGraphNaturalLoop* loop, ArrayStack<BasicBlock*>& worklist);
static bool IsLoopCanonicalizable(FlowGraphNaturalLoop* loop);

public:
Expand All@@ -2157,6 +2166,8 @@ class FlowGraphNaturalLoops
bool IsLoopBackEdge(FlowEdge* edge);
bool IsLoopExitEdge(FlowEdge* edge);

bool IsForWasm() { return m_dfsTree->IsForWasm(); }

class LoopsPostOrderIter
{
jitstd::vector<FlowGraphNaturalLoop*>* m_loops;
Expand DownExpand Up@@ -6237,6 +6248,7 @@ class Compiler

PhaseStatus fgFindOperOrder();

FlowGraphDfsTree* fgWasmDfs();
PhaseStatus fgWasmControlFlow();

// method that returns if you should split here
Expand All@@ -6245,8 +6257,8 @@ class Compiler
PhaseStatus fgSetBlockOrder();
bool fgHasCycleWithoutGCSafePoint();

template <typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile = false>
unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge);
template <typename SuccessorEnumerator, typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile = false>
unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge, jitstd::vector<BasicBlock*>& entries);

template <const bool useProfile = false>
FlowGraphDfsTree* fgComputeDfs();
Expand Down
44 changes: 22 additions & 22 deletions src/coreclr/jit/compiler.hpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -5105,30 +5105,39 @@ inline bool Compiler::compCanHavePatchpoints(const char** reason)
// fgRunDfs: Run DFS over the flow graph.
//
// Type parameters:
// VisitPreorder - Functor type that takes a BasicBlock* and its preorder number
// VisitPostorder - Functor type that takes a BasicBlock* and its postorder number
// VisitEdge - Functor type that takes two BasicBlock*.
// useProfile - If true, determines order of successors visited using profile data
// SuccessorEnumerator - Functor type to enumerate successors of a block
// VisitPreorder - Functor type that takes a BasicBlock* and its preorder number
// VisitPostorder - Functor type that takes a BasicBlock* and its postorder number
// VisitEdge - Functor type that takes two BasicBlock*.
// useProfile - If true, determines order of successors visited using profile data
//
// Parameters:
// visitPreorder - Functor to visit block in its preorder
// visitPostorder - Functor to visit block in its postorder
// visitEdge - Functor to visit an edge. Called after visitPreorder (if
// this is the first time the successor is seen).
// entryBlocks - vector of entry blocks for the flow graph
//
// Returns:
// Number of blocks visited.
//
template <typename VisitPreorder, typename VisitPostorder, typename VisitEdge, const bool useProfile /* = false */>
unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPostorder, VisitEdge visitEdge)
template <typename SuccessorEnumerator,
typename VisitPreorder,
typename VisitPostorder,
typename VisitEdge,
const bool useProfile /* = false */>
unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder,
VisitPostorder visitPostorder,
VisitEdge visitEdge,
jitstd::vector<BasicBlock*>& entryBlocks)
{
BitVecTraits traits(fgBBNumMax + 1, this);
BitVec visited(BitVecOps::MakeEmpty(&traits));

unsigned preOrderIndex = 0;
unsigned postOrderIndex = 0;

ArrayStack<AllSuccessorEnumerator> blocks(getAllocator(CMK_DepthFirstSearch));
ArrayStack<SuccessorEnumerator> blocks(getAllocator(CMK_DepthFirstSearch));

auto dfsFrom = [&](BasicBlock* firstBB) {
BitVecOps::AddElemD(&traits, visited, firstBB->bbNum);
Expand DownExpand Up@@ -5158,23 +5167,14 @@ unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPos
}
};

dfsFrom(fgFirstBB);
assert(!entryBlocks.empty());

if ((fgEntryBB != nullptr) && !BitVecOps::IsMember(&traits, visited, fgEntryBB->bbNum))
for (BasicBlock* const entryBlock : entryBlocks)
{
// OSR methods will early on create flow that looks like it goes to the
// patchpoint, but during morph we may transform to something that
// requires the original entry (fgEntryBB).
assert(opts.IsOSR());
dfsFrom(fgEntryBB);
}

if ((genReturnBB != nullptr) && !BitVecOps::IsMember(&traits, visited, genReturnBB->bbNum))
{
assert(!fgGlobalMorphDone);
// We introduce the merged return BB before morph and will redirect
// other returns to it as part of morph; keep it reachable.
dfsFrom(genReturnBB);
if (!BitVecOps::IsMember(&traits, visited, entryBlock->bbNum))
{
dfsFrom(entryBlock);
}
}

assert(preOrderIndex == postOrderIndex);
Expand Down
30 changes: 24 additions & 6 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -4739,18 +4739,36 @@ void Compiler::fgDebugCheckFlowGraphAnnotations()

auto visitEdge = [](BasicBlock* block, BasicBlock* succ) {};

jitstd::vector<BasicBlock*> entryBlocks(getAllocator(CMK_DepthFirstSearch));

entryBlocks.push_back(fgFirstBB);

if (fgEntryBB != nullptr)
{
// OSR methods will early on create flow that looks like it goes to the
// patchpoint, but during morph we may transform to something that
// requires the original entry (fgEntryBB).
assert(opts.IsOSR());
entryBlocks.push_back(fgEntryBB);
}

if ((genReturnBB != nullptr) && !fgGlobalMorphDone)
{
// We introduce the merged return BB before morph and will redirect
// other returns to it as part of morph; keep it reachable.
entryBlocks.push_back(genReturnBB);
}

unsigned count;
if (m_dfsTree->IsProfileAware())
{
count = fgRunDfs<decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge), true>(visitPreorder,
visitPostorder,
visitEdge);
count = fgRunDfs<AllSuccessorEnumerator, decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge),
true>(visitPreorder, visitPostorder, visitEdge, entryBlocks);
}
else
{
count = fgRunDfs<decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge), false>(visitPreorder,
visitPostorder,
visitEdge);
count = fgRunDfs<AllSuccessorEnumerator, decltype(visitPreorder), decltype(visitPostorder), decltype(visitEdge),
false>(visitPreorder, visitPostorder, visitEdge, entryBlocks);
}

assert(m_dfsTree->GetPostOrderCount() == count);
Expand Down
Loading
Loading