summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp')
-rw-r--r--Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp901
1 files changed, 582 insertions, 319 deletions
diff --git a/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp b/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
index 5bc8d1abb..bc34b3000 100644
--- a/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
+++ b/Source/JavaScriptCore/jit/JITPropertyAccess32_64.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009, 2014, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -30,17 +30,17 @@
#include "JIT.h"
#include "CodeBlock.h"
+#include "DirectArguments.h"
#include "GCAwareJITStubRoutine.h"
#include "Interpreter.h"
#include "JITInlines.h"
#include "JSArray.h"
+#include "JSEnvironmentRecord.h"
#include "JSFunction.h"
-#include "JSPropertyNameIterator.h"
-#include "JSVariableObject.h"
#include "LinkBuffer.h"
-#include "RepatchBuffer.h"
#include "ResultType.h"
-#include "SamplingTool.h"
+#include "SlowPathCall.h"
+#include "StructureStubInfo.h"
#include <wtf/StringPrintStream.h>
@@ -57,17 +57,68 @@ void JIT::emit_op_put_by_index(Instruction* currentInstruction)
callOperation(operationPutByIndex, regT1, regT0, property, regT3, regT2);
}
-void JIT::emit_op_put_getter_setter(Instruction* currentInstruction)
+void JIT::emit_op_put_getter_by_id(Instruction* currentInstruction)
{
int base = currentInstruction[1].u.operand;
int property = currentInstruction[2].u.operand;
- int getter = currentInstruction[3].u.operand;
+ int options = currentInstruction[3].u.operand;
+ int getter = currentInstruction[4].u.operand;
+
+ emitLoadPayload(base, regT1);
+ emitLoadPayload(getter, regT3);
+ callOperation(operationPutGetterById, regT1, m_codeBlock->identifier(property).impl(), options, regT3);
+}
+
+void JIT::emit_op_put_setter_by_id(Instruction* currentInstruction)
+{
+ int base = currentInstruction[1].u.operand;
+ int property = currentInstruction[2].u.operand;
+ unsigned options = currentInstruction[3].u.operand;
int setter = currentInstruction[4].u.operand;
emitLoadPayload(base, regT1);
+ emitLoadPayload(setter, regT3);
+ callOperation(operationPutSetterById, regT1, m_codeBlock->identifier(property).impl(), options, regT3);
+}
+
+void JIT::emit_op_put_getter_setter_by_id(Instruction* currentInstruction)
+{
+ int base = currentInstruction[1].u.operand;
+ int property = currentInstruction[2].u.operand;
+ unsigned attribute = currentInstruction[3].u.operand;
+ int getter = currentInstruction[4].u.operand;
+ int setter = currentInstruction[5].u.operand;
+
+ emitLoadPayload(base, regT1);
emitLoadPayload(getter, regT3);
emitLoadPayload(setter, regT4);
- callOperation(operationPutGetterSetter, regT1, &m_codeBlock->identifier(property), regT3, regT4);
+ callOperation(operationPutGetterSetter, regT1, m_codeBlock->identifier(property).impl(), attribute, regT3, regT4);
+}
+
+void JIT::emit_op_put_getter_by_val(Instruction* currentInstruction)
+{
+ int base = currentInstruction[1].u.operand;
+ int property = currentInstruction[2].u.operand;
+ int32_t attributes = currentInstruction[3].u.operand;
+ int getter = currentInstruction[4].u.operand;
+
+ emitLoadPayload(base, regT2);
+ emitLoad(property, regT1, regT0);
+ emitLoadPayload(getter, regT3);
+ callOperation(operationPutGetterByVal, regT2, regT1, regT0, attributes, regT3);
+}
+
+void JIT::emit_op_put_setter_by_val(Instruction* currentInstruction)
+{
+ int base = currentInstruction[1].u.operand;
+ int property = currentInstruction[2].u.operand;
+ int32_t attributes = currentInstruction[3].u.operand;
+ int getter = currentInstruction[4].u.operand;
+
+ emitLoadPayload(base, regT2);
+ emitLoad(property, regT1, regT0);
+ emitLoadPayload(getter, regT3);
+ callOperation(operationPutSetterByVal, regT2, regT1, regT0, attributes, regT3);
}
void JIT::emit_op_del_by_id(Instruction* currentInstruction)
@@ -76,14 +127,23 @@ void JIT::emit_op_del_by_id(Instruction* currentInstruction)
int base = currentInstruction[2].u.operand;
int property = currentInstruction[3].u.operand;
emitLoad(base, regT1, regT0);
- callOperation(operationDeleteById, dst, regT1, regT0, &m_codeBlock->identifier(property));
+ callOperation(operationDeleteByIdJSResult, dst, regT1, regT0, m_codeBlock->identifier(property).impl());
+}
+
+void JIT::emit_op_del_by_val(Instruction* currentInstruction)
+{
+ int dst = currentInstruction[1].u.operand;
+ int base = currentInstruction[2].u.operand;
+ int property = currentInstruction[3].u.operand;
+ emitLoad2(base, regT1, regT0, property, regT3, regT2);
+ callOperation(operationDeleteByValJSResult, dst, regT1, regT0, regT3, regT2);
}
JIT::CodeRef JIT::stringGetByValStubGenerator(VM* vm)
{
JSInterfaceJIT jit(vm);
JumpList failures;
- failures.append(jit.branchPtr(NotEqual, Address(regT0, JSCell::structureOffset()), TrustedImmPtr(vm->stringStructure.get())));
+ failures.append(jit.branchStructure(NotEqual, Address(regT0, JSCell::structureIDOffset()), vm->stringStructure.get()));
// Load string length to regT1, and start the process of loading the data pointer into regT0
jit.load32(Address(regT0, ThunkHelpers::jsStringLengthOffset()), regT1);
@@ -117,7 +177,7 @@ JIT::CodeRef JIT::stringGetByValStubGenerator(VM* vm)
jit.move(TrustedImm32(0), regT0);
jit.ret();
- LinkBuffer patchBuffer(*vm, &jit, GLOBAL_THUNK_ID);
+ LinkBuffer patchBuffer(*vm, jit, GLOBAL_THUNK_ID);
return FINALIZE_CODE(patchBuffer, ("String get_by_val stub"));
}
@@ -127,13 +187,14 @@ void JIT::emit_op_get_by_val(Instruction* currentInstruction)
int base = currentInstruction[2].u.operand;
int property = currentInstruction[3].u.operand;
ArrayProfile* profile = currentInstruction[4].u.arrayProfile;
+ ByValInfo* byValInfo = m_codeBlock->addByValInfo();
emitLoad2(base, regT1, regT0, property, regT3, regT2);
- addSlowCase(branch32(NotEqual, regT3, TrustedImm32(JSValue::Int32Tag)));
emitJumpSlowCaseIfNotJSCell(base, regT1);
- loadPtr(Address(regT0, JSCell::structureOffset()), regT1);
- emitArrayProfilingSite(regT1, regT3, profile);
+ PatchableJump notIndex = patchableBranch32(NotEqual, regT3, TrustedImm32(JSValue::Int32Tag));
+ addSlowCase(notIndex);
+ emitArrayProfilingSiteWithCell(regT0, regT1, profile);
and32(TrustedImm32(IndexingShapeMask), regT1);
PatchableJump badType;
@@ -162,27 +223,27 @@ void JIT::emit_op_get_by_val(Instruction* currentInstruction)
Label done = label();
-#if !ASSERT_DISABLED
- Jump resultOK = branch32(NotEqual, regT1, TrustedImm32(JSValue::EmptyValueTag));
- breakpoint();
- resultOK.link(this);
-#endif
+ if (!ASSERT_DISABLED) {
+ Jump resultOK = branch32(NotEqual, regT1, TrustedImm32(JSValue::EmptyValueTag));
+ abortWithReason(JITGetByValResultIsNotEmpty);
+ resultOK.link(this);
+ }
emitValueProfilingSite();
emitStore(dst, regT1, regT0);
+
+ Label nextHotPath = label();
- m_byValCompilationInfo.append(ByValCompilationInfo(m_bytecodeOffset, badType, mode, done));
+ m_byValCompilationInfo.append(ByValCompilationInfo(byValInfo, m_bytecodeOffset, notIndex, badType, mode, profile, done, nextHotPath));
}
-JIT::JumpList JIT::emitContiguousGetByVal(Instruction*, PatchableJump& badType, IndexingType expectedShape)
+JIT::JumpList JIT::emitContiguousLoad(Instruction*, PatchableJump& badType, IndexingType expectedShape)
{
JumpList slowCases;
badType = patchableBranch32(NotEqual, regT1, TrustedImm32(expectedShape));
-
loadPtr(Address(regT0, JSObject::butterflyOffset()), regT3);
slowCases.append(branch32(AboveOrEqual, regT2, Address(regT3, Butterfly::offsetOfPublicLength())));
-
load32(BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag)), regT1); // tag
load32(BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload)), regT0); // payload
slowCases.append(branch32(Equal, regT1, TrustedImm32(JSValue::EmptyValueTag)));
@@ -190,52 +251,75 @@ JIT::JumpList JIT::emitContiguousGetByVal(Instruction*, PatchableJump& badType,
return slowCases;
}
-JIT::JumpList JIT::emitDoubleGetByVal(Instruction*, PatchableJump& badType)
+JIT::JumpList JIT::emitDoubleLoad(Instruction*, PatchableJump& badType)
{
JumpList slowCases;
badType = patchableBranch32(NotEqual, regT1, TrustedImm32(DoubleShape));
-
loadPtr(Address(regT0, JSObject::butterflyOffset()), regT3);
slowCases.append(branch32(AboveOrEqual, regT2, Address(regT3, Butterfly::offsetOfPublicLength())));
-
loadDouble(BaseIndex(regT3, regT2, TimesEight), fpRegT0);
slowCases.append(branchDouble(DoubleNotEqualOrUnordered, fpRegT0, fpRegT0));
- moveDoubleToInts(fpRegT0, regT0, regT1);
return slowCases;
}
-JIT::JumpList JIT::emitArrayStorageGetByVal(Instruction*, PatchableJump& badType)
+JIT::JumpList JIT::emitArrayStorageLoad(Instruction*, PatchableJump& badType)
{
JumpList slowCases;
add32(TrustedImm32(-ArrayStorageShape), regT1, regT3);
badType = patchableBranch32(Above, regT3, TrustedImm32(SlowPutArrayStorageShape - ArrayStorageShape));
-
loadPtr(Address(regT0, JSObject::butterflyOffset()), regT3);
slowCases.append(branch32(AboveOrEqual, regT2, Address(regT3, ArrayStorage::vectorLengthOffset())));
-
load32(BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), regT1); // tag
load32(BaseIndex(regT3, regT2, TimesEight, OBJECT_OFFSETOF(ArrayStorage, m_vector[0]) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), regT0); // payload
slowCases.append(branch32(Equal, regT1, TrustedImm32(JSValue::EmptyValueTag)));
return slowCases;
}
-
+
+JITGetByIdGenerator JIT::emitGetByValWithCachedId(ByValInfo* byValInfo, Instruction* currentInstruction, const Identifier& propertyName, Jump& fastDoneCase, Jump& slowDoneCase, JumpList& slowCases)
+{
+ int dst = currentInstruction[1].u.operand;
+
+ // base: tag(regT1), payload(regT0)
+ // property: tag(regT3), payload(regT2)
+ // scratch: regT4
+
+ slowCases.append(branch32(NotEqual, regT3, TrustedImm32(JSValue::CellTag)));
+ emitByValIdentifierCheck(byValInfo, regT2, regT4, propertyName, slowCases);
+
+ JITGetByIdGenerator gen(
+ m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(currentInstruction), RegisterSet::stubUnavailableRegisters(),
+ propertyName.impl(), JSValueRegs::payloadOnly(regT0), JSValueRegs(regT1, regT0), AccessType::Get);
+ gen.generateFastPath(*this);
+
+ fastDoneCase = jump();
+
+ Label coldPathBegin = label();
+ gen.slowPathJump().link(this);
+
+ Call call = callOperation(WithProfile, operationGetByIdOptimize, dst, gen.stubInfo(), regT1, regT0, propertyName.impl());
+ gen.reportSlowPathCall(coldPathBegin, call);
+ slowDoneCase = jump();
+
+ return gen;
+}
+
void JIT::emitSlow_op_get_by_val(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
int dst = currentInstruction[1].u.operand;
int base = currentInstruction[2].u.operand;
int property = currentInstruction[3].u.operand;
- ArrayProfile* profile = currentInstruction[4].u.arrayProfile;
-
- linkSlowCase(iter); // property int32 check
+ ByValInfo* byValInfo = m_byValCompilationInfo[m_byValInstructionIndex].byValInfo;
+
linkSlowCaseIfNotJSCell(iter, base); // base cell check
+ linkSlowCase(iter); // property int32 check
Jump nonCell = jump();
linkSlowCase(iter); // base array check
- Jump notString = branchPtr(NotEqual, Address(regT0, JSCell::structureOffset()), TrustedImmPtr(m_vm->stringStructure.get()));
+ Jump notString = branchStructure(NotEqual, Address(regT0, JSCell::structureIDOffset()), m_vm->stringStructure.get());
emitNakedCall(m_vm->getCTIStub(stringGetByValStubGenerator).code());
Jump failed = branchTestPtr(Zero, regT0);
emitStore(dst, regT1, regT0);
@@ -243,21 +327,15 @@ void JIT::emitSlow_op_get_by_val(Instruction* currentInstruction, Vector<SlowCas
failed.link(this);
notString.link(this);
nonCell.link(this);
-
- Jump skipProfiling = jump();
linkSlowCase(iter); // vector length check
linkSlowCase(iter); // empty value
- emitArrayProfileOutOfBoundsSpecialCase(profile);
-
- skipProfiling.link(this);
-
Label slowPath = label();
emitLoad(base, regT1, regT0);
emitLoad(property, regT3, regT2);
- Call call = callOperation(operationGetByValDefault, dst, regT1, regT0, regT3, regT2);
+ Call call = callOperation(operationGetByValOptimize, dst, regT1, regT0, regT3, regT2, byValInfo);
m_byValCompilationInfo[m_byValInstructionIndex].slowPathTarget = slowPath;
m_byValCompilationInfo[m_byValInstructionIndex].returnAddress = call;
@@ -271,13 +349,14 @@ void JIT::emit_op_put_by_val(Instruction* currentInstruction)
int base = currentInstruction[1].u.operand;
int property = currentInstruction[2].u.operand;
ArrayProfile* profile = currentInstruction[4].u.arrayProfile;
+ ByValInfo* byValInfo = m_codeBlock->addByValInfo();
emitLoad2(base, regT1, regT0, property, regT3, regT2);
- addSlowCase(branch32(NotEqual, regT3, TrustedImm32(JSValue::Int32Tag)));
emitJumpSlowCaseIfNotJSCell(base, regT1);
- loadPtr(Address(regT0, JSCell::structureOffset()), regT1);
- emitArrayProfilingSite(regT1, regT3, profile);
+ PatchableJump notIndex = patchableBranch32(NotEqual, regT3, TrustedImm32(JSValue::Int32Tag));
+ addSlowCase(notIndex);
+ emitArrayProfilingSiteWithCell(regT0, regT1, profile);
and32(TrustedImm32(IndexingShapeMask), regT1);
PatchableJump badType;
@@ -307,7 +386,7 @@ void JIT::emit_op_put_by_val(Instruction* currentInstruction)
Label done = label();
- m_byValCompilationInfo.append(ByValCompilationInfo(m_bytecodeOffset, badType, mode, done));
+ m_byValCompilationInfo.append(ByValCompilationInfo(byValInfo, m_bytecodeOffset, notIndex, badType, mode, profile, done, done));
}
JIT::JumpList JIT::emitGenericContiguousPutByVal(Instruction* currentInstruction, PatchableJump& badType, IndexingType indexingShape)
@@ -406,15 +485,52 @@ JIT::JumpList JIT::emitArrayStoragePutByVal(Instruction* currentInstruction, Pat
return slowCases;
}
+JITPutByIdGenerator JIT::emitPutByValWithCachedId(ByValInfo* byValInfo, Instruction* currentInstruction, PutKind putKind, const Identifier& propertyName, JumpList& doneCases, JumpList& slowCases)
+{
+ // base: tag(regT1), payload(regT0)
+ // property: tag(regT3), payload(regT2)
+
+ int base = currentInstruction[1].u.operand;
+ int value = currentInstruction[3].u.operand;
+
+ slowCases.append(branch32(NotEqual, regT3, TrustedImm32(JSValue::CellTag)));
+ emitByValIdentifierCheck(byValInfo, regT2, regT2, propertyName, slowCases);
+
+ // Write barrier breaks the registers. So after issuing the write barrier,
+ // reload the registers.
+ emitWriteBarrier(base, value, ShouldFilterBase);
+ emitLoadPayload(base, regT0);
+ emitLoad(value, regT3, regT2);
+
+ JITPutByIdGenerator gen(
+ m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(currentInstruction), RegisterSet::stubUnavailableRegisters(),
+ JSValueRegs::payloadOnly(regT0), JSValueRegs(regT3, regT2), regT1, m_codeBlock->ecmaMode(), putKind);
+ gen.generateFastPath(*this);
+ doneCases.append(jump());
+
+ Label coldPathBegin = label();
+ gen.slowPathJump().link(this);
+
+ // JITPutByIdGenerator only preserve the value and the base's payload, we have to reload the tag.
+ emitLoadTag(base, regT1);
+
+ Call call = callOperation(gen.slowPathFunction(), gen.stubInfo(), regT3, regT2, regT1, regT0, propertyName.impl());
+ gen.reportSlowPathCall(coldPathBegin, call);
+ doneCases.append(jump());
+
+ return gen;
+}
+
void JIT::emitSlow_op_put_by_val(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
int base = currentInstruction[1].u.operand;
int property = currentInstruction[2].u.operand;
int value = currentInstruction[3].u.operand;
ArrayProfile* profile = currentInstruction[4].u.arrayProfile;
+ ByValInfo* byValInfo = m_byValCompilationInfo[m_byValInstructionIndex].byValInfo;
- linkSlowCase(iter); // property int32 check
linkSlowCaseIfNotJSCell(iter, base); // base cell check
+ linkSlowCase(iter); // property int32 check
linkSlowCase(iter); // base not array check
JITArrayMode mode = chooseArrayMode(profile);
@@ -451,14 +567,15 @@ void JIT::emitSlow_op_put_by_val(Instruction* currentInstruction, Vector<SlowCas
emitLoad(value, regT0, regT1);
addCallArgument(regT1);
addCallArgument(regT0);
- Call call = appendCallWithExceptionCheck(isDirect ? operationDirectPutByVal : operationPutByVal);
+ addCallArgument(TrustedImmPtr(byValInfo));
+ Call call = appendCallWithExceptionCheck(isDirect ? operationDirectPutByValOptimize : operationPutByValOptimize);
#else
// The register selection below is chosen to reduce register swapping on ARM.
// Swapping shouldn't happen on other platforms.
emitLoad(base, regT2, regT1);
emitLoad(property, regT3, regT0);
emitLoad(value, regT5, regT4);
- Call call = callOperation(isDirect ? operationDirectPutByVal : operationPutByVal, regT2, regT1, regT3, regT0, regT5, regT4);
+ Call call = callOperation(isDirect ? operationDirectPutByValOptimize : operationPutByValOptimize, regT2, regT1, regT3, regT0, regT5, regT4, byValInfo);
#endif
m_byValCompilationInfo[m_byValInstructionIndex].slowPathTarget = slowPath;
@@ -466,6 +583,45 @@ void JIT::emitSlow_op_put_by_val(Instruction* currentInstruction, Vector<SlowCas
m_byValInstructionIndex++;
}
+void JIT::emit_op_try_get_by_id(Instruction* currentInstruction)
+{
+ int dst = currentInstruction[1].u.operand;
+ int base = currentInstruction[2].u.operand;
+ const Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand));
+
+ emitLoad(base, regT1, regT0);
+ emitJumpSlowCaseIfNotJSCell(base, regT1);
+
+ JITGetByIdGenerator gen(
+ m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(currentInstruction), RegisterSet::stubUnavailableRegisters(),
+ ident->impl(), JSValueRegs::payloadOnly(regT0), JSValueRegs(regT1, regT0), AccessType::TryGet);
+ gen.generateFastPath(*this);
+ addSlowCase(gen.slowPathJump());
+ m_getByIds.append(gen);
+
+ emitValueProfilingSite();
+ emitStore(dst, regT1, regT0);
+}
+
+void JIT::emitSlow_op_try_get_by_id(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+ int resultVReg = currentInstruction[1].u.operand;
+ int baseVReg = currentInstruction[2].u.operand;
+ const Identifier* ident = &(m_codeBlock->identifier(currentInstruction[3].u.operand));
+
+ linkSlowCaseIfNotJSCell(iter, baseVReg);
+ linkSlowCase(iter);
+
+ JITGetByIdGenerator& gen = m_getByIds[m_getByIdIndex++];
+
+ Label coldPathBegin = label();
+
+ Call call = callOperation(operationTryGetByIdOptimize, resultVReg, gen.stubInfo(), regT1, regT0, ident->impl());
+
+ gen.reportSlowPathCall(coldPathBegin, call);
+}
+
+
void JIT::emit_op_get_by_id(Instruction* currentInstruction)
{
int dst = currentInstruction[1].u.operand;
@@ -475,14 +631,12 @@ void JIT::emit_op_get_by_id(Instruction* currentInstruction)
emitLoad(base, regT1, regT0);
emitJumpSlowCaseIfNotJSCell(base, regT1);
- if (*ident == m_vm->propertyNames->length && shouldEmitProfiling()) {
- loadPtr(Address(regT0, JSCell::structureOffset()), regT2);
- emitArrayProfilingSiteForBytecodeIndex(regT2, regT3, m_bytecodeOffset);
- }
+ if (*ident == m_vm->propertyNames->length && shouldEmitProfiling())
+ emitArrayProfilingSiteForBytecodeIndexWithCell(regT0, regT2, m_bytecodeOffset);
JITGetByIdGenerator gen(
- m_codeBlock, CodeOrigin(m_bytecodeOffset), RegisterSet::specialRegisters(),
- callFrameRegister, JSValueRegs::payloadOnly(regT0), JSValueRegs(regT1, regT0), true);
+ m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(currentInstruction), RegisterSet::stubUnavailableRegisters(),
+ ident->impl(), JSValueRegs::payloadOnly(regT0), JSValueRegs(regT1, regT0), AccessType::Get);
gen.generateFastPath(*this);
addSlowCase(gen.slowPathJump());
m_getByIds.append(gen);
@@ -517,25 +671,22 @@ void JIT::emit_op_put_by_id(Instruction* currentInstruction)
int base = currentInstruction[1].u.operand;
int value = currentInstruction[3].u.operand;
- int direct = currentInstruction[8].u.operand;
+ int direct = currentInstruction[8].u.putByIdFlags & PutByIdIsDirect;
- emitWriteBarrier(base, value, ShouldFilterBaseAndValue);
-
emitLoad2(base, regT1, regT0, value, regT3, regT2);
emitJumpSlowCaseIfNotJSCell(base, regT1);
-
- emitLoad(base, regT1, regT0);
- emitLoad(value, regT3, regT2);
JITPutByIdGenerator gen(
- m_codeBlock, CodeOrigin(m_bytecodeOffset), RegisterSet::specialRegisters(),
- callFrameRegister, JSValueRegs::payloadOnly(regT0), JSValueRegs(regT3, regT2),
- regT1, true, m_codeBlock->ecmaMode(), direct ? Direct : NotDirect);
+ m_codeBlock, CodeOrigin(m_bytecodeOffset), CallSiteIndex(currentInstruction), RegisterSet::stubUnavailableRegisters(),
+ JSValueRegs::payloadOnly(regT0), JSValueRegs(regT3, regT2),
+ regT1, m_codeBlock->ecmaMode(), direct ? Direct : NotDirect);
gen.generateFastPath(*this);
addSlowCase(gen.slowPathJump());
+ emitWriteBarrier(base, value, ShouldFilterBase);
+
m_putByIds.append(gen);
}
@@ -548,7 +699,10 @@ void JIT::emitSlow_op_put_by_id(Instruction* currentInstruction, Vector<SlowCase
linkSlowCase(iter);
Label coldPathBegin(this);
-
+
+ // JITPutByIdGenerator only preserve the value and the base's payload, we have to reload the tag.
+ emitLoadTag(base, regT1);
+
JITPutByIdGenerator& gen = m_putByIds[m_putByIdIndex++];
Call call = callOperation(
@@ -557,114 +711,6 @@ void JIT::emitSlow_op_put_by_id(Instruction* currentInstruction, Vector<SlowCase
gen.reportSlowPathCall(coldPathBegin, call);
}
-// Compile a store into an object's property storage. May overwrite base.
-void JIT::compilePutDirectOffset(RegisterID base, RegisterID valueTag, RegisterID valuePayload, PropertyOffset cachedOffset)
-{
- if (isOutOfLineOffset(cachedOffset))
- loadPtr(Address(base, JSObject::butterflyOffset()), base);
- emitStore(indexRelativeToBase(cachedOffset), valueTag, valuePayload, base);
-}
-
-// Compile a load from an object's property storage. May overwrite base.
-void JIT::compileGetDirectOffset(RegisterID base, RegisterID resultTag, RegisterID resultPayload, PropertyOffset cachedOffset)
-{
- if (isInlineOffset(cachedOffset)) {
- emitLoad(indexRelativeToBase(cachedOffset), resultTag, resultPayload, base);
- return;
- }
-
- RegisterID temp = resultPayload;
- loadPtr(Address(base, JSObject::butterflyOffset()), temp);
- emitLoad(indexRelativeToBase(cachedOffset), resultTag, resultPayload, temp);
-}
-
-void JIT::compileGetDirectOffset(JSObject* base, RegisterID resultTag, RegisterID resultPayload, PropertyOffset cachedOffset)
-{
- if (isInlineOffset(cachedOffset)) {
- move(TrustedImmPtr(base->locationForOffset(cachedOffset)), resultTag);
- load32(Address(resultTag, OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayload);
- load32(Address(resultTag, OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTag);
- return;
- }
-
- loadPtr(base->butterflyAddress(), resultTag);
- load32(Address(resultTag, offsetInButterfly(cachedOffset) * sizeof(WriteBarrier<Unknown>) + OBJECT_OFFSETOF(JSValue, u.asBits.payload)), resultPayload);
- load32(Address(resultTag, offsetInButterfly(cachedOffset) * sizeof(WriteBarrier<Unknown>) + OBJECT_OFFSETOF(JSValue, u.asBits.tag)), resultTag);
-}
-
-void JIT::compileGetDirectOffset(RegisterID base, RegisterID resultTag, RegisterID resultPayload, RegisterID offset, FinalObjectMode finalObjectMode)
-{
- ASSERT(sizeof(JSValue) == 8);
-
- if (finalObjectMode == MayBeFinal) {
- Jump isInline = branch32(LessThan, offset, TrustedImm32(firstOutOfLineOffset));
- loadPtr(Address(base, JSObject::butterflyOffset()), base);
- neg32(offset);
- Jump done = jump();
- isInline.link(this);
- addPtr(TrustedImmPtr(JSObject::offsetOfInlineStorage() - (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue)), base);
- done.link(this);
- } else {
-#if !ASSERT_DISABLED
- Jump isOutOfLine = branch32(GreaterThanOrEqual, offset, TrustedImm32(firstOutOfLineOffset));
- breakpoint();
- isOutOfLine.link(this);
-#endif
- loadPtr(Address(base, JSObject::butterflyOffset()), base);
- neg32(offset);
- }
- load32(BaseIndex(base, offset, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload) + (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue)), resultPayload);
- load32(BaseIndex(base, offset, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag) + (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue)), resultTag);
-}
-
-void JIT::emit_op_get_by_pname(Instruction* currentInstruction)
-{
- int dst = currentInstruction[1].u.operand;
- int base = currentInstruction[2].u.operand;
- int property = currentInstruction[3].u.operand;
- unsigned expected = currentInstruction[4].u.operand;
- int iter = currentInstruction[5].u.operand;
- int i = currentInstruction[6].u.operand;
-
- emitLoad2(property, regT1, regT0, base, regT3, regT2);
- emitJumpSlowCaseIfNotJSCell(property, regT1);
- addSlowCase(branchPtr(NotEqual, regT0, payloadFor(expected)));
- // Property registers are now available as the property is known
- emitJumpSlowCaseIfNotJSCell(base, regT3);
- emitLoadPayload(iter, regT1);
-
- // Test base's structure
- loadPtr(Address(regT2, JSCell::structureOffset()), regT0);
- addSlowCase(branchPtr(NotEqual, regT0, Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_cachedStructure))));
- load32(addressFor(i), regT3);
- sub32(TrustedImm32(1), regT3);
- addSlowCase(branch32(AboveOrEqual, regT3, Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_numCacheableSlots))));
- Jump inlineProperty = branch32(Below, regT3, Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_cachedStructureInlineCapacity)));
- add32(TrustedImm32(firstOutOfLineOffset), regT3);
- sub32(Address(regT1, OBJECT_OFFSETOF(JSPropertyNameIterator, m_cachedStructureInlineCapacity)), regT3);
- inlineProperty.link(this);
- compileGetDirectOffset(regT2, regT1, regT0, regT3);
-
- emitStore(dst, regT1, regT0);
-}
-
-void JIT::emitSlow_op_get_by_pname(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
-{
- int dst = currentInstruction[1].u.operand;
- int base = currentInstruction[2].u.operand;
- int property = currentInstruction[3].u.operand;
-
- linkSlowCaseIfNotJSCell(iter, property);
- linkSlowCase(iter);
- linkSlowCaseIfNotJSCell(iter, base);
- linkSlowCase(iter);
- linkSlowCase(iter);
-
- emitLoad(base, regT1, regT0);
- emitLoad(property, regT3, regT2);
- callOperation(operationGetByValGeneric, dst, regT1, regT0, regT3, regT2);
-}
-
void JIT::emitVarInjectionCheck(bool needsVarInjectionChecks)
{
if (!needsVarInjectionChecks)
@@ -672,17 +718,11 @@ void JIT::emitVarInjectionCheck(bool needsVarInjectionChecks)
addSlowCase(branch8(Equal, AbsoluteAddress(m_codeBlock->globalObject()->varInjectionWatchpoint()->addressOfState()), TrustedImm32(IsInvalidated)));
}
-void JIT::emitResolveClosure(int dst, bool needsVarInjectionChecks, unsigned depth)
+void JIT::emitResolveClosure(int dst, int scope, bool needsVarInjectionChecks, unsigned depth)
{
emitVarInjectionCheck(needsVarInjectionChecks);
move(TrustedImm32(JSValue::CellTag), regT1);
- emitLoadPayload(JSStack::ScopeChain, regT0);
- if (m_codeBlock->needsActivation()) {
- emitLoadPayload(m_codeBlock->activationRegister().offset(), regT2);
- Jump noActivation = branchTestPtr(Zero, regT2);
- loadPtr(Address(regT2, JSScope::offsetOfNext()), regT0);
- noActivation.link(this);
- }
+ emitLoadPayload(scope, regT0);
for (unsigned i = 0; i < depth; ++i)
loadPtr(Address(regT0, JSScope::offsetOfNext()), regT0);
emitStore(dst, regT1, regT0);
@@ -691,96 +731,214 @@ void JIT::emitResolveClosure(int dst, bool needsVarInjectionChecks, unsigned dep
void JIT::emit_op_resolve_scope(Instruction* currentInstruction)
{
int dst = currentInstruction[1].u.operand;
- ResolveType resolveType = static_cast<ResolveType>(currentInstruction[3].u.operand);
- unsigned depth = currentInstruction[4].u.operand;
-
+ int scope = currentInstruction[2].u.operand;
+ ResolveType resolveType = static_cast<ResolveType>(currentInstruction[4].u.operand);
+ unsigned depth = currentInstruction[5].u.operand;
+ auto emitCode = [&] (ResolveType resolveType) {
+ switch (resolveType) {
+ case GlobalProperty:
+ case GlobalVar:
+ case GlobalLexicalVar:
+ case GlobalPropertyWithVarInjectionChecks:
+ case GlobalVarWithVarInjectionChecks:
+ case GlobalLexicalVarWithVarInjectionChecks: {
+ JSScope* constantScope = JSScope::constantScopeForCodeBlock(resolveType, m_codeBlock);
+ RELEASE_ASSERT(constantScope);
+ emitVarInjectionCheck(needsVarInjectionChecks(resolveType));
+ move(TrustedImm32(JSValue::CellTag), regT1);
+ move(TrustedImmPtr(constantScope), regT0);
+ emitStore(dst, regT1, regT0);
+ break;
+ }
+ case ClosureVar:
+ case ClosureVarWithVarInjectionChecks:
+ emitResolveClosure(dst, scope, needsVarInjectionChecks(resolveType), depth);
+ break;
+ case ModuleVar:
+ move(TrustedImm32(JSValue::CellTag), regT1);
+ move(TrustedImmPtr(currentInstruction[6].u.jsCell.get()), regT0);
+ emitStore(dst, regT1, regT0);
+ break;
+ case Dynamic:
+ addSlowCase(jump());
+ break;
+ case LocalClosureVar:
+ case UnresolvedProperty:
+ case UnresolvedPropertyWithVarInjectionChecks:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ };
switch (resolveType) {
- case GlobalProperty:
- case GlobalVar:
- case GlobalPropertyWithVarInjectionChecks:
- case GlobalVarWithVarInjectionChecks:
- emitVarInjectionCheck(needsVarInjectionChecks(resolveType));
- move(TrustedImm32(JSValue::CellTag), regT1);
- move(TrustedImmPtr(m_codeBlock->globalObject()), regT0);
- emitStore(dst, regT1, regT0);
- break;
- case ClosureVar:
- case ClosureVarWithVarInjectionChecks:
- emitResolveClosure(dst, needsVarInjectionChecks(resolveType), depth);
- break;
- case Dynamic:
+ case UnresolvedProperty:
+ case UnresolvedPropertyWithVarInjectionChecks: {
+ JumpList skipToEnd;
+ load32(&currentInstruction[4], regT0);
+
+ Jump notGlobalProperty = branch32(NotEqual, regT0, TrustedImm32(GlobalProperty));
+ emitCode(GlobalProperty);
+ skipToEnd.append(jump());
+ notGlobalProperty.link(this);
+
+ Jump notGlobalPropertyWithVarInjections = branch32(NotEqual, regT0, TrustedImm32(GlobalPropertyWithVarInjectionChecks));
+ emitCode(GlobalPropertyWithVarInjectionChecks);
+ skipToEnd.append(jump());
+ notGlobalPropertyWithVarInjections.link(this);
+
+ Jump notGlobalLexicalVar = branch32(NotEqual, regT0, TrustedImm32(GlobalLexicalVar));
+ emitCode(GlobalLexicalVar);
+ skipToEnd.append(jump());
+ notGlobalLexicalVar.link(this);
+
+ Jump notGlobalLexicalVarWithVarInjections = branch32(NotEqual, regT0, TrustedImm32(GlobalLexicalVarWithVarInjectionChecks));
+ emitCode(GlobalLexicalVarWithVarInjectionChecks);
+ skipToEnd.append(jump());
+ notGlobalLexicalVarWithVarInjections.link(this);
+
addSlowCase(jump());
+ skipToEnd.link(this);
+ break;
+ }
+
+ default:
+ emitCode(resolveType);
break;
}
}
void JIT::emitSlow_op_resolve_scope(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
- int dst = currentInstruction[1].u.operand;
- ResolveType resolveType = static_cast<ResolveType>(currentInstruction[3].u.operand);
+ ResolveType resolveType = static_cast<ResolveType>(currentInstruction[4].u.operand);
- if (resolveType == GlobalProperty || resolveType == GlobalVar || resolveType == ClosureVar)
+ if (resolveType == GlobalProperty || resolveType == GlobalVar || resolveType == ClosureVar || resolveType == GlobalLexicalVar || resolveType == ModuleVar)
return;
+ if (resolveType == UnresolvedProperty || resolveType == UnresolvedPropertyWithVarInjectionChecks) {
+ linkSlowCase(iter); // Var injections check for GlobalPropertyWithVarInjectionChecks.
+ linkSlowCase(iter); // Var injections check for GlobalLexicalVarWithVarInjectionChecks.
+ }
linkSlowCase(iter);
- int32_t indentifierIndex = currentInstruction[2].u.operand;
- callOperation(operationResolveScope, dst, indentifierIndex);
+ JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_resolve_scope);
+ slowPathCall.call();
}
void JIT::emitLoadWithStructureCheck(int scope, Structure** structureSlot)
{
emitLoad(scope, regT1, regT0);
loadPtr(structureSlot, regT2);
- addSlowCase(branchPtr(NotEqual, Address(regT0, JSCell::structureOffset()), regT2));
+ addSlowCase(branchPtr(NotEqual, Address(regT0, JSCell::structureIDOffset()), regT2));
}
-void JIT::emitGetGlobalProperty(uintptr_t* operandSlot)
+void JIT::emitGetVarFromPointer(JSValue* operand, GPRReg tag, GPRReg payload)
{
- move(regT0, regT2);
- load32(operandSlot, regT3);
- compileGetDirectOffset(regT2, regT1, regT0, regT3, KnownNotFinal);
+ uintptr_t rawAddress = bitwise_cast<uintptr_t>(operand);
+ load32(bitwise_cast<void*>(rawAddress + TagOffset), tag);
+ load32(bitwise_cast<void*>(rawAddress + PayloadOffset), payload);
}
-
-void JIT::emitGetGlobalVar(uintptr_t operand)
+void JIT::emitGetVarFromIndirectPointer(JSValue** operand, GPRReg tag, GPRReg payload)
{
- load32(reinterpret_cast<char*>(operand) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag), regT1);
- load32(reinterpret_cast<char*>(operand) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload), regT0);
+ loadPtr(operand, payload);
+ load32(Address(payload, TagOffset), tag);
+ load32(Address(payload, PayloadOffset), payload);
}
void JIT::emitGetClosureVar(int scope, uintptr_t operand)
{
emitLoad(scope, regT1, regT0);
- loadPtr(Address(regT0, JSVariableObject::offsetOfRegisters()), regT0);
- load32(Address(regT0, operand * sizeof(Register) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), regT1);
- load32(Address(regT0, operand * sizeof(Register) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), regT0);
+ load32(Address(regT0, JSEnvironmentRecord::offsetOfVariables() + operand * sizeof(Register) + TagOffset), regT1);
+ load32(Address(regT0, JSEnvironmentRecord::offsetOfVariables() + operand * sizeof(Register) + PayloadOffset), regT0);
}
void JIT::emit_op_get_from_scope(Instruction* currentInstruction)
{
int dst = currentInstruction[1].u.operand;
int scope = currentInstruction[2].u.operand;
- ResolveType resolveType = ResolveModeAndType(currentInstruction[4].u.operand).type();
+ ResolveType resolveType = GetPutInfo(currentInstruction[4].u.operand).resolveType();
Structure** structureSlot = currentInstruction[5].u.structure.slot();
uintptr_t* operandSlot = reinterpret_cast<uintptr_t*>(&currentInstruction[6].u.pointer);
+
+ auto emitCode = [&] (ResolveType resolveType, bool indirectLoadForOperand) {
+ switch (resolveType) {
+ case GlobalProperty:
+ case GlobalPropertyWithVarInjectionChecks: {
+ emitLoadWithStructureCheck(scope, structureSlot); // Structure check covers var injection.
+ GPRReg base = regT2;
+ GPRReg resultTag = regT1;
+ GPRReg resultPayload = regT0;
+ GPRReg offset = regT3;
+
+ move(regT0, base);
+ load32(operandSlot, offset);
+ if (!ASSERT_DISABLED) {
+ Jump isOutOfLine = branch32(GreaterThanOrEqual, offset, TrustedImm32(firstOutOfLineOffset));
+ abortWithReason(JITOffsetIsNotOutOfLine);
+ isOutOfLine.link(this);
+ }
+ loadPtr(Address(base, JSObject::butterflyOffset()), base);
+ neg32(offset);
+ load32(BaseIndex(base, offset, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload) + (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue)), resultPayload);
+ load32(BaseIndex(base, offset, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag) + (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue)), resultTag);
+ break;
+ }
+ case GlobalVar:
+ case GlobalVarWithVarInjectionChecks:
+ case GlobalLexicalVar:
+ case GlobalLexicalVarWithVarInjectionChecks:
+ emitVarInjectionCheck(needsVarInjectionChecks(resolveType));
+ if (indirectLoadForOperand)
+ emitGetVarFromIndirectPointer(bitwise_cast<JSValue**>(operandSlot), regT1, regT0);
+ else
+ emitGetVarFromPointer(bitwise_cast<JSValue*>(*operandSlot), regT1, regT0);
+ if (resolveType == GlobalLexicalVar || resolveType == GlobalLexicalVarWithVarInjectionChecks) // TDZ check.
+ addSlowCase(branch32(Equal, regT1, TrustedImm32(JSValue::EmptyValueTag)));
+ break;
+ case ClosureVar:
+ case ClosureVarWithVarInjectionChecks:
+ emitVarInjectionCheck(needsVarInjectionChecks(resolveType));
+ emitGetClosureVar(scope, *operandSlot);
+ break;
+ case Dynamic:
+ addSlowCase(jump());
+ break;
+ case ModuleVar:
+ case LocalClosureVar:
+ case UnresolvedProperty:
+ case UnresolvedPropertyWithVarInjectionChecks:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ };
switch (resolveType) {
- case GlobalProperty:
- case GlobalPropertyWithVarInjectionChecks:
- emitLoadWithStructureCheck(scope, structureSlot); // Structure check covers var injection.
- emitGetGlobalProperty(operandSlot);
- break;
- case GlobalVar:
- case GlobalVarWithVarInjectionChecks:
- emitVarInjectionCheck(needsVarInjectionChecks(resolveType));
- emitGetGlobalVar(*operandSlot);
- break;
- case ClosureVar:
- case ClosureVarWithVarInjectionChecks:
- emitVarInjectionCheck(needsVarInjectionChecks(resolveType));
- emitGetClosureVar(scope, *operandSlot);
- break;
- case Dynamic:
+ case UnresolvedProperty:
+ case UnresolvedPropertyWithVarInjectionChecks: {
+ JumpList skipToEnd;
+ load32(&currentInstruction[4], regT0);
+ and32(TrustedImm32(GetPutInfo::typeBits), regT0); // Load ResolveType into T0
+
+ Jump isGlobalProperty = branch32(Equal, regT0, TrustedImm32(GlobalProperty));
+ Jump notGlobalPropertyWithVarInjections = branch32(NotEqual, regT0, TrustedImm32(GlobalPropertyWithVarInjectionChecks));
+ isGlobalProperty.link(this);
+ emitCode(GlobalProperty, false);
+ skipToEnd.append(jump());
+ notGlobalPropertyWithVarInjections.link(this);
+
+ Jump notGlobalLexicalVar = branch32(NotEqual, regT0, TrustedImm32(GlobalLexicalVar));
+ emitCode(GlobalLexicalVar, true);
+ skipToEnd.append(jump());
+ notGlobalLexicalVar.link(this);
+
+ Jump notGlobalLexicalVarWithVarInjections = branch32(NotEqual, regT0, TrustedImm32(GlobalLexicalVarWithVarInjectionChecks));
+ emitCode(GlobalLexicalVarWithVarInjectionChecks, true);
+ skipToEnd.append(jump());
+ notGlobalLexicalVarWithVarInjections.link(this);
+
addSlowCase(jump());
+
+ skipToEnd.link(this);
+ break;
+ }
+
+ default:
+ emitCode(resolveType, false);
break;
}
emitValueProfilingSite();
@@ -790,141 +948,246 @@ void JIT::emit_op_get_from_scope(Instruction* currentInstruction)
void JIT::emitSlow_op_get_from_scope(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
int dst = currentInstruction[1].u.operand;
- ResolveType resolveType = ResolveModeAndType(currentInstruction[4].u.operand).type();
+ ResolveType resolveType = GetPutInfo(currentInstruction[4].u.operand).resolveType();
if (resolveType == GlobalVar || resolveType == ClosureVar)
return;
+ if (resolveType == GlobalLexicalVarWithVarInjectionChecks) // Var Injections check.
+ linkSlowCase(iter);
+
+ if (resolveType == UnresolvedProperty || resolveType == UnresolvedPropertyWithVarInjectionChecks) {
+ // GlobalProperty/GlobalPropertyWithVarInjectionChecks
+ linkSlowCase(iter); // emitLoadWithStructureCheck
+ // GlobalLexicalVar
+ linkSlowCase(iter); // TDZ check.
+ // GlobalLexicalVarWithVarInjectionChecks.
+ linkSlowCase(iter); // var injection check.
+ linkSlowCase(iter); // TDZ check.
+ }
+
linkSlowCase(iter);
callOperation(WithProfile, operationGetFromScope, dst, currentInstruction);
}
-void JIT::emitPutGlobalProperty(uintptr_t* operandSlot, int value)
-{
- emitLoad(value, regT3, regT2);
-
- loadPtr(Address(regT0, JSObject::butterflyOffset()), regT0);
- loadPtr(operandSlot, regT1);
- negPtr(regT1);
- store32(regT3, BaseIndex(regT0, regT1, TimesEight, (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)));
- store32(regT2, BaseIndex(regT0, regT1, TimesEight, (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)));
-}
-
-void JIT::emitNotifyWrite(RegisterID tag, RegisterID payload, RegisterID scratch, VariableWatchpointSet* set)
+void JIT::emitPutGlobalVariable(JSValue* operand, int value, WatchpointSet* set)
{
- if (!set || set->state() == IsInvalidated)
- return;
-
- load8(set->addressOfState(), scratch);
-
- JumpList ready;
-
- ready.append(branch32(Equal, scratch, TrustedImm32(IsInvalidated)));
-
- if (set->state() == ClearWatchpoint) {
- Jump isWatched = branch32(NotEqual, scratch, TrustedImm32(ClearWatchpoint));
-
- store32(tag, &set->addressOfInferredValue()->u.asBits.tag);
- store32(payload, &set->addressOfInferredValue()->u.asBits.payload);
- store8(TrustedImm32(IsWatched), set->addressOfState());
- ready.append(jump());
-
- isWatched.link(this);
- }
-
- Jump definitelyNotEqual = branch32(
- NotEqual, AbsoluteAddress(&set->addressOfInferredValue()->u.asBits.payload), payload);
- ready.append(branch32(
- Equal, AbsoluteAddress(&set->addressOfInferredValue()->u.asBits.tag), tag));
- definitelyNotEqual.link(this);
- addSlowCase(branchTest8(NonZero, AbsoluteAddress(set->addressOfSetIsNotEmpty())));
- store8(TrustedImm32(IsInvalidated), set->addressOfState());
- store32(
- TrustedImm32(JSValue::EmptyValueTag), &set->addressOfInferredValue()->u.asBits.tag);
- store32(TrustedImm32(0), &set->addressOfInferredValue()->u.asBits.payload);
-
- ready.link(this);
+ emitLoad(value, regT1, regT0);
+ emitNotifyWrite(set);
+ uintptr_t rawAddress = bitwise_cast<uintptr_t>(operand);
+ store32(regT1, bitwise_cast<void*>(rawAddress + TagOffset));
+ store32(regT0, bitwise_cast<void*>(rawAddress + PayloadOffset));
}
-void JIT::emitPutGlobalVar(uintptr_t operand, int value, VariableWatchpointSet* set)
+void JIT::emitPutGlobalVariableIndirect(JSValue** addressOfOperand, int value, WatchpointSet** indirectWatchpointSet)
{
emitLoad(value, regT1, regT0);
- emitNotifyWrite(regT1, regT0, regT2, set);
- store32(regT1, reinterpret_cast<char*>(operand) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag));
- store32(regT0, reinterpret_cast<char*>(operand) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload));
+ loadPtr(indirectWatchpointSet, regT2);
+ emitNotifyWrite(regT2);
+ loadPtr(addressOfOperand, regT2);
+ store32(regT1, Address(regT2, TagOffset));
+ store32(regT0, Address(regT2, PayloadOffset));
}
-void JIT::emitPutClosureVar(int scope, uintptr_t operand, int value)
+void JIT::emitPutClosureVar(int scope, uintptr_t operand, int value, WatchpointSet* set)
{
emitLoad(value, regT3, regT2);
emitLoad(scope, regT1, regT0);
- loadPtr(Address(regT0, JSVariableObject::offsetOfRegisters()), regT0);
- store32(regT3, Address(regT0, operand * sizeof(Register) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)));
- store32(regT2, Address(regT0, operand * sizeof(Register) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)));
+ emitNotifyWrite(set);
+ store32(regT3, Address(regT0, JSEnvironmentRecord::offsetOfVariables() + operand * sizeof(Register) + TagOffset));
+ store32(regT2, Address(regT0, JSEnvironmentRecord::offsetOfVariables() + operand * sizeof(Register) + PayloadOffset));
}
void JIT::emit_op_put_to_scope(Instruction* currentInstruction)
{
int scope = currentInstruction[1].u.operand;
int value = currentInstruction[3].u.operand;
- ResolveType resolveType = ResolveModeAndType(currentInstruction[4].u.operand).type();
+ GetPutInfo getPutInfo = GetPutInfo(currentInstruction[4].u.operand);
+ ResolveType resolveType = getPutInfo.resolveType();
Structure** structureSlot = currentInstruction[5].u.structure.slot();
uintptr_t* operandSlot = reinterpret_cast<uintptr_t*>(&currentInstruction[6].u.pointer);
+
+ auto emitCode = [&] (ResolveType resolveType, bool indirectLoadForOperand) {
+ switch (resolveType) {
+ case GlobalProperty:
+ case GlobalPropertyWithVarInjectionChecks: {
+ emitWriteBarrier(m_codeBlock->globalObject(), value, ShouldFilterValue);
+ emitLoadWithStructureCheck(scope, structureSlot); // Structure check covers var injection.
+ emitLoad(value, regT3, regT2);
+
+ loadPtr(Address(regT0, JSObject::butterflyOffset()), regT0);
+ loadPtr(operandSlot, regT1);
+ negPtr(regT1);
+ store32(regT3, BaseIndex(regT0, regT1, TimesEight, (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)));
+ store32(regT2, BaseIndex(regT0, regT1, TimesEight, (firstOutOfLineOffset - 2) * sizeof(EncodedJSValue) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)));
+ break;
+ }
+ case GlobalVar:
+ case GlobalVarWithVarInjectionChecks:
+ case GlobalLexicalVar:
+ case GlobalLexicalVarWithVarInjectionChecks: {
+ JSScope* constantScope = JSScope::constantScopeForCodeBlock(resolveType, m_codeBlock);
+ RELEASE_ASSERT(constantScope);
+ emitWriteBarrier(constantScope, value, ShouldFilterValue);
+ emitVarInjectionCheck(needsVarInjectionChecks(resolveType));
+ if (!isInitialization(getPutInfo.initializationMode()) && (resolveType == GlobalLexicalVar || resolveType == GlobalLexicalVarWithVarInjectionChecks)) {
+ // We need to do a TDZ check here because we can't always prove we need to emit TDZ checks statically.
+ if (indirectLoadForOperand)
+ emitGetVarFromIndirectPointer(bitwise_cast<JSValue**>(operandSlot), regT1, regT0);
+ else
+ emitGetVarFromPointer(bitwise_cast<JSValue*>(*operandSlot), regT1, regT0);
+ addSlowCase(branch32(Equal, regT1, TrustedImm32(JSValue::EmptyValueTag)));
+ }
+ if (indirectLoadForOperand)
+ emitPutGlobalVariableIndirect(bitwise_cast<JSValue**>(operandSlot), value, bitwise_cast<WatchpointSet**>(&currentInstruction[5]));
+ else
+ emitPutGlobalVariable(bitwise_cast<JSValue*>(*operandSlot), value, currentInstruction[5].u.watchpointSet);
+ break;
+ }
+ case LocalClosureVar:
+ case ClosureVar:
+ case ClosureVarWithVarInjectionChecks:
+ emitWriteBarrier(scope, value, ShouldFilterValue);
+ emitVarInjectionCheck(needsVarInjectionChecks(resolveType));
+ emitPutClosureVar(scope, *operandSlot, value, currentInstruction[5].u.watchpointSet);
+ break;
+ case ModuleVar:
+ case Dynamic:
+ addSlowCase(jump());
+ break;
+ case UnresolvedProperty:
+ case UnresolvedPropertyWithVarInjectionChecks:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ };
switch (resolveType) {
- case GlobalProperty:
- case GlobalPropertyWithVarInjectionChecks:
- emitWriteBarrier(m_codeBlock->globalObject(), value, ShouldFilterValue);
- emitLoadWithStructureCheck(scope, structureSlot); // Structure check covers var injection.
- emitPutGlobalProperty(operandSlot, value);
- break;
- case GlobalVar:
- case GlobalVarWithVarInjectionChecks:
- emitWriteBarrier(m_codeBlock->globalObject(), value, ShouldFilterValue);
- emitVarInjectionCheck(needsVarInjectionChecks(resolveType));
- emitPutGlobalVar(*operandSlot, value, currentInstruction[5].u.watchpointSet);
- break;
- case ClosureVar:
- case ClosureVarWithVarInjectionChecks:
- emitWriteBarrier(scope, value, ShouldFilterValue);
- emitVarInjectionCheck(needsVarInjectionChecks(resolveType));
- emitPutClosureVar(scope, *operandSlot, value);
- break;
- case Dynamic:
+ case UnresolvedProperty:
+ case UnresolvedPropertyWithVarInjectionChecks: {
+ JumpList skipToEnd;
+ load32(&currentInstruction[4], regT0);
+ and32(TrustedImm32(GetPutInfo::typeBits), regT0); // Load ResolveType into T0
+
+ Jump isGlobalProperty = branch32(Equal, regT0, TrustedImm32(GlobalProperty));
+ Jump notGlobalPropertyWithVarInjections = branch32(NotEqual, regT0, TrustedImm32(GlobalPropertyWithVarInjectionChecks));
+ isGlobalProperty.link(this);
+ emitCode(GlobalProperty, false);
+ skipToEnd.append(jump());
+ notGlobalPropertyWithVarInjections.link(this);
+
+ Jump notGlobalLexicalVar = branch32(NotEqual, regT0, TrustedImm32(GlobalLexicalVar));
+ emitCode(GlobalLexicalVar, true);
+ skipToEnd.append(jump());
+ notGlobalLexicalVar.link(this);
+
+ Jump notGlobalLexicalVarWithVarInjections = branch32(NotEqual, regT0, TrustedImm32(GlobalLexicalVarWithVarInjectionChecks));
+ emitCode(GlobalLexicalVarWithVarInjectionChecks, true);
+ skipToEnd.append(jump());
+ notGlobalLexicalVarWithVarInjections.link(this);
+
addSlowCase(jump());
+
+ skipToEnd.link(this);
+ break;
+ }
+
+ default:
+ emitCode(resolveType, false);
break;
}
}
void JIT::emitSlow_op_put_to_scope(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
{
- ResolveType resolveType = ResolveModeAndType(currentInstruction[4].u.operand).type();
+ GetPutInfo getPutInfo = GetPutInfo(currentInstruction[4].u.operand);
+ ResolveType resolveType = getPutInfo.resolveType();
unsigned linkCount = 0;
- if (resolveType != GlobalVar && resolveType != ClosureVar)
+ if (resolveType != GlobalVar && resolveType != ClosureVar && resolveType != LocalClosureVar && resolveType != GlobalLexicalVar)
linkCount++;
- if ((resolveType == GlobalVar || resolveType == GlobalVarWithVarInjectionChecks)
- && currentInstruction[5].u.watchpointSet->state() != IsInvalidated)
+ if (resolveType == GlobalVar || resolveType == GlobalVarWithVarInjectionChecks
+ || resolveType == GlobalLexicalVar || resolveType == GlobalLexicalVarWithVarInjectionChecks
+ || resolveType == ClosureVar || resolveType == ClosureVarWithVarInjectionChecks
+ || resolveType == LocalClosureVar)
linkCount++;
+ if (!isInitialization(getPutInfo.initializationMode()) && (resolveType == GlobalLexicalVar || resolveType == GlobalLexicalVarWithVarInjectionChecks)) // TDZ check.
+ linkCount++;
+ if (resolveType == UnresolvedProperty || resolveType == UnresolvedPropertyWithVarInjectionChecks) {
+ // GlobalProperty/GlobalPropertyWithVarInjectionsCheck
+ linkCount++; // emitLoadWithStructureCheck
+
+ // GlobalLexicalVar
+ bool needsTDZCheck = !isInitialization(getPutInfo.initializationMode());
+ if (needsTDZCheck)
+ linkCount++;
+ linkCount++; // Notify write check.
+
+ // GlobalLexicalVarWithVarInjectionsCheck
+ linkCount++; // var injection check.
+ if (needsTDZCheck)
+ linkCount++;
+ linkCount++; // Notify write check.
+ }
if (!linkCount)
return;
while (linkCount--)
linkSlowCase(iter);
- callOperation(operationPutToScope, currentInstruction);
+
+ if (resolveType == ModuleVar) {
+ JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_throw_strict_mode_readonly_property_write_error);
+ slowPathCall.call();
+ } else
+ callOperation(operationPutToScope, currentInstruction);
+}
+
+void JIT::emit_op_get_from_arguments(Instruction* currentInstruction)
+{
+ int dst = currentInstruction[1].u.operand;
+ int arguments = currentInstruction[2].u.operand;
+ int index = currentInstruction[3].u.operand;
+
+ emitLoadPayload(arguments, regT0);
+ load32(Address(regT0, DirectArguments::storageOffset() + index * sizeof(WriteBarrier<Unknown>) + TagOffset), regT1);
+ load32(Address(regT0, DirectArguments::storageOffset() + index * sizeof(WriteBarrier<Unknown>) + PayloadOffset), regT0);
+ emitValueProfilingSite();
+ emitStore(dst, regT1, regT0);
+}
+
+void JIT::emit_op_put_to_arguments(Instruction* currentInstruction)
+{
+ int arguments = currentInstruction[1].u.operand;
+ int index = currentInstruction[2].u.operand;
+ int value = currentInstruction[3].u.operand;
+
+ emitWriteBarrier(arguments, value, ShouldFilterValue);
+
+ emitLoadPayload(arguments, regT0);
+ emitLoad(value, regT1, regT2);
+ store32(regT1, Address(regT0, DirectArguments::storageOffset() + index * sizeof(WriteBarrier<Unknown>) + TagOffset));
+ store32(regT2, Address(regT0, DirectArguments::storageOffset() + index * sizeof(WriteBarrier<Unknown>) + PayloadOffset));
}
-void JIT::emit_op_init_global_const(Instruction* currentInstruction)
+void JIT::emit_op_get_by_id_with_this(Instruction* currentInstruction)
{
- WriteBarrier<Unknown>* registerPointer = currentInstruction[1].u.registerPointer;
- int value = currentInstruction[2].u.operand;
+ JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_get_by_id_with_this);
+ slowPathCall.call();
+}
- JSGlobalObject* globalObject = m_codeBlock->globalObject();
+void JIT::emit_op_get_by_val_with_this(Instruction* currentInstruction)
+{
+ JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_get_by_val_with_this);
+ slowPathCall.call();
+}
- emitWriteBarrier(globalObject, value, ShouldFilterValue);
+void JIT::emit_op_put_by_id_with_this(Instruction* currentInstruction)
+{
+ JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_put_by_id_with_this);
+ slowPathCall.call();
+}
- emitLoad(value, regT1, regT0);
-
- store32(regT1, registerPointer->tagPointer());
- store32(regT0, registerPointer->payloadPointer());
+void JIT::emit_op_put_by_val_with_this(Instruction* currentInstruction)
+{
+ JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_put_by_val_with_this);
+ slowPathCall.call();
}
} // namespace JSC