Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * vim: set ts=8 sts=4 et sw=4 tw=99: |
michael@0 | 3 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 6 | |
michael@0 | 7 | #include "jit/ValueNumbering.h" |
michael@0 | 8 | |
michael@0 | 9 | #include "jit/IonSpewer.h" |
michael@0 | 10 | #include "jit/MIRGenerator.h" |
michael@0 | 11 | #include "jit/MIRGraph.h" |
michael@0 | 12 | |
michael@0 | 13 | using namespace js; |
michael@0 | 14 | using namespace js::jit; |
michael@0 | 15 | |
michael@0 | 16 | ValueNumberer::ValueNumberer(MIRGenerator *mir, MIRGraph &graph, bool optimistic) |
michael@0 | 17 | : mir(mir), |
michael@0 | 18 | graph_(graph), |
michael@0 | 19 | values(graph.alloc()), |
michael@0 | 20 | pessimisticPass_(!optimistic), |
michael@0 | 21 | count_(0) |
michael@0 | 22 | { } |
michael@0 | 23 | |
michael@0 | 24 | TempAllocator & |
michael@0 | 25 | ValueNumberer::alloc() const |
michael@0 | 26 | { |
michael@0 | 27 | return graph_.alloc(); |
michael@0 | 28 | } |
michael@0 | 29 | |
michael@0 | 30 | uint32_t |
michael@0 | 31 | ValueNumberer::lookupValue(MDefinition *ins) |
michael@0 | 32 | { |
michael@0 | 33 | ValueMap::AddPtr p = values.lookupForAdd(ins); |
michael@0 | 34 | if (p) { |
michael@0 | 35 | // make sure this is in the correct group |
michael@0 | 36 | setClass(ins, p->key()); |
michael@0 | 37 | return p->value(); |
michael@0 | 38 | } |
michael@0 | 39 | |
michael@0 | 40 | if (!values.add(p, ins, ins->id())) |
michael@0 | 41 | return 0; |
michael@0 | 42 | breakClass(ins); |
michael@0 | 43 | |
michael@0 | 44 | return ins->id(); |
michael@0 | 45 | } |
michael@0 | 46 | |
michael@0 | 47 | MDefinition * |
michael@0 | 48 | ValueNumberer::simplify(MDefinition *def, bool useValueNumbers) |
michael@0 | 49 | { |
michael@0 | 50 | if (def->isEffectful()) |
michael@0 | 51 | return def; |
michael@0 | 52 | |
michael@0 | 53 | MDefinition *ins = def->foldsTo(alloc(), useValueNumbers); |
michael@0 | 54 | if (ins == def) |
michael@0 | 55 | return def; |
michael@0 | 56 | |
michael@0 | 57 | // Ensure this instruction has a value number. |
michael@0 | 58 | if (!ins->valueNumberData()) |
michael@0 | 59 | ins->setValueNumberData(new(alloc()) ValueNumberData); |
michael@0 | 60 | |
michael@0 | 61 | if (!ins->block()) { |
michael@0 | 62 | // In this case, we made a new def by constant folding, for |
michael@0 | 63 | // example, we replaced add(#3,#4) with a new const(#7) node. |
michael@0 | 64 | |
michael@0 | 65 | // We will only fold a phi into one of its operands. |
michael@0 | 66 | JS_ASSERT(!def->isPhi()); |
michael@0 | 67 | |
michael@0 | 68 | def->block()->insertAfter(def->toInstruction(), ins->toInstruction()); |
michael@0 | 69 | ins->setValueNumber(lookupValue(ins)); |
michael@0 | 70 | } |
michael@0 | 71 | |
michael@0 | 72 | JS_ASSERT(ins->id() != 0); |
michael@0 | 73 | |
michael@0 | 74 | def->replaceAllUsesWith(ins); |
michael@0 | 75 | |
michael@0 | 76 | IonSpew(IonSpew_GVN, "Folding %d to be %d", def->id(), ins->id()); |
michael@0 | 77 | return ins; |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | MControlInstruction * |
michael@0 | 81 | ValueNumberer::simplifyControlInstruction(MControlInstruction *def) |
michael@0 | 82 | { |
michael@0 | 83 | if (def->isEffectful()) |
michael@0 | 84 | return def; |
michael@0 | 85 | |
michael@0 | 86 | MDefinition *repl = def->foldsTo(alloc(), false); |
michael@0 | 87 | if (repl == def) |
michael@0 | 88 | return def; |
michael@0 | 89 | |
michael@0 | 90 | // Ensure this instruction has a value number. |
michael@0 | 91 | if (!repl->valueNumberData()) |
michael@0 | 92 | repl->setValueNumberData(new(alloc()) ValueNumberData); |
michael@0 | 93 | |
michael@0 | 94 | MBasicBlock *block = def->block(); |
michael@0 | 95 | |
michael@0 | 96 | // MControlInstructions should not have consumers. |
michael@0 | 97 | JS_ASSERT(repl->isControlInstruction()); |
michael@0 | 98 | JS_ASSERT(!def->hasUses()); |
michael@0 | 99 | |
michael@0 | 100 | if (def->isInWorklist()) |
michael@0 | 101 | repl->setInWorklist(); |
michael@0 | 102 | |
michael@0 | 103 | block->discardLastIns(); |
michael@0 | 104 | block->end((MControlInstruction *)repl); |
michael@0 | 105 | return (MControlInstruction *)repl; |
michael@0 | 106 | } |
michael@0 | 107 | |
michael@0 | 108 | void |
michael@0 | 109 | ValueNumberer::markDefinition(MDefinition *def) |
michael@0 | 110 | { |
michael@0 | 111 | if (isMarked(def)) |
michael@0 | 112 | return; |
michael@0 | 113 | |
michael@0 | 114 | IonSpew(IonSpew_GVN, "marked %d", def->id()); |
michael@0 | 115 | def->setInWorklist(); |
michael@0 | 116 | count_++; |
michael@0 | 117 | } |
michael@0 | 118 | |
michael@0 | 119 | void |
michael@0 | 120 | ValueNumberer::unmarkDefinition(MDefinition *def) |
michael@0 | 121 | { |
michael@0 | 122 | if (pessimisticPass_) |
michael@0 | 123 | return; |
michael@0 | 124 | |
michael@0 | 125 | JS_ASSERT(count_ > 0); |
michael@0 | 126 | IonSpew(IonSpew_GVN, "unmarked %d", def->id()); |
michael@0 | 127 | def->setNotInWorklist(); |
michael@0 | 128 | count_--; |
michael@0 | 129 | } |
michael@0 | 130 | |
michael@0 | 131 | void |
michael@0 | 132 | ValueNumberer::markBlock(MBasicBlock *block) |
michael@0 | 133 | { |
michael@0 | 134 | for (MDefinitionIterator iter(block); iter; iter++) |
michael@0 | 135 | markDefinition(*iter); |
michael@0 | 136 | markDefinition(block->lastIns()); |
michael@0 | 137 | } |
michael@0 | 138 | |
michael@0 | 139 | void |
michael@0 | 140 | ValueNumberer::markConsumers(MDefinition *def) |
michael@0 | 141 | { |
michael@0 | 142 | if (pessimisticPass_) |
michael@0 | 143 | return; |
michael@0 | 144 | |
michael@0 | 145 | JS_ASSERT(!def->isInWorklist()); |
michael@0 | 146 | JS_ASSERT(!def->isControlInstruction()); |
michael@0 | 147 | for (MUseDefIterator use(def); use; use++) |
michael@0 | 148 | markDefinition(use.def()); |
michael@0 | 149 | } |
michael@0 | 150 | |
michael@0 | 151 | bool |
michael@0 | 152 | ValueNumberer::computeValueNumbers() |
michael@0 | 153 | { |
michael@0 | 154 | // At the end of this function, we will have the value numbering stored in |
michael@0 | 155 | // each instruction. |
michael@0 | 156 | // |
michael@0 | 157 | // We also need an "optimistic" value number, for temporary use, which is |
michael@0 | 158 | // stored in a hashtable. |
michael@0 | 159 | // |
michael@0 | 160 | // For the instruction x := y op z, we map (op, VN[y], VN[z]) to a value |
michael@0 | 161 | // number, say v. If it is not in the map, we use the instruction id. |
michael@0 | 162 | // |
michael@0 | 163 | // If the instruction in question's value number is not already |
michael@0 | 164 | // v, we break the congruence and set it to v. We repeat until saturation. |
michael@0 | 165 | // This will take at worst O(d) time, where d is the loop connectedness |
michael@0 | 166 | // of the SSA def/use graph. |
michael@0 | 167 | // |
michael@0 | 168 | // The algorithm is the simple RPO-based algorithm from |
michael@0 | 169 | // "SCC-Based Value Numbering" by Cooper and Simpson. |
michael@0 | 170 | // |
michael@0 | 171 | // If we are performing a pessimistic pass, then we assume that every |
michael@0 | 172 | // definition is in its own congruence class, since we know nothing about |
michael@0 | 173 | // values that enter Phi nodes through back edges. We then make one pass |
michael@0 | 174 | // through the graph, ignoring back edges. This yields less congruences on |
michael@0 | 175 | // any graph with back-edges, but is much faster to perform. |
michael@0 | 176 | |
michael@0 | 177 | IonSpew(IonSpew_GVN, "Numbering instructions"); |
michael@0 | 178 | |
michael@0 | 179 | if (!values.init()) |
michael@0 | 180 | return false; |
michael@0 | 181 | // Stick a VN object onto every mdefinition |
michael@0 | 182 | for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) { |
michael@0 | 183 | if (mir->shouldCancel("Value Numbering (preparation loop")) |
michael@0 | 184 | return false; |
michael@0 | 185 | for (MDefinitionIterator iter(*block); iter; iter++) |
michael@0 | 186 | iter->setValueNumberData(new(alloc()) ValueNumberData); |
michael@0 | 187 | MControlInstruction *jump = block->lastIns(); |
michael@0 | 188 | jump->setValueNumberData(new(alloc()) ValueNumberData); |
michael@0 | 189 | } |
michael@0 | 190 | |
michael@0 | 191 | // Assign unique value numbers if pessimistic. |
michael@0 | 192 | // It might be productive to do this in the MDefinition constructor or |
michael@0 | 193 | // possibly in a previous pass, if it seems reasonable. |
michael@0 | 194 | if (pessimisticPass_) { |
michael@0 | 195 | for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) { |
michael@0 | 196 | for (MDefinitionIterator iter(*block); iter; iter++) |
michael@0 | 197 | iter->setValueNumber(iter->id()); |
michael@0 | 198 | } |
michael@0 | 199 | } else { |
michael@0 | 200 | // For each root block, add all of its instructions to the worklist. |
michael@0 | 201 | markBlock(*(graph_.begin())); |
michael@0 | 202 | if (graph_.osrBlock()) |
michael@0 | 203 | markBlock(graph_.osrBlock()); |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | while (count_ > 0) { |
michael@0 | 207 | #ifdef DEBUG |
michael@0 | 208 | if (!pessimisticPass_) { |
michael@0 | 209 | size_t debugCount = 0; |
michael@0 | 210 | IonSpew(IonSpew_GVN, "The following instructions require processing:"); |
michael@0 | 211 | for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) { |
michael@0 | 212 | for (MDefinitionIterator iter(*block); iter; iter++) { |
michael@0 | 213 | if (iter->isInWorklist()) { |
michael@0 | 214 | IonSpew(IonSpew_GVN, "\t%d", iter->id()); |
michael@0 | 215 | debugCount++; |
michael@0 | 216 | } |
michael@0 | 217 | } |
michael@0 | 218 | if (block->lastIns()->isInWorklist()) { |
michael@0 | 219 | IonSpew(IonSpew_GVN, "\t%d", block->lastIns()->id()); |
michael@0 | 220 | debugCount++; |
michael@0 | 221 | } |
michael@0 | 222 | } |
michael@0 | 223 | if (!debugCount) |
michael@0 | 224 | IonSpew(IonSpew_GVN, "\tNone"); |
michael@0 | 225 | JS_ASSERT(debugCount == count_); |
michael@0 | 226 | } |
michael@0 | 227 | #endif |
michael@0 | 228 | for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) { |
michael@0 | 229 | if (mir->shouldCancel("Value Numbering (main loop)")) |
michael@0 | 230 | return false; |
michael@0 | 231 | for (MDefinitionIterator iter(*block); iter; ) { |
michael@0 | 232 | |
michael@0 | 233 | if (!isMarked(*iter)) { |
michael@0 | 234 | iter++; |
michael@0 | 235 | continue; |
michael@0 | 236 | } |
michael@0 | 237 | |
michael@0 | 238 | JS_ASSERT_IF(!pessimisticPass_, count_ > 0); |
michael@0 | 239 | unmarkDefinition(*iter); |
michael@0 | 240 | |
michael@0 | 241 | MDefinition *ins = simplify(*iter, false); |
michael@0 | 242 | |
michael@0 | 243 | if (ins != *iter) { |
michael@0 | 244 | iter = block->discardDefAt(iter); |
michael@0 | 245 | continue; |
michael@0 | 246 | } |
michael@0 | 247 | |
michael@0 | 248 | // Don't bother storing this instruction in the HashMap if |
michael@0 | 249 | // (a) eliminateRedundancies will never eliminate it (because |
michael@0 | 250 | // it's non-movable or effectful) and (b) no other instruction's |
michael@0 | 251 | // value number depends on it. |
michael@0 | 252 | if (!ins->hasDefUses() && (!ins->isMovable() || ins->isEffectful())) { |
michael@0 | 253 | iter++; |
michael@0 | 254 | continue; |
michael@0 | 255 | } |
michael@0 | 256 | |
michael@0 | 257 | uint32_t value = lookupValue(ins); |
michael@0 | 258 | |
michael@0 | 259 | if (!value) |
michael@0 | 260 | return false; // Hashtable insertion failed |
michael@0 | 261 | |
michael@0 | 262 | if (ins->valueNumber() != value) { |
michael@0 | 263 | IonSpew(IonSpew_GVN, |
michael@0 | 264 | "Broke congruence for instruction %d (%p) with VN %d (now using %d)", |
michael@0 | 265 | ins->id(), (void *) ins, ins->valueNumber(), value); |
michael@0 | 266 | ins->setValueNumber(value); |
michael@0 | 267 | markConsumers(ins); |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | iter++; |
michael@0 | 271 | } |
michael@0 | 272 | // Process control flow instruction: |
michael@0 | 273 | MControlInstruction *jump = block->lastIns(); |
michael@0 | 274 | jump = simplifyControlInstruction(jump); |
michael@0 | 275 | |
michael@0 | 276 | // If we are pessimistic, then this will never get set. |
michael@0 | 277 | if (!jump->isInWorklist()) |
michael@0 | 278 | continue; |
michael@0 | 279 | unmarkDefinition(jump); |
michael@0 | 280 | if (jump->valueNumber() == 0) { |
michael@0 | 281 | jump->setValueNumber(jump->id()); |
michael@0 | 282 | for (size_t i = 0; i < jump->numSuccessors(); i++) |
michael@0 | 283 | markBlock(jump->getSuccessor(i)); |
michael@0 | 284 | } |
michael@0 | 285 | |
michael@0 | 286 | } |
michael@0 | 287 | |
michael@0 | 288 | // If we are doing a pessimistic pass, we only go once through the |
michael@0 | 289 | // instruction list. |
michael@0 | 290 | if (pessimisticPass_) |
michael@0 | 291 | break; |
michael@0 | 292 | } |
michael@0 | 293 | #ifdef DEBUG |
michael@0 | 294 | for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) { |
michael@0 | 295 | for (MDefinitionIterator iter(*block); iter; iter++) { |
michael@0 | 296 | JS_ASSERT(!iter->isInWorklist()); |
michael@0 | 297 | JS_ASSERT_IF(iter->valueNumber() == 0, |
michael@0 | 298 | !iter->hasDefUses() && (!iter->isMovable() || iter->isEffectful())); |
michael@0 | 299 | } |
michael@0 | 300 | } |
michael@0 | 301 | #endif |
michael@0 | 302 | return true; |
michael@0 | 303 | } |
michael@0 | 304 | |
michael@0 | 305 | MDefinition * |
michael@0 | 306 | ValueNumberer::findDominatingDef(InstructionMap &defs, MDefinition *ins, size_t index) |
michael@0 | 307 | { |
michael@0 | 308 | JS_ASSERT(ins->valueNumber() != 0); |
michael@0 | 309 | InstructionMap::Ptr p = defs.lookup(ins->valueNumber()); |
michael@0 | 310 | MDefinition *dom; |
michael@0 | 311 | if (!p || index > p->value().validUntil) { |
michael@0 | 312 | DominatingValue value; |
michael@0 | 313 | value.def = ins; |
michael@0 | 314 | // Since we are traversing the dominator tree in pre-order, when we |
michael@0 | 315 | // are looking at the |index|-th block, the next numDominated() blocks |
michael@0 | 316 | // we traverse are precisely the set of blocks that are dominated. |
michael@0 | 317 | // |
michael@0 | 318 | // So, this value is visible in all blocks if: |
michael@0 | 319 | // index <= index + ins->block->numDominated() |
michael@0 | 320 | // and becomes invalid after that. |
michael@0 | 321 | value.validUntil = index + ins->block()->numDominated(); |
michael@0 | 322 | |
michael@0 | 323 | if(!defs.put(ins->valueNumber(), value)) |
michael@0 | 324 | return nullptr; |
michael@0 | 325 | |
michael@0 | 326 | dom = ins; |
michael@0 | 327 | } else { |
michael@0 | 328 | dom = p->value().def; |
michael@0 | 329 | } |
michael@0 | 330 | |
michael@0 | 331 | return dom; |
michael@0 | 332 | } |
michael@0 | 333 | |
michael@0 | 334 | bool |
michael@0 | 335 | ValueNumberer::eliminateRedundancies() |
michael@0 | 336 | { |
michael@0 | 337 | // A definition is 'redundant' iff it is dominated by another definition |
michael@0 | 338 | // with the same value number. |
michael@0 | 339 | // |
michael@0 | 340 | // So, we traverse the dominator tree in pre-order, maintaining a hashmap |
michael@0 | 341 | // from value numbers to instructions. |
michael@0 | 342 | // |
michael@0 | 343 | // For each definition d with value number v, we look up v in the hashmap. |
michael@0 | 344 | // |
michael@0 | 345 | // If there is a definition d' in the hashmap, and the current traversal |
michael@0 | 346 | // index is within that instruction's dominated range, then we eliminate d, |
michael@0 | 347 | // replacing all uses of d with uses of d'. |
michael@0 | 348 | // |
michael@0 | 349 | // If there is no valid definition in the hashtable (the current definition |
michael@0 | 350 | // is not in dominated scope), then we insert the current instruction, |
michael@0 | 351 | // since it is the most dominant instruction with the given value number. |
michael@0 | 352 | |
michael@0 | 353 | InstructionMap defs(alloc()); |
michael@0 | 354 | |
michael@0 | 355 | if (!defs.init()) |
michael@0 | 356 | return false; |
michael@0 | 357 | |
michael@0 | 358 | IonSpew(IonSpew_GVN, "Eliminating redundant instructions"); |
michael@0 | 359 | |
michael@0 | 360 | // Stack for pre-order CFG traversal. |
michael@0 | 361 | Vector<MBasicBlock *, 1, IonAllocPolicy> worklist(alloc()); |
michael@0 | 362 | |
michael@0 | 363 | // The index of the current block in the CFG traversal. |
michael@0 | 364 | size_t index = 0; |
michael@0 | 365 | |
michael@0 | 366 | // Add all self-dominating blocks to the worklist. |
michael@0 | 367 | // This includes all roots. Order does not matter. |
michael@0 | 368 | for (MBasicBlockIterator i(graph_.begin()); i != graph_.end(); i++) { |
michael@0 | 369 | MBasicBlock *block = *i; |
michael@0 | 370 | if (block->immediateDominator() == block) { |
michael@0 | 371 | if (!worklist.append(block)) |
michael@0 | 372 | return false; |
michael@0 | 373 | } |
michael@0 | 374 | } |
michael@0 | 375 | |
michael@0 | 376 | // Starting from each self-dominating block, traverse the CFG in pre-order. |
michael@0 | 377 | while (!worklist.empty()) { |
michael@0 | 378 | if (mir->shouldCancel("Value Numbering (eliminate loop)")) |
michael@0 | 379 | return false; |
michael@0 | 380 | MBasicBlock *block = worklist.popCopy(); |
michael@0 | 381 | |
michael@0 | 382 | IonSpew(IonSpew_GVN, "Looking at block %d", block->id()); |
michael@0 | 383 | |
michael@0 | 384 | // Add all immediate dominators to the front of the worklist. |
michael@0 | 385 | if (!worklist.append(block->immediatelyDominatedBlocksBegin(), |
michael@0 | 386 | block->immediatelyDominatedBlocksEnd())) { |
michael@0 | 387 | return false; |
michael@0 | 388 | } |
michael@0 | 389 | |
michael@0 | 390 | // For each instruction, attempt to look up a dominating definition. |
michael@0 | 391 | for (MDefinitionIterator iter(block); iter; ) { |
michael@0 | 392 | MDefinition *ins = simplify(*iter, true); |
michael@0 | 393 | |
michael@0 | 394 | // Instruction was replaced, and all uses have already been fixed. |
michael@0 | 395 | if (ins != *iter) { |
michael@0 | 396 | iter = block->discardDefAt(iter); |
michael@0 | 397 | continue; |
michael@0 | 398 | } |
michael@0 | 399 | |
michael@0 | 400 | // Instruction has side-effects and cannot be folded. |
michael@0 | 401 | if (!ins->isMovable() || ins->isEffectful()) { |
michael@0 | 402 | iter++; |
michael@0 | 403 | continue; |
michael@0 | 404 | } |
michael@0 | 405 | |
michael@0 | 406 | MDefinition *dom = findDominatingDef(defs, ins, index); |
michael@0 | 407 | if (!dom) |
michael@0 | 408 | return false; // Insertion failed. |
michael@0 | 409 | |
michael@0 | 410 | if (dom == ins || !dom->updateForReplacement(ins)) { |
michael@0 | 411 | iter++; |
michael@0 | 412 | continue; |
michael@0 | 413 | } |
michael@0 | 414 | |
michael@0 | 415 | IonSpew(IonSpew_GVN, "instruction %d is dominated by instruction %d (from block %d)", |
michael@0 | 416 | ins->id(), dom->id(), dom->block()->id()); |
michael@0 | 417 | |
michael@0 | 418 | ins->replaceAllUsesWith(dom); |
michael@0 | 419 | |
michael@0 | 420 | JS_ASSERT(!ins->hasUses()); |
michael@0 | 421 | JS_ASSERT(ins->block() == block); |
michael@0 | 422 | JS_ASSERT(!ins->isEffectful()); |
michael@0 | 423 | JS_ASSERT(ins->isMovable()); |
michael@0 | 424 | |
michael@0 | 425 | iter = ins->block()->discardDefAt(iter); |
michael@0 | 426 | } |
michael@0 | 427 | index++; |
michael@0 | 428 | } |
michael@0 | 429 | |
michael@0 | 430 | JS_ASSERT(index == graph_.numBlocks()); |
michael@0 | 431 | return true; |
michael@0 | 432 | } |
michael@0 | 433 | |
michael@0 | 434 | // Exported method, called by the compiler. |
michael@0 | 435 | bool |
michael@0 | 436 | ValueNumberer::analyze() |
michael@0 | 437 | { |
michael@0 | 438 | return computeValueNumbers() && eliminateRedundancies(); |
michael@0 | 439 | } |
michael@0 | 440 | |
michael@0 | 441 | // Called by the compiler if we need to re-run GVN. |
michael@0 | 442 | bool |
michael@0 | 443 | ValueNumberer::clear() |
michael@0 | 444 | { |
michael@0 | 445 | IonSpew(IonSpew_GVN, "Clearing value numbers"); |
michael@0 | 446 | |
michael@0 | 447 | // Clear the VN of every MDefinition |
michael@0 | 448 | for (ReversePostorderIterator block(graph_.rpoBegin()); block != graph_.rpoEnd(); block++) { |
michael@0 | 449 | if (mir->shouldCancel("Value Numbering (clearing)")) |
michael@0 | 450 | return false; |
michael@0 | 451 | for (MDefinitionIterator iter(*block); iter; iter++) |
michael@0 | 452 | iter->clearValueNumberData(); |
michael@0 | 453 | block->lastIns()->clearValueNumberData(); |
michael@0 | 454 | } |
michael@0 | 455 | |
michael@0 | 456 | return true; |
michael@0 | 457 | } |
michael@0 | 458 | |
michael@0 | 459 | uint32_t |
michael@0 | 460 | MDefinition::valueNumber() const |
michael@0 | 461 | { |
michael@0 | 462 | JS_ASSERT(block_); |
michael@0 | 463 | if (valueNumber_ == nullptr) |
michael@0 | 464 | return 0; |
michael@0 | 465 | return valueNumber_->valueNumber(); |
michael@0 | 466 | } |
michael@0 | 467 | void |
michael@0 | 468 | MDefinition::setValueNumber(uint32_t vn) |
michael@0 | 469 | { |
michael@0 | 470 | valueNumber_->setValueNumber(vn); |
michael@0 | 471 | } |
michael@0 | 472 | // Set the class of this to the given representative value. |
michael@0 | 473 | void |
michael@0 | 474 | ValueNumberer::setClass(MDefinition *def, MDefinition *rep) |
michael@0 | 475 | { |
michael@0 | 476 | def->valueNumberData()->setClass(def, rep); |
michael@0 | 477 | } |
michael@0 | 478 | |
michael@0 | 479 | MDefinition * |
michael@0 | 480 | ValueNumberer::findSplit(MDefinition *def) |
michael@0 | 481 | { |
michael@0 | 482 | for (MDefinition *vncheck = def->valueNumberData()->classNext; |
michael@0 | 483 | vncheck != nullptr; |
michael@0 | 484 | vncheck = vncheck->valueNumberData()->classNext) { |
michael@0 | 485 | if (!def->congruentTo(vncheck)) { |
michael@0 | 486 | IonSpew(IonSpew_GVN, "Proceeding with split because %d is not congruent to %d", |
michael@0 | 487 | def->id(), vncheck->id()); |
michael@0 | 488 | return vncheck; |
michael@0 | 489 | } |
michael@0 | 490 | } |
michael@0 | 491 | return nullptr; |
michael@0 | 492 | } |
michael@0 | 493 | |
michael@0 | 494 | void |
michael@0 | 495 | ValueNumberer::breakClass(MDefinition *def) |
michael@0 | 496 | { |
michael@0 | 497 | if (def->valueNumber() == def->id()) { |
michael@0 | 498 | IonSpew(IonSpew_GVN, "Breaking congruence with itself: %d", def->id()); |
michael@0 | 499 | ValueNumberData *defdata = def->valueNumberData(); |
michael@0 | 500 | JS_ASSERT(defdata->classPrev == nullptr); |
michael@0 | 501 | // If the def was the only member of the class, then there is nothing to do. |
michael@0 | 502 | if (defdata->classNext == nullptr) |
michael@0 | 503 | return; |
michael@0 | 504 | // If upon closer inspection, we are still equivalent to this class |
michael@0 | 505 | // then there isn't anything for us to do. |
michael@0 | 506 | MDefinition *newRep = findSplit(def); |
michael@0 | 507 | if (!newRep) |
michael@0 | 508 | return; |
michael@0 | 509 | markConsumers(def); |
michael@0 | 510 | ValueNumberData *newdata = newRep->valueNumberData(); |
michael@0 | 511 | |
michael@0 | 512 | // Right now, |defdata| is at the front of the list, and |newdata| is |
michael@0 | 513 | // somewhere in the middle. |
michael@0 | 514 | // |
michael@0 | 515 | // We want to move |defdata| and everything up to but excluding |
michael@0 | 516 | // |newdata| to a new list, with |defdata| still as the canonical |
michael@0 | 517 | // element. |
michael@0 | 518 | // |
michael@0 | 519 | // We then want to take |newdata| and everything after, and |
michael@0 | 520 | // mark them for processing (since |newdata| is now a new canonical |
michael@0 | 521 | // element). |
michael@0 | 522 | // |
michael@0 | 523 | MDefinition *lastOld = newdata->classPrev; |
michael@0 | 524 | |
michael@0 | 525 | JS_ASSERT(lastOld); // newRep is NOT the first element of the list. |
michael@0 | 526 | JS_ASSERT(lastOld->valueNumberData()->classNext == newRep); |
michael@0 | 527 | |
michael@0 | 528 | //lastOld is now the last element of the old list (congruent to |
michael@0 | 529 | //|def|) |
michael@0 | 530 | lastOld->valueNumberData()->classNext = nullptr; |
michael@0 | 531 | |
michael@0 | 532 | #ifdef DEBUG |
michael@0 | 533 | for (MDefinition *tmp = def; tmp != nullptr; tmp = tmp->valueNumberData()->classNext) { |
michael@0 | 534 | JS_ASSERT(tmp->valueNumber() == def->valueNumber()); |
michael@0 | 535 | JS_ASSERT(tmp->congruentTo(def)); |
michael@0 | 536 | JS_ASSERT(tmp != newRep); |
michael@0 | 537 | } |
michael@0 | 538 | #endif |
michael@0 | 539 | //|newRep| is now the first element of a new list, therefore it is the |
michael@0 | 540 | //new canonical element. Mark the remaining elements in the list |
michael@0 | 541 | //(including |newRep|) |
michael@0 | 542 | newdata->classPrev = nullptr; |
michael@0 | 543 | IonSpew(IonSpew_GVN, "Choosing a new representative: %d", newRep->id()); |
michael@0 | 544 | |
michael@0 | 545 | // make the VN of every member in the class the VN of the new representative number. |
michael@0 | 546 | for (MDefinition *tmp = newRep; tmp != nullptr; tmp = tmp->valueNumberData()->classNext) { |
michael@0 | 547 | // if this instruction is already scheduled to be processed, don't do anything. |
michael@0 | 548 | if (tmp->isInWorklist()) { |
michael@0 | 549 | IonSpew(IonSpew_GVN, "Defer to a new congruence class: %d", tmp->id()); |
michael@0 | 550 | continue; |
michael@0 | 551 | } |
michael@0 | 552 | IonSpew(IonSpew_GVN, "Moving to a new congruence class: %d", tmp->id()); |
michael@0 | 553 | tmp->setValueNumber(newRep->id()); |
michael@0 | 554 | markConsumers(tmp); |
michael@0 | 555 | markDefinition(tmp); |
michael@0 | 556 | } |
michael@0 | 557 | |
michael@0 | 558 | // Insert the new representative => number mapping into the table |
michael@0 | 559 | // Logically, there should not be anything in the table currently, but |
michael@0 | 560 | // old values are never removed, so there's a good chance something will |
michael@0 | 561 | // already be there. |
michael@0 | 562 | values.put(newRep, newRep->id()); |
michael@0 | 563 | } else { |
michael@0 | 564 | // The element that is breaking from the list isn't the representative element |
michael@0 | 565 | // just strip it from the list |
michael@0 | 566 | ValueNumberData *defdata = def->valueNumberData(); |
michael@0 | 567 | if (defdata->classPrev) |
michael@0 | 568 | defdata->classPrev->valueNumberData()->classNext = defdata->classNext; |
michael@0 | 569 | if (defdata->classNext) |
michael@0 | 570 | defdata->classNext->valueNumberData()->classPrev = defdata->classPrev; |
michael@0 | 571 | |
michael@0 | 572 | // Make sure there is no nastinees accidentally linking elements into the old list later. |
michael@0 | 573 | defdata->classPrev = nullptr; |
michael@0 | 574 | defdata->classNext = nullptr; |
michael@0 | 575 | } |
michael@0 | 576 | } |