WasmBCE.cpp (4869B)
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/WasmBCE.h" 8 9 #include "jit/JitSpewer.h" 10 #include "jit/MIRGenerator.h" 11 #include "jit/MIRGraph.h" 12 #include "wasm/WasmMetadata.h" 13 14 using mozilla::HashGeneric; 15 16 using namespace js; 17 using namespace js::jit; 18 19 struct LastCheckedKey { 20 // The type of collection being bounds checked (e.g. a memory or a table). 21 MWasmBoundsCheck::Target target; 22 23 // The index of the collection being bounds checked (e.g. memory 0). 24 uint32_t targetIndex; 25 26 // The MIR ID of the address being bounds checked. 27 uint32_t addressID; 28 29 using Lookup = LastCheckedKey; 30 static HashNumber hash(const Lookup& l) { 31 return HashGeneric(l.target, l.targetIndex, l.addressID); 32 } 33 static bool match(const LastCheckedKey& lhs, const Lookup& rhs) { 34 return lhs.target == rhs.target && lhs.targetIndex == rhs.targetIndex && 35 lhs.addressID == rhs.addressID; 36 } 37 }; 38 39 using LastCheckedMap = js::HashMap<LastCheckedKey, MWasmBoundsCheck*, 40 LastCheckedKey, SystemAllocPolicy>; 41 42 // The Wasm Bounds Check Elimination (BCE) pass looks for bounds checks 43 // on SSA values that have already been checked (in the same block or in a 44 // dominating block). These bounds checks are redundant and thus eliminated. 45 // 46 // Note: This is safe in the presence of dynamic memory sizes as long as they 47 // can ONLY GROW. If we allow SHRINKING the heap, this pass should be 48 // RECONSIDERED. 49 // 50 // TODO (dbounov): Are there a lot of cases where there is no single dominating 51 // check, but a set of checks that together dominate a redundant check? 52 // 53 // TODO (dbounov): Generalize to constant additions relative to one base 54 bool jit::EliminateBoundsChecks(const MIRGenerator* mir, MIRGraph& graph) { 55 JitSpew(JitSpew_WasmBCE, "Begin"); 56 // Map for dominating block where a given definition was checked 57 LastCheckedMap lastChecked; 58 59 for (ReversePostorderIterator bIter(graph.rpoBegin()); 60 bIter != graph.rpoEnd(); bIter++) { 61 MBasicBlock* block = *bIter; 62 for (MDefinitionIterator dIter(block); dIter;) { 63 MDefinition* def = *dIter++; 64 65 if (!def->isWasmBoundsCheck()) { 66 continue; 67 } 68 69 MWasmBoundsCheck* bc = def->toWasmBoundsCheck(); 70 MDefinition* addr = bc->index(); 71 72 if (bc->target() == MWasmBoundsCheck::Other) { 73 continue; 74 } 75 76 if (addr->isConstant()) { 77 // Eliminate constant-address bounds checks to addresses below 78 // the memory/table minimum length. 79 80 uint64_t addrConstantValue = UINT64_MAX; 81 switch (addr->type()) { 82 case MIRType::Int32: { 83 addrConstantValue = addr->toConstant()->toInt32(); 84 } break; 85 case MIRType::Int64: { 86 addrConstantValue = addr->toConstant()->toInt64(); 87 } break; 88 default: 89 break; 90 } 91 92 uint64_t initialLength = 0; 93 switch (bc->target()) { 94 case MWasmBoundsCheck::Memory: { 95 initialLength = mir->wasmCodeMeta() 96 ->memories[bc->targetIndex()] 97 .initialLength(); 98 } break; 99 case MWasmBoundsCheck::Table: { 100 initialLength = 101 mir->wasmCodeMeta()->tables[bc->targetIndex()].initialLength(); 102 } break; 103 default: 104 MOZ_CRASH(); 105 } 106 107 if (addrConstantValue < initialLength) { 108 bc->setRedundant(); 109 if (JitOptions.spectreIndexMasking) { 110 bc->replaceAllUsesWith(addr); 111 } else { 112 MOZ_ASSERT(!bc->hasUses()); 113 } 114 } 115 } else { 116 // Eliminate bounds checks that are dominated by another bounds check of 117 // the same value. 118 119 LastCheckedKey key = (LastCheckedKey){ 120 .target = bc->target(), 121 .targetIndex = bc->targetIndex(), 122 .addressID = addr->id(), 123 }; 124 LastCheckedMap::AddPtr ptr = lastChecked.lookupForAdd(key); 125 if (!ptr) { 126 // We have not yet seen a bounds check for this address; track it. 127 if (!lastChecked.add(ptr, key, bc)) { 128 return false; 129 } 130 } else { 131 MWasmBoundsCheck* prevCheckOfSameAddr = ptr->value(); 132 if (prevCheckOfSameAddr->block()->dominates(block)) { 133 bc->setRedundant(); 134 if (JitOptions.spectreIndexMasking) { 135 bc->replaceAllUsesWith(prevCheckOfSameAddr); 136 } else { 137 MOZ_ASSERT(!bc->hasUses()); 138 } 139 } 140 } 141 } 142 } 143 } 144 145 return true; 146 }