| 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 ssa; | 5 part of ssa; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * [InvokeDynamicSpecializer] and its subclasses are helpers to | 8 * [InvokeDynamicSpecializer] and its subclasses are helpers to |
| 9 * optimize intercepted dynamic calls. It knows what input types | 9 * optimize intercepted dynamic calls. It knows what input types |
| 10 * would be beneficial for performance, and how to change a invoke | 10 * would be beneficial for performance, and how to change a invoke |
| 11 * dynamic to a builtin instruction (e.g. HIndex, HBitNot). | 11 * dynamic to a builtin instruction (e.g. HIndex, HBitNot). |
| 12 */ | 12 */ |
| 13 class InvokeDynamicSpecializer { | 13 class InvokeDynamicSpecializer { |
| 14 const InvokeDynamicSpecializer(); | 14 const InvokeDynamicSpecializer(); |
| 15 | 15 |
| 16 HType computeDesiredTypeForInput(HInvokeDynamicMethod instruction, | 16 HType computeDesiredTypeForInput(HInvokeDynamic instruction, |
| 17 HInstruction input, | 17 HInstruction input, |
| 18 HTypeMap types, | 18 HTypeMap types, |
| 19 Compiler compiler) { | 19 Compiler compiler) { |
| 20 return HType.UNKNOWN; | 20 return HType.UNKNOWN; |
| 21 } | 21 } |
| 22 | 22 |
| 23 HType computeTypeFromInputTypes(HInvokeDynamicMethod instruction, | 23 HType computeTypeFromInputTypes(HInvokeDynamic instruction, |
| 24 HTypeMap types, | 24 HTypeMap types, |
| 25 Compiler compiler) { | 25 Compiler compiler) { |
| 26 return HType.UNKNOWN; | 26 return HType.UNKNOWN; |
| 27 } | 27 } |
| 28 | 28 |
| 29 HInstruction tryConvertToBuiltin(HInvokeDynamicMethod instruction, | 29 HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, |
| 30 HTypeMap types) { | 30 HTypeMap types) { |
| 31 return null; | 31 return null; |
| 32 } | 32 } |
| 33 | 33 |
| 34 Operation operation(ConstantSystem constantSystem) => null; | 34 Operation operation(ConstantSystem constantSystem) => null; |
| 35 | 35 |
| 36 static InvokeDynamicSpecializer lookupSpecializer(Selector selector) { | 36 static InvokeDynamicSpecializer lookupSpecializer(Selector selector) { |
| 37 if (selector.kind == SelectorKind.INDEX) { | 37 if (selector.kind == SelectorKind.INDEX) { |
| 38 return selector.name == const SourceString('[]') | 38 return selector.name == const SourceString('[]') |
| 39 ? const IndexSpecializer() | 39 ? const IndexSpecializer() |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 77 return const GreaterEqualSpecializer(); | 77 return const GreaterEqualSpecializer(); |
| 78 } | 78 } |
| 79 } | 79 } |
| 80 return const InvokeDynamicSpecializer(); | 80 return const InvokeDynamicSpecializer(); |
| 81 } | 81 } |
| 82 } | 82 } |
| 83 | 83 |
| 84 class IndexAssignSpecializer extends InvokeDynamicSpecializer { | 84 class IndexAssignSpecializer extends InvokeDynamicSpecializer { |
| 85 const IndexAssignSpecializer(); | 85 const IndexAssignSpecializer(); |
| 86 | 86 |
| 87 HType computeDesiredTypeForInput(HInvokeDynamicMethod instruction, | 87 HType computeDesiredTypeForInput(HInvokeDynamic instruction, |
| 88 HInstruction input, | 88 HInstruction input, |
| 89 HTypeMap types, | 89 HTypeMap types, |
| 90 Compiler compiler) { | 90 Compiler compiler) { |
| 91 HInstruction index = instruction.inputs[2]; | 91 HInstruction index = instruction.inputs[2]; |
| 92 if (input == instruction.inputs[1] && | 92 if (input == instruction.inputs[1] && |
| 93 (index.isTypeUnknown(types) || index.isNumber(types))) { | 93 (index.isTypeUnknown(types) || index.isNumber(types))) { |
| 94 return HType.MUTABLE_ARRAY; | 94 return HType.MUTABLE_ARRAY; |
| 95 } | 95 } |
| 96 // The index should be an int when the receiver is a string or array. | 96 // The index should be an int when the receiver is a string or array. |
| 97 // However it turns out that inserting an integer check in the optimized | 97 // However it turns out that inserting an integer check in the optimized |
| 98 // version is cheaper than having another bailout case. This is true, | 98 // version is cheaper than having another bailout case. This is true, |
| 99 // because the integer check will simply throw if it fails. | 99 // because the integer check will simply throw if it fails. |
| 100 return HType.UNKNOWN; | 100 return HType.UNKNOWN; |
| 101 } | 101 } |
| 102 | 102 |
| 103 HInstruction tryConvertToBuiltin(HInvokeDynamicMethod instruction, | 103 HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, |
| 104 HTypeMap types) { | 104 HTypeMap types) { |
| 105 if (instruction.inputs[1].isMutableArray(types)) { | 105 if (instruction.inputs[1].isMutableArray(types)) { |
| 106 return new HIndexAssign(instruction.inputs[1], | 106 return new HIndexAssign(instruction.inputs[1], |
| 107 instruction.inputs[2], | 107 instruction.inputs[2], |
| 108 instruction.inputs[3]); | 108 instruction.inputs[3]); |
| 109 } | 109 } |
| 110 return null; | 110 return null; |
| 111 } | 111 } |
| 112 } | 112 } |
| 113 | 113 |
| 114 class IndexSpecializer extends InvokeDynamicSpecializer { | 114 class IndexSpecializer extends InvokeDynamicSpecializer { |
| 115 const IndexSpecializer(); | 115 const IndexSpecializer(); |
| 116 | 116 |
| 117 HType computeDesiredTypeForInput(HInvokeDynamicMethod instruction, | 117 HType computeDesiredTypeForInput(HInvokeDynamic instruction, |
| 118 HInstruction input, | 118 HInstruction input, |
| 119 HTypeMap types, | 119 HTypeMap types, |
| 120 Compiler compiler) { | 120 Compiler compiler) { |
| 121 HInstruction index = instruction.inputs[2]; | 121 HInstruction index = instruction.inputs[2]; |
| 122 if (input == instruction.inputs[1] && | 122 if (input == instruction.inputs[1] && |
| 123 (index.isTypeUnknown(types) || index.isNumber(types))) { | 123 (index.isTypeUnknown(types) || index.isNumber(types))) { |
| 124 return HType.INDEXABLE_PRIMITIVE; | 124 return HType.INDEXABLE_PRIMITIVE; |
| 125 } | 125 } |
| 126 // The index should be an int when the receiver is a string or array. | 126 // The index should be an int when the receiver is a string or array. |
| 127 // However it turns out that inserting an integer check in the optimized | 127 // However it turns out that inserting an integer check in the optimized |
| 128 // version is cheaper than having another bailout case. This is true, | 128 // version is cheaper than having another bailout case. This is true, |
| 129 // because the integer check will simply throw if it fails. | 129 // because the integer check will simply throw if it fails. |
| 130 return HType.UNKNOWN; | 130 return HType.UNKNOWN; |
| 131 } | 131 } |
| 132 | 132 |
| 133 HInstruction tryConvertToBuiltin(HInvokeDynamicMethod instruction, | 133 HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, |
| 134 HTypeMap types) { | 134 HTypeMap types) { |
| 135 if (instruction.inputs[1].isIndexablePrimitive(types)) { | 135 if (instruction.inputs[1].isIndexablePrimitive(types)) { |
| 136 return new HIndex(instruction.inputs[1], instruction.inputs[2]); | 136 return new HIndex(instruction.inputs[1], instruction.inputs[2]); |
| 137 } | 137 } |
| 138 return null; | 138 return null; |
| 139 } | 139 } |
| 140 } | 140 } |
| 141 | 141 |
| 142 class BitNotSpecializer extends InvokeDynamicSpecializer { | 142 class BitNotSpecializer extends InvokeDynamicSpecializer { |
| 143 const BitNotSpecializer(); | 143 const BitNotSpecializer(); |
| 144 | 144 |
| 145 UnaryOperation operation(ConstantSystem constantSystem) { | 145 UnaryOperation operation(ConstantSystem constantSystem) { |
| 146 return constantSystem.bitNot; | 146 return constantSystem.bitNot; |
| 147 } | 147 } |
| 148 | 148 |
| 149 HType computeDesiredTypeForInput(HInvokeDynamicMethod instruction, | 149 HType computeDesiredTypeForInput(HInvokeDynamic instruction, |
| 150 HInstruction input, | 150 HInstruction input, |
| 151 HTypeMap types, | 151 HTypeMap types, |
| 152 Compiler compiler) { | 152 Compiler compiler) { |
| 153 if (input == instruction.inputs[1]) { | 153 if (input == instruction.inputs[1]) { |
| 154 HType propagatedType = types[instruction]; | 154 HType propagatedType = types[instruction]; |
| 155 if (propagatedType.isUnknown() || propagatedType.isNumber()) { | 155 if (propagatedType.isUnknown() || propagatedType.isNumber()) { |
| 156 return HType.INTEGER; | 156 return HType.INTEGER; |
| 157 } | 157 } |
| 158 } | 158 } |
| 159 return HType.UNKNOWN; | 159 return HType.UNKNOWN; |
| 160 } | 160 } |
| 161 | 161 |
| 162 HType computeTypeFromInputTypes(HInvokeDynamicMethod instruction, | 162 HType computeTypeFromInputTypes(HInvokeDynamic instruction, |
| 163 HTypeMap types, | 163 HTypeMap types, |
| 164 Compiler compiler) { | 164 Compiler compiler) { |
| 165 // All bitwise operations on primitive types either produce an | 165 // All bitwise operations on primitive types either produce an |
| 166 // integer or throw an error. | 166 // integer or throw an error. |
| 167 if (instruction.inputs[1].isPrimitive(types)) return HType.INTEGER; | 167 if (instruction.inputs[1].isPrimitive(types)) return HType.INTEGER; |
| 168 return HType.UNKNOWN; | 168 return HType.UNKNOWN; |
| 169 } | 169 } |
| 170 | 170 |
| 171 HInstruction tryConvertToBuiltin(HInvokeDynamicMethod instruction, | 171 HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, |
| 172 HTypeMap types) { | 172 HTypeMap types) { |
| 173 HInstruction input = instruction.inputs[1]; | 173 HInstruction input = instruction.inputs[1]; |
| 174 if (input.isNumber(types)) return new HBitNot(input); | 174 if (input.isNumber(types)) return new HBitNot(input); |
| 175 return null; | 175 return null; |
| 176 } | 176 } |
| 177 } | 177 } |
| 178 | 178 |
| 179 class UnaryNegateSpecializer extends InvokeDynamicSpecializer { | 179 class UnaryNegateSpecializer extends InvokeDynamicSpecializer { |
| 180 const UnaryNegateSpecializer(); | 180 const UnaryNegateSpecializer(); |
| 181 | 181 |
| 182 UnaryOperation operation(ConstantSystem constantSystem) { | 182 UnaryOperation operation(ConstantSystem constantSystem) { |
| 183 return constantSystem.negate; | 183 return constantSystem.negate; |
| 184 } | 184 } |
| 185 | 185 |
| 186 HType computeDesiredTypeForInput(HInvokeDynamicMethod instruction, | 186 HType computeDesiredTypeForInput(HInvokeDynamic instruction, |
| 187 HInstruction input, | 187 HInstruction input, |
| 188 HTypeMap types, | 188 HTypeMap types, |
| 189 Compiler compiler) { | 189 Compiler compiler) { |
| 190 if (input == instruction.inputs[1]) { | 190 if (input == instruction.inputs[1]) { |
| 191 HType propagatedType = types[instruction]; | 191 HType propagatedType = types[instruction]; |
| 192 // If the outgoing type should be a number (integer, double or both) we | 192 // If the outgoing type should be a number (integer, double or both) we |
| 193 // want the outgoing type to be the input too. | 193 // want the outgoing type to be the input too. |
| 194 // If we don't know the outgoing type we try to make it a number. | 194 // If we don't know the outgoing type we try to make it a number. |
| 195 if (propagatedType.isNumber()) return propagatedType; | 195 if (propagatedType.isNumber()) return propagatedType; |
| 196 if (propagatedType.isUnknown()) return HType.NUMBER; | 196 if (propagatedType.isUnknown()) return HType.NUMBER; |
| 197 } | 197 } |
| 198 return HType.UNKNOWN; | 198 return HType.UNKNOWN; |
| 199 } | 199 } |
| 200 | 200 |
| 201 HType computeTypeFromInputTypes(HInvokeDynamicMethod instruction, | 201 HType computeTypeFromInputTypes(HInvokeDynamic instruction, |
| 202 HTypeMap types, | 202 HTypeMap types, |
| 203 Compiler compiler) { | 203 Compiler compiler) { |
| 204 HType operandType = types[instruction.inputs[1]]; | 204 HType operandType = types[instruction.inputs[1]]; |
| 205 if (operandType.isNumber()) return operandType; | 205 if (operandType.isNumber()) return operandType; |
| 206 return HType.UNKNOWN; | 206 return HType.UNKNOWN; |
| 207 } | 207 } |
| 208 | 208 |
| 209 HInstruction tryConvertToBuiltin(HInvokeDynamicMethod instruction, | 209 HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, |
| 210 HTypeMap types) { | 210 HTypeMap types) { |
| 211 HInstruction input = instruction.inputs[1]; | 211 HInstruction input = instruction.inputs[1]; |
| 212 if (input.isNumber(types)) return new HNegate(input); | 212 if (input.isNumber(types)) return new HNegate(input); |
| 213 return null; | 213 return null; |
| 214 } | 214 } |
| 215 } | 215 } |
| 216 | 216 |
| 217 abstract class BinaryArithmeticSpecializer extends InvokeDynamicSpecializer { | 217 abstract class BinaryArithmeticSpecializer extends InvokeDynamicSpecializer { |
| 218 const BinaryArithmeticSpecializer(); | 218 const BinaryArithmeticSpecializer(); |
| 219 | 219 |
| 220 HType computeTypeFromInputTypes(HInvokeDynamicMethod instruction, | 220 HType computeTypeFromInputTypes(HInvokeDynamic instruction, |
| 221 HTypeMap types, | 221 HTypeMap types, |
| 222 Compiler compiler) { | 222 Compiler compiler) { |
| 223 HInstruction left = instruction.inputs[1]; | 223 HInstruction left = instruction.inputs[1]; |
| 224 HInstruction right = instruction.inputs[2]; | 224 HInstruction right = instruction.inputs[2]; |
| 225 if (left.isInteger(types) && right.isInteger(types)) return HType.INTEGER; | 225 if (left.isInteger(types) && right.isInteger(types)) return HType.INTEGER; |
| 226 if (left.isNumber(types)) { | 226 if (left.isNumber(types)) { |
| 227 if (left.isDouble(types) || right.isDouble(types)) return HType.DOUBLE; | 227 if (left.isDouble(types) || right.isDouble(types)) return HType.DOUBLE; |
| 228 return HType.NUMBER; | 228 return HType.NUMBER; |
| 229 } | 229 } |
| 230 return HType.UNKNOWN; | 230 return HType.UNKNOWN; |
| 231 } | 231 } |
| 232 | 232 |
| 233 HType computeDesiredTypeForInput(HInvokeDynamicMethod instruction, | 233 HType computeDesiredTypeForInput(HInvokeDynamic instruction, |
| 234 HInstruction input, | 234 HInstruction input, |
| 235 HTypeMap types, | 235 HTypeMap types, |
| 236 Compiler compiler) { | 236 Compiler compiler) { |
| 237 if (input == instruction.inputs[0]) return HType.UNKNOWN; | 237 if (input == instruction.inputs[0]) return HType.UNKNOWN; |
| 238 | 238 |
| 239 HType propagatedType = types[instruction]; | 239 HType propagatedType = types[instruction]; |
| 240 // If the desired output type should be an integer we want to get two | 240 // If the desired output type should be an integer we want to get two |
| 241 // integers as arguments. | 241 // integers as arguments. |
| 242 if (propagatedType.isInteger()) return HType.INTEGER; | 242 if (propagatedType.isInteger()) return HType.INTEGER; |
| 243 // If the outgoing type should be a number we can get that if both inputs | 243 // If the outgoing type should be a number we can get that if both inputs |
| 244 // are numbers. If we don't know the outgoing type we try to make it a | 244 // are numbers. If we don't know the outgoing type we try to make it a |
| 245 // number. | 245 // number. |
| 246 if (propagatedType.isUnknown() || propagatedType.isNumber()) { | 246 if (propagatedType.isUnknown() || propagatedType.isNumber()) { |
| 247 return HType.NUMBER; | 247 return HType.NUMBER; |
| 248 } | 248 } |
| 249 // Even if the desired outgoing type is not a number we still want the | 249 // Even if the desired outgoing type is not a number we still want the |
| 250 // second argument to be a number if the first one is a number. This will | 250 // second argument to be a number if the first one is a number. This will |
| 251 // not help for the outgoing type, but at least the binary arithmetic | 251 // not help for the outgoing type, but at least the binary arithmetic |
| 252 // operation will not have type problems. | 252 // operation will not have type problems. |
| 253 // TODO(floitsch): normally we shouldn't request a number, but simply | 253 // TODO(floitsch): normally we shouldn't request a number, but simply |
| 254 // throw an ArgumentError if it isn't. This would be similar | 254 // throw an ArgumentError if it isn't. This would be similar |
| 255 // to the array case. | 255 // to the array case. |
| 256 HInstruction left = instruction.inputs[1]; | 256 HInstruction left = instruction.inputs[1]; |
| 257 HInstruction right = instruction.inputs[2]; | 257 HInstruction right = instruction.inputs[2]; |
| 258 if (input == right && left.isNumber(types)) return HType.NUMBER; | 258 if (input == right && left.isNumber(types)) return HType.NUMBER; |
| 259 return HType.UNKNOWN; | 259 return HType.UNKNOWN; |
| 260 } | 260 } |
| 261 | 261 |
| 262 bool isBuiltin(HInvokeDynamicMethod instruction, HTypeMap types) { | 262 bool isBuiltin(HInvokeDynamic instruction, HTypeMap types) { |
| 263 return instruction.inputs[1].isNumber(types) | 263 return instruction.inputs[1].isNumber(types) |
| 264 && instruction.inputs[2].isNumber(types); | 264 && instruction.inputs[2].isNumber(types); |
| 265 } | 265 } |
| 266 | 266 |
| 267 HInstruction tryConvertToBuiltin(HInvokeDynamicMethod instruction, | 267 HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, |
| 268 HTypeMap types) { | 268 HTypeMap types) { |
| 269 if (isBuiltin(instruction, types)) { | 269 if (isBuiltin(instruction, types)) { |
| 270 HInstruction builtin = | 270 HInstruction builtin = |
| 271 newBuiltinVariant(instruction.inputs[1], instruction.inputs[2]); | 271 newBuiltinVariant(instruction.inputs[1], instruction.inputs[2]); |
| 272 if (builtin != null) return builtin; | 272 if (builtin != null) return builtin; |
| 273 // Even if there is no builtin equivalent instruction, we know | 273 // Even if there is no builtin equivalent instruction, we know |
| 274 // the instruction does not have any side effect, and that it | 274 // the instruction does not have any side effect, and that it |
| 275 // can be GVN'ed. | 275 // can be GVN'ed. |
| 276 instruction.clearAllSideEffects(); | 276 instruction.clearAllSideEffects(); |
| 277 instruction.clearAllDependencies(); | 277 instruction.clearAllDependencies(); |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 372 | 372 |
| 373 HInstruction newBuiltinVariant(HInstruction left, HInstruction right) { | 373 HInstruction newBuiltinVariant(HInstruction left, HInstruction right) { |
| 374 // Truncating divide does not have a JS equivalent. | 374 // Truncating divide does not have a JS equivalent. |
| 375 return null; | 375 return null; |
| 376 } | 376 } |
| 377 } | 377 } |
| 378 | 378 |
| 379 abstract class BinaryBitOpSpecializer extends BinaryArithmeticSpecializer { | 379 abstract class BinaryBitOpSpecializer extends BinaryArithmeticSpecializer { |
| 380 const BinaryBitOpSpecializer(); | 380 const BinaryBitOpSpecializer(); |
| 381 | 381 |
| 382 HType computeTypeFromInputTypes(HInvokeDynamicMethod instruction, | 382 HType computeTypeFromInputTypes(HInvokeDynamic instruction, |
| 383 HTypeMap types, | 383 HTypeMap types, |
| 384 Compiler compiler) { | 384 Compiler compiler) { |
| 385 // All bitwise operations on primitive types either produce an | 385 // All bitwise operations on primitive types either produce an |
| 386 // integer or throw an error. | 386 // integer or throw an error. |
| 387 HInstruction left = instruction.inputs[1]; | 387 HInstruction left = instruction.inputs[1]; |
| 388 if (left.isPrimitive(types)) return HType.INTEGER; | 388 if (left.isPrimitive(types)) return HType.INTEGER; |
| 389 return HType.UNKNOWN; | 389 return HType.UNKNOWN; |
| 390 } | 390 } |
| 391 | 391 |
| 392 HType computeDesiredTypeForInput(HInvokeDynamicMethod instruction, | 392 HType computeDesiredTypeForInput(HInvokeDynamic instruction, |
| 393 HInstruction input, | 393 HInstruction input, |
| 394 HTypeMap types, | 394 HTypeMap types, |
| 395 Compiler compiler) { | 395 Compiler compiler) { |
| 396 if (input == instruction.inputs[0]) return HType.UNKNOWN; | 396 if (input == instruction.inputs[0]) return HType.UNKNOWN; |
| 397 HType propagatedType = types[instruction]; | 397 HType propagatedType = types[instruction]; |
| 398 // If the outgoing type should be a number we can get that only if both | 398 // If the outgoing type should be a number we can get that only if both |
| 399 // inputs are integers. If we don't know the outgoing type we try to make | 399 // inputs are integers. If we don't know the outgoing type we try to make |
| 400 // it an integer. | 400 // it an integer. |
| 401 if (propagatedType.isUnknown() || propagatedType.isNumber()) { | 401 if (propagatedType.isUnknown() || propagatedType.isNumber()) { |
| 402 return HType.INTEGER; | 402 return HType.INTEGER; |
| 403 } | 403 } |
| 404 return HType.UNKNOWN; | 404 return HType.UNKNOWN; |
| 405 } | 405 } |
| 406 } | 406 } |
| 407 | 407 |
| 408 class ShiftLeftSpecializer extends BinaryBitOpSpecializer { | 408 class ShiftLeftSpecializer extends BinaryBitOpSpecializer { |
| 409 const ShiftLeftSpecializer(); | 409 const ShiftLeftSpecializer(); |
| 410 | 410 |
| 411 BinaryOperation operation(ConstantSystem constantSystem) { | 411 BinaryOperation operation(ConstantSystem constantSystem) { |
| 412 return constantSystem.shiftLeft; | 412 return constantSystem.shiftLeft; |
| 413 } | 413 } |
| 414 | 414 |
| 415 HInstruction tryConvertToBuiltin(HInvokeDynamicMethod instruction, | 415 HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, |
| 416 HTypeMap types) { | 416 HTypeMap types) { |
| 417 HInstruction left = instruction.inputs[1]; | 417 HInstruction left = instruction.inputs[1]; |
| 418 HInstruction right = instruction.inputs[2]; | 418 HInstruction right = instruction.inputs[2]; |
| 419 if (!left.isNumber(types) || !right.isConstantInteger()) return null; | 419 if (!left.isNumber(types) || !right.isConstantInteger()) return null; |
| 420 HConstant rightConstant = right; | 420 HConstant rightConstant = right; |
| 421 IntConstant intConstant = rightConstant.constant; | 421 IntConstant intConstant = rightConstant.constant; |
| 422 int count = intConstant.value; | 422 int count = intConstant.value; |
| 423 if (count >= 0 && count <= 31) { | 423 if (count >= 0 && count <= 31) { |
| 424 return newBuiltinVariant(left, right); | 424 return newBuiltinVariant(left, right); |
| 425 } | 425 } |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 476 } | 476 } |
| 477 | 477 |
| 478 HInstruction newBuiltinVariant(HInstruction left, HInstruction right) { | 478 HInstruction newBuiltinVariant(HInstruction left, HInstruction right) { |
| 479 return new HBitXor(left, right); | 479 return new HBitXor(left, right); |
| 480 } | 480 } |
| 481 } | 481 } |
| 482 | 482 |
| 483 abstract class RelationalSpecializer extends InvokeDynamicSpecializer { | 483 abstract class RelationalSpecializer extends InvokeDynamicSpecializer { |
| 484 const RelationalSpecializer(); | 484 const RelationalSpecializer(); |
| 485 | 485 |
| 486 HType computeTypeFromInputTypes(HInvokeDynamicMethod instruction, | 486 HType computeTypeFromInputTypes(HInvokeDynamic instruction, |
| 487 HTypeMap types, | 487 HTypeMap types, |
| 488 Compiler compiler) { | 488 Compiler compiler) { |
| 489 if (types[instruction.inputs[1]].isPrimitiveOrNull()) return HType.BOOLEAN; | 489 if (types[instruction.inputs[1]].isPrimitiveOrNull()) return HType.BOOLEAN; |
| 490 return HType.UNKNOWN; | 490 return HType.UNKNOWN; |
| 491 } | 491 } |
| 492 | 492 |
| 493 HType computeDesiredTypeForInput(HInvokeDynamicMethod instruction, | 493 HType computeDesiredTypeForInput(HInvokeDynamic instruction, |
| 494 HInstruction input, | 494 HInstruction input, |
| 495 HTypeMap types, | 495 HTypeMap types, |
| 496 Compiler compiler) { | 496 Compiler compiler) { |
| 497 if (input == instruction.inputs[0]) return HType.UNKNOWN; | 497 if (input == instruction.inputs[0]) return HType.UNKNOWN; |
| 498 HType propagatedType = types[instruction]; | 498 HType propagatedType = types[instruction]; |
| 499 // For all relational operations except HIdentity, we expect to get numbers | 499 // For all relational operations except HIdentity, we expect to get numbers |
| 500 // only. With numbers the outgoing type is a boolean. If something else | 500 // only. With numbers the outgoing type is a boolean. If something else |
| 501 // is desired, then numbers are incorrect, though. | 501 // is desired, then numbers are incorrect, though. |
| 502 if (propagatedType.isUnknown() || propagatedType.isBoolean()) { | 502 if (propagatedType.isUnknown() || propagatedType.isBoolean()) { |
| 503 HInstruction left = instruction.inputs[1]; | 503 HInstruction left = instruction.inputs[1]; |
| 504 if (left.isTypeUnknown(types) || left.isNumber(types)) { | 504 if (left.isTypeUnknown(types) || left.isNumber(types)) { |
| 505 return HType.NUMBER; | 505 return HType.NUMBER; |
| 506 } | 506 } |
| 507 } | 507 } |
| 508 return HType.UNKNOWN; | 508 return HType.UNKNOWN; |
| 509 } | 509 } |
| 510 | 510 |
| 511 HInstruction tryConvertToBuiltin(HInvokeDynamicMethod instruction, | 511 HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, |
| 512 HTypeMap types) { | 512 HTypeMap types) { |
| 513 HInstruction left = instruction.inputs[1]; | 513 HInstruction left = instruction.inputs[1]; |
| 514 HInstruction right = instruction.inputs[2]; | 514 HInstruction right = instruction.inputs[2]; |
| 515 if (left.isNumber(types) && right.isNumber(types)) { | 515 if (left.isNumber(types) && right.isNumber(types)) { |
| 516 return newBuiltinVariant(left, right); | 516 return newBuiltinVariant(left, right); |
| 517 } | 517 } |
| 518 return null; | 518 return null; |
| 519 } | 519 } |
| 520 | 520 |
| 521 HInstruction newBuiltinVariant(HInstruction left, HInstruction right); | 521 HInstruction newBuiltinVariant(HInstruction left, HInstruction right); |
| 522 } | 522 } |
| 523 | 523 |
| 524 class EqualsSpecializer extends RelationalSpecializer { | 524 class EqualsSpecializer extends RelationalSpecializer { |
| 525 const EqualsSpecializer(); | 525 const EqualsSpecializer(); |
| 526 | 526 |
| 527 HType computeDesiredTypeForInput(HInvokeDynamicMethod instruction, | 527 HType computeDesiredTypeForInput(HInvokeDynamic instruction, |
| 528 HInstruction input, | 528 HInstruction input, |
| 529 HTypeMap types, | 529 HTypeMap types, |
| 530 Compiler compiler) { | 530 Compiler compiler) { |
| 531 HInstruction left = instruction.inputs[1]; | 531 HInstruction left = instruction.inputs[1]; |
| 532 HInstruction right = instruction.inputs[2]; | 532 HInstruction right = instruction.inputs[2]; |
| 533 HType propagatedType = types[instruction]; | 533 HType propagatedType = types[instruction]; |
| 534 if (input == left && types[right].isUseful()) { | 534 if (input == left && types[right].isUseful()) { |
| 535 // All our useful types have 'identical' semantics. But we don't want to | 535 // All our useful types have 'identical' semantics. But we don't want to |
| 536 // speculatively test for all possible types. Therefore we try to match | 536 // speculatively test for all possible types. Therefore we try to match |
| 537 // the two types. That is, if we see x == 3, then we speculatively test | 537 // the two types. That is, if we see x == 3, then we speculatively test |
| 538 // if x is a number and bailout if it isn't. | 538 // if x is a number and bailout if it isn't. |
| 539 // If right is a number we don't need more than a number (no need to match | 539 // If right is a number we don't need more than a number (no need to match |
| 540 // the exact type of right). | 540 // the exact type of right). |
| 541 if (right.isNumber(types)) return HType.NUMBER; | 541 if (right.isNumber(types)) return HType.NUMBER; |
| 542 return types[right]; | 542 return types[right]; |
| 543 } | 543 } |
| 544 // String equality testing is much more common than array equality testing. | 544 // String equality testing is much more common than array equality testing. |
| 545 if (input == left && left.isIndexablePrimitive(types)) { | 545 if (input == left && left.isIndexablePrimitive(types)) { |
| 546 return HType.READABLE_ARRAY; | 546 return HType.READABLE_ARRAY; |
| 547 } | 547 } |
| 548 // String equality testing is much more common than array equality testing. | 548 // String equality testing is much more common than array equality testing. |
| 549 if (input == right && right.isIndexablePrimitive(types)) { | 549 if (input == right && right.isIndexablePrimitive(types)) { |
| 550 return HType.STRING; | 550 return HType.STRING; |
| 551 } | 551 } |
| 552 return HType.UNKNOWN; | 552 return HType.UNKNOWN; |
| 553 } | 553 } |
| 554 | 554 |
| 555 HInstruction tryConvertToBuiltin(HInvokeDynamicMethod instruction, | 555 HInstruction tryConvertToBuiltin(HInvokeDynamic instruction, |
| 556 HTypeMap types) { | 556 HTypeMap types) { |
| 557 HInstruction left = instruction.inputs[1]; | 557 HInstruction left = instruction.inputs[1]; |
| 558 HInstruction right = instruction.inputs[2]; | 558 HInstruction right = instruction.inputs[2]; |
| 559 if (types[left].isPrimitiveOrNull() || right.isConstantNull()) { | 559 if (types[left].isPrimitiveOrNull() || right.isConstantNull()) { |
| 560 return newBuiltinVariant(left, right); | 560 return newBuiltinVariant(left, right); |
| 561 } | 561 } |
| 562 return null; | 562 return null; |
| 563 } | 563 } |
| 564 | 564 |
| 565 BinaryOperation operation(ConstantSystem constantSystem) { | 565 BinaryOperation operation(ConstantSystem constantSystem) { |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 611 const LessEqualSpecializer(); | 611 const LessEqualSpecializer(); |
| 612 | 612 |
| 613 BinaryOperation operation(ConstantSystem constantSystem) { | 613 BinaryOperation operation(ConstantSystem constantSystem) { |
| 614 return constantSystem.lessEqual; | 614 return constantSystem.lessEqual; |
| 615 } | 615 } |
| 616 | 616 |
| 617 HInstruction newBuiltinVariant(HInstruction left, HInstruction right) { | 617 HInstruction newBuiltinVariant(HInstruction left, HInstruction right) { |
| 618 return new HLessEqual(left, right); | 618 return new HLessEqual(left, right); |
| 619 } | 619 } |
| 620 } | 620 } |
| OLD | NEW |