blob: 9ed2829ec069fccdb3105d29a79e8f5b79eb2b1e [file] [log] [blame]
// Copyright (c) 2014, 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.
/// Generate code using the cps-based IR pipeline.
library code_generator_task;
import 'glue.dart';
import 'codegen.dart';
import 'unsugar.dart';
import '../js_backend.dart';
import '../../constants/constant_system.dart';
import '../../dart2jslib.dart';
import '../../cps_ir/cps_ir_nodes.dart' as cps;
import '../../cps_ir/cps_ir_integrity.dart';
import '../../cps_ir/cps_ir_builder_task.dart';
import '../../tree_ir/tree_ir_nodes.dart' as tree_ir;
import '../../types/types.dart' show TypeMask, UnionTypeMask, FlatTypeMask,
ForwardingTypeMask;
import '../../elements/elements.dart';
import '../../js/js.dart' as js;
import '../../io/source_information.dart' show SourceInformationStrategy;
import '../../tree_ir/tree_ir_builder.dart' as tree_builder;
import '../../cps_ir/optimizers.dart';
import '../../cps_ir/optimizers.dart' as cps_opt;
import '../../tracer.dart';
import '../../js_backend/codegen/codegen.dart';
import '../../ssa/ssa.dart' as ssa;
import '../../tree_ir/optimization/optimization.dart';
import '../../tree_ir/optimization/optimization.dart' as tree_opt;
import '../../tree_ir/tree_ir_integrity.dart';
import '../../cps_ir/cps_ir_nodes_sexpr.dart';
class CpsFunctionCompiler implements FunctionCompiler {
final ConstantSystem constantSystem;
final Compiler compiler;
final Glue glue;
final SourceInformationStrategy sourceInformationFactory;
// TODO(karlklose,sigurm): remove and update dart-doc of [compile].
final FunctionCompiler fallbackCompiler;
Tracer get tracer => compiler.tracer;
IrBuilderTask get irBuilderTask => compiler.irBuilder;
CpsFunctionCompiler(Compiler compiler, JavaScriptBackend backend,
SourceInformationStrategy sourceInformationFactory)
: fallbackCompiler =
new ssa.SsaFunctionCompiler(backend, sourceInformationFactory),
this.sourceInformationFactory = sourceInformationFactory,
constantSystem = backend.constantSystem,
compiler = compiler,
glue = new Glue(compiler);
String get name => 'CPS Ir pipeline';
/// Generates JavaScript code for `work.element`.
js.Fun compile(CodegenWorkItem work) {
AstElement element = work.element;
JavaScriptBackend backend = compiler.backend;
return compiler.withCurrentElement(element, () {
try {
ClassElement cls = element.enclosingClass;
String name = element.name;
String className = cls == null ? null : cls.name;
LibraryElement library = element.library;
String libraryName = library == null ? null : library.toString();
// TODO(karlklose): remove this fallback.
// Fallback for a few functions that we know require try-finally and
// switch.
if (element.isNative ||
element.isPatched ||
libraryName == 'origin library(dart:typed_data)' ||
// Using switch or try-finally.
library.isInternalLibrary && name == 'unwrapException' ||
library.isPlatformLibrary && className == 'IterableBase' ||
library.isInternalLibrary && className == 'Closure' ||
libraryName == 'origin library(dart:collection)' &&
name == 'mapToString' ||
libraryName == 'library(dart:html)' && name == 'sanitizeNode' ||
className == '_IsolateContext' ||
className == 'IsolateNatives' ||
className == '_Deserializer' ||
name == '_rootRun' ||
name == '_microtaskLoopEntry') {
compiler.log('Using SSA compiler for platform element $element');
return fallbackCompiler.compile(work);
}
if (tracer != null) {
tracer.traceCompilation(element.name, null);
}
cps.FunctionDefinition cpsFunction = compileToCpsIR(element);
cpsFunction = optimizeCpsIR(cpsFunction);
tree_ir.FunctionDefinition treeFunction = compileToTreeIR(cpsFunction);
treeFunction = optimizeTreeIR(treeFunction);
return compileToJavaScript(work, treeFunction);
} on CodegenBailout catch (e) {
String message = "Unable to compile $element with the new compiler.\n"
" Reason: ${e.message}";
compiler.internalError(element, message);
}
});
}
void giveUp(String reason) {
throw new CodegenBailout(null, reason);
}
void traceGraph(String title, var irObject) {
if (tracer != null) {
tracer.traceGraph(title, irObject);
}
}
cps.FunctionDefinition compileToCpsIR(AstElement element) {
// TODO(sigurdm): Support these constructs.
if (element.isNative) {
giveUp('unsupported element kind: ${element.name}:${element.kind}');
}
cps.FunctionDefinition cpsNode = irBuilderTask.buildNode(element);
if (cpsNode == null) {
if (irBuilderTask.bailoutMessage == null) {
giveUp('unable to build cps definition of $element');
} else {
giveUp(irBuilderTask.bailoutMessage);
}
}
traceGraph("IR Builder", cpsNode);
new UnsugarVisitor(glue).rewrite(cpsNode);
traceGraph("Unsugaring", cpsNode);
return cpsNode;
}
static const Pattern PRINT_TYPED_IR_FILTER = null;
String formatTypeMask(TypeMask type) {
if (type is UnionTypeMask) {
return '[${type.disjointMasks.map(formatTypeMask).join(', ')}]';
} else if (type is FlatTypeMask) {
if (type.isEmpty) {
return "null";
}
String suffix = (type.isExact ? "" : "+") + (type.isNullable ? "?" : "!");
return '${type.base.name}$suffix';
} else if (type is ForwardingTypeMask) {
return formatTypeMask(type.forwardTo);
}
throw 'unsupported: $type';
}
void dumpTypedIR(cps.FunctionDefinition cpsNode,
TypePropagator typePropagator) {
if (PRINT_TYPED_IR_FILTER != null &&
PRINT_TYPED_IR_FILTER.matchAsPrefix(cpsNode.element.name) != null) {
String printType(nodeOrRef, String s) {
cps.Node node = nodeOrRef is cps.Reference
? nodeOrRef.definition
: nodeOrRef;
var type = typePropagator.getType(node);
return type == null ? s : "$s:${formatTypeMask(type.type)}";
}
DEBUG_MODE = true;
print(new SExpressionStringifier(printType).visit(cpsNode));
}
}
static bool checkCpsIntegrity(cps.FunctionDefinition node) {
new CheckCpsIntegrity().check(node);
return true; // So this can be used from assert().
}
cps.FunctionDefinition optimizeCpsIR(cps.FunctionDefinition cpsNode) {
// Transformations on the CPS IR.
void applyCpsPass(cps_opt.Pass pass) {
pass.rewrite(cpsNode);
traceGraph(pass.passName, cpsNode);
assert(checkCpsIntegrity(cpsNode));
}
TypePropagator typePropagator = new TypePropagator(compiler);
applyCpsPass(typePropagator);
dumpTypedIR(cpsNode, typePropagator);
applyCpsPass(new RedundantPhiEliminator());
applyCpsPass(new ShrinkingReducer());
return cpsNode;
}
tree_ir.FunctionDefinition compileToTreeIR(cps.FunctionDefinition cpsNode) {
tree_builder.Builder builder = new tree_builder.Builder(
compiler.internalError);
tree_ir.FunctionDefinition treeNode = builder.buildFunction(cpsNode);
assert(treeNode != null);
traceGraph('Tree builder', treeNode);
assert(checkTreeIntegrity(treeNode));
return treeNode;
}
static bool checkTreeIntegrity(tree_ir.FunctionDefinition node) {
new CheckTreeIntegrity().check(node);
return true; // So this can be used from assert().
}
tree_ir.FunctionDefinition optimizeTreeIR(tree_ir.FunctionDefinition node) {
void applyTreePass(tree_opt.Pass pass) {
pass.rewrite(node);
traceGraph(pass.passName, node);
assert(checkTreeIntegrity(node));
}
applyTreePass(new StatementRewriter());
applyTreePass(new VariableMerger());
applyTreePass(new LoopRewriter());
applyTreePass(new LogicalRewriter());
applyTreePass(new PullIntoInitializers());
return node;
}
js.Fun compileToJavaScript(CodegenWorkItem work,
tree_ir.FunctionDefinition definition) {
CodeGenerator codeGen = new CodeGenerator(glue, work.registry);
return attachPosition(codeGen.buildFunction(definition), work.element);
}
Iterable<CompilerTask> get tasks {
// TODO(sigurdm): Make a better list of tasks.
return <CompilerTask>[irBuilderTask]..addAll(fallbackCompiler.tasks);
}
js.Node attachPosition(js.Node node, AstElement element) {
return node.withSourceInformation(
sourceInformationFactory.createBuilderForContext(element)
.buildDeclaration(element));
}
}