From 45ec012c667cd2ab1856e174e550d64667f87b0d Mon Sep 17 00:00:00 2001 From: Alexander Markov Date: Thu, 4 Dec 2025 08:26:41 -0800 Subject: [PATCH] Constant propagation Issue: https://github.com/dart-lang/sdk/issues/61635 Change-Id: I105cf5914cad3db83f3d8097ddf793be454e43d6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/465741 Reviewed-by: Slava Egorov Commit-Queue: Alexander Markov --- pkg/cfg/lib/front_end/ast_to_ir.dart | 34 +- pkg/cfg/lib/passes/constant_propagation.dart | 569 ++++++++++++++++++ pkg/cfg/test/ir_test.dart | 2 + pkg/cfg/testcases/constant_propagation.dart | 63 ++ .../constant_propagation.dart.expect | 98 +++ pkg/cfg/testcases/loops.dart.expect | 4 +- pkg/cfg/testcases/statements.dart.expect | 87 +-- pkg/cfg/testcases/unreachable_ast.dart.expect | 21 +- 8 files changed, 789 insertions(+), 89 deletions(-) create mode 100644 pkg/cfg/lib/passes/constant_propagation.dart create mode 100644 pkg/cfg/testcases/constant_propagation.dart create mode 100644 pkg/cfg/testcases/constant_propagation.dart.expect diff --git a/pkg/cfg/lib/front_end/ast_to_ir.dart b/pkg/cfg/lib/front_end/ast_to_ir.dart index 779350688ed..74af2999a36 100644 --- a/pkg/cfg/lib/front_end/ast_to_ir.dart +++ b/pkg/cfg/lib/front_end/ast_to_ir.dart @@ -692,6 +692,31 @@ class AstToIr extends ast.RecursiveVisitor { }); } + void _generateSwitchComparison( + Definition value, + ast.Expression caseExpression, + ) { + _translateNode(caseExpression); + // TODO(alexmarkov): use proper devirtualization to specialize ==. + final interfaceTarget = (builder.stackTop.type is IntType) + ? coreTypes.index.getProcedure('dart:core', 'num', '==') + : coreTypes.objectEquals; + builder.push(value); + final matcher = recognizedMethods.instanceInvocations[interfaceTarget]; + if (matcher != null) { + final snippet = matcher.match([_staticType(caseExpression), value.type]); + if (snippet != null) { + snippet(builder); + return; + } + } + builder.addInterfaceCall( + functionRegistry.getFunction(interfaceTarget), + 2, + const BoolType(), + ); + } + @override void visitSwitchStatement(ast.SwitchStatement node) { _translateNode(node.expression); @@ -717,14 +742,7 @@ class AstToIr extends ast.RecursiveVisitor { builder.currentSourcePosition = SourcePosition( switchCase.expressionOffsets[i], ); - // TODO(alexmarkov): use more efficient comparison - _translateNode(switchCase.expressions[i]); - builder.push(value); - builder.addInterfaceCall( - functionRegistry.getFunction(coreTypes.objectEquals), - 2, - const BoolType(), - ); + _generateSwitchComparison(value, switchCase.expressions[i]); final trueBlock = builder.newTargetBlock(); final falseBlock = builder.newTargetBlock(); diff --git a/pkg/cfg/lib/passes/constant_propagation.dart b/pkg/cfg/lib/passes/constant_propagation.dart new file mode 100644 index 00000000000..b88254031ea --- /dev/null +++ b/pkg/cfg/lib/passes/constant_propagation.dart @@ -0,0 +1,569 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:cfg/ir/constant_value.dart'; +import 'package:cfg/ir/instructions.dart'; +import 'package:cfg/ir/visitor.dart'; +import 'package:cfg/passes/pass.dart'; +import 'package:cfg/utils/bit_vector.dart'; + +/// Sparse conditional constant propagation. +/// +/// Performs constant propagation and unreachable code elimination +/// at the same time. +/// +/// The algorithm is described in Wegman, Mark N. and Zadeck, F. Kenneth. +/// "Constant Propagation with Conditional Branches". +final class ConstantPropagation extends Pass + implements InstructionVisitor { + final ConstantFolding constantFolding = ConstantFolding(); + + // State transition for blocks: unreachable -> reachable. + late final BitVector _reachable = BitVector(graph.preorder.length); + + // State transitions for definitions: + // unknown -> constant -> non-constant + final Map _constantValues = {}; + late final BitVector _nonConstant = BitVector(graph.instructions.length); + + // State transitions for phis: + // unknown -> redundant -> non-redundant + final Map _redundantPhis = {}; + + // Work lists for blocks and definitions, appended on state transitions. + final _blockWorkList = []; + final _definitionsWorkList = []; + + ConstantPropagation() : super('ConstantPropagation'); + + @override + void run() { + analyze(); + transform(); + } + + void analyze() { + _addReachableBlock(graph.entryBlock); + + while (_blockWorkList.isNotEmpty || _definitionsWorkList.isNotEmpty) { + while (_definitionsWorkList.isNotEmpty) { + final def = _definitionsWorkList.removeLast(); + for (final use in def.inputUses) { + final user = use.getInstruction(graph); + if (_isReachable(user.block!)) { + _visit(user); + } + } + } + while (_blockWorkList.isNotEmpty) { + final block = _blockWorkList.removeLast(); + _visit(block); + } + } + } + + void _visit(Instruction instr) { + currentInstruction = instr; + instr.accept(this); + } + + bool _isReachable(Block block) => _reachable[block.preorderNumber]; + + void _addReachableBlock(Block block) { + if (!_isReachable(block)) { + _reachable[block.preorderNumber] = true; + _blockWorkList.add(block); + } + } + + bool _isNonConstant(Definition instr) => _nonConstant[instr.id]; + + ConstantValue? _getConstantValue(Definition instr) => + instr is Constant ? instr.value : _constantValues[instr]; + + void _setConstantValue(Definition instr, ConstantValue result) { + assert(!_isNonConstant(instr)); + assert(instr is! Constant); + final old = _constantValues[instr]; + if (old == null) { + // State transition: unknown -> constant. + _constantValues[instr] = result; + _definitionsWorkList.add(instr); + } else { + // Constant -> same constant. + assert(old == result); + } + } + + void _setNonConstant(Definition instr) { + assert(instr is! Constant); + if (!_isNonConstant(instr)) { + // State transition: unknown or constant -> non-constant. + _nonConstant[instr.id] = true; + _constantValues.remove(instr); + _definitionsWorkList.add(instr); + } + } + + void _setResult(Definition instr, ConstantValue? result) { + if (result != null) { + _setConstantValue(instr, result); + } else { + _setNonConstant(instr); + } + } + + void _setRedundantPhi(Phi instr, Definition originalInput) { + final old = _redundantPhis[instr]; + if (old == null) { + // State transition: unknown -> redundant. + _redundantPhis[instr] = originalInput; + _definitionsWorkList.add(instr); + } else { + // No state transition: redundant -> redundant with the same input. + assert(old == originalInput); + } + } + + void _setNonRedundantPhi(Phi instr) { + if (_redundantPhis.remove(instr) != null) { + // State transition: redundant -> non-redundant. + _definitionsWorkList.add(instr); + } + } + + Definition _unwrapRedundantPhi(Definition def) => + def is Phi ? (_redundantPhis[def] ?? def) : def; + + bool _sameDefinitions(Definition a, Definition b) => + _unwrapRedundantPhi(a) == _unwrapRedundantPhi(b); + + void visitBlock(Block block) { + currentBlock = block; + assert(_isReachable(block)); + var canThrow = false; + for (final instr in block) { + _visit(instr); + canThrow = canThrow || instr.canThrow; + } + final exceptionHandler = block.exceptionHandler; + if (exceptionHandler != null && canThrow) { + _addReachableBlock(exceptionHandler); + } + currentBlock = null; + } + + @override + void visitEntryBlock(EntryBlock instr) => visitBlock(instr); + + @override + void visitJoinBlock(JoinBlock instr) => visitBlock(instr); + + @override + void visitTargetBlock(TargetBlock instr) => visitBlock(instr); + + @override + void visitCatchBlock(CatchBlock instr) => visitBlock(instr); + + @override + void visitGoto(Goto instr) { + final target = instr.target; + if (!_isReachable(target)) { + _addReachableBlock(target); + } else { + // Re-visit phis in the target block as this predecessor + // became reachable. + if (target is JoinBlock) { + for (final phi in target.phis) { + _visit(phi); + } + } + } + } + + @override + void visitBranch(Branch instr) { + if (_isNonConstant(instr.condition)) { + _addReachableBlock(instr.trueSuccessor); + _addReachableBlock(instr.falseSuccessor); + return; + } + ConstantValue? condition = _getConstantValue(instr.condition); + if (condition != null) { + _addReachableBlock( + condition.boolValue ? instr.trueSuccessor : instr.falseSuccessor, + ); + } + } + + @override + void visitCompareAndBranch(CompareAndBranch instr) { + // Comparison and Branch should be optimized separately + // before combined to CompareAndBranch. + _addReachableBlock(instr.trueSuccessor); + _addReachableBlock(instr.falseSuccessor); + } + + @override + void visitTryEntry(TryEntry instr) { + _addReachableBlock(instr.tryBody); + // Do not mark catch block as reachable here. + // Catch block is marked reachable iff any instruction in the try body + // can throw. + } + + @override + void visitPhi(Phi instr) { + final preds = instr.block!.predecessors; + assert(instr.inputCount == preds.length); + var canBeConstant = true; + var canBeRedundant = true; + ConstantValue? constantValue; + Definition? originalInput; + for (int i = 0, n = instr.inputCount; i < n; ++i) { + if (!_isReachable(preds[i])) { + continue; + } + final input = instr.inputDefAt(i); + if (input == instr) { + continue; + } + if (_isNonConstant(input)) { + // Any input is non-constant => phi is non-constant. + _setNonConstant(instr); + canBeConstant = false; + } else if (canBeConstant) { + ConstantValue? value = _getConstantValue(input); + if (value == null) { + // Unknown input => unknown phi. + canBeConstant = false; + } else { + if (constantValue == null) { + // First constant input is discovered. + constantValue = value; + } else if (constantValue != value) { + // Two constant inputs with different values => + // phi is non-constant. + _setNonConstant(instr); + canBeConstant = false; + } + } + } + if (canBeRedundant) { + if (originalInput == null) { + originalInput = input; + } else if (input != originalInput) { + canBeRedundant = false; + } + } + } + if (canBeConstant) { + _setConstantValue(instr, constantValue!); + } + if (canBeRedundant) { + _setRedundantPhi(instr, originalInput!); + } else { + _setNonRedundantPhi(instr); + } + } + + @override + void visitReturn(Return instr) {} + + @override + void visitConstant(Constant instr) { + // There is no need to flood _constantValues map with Constant + // instructions as they are handled in _getConstantValue directly + // and will not be replaced. + } + + @override + void visitDirectCall(DirectCall instr) { + _setNonConstant(instr); + } + + @override + void visitInterfaceCall(InterfaceCall instr) { + _setNonConstant(instr); + } + + @override + void visitDynamicCall(DynamicCall instr) { + _setNonConstant(instr); + } + + @override + void visitParameter(Parameter instr) { + _setNonConstant(instr); + } + + @override + void visitLoadLocal(LoadLocal instr) => + throw 'Should not be used in SSA form.'; + + @override + void visitStoreLocal(StoreLocal instr) => + throw 'Should not be used in SSA form.'; + + @override + void visitThrow(Throw instr) {} + + @override + void visitTypeParameters(TypeParameters instr) { + _setNonConstant(instr); + } + + @override + void visitTypeCast(TypeCast instr) { + if (_isNonConstant(instr.operand)) { + _setNonConstant(instr); + return; + } + ConstantValue? operand = _getConstantValue(instr.operand); + if (operand != null) { + if (operand.type.isSubtypeOf(instr.testedType)) { + _setResult(instr, operand); + } else { + _setNonConstant(instr); + } + } + } + + @override + void visitTypeTest(TypeTest instr) { + if (_isNonConstant(instr.operand)) { + _setNonConstant(instr); + return; + } + ConstantValue? operand = _getConstantValue(instr.operand); + if (operand != null) { + _setResult( + instr, + ConstantValue.fromBool(operand.type.isSubtypeOf(instr.testedType)), + ); + } + } + + @override + void visitTypeArguments(TypeArguments instr) { + _setNonConstant(instr); + } + + @override + void visitComparison(Comparison instr) { + switch (instr.op) { + case ComparisonOpcode.equal: + case ComparisonOpcode.identical: + case ComparisonOpcode.intEqual: + if (_sameDefinitions(instr.left, instr.right)) { + _setResult(instr, ConstantValue.fromBool(true)); + return; + } + case ComparisonOpcode.notEqual: + case ComparisonOpcode.notIdentical: + case ComparisonOpcode.intNotEqual: + if (_sameDefinitions(instr.left, instr.right)) { + _setResult(instr, ConstantValue.fromBool(false)); + return; + } + default: + } + if (_isNonConstant(instr.left) || _isNonConstant(instr.right)) { + _setNonConstant(instr); + return; + } + ConstantValue? left = _getConstantValue(instr.left); + ConstantValue? right = _getConstantValue(instr.right); + if (left != null && right != null) { + ConstantValue result = constantFolding.comparison(instr.op, left, right); + _setResult(instr, result); + } + } + + @override + void visitBinaryIntOp(BinaryIntOp instr) { + if (_isNonConstant(instr.left) || _isNonConstant(instr.right)) { + _setNonConstant(instr); + return; + } + ConstantValue? left = _getConstantValue(instr.left); + ConstantValue? right = _getConstantValue(instr.right); + if (left != null && right != null) { + ConstantValue? result = constantFolding.binaryIntOp( + instr.op, + left, + right, + ); + _setResult(instr, result); + } + } + + @override + void visitUnaryIntOp(UnaryIntOp instr) { + if (_isNonConstant(instr.operand)) { + _setNonConstant(instr); + return; + } + ConstantValue? operand = _getConstantValue(instr.operand); + if (operand != null) { + ConstantValue? result = constantFolding.unaryIntOp(instr.op, operand); + _setResult(instr, result); + } + } + + @override + void visitBinaryDoubleOp(BinaryDoubleOp instr) { + if (_isNonConstant(instr.left) || _isNonConstant(instr.right)) { + _setNonConstant(instr); + return; + } + ConstantValue? left = _getConstantValue(instr.left); + ConstantValue? right = _getConstantValue(instr.right); + if (left != null && right != null) { + ConstantValue? result = constantFolding.binaryDoubleOp( + instr.op, + left, + right, + ); + _setResult(instr, result); + } + } + + @override + void visitUnaryDoubleOp(UnaryDoubleOp instr) { + if (_isNonConstant(instr.operand)) { + _setNonConstant(instr); + return; + } + ConstantValue? operand = _getConstantValue(instr.operand); + if (operand != null) { + ConstantValue? result = constantFolding.unaryDoubleOp(instr.op, operand); + _setResult(instr, result); + } + } + + @override + void visitParallelMove(ParallelMove instr) {} + + void transform() { + for (final entry in _constantValues.entries) { + final instr = entry.key; + final constantValue = entry.value; + instr.replaceUsesWith(graph.getConstant(constantValue)); + instr.removeFromGraph(); + } + for (final entry in _redundantPhis.entries) { + final instr = entry.key; + final input = entry.value; + if (!_constantValues.containsKey(instr)) { + assert(!_constantValues.containsKey(input)); + instr.replaceUsesWith(input); + instr.removeFromGraph(); + } + } + var recomputeControlFlow = false; + for (final block in graph.preorder) { + if (!_isReachable(block)) { + for (final instr in block) { + instr.removeInputsFromUseLists(); + } + recomputeControlFlow = true; + } + } + if (!recomputeControlFlow) { + graph.invalidateInstructionNumbering(); + return; + } + for (final block in graph.preorder) { + if (_isReachable(block)) { + if (block is JoinBlock) { + _transformPhis(block); + } + block.exceptionHandler = _transformExceptionHandler( + block.exceptionHandler, + ); + _transformLastInstruction(block); + } + } + graph.discoverBlocks(); + } + + void _transformPhis(JoinBlock block) { + var inputCount = 0; + for (int i = 0, n = block.predecessors.length; i < n; ++i) { + if (_isReachable(block.predecessors[i])) { + if (inputCount < i) { + // Move inputs corresponding to the reachable predecessor. + for (final phi in block.phis) { + phi.removeInputFromUseList(i); + phi.setInputAt(inputCount, phi.inputDefAt(i)); + phi.addInputToUseList(inputCount); + } + } + ++inputCount; + } else { + // Remove inputs corresponding to the unreachable predecessor. + for (final phi in block.phis) { + phi.removeInputFromUseList(i); + } + } + } + if (inputCount < block.predecessors.length) { + // Adjust number of inputs. + for (final phi in block.phis) { + phi.truncateInputs(inputCount); + } + } + } + + CatchBlock? _transformExceptionHandler(CatchBlock? handler) { + while (handler != null && !_isReachable(handler)) { + handler = handler.exceptionHandler; + } + return handler; + } + + TargetBlock? _getSingleSuccessor(Block block) { + final last = block.lastInstruction; + switch (last) { + case Goto(): + assert(_isReachable(last.target)); + return null; + case Branch(:var condition): + if (condition is Constant) { + return condition.value.boolValue + ? last.trueSuccessor + : last.falseSuccessor; + } else { + assert(_isReachable(last.trueSuccessor)); + assert(_isReachable(last.falseSuccessor)); + return null; + } + case TryEntry(): + assert(_isReachable(last.tryBody)); + if (!_isReachable(last.catchBlock)) { + return last.tryBody; + } + return null; + case Return() || Throw(): + return null; + default: + throw 'Unexpected block end ${last.runtimeType}'; + } + } + + void _transformLastInstruction(Block block) { + TargetBlock? successor = _getSingleSuccessor(block); + if (successor == null) { + return; + } + assert(_isReachable(successor)); + // Replace last instruction with Goto. + final last = block.lastInstruction; + last.removeFromGraph(); + block.lastInstruction.appendInstruction(Goto(graph, last.sourcePosition)); + block.successors.clear(); + block.successors.add(successor); + } +} diff --git a/pkg/cfg/test/ir_test.dart b/pkg/cfg/test/ir_test.dart index 9565d90c1dc..e44f42bfb94 100644 --- a/pkg/cfg/test/ir_test.dart +++ b/pkg/cfg/test/ir_test.dart @@ -21,6 +21,7 @@ import 'package:cfg/front_end/recognized_methods.dart'; import 'package:cfg/ir/functions.dart'; import 'package:cfg/ir/ir_to_text.dart'; import 'package:cfg/ir/ssa_computation.dart'; +import 'package:cfg/passes/constant_propagation.dart'; import 'package:cfg/passes/pass.dart'; import 'package:cfg/passes/simplification.dart'; import 'package:cfg/passes/value_numbering.dart'; @@ -115,6 +116,7 @@ class CompileAndDumpIr extends RecursiveVisitor { final pipeline = Pipeline([ SSAComputation(), ValueNumbering(simplification: Simplification()), + ConstantPropagation(), ]); pipeline.run(graph); buffer.writeln('--- ${node.name}'); diff --git a/pkg/cfg/testcases/constant_propagation.dart b/pkg/cfg/testcases/constant_propagation.dart new file mode 100644 index 00000000000..9cafe88e3e1 --- /dev/null +++ b/pkg/cfg/testcases/constant_propagation.dart @@ -0,0 +1,63 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +int test1(int x) { + int y; + if (x == x) { + if (x + 1 == x + 1) { + y = 1; + } else { + y = 3; + } + } else { + y = 5; + } + return y + 1; +} + +void test2() { + var x = 0; + for (;;) { + if (x == 0) break; + x = x + 1; + } +} + +void test3(int x, int y) { + if (true) { + x = 10; + } + var z = y; + if (x > 20) { + z = y + 10; + } + try { + ++z; + if (x > 30) { + print('I can throw'); + } + } catch (_) { + print(z); + } +} + +void test4(int x, int z) { + if (true) { + x = 10; + } + int y; + switch (x) { + case 1: + y = 10; + case 2: + y = 20; + case 10: + y = z; + default: + y = -1; + } + print(y); +} + +void main() {} diff --git a/pkg/cfg/testcases/constant_propagation.dart.expect b/pkg/cfg/testcases/constant_propagation.dart.expect new file mode 100644 index 00000000000..2a88db63397 --- /dev/null +++ b/pkg/cfg/testcases/constant_propagation.dart.expect @@ -0,0 +1,98 @@ +--- test1 +B0 = EntryBlock() dominates:(B5) + v9 = Constant(1) + Constant(3) + Constant(5) + Constant(true) + v34 = Constant(2) + v1 = Parameter(x) + Goto(B5) +B5 = TargetBlock() idom:B0 dominates:(B14) + BinaryIntOp +(v1, v9) + Goto(B14) +B14 = TargetBlock() idom:B5 dominates:(B18) + Goto(B18) +B18 = JoinBlock(B14) idom:B14 dominates:(B23) + Goto(B23) +B23 = JoinBlock(B18) idom:B18 + Return(v34) + +--- test2 +B0 = EntryBlock() dominates:(B3) + Constant(0) + Constant(1) + v19 = Constant(null) + Constant(true) + Goto(B3) +B3 = JoinBlock(B0) idom:B0 dominates:(B7) + Goto(B7) +B7 = TargetBlock() idom:B3 dominates:(B12) + Goto(B12) +B12 = JoinBlock(B7) idom:B7 + Return(v19) + +--- test3 +B0 = EntryBlock() dominates:(B4) + Constant(true) + Constant(10) + Constant(20) + v30 = Constant(1) + Constant(30) + Constant("I can throw") + v53 = Constant(null) + Constant(false) + Parameter(x) + v2 = Parameter(y) + Goto(B4) +B4 = TargetBlock() idom:B0 dominates:(B7) + Goto(B7) +B7 = JoinBlock(B4) idom:B4 dominates:(B18) + Goto(B18) +B18 = TargetBlock() idom:B7 dominates:(B20) + Goto(B20) +B20 = JoinBlock(B18) idom:B18 dominates:(B26) + Goto(B26) +B26 = TargetBlock() idom:B20 dominates:(B37) + BinaryIntOp +(v2, v30) + Goto(B37) +B37 = TargetBlock() idom:B26 dominates:(B39) + Goto(B39) +B39 = JoinBlock(B37) idom:B37 dominates:(B44) + Goto(B44) +B44 = JoinBlock(B39) idom:B39 + Return(v53) + +--- test4 +B0 = EntryBlock() dominates:(B4) + Constant(true) + Constant(10) + Constant(1) + Constant(2) + Constant(20) + v49 = Constant(null) + Constant(-1) + Constant(false) + Parameter(x) + v2 = Parameter(z) + Goto(B4) +B4 = TargetBlock() idom:B0 dominates:(B7) + Goto(B7) +B7 = JoinBlock(B4) idom:B4 dominates:(B20) + Goto(B20) +B20 = TargetBlock() idom:B7 dominates:(B26) + Goto(B26) +B26 = TargetBlock() idom:B20 dominates:(B30) + Goto(B30) +B30 = TargetBlock() idom:B26 dominates:(B15) + Goto(B15) +B15 = JoinBlock(B30) idom:B30 dominates:(B36) + Goto(B36) +B36 = JoinBlock(B15) idom:B15 + DirectCall print(v2) + Return(v49) + +--- main +B0 = EntryBlock() + v1 = Constant(null) + Return(v1) + diff --git a/pkg/cfg/testcases/loops.dart.expect b/pkg/cfg/testcases/loops.dart.expect index 98c454f6557..ca370d92833 100644 --- a/pkg/cfg/testcases/loops.dart.expect +++ b/pkg/cfg/testcases/loops.dart.expect @@ -84,12 +84,12 @@ B0 = EntryBlock() dominates:(B8, B34, B4, B3, B7) v27 = Constant(10) v38 = Constant(null) v1 = Parameter(i) - v6 = InterfaceCall Object.==(v5, v1) + v6 = Comparison int ==(v1, v5) Branch(v6, true: B7, false: B8) B7 = TargetBlock() idom:B0 Goto(B3) B8 = TargetBlock() idom:B0 dominates:(B14, B13) - v12 = InterfaceCall Object.==(v11, v1) + v12 = Comparison int ==(v1, v11) Branch(v12, true: B13, false: B14) B13 = TargetBlock() idom:B8 Goto(B4) diff --git a/pkg/cfg/testcases/statements.dart.expect b/pkg/cfg/testcases/statements.dart.expect index d76bf1247fe..f8577c5f351 100644 --- a/pkg/cfg/testcases/statements.dart.expect +++ b/pkg/cfg/testcases/statements.dart.expect @@ -224,12 +224,12 @@ B0 = EntryBlock() dominates:(B9, B34, B3, B8) v39 = Constant("3-4") v43 = Constant(null) v1 = Parameter(x) - v7 = InterfaceCall Object.==(v6, v1) + v7 = Comparison int ==(v1, v6) Branch(v7, true: B8, false: B9) B8 = TargetBlock() idom:B0 Goto(B3) B9 = TargetBlock() idom:B0 dominates:(B15, B14) - v13 = InterfaceCall Object.==(v12, v1) + v13 = Comparison int ==(v1, v12) Branch(v13, true: B14, false: B15) B14 = TargetBlock() idom:B9 Goto(B3) @@ -237,7 +237,7 @@ B3 = JoinBlock(B14, B8) idom:B0 DirectCall print(v32) Goto(B34) B15 = TargetBlock() idom:B9 dominates:(B21, B30, B5, B20) - v19 = InterfaceCall Object.==(v18, v1) + v19 = Comparison int ==(v1, v18) Branch(v19, true: B20, false: B21) B20 = TargetBlock() idom:B15 dominates:(B4) Goto(B4) @@ -245,7 +245,7 @@ B4 = JoinBlock(B20) idom:B20 DirectCall print(v36) Goto(B5) B21 = TargetBlock() idom:B15 dominates:(B27, B26) - v25 = InterfaceCall Object.==(v24, v1) + v25 = Comparison int ==(v1, v24) Branch(v25, true: B26, false: B27) B26 = TargetBlock() idom:B21 Goto(B5) @@ -266,19 +266,13 @@ B0 = EntryBlock() dominates:(B4, B24, B3) v11 = Constant(3) v35 = Constant(null) TryEntry(try-body: B3, catch-block: B4) -B3 = TargetBlock() exception-handler:B4 idom:B0 dominates:(B9, B8) - TryEntry(try-body: B8, catch-block: B9) -B8 = TargetBlock() exception-handler:B9 idom:B3 dominates:(B13) +B3 = TargetBlock() exception-handler:B4 idom:B0 dominates:(B8) + Goto(B8) +B8 = TargetBlock() exception-handler:B4 idom:B3 dominates:(B13) Goto(B13) B13 = JoinBlock(B8) exception-handler:B4 idom:B8 DirectCall print(v11) Goto(B24) -B9 = CatchBlock() exception-handler:B4 idom:B3 - v37 = Parameter(x) - v15 = Parameter(#exception) - v16 = Parameter(#stackTrace) - DirectCall print(v37) - Throw(v15, v16) B4 = CatchBlock() idom:B0 v38 = Parameter(x) v26 = Parameter(#exception) @@ -293,75 +287,38 @@ B24 = JoinBlock(B4, B13) idom:B0 B0 = EntryBlock() dominates:(B2, B1) v4 = Constant(10) v6 = Constant(0) - v11 = Constant(5) - v22 = Constant(3) - v34 = Constant(1) - v45 = Constant(2) + Constant(5) + Constant(3) + Constant(1) + Constant(2) v59 = Constant(20) v84 = Constant(42) + Constant(true) TryEntry(try-body: B1, catch-block: B2) B1 = TargetBlock() exception-handler:B2 idom:B0 dominates:(B8) DirectCall print(v4) Goto(B8) -B8 = JoinBlock(B1, B68) exception-handler:B2 idom:B1 dominates:(B14, B65, B13) loop-header (depth:1 body:(B8, B13, B16, B26, B28, B37, B39, B48, B50, B55, B68, B36, B41) back-edges:(B68)) - v88 = Phi(v6, v76) - v12 = Comparison int <(v88, v11) - Branch(v12, true: B13, false: B14) -B13 = TargetBlock() exception-handler:B2 idom:B8 dominates:(B17, B16) in-loop:B8 +B8 = JoinBlock(B1) exception-handler:B2 idom:B1 dominates:(B13) + Goto(B13) +B13 = TargetBlock() exception-handler:B2 idom:B8 dominates:(B17, B16) TryEntry(try-body: B16, catch-block: B17) -B16 = TargetBlock() exception-handler:B17 idom:B13 dominates:(B26, B25) in-loop:B8 - DirectCall print(v88) - v23 = BinaryIntOp %(v88, v22) - v24 = Comparison int ==(v23, v6) - Branch(v24, true: B25, false: B26) +B16 = TargetBlock() exception-handler:B17 idom:B13 dominates:(B25) + DirectCall print(v6) + Goto(B25) B25 = TargetBlock() exception-handler:B17 idom:B16 dominates:(B30) Goto(B30) -B30 = JoinBlock(B25) exception-handler:B2 idom:B25 +B30 = JoinBlock(B25) exception-handler:B2 idom:B25 dominates:(B65) DirectCall print(v59) Goto(B65) -B26 = TargetBlock() exception-handler:B17 idom:B16 dominates:(B28) in-loop:B8 - Goto(B28) -B28 = JoinBlock(B26) exception-handler:B17 idom:B26 dominates:(B37, B68, B36) in-loop:B8 - v35 = Comparison int ==(v23, v34) - Branch(v35, true: B36, false: B37) -B36 = TargetBlock() exception-handler:B17 idom:B28 dominates:(B41) in-loop:B8 - Goto(B41) -B41 = JoinBlock(B36) exception-handler:B2 idom:B36 in-loop:B8 - DirectCall print(v59) - Goto(B68) -B37 = TargetBlock() exception-handler:B17 idom:B28 dominates:(B39) in-loop:B8 - Goto(B39) -B39 = JoinBlock(B37) exception-handler:B17 idom:B37 dominates:(B48, B47) in-loop:B8 - v46 = Comparison int ==(v23, v45) - Branch(v46, true: B47, false: B48) -B47 = TargetBlock() exception-handler:B17 idom:B39 dominates:(B53) - Goto(B53) -B53 = JoinBlock(B47) exception-handler:B2 idom:B47 dominates:(B71) - DirectCall print(v59) - Goto(B71) -B71 = JoinBlock(B53) idom:B53 +B65 = JoinBlock(B30) exception-handler:B2 idom:B30 dominates:(B80) + Goto(B80) +B80 = JoinBlock(B65) idom:B65 Return(v84) -B48 = TargetBlock() exception-handler:B17 idom:B39 dominates:(B50) in-loop:B8 - Goto(B50) -B50 = JoinBlock(B48) exception-handler:B17 idom:B48 dominates:(B55) in-loop:B8 - Goto(B55) -B55 = JoinBlock(B50) exception-handler:B2 idom:B50 in-loop:B8 - DirectCall print(v59) - Goto(B68) -B68 = JoinBlock(B55, B41) exception-handler:B2 idom:B28 in-loop:B8 - v76 = BinaryIntOp +(v88, v34) - Goto(B8) B17 = CatchBlock() exception-handler:B2 idom:B13 v57 = Parameter(#exception) v58 = Parameter(#stackTrace) DirectCall print(v59) Throw(v57, v58) -B14 = TargetBlock() exception-handler:B2 idom:B8 - Goto(B65) -B65 = JoinBlock(B14, B30) exception-handler:B2 idom:B8 dominates:(B80) - Goto(B80) -B80 = JoinBlock(B65) idom:B65 - Return(v84) B2 = CatchBlock() idom:B0 Parameter(#exception) Parameter(#stackTrace) diff --git a/pkg/cfg/testcases/unreachable_ast.dart.expect b/pkg/cfg/testcases/unreachable_ast.dart.expect index 2df6d006d2f..aa179d7ed03 100644 --- a/pkg/cfg/testcases/unreachable_ast.dart.expect +++ b/pkg/cfg/testcases/unreachable_ast.dart.expect @@ -64,18 +64,16 @@ B10 = TargetBlock() idom:B4 B0 = EntryBlock() dominates:(B4) v2 = Constant(0) Constant(2) - v20 = Constant("Bye") + Constant("Bye") v22 = Constant(null) - v25 = Constant(false) + Constant(false) v1 = Parameter(n) Goto(B4) B4 = JoinBlock(B0) idom:B0 dominates:(B10, B9) v8 = Comparison int >=(v1, v2) Branch(v8, true: B9, false: B10) -B9 = TargetBlock() idom:B4 dominates:(B16, B15) - Branch(v25, true: B15, false: B16) -B15 = TargetBlock() idom:B9 - Throw(v20) +B9 = TargetBlock() idom:B4 dominates:(B16) + Goto(B16) B16 = TargetBlock() idom:B9 dominates:(B18) Goto(B18) B18 = JoinBlock(B16) idom:B16 @@ -91,18 +89,13 @@ B0 = EntryBlock() dominates:(B2, B1) v20 = Constant(3) v22 = Constant("Bye-bye") TryEntry(try-body: B1, catch-block: B2) -B1 = TargetBlock() exception-handler:B2 idom:B0 dominates:(B5, B4) - TryEntry(try-body: B4, catch-block: B5) -B4 = TargetBlock() exception-handler:B5 idom:B1 dominates:(B7) +B1 = TargetBlock() exception-handler:B2 idom:B0 dominates:(B4) + Goto(B4) +B4 = TargetBlock() exception-handler:B2 idom:B1 dominates:(B7) Goto(B7) B7 = JoinBlock(B4) exception-handler:B2 idom:B4 DirectCall print(v11) Throw(v13) -B5 = CatchBlock() exception-handler:B2 idom:B1 - Parameter(#exception) - Parameter(#stackTrace) - DirectCall print(v11) - Throw(v13) B2 = CatchBlock() idom:B0 Parameter(#exception) Parameter(#stackTrace)