blob: 16093d3e59fba4c74dbcc207c1f4e0d7c049828f [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 '../../common/codegen.dart' show
CodegenWorkItem;
import '../../common/tasks.dart' show
CompilerTask;
import '../../compiler.dart' show
Compiler;
import '../../constants/constant_system.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 '../../diagnostics/invariant.dart' show
DEBUG_MODE;
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';
import '../../cps_ir/type_mask_system.dart';
class CpsFunctionCompiler implements FunctionCompiler {
final ConstantSystem constantSystem;
// TODO(karlklose): remove the compiler.
final Compiler compiler;
final Glue glue;
final SourceInformationStrategy sourceInformationFactory;
// TODO(karlklose,sigurdm): remove and update dart-doc of [compile].
final FunctionCompiler fallbackCompiler;
TypeMaskSystem typeSystem;
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';
JavaScriptBackend get backend => compiler.backend;
/// Generates JavaScript code for `work.element`.
js.Fun compile(CodegenWorkItem work) {
AstElement element = work.element;
return compiler.withCurrentElement(element, () {
typeSystem = new TypeMaskSystem(compiler);
try {
// TODO(karlklose): remove this fallback when we do not need it for
// testing anymore.
if (false) {
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) {
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);
// Eliminating redundant phis before the unsugaring pass will make it
// insert fewer getInterceptor calls.
new RedundantPhiEliminator().rewrite(cpsNode);
traceGraph("Redundant phi elimination", 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));
}
TypeMaskSystem typeSystem = new TypeMaskSystem(compiler);
applyCpsPass(new RedundantJoinEliminator());
applyCpsPass(new RedundantPhiEliminator());
applyCpsPass(new InsertRefinements(typeSystem));
TypePropagator typePropagator =
new TypePropagator(compiler, typeSystem, this);
applyCpsPass(typePropagator);
dumpTypedIR(cpsNode, typePropagator);
applyCpsPass(new RemoveRefinements());
applyCpsPass(new ShrinkingReducer());
applyCpsPass(new ScalarReplacer(compiler));
applyCpsPass(new MutableVariableEliminator());
applyCpsPass(new RedundantJoinEliminator());
applyCpsPass(new RedundantPhiEliminator());
applyCpsPass(new ShrinkingReducer());
applyCpsPass(new LoopInvariantCodeMotion());
applyCpsPass(new ShareInterceptors());
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);
Element element = work.element;
js.Fun code = codeGen.buildFunction(definition);
if (element is FunctionElement && element.asyncMarker != AsyncMarker.SYNC) {
code = backend.rewriteAsync(element, code);
work.registry.registerAsyncMarker(element);
}
return attachPosition(code, 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));
}
}