[turbofan] Add Late Escape Analysis to JS pipeline
Add a new late escape analysis pass to JS late optimizations. The new pass simply removes allocations that are not used (besides initializing stores to the object). Bug: v8:12200 Change-Id: I01fc6233cca2f369c77ff2116ed7c4da1a232d95 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3677298 Commit-Queue: Patrick Thier <pthier@chromium.org> Reviewed-by: Tobias Tebbi <tebbi@chromium.org> Cr-Commit-Position: refs/heads/main@{#80862}
This commit is contained in:
parent
b77ff66b4c
commit
f22c7eb65c
@ -2738,6 +2738,8 @@ filegroup(
|
||||
"src/compiler/js-type-hint-lowering.h",
|
||||
"src/compiler/js-typed-lowering.cc",
|
||||
"src/compiler/js-typed-lowering.h",
|
||||
"src/compiler/late-escape-analysis.cc",
|
||||
"src/compiler/late-escape-analysis.h",
|
||||
"src/compiler/linkage.cc",
|
||||
"src/compiler/linkage.h",
|
||||
"src/compiler/load-elimination.cc",
|
||||
|
2
BUILD.gn
2
BUILD.gn
@ -2861,6 +2861,7 @@ v8_header_set("v8_internal_headers") {
|
||||
"src/compiler/js-operator.h",
|
||||
"src/compiler/js-type-hint-lowering.h",
|
||||
"src/compiler/js-typed-lowering.h",
|
||||
"src/compiler/late-escape-analysis.h",
|
||||
"src/compiler/linkage.h",
|
||||
"src/compiler/load-elimination.h",
|
||||
"src/compiler/loop-analysis.h",
|
||||
@ -3994,6 +3995,7 @@ v8_compiler_sources = [
|
||||
"src/compiler/js-operator.cc",
|
||||
"src/compiler/js-type-hint-lowering.cc",
|
||||
"src/compiler/js-typed-lowering.cc",
|
||||
"src/compiler/late-escape-analysis.cc",
|
||||
"src/compiler/linkage.cc",
|
||||
"src/compiler/load-elimination.cc",
|
||||
"src/compiler/loop-analysis.cc",
|
||||
|
145
src/compiler/late-escape-analysis.cc
Normal file
145
src/compiler/late-escape-analysis.cc
Normal file
@ -0,0 +1,145 @@
|
||||
// Copyright 2022 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/compiler/late-escape-analysis.h"
|
||||
|
||||
#include "src/compiler/js-graph.h"
|
||||
#include "src/compiler/node-properties.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
|
||||
LateEscapeAnalysis::LateEscapeAnalysis(Editor* editor, Graph* graph,
|
||||
CommonOperatorBuilder* common,
|
||||
Zone* zone)
|
||||
: AdvancedReducer(editor),
|
||||
dead_(graph->NewNode(common->Dead())),
|
||||
all_allocations_(zone),
|
||||
escaping_allocations_(zone),
|
||||
revisit_(zone) {}
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsStore(Edge edge) {
|
||||
DCHECK_EQ(edge.to()->opcode(), IrOpcode::kAllocateRaw);
|
||||
DCHECK(NodeProperties::IsValueEdge(edge));
|
||||
|
||||
switch (edge.from()->opcode()) {
|
||||
case IrOpcode::kInitializeImmutableInObject:
|
||||
case IrOpcode::kStore:
|
||||
case IrOpcode::kStoreElement:
|
||||
case IrOpcode::kStoreField:
|
||||
case IrOpcode::kStoreToObject:
|
||||
return edge.index() == 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsEscapingAllocationWitness(Edge edge) {
|
||||
if (edge.to()->opcode() != IrOpcode::kAllocateRaw) return false;
|
||||
if (!NodeProperties::IsValueEdge(edge)) return false;
|
||||
return !IsStore(edge);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Reduction LateEscapeAnalysis::Reduce(Node* node) {
|
||||
if (node->opcode() == IrOpcode::kAllocateRaw) {
|
||||
all_allocations_.insert(node);
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
for (Edge edge : node->input_edges()) {
|
||||
if (IsEscapingAllocationWitness(edge)) {
|
||||
RecordEscapingAllocation(edge.to());
|
||||
}
|
||||
}
|
||||
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
void LateEscapeAnalysis::Finalize() {
|
||||
for (Node* alloc : all_allocations_) {
|
||||
if (!IsEscaping(alloc)) {
|
||||
RemoveAllocation(alloc);
|
||||
}
|
||||
}
|
||||
while (!revisit_.empty()) {
|
||||
Node* alloc = revisit_.front();
|
||||
revisit_.pop_front();
|
||||
if (!IsEscaping(alloc) && !alloc->IsDead()) {
|
||||
RemoveAllocation(alloc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
base::Optional<Node*> TryGetStoredValue(Node* node) {
|
||||
int value_index;
|
||||
switch (node->opcode()) {
|
||||
case IrOpcode::kInitializeImmutableInObject:
|
||||
case IrOpcode::kStore:
|
||||
case IrOpcode::kStoreElement:
|
||||
case IrOpcode::kStoreToObject:
|
||||
value_index = 2;
|
||||
break;
|
||||
case IrOpcode::kStoreField:
|
||||
value_index = 1;
|
||||
break;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
|
||||
return NodeProperties::GetValueInput(node, value_index);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool LateEscapeAnalysis::IsEscaping(Node* node) {
|
||||
DCHECK_EQ(node->opcode(), IrOpcode::kAllocateRaw);
|
||||
auto escaping = escaping_allocations_.find(node);
|
||||
if (escaping == escaping_allocations_.end()) return false;
|
||||
return escaping->second != 0;
|
||||
}
|
||||
|
||||
void LateEscapeAnalysis::RemoveAllocation(Node* node) {
|
||||
DCHECK_EQ(node->opcode(), IrOpcode::kAllocateRaw);
|
||||
for (Edge edge : node->use_edges()) {
|
||||
if (!NodeProperties::IsValueEdge(edge)) continue;
|
||||
Node* use = edge.from();
|
||||
// The value stored by this Store node might be another allocation which has
|
||||
// no more uses. Affected allocations are revisited.
|
||||
if (base::Optional<Node*> stored_value = TryGetStoredValue(use);
|
||||
stored_value.has_value() &&
|
||||
stored_value.value()->opcode() == IrOpcode::kAllocateRaw &&
|
||||
stored_value.value() != node) {
|
||||
RemoveWitness(stored_value.value());
|
||||
revisit_.push_back(stored_value.value());
|
||||
}
|
||||
ReplaceWithValue(use, dead());
|
||||
use->Kill();
|
||||
}
|
||||
|
||||
// Remove the allocation from the effect and control chains.
|
||||
ReplaceWithValue(node, dead());
|
||||
node->Kill();
|
||||
}
|
||||
|
||||
void LateEscapeAnalysis::RecordEscapingAllocation(Node* allocation) {
|
||||
DCHECK_EQ(allocation->opcode(), IrOpcode::kAllocateRaw);
|
||||
escaping_allocations_[allocation]++;
|
||||
}
|
||||
|
||||
void LateEscapeAnalysis::RemoveWitness(Node* allocation) {
|
||||
DCHECK_EQ(allocation->opcode(), IrOpcode::kAllocateRaw);
|
||||
DCHECK_GT(escaping_allocations_[allocation], 0);
|
||||
escaping_allocations_[allocation]--;
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
46
src/compiler/late-escape-analysis.h
Normal file
46
src/compiler/late-escape-analysis.h
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright 2022 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef V8_COMPILER_LATE_ESCAPE_ANALYSIS_H_
|
||||
#define V8_COMPILER_LATE_ESCAPE_ANALYSIS_H_
|
||||
|
||||
#include "src/compiler/graph-reducer.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
|
||||
class CommonOperatorBuilder;
|
||||
|
||||
// Eliminate allocated objects that have no uses besides the stores initializing
|
||||
// the object.
|
||||
class LateEscapeAnalysis final : public AdvancedReducer {
|
||||
public:
|
||||
LateEscapeAnalysis(Editor* editor, Graph* graph,
|
||||
CommonOperatorBuilder* common, Zone* zone);
|
||||
|
||||
const char* reducer_name() const override { return "LateEscapeAnalysis"; }
|
||||
|
||||
Reduction Reduce(Node* node) final;
|
||||
void Finalize() override;
|
||||
|
||||
private:
|
||||
bool IsEscaping(Node* node);
|
||||
void RemoveAllocation(Node* node);
|
||||
void RecordEscapingAllocation(Node* allocation);
|
||||
void RemoveWitness(Node* allocation);
|
||||
Node* dead() const { return dead_; }
|
||||
|
||||
Node* dead_;
|
||||
ZoneUnorderedSet<Node*> all_allocations_;
|
||||
// Key: Allocation; Value: Number of witnesses for the allocation escaping.
|
||||
ZoneUnorderedMap<Node*, int> escaping_allocations_;
|
||||
NodeDeque revisit_;
|
||||
};
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_COMPILER_LATE_ESCAPE_ANALYSIS_H_
|
@ -55,6 +55,7 @@
|
||||
#include "src/compiler/js-intrinsic-lowering.h"
|
||||
#include "src/compiler/js-native-context-specialization.h"
|
||||
#include "src/compiler/js-typed-lowering.h"
|
||||
#include "src/compiler/late-escape-analysis.h"
|
||||
#include "src/compiler/load-elimination.h"
|
||||
#include "src/compiler/loop-analysis.h"
|
||||
#include "src/compiler/loop-peeling.h"
|
||||
@ -1950,6 +1951,8 @@ struct LateOptimizationPhase {
|
||||
GraphReducer graph_reducer(
|
||||
temp_zone, data->graph(), &data->info()->tick_counter(), data->broker(),
|
||||
data->jsgraph()->Dead(), data->observe_node_manager());
|
||||
LateEscapeAnalysis escape_analysis(&graph_reducer, data->graph(),
|
||||
data->common(), temp_zone);
|
||||
BranchElimination branch_condition_elimination(
|
||||
&graph_reducer, data->jsgraph(), temp_zone, data->source_positions());
|
||||
DeadCodeElimination dead_code_elimination(&graph_reducer, data->graph(),
|
||||
@ -1961,6 +1964,7 @@ struct LateOptimizationPhase {
|
||||
data->machine(), temp_zone, BranchSemantics::kMachine);
|
||||
JSGraphAssembler graph_assembler(data->jsgraph(), temp_zone);
|
||||
SelectLowering select_lowering(&graph_assembler, data->graph());
|
||||
AddReducer(data, &graph_reducer, &escape_analysis);
|
||||
AddReducer(data, &graph_reducer, &branch_condition_elimination);
|
||||
AddReducer(data, &graph_reducer, &dead_code_elimination);
|
||||
AddReducer(data, &graph_reducer, &machine_reducer);
|
||||
|
Loading…
Reference in New Issue
Block a user