1/* 2 * Copyright (C) 2008, 2009 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 28#if ENABLE(JIT) 29#include "JIT.h" 30 31#include "CodeBlock.h" 32#include "GCAwareJITStubRoutine.h" 33#include "GetterSetter.h" 34#include "Interpreter.h" 35#include "JITInlines.h" 36#include "JITStubCall.h" 37#include "JSArray.h" 38#include "JSFunction.h" 39#include "JSPropertyNameIterator.h" 40#include "JSVariableObject.h" 41#include "LinkBuffer.h" 42#include "RepatchBuffer.h" 43#include "ResultType.h" 44#include "SamplingTool.h" 45#include <wtf/StringPrintStream.h> 46 47#ifndef NDEBUG 48#include <stdio.h> 49#endif 50 51using namespace std; 52 53namespace JSC { 54#if USE(JSVALUE64) 55 56JIT::CodeRef JIT::stringGetByValStubGenerator(VM* vm) 57{ 58 JSInterfaceJIT jit; 59 JumpList failures; 60 failures.append(jit.branchPtr(NotEqual, Address(regT0, JSCell::structureOffset()), TrustedImmPtr(vm->stringStructure.get()))); 61 62 // Load string length to regT2, and start the process of loading the data pointer into regT0 63 jit.load32(Address(regT0, ThunkHelpers::jsStringLengthOffset()), regT2); 64 jit.loadPtr(Address(regT0, ThunkHelpers::jsStringValueOffset()), regT0); 65 failures.append(jit.branchTest32(Zero, regT0)); 66 67 // Do an unsigned compare to simultaneously filter negative indices as well as indices that are too large 68 failures.append(jit.branch32(AboveOrEqual, regT1, regT2)); 69 70 // Load the character 71 JumpList is16Bit; 72 JumpList cont8Bit; 73 // Load the string flags 74 jit.loadPtr(Address(regT0, StringImpl::flagsOffset()), regT2); 75 jit.loadPtr(Address(regT0, StringImpl::dataOffset()), regT0); 76 is16Bit.append(jit.branchTest32(Zero, regT2, TrustedImm32(StringImpl::flagIs8Bit()))); 77 jit.load8(BaseIndex(regT0, regT1, TimesOne, 0), regT0); 78 cont8Bit.append(jit.jump()); 79 is16Bit.link(&jit); 80 jit.load16(BaseIndex(regT0, regT1, TimesTwo, 0), regT0); 81 cont8Bit.link(&jit); 82 83 failures.append(jit.branch32(AboveOrEqual, regT0, TrustedImm32(0x100))); 84 jit.move(TrustedImmPtr(vm->smallStrings.singleCharacterStrings()), regT1); 85 jit.loadPtr(BaseIndex(regT1, regT0, ScalePtr, 0), regT0); 86 jit.ret(); 87 88 failures.link(&jit); 89 jit.move(TrustedImm32(0), regT0); 90 jit.ret(); 91 92 LinkBuffer patchBuffer(*vm, &jit, GLOBAL_THUNK_ID); 93 return FINALIZE_CODE(patchBuffer, ("String get_by_val stub")); 94} 95 96void JIT::emit_op_get_by_val(Instruction* currentInstruction) 97{ 98 unsigned dst = currentInstruction[1].u.operand; 99 unsigned base = currentInstruction[2].u.operand; 100 unsigned property = currentInstruction[3].u.operand; 101 ArrayProfile* profile = currentInstruction[4].u.arrayProfile; 102 103 emitGetVirtualRegisters(base, regT0, property, regT1); 104 emitJumpSlowCaseIfNotImmediateInteger(regT1); 105 106 // This is technically incorrect - we're zero-extending an int32. On the hot path this doesn't matter. 107 // We check the value as if it was a uint32 against the m_vectorLength - which will always fail if 108 // number was signed since m_vectorLength is always less than intmax (since the total allocation 109 // size is always less than 4Gb). As such zero extending wil have been correct (and extending the value 110 // to 64-bits is necessary since it's used in the address calculation. We zero extend rather than sign 111 // extending since it makes it easier to re-tag the value in the slow case. 112 zeroExtend32ToPtr(regT1, regT1); 113 114 emitJumpSlowCaseIfNotJSCell(regT0, base); 115 loadPtr(Address(regT0, JSCell::structureOffset()), regT2); 116 emitArrayProfilingSite(regT2, regT3, profile); 117 and32(TrustedImm32(IndexingShapeMask), regT2); 118 119 PatchableJump badType; 120 JumpList slowCases; 121 122 JITArrayMode mode = chooseArrayMode(profile); 123 switch (mode) { 124 case JITInt32: 125 slowCases = emitInt32GetByVal(currentInstruction, badType); 126 break; 127 case JITDouble: 128 slowCases = emitDoubleGetByVal(currentInstruction, badType); 129 break; 130 case JITContiguous: 131 slowCases = emitContiguousGetByVal(currentInstruction, badType); 132 break; 133 case JITArrayStorage: 134 slowCases = emitArrayStorageGetByVal(currentInstruction, badType); 135 break; 136 default: 137 CRASH(); 138 break; 139 } 140 141 addSlowCase(badType); 142 addSlowCase(slowCases); 143 144 Label done = label(); 145 146#if !ASSERT_DISABLED 147 Jump resultOK = branchTest64(NonZero, regT0); 148 breakpoint(); 149 resultOK.link(this); 150#endif 151 152 emitValueProfilingSite(); 153 emitPutVirtualRegister(dst); 154 155 m_byValCompilationInfo.append(ByValCompilationInfo(m_bytecodeOffset, badType, mode, done)); 156} 157 158JIT::JumpList JIT::emitDoubleGetByVal(Instruction*, PatchableJump& badType) 159{ 160 JumpList slowCases; 161 162 badType = patchableBranch32(NotEqual, regT2, TrustedImm32(DoubleShape)); 163 loadPtr(Address(regT0, JSObject::butterflyOffset()), regT2); 164 slowCases.append(branch32(AboveOrEqual, regT1, Address(regT2, Butterfly::offsetOfPublicLength()))); 165 loadDouble(BaseIndex(regT2, regT1, TimesEight), fpRegT0); 166 slowCases.append(branchDouble(DoubleNotEqualOrUnordered, fpRegT0, fpRegT0)); 167 moveDoubleTo64(fpRegT0, regT0); 168 sub64(tagTypeNumberRegister, regT0); 169 170 return slowCases; 171} 172 173JIT::JumpList JIT::emitContiguousGetByVal(Instruction*, PatchableJump& badType, IndexingType expectedShape) 174{ 175 JumpList slowCases; 176 177 badType = patchableBranch32(NotEqual, regT2, TrustedImm32(expectedShape)); 178 loadPtr(Address(regT0, JSObject::butterflyOffset()), regT2); 179 slowCases.append(branch32(AboveOrEqual, regT1, Address(regT2, Butterfly::offsetOfPublicLength()))); 180 load64(BaseIndex(regT2, regT1, TimesEight), regT0); 181 slowCases.append(branchTest64(Zero, regT0)); 182 183 return slowCases; 184} 185 186JIT::JumpList JIT::emitArrayStorageGetByVal(Instruction*, PatchableJump& badType) 187{ 188 JumpList slowCases; 189 190 add32(TrustedImm32(-ArrayStorageShape), regT2, regT3); 191 badType = patchableBranch32(Above, regT3, TrustedImm32(SlowPutArrayStorageShape - ArrayStorageShape)); 192 193 loadPtr(Address(regT0, JSObject::butterflyOffset()), regT2); 194 slowCases.append(branch32(AboveOrEqual, regT1, Address(regT2, ArrayStorage::vectorLengthOffset()))); 195 196 load64(BaseIndex(regT2, regT1, TimesEight, ArrayStorage::vectorOffset()), regT0); 197 slowCases.append(branchTest64(Zero, regT0)); 198 199 return slowCases; 200} 201 202void JIT::emitSlow_op_get_by_val(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) 203{ 204 unsigned dst = currentInstruction[1].u.operand; 205 unsigned base = currentInstruction[2].u.operand; 206 unsigned property = currentInstruction[3].u.operand; 207 ArrayProfile* profile = currentInstruction[4].u.arrayProfile; 208 209 linkSlowCase(iter); // property int32 check 210 linkSlowCaseIfNotJSCell(iter, base); // base cell check 211 Jump nonCell = jump(); 212 linkSlowCase(iter); // base array check 213 Jump notString = branchPtr(NotEqual, Address(regT0, JSCell::structureOffset()), TrustedImmPtr(m_vm->stringStructure.get())); 214 emitNakedCall(CodeLocationLabel(m_vm->getCTIStub(stringGetByValStubGenerator).code())); 215 Jump failed = branchTest64(Zero, regT0); 216 emitPutVirtualRegister(dst, regT0); 217 emitJumpSlowToHot(jump(), OPCODE_LENGTH(op_get_by_val)); 218 failed.link(this); 219 notString.link(this); 220 nonCell.link(this); 221 222 Jump skipProfiling = jump(); 223 224 linkSlowCase(iter); // vector length check 225 linkSlowCase(iter); // empty value 226 227 emitArrayProfileOutOfBoundsSpecialCase(profile); 228 229 skipProfiling.link(this); 230 231 Label slowPath = label(); 232 233 JITStubCall stubCall(this, cti_op_get_by_val); 234 stubCall.addArgument(base, regT2); 235 stubCall.addArgument(property, regT2); 236 Call call = stubCall.call(dst); 237 238 m_byValCompilationInfo[m_byValInstructionIndex].slowPathTarget = slowPath; 239 m_byValCompilationInfo[m_byValInstructionIndex].returnAddress = call; 240 m_byValInstructionIndex++; 241 242 emitValueProfilingSite(); 243} 244 245void JIT::compileGetDirectOffset(RegisterID base, RegisterID result, RegisterID offset, RegisterID scratch, FinalObjectMode finalObjectMode) 246{ 247 ASSERT(sizeof(JSValue) == 8); 248 249 if (finalObjectMode == MayBeFinal) { 250 Jump isInline = branch32(LessThan, offset, TrustedImm32(firstOutOfLineOffset)); 251 loadPtr(Address(base, JSObject::butterflyOffset()), scratch); 252 neg32(offset); 253 Jump done = jump(); 254 isInline.link(this); 255 addPtr(TrustedImm32(JSObject::offsetOfInlineStorage() - (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue)), base, scratch); 256 done.link(this); 257 } else { 258#if !ASSERT_DISABLED 259 Jump isOutOfLine = branch32(GreaterThanOrEqual, offset, TrustedImm32(firstOutOfLineOffset)); 260 breakpoint(); 261 isOutOfLine.link(this); 262#endif 263 loadPtr(Address(base, JSObject::butterflyOffset()), scratch); 264 neg32(offset); 265 } 266 signExtend32ToPtr(offset, offset); 267 load64(BaseIndex(scratch, offset, TimesEight, (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue)), result); 268} 269 270void JIT::emit_op_get_by_pname(Instruction* currentInstruction) 271{ 272 unsigned dst = currentInstruction[1].u.operand; 273 unsigned base = currentInstruction[2].u.operand; 274 unsigned property = currentInstruction[3].u.operand; 275 unsigned expected = currentInstruction[4].u.operand; 276 unsigned iter = currentInstruction[5].u.operand; 277 unsigned i = currentInstruction[6].u.operand; 278 279 emitGetVirtualRegister(property, regT0); 280 addSlowCase(branch64(NotEqual, regT0, addressFor(expected))); 281 emitGetVirtualRegisters(base, regT0, iter, regT1); 282 emitJumpSlowCaseIfNotJSCell(regT0, base); 283 284 // Test base's structure 285 loadPtr(Address(regT0, JSCell::structureOffset()), regT2); 286 addSlowCase(branchPtr(NotEqual, regT2, Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_cachedStructure)))); 287 load32(addressFor(i), regT3); 288 sub32(TrustedImm32(1), regT3); 289 addSlowCase(branch32(AboveOrEqual, regT3, Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_numCacheableSlots)))); 290 Jump inlineProperty = branch32(Below, regT3, Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_cachedStructureInlineCapacity))); 291 add32(TrustedImm32(firstOutOfLineOffset), regT3); 292 sub32(Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_cachedStructureInlineCapacity)), regT3); 293 inlineProperty.link(this); 294 compileGetDirectOffset(regT0, regT0, regT3, regT1); 295 296 emitPutVirtualRegister(dst, regT0); 297} 298 299void JIT::emitSlow_op_get_by_pname(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) 300{ 301 unsigned dst = currentInstruction[1].u.operand; 302 unsigned base = currentInstruction[2].u.operand; 303 unsigned property = currentInstruction[3].u.operand; 304 305 linkSlowCase(iter); 306 linkSlowCaseIfNotJSCell(iter, base); 307 linkSlowCase(iter); 308 linkSlowCase(iter); 309 310 JITStubCall stubCall(this, cti_op_get_by_val_generic); 311 stubCall.addArgument(base, regT2); 312 stubCall.addArgument(property, regT2); 313 stubCall.call(dst); 314} 315 316void JIT::emit_op_put_by_val(Instruction* currentInstruction) 317{ 318 unsigned base = currentInstruction[1].u.operand; 319 unsigned property = currentInstruction[2].u.operand; 320 ArrayProfile* profile = currentInstruction[4].u.arrayProfile; 321 322 emitGetVirtualRegisters(base, regT0, property, regT1); 323 emitJumpSlowCaseIfNotImmediateInteger(regT1); 324 // See comment in op_get_by_val. 325 zeroExtend32ToPtr(regT1, regT1); 326 emitJumpSlowCaseIfNotJSCell(regT0, base); 327 loadPtr(Address(regT0, JSCell::structureOffset()), regT2); 328 emitArrayProfilingSite(regT2, regT3, profile); 329 and32(TrustedImm32(IndexingShapeMask), regT2); 330 331 PatchableJump badType; 332 JumpList slowCases; 333 334 JITArrayMode mode = chooseArrayMode(profile); 335 switch (mode) { 336 case JITInt32: 337 slowCases = emitInt32PutByVal(currentInstruction, badType); 338 break; 339 case JITDouble: 340 slowCases = emitDoublePutByVal(currentInstruction, badType); 341 break; 342 case JITContiguous: 343 slowCases = emitContiguousPutByVal(currentInstruction, badType); 344 break; 345 case JITArrayStorage: 346 slowCases = emitArrayStoragePutByVal(currentInstruction, badType); 347 break; 348 default: 349 CRASH(); 350 break; 351 } 352 353 addSlowCase(badType); 354 addSlowCase(slowCases); 355 356 Label done = label(); 357 358 m_byValCompilationInfo.append(ByValCompilationInfo(m_bytecodeOffset, badType, mode, done)); 359 360 emitWriteBarrier(regT0, regT3, regT1, regT3, ShouldFilterImmediates, WriteBarrierForPropertyAccess); 361} 362 363JIT::JumpList JIT::emitGenericContiguousPutByVal(Instruction* currentInstruction, PatchableJump& badType, IndexingType indexingShape) 364{ 365 unsigned value = currentInstruction[3].u.operand; 366 ArrayProfile* profile = currentInstruction[4].u.arrayProfile; 367 368 JumpList slowCases; 369 370 badType = patchableBranch32(NotEqual, regT2, TrustedImm32(indexingShape)); 371 372 loadPtr(Address(regT0, JSObject::butterflyOffset()), regT2); 373 Jump outOfBounds = branch32(AboveOrEqual, regT1, Address(regT2, Butterfly::offsetOfPublicLength())); 374 375 Label storeResult = label(); 376 emitGetVirtualRegister(value, regT3); 377 switch (indexingShape) { 378 case Int32Shape: 379 slowCases.append(emitJumpIfNotImmediateInteger(regT3)); 380 store64(regT3, BaseIndex(regT2, regT1, TimesEight)); 381 break; 382 case DoubleShape: { 383 Jump notInt = emitJumpIfNotImmediateInteger(regT3); 384 convertInt32ToDouble(regT3, fpRegT0); 385 Jump ready = jump(); 386 notInt.link(this); 387 add64(tagTypeNumberRegister, regT3); 388 move64ToDouble(regT3, fpRegT0); 389 slowCases.append(branchDouble(DoubleNotEqualOrUnordered, fpRegT0, fpRegT0)); 390 ready.link(this); 391 storeDouble(fpRegT0, BaseIndex(regT2, regT1, TimesEight)); 392 break; 393 } 394 case ContiguousShape: 395 store64(regT3, BaseIndex(regT2, regT1, TimesEight)); 396 break; 397 default: 398 CRASH(); 399 break; 400 } 401 402 Jump done = jump(); 403 outOfBounds.link(this); 404 405 slowCases.append(branch32(AboveOrEqual, regT1, Address(regT2, Butterfly::offsetOfVectorLength()))); 406 407 emitArrayProfileStoreToHoleSpecialCase(profile); 408 409 add32(TrustedImm32(1), regT1, regT3); 410 store32(regT3, Address(regT2, Butterfly::offsetOfPublicLength())); 411 jump().linkTo(storeResult, this); 412 413 done.link(this); 414 415 return slowCases; 416} 417 418JIT::JumpList JIT::emitArrayStoragePutByVal(Instruction* currentInstruction, PatchableJump& badType) 419{ 420 unsigned value = currentInstruction[3].u.operand; 421 ArrayProfile* profile = currentInstruction[4].u.arrayProfile; 422 423 JumpList slowCases; 424 425 badType = patchableBranch32(NotEqual, regT2, TrustedImm32(ArrayStorageShape)); 426 loadPtr(Address(regT0, JSObject::butterflyOffset()), regT2); 427 slowCases.append(branch32(AboveOrEqual, regT1, Address(regT2, ArrayStorage::vectorLengthOffset()))); 428 429 Jump empty = branchTest64(Zero, BaseIndex(regT2, regT1, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]))); 430 431 Label storeResult(this); 432 emitGetVirtualRegister(value, regT3); 433 store64(regT3, BaseIndex(regT2, regT1, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]))); 434 Jump end = jump(); 435 436 empty.link(this); 437 emitArrayProfileStoreToHoleSpecialCase(profile); 438 add32(TrustedImm32(1), Address(regT2, ArrayStorage::numValuesInVectorOffset())); 439 branch32(Below, regT1, Address(regT2, ArrayStorage::lengthOffset())).linkTo(storeResult, this); 440 441 add32(TrustedImm32(1), regT1); 442 store32(regT1, Address(regT2, ArrayStorage::lengthOffset())); 443 sub32(TrustedImm32(1), regT1); 444 jump().linkTo(storeResult, this); 445 446 end.link(this); 447 448 return slowCases; 449} 450 451void JIT::emitSlow_op_put_by_val(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) 452{ 453 unsigned base = currentInstruction[1].u.operand; 454 unsigned property = currentInstruction[2].u.operand; 455 unsigned value = currentInstruction[3].u.operand; 456 ArrayProfile* profile = currentInstruction[4].u.arrayProfile; 457 458 linkSlowCase(iter); // property int32 check 459 linkSlowCaseIfNotJSCell(iter, base); // base cell check 460 linkSlowCase(iter); // base not array check 461 462 JITArrayMode mode = chooseArrayMode(profile); 463 switch (mode) { 464 case JITInt32: 465 case JITDouble: 466 linkSlowCase(iter); // value type check 467 break; 468 default: 469 break; 470 } 471 472 Jump skipProfiling = jump(); 473 linkSlowCase(iter); // out of bounds 474 emitArrayProfileOutOfBoundsSpecialCase(profile); 475 skipProfiling.link(this); 476 477 Label slowPath = label(); 478 479 JITStubCall stubPutByValCall(this, cti_op_put_by_val); 480 stubPutByValCall.addArgument(regT0); 481 stubPutByValCall.addArgument(property, regT2); 482 stubPutByValCall.addArgument(value, regT2); 483 Call call = stubPutByValCall.call(); 484 485 m_byValCompilationInfo[m_byValInstructionIndex].slowPathTarget = slowPath; 486 m_byValCompilationInfo[m_byValInstructionIndex].returnAddress = call; 487 m_byValInstructionIndex++; 488} 489 490void JIT::emit_op_put_by_index(Instruction* currentInstruction) 491{ 492 JITStubCall stubCall(this, cti_op_put_by_index); 493 stubCall.addArgument(currentInstruction[1].u.operand, regT2); 494 stubCall.addArgument(TrustedImm32(currentInstruction[2].u.operand)); 495 stubCall.addArgument(currentInstruction[3].u.operand, regT2); 496 stubCall.call(); 497} 498 499void JIT::emit_op_put_getter_setter(Instruction* currentInstruction) 500{ 501 JITStubCall stubCall(this, cti_op_put_getter_setter); 502 stubCall.addArgument(currentInstruction[1].u.operand, regT2); 503 stubCall.addArgument(TrustedImmPtr(&m_codeBlock->identifier(currentInstruction[2].u.operand))); 504 stubCall.addArgument(currentInstruction[3].u.operand, regT2); 505 stubCall.addArgument(currentInstruction[4].u.operand, regT2); 506 stubCall.call(); 507} 508 509void JIT::emit_op_del_by_id(Instruction* currentInstruction) 510{ 511 JITStubCall stubCall(this, cti_op_del_by_id); 512 stubCall.addArgument(currentInstruction[2].u.operand, regT2); 513 stubCall.addArgument(TrustedImmPtr(&m_codeBlock->identifier(currentInstruction[3].u.operand))); 514 stubCall.call(currentInstruction[1].u.operand); 515} 516 517void JIT::emit_op_get_by_id(Instruction* currentInstruction) 518{ 519 unsigned resultVReg = currentInstruction[1].u.operand; 520 unsigned baseVReg = currentInstruction[2].u.operand; 521 Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand)); 522 523 emitGetVirtualRegister(baseVReg, regT0); 524 compileGetByIdHotPath(baseVReg, ident); 525 emitValueProfilingSite(); 526 emitPutVirtualRegister(resultVReg); 527} 528 529void JIT::compileGetByIdHotPath(int baseVReg, Identifier* ident) 530{ 531 // As for put_by_id, get_by_id requires the offset of the Structure and the offset of the access to be patched. 532 // Additionally, for get_by_id we need patch the offset of the branch to the slow case (we patch this to jump 533 // to array-length / prototype access tranpolines, and finally we also the the property-map access offset as a label 534 // to jump back to if one of these trampolies finds a match. 535 536 emitJumpSlowCaseIfNotJSCell(regT0, baseVReg); 537 538 if (*ident == m_vm->propertyNames->length && shouldEmitProfiling()) { 539 loadPtr(Address(regT0, JSCell::structureOffset()), regT1); 540 emitArrayProfilingSiteForBytecodeIndex(regT1, regT2, m_bytecodeOffset); 541 } 542 543 BEGIN_UNINTERRUPTED_SEQUENCE(sequenceGetByIdHotPath); 544 545 Label hotPathBegin(this); 546 547 DataLabelPtr structureToCompare; 548 PatchableJump structureCheck = patchableBranchPtrWithPatch(NotEqual, Address(regT0, JSCell::structureOffset()), structureToCompare, TrustedImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure))); 549 addSlowCase(structureCheck); 550 551 ConvertibleLoadLabel propertyStorageLoad = convertibleLoadPtr(Address(regT0, JSObject::butterflyOffset()), regT0); 552 DataLabelCompact displacementLabel = load64WithCompactAddressOffsetPatch(Address(regT0, patchGetByIdDefaultOffset), regT0); 553 554 Label putResult(this); 555 556 END_UNINTERRUPTED_SEQUENCE(sequenceGetByIdHotPath); 557 558 m_propertyAccessCompilationInfo.append(PropertyStubCompilationInfo(PropertyStubGetById, m_bytecodeOffset, hotPathBegin, structureToCompare, structureCheck, propertyStorageLoad, displacementLabel, putResult)); 559} 560 561void JIT::emitSlow_op_get_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) 562{ 563 unsigned resultVReg = currentInstruction[1].u.operand; 564 unsigned baseVReg = currentInstruction[2].u.operand; 565 Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand)); 566 567 compileGetByIdSlowCase(resultVReg, baseVReg, ident, iter); 568 emitValueProfilingSite(); 569} 570 571void JIT::compileGetByIdSlowCase(int resultVReg, int baseVReg, Identifier* ident, Vector<SlowCaseEntry>::iterator& iter) 572{ 573 // As for the hot path of get_by_id, above, we ensure that we can use an architecture specific offset 574 // so that we only need track one pointer into the slow case code - we track a pointer to the location 575 // of the call (which we can use to look up the patch information), but should a array-length or 576 // prototype access trampoline fail we want to bail out back to here. To do so we can subtract back 577 // the distance from the call to the head of the slow case. 578 579 linkSlowCaseIfNotJSCell(iter, baseVReg); 580 linkSlowCase(iter); 581 582 BEGIN_UNINTERRUPTED_SEQUENCE(sequenceGetByIdSlowCase); 583 584 Label coldPathBegin(this); 585 JITStubCall stubCall(this, cti_op_get_by_id); 586 stubCall.addArgument(regT0); 587 stubCall.addArgument(TrustedImmPtr(ident)); 588 Call call = stubCall.call(resultVReg); 589 590 END_UNINTERRUPTED_SEQUENCE(sequenceGetByIdSlowCase); 591 592 // Track the location of the call; this will be used to recover patch information. 593 m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex++].slowCaseInfo(PropertyStubGetById, coldPathBegin, call); 594} 595 596void JIT::emit_op_put_by_id(Instruction* currentInstruction) 597{ 598 unsigned baseVReg = currentInstruction[1].u.operand; 599 unsigned valueVReg = currentInstruction[3].u.operand; 600 601 // In order to be able to patch both the Structure, and the object offset, we store one pointer, 602 // to just after the arguments have been loaded into registers 'hotPathBegin', and we generate code 603 // such that the Structure & offset are always at the same distance from this. 604 605 emitGetVirtualRegisters(baseVReg, regT0, valueVReg, regT1); 606 607 // Jump to a slow case if either the base object is an immediate, or if the Structure does not match. 608 emitJumpSlowCaseIfNotJSCell(regT0, baseVReg); 609 610 BEGIN_UNINTERRUPTED_SEQUENCE(sequencePutById); 611 612 Label hotPathBegin(this); 613 614 // It is important that the following instruction plants a 32bit immediate, in order that it can be patched over. 615 DataLabelPtr structureToCompare; 616 addSlowCase(branchPtrWithPatch(NotEqual, Address(regT0, JSCell::structureOffset()), structureToCompare, TrustedImmPtr(reinterpret_cast<void*>(patchGetByIdDefaultStructure)))); 617 618 ConvertibleLoadLabel propertyStorageLoad = convertibleLoadPtr(Address(regT0, JSObject::butterflyOffset()), regT2); 619 DataLabel32 displacementLabel = store64WithAddressOffsetPatch(regT1, Address(regT2, patchPutByIdDefaultOffset)); 620 621 END_UNINTERRUPTED_SEQUENCE(sequencePutById); 622 623 emitWriteBarrier(regT0, regT1, regT2, regT3, ShouldFilterImmediates, WriteBarrierForPropertyAccess); 624 625 m_propertyAccessCompilationInfo.append(PropertyStubCompilationInfo(PropertyStubPutById, m_bytecodeOffset, hotPathBegin, structureToCompare, propertyStorageLoad, displacementLabel)); 626} 627 628void JIT::emitSlow_op_put_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) 629{ 630 unsigned baseVReg = currentInstruction[1].u.operand; 631 Identifier* ident = &(m_codeBlock->identifier(currentInstruction[2].u.operand)); 632 unsigned direct = currentInstruction[8].u.operand; 633 634 linkSlowCaseIfNotJSCell(iter, baseVReg); 635 linkSlowCase(iter); 636 637 JITStubCall stubCall(this, direct ? cti_op_put_by_id_direct : cti_op_put_by_id); 638 stubCall.addArgument(regT0); 639 stubCall.addArgument(TrustedImmPtr(ident)); 640 stubCall.addArgument(regT1); 641 move(regT0, nonArgGPR1); 642 Call call = stubCall.call(); 643 644 // Track the location of the call; this will be used to recover patch information. 645 m_propertyAccessCompilationInfo[m_propertyAccessInstructionIndex++].slowCaseInfo(PropertyStubPutById, call); 646} 647 648// Compile a store into an object's property storage. May overwrite the 649// value in objectReg. 650void JIT::compilePutDirectOffset(RegisterID base, RegisterID value, PropertyOffset cachedOffset) 651{ 652 if (isInlineOffset(cachedOffset)) { 653 store64(value, Address(base, JSObject::offsetOfInlineStorage() + sizeof(JSValue) * offsetInInlineStorage(cachedOffset))); 654 return; 655 } 656 657 loadPtr(Address(base, JSObject::butterflyOffset()), base); 658 store64(value, Address(base, sizeof(JSValue) * offsetInButterfly(cachedOffset))); 659} 660 661// Compile a load from an object's property storage. May overwrite base. 662void JIT::compileGetDirectOffset(RegisterID base, RegisterID result, PropertyOffset cachedOffset) 663{ 664 if (isInlineOffset(cachedOffset)) { 665 load64(Address(base, JSObject::offsetOfInlineStorage() + sizeof(JSValue) * offsetInInlineStorage(cachedOffset)), result); 666 return; 667 } 668 669 loadPtr(Address(base, JSObject::butterflyOffset()), result); 670 load64(Address(result, sizeof(JSValue) * offsetInButterfly(cachedOffset)), result); 671} 672 673void JIT::compileGetDirectOffset(JSObject* base, RegisterID result, PropertyOffset cachedOffset) 674{ 675 if (isInlineOffset(cachedOffset)) { 676 load64(base->locationForOffset(cachedOffset), result); 677 return; 678 } 679 680 loadPtr(base->butterflyAddress(), result); 681 load64(Address(result, offsetInButterfly(cachedOffset) * sizeof(WriteBarrier<Unknown>)), result); 682} 683 684void JIT::privateCompilePutByIdTransition(StructureStubInfo* stubInfo, Structure* oldStructure, Structure* newStructure, PropertyOffset cachedOffset, StructureChain* chain, ReturnAddressPtr returnAddress, bool direct) 685{ 686 move(nonArgGPR1, regT0); 687 688 JumpList failureCases; 689 // Check eax is an object of the right Structure. 690 failureCases.append(emitJumpIfNotJSCell(regT0)); 691 failureCases.append(branchPtr(NotEqual, Address(regT0, JSCell::structureOffset()), TrustedImmPtr(oldStructure))); 692 693 testPrototype(oldStructure->storedPrototype(), failureCases, stubInfo); 694 695 ASSERT(oldStructure->storedPrototype().isNull() || oldStructure->storedPrototype().asCell()->structure() == chain->head()->get()); 696 697 // ecx = baseObject->m_structure 698 if (!direct) { 699 for (WriteBarrier<Structure>* it = chain->head(); *it; ++it) { 700 ASSERT((*it)->storedPrototype().isNull() || (*it)->storedPrototype().asCell()->structure() == it[1].get()); 701 testPrototype((*it)->storedPrototype(), failureCases, stubInfo); 702 } 703 } 704 705 // If we succeed in all of our checks, and the code was optimizable, then make sure we 706 // decrement the rare case counter. 707#if ENABLE(VALUE_PROFILER) 708 if (m_codeBlock->canCompileWithDFG() >= DFG::MayInline) { 709 sub32( 710 TrustedImm32(1), 711 AbsoluteAddress(&m_codeBlock->rareCaseProfileForBytecodeOffset(stubInfo->bytecodeIndex)->m_counter)); 712 } 713#endif 714 715 // emit a call only if storage realloc is needed 716 bool willNeedStorageRealloc = oldStructure->outOfLineCapacity() != newStructure->outOfLineCapacity(); 717 if (willNeedStorageRealloc) { 718 // This trampoline was called to like a JIT stub; before we can can call again we need to 719 // remove the return address from the stack, to prevent the stack from becoming misaligned. 720 preserveReturnAddressAfterCall(regT3); 721 722 JITStubCall stubCall(this, cti_op_put_by_id_transition_realloc); 723 stubCall.skipArgument(); // base 724 stubCall.skipArgument(); // ident 725 stubCall.skipArgument(); // value 726 stubCall.addArgument(TrustedImm32(oldStructure->outOfLineCapacity())); 727 stubCall.addArgument(TrustedImmPtr(newStructure)); 728 stubCall.call(regT0); 729 emitGetJITStubArg(2, regT1); 730 731 restoreReturnAddressBeforeReturn(regT3); 732 } 733 734 // Planting the new structure triggers the write barrier so we need 735 // an unconditional barrier here. 736 emitWriteBarrier(regT0, regT1, regT2, regT3, UnconditionalWriteBarrier, WriteBarrierForPropertyAccess); 737 738 ASSERT(newStructure->classInfo() == oldStructure->classInfo()); 739 storePtr(TrustedImmPtr(newStructure), Address(regT0, JSCell::structureOffset())); 740 compilePutDirectOffset(regT0, regT1, cachedOffset); 741 742 ret(); 743 744 ASSERT(!failureCases.empty()); 745 failureCases.link(this); 746 restoreArgumentReferenceForTrampoline(); 747 Call failureCall = tailRecursiveCall(); 748 749 LinkBuffer patchBuffer(*m_vm, this, m_codeBlock); 750 751 patchBuffer.link(failureCall, FunctionPtr(direct ? cti_op_put_by_id_direct_fail : cti_op_put_by_id_fail)); 752 753 if (willNeedStorageRealloc) { 754 ASSERT(m_calls.size() == 1); 755 patchBuffer.link(m_calls[0].from, FunctionPtr(cti_op_put_by_id_transition_realloc)); 756 } 757 758 stubInfo->stubRoutine = createJITStubRoutine( 759 FINALIZE_CODE( 760 patchBuffer, 761 ("Baseline put_by_id transition for %s, return point %p", 762 toCString(*m_codeBlock).data(), returnAddress.value())), 763 *m_vm, 764 m_codeBlock->ownerExecutable(), 765 willNeedStorageRealloc, 766 newStructure); 767 RepatchBuffer repatchBuffer(m_codeBlock); 768 repatchBuffer.relinkCallerToTrampoline(returnAddress, CodeLocationLabel(stubInfo->stubRoutine->code().code())); 769} 770 771void JIT::patchGetByIdSelf(CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, PropertyOffset cachedOffset, ReturnAddressPtr returnAddress) 772{ 773 RepatchBuffer repatchBuffer(codeBlock); 774 775 // We don't want to patch more than once - in future go to cti_op_get_by_id_generic. 776 // Should probably go to cti_op_get_by_id_fail, but that doesn't do anything interesting right now. 777 repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_self_fail)); 778 779 // Patch the offset into the propoerty map to load from, then patch the Structure to look for. 780 repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(stubInfo->patch.baseline.u.get.structureToCompare), structure); 781 repatchBuffer.setLoadInstructionIsActive(stubInfo->hotPathBegin.convertibleLoadAtOffset(stubInfo->patch.baseline.u.get.propertyStorageLoad), isOutOfLineOffset(cachedOffset)); 782 repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelCompactAtOffset(stubInfo->patch.baseline.u.get.displacementLabel), offsetRelativeToPatchedStorage(cachedOffset)); 783} 784 785void JIT::patchPutByIdReplace(CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, PropertyOffset cachedOffset, ReturnAddressPtr returnAddress, bool direct) 786{ 787 RepatchBuffer repatchBuffer(codeBlock); 788 789 // We don't want to patch more than once - in future go to cti_op_put_by_id_generic. 790 // Should probably go to cti_op_put_by_id_fail, but that doesn't do anything interesting right now. 791 repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(direct ? cti_op_put_by_id_direct_generic : cti_op_put_by_id_generic)); 792 793 // Patch the offset into the propoerty map to load from, then patch the Structure to look for. 794 repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(stubInfo->patch.baseline.u.put.structureToCompare), structure); 795 repatchBuffer.setLoadInstructionIsActive(stubInfo->hotPathBegin.convertibleLoadAtOffset(stubInfo->patch.baseline.u.put.propertyStorageLoad), isOutOfLineOffset(cachedOffset)); 796 repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(stubInfo->patch.baseline.u.put.displacementLabel), offsetRelativeToPatchedStorage(cachedOffset)); 797} 798 799void JIT::privateCompilePatchGetArrayLength(ReturnAddressPtr returnAddress) 800{ 801 StructureStubInfo* stubInfo = &m_codeBlock->getStubInfo(returnAddress); 802 803 // Check eax is an array 804 loadPtr(Address(regT0, JSCell::structureOffset()), regT2); 805 Jump failureCases1 = branchTest32(Zero, regT2, TrustedImm32(IsArray)); 806 Jump failureCases2 = branchTest32(Zero, regT2, TrustedImm32(IndexingShapeMask)); 807 808 // Checks out okay! - get the length from the storage 809 loadPtr(Address(regT0, JSObject::butterflyOffset()), regT3); 810 load32(Address(regT3, ArrayStorage::lengthOffset()), regT2); 811 Jump failureCases3 = branch32(LessThan, regT2, TrustedImm32(0)); 812 813 emitFastArithIntToImmNoCheck(regT2, regT0); 814 Jump success = jump(); 815 816 LinkBuffer patchBuffer(*m_vm, this, m_codeBlock); 817 818 // Use the patch information to link the failure cases back to the original slow case routine. 819 CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin); 820 patchBuffer.link(failureCases1, slowCaseBegin); 821 patchBuffer.link(failureCases2, slowCaseBegin); 822 patchBuffer.link(failureCases3, slowCaseBegin); 823 824 // On success return back to the hot patch code, at a point it will perform the store to dest for us. 825 patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult)); 826 827 // Track the stub we have created so that it will be deleted later. 828 stubInfo->stubRoutine = FINALIZE_CODE_FOR_STUB( 829 patchBuffer, 830 ("Basline JIT get_by_id array length stub for %s, return point %p", 831 toCString(*m_codeBlock).data(), 832 stubInfo->hotPathBegin.labelAtOffset( 833 stubInfo->patch.baseline.u.get.putResult).executableAddress())); 834 835 // Finally patch the jump to slow case back in the hot path to jump here instead. 836 CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck); 837 RepatchBuffer repatchBuffer(m_codeBlock); 838 repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubInfo->stubRoutine->code().code())); 839 840 // We don't want to patch more than once - in future go to cti_op_put_by_id_generic. 841 repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_array_fail)); 842} 843 844void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* structure, Structure* prototypeStructure, const Identifier& ident, const PropertySlot& slot, PropertyOffset cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame) 845{ 846 // The prototype object definitely exists (if this stub exists the CodeBlock is referencing a Structure that is 847 // referencing the prototype object - let's speculatively load it's table nice and early!) 848 JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame)); 849 850 // Check eax is an object of the right Structure. 851 Jump failureCases1 = checkStructure(regT0, structure); 852 853 // Check the prototype object's Structure had not changed. 854 Jump failureCases2 = addStructureTransitionCheck(protoObject, prototypeStructure, stubInfo, regT3); 855 856 bool needsStubLink = false; 857 858 // Checks out okay! 859 if (slot.cachedPropertyType() == PropertySlot::Getter) { 860 needsStubLink = true; 861 compileGetDirectOffset(protoObject, regT1, cachedOffset); 862 JITStubCall stubCall(this, cti_op_get_by_id_getter_stub); 863 stubCall.addArgument(regT1); 864 stubCall.addArgument(regT0); 865 stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress())); 866 stubCall.call(); 867 } else if (slot.cachedPropertyType() == PropertySlot::Custom) { 868 needsStubLink = true; 869 JITStubCall stubCall(this, cti_op_get_by_id_custom_stub); 870 stubCall.addArgument(TrustedImmPtr(protoObject)); 871 stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress())); 872 stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident))); 873 stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress())); 874 stubCall.call(); 875 } else 876 compileGetDirectOffset(protoObject, regT0, cachedOffset); 877 Jump success = jump(); 878 LinkBuffer patchBuffer(*m_vm, this, m_codeBlock); 879 880 // Use the patch information to link the failure cases back to the original slow case routine. 881 CodeLocationLabel slowCaseBegin = stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin); 882 patchBuffer.link(failureCases1, slowCaseBegin); 883 if (failureCases2.isSet()) 884 patchBuffer.link(failureCases2, slowCaseBegin); 885 886 // On success return back to the hot patch code, at a point it will perform the store to dest for us. 887 patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult)); 888 889 if (needsStubLink) { 890 for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) { 891 if (iter->to) 892 patchBuffer.link(iter->from, FunctionPtr(iter->to)); 893 } 894 } 895 // Track the stub we have created so that it will be deleted later. 896 stubInfo->stubRoutine = createJITStubRoutine( 897 FINALIZE_CODE( 898 patchBuffer, 899 ("Baseline JIT get_by_id proto stub for %s, return point %p", 900 toCString(*m_codeBlock).data(), stubInfo->hotPathBegin.labelAtOffset( 901 stubInfo->patch.baseline.u.get.putResult).executableAddress())), 902 *m_vm, 903 m_codeBlock->ownerExecutable(), 904 needsStubLink); 905 906 // Finally patch the jump to slow case back in the hot path to jump here instead. 907 CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck); 908 RepatchBuffer repatchBuffer(m_codeBlock); 909 repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubInfo->stubRoutine->code().code())); 910 911 // We don't want to patch more than once - in future go to cti_op_put_by_id_generic. 912 repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_proto_list)); 913} 914 915void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* polymorphicStructures, int currentIndex, Structure* structure, const Identifier& ident, const PropertySlot& slot, PropertyOffset cachedOffset) 916{ 917 Jump failureCase = checkStructure(regT0, structure); 918 bool needsStubLink = false; 919 bool isDirect = false; 920 if (slot.cachedPropertyType() == PropertySlot::Getter) { 921 needsStubLink = true; 922 compileGetDirectOffset(regT0, regT1, cachedOffset); 923 JITStubCall stubCall(this, cti_op_get_by_id_getter_stub); 924 stubCall.addArgument(regT1); 925 stubCall.addArgument(regT0); 926 stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress())); 927 stubCall.call(); 928 } else if (slot.cachedPropertyType() == PropertySlot::Custom) { 929 needsStubLink = true; 930 JITStubCall stubCall(this, cti_op_get_by_id_custom_stub); 931 stubCall.addArgument(regT0); 932 stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress())); 933 stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident))); 934 stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress())); 935 stubCall.call(); 936 } else { 937 isDirect = true; 938 compileGetDirectOffset(regT0, regT0, cachedOffset); 939 } 940 Jump success = jump(); 941 942 LinkBuffer patchBuffer(*m_vm, this, m_codeBlock); 943 944 if (needsStubLink) { 945 for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) { 946 if (iter->to) 947 patchBuffer.link(iter->from, FunctionPtr(iter->to)); 948 } 949 } 950 951 // Use the patch information to link the failure cases back to the original slow case routine. 952 CodeLocationLabel lastProtoBegin = CodeLocationLabel(JITStubRoutine::asCodePtr(polymorphicStructures->list[currentIndex - 1].stubRoutine)); 953 if (!lastProtoBegin) 954 lastProtoBegin = stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin); 955 956 patchBuffer.link(failureCase, lastProtoBegin); 957 958 // On success return back to the hot patch code, at a point it will perform the store to dest for us. 959 patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult)); 960 961 RefPtr<JITStubRoutine> stubCode = createJITStubRoutine( 962 FINALIZE_CODE( 963 patchBuffer, 964 ("Baseline JIT get_by_id list stub for %s, return point %p", 965 toCString(*m_codeBlock).data(), stubInfo->hotPathBegin.labelAtOffset( 966 stubInfo->patch.baseline.u.get.putResult).executableAddress())), 967 *m_vm, 968 m_codeBlock->ownerExecutable(), 969 needsStubLink); 970 971 polymorphicStructures->list[currentIndex].set(*m_vm, m_codeBlock->ownerExecutable(), stubCode, structure, isDirect); 972 973 // Finally patch the jump to slow case back in the hot path to jump here instead. 974 CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck); 975 RepatchBuffer repatchBuffer(m_codeBlock); 976 repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubCode->code().code())); 977} 978 979void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, Structure* prototypeStructure, const Identifier& ident, const PropertySlot& slot, PropertyOffset cachedOffset, CallFrame* callFrame) 980{ 981 // The prototype object definitely exists (if this stub exists the CodeBlock is referencing a Structure that is 982 // referencing the prototype object - let's speculatively load it's table nice and early!) 983 JSObject* protoObject = asObject(structure->prototypeForLookup(callFrame)); 984 985 // Check eax is an object of the right Structure. 986 Jump failureCases1 = checkStructure(regT0, structure); 987 988 // Check the prototype object's Structure had not changed. 989 Jump failureCases2 = addStructureTransitionCheck(protoObject, prototypeStructure, stubInfo, regT3); 990 991 // Checks out okay! 992 bool needsStubLink = false; 993 bool isDirect = false; 994 if (slot.cachedPropertyType() == PropertySlot::Getter) { 995 needsStubLink = true; 996 compileGetDirectOffset(protoObject, regT1, cachedOffset); 997 JITStubCall stubCall(this, cti_op_get_by_id_getter_stub); 998 stubCall.addArgument(regT1); 999 stubCall.addArgument(regT0); 1000 stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress())); 1001 stubCall.call(); 1002 } else if (slot.cachedPropertyType() == PropertySlot::Custom) { 1003 needsStubLink = true; 1004 JITStubCall stubCall(this, cti_op_get_by_id_custom_stub); 1005 stubCall.addArgument(TrustedImmPtr(protoObject)); 1006 stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress())); 1007 stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident))); 1008 stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress())); 1009 stubCall.call(); 1010 } else { 1011 isDirect = true; 1012 compileGetDirectOffset(protoObject, regT0, cachedOffset); 1013 } 1014 1015 Jump success = jump(); 1016 1017 LinkBuffer patchBuffer(*m_vm, this, m_codeBlock); 1018 1019 if (needsStubLink) { 1020 for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) { 1021 if (iter->to) 1022 patchBuffer.link(iter->from, FunctionPtr(iter->to)); 1023 } 1024 } 1025 1026 // Use the patch information to link the failure cases back to the original slow case routine. 1027 CodeLocationLabel lastProtoBegin = CodeLocationLabel(JITStubRoutine::asCodePtr(prototypeStructures->list[currentIndex - 1].stubRoutine)); 1028 patchBuffer.link(failureCases1, lastProtoBegin); 1029 if (failureCases2.isSet()) 1030 patchBuffer.link(failureCases2, lastProtoBegin); 1031 1032 // On success return back to the hot patch code, at a point it will perform the store to dest for us. 1033 patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult)); 1034 1035 RefPtr<JITStubRoutine> stubCode = createJITStubRoutine( 1036 FINALIZE_CODE( 1037 patchBuffer, 1038 ("Baseline JIT get_by_id proto list stub for %s, return point %p", 1039 toCString(*m_codeBlock).data(), stubInfo->hotPathBegin.labelAtOffset( 1040 stubInfo->patch.baseline.u.get.putResult).executableAddress())), 1041 *m_vm, 1042 m_codeBlock->ownerExecutable(), 1043 needsStubLink); 1044 prototypeStructures->list[currentIndex].set(*m_vm, m_codeBlock->ownerExecutable(), stubCode, structure, prototypeStructure, isDirect); 1045 1046 // Finally patch the jump to slow case back in the hot path to jump here instead. 1047 CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck); 1048 RepatchBuffer repatchBuffer(m_codeBlock); 1049 repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubCode->code().code())); 1050} 1051 1052void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, StructureChain* chain, size_t count, const Identifier& ident, const PropertySlot& slot, PropertyOffset cachedOffset, CallFrame* callFrame) 1053{ 1054 ASSERT(count); 1055 JumpList bucketsOfFail; 1056 1057 // Check eax is an object of the right Structure. 1058 Jump baseObjectCheck = checkStructure(regT0, structure); 1059 bucketsOfFail.append(baseObjectCheck); 1060 1061 Structure* currStructure = structure; 1062 WriteBarrier<Structure>* it = chain->head(); 1063 JSObject* protoObject = 0; 1064 for (unsigned i = 0; i < count; ++i, ++it) { 1065 protoObject = asObject(currStructure->prototypeForLookup(callFrame)); 1066 currStructure = it->get(); 1067 testPrototype(protoObject, bucketsOfFail, stubInfo); 1068 } 1069 ASSERT(protoObject); 1070 1071 bool needsStubLink = false; 1072 bool isDirect = false; 1073 if (slot.cachedPropertyType() == PropertySlot::Getter) { 1074 needsStubLink = true; 1075 compileGetDirectOffset(protoObject, regT1, cachedOffset); 1076 JITStubCall stubCall(this, cti_op_get_by_id_getter_stub); 1077 stubCall.addArgument(regT1); 1078 stubCall.addArgument(regT0); 1079 stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress())); 1080 stubCall.call(); 1081 } else if (slot.cachedPropertyType() == PropertySlot::Custom) { 1082 needsStubLink = true; 1083 JITStubCall stubCall(this, cti_op_get_by_id_custom_stub); 1084 stubCall.addArgument(TrustedImmPtr(protoObject)); 1085 stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress())); 1086 stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident))); 1087 stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress())); 1088 stubCall.call(); 1089 } else { 1090 isDirect = true; 1091 compileGetDirectOffset(protoObject, regT0, cachedOffset); 1092 } 1093 Jump success = jump(); 1094 1095 LinkBuffer patchBuffer(*m_vm, this, m_codeBlock); 1096 1097 if (needsStubLink) { 1098 for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) { 1099 if (iter->to) 1100 patchBuffer.link(iter->from, FunctionPtr(iter->to)); 1101 } 1102 } 1103 1104 // Use the patch information to link the failure cases back to the original slow case routine. 1105 CodeLocationLabel lastProtoBegin = CodeLocationLabel(JITStubRoutine::asCodePtr(prototypeStructures->list[currentIndex - 1].stubRoutine)); 1106 1107 patchBuffer.link(bucketsOfFail, lastProtoBegin); 1108 1109 // On success return back to the hot patch code, at a point it will perform the store to dest for us. 1110 patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult)); 1111 1112 RefPtr<JITStubRoutine> stubRoutine = createJITStubRoutine( 1113 FINALIZE_CODE( 1114 patchBuffer, 1115 ("Baseline JIT get_by_id chain list stub for %s, return point %p", 1116 toCString(*m_codeBlock).data(), stubInfo->hotPathBegin.labelAtOffset( 1117 stubInfo->patch.baseline.u.get.putResult).executableAddress())), 1118 *m_vm, 1119 m_codeBlock->ownerExecutable(), 1120 needsStubLink); 1121 1122 // Track the stub we have created so that it will be deleted later. 1123 prototypeStructures->list[currentIndex].set(callFrame->vm(), m_codeBlock->ownerExecutable(), stubRoutine, structure, chain, isDirect); 1124 1125 // Finally patch the jump to slow case back in the hot path to jump here instead. 1126 CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck); 1127 RepatchBuffer repatchBuffer(m_codeBlock); 1128 repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine->code().code())); 1129} 1130 1131void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* structure, StructureChain* chain, size_t count, const Identifier& ident, const PropertySlot& slot, PropertyOffset cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame) 1132{ 1133 ASSERT(count); 1134 1135 JumpList bucketsOfFail; 1136 1137 // Check eax is an object of the right Structure. 1138 bucketsOfFail.append(checkStructure(regT0, structure)); 1139 1140 Structure* currStructure = structure; 1141 WriteBarrier<Structure>* it = chain->head(); 1142 JSObject* protoObject = 0; 1143 for (unsigned i = 0; i < count; ++i, ++it) { 1144 protoObject = asObject(currStructure->prototypeForLookup(callFrame)); 1145 currStructure = it->get(); 1146 testPrototype(protoObject, bucketsOfFail, stubInfo); 1147 } 1148 ASSERT(protoObject); 1149 1150 bool needsStubLink = false; 1151 if (slot.cachedPropertyType() == PropertySlot::Getter) { 1152 needsStubLink = true; 1153 compileGetDirectOffset(protoObject, regT1, cachedOffset); 1154 JITStubCall stubCall(this, cti_op_get_by_id_getter_stub); 1155 stubCall.addArgument(regT1); 1156 stubCall.addArgument(regT0); 1157 stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress())); 1158 stubCall.call(); 1159 } else if (slot.cachedPropertyType() == PropertySlot::Custom) { 1160 needsStubLink = true; 1161 JITStubCall stubCall(this, cti_op_get_by_id_custom_stub); 1162 stubCall.addArgument(TrustedImmPtr(protoObject)); 1163 stubCall.addArgument(TrustedImmPtr(FunctionPtr(slot.customGetter()).executableAddress())); 1164 stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident))); 1165 stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress())); 1166 stubCall.call(); 1167 } else 1168 compileGetDirectOffset(protoObject, regT0, cachedOffset); 1169 Jump success = jump(); 1170 1171 LinkBuffer patchBuffer(*m_vm, this, m_codeBlock); 1172 1173 if (needsStubLink) { 1174 for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) { 1175 if (iter->to) 1176 patchBuffer.link(iter->from, FunctionPtr(iter->to)); 1177 } 1178 } 1179 1180 // Use the patch information to link the failure cases back to the original slow case routine. 1181 patchBuffer.link(bucketsOfFail, stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin)); 1182 1183 // On success return back to the hot patch code, at a point it will perform the store to dest for us. 1184 patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(stubInfo->patch.baseline.u.get.putResult)); 1185 1186 // Track the stub we have created so that it will be deleted later. 1187 RefPtr<JITStubRoutine> stubRoutine = createJITStubRoutine( 1188 FINALIZE_CODE( 1189 patchBuffer, 1190 ("Baseline JIT get_by_id chain stub for %s, return point %p", 1191 toCString(*m_codeBlock).data(), stubInfo->hotPathBegin.labelAtOffset( 1192 stubInfo->patch.baseline.u.get.putResult).executableAddress())), 1193 *m_vm, 1194 m_codeBlock->ownerExecutable(), 1195 needsStubLink); 1196 stubInfo->stubRoutine = stubRoutine; 1197 1198 // Finally patch the jump to slow case back in the hot path to jump here instead. 1199 CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck); 1200 RepatchBuffer repatchBuffer(m_codeBlock); 1201 repatchBuffer.relink(jumpLocation, CodeLocationLabel(stubRoutine->code().code())); 1202 1203 // We don't want to patch more than once - in future go to cti_op_put_by_id_generic. 1204 repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_proto_list)); 1205} 1206 1207void JIT::emit_op_get_scoped_var(Instruction* currentInstruction) 1208{ 1209 int skip = currentInstruction[3].u.operand; 1210 1211 emitGetFromCallFrameHeaderPtr(JSStack::ScopeChain, regT0); 1212 bool checkTopLevel = m_codeBlock->codeType() == FunctionCode && m_codeBlock->needsFullScopeChain(); 1213 ASSERT(skip || !checkTopLevel); 1214 if (checkTopLevel && skip--) { 1215 Jump activationNotCreated; 1216 if (checkTopLevel) 1217 activationNotCreated = branchTestPtr(Zero, addressFor(m_codeBlock->activationRegister())); 1218 loadPtr(Address(regT0, JSScope::offsetOfNext()), regT0); 1219 activationNotCreated.link(this); 1220 } 1221 while (skip--) 1222 loadPtr(Address(regT0, JSScope::offsetOfNext()), regT0); 1223 1224 loadPtr(Address(regT0, JSVariableObject::offsetOfRegisters()), regT0); 1225 loadPtr(Address(regT0, currentInstruction[2].u.operand * sizeof(Register)), regT0); 1226 emitValueProfilingSite(); 1227 emitPutVirtualRegister(currentInstruction[1].u.operand); 1228} 1229 1230void JIT::emit_op_put_scoped_var(Instruction* currentInstruction) 1231{ 1232 int skip = currentInstruction[2].u.operand; 1233 1234 emitGetVirtualRegister(currentInstruction[3].u.operand, regT0); 1235 1236 emitGetFromCallFrameHeaderPtr(JSStack::ScopeChain, regT1); 1237 bool checkTopLevel = m_codeBlock->codeType() == FunctionCode && m_codeBlock->needsFullScopeChain(); 1238 ASSERT(skip || !checkTopLevel); 1239 if (checkTopLevel && skip--) { 1240 Jump activationNotCreated; 1241 if (checkTopLevel) 1242 activationNotCreated = branchTestPtr(Zero, addressFor(m_codeBlock->activationRegister())); 1243 loadPtr(Address(regT1, JSScope::offsetOfNext()), regT1); 1244 activationNotCreated.link(this); 1245 } 1246 while (skip--) 1247 loadPtr(Address(regT1, JSScope::offsetOfNext()), regT1); 1248 1249 emitWriteBarrier(regT1, regT0, regT2, regT3, ShouldFilterImmediates, WriteBarrierForVariableAccess); 1250 1251 loadPtr(Address(regT1, JSVariableObject::offsetOfRegisters()), regT1); 1252 storePtr(regT0, Address(regT1, currentInstruction[1].u.operand * sizeof(Register))); 1253} 1254 1255void JIT::emit_op_init_global_const(Instruction* currentInstruction) 1256{ 1257 JSGlobalObject* globalObject = m_codeBlock->globalObject(); 1258 1259 emitGetVirtualRegister(currentInstruction[2].u.operand, regT0); 1260 1261 store64(regT0, currentInstruction[1].u.registerPointer); 1262 if (Heap::isWriteBarrierEnabled()) 1263 emitWriteBarrier(globalObject, regT0, regT2, ShouldFilterImmediates, WriteBarrierForVariableAccess); 1264} 1265 1266void JIT::emit_op_init_global_const_check(Instruction* currentInstruction) 1267{ 1268 emitGetVirtualRegister(currentInstruction[2].u.operand, regT0); 1269 1270 addSlowCase(branchTest8(NonZero, AbsoluteAddress(currentInstruction[3].u.predicatePointer))); 1271 1272 JSGlobalObject* globalObject = m_codeBlock->globalObject(); 1273 1274 store64(regT0, currentInstruction[1].u.registerPointer); 1275 if (Heap::isWriteBarrierEnabled()) 1276 emitWriteBarrier(globalObject, regT0, regT2, ShouldFilterImmediates, WriteBarrierForVariableAccess); 1277} 1278 1279void JIT::emitSlow_op_init_global_const_check(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) 1280{ 1281 linkSlowCase(iter); 1282 1283 JITStubCall stubCall(this, cti_op_init_global_const_check); 1284 stubCall.addArgument(regT0); 1285 stubCall.addArgument(TrustedImm32(currentInstruction[4].u.operand)); 1286 stubCall.call(); 1287} 1288 1289void JIT::resetPatchGetById(RepatchBuffer& repatchBuffer, StructureStubInfo* stubInfo) 1290{ 1291 repatchBuffer.relink(stubInfo->callReturnLocation, cti_op_get_by_id); 1292 repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(stubInfo->patch.baseline.u.get.structureToCompare), reinterpret_cast<void*>(unusedPointer)); 1293 repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelCompactAtOffset(stubInfo->patch.baseline.u.get.displacementLabel), 0); 1294 repatchBuffer.relink(stubInfo->hotPathBegin.jumpAtOffset(stubInfo->patch.baseline.u.get.structureCheck), stubInfo->callReturnLocation.labelAtOffset(-stubInfo->patch.baseline.u.get.coldPathBegin)); 1295} 1296 1297void JIT::resetPatchPutById(RepatchBuffer& repatchBuffer, StructureStubInfo* stubInfo) 1298{ 1299 if (isDirectPutById(stubInfo)) 1300 repatchBuffer.relink(stubInfo->callReturnLocation, cti_op_put_by_id_direct); 1301 else 1302 repatchBuffer.relink(stubInfo->callReturnLocation, cti_op_put_by_id); 1303 repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabelPtrAtOffset(stubInfo->patch.baseline.u.put.structureToCompare), reinterpret_cast<void*>(unusedPointer)); 1304 repatchBuffer.repatch(stubInfo->hotPathBegin.dataLabel32AtOffset(stubInfo->patch.baseline.u.put.displacementLabel), 0); 1305} 1306 1307#endif // USE(JSVALUE64) 1308 1309void JIT::emitWriteBarrier(RegisterID owner, RegisterID value, RegisterID scratch, RegisterID scratch2, WriteBarrierMode mode, WriteBarrierUseKind useKind) 1310{ 1311 UNUSED_PARAM(owner); 1312 UNUSED_PARAM(scratch); 1313 UNUSED_PARAM(scratch2); 1314 UNUSED_PARAM(useKind); 1315 UNUSED_PARAM(value); 1316 UNUSED_PARAM(mode); 1317 ASSERT(owner != scratch); 1318 ASSERT(owner != scratch2); 1319 1320#if ENABLE(WRITE_BARRIER_PROFILING) 1321 emitCount(WriteBarrierCounters::jitCounterFor(useKind)); 1322#endif 1323} 1324 1325void JIT::emitWriteBarrier(JSCell* owner, RegisterID value, RegisterID scratch, WriteBarrierMode mode, WriteBarrierUseKind useKind) 1326{ 1327 UNUSED_PARAM(owner); 1328 UNUSED_PARAM(scratch); 1329 UNUSED_PARAM(useKind); 1330 UNUSED_PARAM(value); 1331 UNUSED_PARAM(mode); 1332 1333#if ENABLE(WRITE_BARRIER_PROFILING) 1334 emitCount(WriteBarrierCounters::jitCounterFor(useKind)); 1335#endif 1336} 1337 1338JIT::Jump JIT::addStructureTransitionCheck(JSCell* object, Structure* structure, StructureStubInfo* stubInfo, RegisterID scratch) 1339{ 1340 if (object->structure() == structure && structure->transitionWatchpointSetIsStillValid()) { 1341 structure->addTransitionWatchpoint(stubInfo->addWatchpoint(m_codeBlock)); 1342#if !ASSERT_DISABLED 1343 move(TrustedImmPtr(object), scratch); 1344 Jump ok = branchPtr(Equal, Address(scratch, JSCell::structureOffset()), TrustedImmPtr(structure)); 1345 breakpoint(); 1346 ok.link(this); 1347#endif 1348 Jump result; // Returning an unset jump this way because otherwise VC++ would complain. 1349 return result; 1350 } 1351 1352 move(TrustedImmPtr(object), scratch); 1353 return branchPtr(NotEqual, Address(scratch, JSCell::structureOffset()), TrustedImmPtr(structure)); 1354} 1355 1356void JIT::addStructureTransitionCheck(JSCell* object, Structure* structure, StructureStubInfo* stubInfo, JumpList& failureCases, RegisterID scratch) 1357{ 1358 Jump failureCase = addStructureTransitionCheck(object, structure, stubInfo, scratch); 1359 if (!failureCase.isSet()) 1360 return; 1361 1362 failureCases.append(failureCase); 1363} 1364 1365void JIT::testPrototype(JSValue prototype, JumpList& failureCases, StructureStubInfo* stubInfo) 1366{ 1367 if (prototype.isNull()) 1368 return; 1369 1370 ASSERT(prototype.isCell()); 1371 addStructureTransitionCheck(prototype.asCell(), prototype.asCell()->structure(), stubInfo, failureCases, regT3); 1372} 1373 1374bool JIT::isDirectPutById(StructureStubInfo* stubInfo) 1375{ 1376 switch (stubInfo->accessType) { 1377 case access_put_by_id_transition_normal: 1378 return false; 1379 case access_put_by_id_transition_direct: 1380 return true; 1381 case access_put_by_id_replace: 1382 case access_put_by_id_generic: { 1383 void* oldCall = MacroAssembler::readCallTarget(stubInfo->callReturnLocation).executableAddress(); 1384 if (oldCall == bitwise_cast<void*>(cti_op_put_by_id_direct) 1385 || oldCall == bitwise_cast<void*>(cti_op_put_by_id_direct_generic) 1386 || oldCall == bitwise_cast<void*>(cti_op_put_by_id_direct_fail)) 1387 return true; 1388 ASSERT(oldCall == bitwise_cast<void*>(cti_op_put_by_id) 1389 || oldCall == bitwise_cast<void*>(cti_op_put_by_id_generic) 1390 || oldCall == bitwise_cast<void*>(cti_op_put_by_id_fail)); 1391 return false; 1392 } 1393 default: 1394 RELEASE_ASSERT_NOT_REACHED(); 1395 return false; 1396 } 1397} 1398 1399void JIT::privateCompileGetByVal(ByValInfo* byValInfo, ReturnAddressPtr returnAddress, JITArrayMode arrayMode) 1400{ 1401 Instruction* currentInstruction = m_codeBlock->instructions().begin() + byValInfo->bytecodeIndex; 1402 1403 PatchableJump badType; 1404 JumpList slowCases; 1405 1406 switch (arrayMode) { 1407 case JITInt32: 1408 slowCases = emitInt32GetByVal(currentInstruction, badType); 1409 break; 1410 case JITDouble: 1411 slowCases = emitDoubleGetByVal(currentInstruction, badType); 1412 break; 1413 case JITContiguous: 1414 slowCases = emitContiguousGetByVal(currentInstruction, badType); 1415 break; 1416 case JITArrayStorage: 1417 slowCases = emitArrayStorageGetByVal(currentInstruction, badType); 1418 break; 1419 case JITInt8Array: 1420 slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_vm->int8ArrayDescriptor(), 1, SignedTypedArray); 1421 break; 1422 case JITInt16Array: 1423 slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_vm->int16ArrayDescriptor(), 2, SignedTypedArray); 1424 break; 1425 case JITInt32Array: 1426 slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_vm->int32ArrayDescriptor(), 4, SignedTypedArray); 1427 break; 1428 case JITUint8Array: 1429 slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_vm->uint8ArrayDescriptor(), 1, UnsignedTypedArray); 1430 break; 1431 case JITUint8ClampedArray: 1432 slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_vm->uint8ClampedArrayDescriptor(), 1, UnsignedTypedArray); 1433 break; 1434 case JITUint16Array: 1435 slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_vm->uint16ArrayDescriptor(), 2, UnsignedTypedArray); 1436 break; 1437 case JITUint32Array: 1438 slowCases = emitIntTypedArrayGetByVal(currentInstruction, badType, m_vm->uint32ArrayDescriptor(), 4, UnsignedTypedArray); 1439 break; 1440 case JITFloat32Array: 1441 slowCases = emitFloatTypedArrayGetByVal(currentInstruction, badType, m_vm->float32ArrayDescriptor(), 4); 1442 break; 1443 case JITFloat64Array: 1444 slowCases = emitFloatTypedArrayGetByVal(currentInstruction, badType, m_vm->float64ArrayDescriptor(), 8); 1445 break; 1446 default: 1447 CRASH(); 1448 } 1449 1450 Jump done = jump(); 1451 1452 LinkBuffer patchBuffer(*m_vm, this, m_codeBlock); 1453 1454 patchBuffer.link(badType, CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(returnAddress.value())).labelAtOffset(byValInfo->returnAddressToSlowPath)); 1455 patchBuffer.link(slowCases, CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(returnAddress.value())).labelAtOffset(byValInfo->returnAddressToSlowPath)); 1456 1457 patchBuffer.link(done, byValInfo->badTypeJump.labelAtOffset(byValInfo->badTypeJumpToDone)); 1458 1459 byValInfo->stubRoutine = FINALIZE_CODE_FOR_STUB( 1460 patchBuffer, 1461 ("Baseline get_by_val stub for %s, return point %p", toCString(*m_codeBlock).data(), returnAddress.value())); 1462 1463 RepatchBuffer repatchBuffer(m_codeBlock); 1464 repatchBuffer.relink(byValInfo->badTypeJump, CodeLocationLabel(byValInfo->stubRoutine->code().code())); 1465 repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_val_generic)); 1466} 1467 1468void JIT::privateCompilePutByVal(ByValInfo* byValInfo, ReturnAddressPtr returnAddress, JITArrayMode arrayMode) 1469{ 1470 Instruction* currentInstruction = m_codeBlock->instructions().begin() + byValInfo->bytecodeIndex; 1471 1472 PatchableJump badType; 1473 JumpList slowCases; 1474 1475 switch (arrayMode) { 1476 case JITInt32: 1477 slowCases = emitInt32PutByVal(currentInstruction, badType); 1478 break; 1479 case JITDouble: 1480 slowCases = emitDoublePutByVal(currentInstruction, badType); 1481 break; 1482 case JITContiguous: 1483 slowCases = emitContiguousPutByVal(currentInstruction, badType); 1484 break; 1485 case JITArrayStorage: 1486 slowCases = emitArrayStoragePutByVal(currentInstruction, badType); 1487 break; 1488 case JITInt8Array: 1489 slowCases = emitIntTypedArrayPutByVal(currentInstruction, badType, m_vm->int8ArrayDescriptor(), 1, SignedTypedArray, TruncateRounding); 1490 break; 1491 case JITInt16Array: 1492 slowCases = emitIntTypedArrayPutByVal(currentInstruction, badType, m_vm->int16ArrayDescriptor(), 2, SignedTypedArray, TruncateRounding); 1493 break; 1494 case JITInt32Array: 1495 slowCases = emitIntTypedArrayPutByVal(currentInstruction, badType, m_vm->int32ArrayDescriptor(), 4, SignedTypedArray, TruncateRounding); 1496 break; 1497 case JITUint8Array: 1498 slowCases = emitIntTypedArrayPutByVal(currentInstruction, badType, m_vm->uint8ArrayDescriptor(), 1, UnsignedTypedArray, TruncateRounding); 1499 break; 1500 case JITUint8ClampedArray: 1501 slowCases = emitIntTypedArrayPutByVal(currentInstruction, badType, m_vm->uint8ClampedArrayDescriptor(), 1, UnsignedTypedArray, ClampRounding); 1502 break; 1503 case JITUint16Array: 1504 slowCases = emitIntTypedArrayPutByVal(currentInstruction, badType, m_vm->uint16ArrayDescriptor(), 2, UnsignedTypedArray, TruncateRounding); 1505 break; 1506 case JITUint32Array: 1507 slowCases = emitIntTypedArrayPutByVal(currentInstruction, badType, m_vm->uint32ArrayDescriptor(), 4, UnsignedTypedArray, TruncateRounding); 1508 break; 1509 case JITFloat32Array: 1510 slowCases = emitFloatTypedArrayPutByVal(currentInstruction, badType, m_vm->float32ArrayDescriptor(), 4); 1511 break; 1512 case JITFloat64Array: 1513 slowCases = emitFloatTypedArrayPutByVal(currentInstruction, badType, m_vm->float64ArrayDescriptor(), 8); 1514 break; 1515 default: 1516 CRASH(); 1517 break; 1518 } 1519 1520 Jump done = jump(); 1521 1522 LinkBuffer patchBuffer(*m_vm, this, m_codeBlock); 1523 1524 patchBuffer.link(badType, CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(returnAddress.value())).labelAtOffset(byValInfo->returnAddressToSlowPath)); 1525 patchBuffer.link(slowCases, CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(returnAddress.value())).labelAtOffset(byValInfo->returnAddressToSlowPath)); 1526 1527 patchBuffer.link(done, byValInfo->badTypeJump.labelAtOffset(byValInfo->badTypeJumpToDone)); 1528 1529 byValInfo->stubRoutine = FINALIZE_CODE_FOR_STUB( 1530 patchBuffer, 1531 ("Baseline put_by_val stub for %s, return point %p", toCString(*m_codeBlock).data(), returnAddress.value())); 1532 1533 RepatchBuffer repatchBuffer(m_codeBlock); 1534 repatchBuffer.relink(byValInfo->badTypeJump, CodeLocationLabel(byValInfo->stubRoutine->code().code())); 1535 repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_put_by_val_generic)); 1536} 1537 1538JIT::JumpList JIT::emitIntTypedArrayGetByVal(Instruction*, PatchableJump& badType, const TypedArrayDescriptor& descriptor, size_t elementSize, TypedArraySignedness signedness) 1539{ 1540 // The best way to test the array type is to use the classInfo. We need to do so without 1541 // clobbering the register that holds the indexing type, base, and property. 1542 1543#if USE(JSVALUE64) 1544 RegisterID base = regT0; 1545 RegisterID property = regT1; 1546 RegisterID resultPayload = regT0; 1547 RegisterID scratch = regT3; 1548#else 1549 RegisterID base = regT0; 1550 RegisterID property = regT2; 1551 RegisterID resultPayload = regT0; 1552 RegisterID resultTag = regT1; 1553 RegisterID scratch = regT3; 1554#endif 1555 1556 JumpList slowCases; 1557 1558 loadPtr(Address(base, JSCell::structureOffset()), scratch); 1559 badType = patchableBranchPtr(NotEqual, Address(scratch, Structure::classInfoOffset()), TrustedImmPtr(descriptor.m_classInfo)); 1560 slowCases.append(branch32(AboveOrEqual, property, Address(base, descriptor.m_lengthOffset))); 1561 loadPtr(Address(base, descriptor.m_storageOffset), base); 1562 1563 switch (elementSize) { 1564 case 1: 1565 if (signedness == SignedTypedArray) 1566 load8Signed(BaseIndex(base, property, TimesOne), resultPayload); 1567 else 1568 load8(BaseIndex(base, property, TimesOne), resultPayload); 1569 break; 1570 case 2: 1571 if (signedness == SignedTypedArray) 1572 load16Signed(BaseIndex(base, property, TimesTwo), resultPayload); 1573 else 1574 load16(BaseIndex(base, property, TimesTwo), resultPayload); 1575 break; 1576 case 4: 1577 load32(BaseIndex(base, property, TimesFour), resultPayload); 1578 break; 1579 default: 1580 CRASH(); 1581 } 1582 1583 Jump done; 1584 if (elementSize == 4 && signedness == UnsignedTypedArray) { 1585 Jump canBeInt = branch32(GreaterThanOrEqual, resultPayload, TrustedImm32(0)); 1586 1587 convertInt32ToDouble(resultPayload, fpRegT0); 1588 addDouble(AbsoluteAddress(&twoToThe32), fpRegT0); 1589#if USE(JSVALUE64) 1590 moveDoubleTo64(fpRegT0, resultPayload); 1591 sub64(tagTypeNumberRegister, resultPayload); 1592#else 1593 moveDoubleToInts(fpRegT0, resultPayload, resultTag); 1594#endif 1595 1596 done = jump(); 1597 canBeInt.link(this); 1598 } 1599 1600#if USE(JSVALUE64) 1601 or64(tagTypeNumberRegister, resultPayload); 1602#else 1603 move(TrustedImm32(JSValue::Int32Tag), resultTag); 1604#endif 1605 if (done.isSet()) 1606 done.link(this); 1607 return slowCases; 1608} 1609 1610JIT::JumpList JIT::emitFloatTypedArrayGetByVal(Instruction*, PatchableJump& badType, const TypedArrayDescriptor& descriptor, size_t elementSize) 1611{ 1612#if USE(JSVALUE64) 1613 RegisterID base = regT0; 1614 RegisterID property = regT1; 1615 RegisterID resultPayload = regT0; 1616 RegisterID scratch = regT3; 1617#else 1618 RegisterID base = regT0; 1619 RegisterID property = regT2; 1620 RegisterID resultPayload = regT0; 1621 RegisterID resultTag = regT1; 1622 RegisterID scratch = regT3; 1623#endif 1624 1625 JumpList slowCases; 1626 1627 loadPtr(Address(base, JSCell::structureOffset()), scratch); 1628 badType = patchableBranchPtr(NotEqual, Address(scratch, Structure::classInfoOffset()), TrustedImmPtr(descriptor.m_classInfo)); 1629 slowCases.append(branch32(AboveOrEqual, property, Address(base, descriptor.m_lengthOffset))); 1630 loadPtr(Address(base, descriptor.m_storageOffset), base); 1631 1632 switch (elementSize) { 1633 case 4: 1634 loadFloat(BaseIndex(base, property, TimesFour), fpRegT0); 1635 convertFloatToDouble(fpRegT0, fpRegT0); 1636 break; 1637 case 8: { 1638 loadDouble(BaseIndex(base, property, TimesEight), fpRegT0); 1639 break; 1640 } 1641 default: 1642 CRASH(); 1643 } 1644 1645 Jump notNaN = branchDouble(DoubleEqual, fpRegT0, fpRegT0); 1646 static const double NaN = QNaN; 1647 loadDouble(&NaN, fpRegT0); 1648 notNaN.link(this); 1649 1650#if USE(JSVALUE64) 1651 moveDoubleTo64(fpRegT0, resultPayload); 1652 sub64(tagTypeNumberRegister, resultPayload); 1653#else 1654 moveDoubleToInts(fpRegT0, resultPayload, resultTag); 1655#endif 1656 return slowCases; 1657} 1658 1659JIT::JumpList JIT::emitIntTypedArrayPutByVal(Instruction* currentInstruction, PatchableJump& badType, const TypedArrayDescriptor& descriptor, size_t elementSize, TypedArraySignedness signedness, TypedArrayRounding rounding) 1660{ 1661 unsigned value = currentInstruction[3].u.operand; 1662 1663#if USE(JSVALUE64) 1664 RegisterID base = regT0; 1665 RegisterID property = regT1; 1666 RegisterID earlyScratch = regT3; 1667 RegisterID lateScratch = regT2; 1668#else 1669 RegisterID base = regT0; 1670 RegisterID property = regT2; 1671 RegisterID earlyScratch = regT3; 1672 RegisterID lateScratch = regT1; 1673#endif 1674 1675 JumpList slowCases; 1676 1677 loadPtr(Address(base, JSCell::structureOffset()), earlyScratch); 1678 badType = patchableBranchPtr(NotEqual, Address(earlyScratch, Structure::classInfoOffset()), TrustedImmPtr(descriptor.m_classInfo)); 1679 slowCases.append(branch32(AboveOrEqual, property, Address(base, descriptor.m_lengthOffset))); 1680 1681#if USE(JSVALUE64) 1682 emitGetVirtualRegister(value, earlyScratch); 1683 slowCases.append(emitJumpIfNotImmediateInteger(earlyScratch)); 1684#else 1685 emitLoad(value, lateScratch, earlyScratch); 1686 slowCases.append(branch32(NotEqual, lateScratch, TrustedImm32(JSValue::Int32Tag))); 1687#endif 1688 1689 // We would be loading this into base as in get_by_val, except that the slow 1690 // path expects the base to be unclobbered. 1691 loadPtr(Address(base, descriptor.m_storageOffset), lateScratch); 1692 1693 if (rounding == ClampRounding) { 1694 ASSERT(elementSize == 1); 1695 ASSERT_UNUSED(signedness, signedness = UnsignedTypedArray); 1696 Jump inBounds = branch32(BelowOrEqual, earlyScratch, TrustedImm32(0xff)); 1697 Jump tooBig = branch32(GreaterThan, earlyScratch, TrustedImm32(0xff)); 1698 xor32(earlyScratch, earlyScratch); 1699 Jump clamped = jump(); 1700 tooBig.link(this); 1701 move(TrustedImm32(0xff), earlyScratch); 1702 clamped.link(this); 1703 inBounds.link(this); 1704 } 1705 1706 switch (elementSize) { 1707 case 1: 1708 store8(earlyScratch, BaseIndex(lateScratch, property, TimesOne)); 1709 break; 1710 case 2: 1711 store16(earlyScratch, BaseIndex(lateScratch, property, TimesTwo)); 1712 break; 1713 case 4: 1714 store32(earlyScratch, BaseIndex(lateScratch, property, TimesFour)); 1715 break; 1716 default: 1717 CRASH(); 1718 } 1719 1720 return slowCases; 1721} 1722 1723JIT::JumpList JIT::emitFloatTypedArrayPutByVal(Instruction* currentInstruction, PatchableJump& badType, const TypedArrayDescriptor& descriptor, size_t elementSize) 1724{ 1725 unsigned value = currentInstruction[3].u.operand; 1726 1727#if USE(JSVALUE64) 1728 RegisterID base = regT0; 1729 RegisterID property = regT1; 1730 RegisterID earlyScratch = regT3; 1731 RegisterID lateScratch = regT2; 1732#else 1733 RegisterID base = regT0; 1734 RegisterID property = regT2; 1735 RegisterID earlyScratch = regT3; 1736 RegisterID lateScratch = regT1; 1737#endif 1738 1739 JumpList slowCases; 1740 1741 loadPtr(Address(base, JSCell::structureOffset()), earlyScratch); 1742 badType = patchableBranchPtr(NotEqual, Address(earlyScratch, Structure::classInfoOffset()), TrustedImmPtr(descriptor.m_classInfo)); 1743 slowCases.append(branch32(AboveOrEqual, property, Address(base, descriptor.m_lengthOffset))); 1744 1745#if USE(JSVALUE64) 1746 emitGetVirtualRegister(value, earlyScratch); 1747 Jump doubleCase = emitJumpIfNotImmediateInteger(earlyScratch); 1748 convertInt32ToDouble(earlyScratch, fpRegT0); 1749 Jump ready = jump(); 1750 doubleCase.link(this); 1751 slowCases.append(emitJumpIfNotImmediateNumber(earlyScratch)); 1752 add64(tagTypeNumberRegister, earlyScratch); 1753 move64ToDouble(earlyScratch, fpRegT0); 1754 ready.link(this); 1755#else 1756 emitLoad(value, lateScratch, earlyScratch); 1757 Jump doubleCase = branch32(NotEqual, lateScratch, TrustedImm32(JSValue::Int32Tag)); 1758 convertInt32ToDouble(earlyScratch, fpRegT0); 1759 Jump ready = jump(); 1760 doubleCase.link(this); 1761 slowCases.append(branch32(Above, lateScratch, TrustedImm32(JSValue::LowestTag))); 1762 moveIntsToDouble(earlyScratch, lateScratch, fpRegT0, fpRegT1); 1763 ready.link(this); 1764#endif 1765 1766 // We would be loading this into base as in get_by_val, except that the slow 1767 // path expects the base to be unclobbered. 1768 loadPtr(Address(base, descriptor.m_storageOffset), lateScratch); 1769 1770 switch (elementSize) { 1771 case 4: 1772 convertDoubleToFloat(fpRegT0, fpRegT0); 1773 storeFloat(fpRegT0, BaseIndex(lateScratch, property, TimesFour)); 1774 break; 1775 case 8: 1776 storeDouble(fpRegT0, BaseIndex(lateScratch, property, TimesEight)); 1777 break; 1778 default: 1779 CRASH(); 1780 } 1781 1782 return slowCases; 1783} 1784 1785} // namespace JSC 1786 1787#endif // ENABLE(JIT) 1788