blob: 1ab568f4459928df650fdea4850517d2b597a65f [file] [log] [blame]
// Copyright (c) 2017, 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.
library dart2js.kernel.backend_strategy;
import 'package:kernel/ast.dart' as ir;
import '../backend_strategy.dart';
import '../closure.dart';
import '../common.dart';
import '../common/codegen.dart' show CodegenRegistry, CodegenWorkItem;
import '../common/tasks.dart';
import '../compiler.dart';
import '../elements/entities.dart';
import '../elements/entity_utils.dart' as utils;
import '../elements/jumps.dart';
import '../enqueue.dart';
import '../io/source_information.dart';
import '../js/js_source_mapping.dart';
import '../js_backend/backend.dart';
import '../js_backend/native_data.dart';
import '../js_emitter/sorter.dart';
import '../kernel/element_map.dart';
import '../kernel/element_map_impl.dart';
import '../native/behavior.dart';
import '../options.dart';
import '../ssa/builder_kernel.dart';
import '../ssa/nodes.dart';
import '../ssa/ssa.dart';
import '../ssa/types.dart';
import '../types/types.dart';
import '../universe/selector.dart';
import '../universe/world_builder.dart';
import '../universe/world_impact.dart';
import '../world.dart';
import 'closure.dart';
import 'element_map_impl.dart';
import 'kernel_strategy.dart';
/// A backend strategy based on Kernel IR nodes.
abstract class KernelBackendStrategy implements BackendStrategy {
KernelToElementMapForBuilding get elementMap;
GlobalLocalsMap get globalLocalsMapForTesting;
}
/// Backend strategy that uses the kernel elements as the backend model.
// TODO(redemption): Replace this with a strategy based on the J-element
// model.
class KernelBackendStrategyImpl implements KernelBackendStrategy {
final Compiler _compiler;
Sorter _sorter;
ClosureConversionTask _closureDataLookup;
final GlobalLocalsMap _globalLocalsMap = new GlobalLocalsMap();
KernelBackendStrategyImpl(this._compiler);
KernelToElementMapForBuilding get elementMap {
KernelFrontEndStrategy frontendStrategy = _compiler.frontendStrategy;
return frontendStrategy.elementMap;
}
GlobalLocalsMap get globalLocalsMapForTesting => _globalLocalsMap;
@override
ClosedWorldRefiner createClosedWorldRefiner(
covariant KernelClosedWorld closedWorld) {
return closedWorld;
}
@override
Sorter get sorter {
if (_sorter == null) {
_sorter = new KernelSorter(elementMap);
}
return _sorter;
}
@override
ClosureConversionTask get closureDataLookup =>
_closureDataLookup ??= new KernelClosureConversionTask(
_compiler.measurer, elementMap, _globalLocalsMap);
@override
WorkItemBuilder createCodegenWorkItemBuilder(ClosedWorld closedWorld) {
return new KernelCodegenWorkItemBuilder(_compiler.backend, closedWorld);
}
@override
CodegenWorldBuilder createCodegenWorldBuilder(
NativeBasicData nativeBasicData,
ClosedWorld closedWorld,
SelectorConstraintsStrategy selectorConstraintsStrategy) {
return new KernelCodegenWorldBuilder(
elementMap,
closedWorld.elementEnvironment,
nativeBasicData,
closedWorld,
selectorConstraintsStrategy);
}
@override
SsaBuilder createSsaBuilder(CompilerTask task, JavaScriptBackend backend,
SourceInformationStrategy sourceInformationStrategy) {
return new KernelSsaBuilder(
task, backend.compiler, elementMap, _globalLocalsMap);
}
@override
SourceInformationStrategy get sourceInformationStrategy =>
const JavaScriptSourceInformationStrategy();
}
class KernelCodegenWorkItemBuilder implements WorkItemBuilder {
final JavaScriptBackend _backend;
final ClosedWorld _closedWorld;
KernelCodegenWorkItemBuilder(this._backend, this._closedWorld);
CompilerOptions get _options => _backend.compiler.options;
@override
CodegenWorkItem createWorkItem(MemberEntity entity) {
// Codegen inlines field initializers. It only needs to generate
// code for checked setters.
if (entity.isField && entity.isInstanceMember) {
if (!_options.enableTypeAssertions || entity.enclosingClass.isClosure) {
return null;
}
}
return new KernelCodegenWorkItem(_backend, _closedWorld, entity);
}
}
class KernelCodegenWorkItem extends CodegenWorkItem {
final JavaScriptBackend _backend;
final ClosedWorld _closedWorld;
final MemberEntity element;
final CodegenRegistry registry;
KernelCodegenWorkItem(this._backend, this._closedWorld, this.element)
: registry =
new CodegenRegistry(_closedWorld.elementEnvironment, element);
@override
WorldImpact run() {
return _backend.codegen(this, _closedWorld);
}
}
/// Task for building SSA from kernel IR loaded from .dill.
class KernelSsaBuilder implements SsaBuilder {
final CompilerTask task;
final Compiler _compiler;
final KernelToElementMapForBuilding _elementMap;
final GlobalLocalsMap _globalLocalsMap;
KernelSsaBuilder(
this.task, this._compiler, this._elementMap, this._globalLocalsMap);
@override
HGraph build(CodegenWorkItem work, ClosedWorld closedWorld) {
KernelToLocalsMap localsMap = _globalLocalsMap.getLocalsMap(work.element);
KernelSsaGraphBuilder builder = new KernelSsaGraphBuilder(
work.element,
work.element.enclosingClass,
_elementMap.getMemberNode(work.element),
_compiler,
_elementMap,
new KernelToTypeInferenceMapImpl(closedWorld),
localsMap,
closedWorld,
_compiler.codegenWorldBuilder,
work.registry,
_compiler.backendStrategy.closureDataLookup,
// TODO(redemption): Support these:
const SourceInformationBuilder(),
null, // Function node used as capture scope id.
targetIsConstructorBody: false);
return builder.build();
}
}
class KernelToTypeInferenceMapImpl implements KernelToTypeInferenceMap {
final ClosedWorld _closedWorld;
KernelToTypeInferenceMapImpl(this._closedWorld);
@override
TypeMask typeFromNativeBehavior(
NativeBehavior nativeBehavior, ClosedWorld closedWorld) {
return TypeMaskFactory.fromNativeBehavior(nativeBehavior, closedWorld);
}
@override
TypeMask selectorTypeOf(Selector selector, TypeMask mask) {
return _closedWorld.commonMasks.dynamicType;
}
@override
TypeMask getInferredTypeOf(MemberEntity member) {
return _closedWorld.commonMasks.dynamicType;
}
@override
TypeMask getInferredTypeOfParameter(Local parameter) {
return _closedWorld.commonMasks.dynamicType;
}
@override
TypeMask inferredIndexType(ir.ForInStatement forInStatement) {
return _closedWorld.commonMasks.dynamicType;
}
@override
bool isJsIndexableIterator(
ir.ForInStatement forInStatement, ClosedWorld closedWorld) {
return false;
}
@override
bool isFixedLength(TypeMask mask, ClosedWorld closedWorld) {
return false;
}
@override
TypeMask typeOfIteratorMoveNext(ir.ForInStatement forInStatement) {
return _closedWorld.commonMasks.dynamicType;
}
@override
TypeMask typeOfIteratorCurrent(ir.ForInStatement forInStatement) {
return _closedWorld.commonMasks.dynamicType;
}
@override
TypeMask typeOfIterator(ir.ForInStatement forInStatement) {
return _closedWorld.commonMasks.dynamicType;
}
@override
TypeMask typeOfListLiteral(
MemberEntity owner, ir.ListLiteral listLiteral, ClosedWorld closedWorld) {
return _closedWorld.commonMasks.dynamicType;
}
@override
TypeMask typeOfSet(ir.PropertySet write, ClosedWorld closedWorld) {
return _closedWorld.commonMasks.dynamicType;
}
@override
TypeMask typeOfGet(ir.PropertyGet read) {
return _closedWorld.commonMasks.dynamicType;
}
@override
TypeMask typeOfInvocation(
ir.MethodInvocation invocation, ClosedWorld closedWorld) {
return _closedWorld.commonMasks.dynamicType;
}
@override
TypeMask getReturnTypeOf(FunctionEntity function) {
return _closedWorld.commonMasks.dynamicType;
}
}
class GlobalLocalsMap {
Map<MemberEntity, KernelToLocalsMap> _localsMaps =
<MemberEntity, KernelToLocalsMap>{};
KernelToLocalsMap getLocalsMap(MemberEntity member) {
return _localsMaps.putIfAbsent(
member, () => new KernelToLocalsMapImpl(member));
}
}
class KernelToLocalsMapImpl implements KernelToLocalsMap {
final List<MemberEntity> _members = <MemberEntity>[];
Map<ir.VariableDeclaration, KLocal> _map = <ir.VariableDeclaration, KLocal>{};
Map<ir.TreeNode, KJumpTarget> _jumpTargetMap;
Set<ir.BreakStatement> _breaksAsContinue;
MemberEntity get currentMember => _members.last;
// TODO(johnniwinther): Compute this eagerly from the root of the member.
void _ensureJumpMap(ir.TreeNode node) {
if (_jumpTargetMap == null) {
JumpVisitor visitor = new JumpVisitor(currentMember);
// Find the root node for the current member.
while (node is! ir.Member) {
node = node.parent;
}
node.accept(visitor);
_jumpTargetMap = visitor.jumpTargetMap;
_breaksAsContinue = visitor.breaksAsContinue;
}
}
KernelToLocalsMapImpl(MemberEntity member) {
_members.add(member);
}
@override
void enterInlinedMember(MemberEntity member) {
_members.add(member);
}
@override
void leaveInlinedMember(MemberEntity member) {
assert(member == currentMember);
_members.removeLast();
}
@override
JumpTarget getJumpTargetForBreak(ir.BreakStatement node) {
_ensureJumpMap(node.target);
JumpTarget target = _jumpTargetMap[node];
assert(target != null, failedAt(currentMember, 'No target for $node.'));
return target;
}
@override
bool generateContinueForBreak(ir.BreakStatement node) {
return _breaksAsContinue.contains(node);
}
@override
JumpTarget getJumpTargetForContinueSwitch(ir.ContinueSwitchStatement node) {
_ensureJumpMap(node.target);
throw new UnimplementedError(
'KernelToLocalsMapImpl.getJumpTargetForContinueSwitch');
}
@override
JumpTarget getJumpTargetForSwitchCase(ir.SwitchCase node) {
_ensureJumpMap(node);
throw new UnimplementedError(
'KernelToLocalsMapImpl.getJumpTargetForSwitchCase');
}
@override
JumpTarget getJumpTargetForDo(ir.DoStatement node) {
_ensureJumpMap(node);
return _jumpTargetMap[node.parent];
}
@override
JumpTarget getJumpTargetForLabel(ir.LabeledStatement node) {
_ensureJumpMap(node);
return _jumpTargetMap[node];
}
@override
JumpTarget getJumpTargetForSwitch(ir.SwitchStatement node) {
_ensureJumpMap(node);
throw new UnimplementedError(
'KernelToLocalsMapImpl.getJumpTargetForSwitch');
}
@override
JumpTarget getJumpTargetForFor(ir.ForStatement node) {
_ensureJumpMap(node);
return _jumpTargetMap[node];
}
@override
JumpTarget getJumpTargetForForIn(ir.ForInStatement node) {
_ensureJumpMap(node);
return _jumpTargetMap[node];
}
@override
JumpTarget getJumpTargetForWhile(ir.WhileStatement node) {
_ensureJumpMap(node);
return _jumpTargetMap[node];
}
@override
Local getLocal(ir.VariableDeclaration node) {
return _map.putIfAbsent(node, () {
return new KLocal(node.name, currentMember);
});
}
@override
LoopClosureScope getLoopClosureScope(
ClosureDataLookup closureLookup, ir.TreeNode node) {
return closureLookup.getLoopClosureScope(node);
}
}
class JumpVisitor extends ir.Visitor {
int index = 0;
final MemberEntity member;
final Map<ir.TreeNode, KJumpTarget> jumpTargetMap =
<ir.TreeNode, KJumpTarget>{};
final Set<ir.BreakStatement> breaksAsContinue = new Set<ir.BreakStatement>();
JumpVisitor(this.member);
KJumpTarget _getJumpTarget(ir.TreeNode node) {
return jumpTargetMap.putIfAbsent(node, () {
return new KJumpTarget(member, index++);
});
}
@override
defaultNode(ir.Node node) => node.visitChildren(this);
bool _canBeBreakTarget(ir.TreeNode node) {
// TODO(johnniwinther): Add more.
return node is ir.ForStatement ||
node is ir.ForInStatement ||
node is ir.WhileStatement;
}
bool _canBeContinueTarget(ir.TreeNode node) {
// TODO(johnniwinther): Add more.
return node is ir.ForStatement ||
node is ir.ForInStatement ||
node is ir.WhileStatement;
}
@override
visitBreakStatement(ir.BreakStatement node) {
// TODO(johnniwinther): Add labels if the enclosing loop is not the implicit
// break target.
KJumpTarget target;
ir.TreeNode body = node.target.body;
ir.TreeNode parent = node.target.parent;
if (_canBeBreakTarget(body)) {
// We have code like
//
// l1: for (int i = 0; i < 10; i++) {
// break l1:
// }
//
// and can therefore use the for loop as the break target.
target = _getJumpTarget(body);
target.isBreakTarget = true;
} else if (_canBeContinueTarget(parent)) {
// We have code like
//
// for (int i = 0; i < 10; i++) l1: {
// break l1:
// }
//
// and can therefore use the for loop as a continue target.
target = _getJumpTarget(parent);
target.isContinueTarget = true;
breaksAsContinue.add(node);
} else {
target = _getJumpTarget(node.target);
target.isBreakTarget = true;
}
jumpTargetMap[node] = target;
super.visitBreakStatement(node);
}
}
class KJumpTarget extends JumpTarget<ir.Node> {
final MemberEntity memberContext;
final int nestingLevel;
KJumpTarget(this.memberContext, this.nestingLevel);
bool isBreakTarget = false;
bool isContinueTarget = false;
bool isSwitch = false;
@override
Entity get executableContext => memberContext;
@override
LabelDefinition<ir.Node> addLabel(ir.Node label, String labelName,
{bool isBreakTarget: false}) {
throw new UnimplementedError('KJumpTarget.addLabel');
}
@override
List<LabelDefinition<ir.Node>> get labels {
return const <LabelDefinition<ir.Node>>[];
}
@override
ir.Node get statement {
throw new UnimplementedError('KJumpTarget.statement');
}
String toString() {
StringBuffer sb = new StringBuffer();
sb.write('KJumpTarget[');
sb.write('memberContext=');
sb.write(memberContext);
sb.write(',nestingLevel=');
sb.write(nestingLevel);
sb.write(',isBreakTarget=');
sb.write(isBreakTarget);
sb.write(',isContinueTarget=');
sb.write(isContinueTarget);
sb.write(']');
return sb.toString();
}
}
class KLocal implements Local {
final String name;
final MemberEntity memberContext;
KLocal(this.name, this.memberContext);
@override
Entity get executableContext => memberContext;
String toString() {
StringBuffer sb = new StringBuffer();
sb.write('local(');
if (memberContext.enclosingClass != null) {
sb.write(memberContext.enclosingClass.name);
sb.write('.');
}
sb.write(memberContext.name);
sb.write('#');
sb.write(name);
sb.write(')');
return sb.toString();
}
}
class KernelSorter implements Sorter {
final KernelToElementMapImpl elementMap;
KernelSorter(this.elementMap);
int _compareLibraries(LibraryEntity a, LibraryEntity b) {
return utils.compareLibrariesUris(a.canonicalUri, b.canonicalUri);
}
int _compareNodes(
Entity entity1, ir.TreeNode node1, Entity entity2, ir.TreeNode node2) {
ir.Location location1 = node1.location;
ir.Location location2 = node2.location;
int r = utils.compareSourceUris(
Uri.parse(location1.file), Uri.parse(location2.file));
if (r != 0) return r;
return utils.compareEntities(entity1, location1.line, location1.column,
entity2, location2.line, location2.column);
}
@override
Iterable<LibraryEntity> sortLibraries(Iterable<LibraryEntity> libraries) {
return libraries.toList()..sort(_compareLibraries);
}
@override
Iterable<MemberEntity> sortMembers(Iterable<MemberEntity> members) {
return members.toList()
..sort((MemberEntity a, MemberEntity b) {
int r = _compareLibraries(a.library, b.library);
if (r != 0) return r;
return _compareNodes(
a, elementMap.getMemberNode(a), b, elementMap.getMemberNode(b));
});
}
@override
Iterable<ClassEntity> sortClasses(Iterable<ClassEntity> classes) {
return classes.toList()
..sort((ClassEntity a, ClassEntity b) {
int r = _compareLibraries(a.library, b.library);
if (r != 0) return r;
return _compareNodes(
a, elementMap.getClassNode(a), b, elementMap.getClassNode(b));
});
}
}