| 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 /** | 5 /** |
| 6 * Instead of emitting each SSA instruction with a temporary variable | 6 * Instead of emitting each SSA instruction with a temporary variable |
| 7 * mark instructions that can be emitted at their use-site. | 7 * mark instructions that can be emitted at their use-site. |
| 8 * For example, in: | 8 * For example, in: |
| 9 * t0 = 4; | 9 * t0 = 4; |
| 10 * t1 = 3; | 10 * t1 = 3; |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 171 | 171 |
| 172 /** | 172 /** |
| 173 * Returns true if the given instruction is an expression that uses up all | 173 * Returns true if the given instruction is an expression that uses up all |
| 174 * instructions up to the given [limit]. | 174 * instructions up to the given [limit]. |
| 175 * | 175 * |
| 176 * That is, all instructions starting after the [limit] block (at the branch | 176 * That is, all instructions starting after the [limit] block (at the branch |
| 177 * leading to the [instruction]) down to the given [instruction] can be | 177 * leading to the [instruction]) down to the given [instruction] can be |
| 178 * generated at use-site. | 178 * generated at use-site. |
| 179 */ | 179 */ |
| 180 bool isExpression(HInstruction instruction, HBasicBlock limit) { | 180 bool isExpression(HInstruction instruction, HBasicBlock limit) { |
| 181 if (instruction is HPhi && !logicalOperations.containsKey(instruction)) { | 181 HBasicBlock block = instruction.block; |
| 182 if (instruction is HPhi) { |
| 183 if (!logicalOperations.containsKey(instruction)) { |
| 184 return false; |
| 185 } |
| 186 } else { |
| 187 while (instruction.previous != null) { |
| 188 instruction = instruction.previous; |
| 189 if (!generateAtUseSite.contains(instruction)) { |
| 190 return false; |
| 191 } |
| 192 } |
| 193 // Now [instruction] is the first instruction of the block |
| 194 // (aka [block.first]). If there are also a phi, check the current |
| 195 // [instruction] normally and make [instruction] be the phi. |
| 196 if (!block.phis.isEmpty()) { |
| 197 if (!generateAtUseSite.contains(instruction)) { |
| 198 return false; |
| 199 } |
| 200 instruction = block.phis.last; |
| 201 if (block.phis.first !== instruction) { |
| 202 // If there is more than one phi, don't try to undestand it. |
| 203 return false; |
| 204 } |
| 205 if (!logicalOperations.containsKey(instruction)) { |
| 206 return false; |
| 207 } |
| 208 } |
| 209 } |
| 210 if (instruction is HPhi) { |
| 211 assert(logicalOperations.containsKey(instruction)); |
| 212 return isExpression(instruction.inputs[0], limit); |
| 213 } |
| 214 if (block.predecessors.length !== 1) { |
| 182 return false; | 215 return false; |
| 183 } | 216 } |
| 184 while (instruction.previous != null) { | 217 HBasicBlock previousBlock = block.predecessors[0]; |
| 185 instruction = instruction.previous; | 218 if (previousBlock === limit) return true; |
| 186 if (!generateAtUseSite.contains(instruction)) { | 219 if (previousBlock.successors.length !== 1 || |
| 187 return false; | 220 previousBlock.last is! HGoto) { |
| 188 } | 221 return false; |
| 189 } | 222 } |
| 190 HBasicBlock block = instruction.block; | 223 return isExpression(previousBlock.last, limit); |
| 191 if (!block.phis.isEmpty()) return false; | |
| 192 if (instruction is HPhi && logicalOperations.containsKey(instruction)) { | |
| 193 return isExpression(instruction.inputs[0], limit); | |
| 194 } | |
| 195 return block.predecessors.length == 1 && block.predecessors[0] == limit; | |
| 196 } | 224 } |
| 197 | 225 |
| 198 void replaceWithLogicalOperator(HPhi phi, String type) { | 226 void replaceWithLogicalOperator(HPhi phi, String type) { |
| 199 if (canGenerateAtUseSite(phi)) generateAtUseSite.add(phi); | 227 if (canGenerateAtUseSite(phi)) generateAtUseSite.add(phi); |
| 200 logicalOperations[phi] = type; | 228 logicalOperations[phi] = type; |
| 229 // If the phi corresponds to logical control flow, mark the |
| 230 // control-flow instructions as generate-at-use-site. |
| 231 generateAtUseSite.add(phi.block.predecessors[0].last); |
| 232 generateAtUseSite.add(phi.block.predecessors[1].last); |
| 233 // If the first input is only used as branch condition and result, it too |
| 234 // can be generate-at-use-site. |
| 235 if (phi.inputs[0].usedBy.length == 2) { |
| 236 generateAtUseSite.add(phi.inputs[0]); |
| 237 } |
| 238 if (phi.inputs[1].usedBy.length == 1) { |
| 239 generateAtUseSite.add(phi.inputs[1]); |
| 240 } |
| 201 } | 241 } |
| 202 | 242 |
| 203 bool canGenerateAtUseSite(HPhi phi) { | 243 bool canGenerateAtUseSite(HPhi phi) { |
| 204 if (phi.usedBy.length != 1) return false; | 244 if (phi.usedBy.length != 1) { |
| 245 return false; |
| 246 } |
| 205 assert(phi.next == null); | 247 assert(phi.next == null); |
| 206 HInstruction use = phi.usedBy[0]; | 248 HInstruction use = phi.usedBy[0]; |
| 207 | 249 |
| 208 HInstruction current = phi.block.first; | 250 HInstruction current = phi.block.first; |
| 209 while (current != use) { | 251 while (current != use) { |
| 210 if (!generateAtUseSite.contains(current)) return false; | 252 // Check that every instruction between the start of the block and the |
| 253 // use of the phi (i.e., every instruction between the phi and the use) |
| 254 // is itself generated at use site. That means that the phi can be |
| 255 // moved to its use site without crossing any other code, because those |
| 256 // instructions (if any) are moved too. |
| 257 if (current is! HControlFlow && !generateAtUseSite.contains(current)) { |
| 258 return false; |
| 259 } |
| 211 if (current.next != null) { | 260 if (current.next != null) { |
| 212 current = current.next; | 261 current = current.next; |
| 213 } else if (current is HPhi) { | 262 } else if (current is HPhi) { |
| 214 current = current.block.first; | 263 current = current.block.first; |
| 215 } else { | 264 } else { |
| 216 assert(current is HControlFlow); | 265 assert(current is HControlFlow); |
| 217 if (current is !HGoto) return false; | 266 if (current is !HGoto) { |
| 267 return false; |
| 268 } |
| 218 HBasicBlock nextBlock = current.block.successors[0]; | 269 HBasicBlock nextBlock = current.block.successors[0]; |
| 219 if (!nextBlock.phis.isEmpty()) { | 270 if (!nextBlock.phis.isEmpty()) { |
| 220 current = nextBlock.phis.first; | 271 current = nextBlock.phis.first; |
| 221 } else { | 272 } else { |
| 222 current = nextBlock.first; | 273 current = nextBlock.first; |
| 223 } | 274 } |
| 224 } | 275 } |
| 225 } | 276 } |
| 226 return true; | 277 return true; |
| 227 } | 278 } |
| 228 | 279 |
| 280 HInstruction previousInstruction(HInstruction instruction) { |
| 281 if (instruction.previous != null) return instruction.previous; |
| 282 HBasicBlock block = instruction.block; |
| 283 if (instruction is! HPhi) { |
| 284 if (block.phis.last != null) return block.phis.last; |
| 285 } |
| 286 if (block.predecessors.length == 1) { |
| 287 HBasicBlock previousBlock = block.predecessors[0]; |
| 288 if (previousBlock.last is HGoto) { |
| 289 assert(previousBlock.successors.length == 1); |
| 290 assert(previousBlock.successors[0] === block); |
| 291 return previousInstruction(previousBlock.last); |
| 292 } |
| 293 } |
| 294 return null; |
| 295 } |
| 296 |
| 229 void detectLogicControlFlow(HPhi phi) { | 297 void detectLogicControlFlow(HPhi phi) { |
| 230 // Check for the most common pattern for a short-circuit logic operation: | 298 // Check for the most common pattern for a short-circuit logic operation: |
| 231 // B0 b0 = ...; if (b0) goto B1 else B2 (or: if (!b0) goto B2 else B1) | 299 // B0 b0 = ...; if (b0) goto B1 else B2 (or: if (!b0) goto B2 else B1) |
| 232 // |\ | 300 // |\ |
| 233 // | B1 b1 = ...; goto B2 | 301 // | B1 b1 = ...; goto B2 |
| 234 // |/ | 302 // |/ |
| 235 // B2 b2 = phi(b0,b1); if(b2) ... | 303 // B2 b2 = phi(b0,b1); if(b2) ... |
| 236 // TODO(lrn): Also recognize ?:-flow? | 304 // TODO(lrn): Also recognize ?:-flow? |
| 237 | |
| 238 if (phi.inputs.length != 2) return; | 305 if (phi.inputs.length != 2) return; |
| 239 HInstruction first = phi.inputs[0]; | 306 HInstruction first = phi.inputs[0]; |
| 240 HBasicBlock firstBlock = first.block; | 307 HBasicBlock firstBlock = phi.block.predecessors[0]; |
| 241 HInstruction second = phi.inputs[1]; | 308 HInstruction second = phi.inputs[1]; |
| 242 HBasicBlock secondBlock = second.block; | 309 HBasicBlock secondBlock = phi.block.predecessors[1]; |
| 243 // Check second input of phi being an expression followed by a goto. | 310 // Check second input of phi being an expression followed by a goto. |
| 244 if (second.usedBy.length != 1) return; | 311 if (second.usedBy.length != 1) return; |
| 245 HInstruction secondNext = | 312 HInstruction secondNext = |
| 246 (second is HPhi) ? secondBlock.first : second.next; | 313 (second is HPhi) ? secondBlock.first : second.next; |
| 247 if (secondNext != secondBlock.last) return; | 314 if (secondNext != secondBlock.last) return; |
| 248 if (secondBlock.last is !HGoto) return; | 315 if (secondBlock.last is !HGoto) return; |
| 249 if (secondBlock.successors[0] != phi.block) return; | 316 if (secondBlock.successors[0] != phi.block) return; |
| 250 if (!isExpression(second, firstBlock)) return; | 317 if (!isExpression(second, firstBlock)) return; |
| 251 // Check first input of phi being followed by a (possibly negated) | 318 // Check first input of phi being followed by a (possibly negated) |
| 252 // conditional branch based on the same value. | 319 // conditional branch based on the same value. |
| 253 if (firstBlock != phi.block.dominator) return; | 320 if (firstBlock != phi.block.dominator) return; |
| 254 if (firstBlock.last is !HConditionalBranch) return; | 321 if (firstBlock.last is! HIf) return; |
| 255 HConditionalBranch firstBranch = firstBlock.last; | |
| 256 // Must be used both for value and for control to avoid the second branch. | |
| 257 if (first.usedBy.length != 2) return; | |
| 258 if (firstBlock.successors[1] != phi.block) return; | 322 if (firstBlock.successors[1] != phi.block) return; |
| 259 HInstruction firstNext = (first is HPhi) ? firstBlock.first : first.next; | 323 HIf firstBranch = firstBlock.last; |
| 260 if (firstNext == firstBranch && | 324 HInstruction condition = firstBranch.inputs[0]; |
| 261 firstBranch.condition == first) { | 325 if (condition === first) { |
| 262 replaceWithLogicalOperator(phi, "&&"); | 326 replaceWithLogicalOperator(phi, "&&"); |
| 263 } else if (firstNext is HNot && | 327 } else if (condition is HNot && |
| 264 firstNext.inputs[0] == first && | 328 condition.inputs[0] == first) { |
| 265 generateAtUseSite.contains(firstNext) && | |
| 266 firstNext.next == firstBlock.last && | |
| 267 firstBranch.condition == firstNext) { | |
| 268 replaceWithLogicalOperator(phi, "||"); | 329 replaceWithLogicalOperator(phi, "||"); |
| 269 } else { | 330 // If the negation is only used by this logical operation, or only by |
| 270 return; | 331 // logical operators in general, it won't need to be generated. |
| 332 if (!generateAtUseSite.contains(condition)) { |
| 333 for (HInstruction user in condition.usedBy) { |
| 334 if (user is! HIf || !generateAtUseSite.contains(user)) { |
| 335 return; |
| 336 } |
| 337 } |
| 338 generateAtUseSite.add(condition); |
| 339 } |
| 271 } | 340 } |
| 272 // Detected as logic control flow. Mark the corresponding | 341 return; |
| 273 // inputs as generated at use site. These will now be generated | |
| 274 // as part of an expression. | |
| 275 generateAtUseSite.add(first); | |
| 276 generateAtUseSite.add(firstBlock.last); | |
| 277 generateAtUseSite.add(second); | |
| 278 generateAtUseSite.add(secondBlock.last); | |
| 279 } | 342 } |
| 280 | 343 |
| 281 void visitBasicBlock(HBasicBlock block) { | 344 void visitBasicBlock(HBasicBlock block) { |
| 282 if (!block.phis.isEmpty() && | 345 if (!block.phis.isEmpty() && |
| 283 block.phis.first == block.phis.last) { | 346 block.phis.first === block.phis.last) { |
| 284 detectLogicControlFlow(block.phis.first); | 347 detectLogicControlFlow(block.phis.first); |
| 285 } | 348 } |
| 286 } | 349 } |
| 287 } | 350 } |
| 288 | 351 |
| 289 // Precedence information for JavaScript operators. | 352 // Precedence information for JavaScript operators. |
| 290 class JSPrecedence { | 353 class JSPrecedence { |
| 291 // Used as precedence for something that's not even an expression. | 354 // Used as precedence for something that's not even an expression. |
| 292 static final int STATEMENT_PRECEDENCE = 0; | 355 static final int STATEMENT_PRECEDENCE = 0; |
| 293 // Precedences of JS operators. | 356 // Precedences of JS operators. |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 425 // improving the performance of future lookups. | 488 // improving the performance of future lookups. |
| 426 T root = getRepresentative(parent); | 489 T root = getRepresentative(parent); |
| 427 if (root !== parent) representative[element] = root; | 490 if (root !== parent) representative[element] = root; |
| 428 return root; | 491 return root; |
| 429 } | 492 } |
| 430 | 493 |
| 431 bool areEquivalent(T a, T b) { | 494 bool areEquivalent(T a, T b) { |
| 432 return getRepresentative(a) === getRepresentative(b); | 495 return getRepresentative(a) === getRepresentative(b); |
| 433 } | 496 } |
| 434 } | 497 } |
| OLD | NEW |