| // 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; |
| } |
| } |