InstructionReordering.cpp (8524B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "jit/InstructionReordering.h" 8 9 #include "jit/MIRGenerator.h" 10 #include "jit/MIRGraph.h" 11 12 using namespace js; 13 using namespace js::jit; 14 15 static void MoveBefore(MBasicBlock* block, MInstruction* at, 16 MInstruction* ins) { 17 if (at == ins) { 18 return; 19 } 20 21 // Update instruction numbers. 22 for (MInstructionIterator iter(block->begin(at)); *iter != ins; iter++) { 23 MOZ_ASSERT(iter->id() < ins->id()); 24 iter->setId(iter->id() + 1); 25 } 26 ins->setId(at->id() - 1); 27 block->moveBefore(at, ins); 28 } 29 30 static bool IsLastUse(MDefinition* ins, MDefinition* input, 31 MBasicBlock* loopHeader) { 32 // If we are in a loop, this cannot be the last use of any definitions from 33 // outside the loop, as those definitions can be used in future iterations. 34 if (loopHeader && input->block()->id() < loopHeader->id()) { 35 return false; 36 } 37 for (MUseDefIterator iter(input); iter; iter++) { 38 // Watch for uses defined in blocks which ReorderInstructions hasn't 39 // processed yet. These nodes have not had their ids set yet. 40 if (iter.def()->block()->id() > ins->block()->id()) { 41 return false; 42 } 43 if (iter.def()->id() > ins->id()) { 44 return false; 45 } 46 } 47 return true; 48 } 49 50 static void MoveConstantsToStart(MBasicBlock* block, 51 MInstruction* insertionPoint) { 52 // Move constants with a single use in the current block to the start of the 53 // block. Constants won't be reordered by ReorderInstructions, as they have no 54 // inputs. Moving them up as high as possible can allow their use to be moved 55 // up further, though, and has no cost if the constant is emitted at its use. 56 57 MInstructionIterator iter(block->begin(insertionPoint)); 58 while (iter != block->end()) { 59 MInstruction* ins = *iter; 60 iter++; 61 62 if (!ins->isConstant() || !ins->hasOneUse() || 63 ins->usesBegin()->consumer()->block() != block || 64 IsFloatingPointType(ins->type())) { 65 continue; 66 } 67 68 MOZ_ASSERT(ins->isMovable()); 69 MOZ_ASSERT(insertionPoint != ins); 70 71 // Note: we don't need to use MoveBefore here because MoveConstantsToStart 72 // is called right before we renumber all instructions in this block. 73 block->moveBefore(insertionPoint, ins); 74 } 75 } 76 77 bool jit::ReorderInstructions(MIRGenerator* mir, MIRGraph& graph) { 78 // Renumber all instructions in the graph as we go. 79 size_t nextId = 0; 80 81 // List of the headers of any loops we are in. 82 Vector<MBasicBlock*, 4, SystemAllocPolicy> loopHeaders; 83 84 for (ReversePostorderIterator block(graph.rpoBegin()); 85 block != graph.rpoEnd(); block++) { 86 if (mir->shouldCancel("ReorderInstructions (block loop)")) { 87 return false; 88 } 89 90 // Don't reorder instructions within entry blocks, which have special 91 // requirements. 92 bool isEntryBlock = 93 *block == graph.entryBlock() || *block == graph.osrBlock(); 94 95 MInstruction* insertionPoint = nullptr; 96 if (!isEntryBlock) { 97 // Move constants to the start of the block before renumbering all 98 // instructions. 99 insertionPoint = block->safeInsertTop(); 100 MoveConstantsToStart(*block, insertionPoint); 101 } 102 103 // Renumber all definitions inside the basic blocks. 104 for (MPhiIterator iter(block->phisBegin()); iter != block->phisEnd(); 105 iter++) { 106 iter->setId(nextId++); 107 } 108 109 for (MInstructionIterator iter(block->begin()); iter != block->end(); 110 iter++) { 111 iter->setId(nextId++); 112 } 113 114 if (isEntryBlock) { 115 continue; 116 } 117 118 if (block->isLoopHeader()) { 119 if (!loopHeaders.append(*block)) { 120 return false; 121 } 122 } 123 124 MBasicBlock* innerLoop = loopHeaders.empty() ? nullptr : loopHeaders.back(); 125 126 MInstructionReverseIterator rtop = ++block->rbegin(insertionPoint); 127 for (MInstructionIterator iter(block->begin(insertionPoint)); 128 iter != block->end();) { 129 if (mir->shouldCancel("ReorderInstructions (instruction loop)")) { 130 return false; 131 } 132 133 MInstruction* ins = *iter; 134 135 // Filter out some instructions which are never reordered. 136 if (ins->isEffectful() || !ins->isMovable() || ins->resumePoint() || 137 ins == block->lastIns()) { 138 iter++; 139 continue; 140 } 141 142 // Look for inputs where this instruction is the last use of that 143 // input. If we move this instruction up, the input's lifetime will 144 // be shortened, modulo resume point uses (which don't need to be 145 // stored in a register, and can be handled by the register 146 // allocator by just spilling at some point with no reload). 147 Vector<MDefinition*, 4, SystemAllocPolicy> lastUsedInputs; 148 for (size_t i = 0; i < ins->numOperands(); i++) { 149 MDefinition* input = ins->getOperand(i); 150 if (!input->isConstant() && IsLastUse(ins, input, innerLoop)) { 151 if (!lastUsedInputs.append(input)) { 152 return false; 153 } 154 } 155 } 156 157 // Don't try to move instructions which aren't the last use of any 158 // of their inputs (we really ought to move these down instead). 159 if (lastUsedInputs.length() < 2) { 160 iter++; 161 continue; 162 } 163 164 MInstruction* target = ins; 165 MInstruction* postCallTarget = nullptr; 166 for (MInstructionReverseIterator riter = ++block->rbegin(ins); 167 riter != rtop; riter++) { 168 MInstruction* prev = *riter; 169 if (prev->isInterruptCheck()) { 170 break; 171 } 172 if (prev->isSetInitializedLength()) { 173 break; 174 } 175 176 // The instruction can't be moved before any of its uses. 177 bool isUse = false; 178 for (size_t i = 0; i < ins->numOperands(); i++) { 179 if (ins->getOperand(i) == prev) { 180 isUse = true; 181 break; 182 } 183 } 184 if (isUse) { 185 break; 186 } 187 188 // The instruction can't be moved before an instruction that 189 // stores to a location read by the instruction. 190 if (prev->isEffectful() && 191 (ins->getAliasSet().flags() & prev->getAliasSet().flags()) && 192 ins->mightAlias(prev) != MDefinition::AliasType::NoAlias) { 193 break; 194 } 195 196 // Make sure the instruction will still be the last use of one 197 // of its inputs when moved up this far. 198 for (size_t i = 0; i < lastUsedInputs.length();) { 199 bool found = false; 200 for (size_t j = 0; j < prev->numOperands(); j++) { 201 if (prev->getOperand(j) == lastUsedInputs[i]) { 202 found = true; 203 break; 204 } 205 } 206 if (found) { 207 lastUsedInputs[i] = lastUsedInputs.back(); 208 lastUsedInputs.popBack(); 209 } else { 210 i++; 211 } 212 } 213 if (lastUsedInputs.length() < 2) { 214 break; 215 } 216 217 // If we see a captured call result, either move the instruction before 218 // the corresponding call or don't move it at all. 219 if (prev->isCallResultCapture()) { 220 if (!postCallTarget) { 221 postCallTarget = target; 222 } 223 } else if (postCallTarget) { 224 MOZ_ASSERT(MWasmCallBase::IsWasmCall(prev) || 225 prev->isIonToWasmCall()); 226 postCallTarget = nullptr; 227 } 228 229 // We can move the instruction before this one. 230 target = prev; 231 } 232 233 if (postCallTarget) { 234 // We would have plonked this instruction between a call and its 235 // captured return value. Instead put it after the last corresponding 236 // return value. 237 target = postCallTarget; 238 } 239 240 iter++; 241 MoveBefore(*block, target, ins); 242 243 // Instruction reordering can move a bailing instruction up past a call 244 // that throws an exception, causing spurious bailouts. This should rarely 245 // be an issue in practice, so we only update the bailout kind if we don't 246 // have anything more specific. 247 if (ins->bailoutKind() == BailoutKind::TranspiledCacheIR) { 248 ins->setBailoutKind(BailoutKind::InstructionReordering); 249 } 250 } 251 252 if (block->isLoopBackedge()) { 253 loopHeaders.popBack(); 254 } 255 } 256 257 return true; 258 }