Skip to content

Commit

Permalink
Update object creation code from script
Browse files Browse the repository at this point in the history
* Use ObjectCreatePrepare code everytime
* Implement simple inline-cache object structure in ObjectCreatePrepare

Signed-off-by: Seonghyun Kim <[email protected]>
  • Loading branch information
ksh8281 committed Jan 24, 2024
1 parent 6ec62db commit ca59f64
Show file tree
Hide file tree
Showing 12 changed files with 230 additions and 132 deletions.
32 changes: 24 additions & 8 deletions src/interpreter/ByteCode.h
Original file line number Diff line number Diff line change
Expand Up @@ -963,21 +963,33 @@ class CreateObjectPrepare : public ByteCode {
};

struct CreateObjectData : public gc {
ObjectStructureItemVector m_properties;
bool m_allPrecomputed;
bool m_wasStructureComputed;
bool m_canStoreStructureOnCode;
Vector<ObjectStructureItem, GCUtil::gc_malloc_allocator<ObjectStructureItem>> m_properties;
EncodedValueVector m_values;
Object* m_target;
CreateObjectData(size_t reserveSize, Object* target)
: m_target(target)
CreateObjectPrepare* m_initCode;
CreateObjectData(bool allPrecomputed, size_t reserveSize, Object* target, bool wasStructureComputed,
CreateObjectPrepare* initCode)
: m_allPrecomputed(allPrecomputed)
, m_wasStructureComputed(wasStructureComputed)
, m_canStoreStructureOnCode(allPrecomputed)
, m_target(target)
, m_initCode(initCode)
{
m_properties.reserve(reserveSize);
if (!wasStructureComputed) {
m_properties.reserve(reserveSize);
}
m_values.reserve(reserveSize);
}
};

