InByIdStatus.cpp
1 /* 2 * Copyright (C) 2018 Yusuke Suzuki <utatane.tea@gmail.com>. 3 * Copyright (C) 2018 Apple Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include "config.h" 28 #include "InByIdStatus.h" 29 30 #include "CodeBlock.h" 31 #include "ComplexGetStatus.h" 32 #include "ICStatusUtils.h" 33 #include "PolymorphicAccess.h" 34 #include "StructureStubInfo.h" 35 #include <wtf/ListDump.h> 36 37 #ifdef DARLING 38 #include "JSCellInlines.h" 39 #endif 40 41 namespace JSC { 42 43 bool InByIdStatus::appendVariant(const InByIdVariant& variant) 44 { 45 return appendICStatusVariant(m_variants, variant); 46 } 47 48 #if ENABLE(JIT) 49 InByIdStatus InByIdStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, BytecodeIndex bytecodeIndex, UniquedStringImpl* uid, ExitFlag didExit) 50 { 51 ConcurrentJSLocker locker(profiledBlock->m_lock); 52 53 InByIdStatus result; 54 55 #if ENABLE(DFG_JIT) 56 result = computeForStubInfoWithoutExitSiteFeedback(locker, profiledBlock->vm(), map.get(CodeOrigin(bytecodeIndex)).stubInfo, uid); 57 58 if (!result.takesSlowPath() && didExit) 59 return InByIdStatus(TakesSlowPath); 60 #else 61 UNUSED_PARAM(map); 62 UNUSED_PARAM(bytecodeIndex); 63 UNUSED_PARAM(uid); 64 UNUSED_PARAM(didExit); 65 #endif 66 67 return result; 68 } 69 70 InByIdStatus InByIdStatus::computeFor(CodeBlock* profiledBlock, ICStatusMap& map, BytecodeIndex bytecodeIndex, UniquedStringImpl* uid) 71 { 72 return computeFor(profiledBlock, map, bytecodeIndex, uid, hasBadCacheExitSite(profiledBlock, bytecodeIndex)); 73 } 74 75 InByIdStatus InByIdStatus::computeFor( 76 CodeBlock* profiledBlock, ICStatusMap& baselineMap, 77 ICStatusContextStack& contextStack, CodeOrigin codeOrigin, UniquedStringImpl* uid) 78 { 79 BytecodeIndex bytecodeIndex = codeOrigin.bytecodeIndex(); 80 ExitFlag didExit = hasBadCacheExitSite(profiledBlock, bytecodeIndex); 81 82 for (ICStatusContext* context : contextStack) { 83 ICStatus status = context->get(codeOrigin); 84 85 auto bless = [&] (const InByIdStatus& result) -> InByIdStatus { 86 if (!context->isInlined(codeOrigin)) { 87 InByIdStatus baselineResult = computeFor( 88 profiledBlock, baselineMap, bytecodeIndex, uid, didExit); 89 baselineResult.merge(result); 90 return baselineResult; 91 } 92 if (didExit.isSet(ExitFromInlined)) 93 return InByIdStatus(TakesSlowPath); 94 return result; 95 }; 96 97 #if ENABLE(DFG_JIT) 98 if (status.stubInfo) { 99 InByIdStatus result; 100 { 101 ConcurrentJSLocker locker(context->optimizedCodeBlock->m_lock); 102 result = computeForStubInfoWithoutExitSiteFeedback(locker, profiledBlock->vm(), status.stubInfo, uid); 103 } 104 if (result.isSet()) 105 return bless(result); 106 } 107 #endif 108 109 if (status.inStatus) 110 return bless(*status.inStatus); 111 } 112 113 return computeFor(profiledBlock, baselineMap, bytecodeIndex, uid, didExit); 114 } 115 #endif // ENABLE(JIT) 116 117 #if ENABLE(DFG_JIT) 118 InByIdStatus InByIdStatus::computeForStubInfo(const ConcurrentJSLocker& locker, CodeBlock* profiledBlock, StructureStubInfo* stubInfo, CodeOrigin codeOrigin, UniquedStringImpl* uid) 119 { 120 InByIdStatus result = InByIdStatus::computeForStubInfoWithoutExitSiteFeedback(locker, profiledBlock->vm(), stubInfo, uid); 121 122 if (!result.takesSlowPath() && hasBadCacheExitSite(profiledBlock, codeOrigin.bytecodeIndex())) 123 return InByIdStatus(TakesSlowPath); 124 return result; 125 } 126 127 InByIdStatus InByIdStatus::computeForStubInfoWithoutExitSiteFeedback(const ConcurrentJSLocker&, VM& vm, StructureStubInfo* stubInfo, UniquedStringImpl* uid) 128 { 129 StubInfoSummary summary = StructureStubInfo::summary(vm, stubInfo); 130 if (!isInlineable(summary)) 131 return InByIdStatus(summary); 132 133 // Finally figure out if we can derive an access strategy. 134 InByIdStatus result; 135 result.m_state = Simple; 136 switch (stubInfo->cacheType()) { 137 case CacheType::Unset: 138 return InByIdStatus(NoInformation); 139 140 case CacheType::InByIdSelf: { 141 Structure* structure = stubInfo->u.byIdSelf.baseObjectStructure.get(); 142 if (structure->takesSlowPathInDFGForImpureProperty()) 143 return InByIdStatus(TakesSlowPath); 144 unsigned attributes; 145 InByIdVariant variant; 146 variant.m_offset = structure->getConcurrently(uid, attributes); 147 if (!isValidOffset(variant.m_offset)) 148 return InByIdStatus(TakesSlowPath); 149 if (attributes & PropertyAttribute::CustomAccessorOrValue) 150 return InByIdStatus(TakesSlowPath); 151 152 variant.m_structureSet.add(structure); 153 bool didAppend = result.appendVariant(variant); 154 ASSERT_UNUSED(didAppend, didAppend); 155 return result; 156 } 157 158 case CacheType::Stub: { 159 PolymorphicAccess* list = stubInfo->u.stub; 160 for (unsigned listIndex = 0; listIndex < list->size(); ++listIndex) { 161 const AccessCase& access = list->at(listIndex); 162 if (access.viaProxy()) 163 return InByIdStatus(TakesSlowPath); 164 165 if (access.usesPolyProto()) 166 return InByIdStatus(TakesSlowPath); 167 168 Structure* structure = access.structure(); 169 if (!structure) { 170 // The null structure cases arise due to array.length. We have no way of creating a 171 // InByIdVariant for those, and we don't really have to since the DFG handles those 172 // cases in FixupPhase using value profiling. That's a bit awkward - we shouldn't 173 // have to use value profiling to discover something that the AccessCase could have 174 // told us. But, it works well enough. So, our only concern here is to not 175 // crash on null structure. 176 return InByIdStatus(TakesSlowPath); 177 } 178 179 ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor(structure, access.conditionSet(), uid); 180 switch (complexGetStatus.kind()) { 181 case ComplexGetStatus::ShouldSkip: 182 continue; 183 184 case ComplexGetStatus::TakesSlowPath: 185 return InByIdStatus(TakesSlowPath); 186 187 case ComplexGetStatus::Inlineable: { 188 switch (access.type()) { 189 case AccessCase::InHit: 190 case AccessCase::InMiss: 191 break; 192 default: 193 return InByIdStatus(TakesSlowPath); 194 } 195 196 InByIdVariant variant( 197 StructureSet(structure), complexGetStatus.offset(), 198 complexGetStatus.conditionSet()); 199 200 if (!result.appendVariant(variant)) 201 return InByIdStatus(TakesSlowPath); 202 break; 203 } 204 } 205 } 206 207 return result; 208 } 209 210 default: 211 return InByIdStatus(TakesSlowPath); 212 } 213 214 RELEASE_ASSERT_NOT_REACHED(); 215 return InByIdStatus(); 216 } 217 #endif 218 219 void InByIdStatus::merge(const InByIdStatus& other) 220 { 221 if (other.m_state == NoInformation) 222 return; 223 224 switch (m_state) { 225 case NoInformation: 226 *this = other; 227 return; 228 229 case Simple: 230 if (other.m_state != Simple) { 231 *this = InByIdStatus(TakesSlowPath); 232 return; 233 } 234 for (const InByIdVariant& otherVariant : other.m_variants) { 235 if (!appendVariant(otherVariant)) { 236 *this = InByIdStatus(TakesSlowPath); 237 return; 238 } 239 } 240 return; 241 242 case TakesSlowPath: 243 return; 244 } 245 246 RELEASE_ASSERT_NOT_REACHED(); 247 } 248 249 void InByIdStatus::filter(const StructureSet& structureSet) 250 { 251 if (m_state != Simple) 252 return; 253 filterICStatusVariants(m_variants, structureSet); 254 if (m_variants.isEmpty()) 255 m_state = NoInformation; 256 } 257 258 void InByIdStatus::markIfCheap(SlotVisitor& visitor) 259 { 260 for (InByIdVariant& variant : m_variants) 261 variant.markIfCheap(visitor); 262 } 263 264 bool InByIdStatus::finalize(VM& vm) 265 { 266 for (InByIdVariant& variant : m_variants) { 267 if (!variant.finalize(vm)) 268 return false; 269 } 270 return true; 271 } 272 273 void InByIdStatus::dump(PrintStream& out) const 274 { 275 out.print("("); 276 switch (m_state) { 277 case NoInformation: 278 out.print("NoInformation"); 279 break; 280 case Simple: 281 out.print("Simple"); 282 break; 283 case TakesSlowPath: 284 out.print("TakesSlowPath"); 285 break; 286 } 287 out.print(", ", listDump(m_variants), ")"); 288 } 289 290 } // namespace JSC 291