DeleteByStatus.cpp
1 /* 2 * Copyright (C) 2020 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "DeleteByStatus.h" 28 29 #include "CacheableIdentifierInlines.h" 30 #include "CodeBlock.h" 31 #include "ICStatusUtils.h" 32 #include "PolymorphicAccess.h" 33 #include "StructureStubInfo.h" 34 #include <wtf/ListDump.h> 35 36 namespace JSC { 37 38 bool DeleteByStatus::appendVariant(const DeleteByIdVariant& variant) 39 { 40 return appendICStatusVariant(m_variants, variant); 41 } 42 43 DeleteByStatus DeleteByStatus::computeForBaseline(CodeBlock* baselineBlock, ICStatusMap& map, BytecodeIndex bytecodeIndex, ExitFlag didExit) 44 { 45 ConcurrentJSLocker locker(baselineBlock->m_lock); 46 47 DeleteByStatus result; 48 49 #if ENABLE(DFG_JIT) 50 result = computeForStubInfoWithoutExitSiteFeedback( 51 locker, baselineBlock, map.get(CodeOrigin(bytecodeIndex)).stubInfo); 52 53 if (didExit) 54 return result.slowVersion(); 55 #else 56 UNUSED_PARAM(map); 57 UNUSED_PARAM(didExit); 58 UNUSED_PARAM(bytecodeIndex); 59 #endif 60 61 return result; 62 } 63 64 #if ENABLE(JIT) 65 DeleteByStatus::DeleteByStatus(StubInfoSummary summary, StructureStubInfo& stubInfo) 66 { 67 switch (summary) { 68 case StubInfoSummary::NoInformation: 69 m_state = NoInformation; 70 return; 71 case StubInfoSummary::Simple: 72 case StubInfoSummary::MakesCalls: 73 case StubInfoSummary::TakesSlowPathAndMakesCalls: 74 RELEASE_ASSERT_NOT_REACHED(); 75 return; 76 case StubInfoSummary::TakesSlowPath: 77 m_state = stubInfo.tookSlowPath ? ObservedTakesSlowPath : LikelyTakesSlowPath; 78 return; 79 } 80 RELEASE_ASSERT_NOT_REACHED(); 81 } 82 83 DeleteByStatus DeleteByStatus::computeForStubInfoWithoutExitSiteFeedback( 84 const ConcurrentJSLocker&, CodeBlock* block, StructureStubInfo* stubInfo) 85 { 86 StubInfoSummary summary = StructureStubInfo::summary(block->vm(), stubInfo); 87 if (!isInlineable(summary)) 88 return DeleteByStatus(summary, *stubInfo); 89 90 DeleteByStatus result; 91 result.m_state = Simple; 92 switch (stubInfo->cacheType()) { 93 case CacheType::Unset: 94 return DeleteByStatus(NoInformation); 95 96 case CacheType::Stub: { 97 PolymorphicAccess* list = stubInfo->u.stub; 98 99 for (unsigned listIndex = 0; listIndex < list->size(); ++listIndex) { 100 const AccessCase& access = list->at(listIndex); 101 ASSERT(!access.viaProxy()); 102 103 Structure* structure = access.structure(); 104 ASSERT(structure); 105 106 switch (access.type()) { 107 case AccessCase::DeleteMiss: 108 case AccessCase::DeleteNonConfigurable: { 109 DeleteByIdVariant variant(access.identifier(), access.type() == AccessCase::DeleteMiss ? true : false, structure, nullptr, invalidOffset); 110 if (!result.appendVariant(variant)) 111 return DeleteByStatus(JSC::slowVersion(summary), *stubInfo); 112 break; 113 } 114 case AccessCase::Delete: { 115 PropertyOffset offset; 116 Structure* newStructure = Structure::removePropertyTransitionFromExistingStructureConcurrently(structure, access.identifier().uid(), offset); 117 if (!newStructure) 118 return DeleteByStatus(JSC::slowVersion(summary), *stubInfo); 119 ASSERT_UNUSED(offset, offset == access.offset()); 120 DeleteByIdVariant variant(access.identifier(), true, structure, newStructure, access.offset()); 121 122 if (!result.appendVariant(variant)) 123 return DeleteByStatus(JSC::slowVersion(summary), *stubInfo); 124 break; 125 } 126 default: 127 ASSERT_NOT_REACHED(); 128 return DeleteByStatus(JSC::slowVersion(summary), *stubInfo); 129 } 130 } 131 132 return result; 133 } 134 135 default: 136 return DeleteByStatus(JSC::slowVersion(summary), *stubInfo); 137 } 138 139 RELEASE_ASSERT_NOT_REACHED(); 140 return DeleteByStatus(); 141 } 142 143 DeleteByStatus DeleteByStatus::computeFor( 144 CodeBlock* baselineBlock, ICStatusMap& baselineMap, 145 ICStatusContextStack& contextStack, CodeOrigin codeOrigin) 146 { 147 BytecodeIndex bytecodeIndex = codeOrigin.bytecodeIndex(); 148 ExitFlag didExit = hasBadCacheExitSite(baselineBlock, bytecodeIndex); 149 150 for (ICStatusContext* context : contextStack) { 151 ICStatus status = context->get(codeOrigin); 152 153 auto bless = [&] (const DeleteByStatus& result) -> DeleteByStatus { 154 if (!context->isInlined(codeOrigin)) { 155 DeleteByStatus baselineResult = computeForBaseline( 156 baselineBlock, baselineMap, bytecodeIndex, didExit); 157 baselineResult.merge(result); 158 return baselineResult; 159 } 160 if (didExit.isSet(ExitFromInlined)) 161 return result.slowVersion(); 162 return result; 163 }; 164 165 if (status.stubInfo) { 166 DeleteByStatus result; 167 { 168 ConcurrentJSLocker locker(context->optimizedCodeBlock->m_lock); 169 result = computeForStubInfoWithoutExitSiteFeedback( 170 locker, context->optimizedCodeBlock, status.stubInfo); 171 } 172 if (result.isSet()) 173 return bless(result); 174 } 175 176 if (status.deleteStatus) 177 return bless(*status.deleteStatus); 178 } 179 180 return computeForBaseline(baselineBlock, baselineMap, bytecodeIndex, didExit); 181 } 182 183 #endif // ENABLE(JIT) 184 185 DeleteByStatus DeleteByStatus::slowVersion() const 186 { 187 if (observedSlowPath()) 188 return DeleteByStatus(ObservedTakesSlowPath); 189 return DeleteByStatus(LikelyTakesSlowPath); 190 } 191 192 void DeleteByStatus::merge(const DeleteByStatus& other) 193 { 194 if (other.m_state == NoInformation) 195 return; 196 197 auto mergeSlow = [&] () { 198 if (observedSlowPath() || other.observedSlowPath()) 199 *this = DeleteByStatus(ObservedTakesSlowPath); 200 else 201 *this = DeleteByStatus(LikelyTakesSlowPath); 202 }; 203 204 switch (m_state) { 205 case NoInformation: 206 *this = other; 207 return; 208 209 case Simple: 210 if (m_state != other.m_state) 211 return mergeSlow(); 212 213 for (auto& otherVariant : other.m_variants) { 214 if (!appendVariant(otherVariant)) 215 return mergeSlow(); 216 } 217 return; 218 219 case LikelyTakesSlowPath: 220 case ObservedTakesSlowPath: 221 return mergeSlow(); 222 } 223 224 RELEASE_ASSERT_NOT_REACHED(); 225 } 226 227 void DeleteByStatus::filter(const StructureSet& set) 228 { 229 if (m_state != Simple) 230 return; 231 m_variants.removeAllMatching( 232 [&] (auto& variant) -> bool { 233 return !set.contains(variant.oldStructure()); 234 }); 235 if (m_variants.isEmpty()) 236 m_state = NoInformation; 237 } 238 239 CacheableIdentifier DeleteByStatus::singleIdentifier() const 240 { 241 if (m_variants.isEmpty()) 242 return nullptr; 243 244 CacheableIdentifier result = m_variants.first().identifier(); 245 if (!result) 246 return nullptr; 247 for (size_t i = 1; i < m_variants.size(); ++i) { 248 CacheableIdentifier identifier = m_variants[i].identifier(); 249 if (!identifier) 250 return nullptr; 251 if (identifier != result) 252 return nullptr; 253 } 254 return result; 255 } 256 257 void DeleteByStatus::visitAggregate(SlotVisitor& visitor) 258 { 259 for (DeleteByIdVariant& variant : m_variants) 260 variant.visitAggregate(visitor); 261 } 262 263 void DeleteByStatus::markIfCheap(SlotVisitor& visitor) 264 { 265 for (DeleteByIdVariant& variant : m_variants) 266 variant.markIfCheap(visitor); 267 } 268 269 bool DeleteByStatus::finalize(VM& vm) 270 { 271 for (auto& variant : m_variants) { 272 if (!variant.finalize(vm)) 273 return false; 274 } 275 return true; 276 } 277 278 void DeleteByStatus::dump(PrintStream& out) const 279 { 280 out.print("("); 281 switch (m_state) { 282 case NoInformation: 283 out.print("NoInformation"); 284 break; 285 case Simple: 286 out.print("Simple"); 287 break; 288 case LikelyTakesSlowPath: 289 out.print("LikelyTakesSlowPath"); 290 break; 291 case ObservedTakesSlowPath: 292 out.print("ObservedTakesSlowPath"); 293 break; 294 } 295 out.print(", ", listDump(m_variants), ")"); 296 } 297 298 } // namespace JSC