| /* |
| * Copyright (C) 2015 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "AirSimplifyCFG.h" |
| |
| #if ENABLE(B3_JIT) |
| |
| #include "AirCode.h" |
| #include "AirPhaseScope.h" |
| |
| namespace JSC { namespace B3 { namespace Air { |
| |
| bool simplifyCFG(Code& code) |
| { |
| constexpr bool verbose = false; |
| |
| PhaseScope phaseScope(code, "simplifyCFG"); |
| |
| // We have three easy simplification rules: |
| // |
| // 1) If a successor is a block that just jumps to another block, then jump directly to |
| // that block. |
| // |
| // 2) If all successors are the same and the operation has no effects, then use a jump |
| // instead. |
| // |
| // 3) If you jump to a block that is not you and has one predecessor, then merge. |
| // |
| // Note that because of the first rule, this phase may introduce critical edges. That's fine. |
| // If you need broken critical edges, then you have to break them yourself. |
| |
| bool result = false; |
| for (;;) { |
| if (verbose) { |
| dataLog("Air before an iteration of simplifyCFG:\n"); |
| dataLog(code); |
| } |
| |
| bool changed = false; |
| for (BasicBlock* block : code) { |
| // We rely on predecessors being conservatively correct. Verify this here. |
| if (shouldValidateIRAtEachPhase()) { |
| for (BasicBlock* block : code) { |
| for (BasicBlock* successor : block->successorBlocks()) |
| RELEASE_ASSERT(successor->containsPredecessor(block)); |
| } |
| } |
| |
| // We don't care about blocks that don't have successors. |
| if (!block->numSuccessors()) |
| continue; |
| |
| // First check if any of the successors of this block can be forwarded over. |
| for (BasicBlock*& successor : block->successorBlocks()) { |
| if (successor != block |
| && successor->size() == 1 |
| && successor->last().kind.opcode == Jump) { |
| BasicBlock* newSuccessor = successor->successorBlock(0); |
| if (newSuccessor != successor) { |
| if (verbose) { |
| dataLog( |
| "Replacing ", pointerDump(block), "->", pointerDump(successor), |
| " with ", pointerDump(block), "->", pointerDump(newSuccessor), "\n"); |
| } |
| // Note that we do not do replacePredecessor() because the block we're |
| // skipping will still have newSuccessor as its successor. |
| newSuccessor->addPredecessor(block); |
| successor = newSuccessor; |
| changed = true; |
| } |
| } |
| } |
| |
| // Now check if the block's terminal can be replaced with a jump. The terminal must not |
| // have weird effects. |
| if (block->numSuccessors() > 1 |
| && !block->last().hasNonControlEffects()) { |
| // All of the successors must be the same. |
| bool allSame = true; |
| BasicBlock* firstSuccessor = block->successorBlock(0); |
| for (unsigned i = 1; i < block->numSuccessors(); ++i) { |
| if (block->successorBlock(i) != firstSuccessor) { |
| allSame = false; |
| break; |
| } |
| } |
| if (allSame) { |
| if (verbose) |
| dataLog("Changing ", pointerDump(block), "'s terminal to a Jump.\n"); |
| block->last() = Inst(Jump, block->last().origin); |
| block->successors().resize(1); |
| block->successors()[0].frequency() = FrequencyClass::Normal; |
| changed = true; |
| } |
| } |
| |
| // Finally handle jumps to a block with one predecessor. |
| if (block->numSuccessors() == 1 |
| && !block->last().hasNonControlEffects()) { |
| BasicBlock* successor = block->successorBlock(0); |
| if (successor != block && successor->numPredecessors() == 1) { |
| RELEASE_ASSERT(successor->predecessor(0) == block); |
| |
| // We can merge the two blocks because the predecessor only jumps to the successor |
| // and the successor is only reachable from the predecessor. |
| |
| // Remove the terminal. |
| Value* origin = block->insts().takeLast().origin; |
| |
| // Append the full contents of the successor to the predecessor. |
| block->insts().reserveCapacity(block->size() + successor->size()); |
| for (Inst& inst : *successor) |
| block->appendInst(WTFMove(inst)); |
| |
| // Make sure that our successors are the successor's successors. |
| block->successors() = WTFMove(successor->successors()); |
| |
| // Make sure that the successor has nothing left in it except an oops. |
| successor->resize(1); |
| successor->last() = Inst(Oops, origin); |
| successor->successors().clear(); |
| |
| // Ensure that the predecessors of block's new successors know what's up. |
| for (BasicBlock* newSuccessor : block->successorBlocks()) |
| newSuccessor->replacePredecessor(successor, block); |
| |
| if (verbose) |
| dataLog("Merged ", pointerDump(block), "->", pointerDump(successor), "\n"); |
| changed = true; |
| } |
| } |
| } |
| |
| if (!changed) |
| break; |
| result = true; |
| code.resetReachability(); |
| } |
| |
| return result; |
| } |
| |
| } } } // namespace JSC::B3::Air |
| |
| #endif // ENABLE(B3_JIT) |
| |
| |