summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-serialize-ast.cpp
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2020-09-17 16:47:57 -0400
committerGitHub <noreply@github.com>2020-09-17 13:47:57 -0700
commitb9cddcb9c718f986ee5e4f7c6189ee2ebea4ace1 (patch)
treed4537f98e8ec93459f13d2d271d621b80f797a59 /source/slang/slang-serialize-ast.cpp
parentbbf492a0b78ce8b96372a736b7d591cdc71d5b65 (diff)
Share debug information between AST and IR (#1547)
* Test if blob is returned. * Rename serialize files so can be grouped. * StringRepresentationCache -> SerialStringTable * Split out SerialStringTable from slang-serialize-ir * First pass at reorganizing serialization/containers. Remain some issues about debug info. * Fix bug in calculating sourceloc. * Improve calcFixSourceLoc * Make allocations for payload RiffContainer align to at least 8 bytes. This is important for read, if the payload can contain 8 byte aligned data. Note this has no effect on Riff file format alignment rules. * Improve comments around RiffContainer and alignment. * Remove SerialStringTable, can just use StringSlicePool instead. * Typo fix for Clang/Linux. Co-authored-by: Tim Foley <tfoleyNV@users.noreply.github.com>
Diffstat (limited to 'source/slang/slang-serialize-ast.cpp')
-rw-r--r--source/slang/slang-serialize-ast.cpp2008
1 files changed, 2008 insertions, 0 deletions
diff --git a/source/slang/slang-serialize-ast.cpp b/source/slang/slang-serialize-ast.cpp
new file mode 100644
index 000000000..82cd7d6b4
--- /dev/null
+++ b/source/slang/slang-serialize-ast.cpp
@@ -0,0 +1,2008 @@
+// slang-serialize-ast.cpp
+#include "slang-serialize-ast.h"
+
+#include "slang-ast-generated.h"
+#include "slang-ast-generated-macro.h"
+
+#include "slang-compiler.h"
+#include "slang-type-layout.h"
+
+#include "slang-ast-dump.h"
+#include "slang-mangle.h"
+
+#include "slang-ast-support-types.h"
+
+#include "slang-legalize-types.h"
+
+#include "../core/slang-byte-encode-util.h"
+
+namespace Slang {
+
+
+// Things stored as references:
+//
+// NodeBase derived types
+// Array
+//
+// RefObject derived types:
+//
+// Breadcrumb
+// StringRepresentation
+// Scope
+
+
+
+// Helpers to convert fields treated as values
+
+class ASTSerialReader;
+class ASTSerialWriter;
+
+template <typename NATIVE_TYPE, typename SERIAL_TYPE>
+static void _toSerialValue(ASTSerialWriter* writer, const NATIVE_TYPE& src, SERIAL_TYPE& dst)
+{
+ ASTSerialTypeInfo<NATIVE_TYPE>::toSerial(writer, &src, &dst);
+}
+
+template <typename SERIAL_TYPE, typename NATIVE_TYPE>
+static void _toNativeValue(ASTSerialReader* reader, const SERIAL_TYPE& src, NATIVE_TYPE& dst)
+{
+ ASTSerialTypeInfo<NATIVE_TYPE>::toNative(reader, &src, &dst);
+}
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ModuleASTSerialFilter !!!!!!!!!!!!!!!!!!!!!!!!
+
+
+ASTSerialIndex ModuleASTSerialFilter::writePointer(ASTSerialWriter* writer, const NodeBase* inPtr)
+{
+ NodeBase* ptr = const_cast<NodeBase*>(inPtr);
+ SLANG_ASSERT(ptr);
+
+ if (Decl* decl = as<Decl>(ptr))
+ {
+ ModuleDecl* moduleDecl = findModuleForDecl(decl);
+ SLANG_ASSERT(moduleDecl);
+ if (moduleDecl && moduleDecl != m_moduleDecl)
+ {
+ ASTBuilder* astBuilder = m_moduleDecl->module->getASTBuilder();
+
+ // It's a reference to a declaration in another module, so create an ImportExternalDecl.
+
+ String mangledName = getMangledName(astBuilder, decl);
+
+ ImportExternalDecl* importDecl = astBuilder->create<ImportExternalDecl>();
+ importDecl->mangledName = mangledName;
+ const ASTSerialIndex index = writer->writePointer(importDecl);
+
+ // Set as the index of this
+ writer->setPointerIndex(ptr, index);
+
+ return index;
+ }
+ else
+ {
+ // Okay... we can just write it out then
+ return writer->writePointer(ptr);
+ }
+ }
+
+ // TODO(JS): What we really want to do here is to ignore bodies functions.
+ // It's not 100% clear if this is even right though - for example does type inference
+ // imply the body is needed to say infer a return type?
+ // Also not clear if statements in other scenarios (if there are others) might need to be kept.
+ //
+ // For now we just ignore all stmts
+
+ if (Stmt* stmt = as<Stmt>(ptr))
+ {
+ //
+ writer->setPointerIndex(stmt, ASTSerialIndex(0));
+ return ASTSerialIndex(0);
+ }
+
+ // For now for everything else just write it
+ return writer->writePointer(ptr);
+}
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Serial <-> Native conversion !!!!!!!!!!!!!!!!!!!!!!!!
+
+
+// We need to have a way to map between the two.
+// If no mapping is needed, (just a copy), then we don't bother with the functions
+template <typename T>
+struct ASTSerialBasicTypeInfo
+{
+ typedef T NativeType;
+ typedef T SerialType;
+
+ // We want the alignment to be the same as the size of the type for basic types
+ // NOTE! Might be different from SLANG_ALIGN_OF(SerialType)
+ enum { SerialAlignment = sizeof(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) { SLANG_UNUSED(writer); *(T*)serial = *(const T*)native; }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native) { SLANG_UNUSED(reader); *(T*)native = *(const T*)serial; }
+
+ static const ASTSerialType* getType()
+ {
+ static const ASTSerialType type = { sizeof(SerialType), uint8_t(SerialAlignment), &toSerial, &toNative };
+ return &type;
+ }
+};
+
+template <typename NATIVE_T, typename SERIAL_T>
+struct ASTSerialConvertTypeInfo
+{
+ typedef NATIVE_T NativeType;
+ typedef SERIAL_T SerialType;
+
+ enum { SerialAlignment = ASTSerialBasicTypeInfo<SERIAL_T>::SerialAlignment };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) { SLANG_UNUSED(writer); *(SERIAL_T*)serial = SERIAL_T(*(const NATIVE_T*)native); }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native) { SLANG_UNUSED(reader); *(NATIVE_T*)native = NATIVE_T(*(const SERIAL_T*)serial); }
+};
+
+template <typename T>
+struct ASTSerialIdentityTypeInfo
+{
+ typedef T NativeType;
+ typedef T SerialType;
+
+ enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) { SLANG_UNUSED(writer); *(T*)serial = *(const T*)native; }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native) { SLANG_UNUSED(reader); *(T*)native = *(const T*)serial; }
+};
+
+// Don't need to convert the index type
+
+template <>
+struct ASTSerialTypeInfo<ASTSerialIndex> : public ASTSerialIdentityTypeInfo<ASTSerialIndex> {};
+
+
+// Because is sized, we don't need to convert
+template <>
+struct ASTSerialTypeInfo<FeedbackType::Kind> : public ASTSerialIdentityTypeInfo<FeedbackType::Kind> {};
+
+// Implement for Basic Types
+
+template <>
+struct ASTSerialTypeInfo<uint8_t> : public ASTSerialBasicTypeInfo<uint8_t> {};
+template <>
+struct ASTSerialTypeInfo<uint16_t> : public ASTSerialBasicTypeInfo<uint16_t> {};
+template <>
+struct ASTSerialTypeInfo<uint32_t> : public ASTSerialBasicTypeInfo<uint32_t> {};
+template <>
+struct ASTSerialTypeInfo<uint64_t> : public ASTSerialBasicTypeInfo<uint64_t> {};
+
+template <>
+struct ASTSerialTypeInfo<int8_t> : public ASTSerialBasicTypeInfo<int8_t> {};
+template <>
+struct ASTSerialTypeInfo<int16_t> : public ASTSerialBasicTypeInfo<int16_t> {};
+template <>
+struct ASTSerialTypeInfo<int32_t> : public ASTSerialBasicTypeInfo<int32_t> {};
+template <>
+struct ASTSerialTypeInfo<int64_t> : public ASTSerialBasicTypeInfo<int64_t> {};
+
+template <>
+struct ASTSerialTypeInfo<float> : public ASTSerialBasicTypeInfo<float> {};
+template <>
+struct ASTSerialTypeInfo<double> : public ASTSerialBasicTypeInfo<double> {};
+
+// SamplerStateFlavor
+
+template <>
+struct ASTSerialTypeInfo<SamplerStateFlavor> : public ASTSerialConvertTypeInfo<SamplerStateFlavor, uint8_t> {};
+
+// TextureFlavor
+
+template <>
+struct ASTSerialTypeInfo<TextureFlavor>
+{
+ typedef TextureFlavor NativeType;
+ typedef uint16_t SerialType;
+ enum { SerialAlignment = sizeof(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial) { SLANG_UNUSED(writer); *(SerialType*)serial = ((const NativeType*)native)->flavor; }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native) { SLANG_UNUSED(reader); ((NativeType*)native)->flavor = *(const SerialType*)serial; }
+};
+
+// Fixed arrays
+
+template <typename T, size_t N>
+struct ASTSerialTypeInfo<T[N]>
+{
+ typedef ASTSerialTypeInfo<T> ElementASTSerialType;
+ typedef typename ElementASTSerialType::SerialType SerialElementType;
+
+ typedef T NativeType[N];
+ typedef SerialElementType SerialType[N];
+
+ enum { SerialAlignment = ASTSerialTypeInfo<T>::SerialAlignment };
+
+ static void toSerial(ASTSerialWriter* writer, const void* inNative, void* outSerial)
+ {
+ SerialElementType* serial = (SerialElementType*)outSerial;
+ const T* native = (const T*)inNative;
+ for (Index i = 0; i < Index(N); ++i)
+ {
+ ElementASTSerialType::toSerial(writer, native + i, serial + i);
+ }
+ }
+ static void toNative(ASTSerialReader* reader, const void* inSerial, void* outNative)
+ {
+ const SerialElementType* serial = (const SerialElementType*)inSerial;
+ T* native = (T*)outNative;
+ for (Index i = 0; i < Index(N); ++i)
+ {
+ ElementASTSerialType::toNative(reader, serial + i, native + i);
+ }
+ }
+};
+
+// Special case bool - as we can't rely on size alignment
+template <>
+struct ASTSerialTypeInfo<bool>
+{
+ typedef bool NativeType;
+ typedef uint8_t SerialType;
+
+ enum { SerialAlignment = sizeof(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* inNative, void* outSerial)
+ {
+ SLANG_UNUSED(writer);
+ *(SerialType*)outSerial = *(const NativeType*)inNative ? 1 : 0;
+ }
+ static void toNative(ASTSerialReader* reader, const void* inSerial, void* outNative)
+ {
+ SLANG_UNUSED(reader);
+ *(NativeType*)outNative = (*(const SerialType*)inSerial) != 0;
+ }
+};
+
+
+// Pointer
+// Could handle different pointer base types with some more template magic here, but instead went with Pointer type to keep
+// things simpler.
+template <typename T>
+struct ASTSerialTypeInfo<T*>
+{
+ typedef T* NativeType;
+ typedef ASTSerialIndex SerialType;
+
+ enum
+ {
+ SerialAlignment = SLANG_ALIGN_OF(SerialType)
+ };
+
+ static void toSerial(ASTSerialWriter* writer, const void* inNative, void* outSerial)
+ {
+ *(SerialType*)outSerial = writer->addPointer(*(T**)inNative);
+ }
+ static void toNative(ASTSerialReader* reader, const void* inSerial, void* outNative)
+ {
+ *(T**)outNative = reader->getPointer(*(const SerialType*)inSerial).dynamicCast<T>();
+ }
+};
+
+// Special case Name
+template <>
+struct ASTSerialTypeInfo<Name*> : public ASTSerialTypeInfo<RefObject*>
+{
+ // Special case
+ typedef Name* NativeType;
+ static void toNative(ASTSerialReader* reader, const void* inSerial, void* outNative)
+ {
+ *(Name**)outNative = reader->getName(*(const SerialType*)inSerial);
+ }
+};
+
+template <>
+struct ASTSerialTypeInfo<const Name*> : public ASTSerialTypeInfo<Name*>
+{
+};
+
+
+struct ASTSerialDeclRefBaseTypeInfo
+{
+ typedef DeclRefBase NativeType;
+ struct SerialType
+ {
+ ASTSerialIndex substitutions;
+ ASTSerialIndex decl;
+ };
+ enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* inNative, void* outSerial)
+ {
+ SerialType& serial = *(SerialType*)outSerial;
+ const NativeType& native = *(const NativeType*)inNative;
+
+ serial.decl = writer->addPointer(native.decl);
+ serial.substitutions = writer->addPointer(native.substitutions.substitutions);
+ }
+ static void toNative(ASTSerialReader* reader, const void* inSerial, void* outNative)
+ {
+ DeclRefBase& native = *(DeclRefBase*)(outNative);
+ const SerialType& serial = *(const SerialType*)inSerial;
+
+ native.decl = reader->getPointer(serial.decl).dynamicCast<Decl>();
+ native.substitutions.substitutions = reader->getPointer(serial.substitutions).dynamicCast<Substitutions>();
+ }
+ static const ASTSerialType* getType()
+ {
+ static const ASTSerialType type = { sizeof(SerialType), uint8_t(SerialAlignment), &toSerial, &toNative };
+ return &type;
+ }
+};
+
+template <typename T>
+struct ASTSerialTypeInfo<DeclRef<T>> : public ASTSerialDeclRefBaseTypeInfo {};
+
+// MatrixCoord can just go as is
+template <>
+struct ASTSerialTypeInfo<MatrixCoord> : ASTSerialIdentityTypeInfo<MatrixCoord> {};
+
+// SourceLoc
+
+// Make the type exposed, so we can look for it if we want to remap.
+template <>
+struct ASTSerialTypeInfo<SourceLoc>
+{
+ typedef SourceLoc NativeType;
+ typedef ASTSerialSourceLoc SerialType;
+ enum { SerialAlignment = SLANG_ALIGN_OF(ASTSerialSourceLoc) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* inNative, void* outSerial)
+ {
+ *(SerialType*)outSerial = writer->addSourceLoc(*(const NativeType*)inNative);
+ }
+ static void toNative(ASTSerialReader* reader, const void* inSerial, void* outNative)
+ {
+ *(NativeType*)outNative = reader->getSourceLoc(*(const SerialType*)inSerial);
+ }
+};
+
+// List
+template <typename T, typename ALLOCATOR>
+struct ASTSerialTypeInfo<List<T, ALLOCATOR>>
+{
+ typedef List<T, ALLOCATOR> NativeType;
+ typedef ASTSerialIndex SerialType;
+
+ enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ auto& src = *(const NativeType*)native;
+ auto& dst = *(SerialType*)serial;
+
+ dst = writer->addArray(src.getBuffer(), src.getCount());
+ }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native)
+ {
+ auto& dst = *(NativeType*)native;
+ auto& src = *(const SerialType*)serial;
+
+ reader->getArray(src, dst);
+ }
+};
+
+// Dictionary
+template <typename KEY, typename VALUE>
+struct ASTSerialTypeInfo<Dictionary<KEY, VALUE>>
+{
+ typedef Dictionary<KEY, VALUE> NativeType;
+ struct SerialType
+ {
+ ASTSerialIndex keys; ///< Index an array
+ ASTSerialIndex values; ///< Index an array
+ };
+
+ typedef typename ASTSerialTypeInfo<KEY>::SerialType KeySerialType;
+ typedef typename ASTSerialTypeInfo<VALUE>::SerialType ValueSerialType;
+
+ enum { SerialAlignment = SLANG_ALIGN_OF(ASTSerialIndex) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ auto& src = *(const NativeType*)native;
+ auto& dst = *(SerialType*)serial;
+
+ List<KeySerialType> keys;
+ List<ValueSerialType> values;
+
+ Index count = Index(src.Count());
+ keys.setCount(count);
+ values.setCount(count);
+
+ Index i = 0;
+ for (const auto& pair : src)
+ {
+ ASTSerialTypeInfo<KEY>::toSerial(writer, &pair.Key, &keys[i]);
+ ASTSerialTypeInfo<VALUE>::toSerial(writer, &pair.Value, &values[i]);
+ i++;
+ }
+
+ dst.keys = writer->addArray(keys.getBuffer(), count);
+ dst.values = writer->addArray(values.getBuffer(), count);
+ }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native)
+ {
+ auto& src = *(const SerialType*)serial;
+ auto& dst = *(NativeType*)native;
+
+ // Clear it
+ dst = NativeType();
+
+ List<KEY> keys;
+ List<VALUE> values;
+
+ reader->getArray(src.keys, keys);
+ reader->getArray(src.values, values);
+
+ SLANG_ASSERT(keys.getCount() == values.getCount());
+
+ const Index count = keys.getCount();
+ for (Index i = 0; i < count; ++i)
+ {
+ dst.Add(keys[i], values[i]);
+ }
+ }
+};
+
+// SyntaxClass<T>
+template <typename T>
+struct ASTSerialTypeInfo<SyntaxClass<T>>
+{
+ typedef SyntaxClass<T> NativeType;
+ typedef uint16_t SerialType;
+
+ enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ SLANG_UNUSED(writer);
+ auto& src = *(const NativeType*)native;
+ auto& dst = *(SerialType*)serial;
+ dst = SerialType(src.classInfo->m_classId);
+ }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native)
+ {
+ SLANG_UNUSED(reader);
+ auto& src = *(const SerialType*)serial;
+ auto& dst = *(NativeType*)native;
+ dst.classInfo = ReflectClassInfo::getInfo(ASTNodeType(src));
+ }
+};
+
+// Handle RefPtr - just convert into * to do the conversion
+template <typename T>
+struct ASTSerialTypeInfo<RefPtr<T>>
+{
+ typedef RefPtr<T> NativeType;
+ typedef typename ASTSerialTypeInfo<T*>::SerialType SerialType;
+ enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ auto& src = *(const NativeType*)native;
+ T* obj = src;
+ ASTSerialTypeInfo<T*>::toSerial(writer, &obj, serial);
+ }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native)
+ {
+ T* obj = nullptr;
+ ASTSerialTypeInfo<T*>::toNative(reader, serial, &obj);
+ *(NativeType*)native = obj;
+ }
+};
+
+// QualType
+
+template <>
+struct ASTSerialTypeInfo<QualType>
+{
+ typedef QualType NativeType;
+ struct SerialType
+ {
+ ASTSerialIndex type;
+ uint8_t isLeftValue;
+ };
+ enum { SerialAlignment = SLANG_ALIGN_OF(ASTSerialIndex) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ 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* serial, void* native)
+ {
+ auto src = (const SerialType*)serial;
+ auto dst = (NativeType*)native;
+ dst->type = reader->getPointer(src->type).dynamicCast<Type>();
+ dst->isLeftValue = src->isLeftValue != 0;
+ }
+};
+
+
+// LookupResult::Breadcrumb
+template <>
+struct ASTSerialTypeInfo<LookupResultItem::Breadcrumb>
+{
+ typedef LookupResultItem::Breadcrumb NativeType;
+ struct SerialType
+ {
+ NativeType::Kind kind;
+ NativeType::ThisParameterMode thisParameterMode;
+ ASTSerialTypeInfo<DeclRef<Decl>>::SerialType declRef;
+ ASTSerialTypeInfo<RefPtr<NativeType>> next;
+ };
+ enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ auto& src = *(const NativeType*)native;
+ auto& dst = *(SerialType*)serial;
+
+ dst.kind = src.kind;
+ dst.thisParameterMode = src.thisParameterMode;
+ _toSerialValue(writer, src.declRef, dst.declRef);
+ _toSerialValue(writer, src.next, dst.next);
+ }
+
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native)
+ {
+ auto& dst = *(NativeType*)native;
+ auto& src = *(const SerialType*)serial;
+
+ dst.kind = src.kind;
+ dst.thisParameterMode = src.thisParameterMode;
+ _toNativeValue(reader, src.declRef, dst.declRef);
+ _toNativeValue(reader, src.next, dst.next);
+ }
+};
+
+// LookupResultItem
+template <>
+struct ASTSerialTypeInfo<LookupResultItem>
+{
+ typedef LookupResultItem NativeType;
+ struct SerialType
+ {
+ ASTSerialTypeInfo<DeclRef<Decl>>::SerialType declRef;
+ ASTSerialTypeInfo<RefPtr<NativeType::Breadcrumb>> breadcrumbs;
+ };
+ enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ auto& src = *(const NativeType*)native;
+ auto& dst = *(SerialType*)serial;
+
+ _toSerialValue(writer, src.declRef, dst.declRef);
+ _toSerialValue(writer, src.breadcrumbs, dst.breadcrumbs);
+ }
+
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native)
+ {
+ auto& dst = *(NativeType*)native;
+ auto& src = *(const SerialType*)serial;
+
+ _toNativeValue(reader, src.declRef, dst.declRef);
+ _toNativeValue(reader, src.breadcrumbs, dst.breadcrumbs);
+ }
+};
+
+// LookupResult
+template <>
+struct ASTSerialTypeInfo<LookupResult>
+{
+ typedef LookupResult NativeType;
+ typedef ASTSerialIndex SerialType;
+ enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ auto& src = *(const NativeType*)native;
+ auto& dst = *(SerialType*)serial;
+
+ if (src.isOverloaded())
+ {
+ // Save off as an array
+ dst = writer->addArray(src.items.getBuffer(), src.items.getCount());
+ }
+ else if (src.item.declRef.getDecl())
+ {
+ dst = writer->addArray(&src.item, 1);
+ }
+ else
+ {
+ dst = ASTSerialIndex(0);
+ }
+ }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native)
+ {
+ auto& dst = *(NativeType*)native;
+ auto& src = *(const SerialType*)serial;
+
+ // Initialize
+ dst = NativeType();
+
+ List<LookupResultItem> items;
+ reader->getArray(src, items);
+
+ if (items.getCount() == 1)
+ {
+ dst.item = items[0];
+ }
+ else
+ {
+ dst.items.swapWith(items);
+ // We have to set item such that it is valid/member of items, if items is non empty
+ dst.item = dst.items[0];
+ }
+ }
+};
+
+
+// GlobalGenericParamSubstitution::ConstraintArg
+template <>
+struct ASTSerialTypeInfo<GlobalGenericParamSubstitution::ConstraintArg>
+{
+ typedef GlobalGenericParamSubstitution::ConstraintArg NativeType;
+ struct SerialType
+ {
+ ASTSerialIndex decl;
+ ASTSerialIndex val;
+ };
+ enum { SerialAlignment = SLANG_ALIGN_OF(ASTSerialIndex) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ auto& dst = *(SerialType*)serial;
+ auto& src = *(const NativeType*)native;
+
+ dst.decl = writer->addPointer(src.decl);
+ dst.val = writer->addPointer(src.val);
+ }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native)
+ {
+ auto& src = *(const SerialType*)serial;
+ auto& dst = *(NativeType*)native;
+
+ dst.decl = reader->getPointer(src.decl).dynamicCast<Decl>();
+ dst.val = reader->getPointer(src.val).dynamicCast<Val>();
+ }
+};
+
+// ExpandedSpecializationArg
+template <>
+struct ASTSerialTypeInfo<ExpandedSpecializationArg>
+{
+ typedef ExpandedSpecializationArg NativeType;
+ struct SerialType
+ {
+ ASTSerialIndex val;
+ ASTSerialIndex witness;
+ };
+ enum { SerialAlignment = SLANG_ALIGN_OF(ASTSerialIndex) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ auto& dst = *(SerialType*)serial;
+ auto& src = *(const NativeType*)native;
+
+ dst.witness = writer->addPointer(src.witness);
+ dst.val = writer->addPointer(src.val);
+ }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native)
+ {
+ auto& src = *(const SerialType*)serial;
+ auto& dst = *(NativeType*)native;
+
+ dst.witness = reader->getPointer(src.witness).dynamicCast<Val>();
+ dst.val = reader->getPointer(src.val).dynamicCast<Val>();
+ }
+};
+
+// TypeExp
+template <>
+struct ASTSerialTypeInfo<TypeExp>
+{
+ typedef TypeExp NativeType;
+ struct SerialType
+ {
+ ASTSerialIndex type;
+ ASTSerialIndex expr;
+ };
+ enum { SerialAlignment = SLANG_ALIGN_OF(ASTSerialIndex) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ auto& dst = *(SerialType*)serial;
+ auto& src = *(const NativeType*)native;
+
+ dst.type = writer->addPointer(src.type);
+ dst.expr = writer->addPointer(src.exp);
+ }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native)
+ {
+ auto& src = *(const SerialType*)serial;
+ auto& dst = *(NativeType*)native;
+
+ dst.type = reader->getPointer(src.type).dynamicCast<Type>();
+ dst.exp = reader->getPointer(src.expr).dynamicCast<Expr>();
+ }
+};
+
+// DeclCheckStateExt
+template <>
+struct ASTSerialTypeInfo<DeclCheckStateExt>
+{
+ typedef DeclCheckStateExt NativeType;
+ typedef DeclCheckStateExt::RawType SerialType;
+
+ enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ SLANG_UNUSED(writer);
+ *(SerialType*)serial = (*(const NativeType*)native).getRaw();
+ }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native)
+ {
+ SLANG_UNUSED(reader);
+ (*(NativeType*)serial).setRaw(*(const SerialType*)native);
+ }
+};
+
+// Modifiers
+template <>
+struct ASTSerialTypeInfo<Modifiers>
+{
+ typedef Modifiers NativeType;
+ typedef ASTSerialIndex SerialType;
+
+ enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ // We need to make into an array
+ List<ASTSerialIndex> modifierIndices;
+ for (Modifier* modifier : *(NativeType*)native)
+ {
+ modifierIndices.add(writer->addPointer(modifier));
+ }
+ *(SerialType*)serial = writer->addArray(modifierIndices.getBuffer(), modifierIndices.getCount());
+ }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native)
+ {
+ List<Modifier*> modifiers;
+ reader->getArray(*(const SerialType*)serial, modifiers);
+
+ Modifier* prev = nullptr;
+ for (Modifier* modifier : modifiers)
+ {
+ if (prev)
+ {
+ prev->next = modifier;
+ }
+ }
+
+ NativeType& dst = *(NativeType*)native;
+ dst.first = modifiers.getCount() > 0 ? modifiers[0] : nullptr;
+ }
+};
+
+// ImageFormat
+template <>
+struct ASTSerialTypeInfo<ImageFormat> : public ASTSerialConvertTypeInfo<ImageFormat, uint8_t> {};
+
+// Stage
+template <>
+struct ASTSerialTypeInfo<Stage> : public ASTSerialConvertTypeInfo<Stage, uint8_t> {};
+
+// TokenType
+template <>
+struct ASTSerialTypeInfo<TokenType> : public ASTSerialConvertTypeInfo<TokenType, uint8_t> {};
+
+// BaseType
+template <>
+struct ASTSerialTypeInfo<BaseType> : public ASTSerialConvertTypeInfo<BaseType, uint8_t> {};
+
+// SemanticVersion
+template <>
+struct ASTSerialTypeInfo<SemanticVersion> : public ASTSerialIdentityTypeInfo<SemanticVersion> {};
+
+// ASTNodeType
+template <>
+struct ASTSerialTypeInfo<ASTNodeType> : public ASTSerialConvertTypeInfo<ASTNodeType, uint16_t> {};
+
+// String
+template <>
+struct ASTSerialTypeInfo<String>
+{
+ typedef String NativeType;
+ typedef ASTSerialIndex SerialType;
+ enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ auto& src = *(const NativeType*)native;
+ *(SerialType*)serial = writer->addString(src);
+ }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native)
+ {
+ auto& src = *(const SerialType*)serial;
+ auto& dst = *(NativeType*)native;
+ dst = reader->getString(src);
+ }
+};
+
+// Token
+template <>
+struct ASTSerialTypeInfo<Token>
+{
+ typedef Token NativeType;
+ struct SerialType
+ {
+ ASTSerialTypeInfo<BaseType>::SerialType type;
+ ASTSerialTypeInfo<SourceLoc>::SerialType loc;
+ ASTSerialIndex name;
+ };
+ enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ auto& src = *(const NativeType*)native;
+ auto& dst = *(SerialType*)serial;
+
+ ASTSerialTypeInfo<TokenType>::toSerial(writer, &src.type, &dst.type);
+ ASTSerialTypeInfo<SourceLoc>::toSerial(writer, &src.loc, &dst.loc);
+
+ if (src.flags & TokenFlag::Name)
+ {
+ dst.name = writer->addName(src.getName());
+ }
+ else
+ {
+ dst.name = writer->addString(src.getContent());
+ }
+ }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native)
+ {
+ auto& src = *(const SerialType*)serial;
+ auto& dst = *(NativeType*)native;
+
+ dst.flags = 0;
+ dst.charsNameUnion.chars = nullptr;
+
+ ASTSerialTypeInfo<TokenType>::toNative(reader, &src.type, &dst.type);
+ ASTSerialTypeInfo<SourceLoc>::toNative(reader, &src.loc, &dst.loc);
+
+ // At the other end all token content will appear as Names.
+ if (src.name != ASTSerialIndex(0))
+ {
+ dst.charsNameUnion.name = reader->getName(src.name);
+ dst.flags |= TokenFlag::Name;
+ }
+ }
+};
+
+// NameLoc
+template <>
+struct ASTSerialTypeInfo<NameLoc>
+{
+ typedef NameLoc NativeType;
+ struct SerialType
+ {
+ ASTSerialTypeInfo<SourceLoc>::SerialType loc;
+ ASTSerialIndex name;
+ };
+ enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ auto& src = *(const NativeType*)native;
+ auto& dst = *(SerialType*)serial;
+
+ dst.name = writer->addName(src.name);
+ ASTSerialTypeInfo<SourceLoc>::toSerial(writer, &src.loc, &dst.loc);
+ }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native)
+ {
+ auto& src = *(const SerialType*)serial;
+ auto& dst = *(NativeType*)native;
+
+ dst.name = reader->getName(src.name);
+ ASTSerialTypeInfo<SourceLoc>::toNative(reader, &src.loc, &dst.loc);
+ }
+};
+
+// DiagnosticInfo
+template <>
+struct ASTSerialTypeInfo<const DiagnosticInfo*>
+{
+ typedef const DiagnosticInfo* NativeType;
+ typedef ASTSerialIndex SerialType;
+
+ enum { SerialAlignment = SLANG_ALIGN_OF(SerialType) };
+
+ static void toSerial(ASTSerialWriter* writer, const void* native, void* serial)
+ {
+ auto& src = *(const NativeType*)native;
+ auto& dst = *(SerialType*)serial;
+ dst = src ? writer->addString(UnownedStringSlice(src->name)) : ASTSerialIndex(0);
+ }
+ static void toNative(ASTSerialReader* reader, const void* serial, void* native)
+ {
+ auto& src = *(const SerialType*)serial;
+ auto& dst = *(NativeType*)native;
+
+ if (src == ASTSerialIndex(0))
+ {
+ dst = nullptr;
+ }
+ else
+ {
+ dst = findDiagnosticByName(reader->getStringSlice(src));
+ }
+ }
+};
+
+// !!!!!!!!!!!!!!!!!!!!! ASTSerialGetType<T> !!!!!!!!!!!!!!!!!!!!!!!!!!!
+// Getting the type info, let's use a static variable to hold the state to keep simple
+
+template <typename T>
+struct ASTSerialGetType
+{
+ static const ASTSerialType* getType()
+ {
+ typedef ASTSerialTypeInfo<T> Info;
+ static const ASTSerialType type = { sizeof(typename Info::SerialType), uint8_t(Info::SerialAlignment), &Info::toSerial, &Info::toNative };
+ return &type;
+ }
+};
+
+// Special case DeclRef, because it always uses the same type
+template <typename T>
+struct ASTSerialGetType<DeclRef<T>>
+{
+ static const ASTSerialType* getType() { return ASTSerialDeclRefBaseTypeInfo::getType(); }
+};
+
+// !!!!!!!!!!!!!!!!!!!!!! Generate fields for a type !!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+template <typename T>
+ASTSerialField _calcField(const char* name, T& in)
+{
+ uint8_t* ptr = &reinterpret_cast<uint8_t&>(in);
+
+ ASTSerialField field;
+ field.name = name;
+ field.type = ASTSerialGetType<T>::getType();
+ // This only works because we in is an offset from 1
+ field.nativeOffset = uint32_t(size_t(ptr) - 1);
+ field.serialOffset = 0;
+ return field;
+}
+
+static ASTSerialClass _makeClass(MemoryArena* arena, ASTNodeType type, const List<ASTSerialField>& fields)
+{
+ ASTSerialClass cls = { type, 0, 0, 0, 0 };
+ cls.fieldsCount = fields.getCount();
+ cls.fields = arena->allocateAndCopyArray(fields.getBuffer(), fields.getCount());
+ return cls;
+}
+
+#define SLANG_AST_SERIAL_FIELD(FIELD_NAME, TYPE, param) fields.add(_calcField(#FIELD_NAME, obj->FIELD_NAME));
+
+// Note that the obj point is not nullptr, because some compilers notice this is 'indexing from null'
+// and warn/error. So we offset from 1.
+#define SLANG_AST_SERIAL_MAKE_CLASS(NAME, SUPER, ORIGIN, LAST, MARKER, TYPE, param) \
+{ \
+ NAME* obj = (NAME*)1; \
+ SLANG_UNUSED(obj); \
+ fields.clear(); \
+ SLANG_FIELDS_ASTNode_##NAME(SLANG_AST_SERIAL_FIELD, param) \
+ outClasses[Index(ASTNodeType::NAME)] = _makeClass(arena, ASTNodeType::NAME, fields); \
+}
+
+struct ASTFieldAccess
+{
+ static void calcClasses(MemoryArena* arena, ASTSerialClass outClasses[Index(ASTNodeType::CountOf)])
+ {
+ List<ASTSerialField> fields;
+ SLANG_ALL_ASTNode_NodeBase(SLANG_AST_SERIAL_MAKE_CLASS, _)
+ }
+};
+
+ASTSerialClasses::ASTSerialClasses():
+ m_arena(2048)
+{
+ memset(m_classes, 0, sizeof(m_classes));
+ ASTFieldAccess::calcClasses(&m_arena, m_classes);
+
+ // Now work out the layout
+ for (Index i = 0; i < SLANG_COUNT_OF(m_classes); ++i)
+ {
+ // Set up each class in order, from lowest to highest index
+ // Doing so means super class is always setup
+ ASTSerialClass& serialClass = m_classes[i];
+
+ const ReflectClassInfo* info = ReflectClassInfo::getInfo(serialClass.type);
+
+ size_t maxAlignment = 1;
+ size_t offset = 0;
+
+ const ReflectClassInfo* superInfo = info->m_superClass;
+ if (superInfo)
+ {
+ ASTSerialClass& superSerialInfo = m_classes[superInfo->m_classId];
+
+ // If it's been setup, then alignment must be non zero.
+ // The ordering of ASTNodeType, should mean type have larger ASTNodeType greater than supers ASTNodeType.
+ SLANG_ASSERT(superSerialInfo.alignment != 0);
+
+ // Must be a power of 2
+ SLANG_ASSERT((superSerialInfo.alignment & (superSerialInfo.alignment - 1)) == 0);
+
+ maxAlignment = superSerialInfo.alignment;
+ offset = superSerialInfo.size;
+
+ // Check it is correctly aligned
+ SLANG_ASSERT((offset & (maxAlignment - 1)) == 0);
+ }
+
+ // Okay, go through fields setting their offset
+ ASTSerialField* fields = serialClass.fields;
+ for (Index j = 0; j < serialClass.fieldsCount; j++)
+ {
+ 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);
+
+ // Move past the field
+ offset += field.type->serialSizeInBytes;
+
+ // Calc the maximum alignment
+ maxAlignment = (alignment > maxAlignment) ? alignment : maxAlignment;
+ }
+
+ // Align with maximum alignment
+ offset = (offset + maxAlignment - 1) & ~(maxAlignment - 1);
+
+ serialClass.alignment = uint8_t(maxAlignment);
+ serialClass.size = uint32_t(offset);
+ }
+}
+
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ASTSerialWriter !!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ASTSerialWriter::ASTSerialWriter(ASTSerialClasses* classes, ASTSerialFilter* filter, DebugSerialWriter* debugWriter) :
+ m_arena(2048),
+ m_classes(classes),
+ m_filter(filter),
+ m_debugWriter(debugWriter)
+{
+ // 0 is always the null pointer
+ m_entries.add(nullptr);
+ m_ptrMap.Add(nullptr, 0);
+}
+
+ASTSerialIndex ASTSerialWriter::writePointer(const NodeBase* node)
+{
+ // This pointer cannot be in the map
+ SLANG_ASSERT(m_ptrMap.TryGetValue(node) == nullptr);
+
+ const ASTSerialClass* serialClass = m_classes->getSerialClass(node->astNodeType);
+
+ typedef ASTSerialInfo::NodeEntry NodeEntry;
+
+ 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->info = ASTSerialInfo::makeEntryInfo(serialClass->alignment);
+
+ auto index = _add(node, nodeEntry);
+
+ uint8_t* serialPayload = (uint8_t*)(nodeEntry + 1);
+
+ while (serialClass)
+ {
+ for (Index i = 0; i < serialClass->fieldsCount; ++i)
+ {
+ auto field = serialClass->fields[i];
+
+ // Work out the offsets
+ auto srcField = ((const uint8_t*)node) + field.nativeOffset;
+ auto dstField = serialPayload + field.serialOffset;
+
+ field.type->toSerialFunc(this, srcField, dstField);
+ }
+ // Get the super class
+ const ReflectClassInfo* reflectInfo = ReflectClassInfo::getInfo(serialClass->type);
+ const ReflectClassInfo* superReflectInfo = reflectInfo->m_superClass;
+
+ serialClass = superReflectInfo ? m_classes->getSerialClass(ASTNodeType(superReflectInfo->m_classId)) : nullptr;
+ }
+
+ return index;
+}
+
+void ASTSerialWriter::setPointerIndex(const NodeBase* ptr, ASTSerialIndex index)
+{
+ m_ptrMap.Add(ptr, Index(index));
+}
+
+ASTSerialIndex ASTSerialWriter::addPointer(const NodeBase* node)
+{
+ // Null is always 0
+ if (node == nullptr)
+ {
+ return ASTSerialIndex(0);
+ }
+ // Look up in the map
+ Index* indexPtr = m_ptrMap.TryGetValue(node);
+ if (indexPtr)
+ {
+ return ASTSerialIndex(*indexPtr);
+ }
+
+ if (m_filter)
+ {
+ return m_filter->writePointer(this, node);
+ }
+ else
+ {
+ return writePointer(node);
+ }
+}
+
+ASTSerialIndex ASTSerialWriter::addPointer(const RefObject* obj)
+{
+ // Null is always 0
+ if (obj == nullptr)
+ {
+ return ASTSerialIndex(0);
+ }
+ // Look up in the map
+ Index* indexPtr = m_ptrMap.TryGetValue(obj);
+ if (indexPtr)
+ {
+ return ASTSerialIndex(*indexPtr);
+ }
+
+ if (auto stringRep = dynamicCast<StringRepresentation>(obj))
+ {
+ ASTSerialIndex index = addString(StringRepresentation::asSlice(stringRep));
+ m_ptrMap.Add(obj, Index(index));
+ return index;
+ }
+ else if (auto breadcrumb = dynamicCast<LookupResultItem::Breadcrumb>(obj))
+ {
+ typedef ASTSerialTypeInfo<LookupResultItem::Breadcrumb> TypeInfo;
+ typedef ASTSerialInfo::RefObjectEntry RefObjectEntry;
+
+ size_t alignment = TypeInfo::SerialAlignment;
+ alignment = (alignment < SLANG_ALIGN_OF(ASTSerialInfo::RefObjectEntry)) ? SLANG_ALIGN_OF(ASTSerialInfo::RefObjectEntry) : alignment;
+
+ RefObjectEntry* entry = (RefObjectEntry*)m_arena.allocateAligned(sizeof(RefObjectEntry) + sizeof(TypeInfo::SerialType), alignment);
+
+ 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, entry + 1);
+ return index;
+ }
+ else if (auto name = dynamicCast<const Name>(obj))
+ {
+ return addName(name);
+ }
+ else if (auto scope = dynamicCast<Scope>(obj))
+ {
+ // We don't serialize scope
+ return ASTSerialIndex(0);
+ }
+ else if (auto module = dynamicCast<Module>(obj))
+ {
+ // We don't serialize Module
+ return ASTSerialIndex(0);
+ }
+
+ SLANG_ASSERT(!"Unhandled type");
+ return ASTSerialIndex(0);
+}
+
+ASTSerialIndex ASTSerialWriter::addString(const UnownedStringSlice& slice)
+{
+ typedef ByteEncodeUtil Util;
+ typedef ASTSerialInfo::StringEntry StringEntry;
+
+ if (slice.getLength() == 0)
+ {
+ return ASTSerialIndex(0);
+ }
+
+ Index newIndex = m_entries.getCount();
+
+ Index* indexPtr = m_sliceMap.TryGetValueOrAdd(slice, newIndex);
+ if (indexPtr)
+ {
+ return ASTSerialIndex(*indexPtr);
+ }
+
+ // Okay we need to add the string
+
+ uint8_t encodeBuf[Util::kMaxLiteEncodeUInt32];
+ const int encodeCount = Util::encodeLiteUInt32(uint32_t(slice.getLength()), encodeBuf);
+
+ 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->sizeAndChars);
+ for (int i = 0; i < encodeCount; ++i)
+ {
+ dst[i] = encodeBuf[i];
+ }
+
+ memcpy(dst + encodeCount, slice.begin(), slice.getLength());
+
+ m_entries.add(entry);
+ return ASTSerialIndex(newIndex);
+}
+
+
+ASTSerialIndex ASTSerialWriter::addString(const String& in)
+{
+ return addPointer(in.getStringRepresentation());
+}
+
+ASTSerialIndex ASTSerialWriter::addName(const Name* name)
+{
+ if (name == nullptr)
+ {
+ return ASTSerialIndex(0);
+ }
+
+ // Look it up
+ Index* indexPtr = m_ptrMap.TryGetValue(name);
+ if (indexPtr)
+ {
+ return ASTSerialIndex(*indexPtr);
+ }
+
+ ASTSerialIndex index = addString(name->text);
+ m_ptrMap.Add(name, Index(index));
+ return index;
+}
+
+ASTSerialSourceLoc ASTSerialWriter::addSourceLoc(SourceLoc sourceLoc)
+{
+ if (sourceLoc.isValid() && m_debugWriter)
+ {
+ return m_debugWriter->addSourceLoc(sourceLoc);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+ASTSerialIndex ASTSerialWriter::_addArray(size_t elementSize, size_t alignment, const void* elements, Index elementCount)
+{
+ typedef ASTSerialInfo::ArrayEntry Entry;
+
+ if (elementCount == 0)
+ {
+ 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, alignment);
+
+ entry->type = ASTSerialInfo::Type::Array;
+ 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);
+ return ASTSerialIndex(m_entries.getCount() - 1);
+}
+
+static const uint8_t s_fixBuffer[ASTSerialInfo::MAX_ALIGNMENT]{ 0, };
+
+SlangResult ASTSerialWriter::write(Stream* stream)
+{
+ const Int entriesCount = m_entries.getCount();
+
+ // Add a sentinal so we don't need special handling for
+ ASTSerialInfo::Entry sentinal;
+ sentinal.type = ASTSerialInfo::Type::String;
+ sentinal.info = ASTSerialInfo::EntryInfo::Alignment1;
+
+ 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;
+
+
+ {
+ 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(s_fixBuffer, alignmentFixSize);
+ }
+ }
+ catch (const IOException&)
+ {
+ return SLANG_FAIL;
+ }
+
+ // Onto next
+ offset = nextOffset;
+ entry = next;
+ }
+ }
+
+ return SLANG_OK;
+}
+
+SlangResult ASTSerialWriter::writeIntoContainer(RiffContainer* container)
+{
+ typedef RiffContainer::Chunk Chunk;
+ typedef RiffContainer::ScopeChunk ScopeChunk;
+
+ // This is the container for the AST Data
+ ScopeChunk scopeModule(container, Chunk::Kind::List, ASTSerialBinary::kSlangASTModuleFourCC);
+ {
+ ScopeChunk scopeData(container, Chunk::Kind::Data, ASTSerialBinary::kSlangASTModuleDataFourCC);
+
+ {
+ // Sentinal so we don't need special handling for end of list
+ ASTSerialInfo::Entry sentinal;
+ sentinal.type = ASTSerialInfo::Type::String;
+ sentinal.info = ASTSerialInfo::EntryInfo::Alignment1;
+
+ size_t offset = 0;
+ const Int entriesCount = m_entries.getCount();
+
+ {
+ m_entries.add(&sentinal);
+ m_entries.removeLast();
+ // 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.
+ m_entries.getBuffer()[entriesCount] = &sentinal;
+ }
+
+ ASTSerialInfo::Entry*const* entries = m_entries.getBuffer();
+
+ 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);
+
+ container->write(entry, entrySize);
+ if (alignmentFixSize)
+ {
+ container->write(s_fixBuffer, alignmentFixSize);
+ }
+
+ // 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)
+{
+ 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)
+{
+ 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)
+{
+ if (index == ASTSerialIndex(0))
+ {
+ 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_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)
+{
+ return (loc && m_debugReader) ? m_debugReader->getSourceLoc(loc) : 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, 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 ASTSerialTestUtil::selfTest()
+{
+ RefPtr<ASTSerialClasses> classes = new ASTSerialClasses;
+
+ {
+ struct Thing
+ {
+ Module* node;
+ };
+ Thing thing;
+
+ //Pointer pointer(thing.node);
+
+ auto field = _calcField("node", thing.node);
+
+
+ const ASTSerialType* type = ASTSerialGetType<Type*>::getType();
+ SLANG_UNUSED(type);
+ }
+
+ {
+ const ASTSerialType* type = ASTSerialGetType<int[10]>::getType();
+ SLANG_UNUSED(type);
+ }
+
+ {
+ const ASTSerialType* type = ASTSerialGetType<bool[3]>::getType();
+ SLANG_UNUSED(type);
+ }
+
+ {
+ const ASTSerialType* type = ASTSerialGetType<Type*[3]>::getType();
+ SLANG_UNUSED(type);
+ }
+
+ return SLANG_OK;
+}
+
+/* static */SlangResult ASTSerialTestUtil::testSerialize(NodeBase* node, RootNamePool* rootNamePool, SharedASTBuilder* sharedASTBuilder, SourceManager* sourceManager)
+{
+ RefPtr<ASTSerialClasses> classes = new ASTSerialClasses;
+
+ List<uint8_t> contents;
+
+ {
+ OwnedMemoryStream stream(FileAccess::ReadWrite);
+
+ ModuleDecl* moduleDecl = as<ModuleDecl>(node);
+ ModuleASTSerialFilter filterStorage(moduleDecl);
+
+ ASTSerialFilter* filter = moduleDecl ? &filterStorage : nullptr;
+
+ ASTSerialWriter writer(classes, filter, nullptr);
+
+ // Lets serialize it all
+ writer.addPointer(node);
+ // Let's stick it all in a stream
+ writer.write(&stream);
+
+ stream.swapContents(contents);
+
+ NamePool namePool;
+ namePool.setRootNamePool(rootNamePool);
+
+ ASTSerialReader reader(classes, nullptr);
+
+ ASTBuilder builder(sharedASTBuilder, "Serialize Check");
+
+ // We could now check that the loaded data matches
+
+ {
+ const List<ASTSerialInfo::Entry*>& writtenEntries = writer.getEntries();
+ List<const ASTSerialInfo::Entry*> readEntries;
+
+ SlangResult res = reader.loadEntries(contents.getBuffer(), contents.getCount(), readEntries);
+ SLANG_UNUSED(res);
+
+ SLANG_ASSERT(writtenEntries.getCount() == readEntries.getCount());
+
+ // They should be identical up to the
+ for (Index i = 1; i < readEntries.getCount(); ++i)
+ {
+ auto writtenEntry = writtenEntries[i];
+ auto readEntry = readEntries[i];
+
+ const size_t writtenSize = writtenEntry->calcSize(classes);
+ const size_t readSize = readEntry->calcSize(classes);
+
+ SLANG_ASSERT(readSize == writtenSize);
+ // Check the payload is the same
+ SLANG_ASSERT(memcmp(readEntry, writtenEntry, readSize) == 0);
+ }
+
+ }
+
+ {
+ SlangResult res = reader.load(contents.getBuffer(), contents.getCount(), &builder, &namePool);
+ SLANG_UNUSED(res);
+ }
+
+ // Lets see what we have
+ const ASTDumpUtil::Flags dumpFlags = ASTDumpUtil::Flag::HideSourceLoc | ASTDumpUtil::Flag::HideScope;
+
+ String readDump;
+ {
+ SourceWriter sourceWriter(sourceManager, LineDirectiveMode::None);
+ ASTDumpUtil::dump(reader.getPointer(ASTSerialIndex(1)).dynamicCast<NodeBase>(), ASTDumpUtil::Style::Hierachical, dumpFlags, &sourceWriter);
+ readDump = sourceWriter.getContentAndClear();
+
+ }
+ String origDump;
+ {
+ SourceWriter sourceWriter(sourceManager, LineDirectiveMode::None);
+ ASTDumpUtil::dump(node, ASTDumpUtil::Style::Hierachical, dumpFlags, &sourceWriter);
+ origDump = sourceWriter.getContentAndClear();
+ }
+
+ // Write out
+ File::writeAllText("ast-read.ast-dump", readDump);
+ File::writeAllText("ast-orig.ast-dump", origDump);
+
+
+ if (readDump != origDump)
+ {
+ return SLANG_FAIL;
+ }
+ }
+
+ return SLANG_OK;
+}
+
+
+} // namespace Slang