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 <vegorov@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov
2025-12-04 08:26:41 -08:00
committed by Commit Queue
parent 02641c88d0
commit 45ec012c66
8 changed files with 789 additions and 89 deletions
+26 -8
View File
@@ -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();
@@ -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<void> {
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<Definition, ConstantValue> _constantValues = {};
late final BitVector _nonConstant = BitVector(graph.instructions.length);
// State transitions for phis:
// unknown -> redundant -> non-redundant
final Map<Phi, Definition> _redundantPhis = {};
// Work lists for blocks and definitions, appended on state transitions.
final _blockWorkList = <Block>[];
final _definitionsWorkList = <Definition>[];
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);
}
}
+2
View File
@@ -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}');
@@ -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() {}
@@ -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)
+2 -2
View File
@@ -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)
+22 -65
View File
@@ -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)
+7 -14
View File
@@ -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)