| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 type_graph_inferrer; | 5 part of type_graph_inferrer; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * A set of selector names that [List] implements, that we know do not | 8 * A set of selector names that [List] implements, that we know do not |
| 9 * change the element type of the list, or let the list escape to code | 9 * change the element type of the list, or let the list escape to code |
| 10 * that might change the element type. | 10 * that might change the element type. |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 162 'shuffle', | 162 'shuffle', |
| 163 '[]=', | 163 '[]=', |
| 164 | 164 |
| 165 // From JSArray. | 165 // From JSArray. |
| 166 'checkMutable', | 166 'checkMutable', |
| 167 'checkGrowable', | 167 'checkGrowable', |
| 168 ]); | 168 ]); |
| 169 | 169 |
| 170 bool _VERBOSE = false; | 170 bool _VERBOSE = false; |
| 171 | 171 |
| 172 class ContainerTracerVisitor implements TypeInformationVisitor { | 172 abstract class TracerVisitor implements TypeInformationVisitor { |
| 173 final ListTypeInformation container; | 173 final TypeInformation tracedType; |
| 174 final TypeGraphInferrerEngine inferrer; | 174 final TypeGraphInferrerEngine inferrer; |
| 175 final Compiler compiler; | 175 final Compiler compiler; |
| 176 | 176 |
| 177 static const int MAX_ANALYSIS_COUNT = 16; |
| 178 final Setlet<Element> analyzedElements = new Setlet<Element>(); |
| 179 |
| 180 TracerVisitor(this.tracedType, inferrer) |
| 181 : this.inferrer = inferrer, this.compiler = inferrer.compiler; |
| 177 | 182 |
| 178 // Work list that gets populated with [TypeInformation] that could | 183 // Work list that gets populated with [TypeInformation] that could |
| 179 // contain the container. | 184 // contain the container. |
| 180 final List<TypeInformation> workList = <TypeInformation>[]; | 185 final List<TypeInformation> workList = <TypeInformation>[]; |
| 181 | 186 |
| 182 // Work list of containers to analyze after analyzing the users of a | 187 // Work list of containers to analyze after analyzing the users of a |
| 183 // [TypeInformation] that may be [container]. We know [container] | 188 // [TypeInformation] that may be [container]. We know [container] |
| 184 // has been stored in these containers and we must check how | 189 // has been stored in these containers and we must check how |
| 185 // [container] escapes from these containers. | 190 // [container] escapes from these containers. |
| 186 final List<ListTypeInformation> containersToAnalyze = | 191 final List<ListTypeInformation> containersToAnalyze = |
| 187 <ListTypeInformation>[]; | 192 <ListTypeInformation>[]; |
| 188 | 193 |
| 194 final Setlet<TypeInformation> flowsInto = new Setlet<TypeInformation>(); |
| 195 |
| 189 // The current [TypeInformation] in the analysis. | 196 // The current [TypeInformation] in the analysis. |
| 190 TypeInformation currentUser; | 197 TypeInformation currentUser; |
| 191 | |
| 192 // The list of found assignments to the container. | |
| 193 final List<TypeInformation> assignments = <TypeInformation>[]; | |
| 194 | |
| 195 bool callsGrowableMethod = false; | |
| 196 bool continueAnalyzing = true; | 198 bool continueAnalyzing = true; |
| 197 | |
| 198 static const int MAX_ANALYSIS_COUNT = 16; | |
| 199 final Setlet<Element> analyzedElements = new Setlet<Element>(); | |
| 200 | |
| 201 ContainerTracerVisitor(this.container, inferrer) | |
| 202 : this.inferrer = inferrer, this.compiler = inferrer.compiler; | |
| 203 | 199 |
| 204 void addNewEscapeInformation(TypeInformation info) { | 200 void addNewEscapeInformation(TypeInformation info) { |
| 205 if (container.flowsInto.contains(info)) return; | 201 if (flowsInto.contains(info)) return; |
| 206 container.flowsInto.add(info); | 202 flowsInto.add(info); |
| 207 workList.add(info); | 203 workList.add(info); |
| 208 } | 204 } |
| 209 | 205 |
| 210 List<TypeInformation> run() { | 206 void analyze() { |
| 211 // Collect the [TypeInformation] where the container can flow in, | 207 // Collect the [TypeInformation] where the container can flow in, |
| 212 // as well as the operations done on all these [TypeInformation]s. | 208 // as well as the operations done on all these [TypeInformation]s. |
| 213 addNewEscapeInformation(container); | 209 addNewEscapeInformation(tracedType); |
| 214 while (!workList.isEmpty) { | 210 while (!workList.isEmpty) { |
| 215 currentUser = workList.removeLast(); | 211 currentUser = workList.removeLast(); |
| 216 currentUser.users.forEach((TypeInformation info) { | 212 currentUser.users.forEach((TypeInformation info) { |
| 217 analyzedElements.add(info.owner); | 213 analyzedElements.add(info.owner); |
| 218 info.accept(this); | 214 info.accept(this); |
| 219 }); | 215 }); |
| 220 while (!containersToAnalyze.isEmpty) { | 216 while (!containersToAnalyze.isEmpty) { |
| 221 analyzeStoredIntoContainer(containersToAnalyze.removeLast()); | 217 analyzeStoredIntoContainer(containersToAnalyze.removeLast()); |
| 222 } | 218 } |
| 223 if (!continueAnalyzing) break; | 219 if (!continueAnalyzing) break; |
| 224 if (analyzedElements.length > MAX_ANALYSIS_COUNT) { | 220 if (analyzedElements.length > MAX_ANALYSIS_COUNT) { |
| 225 bailout('Too many users'); | 221 bailout('Too many users'); |
| 226 break; | 222 break; |
| 227 } | 223 } |
| 228 } | 224 } |
| 229 | |
| 230 if (continueAnalyzing) { | |
| 231 if (!callsGrowableMethod && container.inferredLength == null) { | |
| 232 container.inferredLength = container.originalLength; | |
| 233 } | |
| 234 return assignments; | |
| 235 } | |
| 236 return null; | |
| 237 } | 225 } |
| 238 | 226 |
| 239 void bailout(String reason) { | 227 void bailout(String reason) { |
| 240 if (_VERBOSE) { | 228 if (_VERBOSE) { |
| 241 ContainerTypeMask mask = container.type; | 229 print('Bailing out on $tracedType because: $reason'); |
| 242 print('Bailing out on ${mask.allocationNode} ${mask.allocationElement} ' | |
| 243 'because: $reason'); | |
| 244 } | 230 } |
| 245 continueAnalyzing = false; | 231 continueAnalyzing = false; |
| 246 callsGrowableMethod = true; | |
| 247 } | 232 } |
| 248 | 233 |
| 249 visitNarrowTypeInformation(NarrowTypeInformation info) { | 234 void visitNarrowTypeInformation(NarrowTypeInformation info) { |
| 250 addNewEscapeInformation(info); | 235 addNewEscapeInformation(info); |
| 251 } | 236 } |
| 252 | 237 |
| 253 visitPhiElementTypeInformation(PhiElementTypeInformation info) { | 238 void visitPhiElementTypeInformation(PhiElementTypeInformation info) { |
| 254 addNewEscapeInformation(info); | 239 addNewEscapeInformation(info); |
| 255 } | 240 } |
| 256 | 241 |
| 257 visitElementInContainerTypeInformation( | 242 void visitElementInContainerTypeInformation( |
| 258 ElementInContainerTypeInformation info) { | 243 ElementInContainerTypeInformation info) { |
| 259 addNewEscapeInformation(info); | 244 addNewEscapeInformation(info); |
| 260 } | 245 } |
| 261 | 246 |
| 262 visitListTypeInformation(ListTypeInformation info) { | 247 visitListTypeInformation(ListTypeInformation info) { |
| 263 containersToAnalyze.add(info); | 248 containersToAnalyze.add(info); |
| 264 } | 249 } |
| 265 | 250 |
| 266 visitMapTypeInformation(MapTypeInformation info) { | 251 void visitConcreteTypeInformation(ConcreteTypeInformation info) {} |
| 267 bailout('Stored in a map'); | |
| 268 } | |
| 269 | 252 |
| 270 visitConcreteTypeInformation(ConcreteTypeInformation info) {} | 253 void visitClosureTypeInformation(ClosureTypeInformation info) {} |
| 271 | 254 |
| 272 visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { | 255 void visitClosureCallSiteTypeInformation( |
| 273 bailout('Passed to a closure'); | 256 ClosureCallSiteTypeInformation info) {} |
| 274 } | |
| 275 | 257 |
| 276 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { | 258 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { |
| 277 Element called = info.calledElement; | 259 Element called = info.calledElement; |
| 278 if (called.isForeign(compiler) && called.name == 'JS') { | |
| 279 bailout('Used in JS ${info.call}'); | |
| 280 } | |
| 281 if (inferrer.types.getInferredTypeOf(called) == currentUser) { | 260 if (inferrer.types.getInferredTypeOf(called) == currentUser) { |
| 282 addNewEscapeInformation(info); | 261 addNewEscapeInformation(info); |
| 283 } | 262 } |
| 284 } | 263 } |
| 285 | 264 |
| 286 void analyzeStoredIntoContainer(ListTypeInformation container) { | 265 void analyzeStoredIntoContainer(ListTypeInformation container) { |
| 287 inferrer.analyzeContainer(container); | 266 inferrer.analyzeContainer(container); |
| 288 if (container.bailedOut) { | 267 if (container.bailedOut) { |
| 289 bailout('Stored in a container that bailed out'); | 268 bailout('Stored in a container that bailed out'); |
| 290 } else { | 269 } else { |
| 291 container.flowsInto.forEach((flow) { | 270 container.flowsInto.forEach((flow) { |
| 292 flow.users.forEach((user) { | 271 flow.users.forEach((user) { |
| 293 if (user is !DynamicCallSiteTypeInformation) return; | 272 if (user is !DynamicCallSiteTypeInformation) return; |
| 294 if (user.receiver != flow) return; | 273 if (user.receiver != flow) return; |
| 295 if (returnsElementTypeSet.contains(user.selector)) { | 274 if (returnsElementTypeSet.contains(user.selector)) { |
| 296 addNewEscapeInformation(user); | 275 addNewEscapeInformation(user); |
| 297 } else if (!doesNotEscapeElementSet.contains(user.selector.name)) { | 276 } else if (!doesNotEscapeElementSet.contains(user.selector.name)) { |
| 298 bailout('Escape from a container'); | 277 bailout('Escape from a container'); |
| 299 } | 278 } |
| 300 }); | 279 }); |
| 301 }); | 280 }); |
| 302 } | 281 } |
| 303 } | 282 } |
| 304 | 283 |
| 305 bool isAddedToContainer(DynamicCallSiteTypeInformation info) { | 284 bool isAddedToContainer(DynamicCallSiteTypeInformation info) { |
| 285 if (info.arguments == null) return false; |
| 306 var receiverType = info.receiver.type; | 286 var receiverType = info.receiver.type; |
| 307 if (!receiverType.isContainer) return false; | 287 if (!receiverType.isContainer) return false; |
| 308 String selectorName = info.selector.name; | 288 String selectorName = info.selector.name; |
| 309 List<TypeInformation> arguments = info.arguments.positional; | 289 List<TypeInformation> arguments = info.arguments.positional; |
| 310 return (selectorName == '[]=' && currentUser == arguments[1]) | 290 return (selectorName == '[]=' && currentUser == arguments[1]) |
| 311 || (selectorName == 'insert' && currentUser == arguments[0]) | 291 || (selectorName == 'insert' && currentUser == arguments[0]) |
| 312 || (selectorName == 'add' && currentUser == arguments[0]); | 292 || (selectorName == 'add' && currentUser == arguments[0]); |
| 313 } | 293 } |
| 314 | 294 |
| 295 void visitDynamicCallSiteTypeInformation( |
| 296 DynamicCallSiteTypeInformation info) { |
| 297 if (isAddedToContainer(info)) { |
| 298 ContainerTypeMask mask = info.receiver.type; |
| 299 if (mask.allocationNode != null) { |
| 300 ListTypeInformation container = |
| 301 inferrer.types.allocatedLists[mask.allocationNode]; |
| 302 containersToAnalyze.add(container); |
| 303 } else { |
| 304 // The [ContainerTypeMask] is a union of two containers, and |
| 305 // we lose track of where these containers have been allocated |
| 306 // at this point. |
| 307 bailout('Stored in too many containers'); |
| 308 } |
| 309 } |
| 310 |
| 311 Iterable<Element> inferredTargetTypes = info.targets.map((element) { |
| 312 return inferrer.types.getInferredTypeOf(element); |
| 313 }); |
| 314 if (inferredTargetTypes.any((user) => user == currentUser)) { |
| 315 addNewEscapeInformation(info); |
| 316 } |
| 317 } |
| 318 |
| 319 bool isParameterOfListAddingMethod(Element element) { |
| 320 if (!element.isParameter()) return false; |
| 321 if (element.getEnclosingClass() != compiler.backend.listImplementation) { |
| 322 return false; |
| 323 } |
| 324 Element method = element.enclosingElement; |
| 325 return (method.name == '[]=') |
| 326 || (method.name == 'add') |
| 327 || (method.name == 'insert'); |
| 328 } |
| 329 |
| 330 bool isClosure(Element element) { |
| 331 if (!element.isFunction()) return false; |
| 332 Element outermost = element.getOutermostEnclosingMemberOrTopLevel(); |
| 333 return outermost.declaration != element.declaration; |
| 334 } |
| 335 |
| 336 void visitElementTypeInformation(ElementTypeInformation info) { |
| 337 Element element = info.element; |
| 338 if (element.isParameter() |
| 339 && inferrer.isNativeElement(element.enclosingElement)) { |
| 340 bailout('Passed to a native method'); |
| 341 } |
| 342 if (info.isClosurized()) { |
| 343 bailout('Returned from a closurized method'); |
| 344 } |
| 345 if (isClosure(info.element)) { |
| 346 bailout('Returned from a closure'); |
| 347 } |
| 348 if (compiler.backend.isNeededForReflection(info.element)) { |
| 349 bailout('Escape in reflection'); |
| 350 } |
| 351 if (isParameterOfListAddingMethod(info.element)) { |
| 352 // These elements are being handled in |
| 353 // [visitDynamicCallSiteTypeInformation]. |
| 354 return; |
| 355 } |
| 356 addNewEscapeInformation(info); |
| 357 } |
| 358 } |
| 359 |
| 360 class ContainerTracerVisitor extends TracerVisitor { |
| 361 // The list of found assignments to the container. |
| 362 final List<TypeInformation> assignments = <TypeInformation>[]; |
| 363 bool callsGrowableMethod = false; |
| 364 |
| 365 ContainerTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer); |
| 366 |
| 367 List<TypeInformation> run() { |
| 368 analyze(); |
| 369 ListTypeInformation container = tracedType; |
| 370 if (continueAnalyzing) { |
| 371 if (!callsGrowableMethod && container.inferredLength == null) { |
| 372 container.inferredLength = container.originalLength; |
| 373 } |
| 374 container.flowsInto.addAll(flowsInto); |
| 375 return assignments; |
| 376 } else { |
| 377 callsGrowableMethod = true; |
| 378 return null; |
| 379 } |
| 380 } |
| 381 |
| 382 visitMapTypeInformation(MapTypeInformation info) { |
| 383 bailout('Stored in a map'); |
| 384 } |
| 385 |
| 386 visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { |
| 387 bailout('Passed to a closure'); |
| 388 } |
| 389 |
| 390 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { |
| 391 super.visitStaticCallSiteTypeInformation(info); |
| 392 Element called = info.calledElement; |
| 393 if (called.isForeign(compiler) && called.name == 'JS') { |
| 394 bailout('Used in JS ${info.call}'); |
| 395 } |
| 396 } |
| 397 |
| 315 visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { | 398 visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { |
| 399 super.visitDynamicCallSiteTypeInformation(info); |
| 316 Selector selector = info.selector; | 400 Selector selector = info.selector; |
| 317 String selectorName = selector.name; | 401 String selectorName = selector.name; |
| 318 if (currentUser == info.receiver) { | 402 if (currentUser == info.receiver) { |
| 319 if (!okSelectorsSet.contains(selectorName)) { | 403 if (!okSelectorsSet.contains(selectorName)) { |
| 320 if (selector.isCall()) { | 404 if (selector.isCall()) { |
| 321 int positionalLength = info.arguments.positional.length; | 405 int positionalLength = info.arguments.positional.length; |
| 322 if (selectorName == 'add') { | 406 if (selectorName == 'add') { |
| 323 if (positionalLength == 1) { | 407 if (positionalLength == 1) { |
| 324 assignments.add(info.arguments.positional[0]); | 408 assignments.add(info.arguments.positional[0]); |
| 325 } | 409 } |
| (...skipping 16 matching lines...) Expand all Loading... |
| 342 callsGrowableMethod = true; | 426 callsGrowableMethod = true; |
| 343 } | 427 } |
| 344 if (selectorName == 'length' && selector.isSetter()) { | 428 if (selectorName == 'length' && selector.isSetter()) { |
| 345 callsGrowableMethod = true; | 429 callsGrowableMethod = true; |
| 346 assignments.add(inferrer.types.nullType); | 430 assignments.add(inferrer.types.nullType); |
| 347 } | 431 } |
| 348 } else if (selector.isCall() | 432 } else if (selector.isCall() |
| 349 && !info.targets.every((element) => element.isFunction())) { | 433 && !info.targets.every((element) => element.isFunction())) { |
| 350 bailout('Passed to a closure'); | 434 bailout('Passed to a closure'); |
| 351 return; | 435 return; |
| 352 } else if (isAddedToContainer(info)) { | 436 } |
| 353 ContainerTypeMask mask = info.receiver.type; | 437 } |
| 354 if (mask.allocationNode != null) { | 438 } |
| 355 ListTypeInformation container = | 439 |
| 356 inferrer.types.allocatedLists[mask.allocationNode]; | 440 class ClosureTracerVisitor extends TracerVisitor { |
| 357 containersToAnalyze.add(container); | 441 ClosureTracerVisitor(tracedType, inferrer) : super(tracedType, inferrer); |
| 442 |
| 443 void run() { |
| 444 ClosureTypeInformation closure = tracedType; |
| 445 FunctionElement element = closure.element; |
| 446 element.functionSignature.forEachParameter((Element parameter) { |
| 447 ElementTypeInformation info = inferrer.types.getInferredTypeOf(parameter); |
| 448 info.abandonInferencing = false; |
| 449 }); |
| 450 analyze(); |
| 451 element.functionSignature.forEachParameter((Element parameter) { |
| 452 ElementTypeInformation info = inferrer.types.getInferredTypeOf(parameter); |
| 453 if (continueAnalyzing) { |
| 454 info.disableHandleSpecialCases = true; |
| 358 } else { | 455 } else { |
| 359 // The [ContainerTypeMask] is a union of two containers, and | 456 info.giveUp(inferrer); |
| 360 // we lose track of where these containers have been allocated | |
| 361 // at this point. | |
| 362 bailout('Stored in too many containers'); | |
| 363 } | 457 } |
| 364 } | 458 }); |
| 459 } |
| 365 | 460 |
| 366 if (info.targets | 461 visitMapTypeInformation(MapTypeInformation info) { |
| 367 .map((element) => inferrer.types.getInferredTypeOf(element)) | 462 bailout('Stored in a map'); |
| 368 .any((other) => other == currentUser)) { | 463 } |
| 369 addNewEscapeInformation(info); | 464 |
| 465 void analyzeCall(CallSiteTypeInformation info) { |
| 466 ClosureTypeInformation closure = tracedType; |
| 467 FunctionElement element = closure.element; |
| 468 Selector selector = info.selector; |
| 469 if (!selector.signatureApplies(element, compiler)) return; |
| 470 inferrer.updateParameterAssignments( |
| 471 info, element, info.arguments, selector, remove: false, |
| 472 addToQueue: false); |
| 473 } |
| 474 |
| 475 visitClosureCallSiteTypeInformation(ClosureCallSiteTypeInformation info) { |
| 476 super.visitClosureCallSiteTypeInformation(info); |
| 477 if (info.closure == currentUser) { |
| 478 analyzeCall(info); |
| 479 } else { |
| 480 bailout('Passed to a closure'); |
| 370 } | 481 } |
| 371 } | 482 } |
| 372 | 483 |
| 373 bool isClosure(Element element) { | 484 visitStaticCallSiteTypeInformation(StaticCallSiteTypeInformation info) { |
| 374 if (!element.isFunction()) return false; | 485 super.visitStaticCallSiteTypeInformation(info); |
| 375 Element outermost = element.getOutermostEnclosingMemberOrTopLevel(); | 486 Element called = info.calledElement; |
| 376 return outermost.declaration != element.declaration; | 487 if (called.isForeign(compiler) && called.name == 'JS') { |
| 488 bailout('Used in JS ${info.call}'); |
| 489 } |
| 377 } | 490 } |
| 378 | 491 |
| 379 bool isParameterOfListAddingMethod(Element element) { | 492 bool checkIfCurrentUser(element) { |
| 380 if (!element.isParameter()) return false; | 493 return inferrer.types.getInferredTypeOf(element) == currentUser; |
| 381 if (element.getEnclosingClass() != compiler.backend.listImplementation) { | |
| 382 return false; | |
| 383 } | |
| 384 Element method = element.enclosingElement; | |
| 385 return (method.name == '[]=') | |
| 386 || (method.name == 'add') | |
| 387 || (method.name == 'insert'); | |
| 388 } | 494 } |
| 389 | 495 |
| 390 visitElementTypeInformation(ElementTypeInformation info) { | 496 visitDynamicCallSiteTypeInformation(DynamicCallSiteTypeInformation info) { |
| 391 if (info.isClosurized()) { | 497 super.visitDynamicCallSiteTypeInformation(info); |
| 392 bailout('Returned from a closurized method'); | 498 if (info.selector.isCall()) { |
| 499 if (info.arguments.contains(currentUser) |
| 500 && !info.targets.every((element) => element.isFunction())) { |
| 501 bailout('Passed to a closure'); |
| 502 } else if (info.targets.any((element) => checkIfCurrentUser(element))) { |
| 503 analyzeCall(info); |
| 504 } |
| 393 } | 505 } |
| 394 if (isClosure(info.element)) { | |
| 395 bailout('Returned from a closure'); | |
| 396 } | |
| 397 if (compiler.backend.isNeededForReflection(info.element)) { | |
| 398 bailout('Escape in reflection'); | |
| 399 } | |
| 400 if (isParameterOfListAddingMethod(info.element)) { | |
| 401 // These elements are being handled in | |
| 402 // [visitDynamicCallSiteTypeInformation]. | |
| 403 return; | |
| 404 } | |
| 405 addNewEscapeInformation(info); | |
| 406 } | 506 } |
| 407 } | 507 } |
| OLD | NEW |