1/* 2 * Copyright (C) 2012 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 "PutByIdStatus.h" 28 29#include "CodeBlock.h" 30#include "LLIntData.h" 31#include "LowLevelInterpreter.h" 32#include "Operations.h" 33#include "Structure.h" 34#include "StructureChain.h" 35 36namespace JSC { 37 38PutByIdStatus PutByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned bytecodeIndex, Identifier& ident) 39{ 40 UNUSED_PARAM(profiledBlock); 41 UNUSED_PARAM(bytecodeIndex); 42 UNUSED_PARAM(ident); 43#if ENABLE(LLINT) 44 Instruction* instruction = profiledBlock->instructions().begin() + bytecodeIndex; 45 46 Structure* structure = instruction[4].u.structure.get(); 47 if (!structure) 48 return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset); 49 50 if (instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id) 51 || instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_out_of_line)) { 52 PropertyOffset offset = structure->get(*profiledBlock->vm(), ident); 53 if (!isValidOffset(offset)) 54 return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset); 55 56 return PutByIdStatus(SimpleReplace, structure, 0, 0, offset); 57 } 58 59 ASSERT(structure->transitionWatchpointSetHasBeenInvalidated()); 60 61 ASSERT(instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_transition_direct) 62 || instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_transition_normal) 63 || instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_transition_direct_out_of_line) 64 || instruction[0].u.opcode == LLInt::getOpcode(llint_op_put_by_id_transition_normal_out_of_line)); 65 66 Structure* newStructure = instruction[6].u.structure.get(); 67 StructureChain* chain = instruction[7].u.structureChain.get(); 68 ASSERT(newStructure); 69 ASSERT(chain); 70 71 PropertyOffset offset = newStructure->get(*profiledBlock->vm(), ident); 72 if (!isValidOffset(offset)) 73 return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset); 74 75 return PutByIdStatus(SimpleTransition, structure, newStructure, chain, offset); 76#else 77 return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset); 78#endif 79} 80 81PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, unsigned bytecodeIndex, Identifier& ident) 82{ 83 UNUSED_PARAM(profiledBlock); 84 UNUSED_PARAM(bytecodeIndex); 85 UNUSED_PARAM(ident); 86#if ENABLE(JIT) && ENABLE(VALUE_PROFILER) 87 if (!profiledBlock->numberOfStructureStubInfos()) 88 return computeFromLLInt(profiledBlock, bytecodeIndex, ident); 89 90 if (profiledBlock->likelyToTakeSlowCase(bytecodeIndex)) 91 return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); 92 93 StructureStubInfo& stubInfo = profiledBlock->getStubInfo(bytecodeIndex); 94 if (!stubInfo.seen) 95 return computeFromLLInt(profiledBlock, bytecodeIndex, ident); 96 97 if (stubInfo.resetByGC) 98 return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); 99 100 switch (stubInfo.accessType) { 101 case access_unset: 102 // If the JIT saw it but didn't optimize it, then assume that this takes slow path. 103 return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); 104 105 case access_put_by_id_replace: { 106 PropertyOffset offset = stubInfo.u.putByIdReplace.baseObjectStructure->get( 107 *profiledBlock->vm(), ident); 108 if (isValidOffset(offset)) { 109 return PutByIdStatus( 110 SimpleReplace, 111 stubInfo.u.putByIdReplace.baseObjectStructure.get(), 112 0, 0, 113 offset); 114 } 115 return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); 116 } 117 118 case access_put_by_id_transition_normal: 119 case access_put_by_id_transition_direct: { 120 ASSERT(stubInfo.u.putByIdTransition.previousStructure->transitionWatchpointSetHasBeenInvalidated()); 121 PropertyOffset offset = stubInfo.u.putByIdTransition.structure->get( 122 *profiledBlock->vm(), ident); 123 if (isValidOffset(offset)) { 124 return PutByIdStatus( 125 SimpleTransition, 126 stubInfo.u.putByIdTransition.previousStructure.get(), 127 stubInfo.u.putByIdTransition.structure.get(), 128 stubInfo.u.putByIdTransition.chain.get(), 129 offset); 130 } 131 return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); 132 } 133 134 default: 135 return PutByIdStatus(TakesSlowPath, 0, 0, 0, invalidOffset); 136 } 137#else // ENABLE(JIT) 138 return PutByIdStatus(NoInformation, 0, 0, 0, invalidOffset); 139#endif // ENABLE(JIT) 140} 141 142PutByIdStatus PutByIdStatus::computeFor(VM& vm, JSGlobalObject* globalObject, Structure* structure, Identifier& ident, bool isDirect) 143{ 144 if (PropertyName(ident).asIndex() != PropertyName::NotAnIndex) 145 return PutByIdStatus(TakesSlowPath); 146 147 if (structure->typeInfo().overridesGetOwnPropertySlot()) 148 return PutByIdStatus(TakesSlowPath); 149 150 if (!structure->propertyAccessesAreCacheable()) 151 return PutByIdStatus(TakesSlowPath); 152 153 unsigned attributes; 154 JSCell* specificValue; 155 PropertyOffset offset = structure->get(vm, ident, attributes, specificValue); 156 if (isValidOffset(offset)) { 157 if (attributes & (Accessor | ReadOnly)) 158 return PutByIdStatus(TakesSlowPath); 159 if (specificValue) { 160 // We need the PutById slow path to verify that we're storing the right value into 161 // the specialized slot. 162 return PutByIdStatus(TakesSlowPath); 163 } 164 return PutByIdStatus(SimpleReplace, structure, 0, 0, offset); 165 } 166 167 // Our hypothesis is that we're doing a transition. Before we prove that this is really 168 // true, we want to do some sanity checks. 169 170 // Don't cache put transitions on dictionaries. 171 if (structure->isDictionary()) 172 return PutByIdStatus(TakesSlowPath); 173 174 // If the structure corresponds to something that isn't an object, then give up, since 175 // we don't want to be adding properties to strings. 176 if (structure->typeInfo().type() == StringType) 177 return PutByIdStatus(TakesSlowPath); 178 179 if (!isDirect) { 180 // If the prototype chain has setters or read-only properties, then give up. 181 if (structure->prototypeChainMayInterceptStoreTo(vm, ident)) 182 return PutByIdStatus(TakesSlowPath); 183 184 // If the prototype chain hasn't been normalized (i.e. there are proxies or dictionaries) 185 // then give up. The dictionary case would only happen if this structure has not been 186 // used in an optimized put_by_id transition. And really the only reason why we would 187 // bail here is that I don't really feel like having the optimizing JIT go and flatten 188 // dictionaries if we have evidence to suggest that those objects were never used as 189 // prototypes in a cacheable prototype access - i.e. there's a good chance that some of 190 // the other checks below will fail. 191 if (!isPrototypeChainNormalized(globalObject, structure)) 192 return PutByIdStatus(TakesSlowPath); 193 } 194 195 // We only optimize if there is already a structure that the transition is cached to. 196 // Among other things, this allows us to guard against a transition with a specific 197 // value. 198 // 199 // - If we're storing a value that could be specific: this would only be a problem if 200 // the existing transition did have a specific value already, since if it didn't, 201 // then we would behave "as if" we were not storing a specific value. If it did 202 // have a specific value, then we'll know - the fact that we pass 0 for 203 // specificValue will tell us. 204 // 205 // - If we're not storing a value that could be specific: again, this would only be a 206 // problem if the existing transition did have a specific value, which we check for 207 // by passing 0 for the specificValue. 208 Structure* transition = Structure::addPropertyTransitionToExistingStructure(structure, ident, 0, 0, offset); 209 if (!transition) 210 return PutByIdStatus(TakesSlowPath); // This occurs in bizarre cases only. See above. 211 ASSERT(!transition->transitionDidInvolveSpecificValue()); 212 ASSERT(isValidOffset(offset)); 213 214 return PutByIdStatus( 215 SimpleTransition, structure, transition, 216 structure->prototypeChain(vm, globalObject), offset); 217} 218 219} // namespace JSC 220 221