| 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 import '../compiler.dart' show Compiler; | 5 import '../compiler.dart' show Compiler; |
| 6 import '../elements/elements.dart'; | 6 import '../elements/elements.dart'; |
| 7 import '../js_backend/js_backend.dart'; | 7 import '../js_backend/js_backend.dart'; |
| 8 import '../types/types.dart'; | 8 import '../types/types.dart'; |
| 9 import '../universe/selector.dart' show Selector; | 9 import '../universe/selector.dart' show Selector; |
| 10 import '../world.dart' show ClassWorld, World; | 10 import '../world.dart' show ClassWorld, World; |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 91 } | 91 } |
| 92 } | 92 } |
| 93 // While processing the optimizable arithmetic instructions, we | 93 // While processing the optimizable arithmetic instructions, we |
| 94 // may discover better type information for dominated users of | 94 // may discover better type information for dominated users of |
| 95 // replaced operands, so we may need to take another stab at | 95 // replaced operands, so we may need to take another stab at |
| 96 // emptying the worklist afterwards. | 96 // emptying the worklist afterwards. |
| 97 processPendingOptimizations(); | 97 processPendingOptimizations(); |
| 98 } while (!worklist.isEmpty); | 98 } while (!worklist.isEmpty); |
| 99 } | 99 } |
| 100 | 100 |
| 101 | |
| 102 void addToWorkList(HInstruction instruction) { | 101 void addToWorkList(HInstruction instruction) { |
| 103 final int id = instruction.id; | 102 final int id = instruction.id; |
| 104 | 103 |
| 105 if (!workmap.containsKey(id)) { | 104 if (!workmap.containsKey(id)) { |
| 106 worklist.add(id); | 105 worklist.add(id); |
| 107 workmap[id] = instruction; | 106 workmap[id] = instruction; |
| 108 } | 107 } |
| 109 } | 108 } |
| 110 | 109 |
| 111 TypeMask visitBinaryArithmetic(HBinaryArithmetic instruction) { | 110 TypeMask visitBinaryArithmetic(HBinaryArithmetic instruction) { |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 168 } | 167 } |
| 169 | 168 |
| 170 TypeMask visitTypeConversion(HTypeConversion instruction) { | 169 TypeMask visitTypeConversion(HTypeConversion instruction) { |
| 171 HInstruction input = instruction.checkedInput; | 170 HInstruction input = instruction.checkedInput; |
| 172 TypeMask inputType = input.instructionType; | 171 TypeMask inputType = input.instructionType; |
| 173 TypeMask checkedType = instruction.checkedType; | 172 TypeMask checkedType = instruction.checkedType; |
| 174 if (instruction.isArgumentTypeCheck || instruction.isReceiverTypeCheck) { | 173 if (instruction.isArgumentTypeCheck || instruction.isReceiverTypeCheck) { |
| 175 // We must make sure a type conversion for receiver or argument check | 174 // We must make sure a type conversion for receiver or argument check |
| 176 // does not try to do an int check, because an int check is not enough. | 175 // does not try to do an int check, because an int check is not enough. |
| 177 // We only do an int check if the input is integer or null. | 176 // We only do an int check if the input is integer or null. |
| 178 if (checkedType.containsOnlyNum(classWorld) | 177 if (checkedType.containsOnlyNum(classWorld) && |
| 179 && !checkedType.containsOnlyDouble(classWorld) | 178 !checkedType.containsOnlyDouble(classWorld) && |
| 180 && input.isIntegerOrNull(compiler)) { | 179 input.isIntegerOrNull(compiler)) { |
| 181 instruction.checkedType = backend.intType; | 180 instruction.checkedType = backend.intType; |
| 182 } else if (checkedType.containsOnlyInt(classWorld) | 181 } else if (checkedType.containsOnlyInt(classWorld) && |
| 183 && !input.isIntegerOrNull(compiler)) { | 182 !input.isIntegerOrNull(compiler)) { |
| 184 instruction.checkedType = backend.numType; | 183 instruction.checkedType = backend.numType; |
| 185 } | 184 } |
| 186 } | 185 } |
| 187 | 186 |
| 188 TypeMask outputType = checkedType.intersection(inputType, classWorld); | 187 TypeMask outputType = checkedType.intersection(inputType, classWorld); |
| 189 if (outputType.isEmpty) { | 188 if (outputType.isEmpty) { |
| 190 // Intersection of double and integer conflicts (is empty), but JS numbers | 189 // Intersection of double and integer conflicts (is empty), but JS numbers |
| 191 // can be both int and double at the same time. For example, the input | 190 // can be both int and double at the same time. For example, the input |
| 192 // can be a literal double '8.0' that is marked as an integer (because 'is | 191 // can be a literal double '8.0' that is marked as an integer (because 'is |
| 193 // int' will return 'true'). What we really need to do is make the | 192 // int' will return 'true'). What we really need to do is make the |
| 194 // overlap between int and double values explicit in the TypeMask system. | 193 // overlap between int and double values explicit in the TypeMask system. |
| 195 if (inputType.containsOnlyInt(classWorld) | 194 if (inputType.containsOnlyInt(classWorld) && |
| 196 && checkedType.containsOnlyDouble(classWorld)) { | 195 checkedType.containsOnlyDouble(classWorld)) { |
| 197 if (inputType.isNullable && checkedType.isNullable) { | 196 if (inputType.isNullable && checkedType.isNullable) { |
| 198 outputType = backend.doubleType.nullable(); | 197 outputType = backend.doubleType.nullable(); |
| 199 } else { | 198 } else { |
| 200 outputType = backend.doubleType; | 199 outputType = backend.doubleType; |
| 201 } | 200 } |
| 202 } | 201 } |
| 203 } | 202 } |
| 204 if (inputType != outputType) { | 203 if (inputType != outputType) { |
| 205 input.replaceAllUsersDominatedBy(instruction.next, instruction); | 204 input.replaceAllUsersDominatedBy(instruction.next, instruction); |
| 206 } | 205 } |
| 207 return outputType; | 206 return outputType; |
| 208 } | 207 } |
| 209 | 208 |
| 210 TypeMask visitTypeKnown(HTypeKnown instruction) { | 209 TypeMask visitTypeKnown(HTypeKnown instruction) { |
| 211 HInstruction input = instruction.checkedInput; | 210 HInstruction input = instruction.checkedInput; |
| 212 TypeMask inputType = input.instructionType; | 211 TypeMask inputType = input.instructionType; |
| 213 TypeMask outputType = | 212 TypeMask outputType = |
| 214 instruction.knownType.intersection(inputType, classWorld); | 213 instruction.knownType.intersection(inputType, classWorld); |
| 215 if (inputType != outputType) { | 214 if (inputType != outputType) { |
| 216 input.replaceAllUsersDominatedBy(instruction.next, instruction); | 215 input.replaceAllUsersDominatedBy(instruction.next, instruction); |
| 217 } | 216 } |
| 218 return outputType; | 217 return outputType; |
| 219 } | 218 } |
| 220 | 219 |
| 221 void convertInput(HInvokeDynamic instruction, | 220 void convertInput( |
| 222 HInstruction input, | 221 HInvokeDynamic instruction, HInstruction input, TypeMask type, int kind) { |
| 223 TypeMask type, | |
| 224 int kind) { | |
| 225 Selector selector = (kind == HTypeConversion.RECEIVER_TYPE_CHECK) | 222 Selector selector = (kind == HTypeConversion.RECEIVER_TYPE_CHECK) |
| 226 ? instruction.selector | 223 ? instruction.selector |
| 227 : null; | 224 : null; |
| 228 HTypeConversion converted = new HTypeConversion( | 225 HTypeConversion converted = |
| 229 null, kind, type, input, selector) | 226 new HTypeConversion(null, kind, type, input, selector) |
| 230 ..sourceInformation = instruction.sourceInformation; | 227 ..sourceInformation = instruction.sourceInformation; |
| 231 instruction.block.addBefore(instruction, converted); | 228 instruction.block.addBefore(instruction, converted); |
| 232 input.replaceAllUsersDominatedBy(instruction, converted); | 229 input.replaceAllUsersDominatedBy(instruction, converted); |
| 233 } | 230 } |
| 234 | 231 |
| 235 bool isCheckEnoughForNsmOrAe(HInstruction instruction, | 232 bool isCheckEnoughForNsmOrAe(HInstruction instruction, TypeMask type) { |
| 236 TypeMask type) { | |
| 237 // In some cases, we want the receiver to be an integer, | 233 // In some cases, we want the receiver to be an integer, |
| 238 // but that does not mean we will get a NoSuchMethodError | 234 // but that does not mean we will get a NoSuchMethodError |
| 239 // if it's not: the receiver could be a double. | 235 // if it's not: the receiver could be a double. |
| 240 if (type.containsOnlyInt(classWorld)) { | 236 if (type.containsOnlyInt(classWorld)) { |
| 241 // If the instruction's type is integer or null, the codegen | 237 // If the instruction's type is integer or null, the codegen |
| 242 // will emit a null check, which is enough to know if it will | 238 // will emit a null check, which is enough to know if it will |
| 243 // hit a noSuchMethod. | 239 // hit a noSuchMethod. |
| 244 return instruction.isIntegerOrNull(compiler); | 240 return instruction.isIntegerOrNull(compiler); |
| 245 } | 241 } |
| 246 return true; | 242 return true; |
| 247 } | 243 } |
| 248 | 244 |
| 249 // Add a receiver type check when the call can only hit | 245 // Add a receiver type check when the call can only hit |
| 250 // [noSuchMethod] if the receiver is not of a specific type. | 246 // [noSuchMethod] if the receiver is not of a specific type. |
| 251 // Return true if the receiver type check was added. | 247 // Return true if the receiver type check was added. |
| 252 bool checkReceiver(HInvokeDynamic instruction) { | 248 bool checkReceiver(HInvokeDynamic instruction) { |
| 253 assert(instruction.isInterceptedCall); | 249 assert(instruction.isInterceptedCall); |
| 254 HInstruction receiver = instruction.inputs[1]; | 250 HInstruction receiver = instruction.inputs[1]; |
| 255 if (receiver.isNumber(compiler)) return false; | 251 if (receiver.isNumber(compiler)) return false; |
| 256 if (receiver.isNumberOrNull(compiler)) { | 252 if (receiver.isNumberOrNull(compiler)) { |
| 257 convertInput(instruction, | 253 convertInput( |
| 258 receiver, | 254 instruction, |
| 259 receiver.instructionType.nonNullable(), | 255 receiver, |
| 260 HTypeConversion.RECEIVER_TYPE_CHECK); | 256 receiver.instructionType.nonNullable(), |
| 257 HTypeConversion.RECEIVER_TYPE_CHECK); |
| 261 return true; | 258 return true; |
| 262 } else if (instruction.element == null) { | 259 } else if (instruction.element == null) { |
| 263 Iterable<Element> targets = | 260 Iterable<Element> targets = compiler.world.allFunctions |
| 264 compiler.world.allFunctions.filter( | 261 .filter(instruction.selector, instruction.mask); |
| 265 instruction.selector, instruction.mask); | |
| 266 if (targets.length == 1) { | 262 if (targets.length == 1) { |
| 267 Element target = targets.first; | 263 Element target = targets.first; |
| 268 ClassElement cls = target.enclosingClass; | 264 ClassElement cls = target.enclosingClass; |
| 269 TypeMask type = new TypeMask.nonNullSubclass( | 265 TypeMask type = |
| 270 cls.declaration, classWorld); | 266 new TypeMask.nonNullSubclass(cls.declaration, classWorld); |
| 271 // TODO(ngeoffray): We currently only optimize on primitive | 267 // TODO(ngeoffray): We currently only optimize on primitive |
| 272 // types. | 268 // types. |
| 273 if (!type.satisfies(backend.helpers.jsIndexableClass, classWorld) && | 269 if (!type.satisfies(backend.helpers.jsIndexableClass, classWorld) && |
| 274 !type.containsOnlyNum(classWorld) && | 270 !type.containsOnlyNum(classWorld) && |
| 275 !type.containsOnlyBool(classWorld)) { | 271 !type.containsOnlyBool(classWorld)) { |
| 276 return false; | 272 return false; |
| 277 } | 273 } |
| 278 if (!isCheckEnoughForNsmOrAe(receiver, type)) return false; | 274 if (!isCheckEnoughForNsmOrAe(receiver, type)) return false; |
| 279 instruction.element = target; | 275 instruction.element = target; |
| 280 convertInput(instruction, | 276 convertInput( |
| 281 receiver, | 277 instruction, receiver, type, HTypeConversion.RECEIVER_TYPE_CHECK); |
| 282 type, | |
| 283 HTypeConversion.RECEIVER_TYPE_CHECK); | |
| 284 return true; | 278 return true; |
| 285 } | 279 } |
| 286 } | 280 } |
| 287 return false; | 281 return false; |
| 288 } | 282 } |
| 289 | 283 |
| 290 // Add an argument type check if the argument is not of a type | 284 // Add an argument type check if the argument is not of a type |
| 291 // expected by the call. | 285 // expected by the call. |
| 292 // Return true if the argument type check was added. | 286 // Return true if the argument type check was added. |
| 293 bool checkArgument(HInvokeDynamic instruction) { | 287 bool checkArgument(HInvokeDynamic instruction) { |
| 294 // We want the right error in checked mode. | 288 // We want the right error in checked mode. |
| 295 if (compiler.options.enableTypeAssertions) return false; | 289 if (compiler.options.enableTypeAssertions) return false; |
| 296 HInstruction left = instruction.inputs[1]; | 290 HInstruction left = instruction.inputs[1]; |
| 297 HInstruction right = instruction.inputs[2]; | 291 HInstruction right = instruction.inputs[2]; |
| 298 | 292 |
| 299 Selector selector = instruction.selector; | 293 Selector selector = instruction.selector; |
| 300 if (selector.isOperator && left.isNumber(compiler)) { | 294 if (selector.isOperator && left.isNumber(compiler)) { |
| 301 if (right.isNumber(compiler)) return false; | 295 if (right.isNumber(compiler)) return false; |
| 302 TypeMask type = right.isIntegerOrNull(compiler) | 296 TypeMask type = right.isIntegerOrNull(compiler) |
| 303 ? right.instructionType.nonNullable() | 297 ? right.instructionType.nonNullable() |
| 304 : backend.numType; | 298 : backend.numType; |
| 305 // TODO(ngeoffray): Some number operations don't have a builtin | 299 // TODO(ngeoffray): Some number operations don't have a builtin |
| 306 // variant and will do the check in their method anyway. We | 300 // variant and will do the check in their method anyway. We |
| 307 // still add a check because it allows to GVN these operations, | 301 // still add a check because it allows to GVN these operations, |
| 308 // but we should find a better way. | 302 // but we should find a better way. |
| 309 convertInput(instruction, | 303 convertInput( |
| 310 right, | 304 instruction, right, type, HTypeConversion.ARGUMENT_TYPE_CHECK); |
| 311 type, | |
| 312 HTypeConversion.ARGUMENT_TYPE_CHECK); | |
| 313 return true; | 305 return true; |
| 314 } | 306 } |
| 315 return false; | 307 return false; |
| 316 } | 308 } |
| 317 | 309 |
| 318 void processPendingOptimizations() { | 310 void processPendingOptimizations() { |
| 319 pendingOptimizations.forEach((instruction, action) => action()); | 311 pendingOptimizations.forEach((instruction, action) => action()); |
| 320 pendingOptimizations.clear(); | 312 pendingOptimizations.clear(); |
| 321 } | 313 } |
| 322 | 314 |
| 323 void addDependentInstructionsToWorkList(HInstruction instruction) { | 315 void addDependentInstructionsToWorkList(HInstruction instruction) { |
| 324 for (int i = 0, length = instruction.usedBy.length; i < length; i++) { | 316 for (int i = 0, length = instruction.usedBy.length; i < length; i++) { |
| 325 // The type propagator only propagates types forward. We | 317 // The type propagator only propagates types forward. We |
| 326 // thus only need to add the users of the [instruction] to the list. | 318 // thus only need to add the users of the [instruction] to the list. |
| 327 addToWorkList(instruction.usedBy[i]); | 319 addToWorkList(instruction.usedBy[i]); |
| 328 } | 320 } |
| 329 } | 321 } |
| 330 | 322 |
| 331 void addAllUsersBut(HInvokeDynamic invoke, HInstruction instruction) { | 323 void addAllUsersBut(HInvokeDynamic invoke, HInstruction instruction) { |
| 332 instruction.usedBy.forEach((HInstruction user) { | 324 instruction.usedBy.forEach((HInstruction user) { |
| 333 if (user != invoke) addToWorkList(user); | 325 if (user != invoke) addToWorkList(user); |
| 334 }); | 326 }); |
| 335 } | 327 } |
| 336 | 328 |
| 337 TypeMask visitInvokeDynamic(HInvokeDynamic instruction) { | 329 TypeMask visitInvokeDynamic(HInvokeDynamic instruction) { |
| 338 if (instruction.isInterceptedCall) { | 330 if (instruction.isInterceptedCall) { |
| 339 // We cannot do the following optimization now, because we have | 331 // We cannot do the following optimization now, because we have |
| 340 // to wait for the type propagation to be stable. The receiver | 332 // to wait for the type propagation to be stable. The receiver |
| 341 // of [instruction] might move from number to dynamic. | 333 // of [instruction] might move from number to dynamic. |
| 342 pendingOptimizations.putIfAbsent(instruction, () => () { | 334 pendingOptimizations.putIfAbsent( |
| 343 Selector selector = instruction.selector; | 335 instruction, |
| 344 if (selector.isOperator && selector.name != '==') { | 336 () => () { |
| 345 if (checkReceiver(instruction)) { | 337 Selector selector = instruction.selector; |
| 346 addAllUsersBut(instruction, instruction.inputs[1]); | 338 if (selector.isOperator && selector.name != '==') { |
| 347 } | 339 if (checkReceiver(instruction)) { |
| 348 if (!selector.isUnaryOperator && | 340 addAllUsersBut(instruction, instruction.inputs[1]); |
| 349 checkArgument(instruction)) { | 341 } |
| 350 addAllUsersBut(instruction, instruction.inputs[2]); | 342 if (!selector.isUnaryOperator && checkArgument(instruction)) { |
| 351 } | 343 addAllUsersBut(instruction, instruction.inputs[2]); |
| 352 } | 344 } |
| 353 }); | 345 } |
| 346 }); |
| 354 } | 347 } |
| 355 | 348 |
| 356 HInstruction receiver = instruction.getDartReceiver(compiler); | 349 HInstruction receiver = instruction.getDartReceiver(compiler); |
| 357 TypeMask receiverType = receiver.instructionType; | 350 TypeMask receiverType = receiver.instructionType; |
| 358 instruction.mask = receiverType; | 351 instruction.mask = receiverType; |
| 359 | 352 |
| 360 // Try to specialize the receiver after this call. | 353 // Try to specialize the receiver after this call. |
| 361 if (receiver.dominatedUsers(instruction).length != 1 | 354 if (receiver.dominatedUsers(instruction).length != 1 && |
| 362 && !instruction.selector.isClosureCall) { | 355 !instruction.selector.isClosureCall) { |
| 363 TypeMask newType = compiler.world.allFunctions.receiverType( | 356 TypeMask newType = compiler.world.allFunctions |
| 364 instruction.selector, instruction.mask); | 357 .receiverType(instruction.selector, instruction.mask); |
| 365 newType = newType.intersection(receiverType, classWorld); | 358 newType = newType.intersection(receiverType, classWorld); |
| 366 var next = instruction.next; | 359 var next = instruction.next; |
| 367 if (next is HTypeKnown && next.checkedInput == receiver) { | 360 if (next is HTypeKnown && next.checkedInput == receiver) { |
| 368 // We already have refined [receiver]. We still update the | 361 // We already have refined [receiver]. We still update the |
| 369 // type of the [HTypeKnown] instruction because it may have | 362 // type of the [HTypeKnown] instruction because it may have |
| 370 // been refined with a correct type at the time, but | 363 // been refined with a correct type at the time, but |
| 371 // incorrect now. | 364 // incorrect now. |
| 372 if (next.instructionType != newType) { | 365 if (next.instructionType != newType) { |
| 373 next.knownType = next.instructionType = newType; | 366 next.knownType = next.instructionType = newType; |
| 374 addDependentInstructionsToWorkList(next); | 367 addDependentInstructionsToWorkList(next); |
| 375 } | 368 } |
| 376 } else if (newType != receiverType) { | 369 } else if (newType != receiverType) { |
| 377 // Insert a refinement node after the call and update all | 370 // Insert a refinement node after the call and update all |
| 378 // users dominated by the call to use that node instead of | 371 // users dominated by the call to use that node instead of |
| 379 // [receiver]. | 372 // [receiver]. |
| 380 HTypeKnown converted = | 373 HTypeKnown converted = |
| 381 new HTypeKnown.witnessed(newType, receiver, instruction); | 374 new HTypeKnown.witnessed(newType, receiver, instruction); |
| 382 instruction.block.addBefore(instruction.next, converted); | 375 instruction.block.addBefore(instruction.next, converted); |
| 383 receiver.replaceAllUsersDominatedBy(converted.next, converted); | 376 receiver.replaceAllUsersDominatedBy(converted.next, converted); |
| 384 addDependentInstructionsToWorkList(converted); | 377 addDependentInstructionsToWorkList(converted); |
| 385 } | 378 } |
| 386 } | 379 } |
| 387 | 380 |
| 388 return instruction.specializer.computeTypeFromInputTypes( | 381 return instruction.specializer |
| 389 instruction, compiler); | 382 .computeTypeFromInputTypes(instruction, compiler); |
| 390 } | 383 } |
| 391 } | 384 } |
| OLD | NEW |