Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of ssa; | 5 part of ssa; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * A special element for the extra parameter taken by intercepted | 8 * A special element for the extra parameter taken by intercepted |
| 9 * methods. We need to override [Element.computeType] because our | 9 * methods. We need to override [Element.computeType] because our |
| 10 * optimizers may look at its declared type. | 10 * optimizers may look at its declared type. |
| (...skipping 911 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 922 activationVariables = new Map<Element, HLocalValue>(), | 922 activationVariables = new Map<Element, HLocalValue>(), |
| 923 jumpTargets = new Map<TargetElement, JumpHandler>(), | 923 jumpTargets = new Map<TargetElement, JumpHandler>(), |
| 924 parameters = new Map<Element, HInstruction>(), | 924 parameters = new Map<Element, HInstruction>(), |
| 925 sourceElementStack = <Element>[work.element], | 925 sourceElementStack = <Element>[work.element], |
| 926 inliningStack = <InliningState>[], | 926 inliningStack = <InliningState>[], |
| 927 rti = builder.backend.rti, | 927 rti = builder.backend.rti, |
| 928 super(work.resolutionTree) { | 928 super(work.resolutionTree) { |
| 929 localsHandler = new LocalsHandler(this); | 929 localsHandler = new LocalsHandler(this); |
| 930 } | 930 } |
| 931 | 931 |
| 932 static const MAX_INLINING_DEPTH = 3; | |
| 933 static const MAX_INLINING_NODES = 46; | |
| 934 | |
| 935 List<InliningState> inliningStack; | 932 List<InliningState> inliningStack; |
| 936 | 933 |
| 937 Element returnElement; | 934 Element returnElement; |
| 938 DartType returnType; | 935 DartType returnType; |
| 939 | 936 |
| 940 bool inTryStatement = false; | 937 bool inTryStatement = false; |
| 941 int loopNesting = 0; | 938 int loopNesting = 0; |
| 942 | 939 |
| 943 HBasicBlock get current => _current; | 940 HBasicBlock get current => _current; |
| 944 void set current(c) { | 941 void set current(c) { |
| (...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1218 bool tryInlineMethod(Element element, | 1215 bool tryInlineMethod(Element element, |
| 1219 Selector selector, | 1216 Selector selector, |
| 1220 List<HInstruction> providedArguments, | 1217 List<HInstruction> providedArguments, |
| 1221 Node currentNode) { | 1218 Node currentNode) { |
| 1222 backend.registerStaticUse(element, compiler.enqueuer.codegen); | 1219 backend.registerStaticUse(element, compiler.enqueuer.codegen); |
| 1223 | 1220 |
| 1224 // Ensure that [element] is an implementation element. | 1221 // Ensure that [element] is an implementation element. |
| 1225 element = element.implementation; | 1222 element = element.implementation; |
| 1226 FunctionElement function = element; | 1223 FunctionElement function = element; |
| 1227 | 1224 |
| 1228 bool cachedCanBeInlined = backend.canBeInlined[function]; | 1225 bool insideLoop = loopNesting > 0 || graph.calledInLoop; |
| 1226 | |
| 1227 // Bail out early if the inlining decision is in the cache and we can't | |
| 1228 // inline (no need to check the hard constraints). | |
| 1229 bool cachedCanBeInlined = | |
| 1230 backend.inlineCache.canInline(function, insideLoop: insideLoop); | |
| 1229 if (cachedCanBeInlined == false) return false; | 1231 if (cachedCanBeInlined == false) return false; |
| 1230 | 1232 |
| 1231 bool meetsHardConstraints() { | 1233 bool meetsHardConstraints() { |
| 1232 // We cannot inline a method from a deferred library into a method | 1234 // We cannot inline a method from a deferred library into a method |
| 1233 // which isn't deferred. | 1235 // which isn't deferred. |
| 1234 // TODO(ahe): But we should still inline into the same | 1236 // TODO(ahe): But we should still inline into the same |
| 1235 // connected-component of the deferred library. | 1237 // connected-component of the deferred library. |
| 1236 if (compiler.deferredLoadTask.isDeferred(element)) return false; | 1238 if (compiler.deferredLoadTask.isDeferred(element)) return false; |
| 1237 if (compiler.disableInlining) return false; | 1239 if (compiler.disableInlining) return false; |
| 1238 | 1240 |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 1252 | 1254 |
| 1253 // Don't inline if the return type was inferred to be non-null empty. This | 1255 // Don't inline if the return type was inferred to be non-null empty. This |
| 1254 // means that the function always throws an exception. | 1256 // means that the function always throws an exception. |
| 1255 TypeMask returnType = | 1257 TypeMask returnType = |
| 1256 compiler.typesTask.getGuaranteedReturnTypeOfElement(element); | 1258 compiler.typesTask.getGuaranteedReturnTypeOfElement(element); |
| 1257 if (returnType != null && returnType.isEmpty && !returnType.isNullable) { | 1259 if (returnType != null && returnType.isEmpty && !returnType.isNullable) { |
| 1258 isReachable = false; | 1260 isReachable = false; |
| 1259 return false; | 1261 return false; |
| 1260 } | 1262 } |
| 1261 | 1263 |
| 1264 // Don't inline recursivly | |
|
ngeoffray
2013/07/16 17:17:37
As discussed, removing the inlining depth is a heu
kustermann
2013/07/17 09:46:42
Done.
| |
| 1265 if (inliningStack.any((entry) => entry.function == function)) { | |
| 1266 return false; | |
| 1267 } | |
| 1268 | |
| 1262 return true; | 1269 return true; |
| 1263 } | 1270 } |
| 1264 | 1271 |
| 1265 bool heuristicsSayGoodToGo(FunctionExpression functionExpression, | 1272 bool heuristicSayGoodToGo(FunctionExpression functionExpression) { |
| 1266 TreeElements newElements) { | 1273 if (cachedCanBeInlined == true) return cachedCanBeInlined; |
| 1267 if (loopNesting == 0 && !graph.calledInLoop) return false; | |
| 1268 | 1274 |
| 1269 int maxDepth = (loopNesting > 0) ? MAX_INLINING_DEPTH : 1; | 1275 int numParameters = function.functionSignature.parameterCount; |
| 1270 if (inliningStack.length >= maxDepth) return false; | 1276 int maxInliningNodes; |
| 1271 | 1277 if (insideLoop) { |
| 1272 if (cachedCanBeInlined == null) { | 1278 maxInliningNodes = InlineWeeder.INLINING_NODES_INSIDE_LOOP + |
| 1273 var canBeInlined = | 1279 InlineWeeder.INLINING_NODES_INSIDE_LOOP_ARG_FACTOR * numParameters; |
| 1274 InlineWeeder.canBeInlined(functionExpression, newElements); | 1280 } else { |
| 1275 backend.canBeInlined[function] = canBeInlined; | 1281 maxInliningNodes = InlineWeeder.INLINING_NODES_OUTSIDE_LOOP + |
| 1276 return canBeInlined; | 1282 InlineWeeder.INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR * numParameters; |
| 1277 } | 1283 } |
| 1278 return cachedCanBeInlined; | 1284 bool canBeInlined = InlineWeeder.canBeInlined( |
| 1285 functionExpression, maxInliningNodes); | |
| 1286 if (canBeInlined) { | |
| 1287 backend.inlineCache.markAsInlinable(element, insideLoop: insideLoop); | |
| 1288 } else { | |
| 1289 backend.inlineCache.markAsNonInlinable(element, insideLoop: insideLoop); | |
| 1290 } | |
| 1291 return canBeInlined; | |
| 1279 } | 1292 } |
| 1280 | 1293 |
| 1281 void doInlining(FunctionExpression functionExpression) { | 1294 void doInlining(FunctionExpression functionExpression) { |
| 1282 // Add an explicit null check on the receiver before doing the | 1295 // Add an explicit null check on the receiver before doing the |
| 1283 // inlining. We use [element] to get the same name in the | 1296 // inlining. We use [element] to get the same name in the |
| 1284 // NoSuchMethodError message as if we had called it. | 1297 // NoSuchMethodError message as if we had called it. |
| 1285 if (element.isInstanceMember() | 1298 if (element.isInstanceMember() |
| 1286 && !element.isGenerativeConstructorBody() | 1299 && !element.isGenerativeConstructorBody() |
| 1287 && (selector.mask == null || selector.mask.isNullable)) { | 1300 && (selector.mask == null || selector.mask.isNullable)) { |
| 1288 addWithPosition( | 1301 addWithPosition( |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 1300 }); | 1313 }); |
| 1301 element.isGenerativeConstructor() | 1314 element.isGenerativeConstructor() |
| 1302 ? buildFactory(element) | 1315 ? buildFactory(element) |
| 1303 : functionExpression.body.accept(this); | 1316 : functionExpression.body.accept(this); |
| 1304 }); | 1317 }); |
| 1305 leaveInlinedMethod(state); | 1318 leaveInlinedMethod(state); |
| 1306 } | 1319 } |
| 1307 | 1320 |
| 1308 if (meetsHardConstraints()) { | 1321 if (meetsHardConstraints()) { |
| 1309 FunctionExpression functionExpression = function.parseNode(compiler); | 1322 FunctionExpression functionExpression = function.parseNode(compiler); |
| 1310 TreeElements newElements = | |
| 1311 compiler.enqueuer.resolution.getCachedElements(function); | |
| 1312 if (newElements == null) { | |
| 1313 compiler.internalError("Element not resolved: $function"); | |
| 1314 } | |
| 1315 | 1323 |
| 1316 if (heuristicsSayGoodToGo(functionExpression, newElements)) { | 1324 if (heuristicSayGoodToGo(functionExpression)) { |
| 1317 doInlining(functionExpression); | 1325 doInlining(functionExpression); |
| 1318 return true; | 1326 return true; |
| 1319 } | 1327 } |
| 1320 } | 1328 } |
| 1321 | 1329 |
| 1322 return false; | 1330 return false; |
| 1323 } | 1331 } |
| 1324 | 1332 |
| 1325 inlinedFrom(Element element, f()) { | 1333 inlinedFrom(Element element, f()) { |
| 1326 assert(element is FunctionElement || element is VariableElement); | 1334 assert(element is FunctionElement || element is VariableElement); |
| (...skipping 3791 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 5118 builder.add(instruction); | 5126 builder.add(instruction); |
| 5119 return instruction; | 5127 return instruction; |
| 5120 } | 5128 } |
| 5121 } | 5129 } |
| 5122 | 5130 |
| 5123 /** | 5131 /** |
| 5124 * This class visits the method that is a candidate for inlining and | 5132 * This class visits the method that is a candidate for inlining and |
| 5125 * finds whether it is too difficult to inline. | 5133 * finds whether it is too difficult to inline. |
| 5126 */ | 5134 */ |
| 5127 class InlineWeeder extends Visitor { | 5135 class InlineWeeder extends Visitor { |
| 5128 final TreeElements elements; | 5136 // Invariant: *INSIDE_LOOP* > *OUTSIDE_LOOP* |
| 5137 static const INLINING_NODES_OUTSIDE_LOOP = 18; | |
| 5138 static const INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR = 3; | |
| 5139 static const INLINING_NODES_INSIDE_LOOP = 42; | |
| 5140 static const INLINING_NODES_INSIDE_LOOP_ARG_FACTOR = 4; | |
| 5129 | 5141 |
| 5130 bool seenReturn = false; | 5142 bool seenReturn = false; |
| 5131 bool tooDifficult = false; | 5143 bool tooDifficult = false; |
| 5132 int nodeCount = 0; | 5144 int nodeCount = 0; |
| 5145 final int maxInliningNodes; | |
| 5133 | 5146 |
| 5134 InlineWeeder(this.elements); | 5147 InlineWeeder(this.maxInliningNodes); |
| 5135 | 5148 |
| 5136 static bool canBeInlined(FunctionExpression functionExpression, | 5149 static bool canBeInlined(FunctionExpression functionExpression, |
| 5137 TreeElements elements) { | 5150 int maxInliningNodes) { |
| 5138 InlineWeeder weeder = new InlineWeeder(elements); | 5151 InlineWeeder weeder = new InlineWeeder(maxInliningNodes); |
| 5139 weeder.visit(functionExpression.initializers); | 5152 weeder.visit(functionExpression.initializers); |
| 5140 weeder.visit(functionExpression.body); | 5153 weeder.visit(functionExpression.body); |
| 5141 if (weeder.tooDifficult) return false; | 5154 return !weeder.tooDifficult; |
| 5142 return true; | |
| 5143 } | 5155 } |
| 5144 | 5156 |
| 5145 bool registerNode() { | 5157 bool registerNode() { |
| 5146 if (nodeCount++ > SsaBuilder.MAX_INLINING_NODES) { | 5158 if (nodeCount++ > maxInliningNodes) { |
| 5147 tooDifficult = true; | 5159 tooDifficult = true; |
| 5148 return false; | 5160 return false; |
| 5149 } else { | 5161 } else { |
| 5150 return true; | 5162 return true; |
| 5151 } | 5163 } |
| 5152 } | 5164 } |
| 5153 | 5165 |
| 5154 void visit(Node node) { | 5166 void visit(Node node) { |
| 5155 if (node != null) node.accept(this); | 5167 if (node != null) node.accept(this); |
| 5156 } | 5168 } |
| (...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 5467 new HSubGraphBlockInformation(elseBranch.graph)); | 5479 new HSubGraphBlockInformation(elseBranch.graph)); |
| 5468 | 5480 |
| 5469 HBasicBlock conditionStartBlock = conditionBranch.block; | 5481 HBasicBlock conditionStartBlock = conditionBranch.block; |
| 5470 conditionStartBlock.setBlockFlow(info, joinBlock); | 5482 conditionStartBlock.setBlockFlow(info, joinBlock); |
| 5471 SubGraph conditionGraph = conditionBranch.graph; | 5483 SubGraph conditionGraph = conditionBranch.graph; |
| 5472 HIf branch = conditionGraph.end.last; | 5484 HIf branch = conditionGraph.end.last; |
| 5473 assert(branch is HIf); | 5485 assert(branch is HIf); |
| 5474 branch.blockInformation = conditionStartBlock.blockFlow; | 5486 branch.blockInformation = conditionStartBlock.blockFlow; |
| 5475 } | 5487 } |
| 5476 } | 5488 } |
| OLD | NEW |