blob: fa6fd822bffe2d17a49eb8a999981b6007e944b0 [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/codegen.dart' show CodegenRegistry;
import '../compiler.dart';
import '../elements/elements.dart';
import '../elements/entities.dart' show Entity, Local;
import '../elements/resolution_types.dart';
import '../js_backend/js_backend.dart';
import '../resolution/tree_elements.dart';
import '../tree/tree.dart' as ast;
import '../types/types.dart';
import '../world.dart' show ClosedWorld;
import 'jump_handler.dart';
import 'locals_handler.dart';
import 'nodes.dart';
import 'type_builder.dart';
/// Base class for objects that build up an SSA graph.
/// This contains helpers for building the graph and tracking information about
/// the current state of the graph being built.
abstract class GraphBuilder {
/// Holds the resulting SSA graph.
final HGraph graph = new HGraph();
// TODO(het): remove this
/// A reference to the compiler.
Compiler compiler;
/// True if the builder is processing nodes inside a try statement. This is
/// important for generating control flow out of a try block like returns or
/// breaks.
bool inTryStatement = false;
/// The tree elements for the element being built into an SSA graph.
TreeElements get elements;
/// The JavaScript backend we are targeting in this compilation.
JavaScriptBackend get backend;
CodegenRegistry get registry;
ClosedWorld get closedWorld;
CommonMasks get commonMasks => closedWorld.commonMasks;
GlobalTypeInferenceResults get globalInferenceResults =>
/// Used to track the locals while building the graph.
LocalsHandler localsHandler;
/// A stack of instructions.
/// We build the SSA graph by simulating a stack machine.
List<HInstruction> stack = <HInstruction>[];
/// The count of nested loops we are currently building.
/// The loop nesting is consulted when inlining a function invocation. The
/// inlining heuristics take this information into account.
int loopDepth = 0;
/// A mapping from jump targets to their handlers.
Map<JumpTarget, JumpHandler> jumpTargets = <JumpTarget, JumpHandler>{};
void push(HInstruction instruction) {
HInstruction pop() {
return stack.removeLast();
/// Pops the most recent instruction from the stack and 'boolifies' it.
/// Boolification is checking if the value is '=== true'.
HInstruction popBoolified();
/// Pushes a boolean checking [expression] against null.
pushCheckNull(HInstruction expression) {
push(new HIdentity(expression, graph.addConstantNull(closedWorld), null,
void dup() {
HBasicBlock _current;
/// The current block to add instructions to. Might be null, if we are
/// visiting dead code, but see [isReachable].
HBasicBlock get current => _current;
void set current(c) {
isReachable = c != null;
_current = c;
/// The most recently opened block. Has the same value as [current] while
/// the block is open, but unlike [current], it isn't cleared when the
/// current block is closed.
HBasicBlock lastOpenedBlock;
/// Indicates whether the current block is dead (because it has a throw or a
/// return further up). If this is false, then [current] may be null. If the
/// block is dead then it may also be aborted, but for simplicity we only
/// abort on statement boundaries, not in the middle of expressions. See
/// [isAborted].
bool isReachable = true;
HParameterValue lastAddedParameter;
Map<ParameterElement, HInstruction> parameters =
<ParameterElement, HInstruction>{};
HBasicBlock addNewBlock() {
HBasicBlock block = graph.addNewBlock();
// If adding a new block during building of an expression, it is due to
// conditional expressions or short-circuit logical operators.
return block;
void open(HBasicBlock block) {;
current = block;
lastOpenedBlock = block;
HBasicBlock close(HControlFlow end) {
HBasicBlock result = current;
current = null;
return result;
HBasicBlock closeAndGotoExit(HControlFlow end) {
HBasicBlock result = current;
current = null;
return result;
void goto(HBasicBlock from, HBasicBlock to) {
from.close(new HGoto());
bool isAborted() {
return current == null;
/// Creates a new block, transitions to it from any current block, and
/// opens the new block.
HBasicBlock openNewBlock() {
HBasicBlock newBlock = addNewBlock();
if (!isAborted()) goto(current, newBlock);
return newBlock;
void add(HInstruction instruction) {
HParameterValue addParameter(Entity parameter, TypeMask type) {
HParameterValue result = new HParameterValue(parameter, type);
if (lastAddedParameter == null) {
graph.entry.addBefore(graph.entry.first, result);
} else {
graph.entry.addAfter(lastAddedParameter, result);
lastAddedParameter = result;
return result;
HSubGraphBlockInformation wrapStatementGraph(SubGraph statements) {
if (statements == null) return null;
return new HSubGraphBlockInformation(statements);
HSubExpressionBlockInformation wrapExpressionGraph(SubExpression expression) {
if (expression == null) return null;
return new HSubExpressionBlockInformation(expression);
/// Returns the current source element.
/// The returned element is a declaration element.
Element get sourceElement;
// TODO(karlklose): this is needed to avoid a bug where the resolved type is
// not stored on a type annotation in the closure translator. Remove when
// fixed.
bool hasDirectLocal(Local local) {
return !localsHandler.isAccessedDirectly(local) ||
localsHandler.directLocals[local] != null;
HInstruction callSetRuntimeTypeInfoWithTypeArguments(ResolutionDartType type,
List<HInstruction> rtiInputs, HInstruction newObject) {
if (!backend.rtiNeed.classNeedsRti(type.element)) {
return newObject;
HInstruction typeInfo = new HTypeInfoExpression(
(type.element as ClassElement).thisType,
return callSetRuntimeTypeInfo(typeInfo, newObject);
/// Called when control flow is about to change, in which case we need to
/// specify special successors if we are already in a try/catch/finally block.
void handleInTryStatement() {
if (!inTryStatement) return;
HBasicBlock block = close(new HExitTry());
HBasicBlock newBlock = graph.addNewBlock();
HInstruction callSetRuntimeTypeInfo(
HInstruction typeInfo, HInstruction newObject);
/// The element for which this SSA builder is being used.
Element get targetElement;
TypeBuilder get typeBuilder;