blob: 78d0ccd2b42f8ce5b44e8df54a5c2f9ca6796199 [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 '../closure.dart' show ClosureDataLookup;
import '../constants/constant_system.dart';
import '../common/codegen.dart' show CodegenRegistry;
import '../common_elements.dart';
import '../compiler.dart';
import '../deferred_load.dart';
import '../diagnostics/diagnostic_listener.dart';
import '../elements/entities.dart' show Entity, Local, MemberEntity;
import '../elements/jumps.dart';
import '../elements/types.dart';
import '../io/source_information.dart';
import '../js_backend/backend.dart';
import '../js_backend/backend_usage.dart';
import '../js_backend/constant_handler_javascript.dart';
import '../js_backend/interceptor_data.dart';
import '../js_backend/inferred_data.dart';
import '../js_backend/js_interop_analysis.dart';
import '../js_backend/namer.dart';
import '../js_backend/native_data.dart';
import '../js_backend/runtime_types.dart';
import '../js_emitter/code_emitter_task.dart';
import '../options.dart';
import '../types/abstract_value_domain.dart';
import '../types/types.dart';
import '../world.dart' show JClosedWorld;
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 get 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 JavaScript backend we are targeting in this compilation.
JavaScriptBackend get backend;
CodegenRegistry get registry;
JClosedWorld get closedWorld;
AbstractValueDomain get abstractValueDomain =>
closedWorld.abstractValueDomain;
DiagnosticReporter get reporter => backend.reporter;
CompilerOptions get options => compiler.options;
CommonElements get commonElements => closedWorld.commonElements;
CodeEmitterTask get emitter => backend.emitter;
GlobalTypeInferenceResults get globalInferenceResults =>
compiler.globalInference.results;
ClosureDataLookup get closureDataLookup =>
compiler.backendStrategy.closureDataLookup;
NativeData get nativeData => closedWorld.nativeData;
InterceptorData get interceptorData => closedWorld.interceptorData;
BackendUsage get backendUsage => closedWorld.backendUsage;
Namer get namer => backend.namer;
RuntimeTypesNeed get rtiNeed => closedWorld.rtiNeed;
JavaScriptConstantCompiler get constants => backend.constants;
ConstantSystem get constantSystem => constants.constantSystem;
RuntimeTypesEncoder get rtiEncoder => backend.rtiEncoder;
FunctionInlineCache get inlineCache => backend.inlineCache;
JsInteropAnalysis get jsInteropAnalysis => backend.jsInteropAnalysis;
InferredData get inferredData => compiler.globalInference.inferredData;
DeferredLoadTask get deferredLoadTask => compiler.deferredLoadTask;
DartTypes get types => closedWorld.dartTypes;
/// 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) {
add(instruction);
stack.add(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,
abstractValueDomain.boolType));
}
void dup() {
stack.add(stack.last);
}
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<Local, HInstruction> parameters = <Local, 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) {
block.open();
current = block;
lastOpenedBlock = block;
}
HBasicBlock close(HControlFlow end) {
HBasicBlock result = current;
current.close(end);
current = null;
return result;
}
HBasicBlock closeAndGotoExit(HControlFlow end) {
HBasicBlock result = current;
current.close(end);
current = null;
result.addSuccessor(graph.exit);
return result;
}
void goto(HBasicBlock from, HBasicBlock to) {
from.close(new HGoto(abstractValueDomain));
from.addSuccessor(to);
}
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);
open(newBlock);
return newBlock;
}
void add(HInstruction instruction) {
current.add(instruction);
}
HParameterValue addParameter(Entity parameter, AbstractValue 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.
MemberEntity get sourceElement;
HLiteralList buildLiteralList(List<HInstruction> inputs) {
return new HLiteralList(inputs, abstractValueDomain.growableListType);
}
HInstruction callSetRuntimeTypeInfoWithTypeArguments(
InterfaceType type,
List<HInstruction> rtiInputs,
HInstruction newObject,
SourceInformation sourceInformation) {
if (!rtiNeed.classNeedsTypeArguments(type.element)) {
return newObject;
}
HInstruction typeInfo = new HTypeInfoExpression(
TypeInfoExpressionKind.INSTANCE,
closedWorld.elementEnvironment.getThisType(type.element),
rtiInputs,
abstractValueDomain.dynamicType);
add(typeInfo);
return callSetRuntimeTypeInfo(typeInfo, newObject, sourceInformation);
}
/// 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(abstractValueDomain));
HBasicBlock newBlock = graph.addNewBlock();
block.addSuccessor(newBlock);
open(newBlock);
}
HInstruction callSetRuntimeTypeInfo(HInstruction typeInfo,
HInstruction newObject, SourceInformation sourceInformation);
/// The element for which this SSA builder is being used.
MemberEntity get targetElement;
TypeBuilder get typeBuilder;
/// Helper to implement JS_GET_FLAG.
///
/// The concrete SSA graph builder will extract a flag parameter from the
/// JS_GET_FLAG call and then push a boolean result onto the stack. This
/// function provides the boolean value corresponding to the given [flagName].
/// If [flagName] is not recognized, this function returns `null` and the
/// concrete SSA builder reports an error.
bool getFlagValue(String flagName) {
switch (flagName) {
case 'MUST_RETAIN_METADATA':
return false;
case 'USE_CONTENT_SECURITY_POLICY':
return options.useContentSecurityPolicy;
case 'IS_FULL_EMITTER':
return !options.useStartupEmitter;
case 'STRONG_MODE':
return options.strongMode;
default:
return null;
}
}
}