[dart2js] SSA cleanup
This change essentially converts the complex
`SsaOptimizerTask.optimize` method with local functions into a
`SsaOptimizerWorkItem` object.
- The work item now owns the ranges rather than the global task (which
was a kind of leak, although inconsequential).
- Moved some optimization phase constructor calls into methods to
reduce repeated code.
- Factor out code that updates the properties of an invocation.
Change-Id: I204acd0a68fd8bc4e64e707ff16f1b743125ff29
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/448421
Reviewed-by: Nate Biggs <natebiggs@google.com>
Commit-Queue: Stephen Adams <sra@google.com>
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 6e998ef..794fd53 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -2117,6 +2117,10 @@
}
@override
R accept<R>(HVisitor<R> visitor) => visitor.visitInvokeClosure(this);
+
+ @override
+ String toString() =>
+ 'invoke closure: selector=$selector, mask=$receiverType, element=$element';
}
class HInvokeDynamicMethod extends HInvokeDynamic {
@@ -2208,8 +2212,7 @@
MemberEntity? element,
List<HInstruction> inputs,
bool isIntercepted,
- // TODO(johnniwinther): The result type for a setter should be the empty
- // type.
+ // TODO(johnniwinther): The result type for a setter should be 'void'.
AbstractValue resultType,
SourceInformation? sourceInformation,
) : super(
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index 1ede018..b95e85f 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -60,8 +60,6 @@
class SsaOptimizerTask extends CompilerTask {
final CompilerOptions _options;
- Map<HInstruction, Range> ranges = {};
-
final Map<MemberEntity, OptimizationTestLog> loggersForTesting = {};
SsaOptimizerTask(super.measurer, this._options);
@@ -78,90 +76,112 @@
CodegenRegistry registry,
SsaMetrics metrics,
) {
- void runPhase(OptimizationPhase phase) {
- measureSubtask(phase.name, () => phase.visitGraph(graph));
- codegen.tracer.traceGraph(phase.name, graph);
- assert(graph.isValid(), 'Graph not valid after ${phase.name}');
- assert(
- phase.validPostcondition(graph),
- 'Graph does not satisfy phase postcondition after ${phase.name}',
- );
- }
+ final workItem = SsaOptimizerWorkItem(
+ this,
+ member,
+ graph,
+ codegen,
+ closedWorld,
+ globalInferenceResults,
+ registry,
+ metrics,
+ );
+ workItem.optimize();
+ }
+}
+class SsaOptimizerWorkItem {
+ final SsaOptimizerTask task;
+
+ final MemberEntity member;
+ final HGraph graph;
+ final CodegenInputs codegen;
+ final JClosedWorld closedWorld;
+ final GlobalTypeInferenceResults globalInferenceResults;
+ final CodegenRegistry registry;
+ final SsaMetrics metrics;
+
+ late final OptimizationTestLog? log = retainDataForTesting
+ ? task.loggersForTesting[member] = OptimizationTestLog(
+ closedWorld.dartTypes,
+ )
+ : null;
+
+ late final TypeRecipeDomain typeRecipeDomain = TypeRecipeDomainImpl(
+ closedWorld.dartTypes,
+ );
+
+ Map<HInstruction, Range> ranges = {};
+
+ SsaOptimizerWorkItem(
+ this.task,
+ this.member,
+ this.graph,
+ this.codegen,
+ this.closedWorld,
+ this.globalInferenceResults,
+ this.registry,
+ this.metrics,
+ );
+
+ void runPhase(OptimizationPhase phase) {
+ task.measureSubtask(phase.name, () => phase.visitGraph(graph));
+ codegen.tracer.traceGraph(phase.name, graph);
+ assert(graph.isValid(), 'Graph not valid after ${phase.name}');
+ assert(
+ phase.validPostcondition(graph),
+ 'Graph does not satisfy phase postcondition after ${phase.name}',
+ );
+ }
+
+ SsaInstructionSimplifier simplifierPhase({
+ bool beforeTypePropagation = false,
+ }) {
+ return SsaInstructionSimplifier(
+ globalInferenceResults,
+ task._options,
+ closedWorld,
+ typeRecipeDomain,
+ registry,
+ log,
+ metrics,
+ beforeTypePropagation: beforeTypePropagation,
+ );
+ }
+
+ SsaTypePropagator typePropagatorPhase() {
+ return SsaTypePropagator(
+ globalInferenceResults,
+ closedWorld.commonElements,
+ closedWorld,
+ log,
+ );
+ }
+
+ void optimize() {
SsaCodeMotion codeMotion;
SsaLoadElimination loadElimination;
- TypeRecipeDomain typeRecipeDomain = TypeRecipeDomainImpl(
- closedWorld.dartTypes,
- );
-
- OptimizationTestLog? log;
- if (retainDataForTesting) {
- log = loggersForTesting[member] = OptimizationTestLog(
- closedWorld.dartTypes,
- );
- }
-
- measure(() {
+ task.measure(() {
List<OptimizationPhase> phases = [
// Run trivial instruction simplification first to optimize some
// patterns useful for type conversion.
- SsaInstructionSimplifier(
- globalInferenceResults,
- _options,
- closedWorld,
- typeRecipeDomain,
- registry,
- log,
- metrics,
- beforeTypePropagation: true,
- ),
+ simplifierPhase(beforeTypePropagation: true),
SsaTypeConversionInserter(closedWorld),
SsaRedundantPhiEliminator(),
SsaDeadPhiEliminator(),
- SsaTypePropagator(
- globalInferenceResults,
- closedWorld.commonElements,
- closedWorld,
- log,
- ),
+ typePropagatorPhase(),
// After type propagation, more instructions can be simplified.
- SsaInstructionSimplifier(
- globalInferenceResults,
- _options,
- closedWorld,
- typeRecipeDomain,
- registry,
- log,
- metrics,
- ),
- SsaInstructionSimplifier(
- globalInferenceResults,
- _options,
- closedWorld,
- typeRecipeDomain,
- registry,
- log,
- metrics,
- ),
- SsaTypePropagator(
- globalInferenceResults,
- closedWorld.commonElements,
- closedWorld,
- log,
- ),
+ simplifierPhase(),
+ simplifierPhase(),
+ typePropagatorPhase(),
// Run a dead code eliminator before LICM because dead
// interceptors are often in the way of LICM'able instructions.
SsaDeadCodeEliminator(closedWorld, this),
SsaGlobalValueNumberer(closedWorld.abstractValueDomain),
// After GVN, some instructions might need their type to be
// updated because they now have different inputs.
- SsaTypePropagator(
- globalInferenceResults,
- closedWorld.commonElements,
- closedWorld,
- log,
- ),
+ typePropagatorPhase(),
codeMotion = SsaCodeMotion(closedWorld.abstractValueDomain),
loadElimination = SsaLoadElimination(closedWorld),
SsaRedundantPhiEliminator(),
@@ -171,29 +191,16 @@
// controlled by a test on the value, so redo 'conversion insertion' to
// learn from the refined type.
SsaTypeConversionInserter(closedWorld),
- SsaTypePropagator(
- globalInferenceResults,
- closedWorld.commonElements,
- closedWorld,
- log,
- ),
+ typePropagatorPhase(),
SsaValueRangeAnalyzer(closedWorld, this, codegen.tracer),
// Previous optimizations may have generated new
// opportunities for instruction simplification.
- SsaInstructionSimplifier(
- globalInferenceResults,
- _options,
- closedWorld,
- typeRecipeDomain,
- registry,
- log,
- metrics,
- ),
+ simplifierPhase(),
];
phases.forEach(runPhase);
- // Simplifying interceptors is just an optimization, it is required for
- // implementation correctness because the code generator assumes it is
+ // Simplifying interceptors is not just an optimization, it is required
+ // for implementation correctness because the code generator assumes it is
// always performed to compute the intercepted classes sets.
runPhase(SsaSimplifyInterceptors(closedWorld, member.enclosingClass));
@@ -204,46 +211,20 @@
dce.newGvnCandidates ||
loadElimination.newGvnCandidates) {
phases = [
- SsaTypePropagator(
- globalInferenceResults,
- closedWorld.commonElements,
- closedWorld,
- log,
- ),
+ typePropagatorPhase(),
SsaGlobalValueNumberer(closedWorld.abstractValueDomain),
SsaCodeMotion(closedWorld.abstractValueDomain),
SsaValueRangeAnalyzer(closedWorld, this, codegen.tracer),
- SsaInstructionSimplifier(
- globalInferenceResults,
- _options,
- closedWorld,
- typeRecipeDomain,
- registry,
- log,
- metrics,
- ),
+ simplifierPhase(),
SsaSimplifyInterceptors(closedWorld, member.enclosingClass),
SsaDeadCodeEliminator(closedWorld, this),
];
} else {
phases = [
- SsaTypePropagator(
- globalInferenceResults,
- closedWorld.commonElements,
- closedWorld,
- log,
- ),
+ typePropagatorPhase(),
// Run the simplifier to remove unneeded type checks inserted by
// type propagation.
- SsaInstructionSimplifier(
- globalInferenceResults,
- _options,
- closedWorld,
- typeRecipeDomain,
- registry,
- log,
- metrics,
- ),
+ simplifierPhase(),
];
}
phases.forEach(runPhase);
@@ -1028,6 +1009,8 @@
resultMask,
const <DartType>[],
);
+ _updateInvocationAttributes(tagInstruction, commonElements.setArrayType);
+
// 'Linear typing' trick: [tagInstruction] is the only use of the
// [splitInstruction], so it becomes the sole alias.
// TODO(sra): Build this knowledge into alias analysis.
@@ -1073,15 +1056,7 @@
node.element = method;
}
- node.sideEffects.restrictTo(
- _globalInferenceResults.inferredData.getSideEffectsOfElement(element),
- );
- if (_closedWorld.annotationsData.allowCSE(element)) {
- node.allowCSE = true;
- }
- if (_closedWorld.annotationsData.allowDCE(element)) {
- node.allowDCE = true;
- }
+ _updateInvocationAttributes(node, method);
return node;
}
@@ -2091,15 +2066,7 @@
if (_nativeData.isNativeMember(member)) {
return tryInlineNativeGetter(node, member) ?? node;
}
- node.sideEffects.restrictTo(
- _globalInferenceResults.inferredData.getSideEffectsOfElement(member),
- );
- if (_closedWorld.annotationsData.allowCSE(member)) {
- node.allowCSE = true;
- }
- if (_closedWorld.annotationsData.allowDCE(member)) {
- node.allowDCE = true;
- }
+ _updateInvocationAttributes(node, member);
}
}
@@ -2113,6 +2080,18 @@
return node;
}
+ void _updateInvocationAttributes(HInvoke node, FunctionEntity member) {
+ node.sideEffects.restrictTo(
+ _globalInferenceResults.inferredData.getSideEffectsOfElement(member),
+ );
+ if (_closedWorld.annotationsData.allowCSE(member)) {
+ node.allowCSE = true;
+ }
+ if (_closedWorld.annotationsData.allowDCE(member)) {
+ node.allowDCE = true;
+ }
+ }
+
HFieldGet _directFieldGet(
HInstruction receiver,
FieldEntity field,
@@ -3197,13 +3176,13 @@
final String name = "SsaDeadCodeEliminator";
final JClosedWorld closedWorld;
- final SsaOptimizerTask optimizer;
+ final SsaOptimizerWorkItem workItem;
late final HGraph _graph;
Map<HInstruction, bool> trivialDeadStoreReceivers = Maplet();
bool eliminatedSideEffects = false;
bool newGvnCandidates = false;
- SsaDeadCodeEliminator(this.closedWorld, this.optimizer);
+ SsaDeadCodeEliminator(this.closedWorld, this.workItem);
AbstractValueDomain get _abstractValueDomain =>
closedWorld.abstractValueDomain;
@@ -3261,7 +3240,7 @@
}
void _computeLiveness() {
- var analyzer = SsaLiveBlockAnalyzer(_graph, closedWorld, optimizer);
+ var analyzer = SsaLiveBlockAnalyzer(_graph, closedWorld, workItem);
analyzer.analyze();
for (HBasicBlock block in _graph.blocks) {
block.isLive = analyzer.isLiveBlock(block);
@@ -3511,15 +3490,15 @@
final HGraph graph;
final Set<HBasicBlock> live = {};
final List<HBasicBlock> worklist = [];
- final SsaOptimizerTask optimizer;
+ final SsaOptimizerWorkItem workItem;
final JClosedWorld closedWorld;
- SsaLiveBlockAnalyzer(this.graph, this.closedWorld, this.optimizer);
+ SsaLiveBlockAnalyzer(this.graph, this.closedWorld, this.workItem);
AbstractValueDomain get _abstractValueDomain =>
closedWorld.abstractValueDomain;
- Map<HInstruction, Range> get ranges => optimizer.ranges;
+ Map<HInstruction, Range> get ranges => workItem.ranges;
bool isLiveBlock(HBasicBlock block) => live.contains(block);
diff --git a/pkg/compiler/lib/src/ssa/value_range_analyzer.dart b/pkg/compiler/lib/src/ssa/value_range_analyzer.dart
index 7779e41..e4ed34c 100644
--- a/pkg/compiler/lib/src/ssa/value_range_analyzer.dart
+++ b/pkg/compiler/lib/src/ssa/value_range_analyzer.dart
@@ -9,7 +9,7 @@
import '../js_model/js_world.dart' show JClosedWorld;
import '../tracer.dart';
import 'nodes.dart';
-import 'optimize.dart' show OptimizationPhase, SsaOptimizerTask;
+import 'optimize.dart' show OptimizationPhase, SsaOptimizerWorkItem;
bool _debug = false;
@@ -746,12 +746,12 @@
final JClosedWorld closedWorld;
final ValueRangeInfo info = ValueRangeInfo();
- final SsaOptimizerTask optimizer;
+ final SsaOptimizerWorkItem workItem;
final Tracer tracer;
late HGraph graph;
- SsaValueRangeAnalyzer(this.closedWorld, this.optimizer, this.tracer);
+ SsaValueRangeAnalyzer(this.closedWorld, this.workItem, this.tracer);
@override
void visitGraph(HGraph graph) {
@@ -772,7 +772,7 @@
removeRangeConversion();
// TODO(herhut): Find a cleaner way to pass around ranges.
- optimizer.ranges = ranges;
+ workItem.ranges = ranges;
}
@override