blob: 965ac58e187843d28fd11123d886d8d2d452a52d [file] [log] [blame]
// Copyright (c) 2016, 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 '../common.dart';
import '../inferrer/abstract_value_domain.dart';
import '../io/source_information.dart';
import 'builder.dart';
import 'locals_handler.dart';
import 'nodes.dart';
class SsaBranch {
final SsaBranchBuilder branchBuilder;
final HBasicBlock block;
LocalsHandler? startLocals;
LocalsHandler? exitLocals;
SubGraph? graph;
SsaBranch(this.branchBuilder) : block = HBasicBlock();
}
class SsaBranchBuilder {
final KernelSsaGraphBuilder builder;
final Spannable? diagnosticNode;
SsaBranchBuilder(this.builder, [this.diagnosticNode]);
AbstractValueDomain get _abstractValueDomain =>
builder.closedWorld.abstractValueDomain;
void checkNotAborted() {
if (builder.isAborted()) {
failedAt(diagnosticNode!, "aborted control flow");
}
}
void buildCondition(
void Function() visitCondition,
SsaBranch conditionBranch,
SsaBranch thenBranch,
SsaBranch elseBranch,
SourceInformation? sourceInformation,
) {
startBranch(conditionBranch);
visitCondition();
checkNotAborted();
assert(identical(builder.current, builder.lastOpenedBlock));
HInstruction conditionValue = builder.pop();
HIf branch = HIf(conditionValue)..sourceInformation = sourceInformation;
HBasicBlock conditionExitBlock = builder.current!;
builder.close(branch);
conditionBranch.exitLocals = builder.localsHandler;
conditionExitBlock.addSuccessor(thenBranch.block);
conditionExitBlock.addSuccessor(elseBranch.block);
bool conditionBranchLocalsCanBeReused = mergeLocals(
conditionBranch,
thenBranch,
mayReuseFromLocals: true,
);
mergeLocals(
conditionBranch,
elseBranch,
mayReuseFromLocals: conditionBranchLocalsCanBeReused,
);
conditionBranch.graph = SubExpression(
conditionBranch.block,
conditionExitBlock,
);
}
/// Returns true if the locals of the [fromBranch] may be reused. A [:true:]
/// return value implies that [mayReuseFromLocals] was set to [:true:].
bool mergeLocals(
SsaBranch fromBranch,
SsaBranch toBranch, {
required bool mayReuseFromLocals,
}) {
LocalsHandler fromLocals = fromBranch.exitLocals!;
if (toBranch.startLocals == null) {
if (mayReuseFromLocals) {
toBranch.startLocals = fromLocals;
return false;
} else {
toBranch.startLocals = LocalsHandler.from(fromLocals);
return true;
}
} else {
toBranch.startLocals!.mergeWith(fromLocals, toBranch.block);
return true;
}
}
void startBranch(SsaBranch branch) {
builder.graph.addBlock(branch.block);
builder.localsHandler = branch.startLocals!;
builder.open(branch.block);
}
HInstruction? buildBranch(
SsaBranch branch,
void Function() visitBranch,
SsaBranch joinBranch,
bool isExpression,
) {
startBranch(branch);
visitBranch();
branch.graph = SubGraph(branch.block, builder.lastOpenedBlock);
branch.exitLocals = builder.localsHandler;
if (!builder.isAborted()) {
builder.goto(builder.current!, joinBranch.block);
mergeLocals(branch, joinBranch, mayReuseFromLocals: true);
}
if (isExpression) {
checkNotAborted();
return builder.pop();
}
return null;
}
void handleIf(
void Function() visitCondition,
void Function() visitThen,
void Function()? visitElse, {
SourceInformation? sourceInformation,
}) {
visitElse ??= () {};
_handleDiamondBranch(
visitCondition,
visitThen,
visitElse,
isExpression: false,
sourceInformation: sourceInformation,
);
}
void handleConditional(
void Function() visitCondition,
void Function() visitThen,
void Function() visitElse,
) {
_handleDiamondBranch(
visitCondition,
visitThen,
visitElse,
isExpression: true,
);
}
void handleIfNull(void Function() left, void Function() right) {
// x ?? y is transformed into: x == null ? y : x
late final HInstruction leftExpression;
handleConditional(
() {
left();
leftExpression = builder.pop();
builder.pushCheckNull(leftExpression);
},
right,
() => builder.stack.add(leftExpression),
);
}
/// Creates the graph for '&&' or '||' operators.
///
/// x && y is transformed into:
///
/// t0 = boolify(x);
/// if (t0) {
/// t1 = boolify(y);
/// }
/// result = phi(t1, false);
///
/// x || y is transformed into:
///
/// t0 = boolify(x);
/// if (not(t0)) {
/// t1 = boolify(y);
/// }
/// result = phi(t1, true);
void handleLogicalBinary(
void Function() left,
void Function() right,
SourceInformation? sourceInformation, {
required bool isAnd,
}) {
late HInstruction boolifiedLeft;
late HInstruction boolifiedRight;
void visitCondition() {
left();
boolifiedLeft = builder.pop();
builder.stack.add(boolifiedLeft);
if (!isAnd) {
builder.push(
HNot(builder.pop(), _abstractValueDomain.boolType)
..sourceInformation = sourceInformation,
);
}
}
void visitThen() {
right();
boolifiedRight = builder.pop();
}
handleIf(
visitCondition,
visitThen,
null,
sourceInformation: sourceInformation,
);
HConstant notIsAnd = builder.graph.addConstantBool(
!isAnd,
builder.closedWorld,
);
HPhi result = HPhi.manyInputs(null, [
boolifiedRight,
notIsAnd,
], _abstractValueDomain.dynamicType)..sourceInformation = sourceInformation;
builder.current!.addPhi(result);
builder.stack.add(result);
}
void _handleDiamondBranch(
void Function() visitCondition,
void Function() visitThen,
void Function() visitElse, {
required bool isExpression,
SourceInformation? sourceInformation,
}) {
SsaBranch conditionBranch = SsaBranch(this);
SsaBranch thenBranch = SsaBranch(this);
SsaBranch elseBranch = SsaBranch(this);
SsaBranch joinBranch = SsaBranch(this);
conditionBranch.startLocals = builder.localsHandler;
builder.goto(builder.current!, conditionBranch.block);
buildCondition(
visitCondition,
conditionBranch,
thenBranch,
elseBranch,
sourceInformation,
);
final thenValue = buildBranch(
thenBranch,
visitThen,
joinBranch,
isExpression,
);
final elseValue = buildBranch(
elseBranch,
visitElse,
joinBranch,
isExpression,
);
if (isExpression) {
HPhi phi = HPhi.manyInputs(null, [
thenValue!,
elseValue!,
], _abstractValueDomain.dynamicType);
joinBranch.block.addPhi(phi);
builder.stack.add(phi);
}
HBasicBlock? joinBlock;
// If at least one branch did not abort, open the joinBranch.
if (joinBranch.block.predecessors.isNotEmpty) {
startBranch(joinBranch);
joinBlock = joinBranch.block;
}
HIfBlockInformation info = HIfBlockInformation(
HSubExpressionBlockInformation(conditionBranch.graph as SubExpression?),
HSubGraphBlockInformation(thenBranch.graph),
HSubGraphBlockInformation(elseBranch.graph),
);
HBasicBlock conditionStartBlock = conditionBranch.block;
conditionStartBlock.setBlockFlow(info, joinBlock);
final conditionGraph = conditionBranch.graph!;
final branch = conditionGraph.end.last as HIf;
branch.blockInformation = conditionStartBlock.blockFlow;
}
}