diff --git a/src/interpreter/ByteCode.h b/src/interpreter/ByteCode.h index 0fccb0ae3..1285a33fd 100644 --- a/src/interpreter/ByteCode.h +++ b/src/interpreter/ByteCode.h @@ -64,6 +64,7 @@ struct GlobalVariableAccessCacheItem; F(BinaryUnsignedRightShift) \ F(BinaryInOperation) \ F(BinaryInstanceOfOperation) \ + F(CreateObjectPrepare) \ F(CreateObject) \ F(CreateArray) \ F(CreateSpreadArrayObject) \ @@ -953,20 +954,91 @@ class BreakpointEnabled : public ByteCode { COMPILE_ASSERT(sizeof(BreakpointDisabled) == sizeof(BreakpointEnabled), ""); #endif /* ESCARGOT_DEBUGGER */ +class CreateObjectPrepare : public ByteCode { +public: + enum Stage ENSURE_ENUM_UNSIGNED { + Init, + FillKeyValue + }; + + struct CreateObjectData : public gc { + bool m_hasSymbol; + bool m_hasNonAtomicPropertyName; + size_t m_fillIndex; + ObjectStructureItemTightVector m_properties; + ObjectPropertyValueVector m_values; + Object* m_target; + CreateObjectData(size_t propertyCount, Object* target) + : m_hasSymbol(false) + , m_hasNonAtomicPropertyName(false) + , m_fillIndex(0) + , m_target(target) + { + m_properties.resizeWithUninitializedValues(propertyCount); + m_values.resizeWithUninitializedValues(0, propertyCount); + } + }; + + CreateObjectPrepare(const ByteCodeLOC& loc, const size_t dataRegisterIndex, const size_t propertyCount, const size_t objectIndex) + : ByteCode(Opcode::CreateObjectPrepareOpcode, loc) + , m_stage(Stage::Init) + , m_hasAtomicString(false) + , m_dataRegisterIndex(dataRegisterIndex) + , m_propertyCount(propertyCount) + , m_objectIndex(objectIndex) + { + } + + CreateObjectPrepare(const ByteCodeLOC& loc, const bool hasAtomicString, const size_t dataRegisterIndex, const size_t keyIndex, const size_t valueIndex) + : ByteCode(Opcode::CreateObjectPrepareOpcode, loc) + , m_stage(Stage::FillKeyValue) + , m_hasAtomicString(hasAtomicString) + , m_dataRegisterIndex(dataRegisterIndex) + , m_keyIndex(keyIndex) + , m_valueIndex(valueIndex) + { + } + + Stage m_stage : 1; + bool m_hasAtomicString : 1; + ByteCodeRegisterIndex m_dataRegisterIndex : REGISTER_INDEX_IN_BIT; + + union { + struct { + ByteCodeRegisterIndex m_propertyCount : REGISTER_INDEX_IN_BIT; + ByteCodeRegisterIndex m_objectIndex : REGISTER_INDEX_IN_BIT; + }; + + struct { + ByteCodeRegisterIndex m_keyIndex : REGISTER_INDEX_IN_BIT; + ByteCodeRegisterIndex m_valueIndex : REGISTER_INDEX_IN_BIT; + }; + }; + +#ifndef NDEBUG + void dump() + { + printf("createobjectprepare -> r%u stage: %d", m_dataRegisterIndex, m_stage); + } +#endif +}; + class CreateObject : public ByteCode { public: - CreateObject(const ByteCodeLOC& loc, const size_t registerIndex) + CreateObject(const ByteCodeLOC& loc, const size_t registerIndex, const size_t dataRegisterIndex = SIZE_MAX) : ByteCode(Opcode::CreateObjectOpcode, loc) , m_registerIndex(registerIndex) + , m_dataRegisterIndex(dataRegisterIndex) { } ByteCodeRegisterIndex m_registerIndex; + ByteCodeRegisterIndex m_dataRegisterIndex; #ifndef NDEBUG void dump() { - printf("createobject -> r%u", m_registerIndex); + printf("createobject -> r%u r%u", m_registerIndex, m_dataRegisterIndex); } #endif }; diff --git a/src/interpreter/ByteCodeGenerator.cpp b/src/interpreter/ByteCodeGenerator.cpp index 6f7b79e7d..7d8e4e01e 100644 --- a/src/interpreter/ByteCodeGenerator.cpp +++ b/src/interpreter/ByteCodeGenerator.cpp @@ -433,6 +433,16 @@ void ByteCodeGenerator::relocateByteCode(ByteCodeBlock* block) case CreateObjectOpcode: { CreateObject* cd = (CreateObject*)currentCode; ASSIGN_STACKINDEX_IF_NEEDED(cd->m_registerIndex, stackBase, stackBaseWillBe, stackVariableSize); + ASSIGN_STACKINDEX_IF_NEEDED(cd->m_dataRegisterIndex, stackBase, stackBaseWillBe, stackVariableSize); + break; + } + case CreateObjectPrepareOpcode: { + CreateObjectPrepare* cd = (CreateObjectPrepare*)currentCode; + ASSIGN_STACKINDEX_IF_NEEDED(cd->m_dataRegisterIndex, stackBase, stackBaseWillBe, stackVariableSize); + if (cd->m_stage == CreateObjectPrepare::FillKeyValue) { + ASSIGN_STACKINDEX_IF_NEEDED(cd->m_keyIndex, stackBase, stackBaseWillBe, stackVariableSize); + ASSIGN_STACKINDEX_IF_NEEDED(cd->m_valueIndex, stackBase, stackBaseWillBe, stackVariableSize); + } break; } case CreateArrayOpcode: { diff --git a/src/interpreter/ByteCodeInterpreter.cpp b/src/interpreter/ByteCodeInterpreter.cpp index 678f31958..27dcef845 100644 --- a/src/interpreter/ByteCodeInterpreter.cpp +++ b/src/interpreter/ByteCodeInterpreter.cpp @@ -140,6 +140,9 @@ class InterpreterSlowPath { static Value tryOperation(ExecutionState*& state, size_t& programCounter, ByteCodeBlock* byteCodeBlock, Value* registerFile); + static void createObjectOperation(ExecutionState& state, CreateObject* createObject, ByteCodeBlock* byteCodeBlock, Value* registerFile); + static void createObjectPrepareOperation(ExecutionState& state, CreateObjectPrepare* createObject, ByteCodeBlock* byteCodeBlock, Value* registerFile); + static void createArrayOperation(ExecutionState& state, CreateArray* createArray, ByteCodeBlock* byteCodeBlock, Value* registerFile); static void createFunctionOperation(ExecutionState& state, CreateFunction* createFunction, ByteCodeBlock* byteCodeBlock, Value* registerFile); static ArrayObject* createRestElementOperation(ExecutionState& state, ByteCodeBlock* byteCodeBlock); static void initializeClassOperation(ExecutionState& state, InitializeClass* code, Value* registerFile); @@ -1157,19 +1160,25 @@ Value Interpreter::interpret(ExecutionState* state, ByteCodeBlock* byteCodeBlock : { CreateObject* code = (CreateObject*)programCounter; - registerFile[code->m_registerIndex] = new Object(*state); -#if defined(ESCARGOT_SMALL_CONFIG) - registerFile[code->m_registerIndex].asObject()->markThisObjectDontNeedStructureTransitionTable(); -#endif + InterpreterSlowPath::createObjectOperation(*state, code, byteCodeBlock, registerFile); ADD_PROGRAM_COUNTER(CreateObject); NEXT_INSTRUCTION(); } + DEFINE_OPCODE(CreateObjectPrepare) + : + { + CreateObjectPrepare* code = (CreateObjectPrepare*)programCounter; + InterpreterSlowPath::createObjectPrepareOperation(*state, code, byteCodeBlock, registerFile); + ADD_PROGRAM_COUNTER(CreateObjectPrepare); + NEXT_INSTRUCTION(); + } + DEFINE_OPCODE(CreateArray) : { CreateArray* code = (CreateArray*)programCounter; - registerFile[code->m_registerIndex] = new ArrayObject(*state, (uint64_t)code->m_length); + InterpreterSlowPath::createArrayOperation(*state, code, byteCodeBlock, registerFile); ADD_PROGRAM_COUNTER(CreateArray); NEXT_INSTRUCTION(); } @@ -2961,6 +2970,52 @@ NEVER_INLINE void InterpreterSlowPath::initializeGlobalVariable(ExecutionState& ASSERT_NOT_REACHED(); } +NEVER_INLINE void InterpreterSlowPath::createObjectOperation(ExecutionState& state, CreateObject* code, ByteCodeBlock* byteCodeBlock, Value* registerFile) +{ + if (code->m_dataRegisterIndex != REGISTER_LIMIT) { + CreateObjectPrepare::CreateObjectData* data = reinterpret_cast(registerFile[code->m_dataRegisterIndex].payload()); + ASSERT(data->m_fillIndex == data->m_properties.size()); + Object* obj = registerFile[code->m_registerIndex].asObject(); + obj->m_values = std::move(data->m_values); + obj->m_structure = ObjectStructure::create(state.context(), std::move(data->m_properties)); + } else { + registerFile[code->m_registerIndex] = new Object(state); + } +#if defined(ESCARGOT_SMALL_CONFIG) + registerFile[code->m_registerIndex].asObject()->markThisObjectDontNeedStructureTransitionTable(); +#endif +} + +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_propertyCount, new Object(state)); + registerFile[code->m_dataRegisterIndex] = Value(Value::FromPayload, reinterpret_cast(data)); + registerFile[code->m_objectIndex] = data->m_target; + } else { + ASSERT(code->m_stage == CreateObjectPrepare::FillKeyValue); + CreateObjectPrepare::CreateObjectData* data = reinterpret_cast(registerFile[code->m_dataRegisterIndex].payload()); + + if (code->m_hasAtomicString) { + data->m_properties[data->m_fillIndex] = ObjectStructureItem(ObjectStructurePropertyName(AtomicString::fromPayload(registerFile[code->m_keyIndex].asString())), + ObjectStructurePropertyDescriptor::createDataDescriptor(ObjectStructurePropertyDescriptor::AllPresent)); + } else { + data->m_properties[data->m_fillIndex] = ObjectStructureItem(ObjectStructurePropertyName(state, + registerFile[code->m_keyIndex]), + ObjectStructurePropertyDescriptor::createDataDescriptor(ObjectStructurePropertyDescriptor::AllPresent)); + data->m_hasNonAtomicPropertyName = true; + } + + data->m_values[data->m_fillIndex] = registerFile[code->m_valueIndex]; + data->m_fillIndex++; + } +} + +NEVER_INLINE void InterpreterSlowPath::createArrayOperation(ExecutionState& state, CreateArray* code, ByteCodeBlock* byteCodeBlock, Value* registerFile) +{ + registerFile[code->m_registerIndex] = new ArrayObject(state, (uint64_t)code->m_length); +} + NEVER_INLINE void InterpreterSlowPath::createFunctionOperation(ExecutionState& state, CreateFunction* code, ByteCodeBlock* byteCodeBlock, Value* registerFile) { InterpretedCodeBlock* cb = code->m_codeBlock; diff --git a/src/parser/ast/ObjectExpressionNode.h b/src/parser/ast/ObjectExpressionNode.h index c26d692a4..64881b2d8 100644 --- a/src/parser/ast/ObjectExpressionNode.h +++ b/src/parser/ast/ObjectExpressionNode.h @@ -41,9 +41,68 @@ class ObjectExpressionNode : public ExpressionNode { return m_properties; } + size_t generateInitValueByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, PropertyNode* p, ByteCodeRegisterIndex dstRegister) + { + size_t valueIndex = p->value()->getRegister(codeBlock, context); + const ClassContextInformation classInfoBefore = context->m_classInfo; + context->m_classInfo.m_prototypeIndex = dstRegister; + context->m_classInfo.m_constructorIndex = SIZE_MAX; + context->m_classInfo.m_superIndex = SIZE_MAX; + p->value()->generateExpressionByteCode(codeBlock, context, valueIndex); + context->m_classInfo = classInfoBefore; + return valueIndex; + } + virtual ASTNodeType type() override { return ASTNodeType::ObjectExpression; } virtual void generateExpressionByteCode(ByteCodeBlock* codeBlock, ByteCodeGenerateContext* context, ByteCodeRegisterIndex dstRegister) override { + size_t initPropertyCount = 0; + bool allSimpleCase = true; + for (SentinelNode* property = m_properties.begin(); property != m_properties.end(); property = property->next()) { + initPropertyCount++; + if (property->astNode()->isProperty()) { + PropertyNode* p = property->astNode()->asProperty(); + if (p->kind() != PropertyNode::Kind::Init) { + allSimpleCase = false; + break; + } + } else { + allSimpleCase = false; + break; + } + } + + if (allSimpleCase && initPropertyCount >= ESCARGOT_OBJECT_STRUCTURE_TRANSITION_MODE_MAX_SIZE && initPropertyCount <= REGISTER_LIMIT) { + size_t dataIndex = context->getRegister(); + codeBlock->pushCode(CreateObjectPrepare(ByteCodeLOC(m_loc.index), dataIndex, initPropertyCount, dstRegister), context, this->m_loc.index); + + size_t propertyIndex = 0; + for (SentinelNode* property = m_properties.begin(); property != m_properties.end(); propertyIndex++, property = property->next()) { + PropertyNode* p = property->astNode()->asProperty(); + ASSERT(p->kind() == PropertyNode::Kind::Init); + + bool hasKeyName = false; + size_t propertyIndex = SIZE_MAX; + if (p->key()->isIdentifier() && !p->computed()) { + hasKeyName = true; + propertyIndex = context->getRegister(); + codeBlock->pushCode(LoadLiteral(ByteCodeLOC(m_loc.index), propertyIndex, Value(p->key()->asIdentifier()->name().string())), context, this->m_loc.index); + } else { + propertyIndex = p->key()->getRegister(codeBlock, context); + p->key()->generateExpressionByteCode(codeBlock, context, propertyIndex); + } + + size_t valueIndex = generateInitValueByteCode(codeBlock, context, p, dstRegister); + codeBlock->pushCode(CreateObjectPrepare(ByteCodeLOC(m_loc.index), hasKeyName, dataIndex, propertyIndex, valueIndex), context, this->m_loc.index); + context->giveUpRegister(); + context->giveUpRegister(); + } + + codeBlock->pushCode(CreateObject(ByteCodeLOC(m_loc.index), dstRegister, dataIndex), context, this->m_loc.index); + context->giveUpRegister(); + return; + } + codeBlock->pushCode(CreateObject(ByteCodeLOC(m_loc.index), dstRegister), context, this->m_loc.index); size_t objIndex = dstRegister; for (SentinelNode* property = m_properties.begin(); property != m_properties.end(); property = property->next()) { @@ -58,13 +117,7 @@ class ObjectExpressionNode : public ExpressionNode { p->key()->generateExpressionByteCode(codeBlock, context, propertyIndex); } - size_t valueIndex = p->value()->getRegister(codeBlock, context); - const ClassContextInformation classInfoBefore = context->m_classInfo; - context->m_classInfo.m_prototypeIndex = dstRegister; - context->m_classInfo.m_constructorIndex = SIZE_MAX; - context->m_classInfo.m_superIndex = SIZE_MAX; - p->value()->generateExpressionByteCode(codeBlock, context, valueIndex); - context->m_classInfo = classInfoBefore; + size_t valueIndex = generateInitValueByteCode(codeBlock, context, p, dstRegister); if (p->kind() == PropertyNode::Kind::Init) { if (hasKeyName) { diff --git a/src/runtime/ObjectStructure.cpp b/src/runtime/ObjectStructure.cpp index a3bc5b8a9..9e5b6e48a 100644 --- a/src/runtime/ObjectStructure.cpp +++ b/src/runtime/ObjectStructure.cpp @@ -48,6 +48,33 @@ void* ObjectStructureWithoutTransition::operator new(size_t size) return GC_MALLOC_EXPLICITLY_TYPED(size, descr); } +ObjectStructure* ObjectStructure::create(Context* ctx, ObjectStructureItemTightVector&& properties) +{ + bool hasIndexStringAsPropertyName = false; + bool hasSymbol = false; + bool hasNonAtomicPropertyName = false; + bool hasEnumerableProperty = true; + for (size_t i = 0; i < properties.size(); i++) { + const ObjectStructurePropertyName& propertyName = properties[i].m_propertyName; + if (propertyName.isSymbol()) { + hasSymbol = true; + } + if (!hasIndexStringAsPropertyName) { + hasIndexStringAsPropertyName |= propertyName.isIndexString(); + } + + hasNonAtomicPropertyName |= !propertyName.hasAtomicString(); + hasEnumerableProperty |= properties[i].m_descriptor.isEnumerable(); + } + + ObjectStructure* newObjectStructure; + if (properties.size() > ESCARGOT_OBJECT_STRUCTURE_ACCESS_CACHE_BUILD_MIN_SIZE) { + return new ObjectStructureWithMap(hasIndexStringAsPropertyName, hasSymbol, hasEnumerableProperty, std::move(properties)); + } else { + return new ObjectStructureWithoutTransition(new ObjectStructureItemVector(std::move(properties)), hasIndexStringAsPropertyName, hasSymbol, hasNonAtomicPropertyName, hasEnumerableProperty); + } +} + std::pair> ObjectStructureWithoutTransition::findProperty(const ObjectStructurePropertyName& s) { if (m_properties->size() && m_lastFoundPropertyName == s) { diff --git a/src/runtime/ObjectStructure.h b/src/runtime/ObjectStructure.h index 1e2800163..56dc7a3b9 100644 --- a/src/runtime/ObjectStructure.h +++ b/src/runtime/ObjectStructure.h @@ -127,6 +127,9 @@ COMPILE_ASSERT(ESCARGOT_OBJECT_STRUCTURE_ACCESS_CACHE_BUILD_MIN_SIZE < 65536, "" class ObjectStructure : public gc { public: virtual ~ObjectStructure() {} + + static ObjectStructure* create(Context* ctx, ObjectStructureItemTightVector&& properties); + std::pair> findProperty(ExecutionState& state, String* propertyName) { ObjectStructurePropertyName name(state, propertyName); diff --git a/src/runtime/Template.cpp b/src/runtime/Template.cpp index 3006a5d9a..8965291f0 100644 --- a/src/runtime/Template.cpp +++ b/src/runtime/Template.cpp @@ -94,10 +94,6 @@ Template::CachedObjectStructure Template::constructObjectStructure(Context* ctx, structureItemVector[i] = baseItems[i]; } - bool hasIndexStringAsPropertyName = false; - bool hasSymbol = false; - bool hasNonAtomicPropertyName = false; - bool hasEnumerableProperty = false; bool isInlineNonCacheable = false; for (size_t i = baseItemCount; i < propertyCount; i++) { auto propertyIndex = i - baseItemCount; @@ -109,15 +105,8 @@ Template::CachedObjectStructure Template::constructObjectStructure(Context* ctx, } else { ASSERT(propertyNameValue.isSymbol()); propertyName = ObjectStructurePropertyName(propertyNameValue.asSymbol()); - hasSymbol = true; } - if (!hasIndexStringAsPropertyName) { - hasIndexStringAsPropertyName |= propertyName.isIndexString(); - } - - hasNonAtomicPropertyName |= !propertyName.hasAtomicString(); - auto type = m_properties[propertyIndex].second.propertyType(); ObjectStructurePropertyDescriptor desc; if (type == Template::TemplatePropertyData::PropertyType::PropertyValueData || type == Template::TemplatePropertyData::PropertyType::PropertyTemplateData) { @@ -130,20 +119,11 @@ Template::CachedObjectStructure Template::constructObjectStructure(Context* ctx, desc = ObjectStructurePropertyDescriptor::createAccessorDescriptor(m_properties[propertyIndex].second.presentAttributes()); } - hasEnumerableProperty |= desc.isEnumerable(); - structureItemVector[i] = ObjectStructureItem(propertyName, desc); } - ObjectStructure* newObjectStructure; - if (propertyCount > ESCARGOT_OBJECT_STRUCTURE_ACCESS_CACHE_BUILD_MIN_SIZE) { - newObjectStructure = new ObjectStructureWithMap(hasIndexStringAsPropertyName, hasSymbol, hasEnumerableProperty, std::move(structureItemVector)); - } else { - newObjectStructure = new ObjectStructureWithTransition(std::move(structureItemVector), hasIndexStringAsPropertyName, hasSymbol, hasNonAtomicPropertyName, hasEnumerableProperty); - } - CachedObjectStructure s; - s.m_objectStructure = newObjectStructure; + s.m_objectStructure = ObjectStructure::create(ctx, std::move(structureItemVector)); s.m_inlineCacheable = !isInlineNonCacheable; return s; } diff --git a/src/util/TightVector.h b/src/util/TightVector.h index 08290db21..191cd1d73 100644 --- a/src/util/TightVector.h +++ b/src/util/TightVector.h @@ -304,6 +304,14 @@ class TightVectorWithNoSize : public gc { TightVectorWithNoSize(const TightVectorWithNoSize& other) = delete; const TightVectorWithNoSize& operator=(const TightVectorWithNoSize& other) = delete; + void operator=(TightVectorWithNoSize&& other) + { + if (m_buffer) { + Allocator().deallocate(m_buffer); + } + m_buffer = other.m_buffer; + other.m_buffer = nullptr; + } ~TightVectorWithNoSize() { @@ -415,6 +423,14 @@ class TightVectorWithNoSizeUseGCRealloc : public gc { TightVectorWithNoSizeUseGCRealloc(const TightVectorWithNoSizeUseGCRealloc& other) = delete; const TightVectorWithNoSizeUseGCRealloc& operator=(const TightVectorWithNoSizeUseGCRealloc& other) = delete; + void operator=(TightVectorWithNoSizeUseGCRealloc&& other) + { + if (m_buffer) { + GC_FREE(m_buffer); + } + m_buffer = other.m_buffer; + other.m_buffer = nullptr; + } ~TightVectorWithNoSizeUseGCRealloc() {