CreateObjectPrepare(const ByteCodeLOC& loc, const size_t dataRegisterIndex, const size_t objectIndex)
: ByteCode(Opcode::CreateObjectPrepareOpcode, loc)
, m_stage(Stage::Init)
, m_hasPreComputedKey(false)
, m_allPrecomputed(false)
, m_hasPrecomputedKey(false)
, m_needsToUpdateFunctionName(false)
, m_isGetter(false)
, m_dataRegisterIndex(dataRegisterIndex)
Expand All @@ -989,7 +1001,8 @@ class CreateObjectPrepare : public ByteCode {
CreateObjectPrepare(const ByteCodeLOC& loc, const bool hasPreComputedKey, const size_t dataRegisterIndex, const size_t keyIndex, const size_t valueIndex, const bool needsToUpdateFunctionName)
: ByteCode(Opcode::CreateObjectPrepareOpcode, loc)
, m_stage(Stage::FillKeyValue)
, m_hasPreComputedKey(hasPreComputedKey)
, m_allPrecomputed(false)
, m_hasPrecomputedKey(hasPreComputedKey)
, m_needsToUpdateFunctionName(needsToUpdateFunctionName)
, m_isGetter(false)
, m_dataRegisterIndex(dataRegisterIndex)
Expand All @@ -1001,7 +1014,8 @@ class CreateObjectPrepare : public ByteCode {
CreateObjectPrepare(const ByteCodeLOC& loc, const bool hasPreComputedKey, const bool isGetter, const size_t dataRegisterIndex, const size_t keyIndex, const size_t valueIndex)
: ByteCode(Opcode::CreateObjectPrepareOpcode, loc)
, m_stage(Stage::DefineGetterSetter)
, m_hasPreComputedKey(hasPreComputedKey)
, m_allPrecomputed(false)
, m_hasPrecomputedKey(hasPreComputedKey)
, m_needsToUpdateFunctionName(false)
, m_isGetter(isGetter)
, m_dataRegisterIndex(dataRegisterIndex)
Expand All @@ -1011,10 +1025,12 @@ class CreateObjectPrepare : public ByteCode {
}

Stage m_stage : 2;
bool m_hasPreComputedKey : 1;
bool m_allPrecomputed : 1;
bool m_hasPrecomputedKey : 1;
bool m_needsToUpdateFunctionName : 1;
bool m_isGetter : 1; // other case, this is setter
ByteCodeRegisterIndex m_dataRegisterIndex : REGISTER_INDEX_IN_BIT;
Optional<ObjectStructure*> m_cachedObjectStructure;

union {
struct {
Expand Down
4 changes: 3 additions & 1 deletion src/interpreter/ByteCodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,9 @@ void ByteCodeGenerator::relocateByteCode(ByteCodeBlock* block)
case CreateObjectPrepareOpcode: {
CreateObjectPrepare* cd = (CreateObjectPrepare*)currentCode;
ASSIGN_STACKINDEX_IF_NEEDED(cd->m_dataRegisterIndex, stackBase, stackBaseWillBe, stackVariableSize);
if (cd->m_stage == CreateObjectPrepare::FillKeyValue || cd->m_stage == CreateObjectPrepare::DefineGetterSetter) {
if (cd->m_stage == CreateObjectPrepare::Init) {
ASSIGN_STACKINDEX_IF_NEEDED(cd->m_objectIndex, stackBase, stackBaseWillBe, stackVariableSize);
} else if (cd->m_stage == CreateObjectPrepare::FillKeyValue || cd->m_stage == CreateObjectPrepare::DefineGetterSetter) {
ASSIGN_STACKINDEX_IF_NEEDED(cd->m_keyIndex, stackBase, stackBaseWillBe, stackVariableSize);
ASSIGN_STACKINDEX_IF_NEEDED(cd->m_valueIndex, stackBase, stackBaseWillBe, stackVariableSize);
}
Expand Down
89 changes: 70 additions & 19 deletions src/interpreter/ByteCodeInterpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2974,18 +2974,46 @@ NEVER_INLINE void InterpreterSlowPath::initializeGlobalVariable(ExecutionState&
NEVER_INLINE void InterpreterSlowPath::createObjectOperation(ExecutionState& state, CreateObject* code, ByteCodeBlock* byteCodeBlock, Value* registerFile)
{
if (code->m_dataRegisterIndex != REGISTER_LIMIT) {
CreateObjectPrepare::CreateObjectData* data = reinterpret_cast<CreateObjectPrepare::CreateObjectData*>(registerFile[code->m_dataRegisterIndex].payload());
CreateObjectPrepare::CreateObjectData* data;
if (byteCodeBlock->codeBlock()->isAsyncOrGenerator()) {
data = reinterpret_cast<CreateObjectPrepare::CreateObjectData*>(registerFile[code->m_dataRegisterIndex].payload());
} else {
data = reinterpret_cast<CreateObjectPrepare::CreateObjectData*>(&registerFile[code->m_dataRegisterIndex]);
}

Object* obj = registerFile[code->m_registerIndex].asObject();
ObjectStructureItemTightVector properties;
properties.reset(data->m_properties.takeBuffer(), data->m_values.size());
obj->m_structure = ObjectStructure::create(state.context(), std::move(properties));
if (!data->m_wasStructureComputed) {
size_t propertyCount = data->m_properties.size();
Optional<ObjectStructure*> cache;
if (data->m_allPrecomputed && ObjectStructure::isTransitionModeAvailable(propertyCount)) {
cache = state.context()->vmInstance()->findRootedObjectStructure(data->m_properties.data(), propertyCount);
}
if (cache) {
obj->m_structure = cache.value();
} else {
obj->m_structure = ObjectStructure::create(state.context(),
ObjectStructureItemTightVector(data->m_properties.data(), data->m_properties.data() + propertyCount),
#if defined(ESCARGOT_SMALL_CONFIG)
false);
#else
data->m_allPrecomputed);
#endif
if (data->m_canStoreStructureOnCode) {
data->m_initCode->m_cachedObjectStructure = obj->m_structure;
data->m_initCode->m_cachedObjectStructure->markReferencedByInlineCache();
byteCodeBlock->m_otherLiteralData.pushBack(obj->m_structure);
}
}
}
obj->m_values.reset(data->m_values.takeBuffer());
// reset creation area prevent leak from stack
memset(data, 0, sizeof(CreateObjectPrepare::CreateObjectData));
} else {
registerFile[code->m_registerIndex] = new Object(state);
}
#if defined(ESCARGOT_SMALL_CONFIG)
registerFile[code->m_registerIndex].asObject()->markThisObjectDontNeedStructureTransitionTable();
registerFile[code->m_registerIndex].asObject()->markThisObjectDontNeedStructureTransitionTable();
#endif
}
}

static Value createObjectPropertyFunctionName(ExecutionState& state, const Value& name, const char* prefix)
Expand All @@ -3009,15 +3037,33 @@ static Value createObjectPropertyFunctionName(ExecutionState& state, const Value
NEVER_INLINE void InterpreterSlowPath::createObjectPrepareOperation(ExecutionState& state, CreateObjectPrepare* code, ByteCodeBlock* byteCodeBlock, Value* registerFile)
{
if (code->m_stage == CreateObjectPrepare::Init) {
CreateObjectPrepare::CreateObjectData* data = new CreateObjectPrepare::CreateObjectData(code->m_propertyReserveSize, new Object(state));
registerFile[code->m_dataRegisterIndex] = Value(Value::FromPayload, reinterpret_cast<intptr_t>(data));
void* ptr;
if (byteCodeBlock->codeBlock()->isAsyncOrGenerator()) {
// async or generator uses ValueVector alloctor for allocating registerFile
// ValueVector allocator cannot track CreateObjectData if there is CreateObjectData on registerFile
ptr = GC_MALLOC(sizeof(CreateObjectPrepare::CreateObjectData));
registerFile[code->m_dataRegisterIndex] = Value(Value::FromPayload, reinterpret_cast<intptr_t>(ptr));
} else {
ptr = &registerFile[code->m_dataRegisterIndex];
}
CreateObjectPrepare::CreateObjectData* data = new (ptr) CreateObjectPrepare::CreateObjectData(
code->m_allPrecomputed, code->m_propertyReserveSize,
new Object(state), code->m_cachedObjectStructure.hasValue(), code);
registerFile[code->m_objectIndex] = data->m_target;
if (data->m_wasStructureComputed) {
data->m_target->m_structure = code->m_cachedObjectStructure.value();
}
} else {
ASSERT(code->m_stage == CreateObjectPrepare::FillKeyValue || code->m_stage == CreateObjectPrepare::DefineGetterSetter);
CreateObjectPrepare::CreateObjectData* data = reinterpret_cast<CreateObjectPrepare::CreateObjectData*>(registerFile[code->m_dataRegisterIndex].payload());
CreateObjectPrepare::CreateObjectData* data;
if (byteCodeBlock->codeBlock()->isAsyncOrGenerator()) {
data = reinterpret_cast<CreateObjectPrepare::CreateObjectData*>(registerFile[code->m_dataRegisterIndex].payload());
} else {
data = reinterpret_cast<CreateObjectPrepare::CreateObjectData*>(&registerFile[code->m_dataRegisterIndex]);
}

ObjectStructurePropertyName propertyName;
if (code->m_hasPreComputedKey) {
if (code->m_hasPrecomputedKey) {
propertyName = ObjectStructurePropertyName(AtomicString::fromPayload(registerFile[code->m_keyIndex].asString()));
// http://www.ecma-international.org/ecma-262/6.0/#sec-__proto__-property-names-in-object-initializers
if (UNLIKELY(propertyName.asAtomicString() == state.context()->staticStrings().__proto__ && code->m_stage == CreateObjectPrepare::FillKeyValue)) {
Expand All @@ -3031,30 +3077,35 @@ NEVER_INLINE void InterpreterSlowPath::createObjectPrepareOperation(ExecutionSta
propertyName = ObjectStructurePropertyName(state, registerFile[code->m_keyIndex]);
}

Value newValue = registerFile[code->m_valueIndex];
if (UNLIKELY(code->m_needsToUpdateFunctionName)) {
Value propertyStringOrSymbol(propertyName.isSymbol() ? Value(propertyName.symbol()) : Value(propertyName.toValue().toString(state)));
Value fnName = createObjectPropertyFunctionName(state, propertyStringOrSymbol, "");
newValue.asFunction()->defineOwnProperty(state, state.context()->staticStrings().name, ObjectPropertyDescriptor(fnName));
}

if (data->m_wasStructureComputed) {
data->m_values.pushBack(newValue);
return;
}

size_t lastPropertyCount = data->m_properties.size();
size_t targetIndex = lastPropertyCount;
bool updateProperty = false;
for (size_t i = 0; i < lastPropertyCount; i++) {
if (data->m_properties[i].m_propertyName == propertyName) {
targetIndex = i;
updateProperty = true;
data->m_canStoreStructureOnCode = false;
break;
}
}

ObjectStructurePropertyDescriptor newDesc;
Value newValue;
if (code->m_stage == CreateObjectPrepare::FillKeyValue) {
Value& value = registerFile[code->m_valueIndex];
if (code->m_needsToUpdateFunctionName) {
Value propertyStringOrSymbol(propertyName.isSymbol() ? Value(propertyName.symbol()) : Value(propertyName.toValue().toString(state)));
ASSERT(value.isFunction());
Value fnName = createObjectPropertyFunctionName(state, propertyStringOrSymbol, "");
value.asFunction()->defineOwnProperty(state, state.context()->staticStrings().name, ObjectPropertyDescriptor(fnName));
}
newDesc = ObjectStructurePropertyDescriptor::createDataDescriptor(ObjectStructurePropertyDescriptor::AllPresent);
newValue = value;
} else {
data->m_canStoreStructureOnCode = false;
FunctionObject* fn = registerFile[code->m_valueIndex].asFunction();
updateObjectGetterSetterFunctionName(state, fn, registerFile[code->m_keyIndex], code->m_isGetter);
int flag = ObjectStructurePropertyDescriptor::ConfigurablePresent | ObjectStructurePropertyDescriptor::EnumerablePresent;
Expand Down
5 changes: 5 additions & 0 deletions src/parser/CodeBlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,11 @@ class InterpretedCodeBlock : public CodeBlock {
return m_isAsync;
}

bool isAsyncOrGenerator() const
{
return m_isGenerator || m_isAsync;
}

// for TCO
bool isTailRecursionTarget(size_t argc, const AtomicString& calleeName) const
{
Expand Down
Loading

0 comments on commit ca59f64

Please sign in to comment.