Commit 3f1de995 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by V8 LUCI CQ

[turbofan] Do not unroll loops with function calls

Spending time on unrolling loops that contain function calls will often
not be worth it, as these loops will run for potentially a long time,
mitigating the performance benefit of loop unrolling.

Bug: v8:11298, v8:12047
Change-Id: Idd59cea499e495e90c48f82133a809b22f899d74
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3081614
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76203}
parent fc6b999e
......@@ -543,8 +543,9 @@ LoopTree* LoopFinder::BuildLoopTree(Graph* graph, TickCounter* tick_counter,
return loop_tree;
}
#if V8_ENABLE_WEBASSEMBLY
// static
ZoneUnorderedSet<Node*>* LoopFinder::FindUnnestedLoopFromHeader(
ZoneUnorderedSet<Node*>* LoopFinder::FindSmallUnnestedLoopFromHeader(
Node* loop_header, Zone* zone, size_t max_size) {
auto* visited = zone->New<ZoneUnorderedSet<Node*>>(zone);
std::vector<Node*> queue;
......@@ -580,6 +581,12 @@ ZoneUnorderedSet<Node*>* LoopFinder::FindUnnestedLoopFromHeader(
loop_header);
// All uses are outside the loop, do nothing.
break;
case IrOpcode::kCall:
case IrOpcode::kTailCall:
case IrOpcode::kJSWasmCall:
case IrOpcode::kJSCall:
// Call nodes are considered to have unbounded size, i.e. >max_size.
return nullptr;
default:
for (Node* use : node->uses()) {
if (visited->count(use) == 0) queue.push_back(use);
......@@ -614,6 +621,7 @@ ZoneUnorderedSet<Node*>* LoopFinder::FindUnnestedLoopFromHeader(
return visited;
}
#endif // V8_ENABLE_WEBASSEMBLY
bool LoopFinder::HasMarkedExits(LoopTree* loop_tree,
const LoopTree::Loop* loop) {
......
......@@ -179,16 +179,19 @@ class V8_EXPORT_PRIVATE LoopFinder {
static bool HasMarkedExits(LoopTree* loop_tree_, const LoopTree::Loop* loop);
// Find all nodes of a loop given its header node. Will exit early once the
// current loop size exceed {max_size}. This is a very restricted version of
// BuildLoopTree.
// Assumptions:
#if V8_ENABLE_WEBASSEMBLY
// Find all nodes of a loop given headed by {loop_header}. Returns {nullptr}
// if the loop size in Nodes exceeds {max_size}. In that context, function
// calls are considered to have unbounded size, so if the loop contains a
// function call, {nullptr} is always returned.
// This is a very restricted version of BuildLoopTree and makes the following
// assumptions:
// 1) All loop exits of the loop are marked with LoopExit, LoopExitEffect,
// and LoopExitValue nodes.
// 2) There are no nested loops within this loop.
static ZoneUnorderedSet<Node*>* FindUnnestedLoopFromHeader(Node* loop_header,
Zone* zone,
size_t max_size);
static ZoneUnorderedSet<Node*>* FindSmallUnnestedLoopFromHeader(
Node* loop_header, Zone* zone, size_t max_size);
#endif
};
// Copies a range of nodes any number of times.
......
......@@ -1672,11 +1672,12 @@ struct WasmLoopUnrollingPhase {
std::vector<compiler::WasmLoopInfo>* loop_infos) {
for (WasmLoopInfo& loop_info : *loop_infos) {
if (loop_info.is_innermost) {
ZoneUnorderedSet<Node*>* loop = LoopFinder::FindUnnestedLoopFromHeader(
loop_info.header, temp_zone,
// Only discover the loop until its size is the maximum unrolled
// size for its depth.
maximum_unrollable_size(loop_info.nesting_depth));
ZoneUnorderedSet<Node*>* loop =
LoopFinder::FindSmallUnnestedLoopFromHeader(
loop_info.header, temp_zone,
// Only discover the loop until its size is the maximum unrolled
// size for its depth.
maximum_unrollable_size(loop_info.nesting_depth));
UnrollLoop(loop_info.header, loop, loop_info.nesting_depth,
data->graph(), data->common(), temp_zone,
data->source_positions(), data->node_origins());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment