| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 /// Generate code using the cps-based IR pipeline. | |
| 6 library code_generator_task; | |
| 7 | |
| 8 import '../../common.dart'; | |
| 9 import '../../common/codegen.dart' show CodegenWorkItem; | |
| 10 import '../../common/tasks.dart' show CompilerTask, GenericTask; | |
| 11 import '../../compiler.dart' show Compiler; | |
| 12 import '../../constants/constant_system.dart'; | |
| 13 import '../../cps_ir/cps_ir_builder_task.dart'; | |
| 14 import '../../cps_ir/cps_ir_integrity.dart'; | |
| 15 import '../../cps_ir/cps_ir_nodes.dart' as cps; | |
| 16 import '../../cps_ir/cps_ir_nodes_sexpr.dart'; | |
| 17 import '../../cps_ir/finalize.dart' show Finalize; | |
| 18 import '../../cps_ir/optimizers.dart'; | |
| 19 import '../../cps_ir/optimizers.dart' as cps_opt; | |
| 20 import '../../cps_ir/type_mask_system.dart'; | |
| 21 import '../../diagnostics/invariant.dart' show DEBUG_MODE; | |
| 22 import '../../elements/elements.dart'; | |
| 23 import '../../io/source_information.dart' show SourceInformationStrategy; | |
| 24 import '../../js/js.dart' as js; | |
| 25 import '../../js_backend/codegen/codegen.dart'; | |
| 26 import '../../ssa/ssa.dart' as ssa; | |
| 27 import '../../tracer.dart'; | |
| 28 import '../../tree_ir/optimization/optimization.dart'; | |
| 29 import '../../tree_ir/optimization/optimization.dart' as tree_opt; | |
| 30 import '../../tree_ir/tree_ir_builder.dart' as tree_builder; | |
| 31 import '../../tree_ir/tree_ir_integrity.dart'; | |
| 32 import '../../tree_ir/tree_ir_nodes.dart' as tree_ir; | |
| 33 import '../../types/types.dart' | |
| 34 show FlatTypeMask, ForwardingTypeMask, TypeMask, UnionTypeMask; | |
| 35 import '../js_backend.dart'; | |
| 36 import 'codegen.dart'; | |
| 37 import 'glue.dart'; | |
| 38 import 'unsugar.dart'; | |
| 39 | |
| 40 class CpsFunctionCompiler implements FunctionCompiler { | |
| 41 final ConstantSystem constantSystem; | |
| 42 // TODO(karlklose): remove the compiler. | |
| 43 final Compiler compiler; | |
| 44 final Glue glue; | |
| 45 final SourceInformationStrategy sourceInformationFactory; | |
| 46 | |
| 47 // TODO(karlklose,sigurdm): remove and update dart-doc of [compile]. | |
| 48 final FunctionCompiler fallbackCompiler; | |
| 49 TypeMaskSystem typeSystem; | |
| 50 | |
| 51 Tracer get tracer => compiler.tracer; | |
| 52 | |
| 53 final IrBuilderTask cpsBuilderTask; | |
| 54 final GenericTask cpsOptimizationTask; | |
| 55 final GenericTask treeBuilderTask; | |
| 56 final GenericTask treeOptimizationTask; | |
| 57 | |
| 58 Inliner inliner; | |
| 59 | |
| 60 CpsFunctionCompiler(Compiler compiler, JavaScriptBackend backend, | |
| 61 SourceInformationStrategy sourceInformationFactory) | |
| 62 : fallbackCompiler = | |
| 63 new ssa.SsaFunctionCompiler(backend, sourceInformationFactory), | |
| 64 cpsBuilderTask = new IrBuilderTask(compiler, sourceInformationFactory), | |
| 65 sourceInformationFactory = sourceInformationFactory, | |
| 66 constantSystem = backend.constantSystem, | |
| 67 compiler = compiler, | |
| 68 glue = new Glue(compiler), | |
| 69 cpsOptimizationTask = | |
| 70 new GenericTask('CPS optimization', compiler.measurer), | |
| 71 treeBuilderTask = new GenericTask('Tree builder', compiler.measurer), | |
| 72 treeOptimizationTask = | |
| 73 new GenericTask('Tree optimization', compiler.measurer) { | |
| 74 inliner = new Inliner(this); | |
| 75 } | |
| 76 | |
| 77 String get name => 'CPS Ir pipeline'; | |
| 78 | |
| 79 JavaScriptBackend get backend => compiler.backend; | |
| 80 | |
| 81 DiagnosticReporter get reporter => compiler.reporter; | |
| 82 | |
| 83 /// Generates JavaScript code for `work.element`. | |
| 84 js.Fun compile(CodegenWorkItem work) { | |
| 85 if (typeSystem == null) typeSystem = new TypeMaskSystem(compiler); | |
| 86 AstElement element = work.element; | |
| 87 return reporter.withCurrentElement(element, () { | |
| 88 try { | |
| 89 // TODO(karlklose): remove this fallback when we do not need it for | |
| 90 // testing anymore. | |
| 91 if (false) { | |
| 92 reporter.log('Using SSA compiler for platform element $element'); | |
| 93 return fallbackCompiler.compile(work); | |
| 94 } | |
| 95 | |
| 96 if (tracer != null) { | |
| 97 tracer.traceCompilation('$element', null); | |
| 98 } | |
| 99 cps.FunctionDefinition cpsFunction = compileToCpsIr(element); | |
| 100 optimizeCpsBeforeInlining(cpsFunction); | |
| 101 applyCpsPass(inliner, cpsFunction); | |
| 102 optimizeCpsAfterInlining(cpsFunction); | |
| 103 cpsIntegrityChecker = null; | |
| 104 tree_ir.FunctionDefinition treeFunction = compileToTreeIr(cpsFunction); | |
| 105 treeFunction = optimizeTreeIr(treeFunction); | |
| 106 return compileToJavaScript(work, treeFunction); | |
| 107 } on CodegenBailout catch (e) { | |
| 108 String message = "Unable to compile $element with the new compiler.\n" | |
| 109 " Reason: ${e.message}"; | |
| 110 reporter.internalError(element, message); | |
| 111 } | |
| 112 }); | |
| 113 } | |
| 114 | |
| 115 void giveUp(String reason) { | |
| 116 throw new CodegenBailout(null, reason); | |
| 117 } | |
| 118 | |
| 119 void traceGraph(String title, var irObject) { | |
| 120 if (tracer != null) { | |
| 121 tracer.traceGraph(title, irObject); | |
| 122 } | |
| 123 } | |
| 124 | |
| 125 String stringify(cps.FunctionDefinition node) { | |
| 126 return new SExpressionStringifier().withTypes().visit(node); | |
| 127 } | |
| 128 | |
| 129 /// For debugging purposes, replace a call to [applyCpsPass] with a call | |
| 130 /// to [debugCpsPass] to check that this pass is idempotent. | |
| 131 /// | |
| 132 /// This runs [pass] followed by shrinking reductions, and then checks that | |
| 133 /// one more run of [pass] does not change the IR. The intermediate shrinking | |
| 134 /// reductions pass is omitted if [pass] itself is shrinking reductions. | |
| 135 /// | |
| 136 /// If [targetName] is given, functions whose name contains that substring | |
| 137 /// will be dumped out if the idempotency test fails. | |
| 138 void debugCpsPass(cps_opt.Pass makePass(), cps.FunctionDefinition cpsFunction, | |
| 139 [String targetName]) { | |
| 140 String original = stringify(cpsFunction); | |
| 141 cps_opt.Pass pass = makePass(); | |
| 142 pass.rewrite(cpsFunction); | |
| 143 assert(checkCpsIntegrity(cpsFunction, pass.passName)); | |
| 144 if (pass is! ShrinkingReducer) { | |
| 145 new ShrinkingReducer().rewrite(cpsFunction); | |
| 146 } | |
| 147 String before = stringify(cpsFunction); | |
| 148 makePass().rewrite(cpsFunction); | |
| 149 String after = stringify(cpsFunction); | |
| 150 if (before != after) { | |
| 151 print('SExpression changed for ${cpsFunction.element}'); | |
| 152 if (targetName != null && '${cpsFunction.element}'.contains(targetName)) { | |
| 153 print(original); | |
| 154 print('\n-->\n'); | |
| 155 print(before); | |
| 156 print('\n-->\n'); | |
| 157 print(after); | |
| 158 compiler.outputProvider('original', 'dump') | |
| 159 ..add(original) | |
| 160 ..close(); | |
| 161 compiler.outputProvider('before', 'dump') | |
| 162 ..add(before) | |
| 163 ..close(); | |
| 164 compiler.outputProvider('after', 'dump') | |
| 165 ..add(after) | |
| 166 ..close(); | |
| 167 } | |
| 168 } | |
| 169 traceGraph(pass.passName, cpsFunction); | |
| 170 dumpTypedIr(pass.passName, cpsFunction); | |
| 171 } | |
| 172 | |
| 173 void applyCpsPass(cps_opt.Pass pass, cps.FunctionDefinition cpsFunction) { | |
| 174 cpsOptimizationTask.measureSubtask(pass.passName, () { | |
| 175 pass.rewrite(cpsFunction); | |
| 176 }); | |
| 177 traceGraph(pass.passName, cpsFunction); | |
| 178 dumpTypedIr(pass.passName, cpsFunction); | |
| 179 assert(checkCpsIntegrity(cpsFunction, pass.passName)); | |
| 180 } | |
| 181 | |
| 182 cps.FunctionDefinition compileToCpsIr(AstElement element) { | |
| 183 cps.FunctionDefinition cpsFunction = inliner.cache.getUnoptimized(element); | |
| 184 if (cpsFunction != null) return cpsFunction; | |
| 185 | |
| 186 cpsFunction = cpsBuilderTask.buildNode(element, typeSystem); | |
| 187 if (cpsFunction == null) { | |
| 188 if (cpsBuilderTask.bailoutMessage == null) { | |
| 189 giveUp('unable to build cps definition of $element'); | |
| 190 } else { | |
| 191 giveUp(cpsBuilderTask.bailoutMessage); | |
| 192 } | |
| 193 } | |
| 194 ParentVisitor.setParents(cpsFunction); | |
| 195 traceGraph('IR Builder', cpsFunction); | |
| 196 dumpTypedIr('IR Builder', cpsFunction); | |
| 197 // Eliminating redundant phis before the unsugaring pass will make it | |
| 198 // insert fewer getInterceptor calls. | |
| 199 applyCpsPass(new RedundantPhiEliminator(), cpsFunction); | |
| 200 applyCpsPass(new UnsugarVisitor(glue), cpsFunction); | |
| 201 applyCpsPass(new RedundantJoinEliminator(), cpsFunction); | |
| 202 applyCpsPass(new RedundantPhiEliminator(), cpsFunction); | |
| 203 applyCpsPass(new InsertRefinements(typeSystem), cpsFunction); | |
| 204 | |
| 205 inliner.cache.putUnoptimized(element, cpsFunction); | |
| 206 return cpsFunction; | |
| 207 } | |
| 208 | |
| 209 static const Pattern PRINT_TYPED_IR_FILTER = null; | |
| 210 | |
| 211 String formatTypeMask(TypeMask type) { | |
| 212 if (type is UnionTypeMask) { | |
| 213 return '[${type.disjointMasks.map(formatTypeMask).join(', ')}]'; | |
| 214 } else if (type is FlatTypeMask) { | |
| 215 if (type.isEmpty) return "empty"; | |
| 216 if (type.isNull) return "null"; | |
| 217 String suffix = (type.isExact ? "" : "+") + (type.isNullable ? "?" : "!"); | |
| 218 return '${type.base.name}$suffix'; | |
| 219 } else if (type is ForwardingTypeMask) { | |
| 220 return formatTypeMask(type.forwardTo); | |
| 221 } | |
| 222 throw 'unsupported: $type'; | |
| 223 } | |
| 224 | |
| 225 void dumpTypedIr(String passName, cps.FunctionDefinition cpsFunction) { | |
| 226 if (PRINT_TYPED_IR_FILTER != null && | |
| 227 PRINT_TYPED_IR_FILTER.matchAsPrefix(cpsFunction.element.name) != null) { | |
| 228 String printType(nodeOrRef, String s) { | |
| 229 cps.Node node = | |
| 230 nodeOrRef is cps.Reference ? nodeOrRef.definition : nodeOrRef; | |
| 231 return node is cps.Variable && node.type != null | |
| 232 ? '$s:${formatTypeMask(node.type)}' | |
| 233 : s; | |
| 234 } | |
| 235 DEBUG_MODE = true; | |
| 236 print(';;; ==== After $passName ===='); | |
| 237 print(new SExpressionStringifier(printType).visit(cpsFunction)); | |
| 238 } | |
| 239 } | |
| 240 | |
| 241 CheckCpsIntegrity cpsIntegrityChecker; | |
| 242 | |
| 243 bool checkCpsIntegrity(cps.FunctionDefinition node, String previousPass) { | |
| 244 cpsOptimizationTask.measureSubtask('Check integrity', () { | |
| 245 if (cpsIntegrityChecker == null) { | |
| 246 cpsIntegrityChecker = new CheckCpsIntegrity(); | |
| 247 } | |
| 248 cpsIntegrityChecker.check(node, previousPass); | |
| 249 }); | |
| 250 return true; // So this can be used from assert(). | |
| 251 } | |
| 252 | |
| 253 void optimizeCpsBeforeInlining(cps.FunctionDefinition cpsFunction) { | |
| 254 cpsOptimizationTask.measure(() { | |
| 255 applyCpsPass(new TypePropagator(this), cpsFunction); | |
| 256 applyCpsPass(new RedundantJoinEliminator(), cpsFunction); | |
| 257 applyCpsPass(new ShrinkingReducer(), cpsFunction); | |
| 258 }); | |
| 259 } | |
| 260 | |
| 261 void optimizeCpsAfterInlining(cps.FunctionDefinition cpsFunction) { | |
| 262 cpsOptimizationTask.measure(() { | |
| 263 applyCpsPass(new RedundantJoinEliminator(), cpsFunction); | |
| 264 applyCpsPass(new ShrinkingReducer(), cpsFunction); | |
| 265 applyCpsPass(new RedundantRefinementEliminator(typeSystem), cpsFunction); | |
| 266 applyCpsPass(new UpdateRefinements(typeSystem), cpsFunction); | |
| 267 applyCpsPass(new TypePropagator(this, recomputeAll: true), cpsFunction); | |
| 268 applyCpsPass(new ShrinkingReducer(), cpsFunction); | |
| 269 applyCpsPass(new EagerlyLoadStatics(), cpsFunction); | |
| 270 applyCpsPass(new GVN(compiler, typeSystem), cpsFunction); | |
| 271 applyCpsPass(new PathBasedOptimizer(backend, typeSystem), cpsFunction); | |
| 272 applyCpsPass(new ShrinkingReducer(), cpsFunction); | |
| 273 applyCpsPass(new UpdateRefinements(typeSystem), cpsFunction); | |
| 274 applyCpsPass(new BoundsChecker(typeSystem, compiler.world), cpsFunction); | |
| 275 applyCpsPass(new LoopInvariantBranchMotion(), cpsFunction); | |
| 276 applyCpsPass(new ShrinkingReducer(), cpsFunction); | |
| 277 applyCpsPass(new ScalarReplacer(compiler), cpsFunction); | |
| 278 applyCpsPass(new UseFieldInitializers(backend), cpsFunction); | |
| 279 applyCpsPass(new MutableVariableEliminator(), cpsFunction); | |
| 280 applyCpsPass(new RedundantJoinEliminator(), cpsFunction); | |
| 281 applyCpsPass(new RedundantPhiEliminator(), cpsFunction); | |
| 282 applyCpsPass(new UpdateRefinements(typeSystem), cpsFunction); | |
| 283 applyCpsPass(new ShrinkingReducer(), cpsFunction); | |
| 284 applyCpsPass(new OptimizeInterceptors(backend, typeSystem), cpsFunction); | |
| 285 applyCpsPass(new BackwardNullCheckRemover(typeSystem), cpsFunction); | |
| 286 applyCpsPass(new ShrinkingReducer(), cpsFunction); | |
| 287 }); | |
| 288 } | |
| 289 | |
| 290 tree_ir.FunctionDefinition compileToTreeIr(cps.FunctionDefinition cpsNode) { | |
| 291 applyCpsPass(new Finalize(backend), cpsNode); | |
| 292 tree_builder.Builder builder = | |
| 293 new tree_builder.Builder(reporter.internalError, glue); | |
| 294 tree_ir.FunctionDefinition treeNode = | |
| 295 treeBuilderTask.measure(() => builder.buildFunction(cpsNode)); | |
| 296 assert(treeNode != null); | |
| 297 traceGraph('Tree builder', treeNode); | |
| 298 assert(checkTreeIntegrity(treeNode)); | |
| 299 return treeNode; | |
| 300 } | |
| 301 | |
| 302 bool checkTreeIntegrity(tree_ir.FunctionDefinition node) { | |
| 303 treeOptimizationTask.measureSubtask('Check integrity', () { | |
| 304 new CheckTreeIntegrity().check(node); | |
| 305 }); | |
| 306 return true; // So this can be used from assert(). | |
| 307 } | |
| 308 | |
| 309 tree_ir.FunctionDefinition optimizeTreeIr(tree_ir.FunctionDefinition node) { | |
| 310 void applyTreePass(tree_opt.Pass pass) { | |
| 311 treeOptimizationTask.measureSubtask(pass.passName, () { | |
| 312 pass.rewrite(node); | |
| 313 }); | |
| 314 traceGraph(pass.passName, node); | |
| 315 assert(checkTreeIntegrity(node)); | |
| 316 } | |
| 317 | |
| 318 treeOptimizationTask.measure(() { | |
| 319 applyTreePass(new StatementRewriter()); | |
| 320 applyTreePass( | |
| 321 new VariableMerger(minifying: compiler.options.enableMinification)); | |
| 322 applyTreePass(new LoopRewriter()); | |
| 323 applyTreePass(new LogicalRewriter()); | |
| 324 applyTreePass(new PullIntoInitializers()); | |
| 325 }); | |
| 326 | |
| 327 return node; | |
| 328 } | |
| 329 | |
| 330 js.Fun compileToJavaScript( | |
| 331 CodegenWorkItem work, tree_ir.FunctionDefinition definition) { | |
| 332 CodeGenerator codeGen = new CodeGenerator(glue, work.registry); | |
| 333 Element element = work.element; | |
| 334 js.Fun code = codeGen.buildFunction(definition); | |
| 335 if (element is FunctionElement && element.asyncMarker != AsyncMarker.SYNC) { | |
| 336 code = backend.rewriteAsync(element, code); | |
| 337 work.registry.registerAsyncMarker(element); | |
| 338 } | |
| 339 return attachPosition(code, work.resolvedAst); | |
| 340 } | |
| 341 | |
| 342 Iterable<CompilerTask> get tasks { | |
| 343 return <CompilerTask>[ | |
| 344 cpsBuilderTask, | |
| 345 cpsOptimizationTask, | |
| 346 treeBuilderTask, | |
| 347 treeOptimizationTask | |
| 348 ]..addAll(fallbackCompiler.tasks); | |
| 349 } | |
| 350 | |
| 351 js.Node attachPosition(js.Node node, ResolvedAst resolvedAst) { | |
| 352 return node.withSourceInformation(sourceInformationFactory | |
| 353 .createBuilderForContext(resolvedAst) | |
| 354 .buildDeclaration(resolvedAst)); | |
| 355 } | |
| 356 } | |
| OLD | NEW |