diff options
| -rw-r--r-- | source/slang/slang-ast-builder.cpp | 14 | ||||
| -rw-r--r-- | source/slang/slang-ast-builder.h | 2 | ||||
| -rw-r--r-- | source/slang/slang-ast-reflect.cpp | 2 | ||||
| -rw-r--r-- | source/slang/slang-ast-serialize.cpp | 511 | ||||
| -rw-r--r-- | source/slang/slang-ast-serialize.h | 140 | ||||
| -rw-r--r-- | source/slang/slang-ast-support-types.h | 2 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 14 |
7 files changed, 631 insertions, 54 deletions
diff --git a/source/slang/slang-ast-builder.cpp b/source/slang/slang-ast-builder.cpp index 3ff6130bd..ec4fd20f1 100644 --- a/source/slang/slang-ast-builder.cpp +++ b/source/slang/slang-ast-builder.cpp @@ -167,6 +167,20 @@ ASTBuilder::~ASTBuilder() } } +NodeBase* ASTBuilder::createByNodeType(ASTNodeType nodeType) +{ + const ReflectClassInfo* info = ReflectClassInfo::getInfo(nodeType); + + auto createFunc = info->m_createFunc; + SLANG_ASSERT(createFunc); + if (!createFunc) + { + return nullptr; + } + + return (NodeBase*)createFunc(this); +} + PtrType* ASTBuilder::getPtrType(Type* valueType) { return dynamicCast<PtrType>(getPtrType(valueType, "PtrType")); diff --git a/source/slang/slang-ast-builder.h b/source/slang/slang-ast-builder.h index 5bf7441f4..2cc03054b 100644 --- a/source/slang/slang-ast-builder.h +++ b/source/slang/slang-ast-builder.h @@ -103,6 +103,8 @@ public: template<typename T, typename P0, typename P1> T* create(const P0& p0, const P1& p1) { return _initAndAdd(new (m_arena.allocate(sizeof(T))) T(p0, p1));} + NodeBase* createByNodeType(ASTNodeType nodeType); + /// Get the built in types SLANG_FORCE_INLINE Type* getBoolType() { return m_sharedASTBuilder->m_builtinTypes[Index(BaseType::Bool)]; } SLANG_FORCE_INLINE Type* getHalfType() { return m_sharedASTBuilder->m_builtinTypes[Index(BaseType::Half)]; } diff --git a/source/slang/slang-ast-reflect.cpp b/source/slang/slang-ast-reflect.cpp index 7d4fd0c1a..7ed5057b9 100644 --- a/source/slang/slang-ast-reflect.cpp +++ b/source/slang/slang-ast-reflect.cpp @@ -72,7 +72,7 @@ struct ASTConstructAccess #define SLANG_GET_DESTROY_FUNC_NONE(NAME) &ASTConstructAccess::Impl<NAME>::destroy #define SLANG_REFLECT_CLASS_INFO(NAME, SUPER, ORIGIN, LAST, MARKER, TYPE, param) \ - /* static */const ReflectClassInfo NAME::kReflectClassInfo = { uint32_t(ASTNodeType::NAME), uint32_t(ASTNodeType::LAST), SLANG_GET_SUPER_##TYPE(SUPER), #NAME, SLANG_GET_CREATE_FUNC_##MARKER(NAME), SLANG_GET_DESTROY_FUNC_##MARKER(NAME) }; + /* static */const ReflectClassInfo NAME::kReflectClassInfo = { uint32_t(ASTNodeType::NAME), uint32_t(ASTNodeType::LAST), SLANG_GET_SUPER_##TYPE(SUPER), #NAME, SLANG_GET_CREATE_FUNC_##MARKER(NAME), SLANG_GET_DESTROY_FUNC_##MARKER(NAME), uint32_t(sizeof(NAME)), uint8_t(SLANG_ALIGN_OF(NAME)) }; SLANG_ALL_ASTNode_NodeBase(SLANG_REFLECT_CLASS_INFO, _) diff --git a/source/slang/slang-ast-serialize.cpp b/source/slang/slang-ast-serialize.cpp index 411e7a7ad..f8364e777 100644 --- a/source/slang/slang-ast-serialize.cpp +++ b/source/slang/slang-ast-serialize.cpp @@ -387,8 +387,8 @@ struct ASTSerialTypeInfo<SyntaxClass<T>> static void toNative(ASTSerialReader* reader, const void* serial, void* native) { SLANG_UNUSED(reader); - auto& src = *(const SerialType*)native; - auto& dst = *(NativeType*)serial; + auto& src = *(const SerialType*)serial; + auto& dst = *(NativeType*)native; dst.classInfo = ReflectClassInfo::getInfo(ASTNodeType(src)); } }; @@ -428,17 +428,17 @@ struct ASTSerialTypeInfo<QualType> }; enum { SerialAlignment = SLANG_ALIGN_OF(ASTSerialIndex) }; - static void toSerial(ASTSerialWriter* writer, const void* inNative, void* outSerial) + static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) { - auto dst = (SerialType*)outSerial; - auto src = (const NativeType*)inNative; + auto dst = (SerialType*)serial; + auto src = (const NativeType*)native; dst->isLeftValue = src->isLeftValue ? 1 : 0; dst->type = writer->addPointer(src->type); } - static void toNative(ASTSerialReader* reader, const void* inSerial, void* outNative) + static void toNative(ASTSerialReader* reader, const void* serial, void* native) { - auto src = (const SerialType*)inSerial; - auto dst = (NativeType*)outNative; + auto src = (const SerialType*)serial; + auto dst = (NativeType*)native; dst->type = reader->getPointer(src->type).dynamicCast<Type>(); dst->isLeftValue = src->isLeftValue != 0; } @@ -968,33 +968,33 @@ ASTSerialClasses::ASTSerialClasses(): } // Okay, go through fields setting their offset - ASTSerialField* field = serialClass.fields; + ASTSerialField* fields = serialClass.fields; for (Index j = 0; j < serialClass.fieldsCount; j++) { - size_t alignment = field->type->serialAlignment; + ASTSerialField& field = fields[j]; + + size_t alignment = field.type->serialAlignment; // Make sure the offset is aligned for the field requirement offset = (offset + alignment - 1) & ~(alignment - 1); // Save the field offset - field->serialOffset = uint32_t(offset); + field.serialOffset = uint32_t(offset); // Move past the field - offset += field->type->serialSizeInBytes; + offset += field.type->serialSizeInBytes; // Calc the maximum alignment maxAlignment = (alignment > maxAlignment) ? alignment : maxAlignment; } // Align with maximum alignment - offset += (offset + maxAlignment - 1) & ~(maxAlignment - 1); + offset = (offset + maxAlignment - 1) & ~(maxAlignment - 1); serialClass.alignment = uint8_t(maxAlignment); serialClass.size = uint32_t(offset); } } - - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ASTSerialWriter !!!!!!!!!!!!!!!!!!!!!!!!!!!! ASTSerialWriter::ASTSerialWriter(ASTSerialClasses* classes) : @@ -1024,11 +1024,11 @@ ASTSerialIndex ASTSerialWriter::addPointer(const NodeBase* node) typedef ASTSerialInfo::NodeEntry NodeEntry; - NodeEntry* nodeEntry = (NodeEntry*)m_arena.allocateAligned(sizeof(NodeEntry) + serialClass->size, 8); + NodeEntry* nodeEntry = (NodeEntry*)m_arena.allocateAligned(sizeof(NodeEntry) + serialClass->size, ASTSerialInfo::MAX_ALIGNMENT); nodeEntry->type = ASTSerialInfo::Type::Node; nodeEntry->astNodeType = uint16_t(node->astNodeType); - nodeEntry->nextAlignment = 0; + nodeEntry->info = ASTSerialInfo::makeEntryInfo(serialClass->alignment); auto index = _add(node, nodeEntry); @@ -1080,15 +1080,19 @@ ASTSerialIndex ASTSerialWriter::addPointer(const RefObject* obj) typedef ASTSerialTypeInfo<LookupResultItem::Breadcrumb> TypeInfo; typedef ASTSerialInfo::RefObjectEntry RefObjectEntry; - RefObjectEntry* refEntry = (RefObjectEntry*)m_arena.allocateAligned(sizeof(RefObjectEntry) + sizeof(TypeInfo::SerialType), 8); + size_t alignment = TypeInfo::SerialAlignment; + alignment = (alignment < SLANG_ALIGN_OF(ASTSerialInfo::RefObjectEntry)) ? SLANG_ALIGN_OF(ASTSerialInfo::RefObjectEntry) : alignment; - refEntry->type = ASTSerialInfo::Type::RefObject; - refEntry->subType = RefObjectEntry::SubType::Breadcrumb; + RefObjectEntry* entry = (RefObjectEntry*)m_arena.allocateAligned(sizeof(RefObjectEntry) + sizeof(TypeInfo::SerialType), alignment); - auto index = _add(breadcrumb, refEntry); + entry->type = ASTSerialInfo::Type::RefObject; + entry->info = ASTSerialInfo::makeEntryInfo(int(alignment)); + entry->subType = RefObjectEntry::SubType::Breadcrumb; + + auto index = _add(breadcrumb, entry); // Do any conversion - TypeInfo::toSerial(this, breadcrumb, refEntry + 1); + TypeInfo::toSerial(this, breadcrumb, entry + 1); return index; } else if (auto name = dynamicCast<const Name>(obj)) @@ -1133,11 +1137,11 @@ ASTSerialIndex ASTSerialWriter::addString(const UnownedStringSlice& slice) uint8_t encodeBuf[Util::kMaxLiteEncodeUInt32]; const int encodeCount = Util::encodeLiteUInt32(uint32_t(slice.getLength()), encodeBuf); - StringEntry* entry = (StringEntry*)m_arena.allocateUnaligned(sizeof(StringEntry) + encodeCount + slice.getLength()); - entry->nextAlignment = 0; + StringEntry* entry = (StringEntry*)m_arena.allocateUnaligned(SLANG_OFFSET_OF(StringEntry, sizeAndChars) + encodeCount + slice.getLength()); + entry->info = ASTSerialInfo::EntryInfo::Alignment1; entry->type = ASTSerialInfo::Type::String; - uint8_t* dst = (uint8_t*)(entry + 1); + uint8_t* dst = (uint8_t*)(entry->sizeAndChars); for (int i = 0; i < encodeCount; ++i) { dst[i] = encodeBuf[i]; @@ -1180,7 +1184,7 @@ ASTSerialSourceLoc ASTSerialWriter::addSourceLoc(SourceLoc sourceLoc) return 0; } -ASTSerialIndex ASTSerialWriter::_addArray(size_t elementSize, const void* elements, Index elementCount) +ASTSerialIndex ASTSerialWriter::_addArray(size_t elementSize, size_t alignment, const void* elements, Index elementCount) { typedef ASTSerialInfo::ArrayEntry Entry; @@ -1189,39 +1193,261 @@ ASTSerialIndex ASTSerialWriter::_addArray(size_t elementSize, const void* elemen return ASTSerialIndex(0); } + SLANG_ASSERT(alignment >= 1 && alignment <= ASTSerialInfo::MAX_ALIGNMENT); + + // We must at a minimum have the alignment for the array prefix info + alignment = (alignment < SLANG_ALIGN_OF(Entry)) ? SLANG_ALIGN_OF(Entry) : alignment; + size_t payloadSize = elementCount * elementSize; - Entry* entry = (Entry*)m_arena.allocateAligned(sizeof(Entry) + payloadSize, 8); + Entry* entry = (Entry*)m_arena.allocateAligned(sizeof(Entry) + payloadSize, alignment); entry->type = ASTSerialInfo::Type::Array; - entry->nextAlignment = 0; + entry->info = ASTSerialInfo::makeEntryInfo(int(alignment)); entry->elementSize = uint16_t(elementSize); entry->elementCount = uint32_t(elementCount); memcpy(entry + 1, elements, payloadSize); m_entries.add(entry); - ASTSerialIndex index = ASTSerialIndex(m_entries.getCount() - 1); + return ASTSerialIndex(m_entries.getCount() - 1); +} - // We don't add to a pointer map, because arrays are not shared +SlangResult ASTSerialWriter::write(Stream* stream) +{ + const Int entriesCount = m_entries.getCount(); - // Do the conversion + // Add a sentinal so we don't need special handling for + ASTSerialInfo::Entry sentinal; + sentinal.type = ASTSerialInfo::Type::String; + sentinal.info = ASTSerialInfo::EntryInfo::Alignment1; - return index; + m_entries.add(&sentinal); + m_entries.removeLast(); + + ASTSerialInfo::Entry** entries = m_entries.getBuffer(); + // Note strictly required in our impl of List. But by writing this and + // knowing that removeLast cannot release memory, means the sentinal must be at the last position. + entries[entriesCount] = &sentinal; + + + static const uint8_t fixBuffer[ASTSerialInfo::MAX_ALIGNMENT] { 0, }; + + { + size_t offset = 0; + + ASTSerialInfo::Entry* entry = entries[1]; + // We start on 1, because 0 is nullptr and not used for anything + for (Index i = 1; i < entriesCount; ++i) + { + ASTSerialInfo::Entry* next = entries[i + 1]; + // Before writing we need to store the next alignment + + const size_t nextAlignment = ASTSerialInfo::getAlignment(next->info); + const size_t alignment = ASTSerialInfo::getAlignment(entry->info); + + entry->info = ASTSerialInfo::combineWithNext(entry->info, next->info); + + // Check we are aligned correctly + SLANG_ASSERT((offset & (alignment - 1)) == 0); + + // When we write, we need to make sure it take into account the next alignment + const size_t entrySize = entry->calcSize(m_classes); + + // Work out the fix for next alignment + size_t nextOffset = offset + entrySize; + nextOffset = (nextOffset + nextAlignment - 1) & ~(nextAlignment - 1); + + size_t alignmentFixSize = nextOffset - (offset + entrySize); + + // The fix must be less than max alignment. We require it to be less because we aligned each Entry to + // MAX_ALIGNMENT, and so < MAX_ALIGNMENT is the most extra bytes we can write + SLANG_ASSERT( alignmentFixSize < ASTSerialInfo::MAX_ALIGNMENT); + + try + { + stream->write(entry, entrySize); + // If we needed to fix so that subsequent alignment is right, write out extra bytes here + if (alignmentFixSize) + { + stream->write(fixBuffer, alignmentFixSize); + } + } + catch (const IOException&) + { + return SLANG_FAIL; + } + + // Onto next + offset = nextOffset; + entry = next; + } + } + + return SLANG_OK; +} + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ASTSerialInfo::Entry !!!!!!!!!!!!!!!!!!!!!!!! + +size_t ASTSerialInfo::Entry::calcSize(ASTSerialClasses* serialClasses) const +{ + switch (type) + { + case Type::String: + { + auto entry = static_cast<const StringEntry*>(this); + const uint8_t* cur = (const uint8_t*)entry->sizeAndChars; + uint32_t charsSize; + int sizeSize = ByteEncodeUtil::decodeLiteUInt32(cur, &charsSize); + return SLANG_OFFSET_OF(StringEntry, sizeAndChars) + sizeSize + charsSize; + } + case Type::Node: + { + auto entry = static_cast<const NodeEntry*>(this); + auto serialClass = serialClasses->getSerialClass(ASTNodeType(entry->astNodeType)); + + // Align by the alignment of the entry + size_t alignment = getAlignment(entry->info); + size_t size = sizeof(NodeEntry) + serialClass->size; + + size = size + (alignment - 1) & ~(alignment - 1); + return size; + } + case Type::RefObject: + { + auto entry = static_cast<const RefObjectEntry*>(this); + + size_t payloadSize; + switch (entry->subType) + { + case RefObjectEntry::SubType::Breadcrumb: + { + payloadSize = sizeof(ASTSerialTypeInfo<LookupResultItem::Breadcrumb>::SerialType); + break; + } + default: + { + SLANG_ASSERT(!"Unknown type"); + return 0; + } + } + + return sizeof(RefObjectEntry) + payloadSize; + } + case Type::Array: + { + auto entry = static_cast<const ArrayEntry*>(this); + return sizeof(ArrayEntry) + entry->elementSize * entry->elementCount; + } + default: break; + } + + SLANG_ASSERT(!"Unknown type"); + return 0; } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ASTSerialReader !!!!!!!!!!!!!!!!!!!!!!!!!!!! +const void* ASTSerialReader::getArray(ASTSerialIndex index, Index& outCount) +{ + if (index == ASTSerialIndex(0)) + { + outCount = 0; + return nullptr; + } + + SLANG_ASSERT(ASTSerialIndexRaw(index) < ASTSerialIndexRaw(m_entries.getCount())); + const Entry* entry = m_entries[Index(index)]; + + switch (entry->type) + { + case Type::Array: + { + auto arrayEntry = static_cast<const ASTSerialInfo::ArrayEntry*>(entry); + outCount = Index(arrayEntry->elementCount); + return (arrayEntry + 1); + } + default: break; + } + + SLANG_ASSERT(!"Not an array"); + outCount = 0; + return nullptr; +} + ASTSerialPointer ASTSerialReader::getPointer(ASTSerialIndex index) { - SLANG_UNUSED(index); + if (index == ASTSerialIndex(0)) + { + return ASTSerialPointer(); + } + + SLANG_ASSERT(ASTSerialIndexRaw(index) < ASTSerialIndexRaw(m_entries.getCount())); + const Entry* entry = m_entries[Index(index)]; + + switch (entry->type) + { + case Type::String: + { + // Hmm. Tricky -> we don't know if will be cast as Name or String. Lets assume string. + String string = getString(index); + return ASTSerialPointer(string.getStringRepresentation()); + } + case Type::Node: + { + return ASTSerialPointer((NodeBase*)m_objects[Index(index)]); + } + case Type::RefObject: + { + return ASTSerialPointer((RefObject*)m_objects[Index(index)]); + } + default: break; + } + + SLANG_ASSERT(!"Cannot access as a pointer"); return ASTSerialPointer(); } String ASTSerialReader::getString(ASTSerialIndex index) { - SLANG_UNUSED(index); - return String(); + if (index == ASTSerialIndex(0)) + { + return String(); + } + + SLANG_ASSERT(ASTSerialIndexRaw(index) < ASTSerialIndexRaw(m_entries.getCount())); + const Entry* entry = m_entries[Index(index)]; + + // It has to be a string type + if (entry->type != Type::String) + { + SLANG_ASSERT(!"Not a string"); + return String(); + } + + RefObject* obj = (RefObject*)m_objects[Index(index)]; + + if (obj) + { + StringRepresentation* stringRep = dynamicCast<StringRepresentation>(obj); + if (stringRep) + { + return String(stringRep); + } + // Must be a name then + Name* name = dynamicCast<Name>(obj); + SLANG_ASSERT(name); + return name->text; + } + + // Okay we need to construct as a string + UnownedStringSlice slice = getStringSlice(index); + String string(slice); + StringRepresentation* stringRep = string.getStringRepresentation(); + + m_scope.add(stringRep); + m_objects[Index(index)] = stringRep; + return string; } Name* ASTSerialReader::getName(ASTSerialIndex index) @@ -1230,13 +1456,66 @@ Name* ASTSerialReader::getName(ASTSerialIndex index) { return nullptr; } - return nullptr; + + SLANG_ASSERT(ASTSerialIndexRaw(index) < ASTSerialIndexRaw(m_entries.getCount())); + const Entry* entry = m_entries[Index(index)]; + + // It has to be a string type + if (entry->type != Type::String) + { + SLANG_ASSERT(!"Not a string"); + return nullptr; + } + + RefObject* obj = (RefObject*)m_objects[Index(index)]; + + if (obj) + { + Name* name = dynamicCast<Name>(obj); + if (name) + { + return name; + } + // Can only be a string then + StringRepresentation* stringRep = dynamicCast<StringRepresentation>(obj); + SLANG_ASSERT(stringRep); + + // I don't need to scope, as scoped in NamePool + name = m_namePool->getName(String(stringRep)); + + // Store as name, as can always access the inner string if needed + m_objects[Index(index)] = name; + return name; + } + + UnownedStringSlice slice = getStringSlice(index); + String string(slice); + Name* name = m_namePool->getName(string); + // Don't need to add to scope, because scoped on the pool + m_objects[Index(index)] = name; + return name; } UnownedStringSlice ASTSerialReader::getStringSlice(ASTSerialIndex index) { - SLANG_UNUSED(index); - return UnownedStringSlice(); + SLANG_ASSERT(ASTSerialIndexRaw(index) < ASTSerialIndexRaw(m_entries.getCount())); + const Entry* entry = m_entries[Index(index)]; + + // It has to be a string type + if (entry->type != Type::String) + { + SLANG_ASSERT(!"Not a string"); + return UnownedStringSlice(); + } + + auto stringEntry = static_cast<const ASTSerialInfo::StringEntry*>(entry); + + const uint8_t* src = (const uint8_t*)stringEntry->sizeAndChars; + + // Decode the string + uint32_t size; + int sizeSize = ByteEncodeUtil::decodeLiteUInt32(src, &size); + return UnownedStringSlice((const char*)src + sizeSize, size); } SourceLoc ASTSerialReader::getSourceLoc(ASTSerialSourceLoc loc) @@ -1245,6 +1524,162 @@ SourceLoc ASTSerialReader::getSourceLoc(ASTSerialSourceLoc loc) return SourceLoc(); } +SlangResult ASTSerialReader::loadEntries(const uint8_t* data, size_t dataCount, List<const ASTSerialInfo::Entry*>& outEntries) +{ + // Check the input data is at least aligned to the max alignment (otherwise everything cannot be aligned correctly) + SLANG_ASSERT((size_t(data) & (ASTSerialInfo::MAX_ALIGNMENT - 1)) == 0); + + outEntries.setCount(1); + outEntries[0] = nullptr; + + const uint8_t*const end = data + dataCount; + + const uint8_t* cur = data; + while (cur < end) + { + const Entry* entry = (const Entry*)cur; + outEntries.add(entry); + + const size_t entrySize = entry->calcSize(m_classes); + cur += entrySize; + + // Need to get the next alignment + const size_t nextAlignment = ASTSerialInfo::getNextAlignment(entry->info); + + // Need to fix cur with the alignment + cur = (const uint8_t*)((size_t(cur) + nextAlignment - 1) & ~(nextAlignment - 1)); + } + + return SLANG_OK; +} + +SlangResult ASTSerialReader::load(const uint8_t* data, size_t dataCount, ASTBuilder* builder, NamePool* namePool) +{ + SLANG_RETURN_ON_FAIL(loadEntries(data, dataCount, m_entries)); + + m_namePool = namePool; + + m_objects.clearAndDeallocate(); + m_objects.setCount(m_entries.getCount()); + memset(m_objects.getBuffer(), 0, m_objects.getCount() * sizeof(void*)); + + // Go through entries, constructing objects. + for (Index i = 1; i < m_entries.getCount(); ++i) + { + const Entry* entry = m_entries[i]; + + switch (entry->type) + { + case Type::String: + { + // Don't need to construct an object. This is probably a StringRepresentation, or a Name + // Will evaluate lazily. + break; + } + case Type::Node: + { + auto nodeEntry = static_cast<const ASTSerialInfo::NodeEntry*>(entry); + m_objects[i] = builder->createByNodeType(ASTNodeType(nodeEntry->astNodeType)); + break; + } + case Type::RefObject: + { + auto objEntry = static_cast<const ASTSerialInfo::RefObjectEntry*>(entry); + switch (objEntry->subType) + { + case ASTSerialInfo::RefObjectEntry::SubType::Breadcrumb: + { + typedef LookupResultItem::Breadcrumb Breadcrumb; + + auto breadcrumb = new LookupResultItem::Breadcrumb(Breadcrumb::Kind::Member, DeclRef<Decl>(), nullptr); + m_scope.add(breadcrumb); + m_objects[i] = breadcrumb; + break; + } + default: + { + SLANG_ASSERT(!"Unknown type"); + return SLANG_FAIL; + } + } + break; + } + case Type::Array: + { + // Don't need to construct an object, as will be accessed an interpreted by the object that holds it + break; + } + } + } + + // Deserialize + for (Index i = 1; i < m_entries.getCount(); ++i) + { + const Entry* entry = m_entries[i]; + void* native = m_objects[i]; + if (!native) + { + continue; + } + switch (entry->type) + { + case Type::Node: + { + auto nodeEntry = static_cast<const ASTSerialInfo::NodeEntry*>(entry); + auto serialClass = m_classes->getSerialClass(ASTNodeType(nodeEntry->astNodeType)); + + const uint8_t* src = (const uint8_t*)(nodeEntry + 1); + uint8_t* dst = (uint8_t*)m_objects[i]; + + // It must be constructed + SLANG_ASSERT(dst); + + while (serialClass) + { + for (Index j = 0; j < serialClass->fieldsCount; ++j) + { + auto field = serialClass->fields[j]; + auto fieldType = field.type; + fieldType->toNativeFunc(this, src + field.serialOffset, dst + field.nativeOffset); + } + + auto cls = ReflectClassInfo::getInfo(serialClass->type); + auto superCls = cls->m_superClass; + + // Get the super class + serialClass = superCls ? m_classes->getSerialClass(ASTNodeType(superCls->m_classId)) : nullptr; + } + + break; + } + case Type::RefObject: + { + auto objEntry = static_cast<const ASTSerialInfo::RefObjectEntry*>(entry); + switch (objEntry->subType) + { + case ASTSerialInfo::RefObjectEntry::SubType::Breadcrumb: + { + typedef LookupResultItem::Breadcrumb Breadcrumb; + auto serialType = ASTSerialGetType<Breadcrumb>::getType(); + serialType->toNativeFunc(this, (entry + 1), m_objects[i]); + break; + } + default: + { + SLANG_ASSERT(!"Unknown type"); + return SLANG_FAIL; + } + } + break; + } + default: break; + } + } + + return SLANG_OK; +} + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ASTSerializeUtil !!!!!!!!!!!!!!!!!!!!!!!!!!!! /* static */SlangResult ASTSerializeUtil::selfTest() diff --git a/source/slang/slang-ast-serialize.h b/source/slang/slang-ast-serialize.h index a20da2e37..453102835 100644 --- a/source/slang/slang-ast-serialize.h +++ b/source/slang/slang-ast-serialize.h @@ -7,15 +7,58 @@ #include "slang-ast-support-types.h" #include "slang-ast-all.h" +#include "../core/slang-byte-encode-util.h" + +#include "../core/slang-stream.h" + namespace Slang { +class ASTSerialClasses; + // Type used to implement mechanisms to convert to and from serial types. template <typename T> struct ASTSerialTypeInfo; struct ASTSerialInfo { + enum + { + // Data held in serialized format, the maximally allowed alignment + MAX_ALIGNMENT = 8, + }; + + // We only allow up to MAX_ALIGNMENT bytes of alignment. We store alignments as shifts, so 2 bits needed for 1 - 8 + enum class EntryInfo : uint8_t + { + Alignment1 = 0, + }; + + static EntryInfo makeEntryInfo(int alignment, int nextAlignment) + { + // Make sure they are power of 2 + SLANG_ASSERT((alignment & (alignment - 1)) == 0); + SLANG_ASSERT((nextAlignment & (nextAlignment - 1)) == 0); + + const int alignmentShift = ByteEncodeUtil::calcMsb8(alignment); + const int nextAlignmentShift = ByteEncodeUtil::calcMsb8(nextAlignment); + return EntryInfo((nextAlignmentShift << 2) | alignmentShift); + } + static EntryInfo makeEntryInfo(int alignment) + { + // Make sure they are power of 2 + SLANG_ASSERT((alignment & (alignment - 1)) == 0); + return EntryInfo(ByteEncodeUtil::calcMsb8(alignment)); + } + /// Apply with the next alignment + static EntryInfo combineWithNext(EntryInfo cur, EntryInfo next) + { + return EntryInfo((int(cur) & ~0xc0) | ((int(next) & 3) << 2)); + } + + static int getAlignment(EntryInfo info) { return 1 << (int(info) & 3); } + static int getNextAlignment(EntryInfo info) { return 1 << ((int(info) >> 2) & 3); } + enum class Type : uint8_t { String, ///< String @@ -24,20 +67,30 @@ struct ASTSerialInfo Array, ///< Array }; + + /* Alignment is a little tricky. We have a 'Entry' header before the payload. The payload alignment may change. + If we only align on the Entry header, then it's size *must* be some modulo of the maximum alignment allowed. + + We could hold Entry separate from payload. We could make the header not require the alignment of the payload - but then + we'd need payload alignment separate from entry alignment. + */ struct Entry { Type type; - uint8_t nextAlignment; ///< Alignment of next entry + EntryInfo info; + + size_t calcSize(ASTSerialClasses* serialClasses) const; }; struct StringEntry : Entry { + char sizeAndChars[1]; }; struct NodeEntry : Entry { uint16_t astNodeType; - uint32_t _pad0; + uint32_t _pad0; ///< Necessary, because a node *can* have MAX_ALIGNEMENT }; struct RefObjectEntry : Entry @@ -47,7 +100,8 @@ struct ASTSerialInfo Breadcrumb, }; SubType subType; - uint32_t _pad0; + uint8_t _pad0; + uint32_t _pad1; ///< Necessary because RefObjectEntry *can* have MAX_ALIGNEMENT }; struct ArrayEntry : Entry @@ -57,7 +111,8 @@ struct ASTSerialInfo }; }; -enum class ASTSerialIndex : uint32_t; +typedef uint32_t ASTSerialIndexRaw; +enum class ASTSerialIndex : ASTSerialIndexRaw; typedef uint32_t ASTSerialSourceLoc; /* A type to convert pointers into types such that they can be passed around to readers/writers without @@ -112,23 +167,78 @@ class ASTSerialReader : public RefObject { public: - ASTSerialPointer getPointer(ASTSerialIndex index); + typedef ASTSerialInfo::Entry Entry; + typedef ASTSerialInfo::Type Type; + template <typename T> - void getArray(ASTSerialIndex index, List<T>& outArray); + void getArray(ASTSerialIndex index, List<T>& out); + + const void* getArray(ASTSerialIndex index, Index& outCount); + + ASTSerialPointer getPointer(ASTSerialIndex index); String getString(ASTSerialIndex index); Name* getName(ASTSerialIndex index); UnownedStringSlice getStringSlice(ASTSerialIndex index); SourceLoc getSourceLoc(ASTSerialSourceLoc loc); + + + /// Load the entries table (without deserializing anything) + /// NOTE! data must stay ins scope for outEntries to be valid + SlangResult loadEntries(const uint8_t* data, size_t dataCount, List<const ASTSerialInfo::Entry*>& outEntries); + + /// NOTE! data must stay ins scope when reading takes place + SlangResult load(const uint8_t* data, size_t dataCount, ASTBuilder* builder, NamePool* namePool); + + ASTSerialReader(ASTSerialClasses* classes): + m_classes(classes) + { + } + +protected: + List<const Entry*> m_entries; ///< The entries + List<void*> m_objects; ///< The constructed objects + + List<RefPtr<RefObject>> m_scope; ///< Objects to keep in scope during construction + + NamePool* m_namePool; + + ASTSerialClasses* m_classes; ///< Used to deserialize }; // --------------------------------------------------------------------------- template <typename T> -void ASTSerialReader::getArray(ASTSerialIndex index, List<T>& outArray) +void ASTSerialReader::getArray(ASTSerialIndex index, List<T>& out) { - SLANG_UNUSED(index); - outArray.clear(); + typedef ASTSerialTypeInfo<T> ElementTypeInfo; + typedef typename ElementTypeInfo::SerialType ElementSerialType; + + Index count; + auto serialElements = (const ElementSerialType*)getArray(index, count); + + if (count == 0) + { + out.clear(); + return; + } + + if (std::is_same<T, ElementSerialType>::value) + { + // If they are the same we can just write out + out.clear(); + out.insertRange(0, (const T*)serialElements, count); + } + else + { + // Else we need to convert + out.setCount(count); + for (Index i = 0; i < count; ++i) + { + ElementTypeInfo::toNative(this, (const void*)&serialElements[i], (void*)&out[i]); + } + } } + class ASTSerialClasses; /* This is a class used tby toSerial implementations to turn native type into the serial type */ @@ -146,11 +256,17 @@ public: ASTSerialIndex addName(const Name* name); ASTSerialSourceLoc addSourceLoc(SourceLoc sourceLoc); + /// Get the entries table holding how each index maps to an entry + const List<ASTSerialInfo::Entry*>& getEntries() const { return m_entries; } + + /// Write to a stream + SlangResult write(Stream* stream); + ASTSerialWriter(ASTSerialClasses* classes); protected: - ASTSerialIndex _addArray(size_t elementSize, const void* elements, Index elementCount); + ASTSerialIndex _addArray(size_t elementSize, size_t alignment, const void* elements, Index elementCount); ASTSerialIndex _add(const void* nativePtr, ASTSerialInfo::Entry* entry) { @@ -182,7 +298,7 @@ ASTSerialIndex ASTSerialWriter::addArray(const T* in, Index count) if (std::is_same<T, ElementSerialType>::value) { // If they are the same we can just write out - return _addArray(sizeof(T), in, count); + return _addArray(sizeof(T), SLANG_ALIGN_OF(ElementSerialType), in, count); } else { @@ -194,7 +310,7 @@ ASTSerialIndex ASTSerialWriter::addArray(const T* in, Index count) { ElementTypeInfo::toSerial(this, &in[i], &work[i]); } - return _addArray(sizeof(ElementSerialType), in, count); + return _addArray(sizeof(ElementSerialType), SLANG_ALIGN_OF(ElementSerialType), work.getBuffer(), count); } } diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h index 6fe8781c5..6ab9167f2 100644 --- a/source/slang/slang-ast-support-types.h +++ b/source/slang/slang-ast-support-types.h @@ -499,6 +499,8 @@ namespace Slang const char* m_name; ///< Textual class name, for debugging CreateFunc m_createFunc; ///< Callback to use when creating instances (using an ASTBuilder for backing memory) DestructorFunc m_destructorFunc; ///< The destructor for this type. Being just destructor, does not free backing memory for type. + uint32_t m_sizeInBytes; ///< Total size of the type + uint8_t m_alignment; ///< The required alignment of the type struct Infos { diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 7c535d64b..6d4a27733 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -1080,10 +1080,18 @@ void FrontEndCompileRequest::parseTranslationUnit( // Test serialization { RefPtr<ASTSerialClasses> classes = new ASTSerialClasses; - ASTSerialWriter writer(classes); - // Lets serialize it all - writer.addPointer(translationUnit->getModuleDecl()); + OwnedMemoryStream stream(FileAccess::ReadWrite); + + { + ASTSerialWriter writer(classes); + + // Lets serialize it all + writer.addPointer(translationUnit->getModuleDecl()); + // Let's stick it all in a stream + writer.write(&stream); + } + } #endif |
