blob: 4797173f1917d339a90ab36005144cf899fd41c6 [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 '../../dart2jslib.dart';
import '../../io/source_file.dart';
import '../../cps_ir/cps_ir_nodes.dart' as cps;
import '../../cps_ir/cps_ir_builder.dart';
import '../../tree_ir/tree_ir_nodes.dart' as tree_ir;
import '../../tree/tree.dart' as ast;
import '../../types/types.dart' show TypeMask, UnionTypeMask, FlatTypeMask,
ForwardingTypeMask;
import '../../scanner/scannerlib.dart' as scanner;
import '../../elements/elements.dart';
import '../../js/js.dart' as js;
import '../../io/source_map_builder.dart';
import '../../tree_ir/tree_ir_builder.dart' as tree_builder;
import '../../dart_backend/backend_ast_emitter.dart' as backend_ast_emitter;
import '../../cps_ir/optimizers.dart';
import '../../tracer.dart';
import '../../js_backend/codegen/codegen.dart';
import '../../ssa/ssa.dart' as ssa;
import '../../tree_ir/optimization/optimization.dart';
import '../../cps_ir/cps_ir_nodes_sexpr.dart';
import 'js_tree_builder.dart';
class CpsFunctionCompiler implements FunctionCompiler {
final IrBuilderTask irBuilderTask;
final ConstantSystem constantSystem;
final Compiler compiler;
final Glue glue;
TypeSystem types;
// TODO(karlklose,sigurm): remove and update dart-doc of [compile].
final FunctionCompiler fallbackCompiler;
Tracer get tracer => compiler.tracer;
CpsFunctionCompiler(Compiler compiler, JavaScriptBackend backend)
: irBuilderTask = new IrBuilderTask(compiler),
fallbackCompiler = new ssa.SsaFunctionCompiler(backend, true),
constantSystem = backend.constantSystem,
compiler = compiler,
glue = new Glue(compiler);
String get name => 'CPS Ir pipeline';
/// Generates JavaScript code for `work.element`. First tries to use the
/// Cps Ir -> tree ir -> js pipeline, and if that fails due to language
/// features not implemented it will fall back to the ssa pipeline (for
/// platform code) or will cancel compilation (for user code).
js.Fun compile(CodegenWorkItem work) {
types = new TypeMaskSystem(compiler);
AstElement element = work.element;
JavaScriptBackend backend = compiler.backend;
return compiler.withCurrentElement(element, () {
if (element.library.isPlatformLibrary ||
element.library == backend.interceptorsLibrary) {
compiler.log('Using SSA compiler for platform element $element');
return fallbackCompiler.compile(work);
}
try {
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 ||
element.isField) {
giveUp('unsupported element kind: ${element.name}:${element.kind}');
}
cps.FunctionDefinition cpsNode = irBuilderTask.buildNode(element);
if (cpsNode == null) {
giveUp('unable to build cps definition of $element');
}
if (element.isInstanceMember && !element.isGenerativeConstructorBody) {
Selector selector = new Selector.fromElement(cpsNode.element);
if (glue.isInterceptedSelector(selector)) {
giveUp('cannot compile methods that need interceptor calling '
'convention.');
}
}
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';
}
cps.FunctionDefinition optimizeCpsIR(cps.FunctionDefinition cpsNode) {
// Transformations on the CPS IR.
TypePropagator typePropagator = new TypePropagator<TypeMask>(
compiler.types,
constantSystem,
new TypeMaskSystem(compiler),
compiler.internalError);
typePropagator.rewrite(cpsNode);
traceGraph("Sparse constant propagation", cpsNode);
if (PRINT_TYPED_IR_FILTER != null &&
PRINT_TYPED_IR_FILTER.matchAsPrefix(cpsNode.element.name) != null) {
String printType(cps.Node node, String s) {
var type = typePropagator.getType(node);
return type == null ? s : "$s:${formatTypeMask(type.type)}";
}
DEBUG_MODE = true;
print(new SExpressionStringifier(printType).visit(cpsNode));
}
new RedundantPhiEliminator().rewrite(cpsNode);
traceGraph("Redundant phi elimination", cpsNode);
new ShrinkingReducer().rewrite(cpsNode);
traceGraph("Shrinking reductions", cpsNode);
// Do not rewrite the IR after variable allocation. Allocation
// makes decisions based on an approximation of IR variable live
// ranges that can be invalidated by transforming the IR.
new cps.RegisterAllocator().visit(cpsNode);
return cpsNode;
}
tree_ir.FunctionDefinition compileToTreeIR(cps.FunctionDefinition cpsNode) {
tree_builder.Builder builder = new JsTreeBuilder(
compiler.internalError, compiler.identicalFunction, glue);
tree_ir.FunctionDefinition treeNode = builder.buildFunction(cpsNode);
assert(treeNode != null);
traceGraph('Tree builder', treeNode);
return treeNode;
}
tree_ir.FunctionDefinition optimizeTreeIR(
tree_ir.FunctionDefinition treeNode) {
// Transformations on the Tree IR.
new StatementRewriter().rewrite(treeNode);
traceGraph('Statement rewriter', treeNode);
new CopyPropagator().rewrite(treeNode);
traceGraph('Copy propagation', treeNode);
new LoopRewriter().rewrite(treeNode);
traceGraph('Loop rewriter', treeNode);
new LogicalRewriter().rewrite(treeNode);
traceGraph('Logical rewriter', treeNode);
new backend_ast_emitter.UnshadowParameters().unshadow(treeNode);
traceGraph('Unshadow parameters', treeNode);
return treeNode;
}
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) {
// TODO(sra): Attaching positions might be cleaner if the source position
// was on a wrapping node.
SourceFile sourceFile = sourceFileOfElement(element);
String name = element.name;
AstElement implementation = element.implementation;
ast.Node expression = implementation.node;
scanner.Token beginToken;
scanner.Token endToken;
if (expression == null) {
// Synthesized node. Use the enclosing element for the location.
beginToken = endToken = element.position;
} else {
beginToken = expression.getBeginToken();
endToken = expression.getEndToken();
}
// TODO(podivilov): find the right sourceFile here and remove offset
// checks below.
var sourcePosition, endSourcePosition;
if (beginToken.charOffset < sourceFile.length) {
sourcePosition =
new TokenSourceFileLocation(sourceFile, beginToken, name);
}
if (endToken.charOffset < sourceFile.length) {
endSourcePosition =
new TokenSourceFileLocation(sourceFile, endToken, name);
}
return node.withPosition(sourcePosition, endSourcePosition);
}
SourceFile sourceFileOfElement(Element element) {
return element.implementation.compilationUnit.script.file;
}
}