| 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 |
| 1239 assert(selector != null | 1241 assert(selector != null |
| 1240 || Elements.isStaticOrTopLevel(element) | 1242 || Elements.isStaticOrTopLevel(element) |
| 1241 || element.isGenerativeConstructorBody()); | 1243 || element.isGenerativeConstructorBody()); |
| 1242 if (selector != null && !selector.applies(function, compiler)) | 1244 if (selector != null && !selector.applies(function, compiler)) { |
| 1243 return false; | 1245 return false; |
| 1246 } |
| 1244 | 1247 |
| 1245 // Don't inline operator== methods if the parameter can be null. | 1248 // Don't inline operator== methods if the parameter can be null. |
| 1246 if (element.name == const SourceString('==')) { | 1249 if (element.name == const SourceString('==')) { |
| 1247 if (element.getEnclosingClass() != compiler.objectClass | 1250 if (element.getEnclosingClass() != compiler.objectClass |
| 1248 && providedArguments[1].canBeNull()) { | 1251 && providedArguments[1].canBeNull()) { |
| 1249 return false; | 1252 return false; |
| 1250 } | 1253 } |
| 1251 } | 1254 } |
| 1252 | 1255 |
| 1253 // Don't inline if the return type was inferred to be non-null empty. This | 1256 // Don't inline if the return type was inferred to be non-null empty. This |
| 1254 // means that the function always throws an exception. | 1257 // means that the function always throws an exception. |
| 1255 TypeMask returnType = | 1258 TypeMask returnType = |
| 1256 compiler.typesTask.getGuaranteedReturnTypeOfElement(element); | 1259 compiler.typesTask.getGuaranteedReturnTypeOfElement(element); |
| 1257 if (returnType != null && returnType.isEmpty && !returnType.isNullable) { | 1260 if (returnType != null && returnType.isEmpty && !returnType.isNullable) { |
| 1258 isReachable = false; | 1261 isReachable = false; |
| 1259 return false; | 1262 return false; |
| 1260 } | 1263 } |
| 1261 | 1264 |
| 1262 return true; | 1265 return true; |
| 1263 } | 1266 } |
| 1264 | 1267 |
| 1265 bool heuristicsSayGoodToGo(FunctionExpression functionExpression, | 1268 bool heuristicSayGoodToGo(FunctionExpression functionExpression) { |
| 1266 TreeElements newElements) { | 1269 // Don't inline recursivly |
| 1267 if (loopNesting == 0 && !graph.calledInLoop) return false; | 1270 if (inliningStack.any((entry) => entry.function == function)) { |
| 1271 return false; |
| 1272 } |
| 1268 | 1273 |
| 1269 int maxDepth = (loopNesting > 0) ? MAX_INLINING_DEPTH : 1; | 1274 if (cachedCanBeInlined == true) return cachedCanBeInlined; |
| 1270 if (inliningStack.length >= maxDepth) return false; | |
| 1271 | 1275 |
| 1272 if (cachedCanBeInlined == null) { | 1276 int numParameters = function.functionSignature.parameterCount; |
| 1273 var canBeInlined = | 1277 int maxInliningNodes; |
| 1274 InlineWeeder.canBeInlined(functionExpression, newElements); | 1278 if (insideLoop) { |
| 1275 backend.canBeInlined[function] = canBeInlined; | 1279 maxInliningNodes = InlineWeeder.INLINING_NODES_INSIDE_LOOP + |
| 1276 return canBeInlined; | 1280 InlineWeeder.INLINING_NODES_INSIDE_LOOP_ARG_FACTOR * numParameters; |
| 1281 } else { |
| 1282 maxInliningNodes = InlineWeeder.INLINING_NODES_OUTSIDE_LOOP + |
| 1283 InlineWeeder.INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR * numParameters; |
| 1277 } | 1284 } |
| 1278 return cachedCanBeInlined; | 1285 bool canBeInlined = InlineWeeder.canBeInlined( |
| 1286 functionExpression, maxInliningNodes); |
| 1287 if (canBeInlined) { |
| 1288 backend.inlineCache.markAsInlinable(element, insideLoop: insideLoop); |
| 1289 } else { |
| 1290 backend.inlineCache.markAsNonInlinable(element, insideLoop: insideLoop); |
| 1291 } |
| 1292 return canBeInlined; |
| 1279 } | 1293 } |
| 1280 | 1294 |
| 1281 void doInlining(FunctionExpression functionExpression) { | 1295 void doInlining(FunctionExpression functionExpression) { |
| 1282 // Add an explicit null check on the receiver before doing the | 1296 // Add an explicit null check on the receiver before doing the |
| 1283 // inlining. We use [element] to get the same name in the | 1297 // inlining. We use [element] to get the same name in the |
| 1284 // NoSuchMethodError message as if we had called it. | 1298 // NoSuchMethodError message as if we had called it. |
| 1285 if (element.isInstanceMember() | 1299 if (element.isInstanceMember() |
| 1286 && !element.isGenerativeConstructorBody() | 1300 && !element.isGenerativeConstructorBody() |
| 1287 && (selector.mask == null || selector.mask.isNullable)) { | 1301 && (selector.mask == null || selector.mask.isNullable)) { |
| 1288 addWithPosition( | 1302 addWithPosition( |
| (...skipping 11 matching lines...) Expand all Loading... |
| 1300 }); | 1314 }); |
| 1301 element.isGenerativeConstructor() | 1315 element.isGenerativeConstructor() |
| 1302 ? buildFactory(element) | 1316 ? buildFactory(element) |
| 1303 : functionExpression.body.accept(this); | 1317 : functionExpression.body.accept(this); |
| 1304 }); | 1318 }); |
| 1305 leaveInlinedMethod(state); | 1319 leaveInlinedMethod(state); |
| 1306 } | 1320 } |
| 1307 | 1321 |
| 1308 if (meetsHardConstraints()) { | 1322 if (meetsHardConstraints()) { |
| 1309 FunctionExpression functionExpression = function.parseNode(compiler); | 1323 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 | 1324 |
| 1316 if (heuristicsSayGoodToGo(functionExpression, newElements)) { | 1325 if (heuristicSayGoodToGo(functionExpression)) { |
| 1317 doInlining(functionExpression); | 1326 doInlining(functionExpression); |
| 1318 return true; | 1327 return true; |
| 1319 } | 1328 } |
| 1320 } | 1329 } |
| 1321 | 1330 |
| 1322 return false; | 1331 return false; |
| 1323 } | 1332 } |
| 1324 | 1333 |
| 1325 inlinedFrom(Element element, f()) { | 1334 inlinedFrom(Element element, f()) { |
| 1326 assert(element is FunctionElement || element is VariableElement); | 1335 assert(element is FunctionElement || element is VariableElement); |
| (...skipping 3791 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 5118 builder.add(instruction); | 5127 builder.add(instruction); |
| 5119 return instruction; | 5128 return instruction; |
| 5120 } | 5129 } |
| 5121 } | 5130 } |
| 5122 | 5131 |
| 5123 /** | 5132 /** |
| 5124 * This class visits the method that is a candidate for inlining and | 5133 * This class visits the method that is a candidate for inlining and |
| 5125 * finds whether it is too difficult to inline. | 5134 * finds whether it is too difficult to inline. |
| 5126 */ | 5135 */ |
| 5127 class InlineWeeder extends Visitor { | 5136 class InlineWeeder extends Visitor { |
| 5128 final TreeElements elements; | 5137 // Invariant: *INSIDE_LOOP* > *OUTSIDE_LOOP* |
| 5138 static const INLINING_NODES_OUTSIDE_LOOP = 18; |
| 5139 static const INLINING_NODES_OUTSIDE_LOOP_ARG_FACTOR = 3; |
| 5140 static const INLINING_NODES_INSIDE_LOOP = 42; |
| 5141 static const INLINING_NODES_INSIDE_LOOP_ARG_FACTOR = 4; |
| 5129 | 5142 |
| 5130 bool seenReturn = false; | 5143 bool seenReturn = false; |
| 5131 bool tooDifficult = false; | 5144 bool tooDifficult = false; |
| 5132 int nodeCount = 0; | 5145 int nodeCount = 0; |
| 5146 final int maxInliningNodes; |
| 5133 | 5147 |
| 5134 InlineWeeder(this.elements); | 5148 InlineWeeder(this.maxInliningNodes); |
| 5135 | 5149 |
| 5136 static bool canBeInlined(FunctionExpression functionExpression, | 5150 static bool canBeInlined(FunctionExpression functionExpression, |
| 5137 TreeElements elements) { | 5151 int maxInliningNodes) { |
| 5138 InlineWeeder weeder = new InlineWeeder(elements); | 5152 InlineWeeder weeder = new InlineWeeder(maxInliningNodes); |
| 5139 weeder.visit(functionExpression.initializers); | 5153 weeder.visit(functionExpression.initializers); |
| 5140 weeder.visit(functionExpression.body); | 5154 weeder.visit(functionExpression.body); |
| 5141 if (weeder.tooDifficult) return false; | 5155 return !weeder.tooDifficult; |
| 5142 return true; | |
| 5143 } | 5156 } |
| 5144 | 5157 |
| 5145 bool registerNode() { | 5158 bool registerNode() { |
| 5146 if (nodeCount++ > SsaBuilder.MAX_INLINING_NODES) { | 5159 if (nodeCount++ > maxInliningNodes) { |
| 5147 tooDifficult = true; | 5160 tooDifficult = true; |
| 5148 return false; | 5161 return false; |
| 5149 } else { | 5162 } else { |
| 5150 return true; | 5163 return true; |
| 5151 } | 5164 } |
| 5152 } | 5165 } |
| 5153 | 5166 |
| 5154 void visit(Node node) { | 5167 void visit(Node node) { |
| 5155 if (node != null) node.accept(this); | 5168 if (node != null) node.accept(this); |
| 5156 } | 5169 } |
| (...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 5467 new HSubGraphBlockInformation(elseBranch.graph)); | 5480 new HSubGraphBlockInformation(elseBranch.graph)); |
| 5468 | 5481 |
| 5469 HBasicBlock conditionStartBlock = conditionBranch.block; | 5482 HBasicBlock conditionStartBlock = conditionBranch.block; |
| 5470 conditionStartBlock.setBlockFlow(info, joinBlock); | 5483 conditionStartBlock.setBlockFlow(info, joinBlock); |
| 5471 SubGraph conditionGraph = conditionBranch.graph; | 5484 SubGraph conditionGraph = conditionBranch.graph; |
| 5472 HIf branch = conditionGraph.end.last; | 5485 HIf branch = conditionGraph.end.last; |
| 5473 assert(branch is HIf); | 5486 assert(branch is HIf); |
| 5474 branch.blockInformation = conditionStartBlock.blockFlow; | 5487 branch.blockInformation = conditionStartBlock.blockFlow; |
| 5475 } | 5488 } |
| 5476 } | 5489 } |
| OLD | NEW |