summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2021-06-18 17:09:35 -0400
committerGitHub <noreply@github.com>2021-06-18 17:09:35 -0400
commit0afa24a3fe7d0e1787cc909f9c7641f477c30e5c (patch)
tree08724f28641b221167a03ca2192ee8297328becd /source
parent89051251016be7d3798c0b9586c6db7b4ed5f21d (diff)
StructTag versioning (#1888)
* #include an absolute path didn't work - because paths were taken to always be relative. * WIP Abi struct. * Use AbiSystem on SessionDesc. * Use mask/shift constants. * Fix issue causing warning on linux. * Abi -> Api. * Fix typo. * Refactor to use StructTag. * Mechanism to be able to follow fields. * Field adding is working. * WIP with StructTagConverter. * First pass of StructTag appears to work. Still needs diagnostics. * Small tidy up around Field. * Use bit field to record what fields are recorded to remove allocation around the m_stack. Use ScopeStack for RAII. * Return SlangResult instead of pointers. * Use SlangResult with copy. * Split StructTagConverter implementations. * Fix some bugs around lazy converting. * First pass at unit test for StructTag. * Testing StructTag going backwards in time. * First pass as StructTag diagnostics. * Make Traits a namespace. * Fix some issues with Traits not being a class. * Fix 32 bit warning.
Diffstat (limited to 'source')
-rw-r--r--source/compiler-core/slang-misc-diagnostic-defs.h5
-rw-r--r--source/compiler-core/slang-struct-tag-converter.cpp538
-rw-r--r--source/compiler-core/slang-struct-tag-converter.h153
-rw-r--r--source/compiler-core/slang-struct-tag-system.cpp126
-rw-r--r--source/compiler-core/slang-struct-tag-system.h259
-rwxr-xr-xsource/slang/slang-compiler.h9
-rw-r--r--source/slang/slang.cpp66
7 files changed, 1138 insertions, 18 deletions
diff --git a/source/compiler-core/slang-misc-diagnostic-defs.h b/source/compiler-core/slang-misc-diagnostic-defs.h
index aa87f02f9..f3ac0bae5 100644
--- a/source/compiler-core/slang-misc-diagnostic-defs.h
+++ b/source/compiler-core/slang-misc-diagnostic-defs.h
@@ -30,6 +30,11 @@ DIAGNOSTIC(100002, Error, unbalancedDownstreamArguments, "unbalanced downstream
DIAGNOSTIC(100003, Error, closeOfUnopenDownstreamArgs, "close of an unopen downstream argument scope")
DIAGNOSTIC(100004, Error, downstreamToolNameNotDefined, "downstream tool name not defined")
+DIAGNOSTIC(110001, Error, structTagConversionFailureNoArena, "StructTag conversion failed - no arena")
+DIAGNOSTIC(110002, Error, unknownStructTag, "unknown StructTag $0")
+DIAGNOSTIC(110003, Error, cannotConvertStructTag, "cannot convert StructTag $0 to $1")
+DIAGNOSTIC(110004, Error, cannotConvertDifferentStructTag, "cannot convert different StructTag types $0 to $1")
+
DIAGNOSTIC(99999, Note, noteLocationOfInternalError, "an internal error threw an exception while working on code near this location")
#undef DIAGNOSTIC
diff --git a/source/compiler-core/slang-struct-tag-converter.cpp b/source/compiler-core/slang-struct-tag-converter.cpp
new file mode 100644
index 000000000..9e891e642
--- /dev/null
+++ b/source/compiler-core/slang-struct-tag-converter.cpp
@@ -0,0 +1,538 @@
+#include "slang-struct-tag-converter.h"
+
+#include "slang-core-diagnostics.h"
+
+namespace Slang {
+
+static Index _getCount(const StructTagType::Field& field, const void* in)
+{
+ typedef StructTagField::Type FieldType;
+
+ const uint8_t* ptr = (const uint8_t*)in;
+
+ switch (field.m_countType)
+ {
+ case FieldType::I32: return Index(*(const int32_t*)(ptr + field.m_countOffset));
+ case FieldType::I64: return Index(*(const int64_t*)(ptr + field.m_countOffset));
+ default: break;
+ }
+
+ SLANG_ASSERT(!"Cannot access as count");
+ return -1;
+}
+
+SlangResult StructTagConverterBase::_requireArena()
+{
+ if (!m_arena)
+ {
+ if (m_sink)
+ {
+ // Diagnose that we need an arena
+ m_sink->diagnose(SourceLoc(), MiscDiagnostics::structTagConversionFailureNoArena);
+ }
+ return SLANG_FAIL;
+ }
+
+ return SLANG_OK;
+}
+
+SlangResult StructTagConverterBase::_diagnoseCantConvert(slang::StructTag tag, StructTagType* structType)
+{
+ SLANG_UNUSED(tag);
+ SLANG_UNUSED(structType);
+
+ if (m_sink)
+ {
+ // Diagnose why the tag couldn't be converted
+ StringBuilder from, to;
+ m_system->appendName(tag, from);
+ m_system->appendName(structType->m_tag, to);
+
+ m_sink->diagnose(SourceLoc(), MiscDiagnostics::cannotConvertStructTag, from, to);
+ }
+
+ return SLANG_E_STRUCT_TAG_INCOMPATIBLE;
+}
+
+SlangResult StructTagConverterBase::_diagnoseUnknownType(slang::StructTag tag)
+{
+ SLANG_UNUSED(tag);
+
+ if (m_sink)
+ {
+ StringBuilder buf;
+ m_system->appendName(tag, buf);
+ m_sink->diagnose(SourceLoc(), MiscDiagnostics::unknownStructTag, buf);
+ }
+
+ // Perhaps this isn't quite right - but it's probably the most suitable error
+ return SLANG_E_STRUCT_TAG_INCOMPATIBLE;
+}
+
+SlangResult StructTagConverterBase::_diagnoseDifferentTypes(slang::StructTag tagA, slang::StructTag tagB)
+{
+ if (m_sink)
+ {
+ StringBuilder a, b;
+ m_system->appendName(tagA, a);
+ m_system->appendName(tagB, b);
+
+ m_sink->diagnose(SourceLoc(), MiscDiagnostics::cannotConvertDifferentStructTag, a, b);
+ }
+
+ // Perhaps this isn't quite right - but it's probably the most suitable error
+ return SLANG_E_STRUCT_TAG_INCOMPATIBLE;
+}
+
+bool StructTagConverterBase::canConvertToCurrent(slang::StructTag tag, StructTagType* type) const
+{
+ // Means can be used without any modification
+ if (StructTagUtil::isReadCompatible(tag, type->m_tag))
+ {
+ return true;
+ }
+
+ // We may want to allow zero extension, or initialization
+ // We can accept for conversion if it's the same type with only difference being the minor version.
+ return StructTagUtil::areSameMajorType(tag, type->m_tag);
+}
+
+void StructTagConverterBase::copy(const StructTagType* structType, const void* src, void* dst)
+{
+ const slang::TaggedStructBase* srcBase = reinterpret_cast<const slang::TaggedStructBase*>(src);
+
+ const slang::StructSize size = std::min(structType->m_sizeInBytes, srcBase->structSize);
+
+ // Copy
+ ::memcpy(dst, src, size);
+
+ // TODO(JS): Alternatively if we have the default set on the structType, we could initialize
+ // other fields with the default values. For the moment we zero
+
+ // Zero any extra
+ if (size < structType->m_sizeInBytes)
+ {
+ ::memset((char*)dst + size, 0, structType->m_sizeInBytes - size);
+ }
+
+ // Set the type and the size
+ slang::TaggedStructBase* dstBase = reinterpret_cast<slang::TaggedStructBase*>(dst);
+ dstBase->structTag = structType->m_tag;
+ dstBase->structSize = structType->m_sizeInBytes;
+}
+
+void* StructTagConverterBase::allocateAndCopy(const StructTagType* structType, const void* src)
+{
+ uint8_t* dst = (uint8_t*)m_arena->allocate(structType->m_sizeInBytes);
+ copy(structType, src, dst);
+ return dst;
+}
+
+SlangResult StructTagConverterBase::convertCurrent(slang::StructTag tag, const void* in, void*& out)
+{
+ auto base = reinterpret_cast<const slang::TaggedStructBase*>(in);
+ auto inTag = base->structTag;
+
+ // Currently we can only convert if same major type.
+ // In future it might be possible to improve on this
+ if (!StructTagUtil::areSameMajorType(inTag, tag))
+ {
+ return _diagnoseDifferentTypes(inTag, tag);
+ }
+
+ return convertCurrent(in, out);
+}
+
+SlangResult StructTagConverterBase::convertArrayField(const FieldType type, const void* in, Index count, void*& out)
+{
+ if (count <= 0)
+ {
+ out = const_cast<void*>(in);
+ return SLANG_OK;
+ }
+
+ SLANG_ASSERT(in);
+ switch (type)
+ {
+ case FieldType::PtrTaggedStruct: return convertCurrentArray(in, count, out);
+ case FieldType::PtrPtrTaggedStruct: return convertCurrentPtrArray((const void*const*)in, count, (void**&)out);
+ default: break;
+ }
+ return SLANG_FAIL;
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ CopyStructTagConverter
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+SlangResult CopyStructTagConverter::convertCurrentPtrArray(const void*const* in, Index count, void**& out)
+{
+ if (count == 0)
+ {
+ out = nullptr;
+ return SLANG_OK;
+ }
+ void** dst = (void**)m_arena->allocate(count * sizeof(void*));
+ for (Index i = 0; i < count; ++i)
+ {
+ SLANG_RETURN_ON_FAIL(convertCurrent(in[i], dst[i]));
+ }
+ out = dst;
+ return SLANG_OK;
+}
+
+SlangResult CopyStructTagConverter::convertCurrentArray(const void* in, Index count, void*& out)
+{
+ if (count <= 0)
+ {
+ out = nullptr;
+ return SLANG_OK;
+ }
+
+ if (count == 1)
+ {
+ return convertCurrent(in, out);
+ }
+
+ const slang::TaggedStructBase* arr = reinterpret_cast<const slang::TaggedStructBase*>(in);
+
+ // We assume all have the same size/type
+ auto tag = arr[0].structTag;
+
+ auto structType = m_system->getType(tag);
+
+ if (!structType)
+ {
+ return _diagnoseUnknownType(tag);
+ }
+ if (!canConvertToCurrent(tag, structType))
+ {
+ return _diagnoseCantConvert(tag, structType);
+ }
+
+ const size_t dstStride = structType->m_sizeInBytes;
+ uint8_t*const dstStart = (uint8_t*)m_arena->allocate(dstStride * count);
+ uint8_t* dst = dstStart;
+
+ size_t srcStride = arr[0].structSize;
+ const uint8_t* src = reinterpret_cast<const uint8_t*>(in);
+
+ for (Index i = 0; i < count; ++i)
+ {
+ copy(structType, src, dst);
+ SLANG_RETURN_ON_FAIL(convertCurrentContained(structType, dst));
+
+ src += srcStride;
+ dst += dstStride;
+ }
+
+ out = dstStart;
+ return SLANG_OK;
+}
+
+SlangResult CopyStructTagConverter::convertCurrentContained(const StructTagType* structType, void* inout)
+{
+ // Convert primary
+ if (StructTagUtil::isPrimary(structType->m_tag))
+ {
+ // Copy extensions if needed
+ slang::PrimaryTaggedStruct* primary = reinterpret_cast<slang::PrimaryTaggedStruct*>(inout);
+ if (primary->extsCount > 0)
+ {
+ void** dstExts;
+ SLANG_RETURN_ON_FAIL(convertCurrentPtrArray((const void*const*)primary->exts, primary->extsCount, dstExts));
+ primary->exts = (const slang::StructTag**)dstExts;
+ }
+ }
+
+ // It may have fields that need to be converted
+ for (const auto& field : structType->m_fields)
+ {
+ const Index count = _getCount(field, inout);
+ if (count)
+ {
+ void*& ptrRef = *(void**)(reinterpret_cast<uint8_t*>(inout) + field.m_offset);
+ SLANG_RETURN_ON_FAIL(convertArrayField(field.m_type, ptrRef, count, ptrRef));
+ }
+ }
+
+ return SLANG_OK;
+}
+
+SlangResult CopyStructTagConverter::convertCurrent(const void* in, void*& out)
+{
+ auto tag = reinterpret_cast<const slang::TaggedStructBase*>(in)->structTag;
+
+ auto structType = m_system->getType(tag);
+ if (!structType)
+ {
+ return _diagnoseUnknownType(tag);
+ }
+ if (!canConvertToCurrent(tag, structType))
+ {
+ return _diagnoseCantConvert(tag, structType);
+ }
+
+ slang::TaggedStructBase* dst = (slang::TaggedStructBase*)allocateAndCopy(structType, in);
+ SLANG_RETURN_ON_FAIL(convertCurrentContained(structType, dst));
+ out = dst;
+ return SLANG_OK;
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+ LazyStructTagConverter
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+SlangResult LazyStructTagConverter::convertCurrentPtrArray(const void*const* in, Index count, void**& out)
+{
+ if (count == 0)
+ {
+ out = (void**)in;
+ return SLANG_OK;
+ }
+
+ Index numConverted = 0;
+ ScopeStack stackScope(this);
+
+ for (Index i = 0; i < count; ++i)
+ {
+ const void* src = in[i];
+ void* dst;
+ SLANG_RETURN_ON_FAIL(convertCurrent(src, dst));
+
+ // Make space if not set up
+ numConverted += Index(dst != src);
+ m_convertStack.add(dst);
+ }
+
+ // We need to make a copy of the exts
+ if (numConverted)
+ {
+ SLANG_RETURN_ON_FAIL(_requireArena());
+ out = (void**)m_arena->allocateAndCopyArray(m_convertStack.getBuffer() + stackScope.getStartIndex(), count);
+ }
+ else
+ {
+ out = (void**)in;
+ }
+
+ return SLANG_OK;
+}
+
+void LazyStructTagConverter::setContainedConverted(const StructTagType* structType, Index stackIndex, BitField fieldsSet, void* out)
+{
+ if (fieldsSet == 0)
+ {
+ return;
+ }
+
+ if (StructTagUtil::isPrimary(structType->m_tag))
+ {
+ if (fieldsSet & 1)
+ {
+ // Copy extensions if needed
+ slang::PrimaryTaggedStruct* primary = reinterpret_cast<slang::PrimaryTaggedStruct*>(out);
+ primary->exts = (const slang::StructTag**)m_convertStack[stackIndex++];
+ }
+ fieldsSet >>= 1;
+ }
+
+ {
+ for (const auto& field : structType->m_fields)
+ {
+ if (fieldsSet == 0)
+ {
+ return;
+ }
+
+ // If the field is set, copy
+ if (fieldsSet & 1)
+ {
+ switch (field.m_type)
+ {
+ case FieldType::PtrTaggedStruct:
+ case FieldType::PtrPtrTaggedStruct:
+ {
+ *(const void**)(reinterpret_cast<const uint8_t*>(out) + field.m_offset) = m_convertStack[stackIndex++];
+ break;
+ }
+ default: break;
+ }
+ }
+
+ // Remove the bit
+ fieldsSet >>= 1;
+ }
+ }
+}
+
+
+SlangResult LazyStructTagConverter::maybeConvertCurrentContained(const StructTagType* structType, const void* in, BitField* outFieldsSet)
+{
+ BitField fieldsSet = 0;
+ BitField bit = 1;
+
+ if (StructTagUtil::isPrimary(structType->m_tag))
+ {
+ // Copy extensions if needed
+ const slang::PrimaryTaggedStruct* primary = reinterpret_cast<const slang::PrimaryTaggedStruct*>(in);
+ if (primary->extsCount > 0)
+ {
+ auto srcExts = (const void*const*)primary->exts;
+ void** dstExts;
+ SLANG_RETURN_ON_FAIL(convertCurrentPtrArray(srcExts, primary->extsCount, dstExts));
+
+ if (dstExts != srcExts)
+ {
+ m_convertStack.add(dstExts);
+ fieldsSet |= bit;
+ }
+ }
+
+ bit += bit;
+ }
+
+ // It may have fields that need to be converted
+
+ {
+ for (const auto& field : structType->m_fields)
+ {
+ const Index count = _getCount(field, in);
+ if (count > 0)
+ {
+ void* dst = nullptr;
+ const void* src = *(const void**)(reinterpret_cast<const uint8_t*>(in) + field.m_offset);
+ SLANG_RETURN_ON_FAIL(convertArrayField(field.m_type, src, count, dst));
+
+ if (dst != src)
+ {
+ // Set the and add to the stack
+ m_convertStack.add(dst);
+ fieldsSet |= bit;
+ }
+ }
+
+ bit += bit;
+ }
+ }
+
+ *outFieldsSet = fieldsSet;
+ return SLANG_OK;
+}
+
+SlangResult LazyStructTagConverter::convertCurrentArray(const void* in, Index count, void*& out)
+{
+ if (count == 0)
+ {
+ out = (void*)in;
+ return SLANG_OK;
+ }
+
+ if (count == 1)
+ {
+ return convertCurrent(in, out);
+ }
+
+ const slang::TaggedStructBase* arr = reinterpret_cast<const slang::TaggedStructBase*>(in);
+
+ // We assume all have the same size/type
+ auto tag = arr[0].structTag;
+
+ auto structType = m_system->getType(tag);
+
+ if (!structType)
+ {
+ return _diagnoseUnknownType(tag);
+ }
+
+ if (StructTagUtil::areSameMajorType(tag, structType->m_tag) && structType->m_sizeInBytes == arr[0].structSize)
+ {
+ // Can just use what was passed in
+ out = (void*)in;
+ return SLANG_OK;
+ }
+
+ if (!canConvertToCurrent(tag, structType))
+ {
+ return _diagnoseCantConvert(tag, structType);
+ }
+
+ SLANG_RETURN_ON_FAIL(_requireArena());
+
+ const size_t dstStride = structType->m_sizeInBytes;
+ uint8_t*const dstStart = (uint8_t*)m_arena->allocate(dstStride * count);
+ uint8_t* dst = dstStart;
+
+ size_t srcStride = arr[0].structSize;
+ const uint8_t* src = reinterpret_cast<const uint8_t*>(in);
+
+ for (Index i = 0; i < count; ++i)
+ {
+ // Do the straight copy
+ copy(structType, src, dst);
+
+ // Work out what was converted
+ ScopeStack scopeStack(this);
+ BitField fieldsSet;
+ SLANG_RETURN_ON_FAIL(maybeConvertCurrentContained(structType, in, &fieldsSet));
+
+ // Copy anything converted
+ setContainedConverted(structType, scopeStack, fieldsSet, dst);
+
+ src += srcStride;
+ dst += dstStride;
+ }
+
+ out = dstStart;
+ return SLANG_OK;
+}
+
+SlangResult LazyStructTagConverter::convertCurrent(const void* in, void*& out)
+{
+ auto base = reinterpret_cast<const slang::TaggedStructBase*>(in);
+ auto tag = base->structTag;
+
+ // If can't find the type it's incompatible
+ auto structType = m_system->getType(tag);
+ if (!structType)
+ {
+ return _diagnoseUnknownType(tag);
+ }
+
+ if (!canConvertToCurrent(tag, structType))
+ {
+ return _diagnoseCantConvert(tag, structType);
+ }
+
+ // Let's see if how everything contained converts
+ ScopeStack stackScope(this);
+ BitField fieldsSet;
+ SLANG_RETURN_ON_FAIL(maybeConvertCurrentContained(structType, in, &fieldsSet));
+
+ // If there were no fields set
+ if (fieldsSet == 0)
+ {
+ // And it's the same major type, and is at least as big a the current struct
+ // We can just use as is
+ if (StructTagUtil::areSameMajorType(tag, structType->m_tag) && base->structSize >= structType->m_sizeInBytes)
+ {
+ out = (void*)in;
+ return SLANG_OK;
+ }
+ }
+
+ // Okay we will need to allocate and copy
+ void* dst = allocateAndCopy(structType, in);
+
+ // Copy anything converted
+ setContainedConverted(structType, stackScope, fieldsSet, dst);
+
+ out = dst;
+ return SLANG_OK;
+}
+
+} // namespace Slang
diff --git a/source/compiler-core/slang-struct-tag-converter.h b/source/compiler-core/slang-struct-tag-converter.h
new file mode 100644
index 000000000..5f0ec7087
--- /dev/null
+++ b/source/compiler-core/slang-struct-tag-converter.h
@@ -0,0 +1,153 @@
+#ifndef SLANG_COMPILER_CORE_STRUCT_TAG_CONVERTER_H
+#define SLANG_COMPILER_CORE_STRUCT_TAG_CONVERTER_H
+
+#include "slang-struct-tag-system.h"
+#include "slang-diagnostic-sink.h"
+
+namespace Slang {
+
+class StructTagConverterBase
+{
+public:
+ typedef StructTagConverterBase ThisType;
+ typedef StructTagField Field;
+ typedef Field::Type FieldType;
+
+ virtual SlangResult convertCurrent(const void* in, void*& out) = 0;
+ virtual SlangResult convertCurrentArray(const void* in, Index count, void*& out) = 0;
+ virtual SlangResult convertCurrentPtrArray(const void*const* in, Index count, void**& out) = 0;
+
+ SlangResult convertCurrent(slang::StructTag tag, const void* in, void*& out);
+
+ /// Allocates of type and copies src to dst
+ void* allocateAndCopy(const StructTagType* type, const void* src);
+ /// Copy from src to dst, zero extending or shrinking however structType requires
+ void copy(const StructTagType* structType, const void* src, void* dst);
+
+ /// Returns true if it's possible to convert tag to current type
+ bool canConvertToCurrent(slang::StructTag tag, StructTagType* type) const;
+
+ template <typename T>
+ const T* convertToCurrent(const void* in)
+ {
+ void* dst;
+ return SLANG_SUCCEEDED(convertCurrent(T::kStructTag, in, dst)) ? (const T*)dst : nullptr;
+ }
+
+ template <typename T>
+ SlangResult convertToCurrent(const void* in, const T** out)
+ {
+ void* dst;
+ SLANG_RETURN_ON_FAIL(convertCurrent(T::kStructTag, in, dst));
+ *out = (const T*)dst;
+ return SLANG_OK;
+ }
+
+ /// Convert a single field which is an array type
+ SlangResult convertArrayField(const FieldType type, const void* in, Index count, void*& out);
+
+ /// Ctor. Arena and sink can be optionally set (pass nullptr if not wanted)
+ StructTagConverterBase(StructTagSystem* system, MemoryArena* arena, DiagnosticSink* sink) :
+ m_system(system),
+ m_arena(arena),
+ m_sink(sink)
+ {
+ }
+
+protected:
+ StructTagConverterBase(const ThisType& rhs) = delete;
+ void operator=(const ThisType& rhs) = delete;
+
+ SlangResult _requireArena();
+ SlangResult _diagnoseCantConvert(slang::StructTag tag, StructTagType* type);
+ SlangResult _diagnoseUnknownType(slang::StructTag tag);
+ SlangResult _diagnoseDifferentTypes(slang::StructTag tagA, slang::StructTag tagB);
+
+ StructTagSystem* m_system;
+ DiagnosticSink* m_sink;
+ MemoryArena* m_arena;
+};
+
+class CopyStructTagConverter : public StructTagConverterBase
+{
+public:
+ typedef StructTagConverterBase Super;
+
+ // StructTagConverterBase
+ virtual SlangResult convertCurrent(const void* in, void*& out) SLANG_OVERRIDE;
+ virtual SlangResult convertCurrentArray(const void* in, Index count, void*& out) SLANG_OVERRIDE;
+ virtual SlangResult convertCurrentPtrArray(const void*const* in, Index count, void**& out) SLANG_OVERRIDE;
+
+ /// Convert the items contained in inout
+ SlangResult convertCurrentContained(const StructTagType* structType, void* inout);
+
+ CopyStructTagConverter(StructTagSystem* system, MemoryArena* arena, DiagnosticSink* sink) :
+ Super(system, arena, sink)
+ {
+ // If we are going to copy -> we have to have an arena
+ SLANG_ASSERT(arena);
+ }
+};
+
+class LazyStructTagConverter : public StructTagConverterBase
+{
+public:
+ typedef StructTagConverterBase Super;
+
+ typedef uint32_t BitField;
+
+ struct ScopeStack
+ {
+ ScopeStack(LazyStructTagConverter* converter):
+ m_stack(converter->m_convertStack),
+ m_startIndex(converter->m_convertStack.getCount())
+ {
+ }
+ ~ScopeStack()
+ {
+ m_stack.setCount(m_startIndex);
+ }
+
+ Index getStartIndex() const { return m_startIndex; }
+ operator Index() const { return m_startIndex; }
+
+ protected:
+ List<void*>& m_stack;
+ Index m_startIndex;
+ };
+
+ // StructTagConverterBase
+ virtual SlangResult convertCurrent(const void* in, void*& out) SLANG_OVERRIDE;
+ virtual SlangResult convertCurrentArray(const void* in, Index count, void*& out) SLANG_OVERRIDE;
+ virtual SlangResult convertCurrentPtrArray(const void*const* in, Index count, void**& out) SLANG_OVERRIDE;
+
+ /// Convert all the referenced items starting at in.
+ /// Items that are converted are stored on the m_convertStack.
+ /// The BitField records a bit for every 'field' (where exts is the 0 field) where there is something converted.
+ /// If the BitField has no bits set -> then nothing was converted and can be used as is.
+ /// To write the converted data, use setContainedConverted.
+ ///
+ /// NOTE! This method adds items to the end of the m_convertStack, it is the responsibility of the caller to clean up
+ /// This can be made simpler by just using ScopeStack.
+ SlangResult maybeConvertCurrentContained(const StructTagType* structType, const void* in, BitField* outFieldsSet);
+
+ /// For every fieldSet bit set, copys over the data held in the m_convertStack (indexed from stackStartIndex).
+ void setContainedConverted(const StructTagType* structType, Index stackIndex, BitField fieldsSet, void* dst);
+
+ /// Ctor. The sink and arena are optional. If the arena isn't set then it is not possible to copy convert anything
+ /// and so if a copy convert is required, it will fail.
+ /// The sink is optional - if it's set failures will occur silently.
+ LazyStructTagConverter(StructTagSystem* system, MemoryArena* arena, DiagnosticSink* sink):
+ Super(system, arena, sink)
+ {
+ }
+
+protected:
+
+ /// Used to hold pointers to things that have been converted.
+ List<void*> m_convertStack;
+};
+
+} // namespace Slang
+
+#endif // SLANG_COMPILER_CORE_STRUCT_TAG_CONVERTER_H
diff --git a/source/compiler-core/slang-struct-tag-system.cpp b/source/compiler-core/slang-struct-tag-system.cpp
new file mode 100644
index 000000000..405d17360
--- /dev/null
+++ b/source/compiler-core/slang-struct-tag-system.cpp
@@ -0,0 +1,126 @@
+#include "slang-struct-tag-system.h"
+
+
+namespace Slang {
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! StructTagCategoryInfo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+StructTagCategoryInfo::~StructTagCategoryInfo()
+{
+ for (auto type : m_types)
+ {
+ if (type)
+ {
+ type->~StructTagType();
+ }
+ }
+
+}
+
+void StructTagCategoryInfo::addType(StructTagType* type)
+{
+ auto typeIndex = StructTagUtil::getTypeIndex(type->m_tag);
+
+ if (typeIndex >= m_types.getCount())
+ {
+ Index prevCount = m_types.getCount();
+ m_types.setCount(typeIndex + 1);
+ // Zero it
+ ::memset(m_types.getBuffer() + prevCount, 0, sizeof(StructTagType*) * (m_types.getCount() - prevCount));
+ }
+
+ SLANG_ASSERT(m_types[typeIndex] == nullptr);
+ m_types[typeIndex] = type;
+}
+
+/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! StructTagSystem !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
+
+StructTagSystem::~StructTagSystem()
+{
+ for (auto category : m_categories)
+ {
+ if (category)
+ {
+ category->~StructTagCategoryInfo();
+ }
+ }
+}
+
+StructTagCategoryInfo* StructTagSystem::getCategoryInfo(slang::StructTagCategory category)
+{
+ const Index index = Index(category);
+ return (index < m_categories.getCount()) ? m_categories[index] : nullptr;
+}
+
+StructTagCategoryInfo* StructTagSystem::addCategoryInfo(slang::StructTagCategory category, const String& name)
+{
+ StructTagCategoryInfo* categoryInfo = new (m_arena.allocateAligned(sizeof(StructTagCategoryInfo), SLANG_ALIGN_OF(StructTagCategoryInfo))) StructTagCategoryInfo(category, name);
+
+ const Index index = Index(category);
+
+ if (index >= m_categories.getCount())
+ {
+ m_categories.setCount(index + 1);
+ }
+ m_categories[index] = categoryInfo;
+ return categoryInfo;
+}
+
+StructTagType* StructTagSystem::addType(slang::StructTag tag, const String& name, size_t sizeInBytes)
+{
+ auto category = StructTagUtil::getCategory(tag);
+ auto categoryInfo = getCategoryInfo(category);
+
+ auto structType = new (m_arena.allocate<StructTagType>()) StructTagType(tag, name, sizeInBytes);
+ categoryInfo->addType(structType);
+
+ return structType;
+}
+
+StructTagType* StructTagSystem::getType(slang::StructTag tag)
+{
+ const auto category = StructTagUtil::getCategory(tag);
+ auto categoryInfo = getCategoryInfo(category);
+ if (categoryInfo)
+ {
+ auto typeIndex = StructTagUtil::getTypeIndex(tag);
+ return categoryInfo->getType(typeIndex);
+ }
+
+ return nullptr;
+}
+
+void StructTagSystem::appendName(slang::StructTag tag, StringBuilder& out)
+{
+ auto info = StructTagUtil::getTypeInfo(tag);
+
+ auto categoryInfo = getCategoryInfo(info.category);
+ if (categoryInfo)
+ {
+ out << categoryInfo->m_name;
+ }
+ else
+ {
+ out << Index(info.category);
+ }
+
+ out << "::";
+
+ auto type = categoryInfo->getType(info.typeIndex);
+
+ if (type)
+ {
+ out << type->m_name;
+ }
+ else
+ {
+ out << "~" << Index(info.typeIndex) << "~";
+ }
+
+ out << "_";
+ out << Index(info.majorVersion);
+ out << ".";
+ out << Index(info.minorVersion);
+}
+
+} // namespace Slang
diff --git a/source/compiler-core/slang-struct-tag-system.h b/source/compiler-core/slang-struct-tag-system.h
new file mode 100644
index 000000000..20581cec7
--- /dev/null
+++ b/source/compiler-core/slang-struct-tag-system.h
@@ -0,0 +1,259 @@
+#ifndef SLANG_COMPILER_CORE_STRUCT_TAG_SYSTEM_H
+#define SLANG_COMPILER_CORE_STRUCT_TAG_SYSTEM_H
+
+#include "../../slang.h"
+
+#include "../../slang-com-helper.h"
+#include "../../slang-com-ptr.h"
+
+#include "../core/slang-smart-pointer.h"
+
+#include "../core/slang-dictionary.h"
+#include "../core/slang-semantic-version.h"
+#include "../core/slang-memory-arena.h"
+
+namespace Slang {
+
+struct StructTagUtil
+{
+ struct TypeInfo
+ {
+ slang::StructTagKind kind; ///< The kind
+ slang::StructTagCategory category; ///< The category
+ uint8_t typeIndex; ///< Type index for the category type
+ uint8_t majorVersion; ///< The major semantic version
+ uint8_t minorVersion; ///< The minor semantic version
+ };
+
+ /// True if it's a primary struct
+ static bool isPrimary(slang::StructTag tag) { return (slang::StructTagInt(tag) & slang::kStructTagPrimaryMask) != 0; }
+ /// True if it's an extension
+ static bool isExtension(slang::StructTag tag) { return !isPrimary(tag); }
+
+ inline static TypeInfo getTypeInfo(slang::StructTag tag);
+
+ /// Get the category and type from the value
+ static slang::StructTagInt getCategoryTypeIndex(slang::StructTag tag) { return (slang::StructTagInt(tag) & slang::kStructTagCategoryTypeIndexMask) >> slang::kStructTagCategoryTypeIndexShift; }
+
+ /// Get the type index
+ static Index getTypeIndex(slang::StructTag tag) { return Index((slang::StructTagInt(tag) & slang::kStructTagTypeIndexMask) >> slang::kStructTagTypeIndexShift); }
+
+ /// Get the category
+ static slang::StructTagCategory getCategory(slang::StructTag tag) { return slang::StructTagCategory((slang::StructTagInt(tag) & slang::kStructTagCategoryMask) >> slang::kStructTagCategoryShift); }
+
+ /// They are the same type and have same major version
+ static bool areSameMajorType(slang::StructTag a, slang::StructTag b)
+ {
+ const auto typeMask = slang::StructTagInt(slang::kStructTagCategoryTypeMajorMask);
+ return ((slang::StructTagInt(a) ^ slang::StructTagInt(b)) & typeMask) == 0;
+ }
+
+ /// This will *only* determine if *just* this type is compatible for read and not if it contains other types (say in the form of extensions)
+ static bool isReadCompatible(slang::StructTag inTag, slang::StructTag inCurrentTag)
+ {
+ // Uniquely identifies the 'type'.
+ const auto typeMask = slang::StructTagInt(slang::kStructTagCategoryTypeMajorMask);
+ const auto minorMask = slang::StructTagInt(slang::kStructTagMinorMask);
+
+ const auto tag = slang::StructTagInt(inTag);
+ const auto currentTag = slang::StructTagInt(inCurrentTag);
+
+ // If they are the same type, and the input types minor is greater than equal to current minor we can accept for read (singly)
+ return ((tag ^ currentTag) & typeMask) == 0 && (tag & minorMask) >= (currentTag & minorMask);
+ }
+};
+
+/* static */ inline StructTagUtil::TypeInfo StructTagUtil::getTypeInfo(slang::StructTag tag)
+{
+ const auto intTag = slang::StructTagInt(tag);
+
+ TypeInfo info;
+ info.kind = (intTag & slang::kStructTagPrimaryMask) ? slang::StructTagKind::Primary : slang::StructTagKind::Extension;
+ info.category = getCategory(tag);
+ info.typeIndex = uint8_t(getTypeIndex(tag));
+ info.majorVersion = uint8_t((intTag & slang::kStructTagMajorMask) >> slang::kStructTagMajorShift);
+ info.minorVersion = uint8_t((intTag & slang::kStructTagMinorMask) >> slang::kStructTagMinorShift);
+ return info;
+}
+
+/// We can have a 'field' that is made up of 2 elements, so we have two entries.
+/// If m_countType is Unknown, then the entry can be ignored
+struct StructTagField
+{
+ enum class Type : uint8_t
+ {
+ Unknown,
+ TaggedStruct,
+ PtrTaggedStruct,
+ PtrPtrTaggedStruct,
+ I32,
+ I64,
+ };
+
+ SLANG_FORCE_INLINE static bool isInRange(Type type, Type start, Type end) { return Index(type) >= Index(start) && Index(type) <= Index(end); }
+
+ /// True if it's an integral
+ static bool isIntegral(Type type) { return isInRange(type, Type::I32, Type::I64); }
+ /// True if it's a pointer or pointer to a pointer
+ static bool isPtrLike(Type type) { return isInRange(type, Type::PtrTaggedStruct, Type::PtrPtrTaggedStruct); }
+
+ Type m_type;
+ Type m_countType;
+ uint16_t m_offset;
+ uint16_t m_countOffset;
+};
+
+struct StructTagType
+{
+public:
+ typedef StructTagField Field;
+
+ StructTagType(slang::StructTag tag, const String& name, size_t sizeInBytes):
+ m_tag(tag),
+ m_name(name),
+ m_sizeInBytes(slang::StructSize(sizeInBytes))
+ {
+ }
+
+ slang::StructTag m_tag; ///< The type/current version
+ String m_name; ///< The name of the type
+ slang::StructSize m_sizeInBytes; ///< The size of this version in bytes
+
+ List<Field> m_fields; ///< Fields that need to be followed
+};
+
+namespace StructTagTypeTraits
+{
+ typedef StructTagField Field;
+ typedef Field::Type Type;
+
+ // Helper that works out what a pointer to the inner type is.
+ SLANG_FORCE_INLINE Type getPtrType(Type innerType)
+ {
+ switch (innerType)
+ {
+ case Type::TaggedStruct: return Type::PtrTaggedStruct;
+ case Type::PtrTaggedStruct: return Type::PtrPtrTaggedStruct;
+ default: return Type::Unknown;
+ }
+ }
+
+ template <typename T, typename F>
+ SLANG_FORCE_INLINE uint16_t getOffset(T* obj, const F* f)
+ {
+ return uint16_t((const char*)f - (const char*)obj);
+ }
+
+ // Use `substitution failure is not an error` (SFINAE) to detect tagged struct types
+ template <typename T>
+ struct IsTaggedStruct
+ {
+ typedef int32_t True;
+ typedef int8_t False;
+
+ template <typename C>
+ static True check(typename C::Tag*);
+ template <typename>
+ static False check(...);
+
+ // Is != 0 if it is a TaggedStruct type
+ enum { kValue = int(sizeof(check<T>(nullptr)) == sizeof(True)) };
+ };
+
+ template <typename T>
+ struct Impl { static Type getType() { return IsTaggedStruct<T>::kValue ? Type::TaggedStruct : Type::Unknown; } };
+
+ // Doesn't currently handle fixed arrays, but could be added quite easily, with say a byte for the fixed size.
+
+ // Integer types
+ // We won't bother with sign for now
+ template <> struct Impl<uint64_t> { static Type getType() { return Type::I64; } };
+ template <> struct Impl<int64_t> { static Type getType() { return Type::I64; } };
+ template <> struct Impl<uint32_t> { static Type getType() { return Type::I32; } };
+ template <> struct Impl<int32_t> { static Type getType() { return Type::I32; } };
+
+ // StructTag is used to indicate it can be any 'tagged struct type'
+ template <> struct Impl<slang::StructTag> { static Type getType() { return Type::TaggedStruct; } };
+
+ // Pointer
+ template <typename T> struct Impl<T*> { static Type getType() { return getPtrType(Impl<T>::getType()); } };
+
+ /// f1 should hold the count
+ template <typename T, typename F0, typename F1>
+ Field getFieldWithCount(const T* obj, const F0* ptr, const F1* count)
+ {
+ Field field;
+ field.m_type = Impl<F0>::getType();
+ field.m_countType = Impl<F1>::getType();
+ field.m_offset = getOffset(obj, ptr);
+ field.m_countOffset = getOffset(obj, count);
+
+ SLANG_ASSERT(StructTagField::isPtrLike(field.m_type));
+ SLANG_ASSERT(StructTagField::isIntegral(field.m_countType));
+
+ return field;
+ }
+}
+
+class StructTagCategoryInfo
+{
+public:
+
+ /// Add a type. Will replace a type if there is already one setup for the m_Type
+ void addType(StructTagType* type);
+
+ /// Get a type
+ StructTagType* getType(Index typeIndex) const { return typeIndex < m_types.getCount() ? m_types[typeIndex] : nullptr; }
+
+ StructTagCategoryInfo(slang::StructTagCategory category, const String& name) :
+ m_category(category),
+ m_name(name)
+ {
+ }
+ ~StructTagCategoryInfo();
+
+ slang::StructTagCategory m_category; ///< The category type
+ String m_name; ///< The name
+
+ // All the types in this category
+ List<StructTagType*> m_types;
+};
+
+/* Holds the information about TaggedStruct types. Use the StructTagConverter to actually convert to conforming
+types. */
+class StructTagSystem : public RefObject
+{
+public:
+
+ /// Add a category
+ StructTagCategoryInfo* addCategoryInfo(slang::StructTagCategory category, const String& name);
+ StructTagCategoryInfo* getCategoryInfo(slang::StructTagCategory category);
+
+ /// Get struct type
+ StructTagType* getType(slang::StructTag tag);
+
+ /// Add the struct type
+ StructTagType* addType(slang::StructTag tag, const String& name, size_t sizeInBytes);
+
+ void appendName(slang::StructTag tag, StringBuilder& out);
+ String getName(slang::StructTag tag) { StringBuilder buf; appendName(tag, buf); return buf.ProduceString(); }
+
+ StructTagSystem():
+ m_arena(1024)
+ {
+ }
+
+ ~StructTagSystem();
+
+protected:
+
+ /// Arena stores all of the types
+ MemoryArena m_arena;
+
+ /// All of the categories
+ List<StructTagCategoryInfo*> m_categories;
+};
+
+} // namespace Slang
+
+#endif // SLANG_COMPILER_CORE_STRUCT_TAG_SYSTEM_H
diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h
index 0ee63fd09..e6684108b 100755
--- a/source/slang/slang-compiler.h
+++ b/source/slang/slang-compiler.h
@@ -11,6 +11,8 @@
#include "../compiler-core/slang-include-system.h"
#include "../compiler-core/slang-command-line-args.h"
+#include "../compiler-core/slang-struct-tag-system.h"
+
#include "../core/slang-std-writers.h"
#include "../../slang-com-ptr.h"
@@ -1433,7 +1435,7 @@ namespace Slang
Type* const* args,
DiagnosticSink* sink);
- /// Add a mew target amd return its index.
+ /// Add a mew target and return its index.
UInt addTarget(
CodeGenTarget target);
@@ -2318,6 +2320,9 @@ namespace Slang
/// Get the built in linkage -> handy to get the stdlibs from
Linkage* getBuiltinLinkage() const { return m_builtinLinkage; }
+ /// Get the Abi system used for managing binary compatibility of interface types (outside of COM mechanisms)
+ StructTagSystem* getStructTagSystem() const { return m_structTagSystem; }
+
void init();
void addBuiltinSource(
@@ -2343,6 +2348,8 @@ namespace Slang
/// Linkage used for all built-in (stdlib) code.
RefPtr<Linkage> m_builtinLinkage;
+ RefPtr<StructTagSystem> m_structTagSystem;
+
String m_downstreamCompilerPaths[int(PassThroughMode::CountOf)]; ///< Paths for each pass through
String m_languagePreludes[int(SourceLanguage::CountOf)]; ///< Prelude for each source language
PassThroughMode m_defaultDownstreamCompilers[int(SourceLanguage::CountOf)];
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index eed172bbf..8d82ca1b7 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -23,6 +23,7 @@
#include "../core/slang-writer.h"
#include "../compiler-core/slang-source-loc.h"
+#include "../compiler-core/slang-struct-tag-converter.h"
#include "slang-ast-dump.h"
@@ -114,6 +115,32 @@ const char* getBuildTagString()
return SLANG_TAG_VERSION;
}
+static RefPtr<StructTagSystem> _createStructTagSystem()
+{
+ RefPtr<StructTagSystem> system = new StructTagSystem;
+
+ {
+#define SLANG_STRUCT_TAG_ADD_CATEGORY(x) system->addCategoryInfo(slang::StructTagCategory::x, #x);
+ SLANG_STRUCT_TAG_CATEGORIES(SLANG_STRUCT_TAG_ADD_CATEGORY)
+ }
+
+ {
+#define SLANG_STRUCT_TAG_ADD_TYPE(X) system->addType(slang::X::kStructTag, "slang::" #X, sizeof(slang::X));
+SLANG_TAGGED_STRUCTS(SLANG_STRUCT_TAG_ADD_TYPE)
+
+ // Add field that references more tagged structs
+ {
+ slang::SessionDesc desc;
+ auto field = StructTagTypeTraits::getFieldWithCount(&desc, &desc.targets, &desc.targetCount);
+
+ auto type = system->getType(slang::SessionDesc::kStructTag);
+ type->m_fields.add(field);
+ }
+ }
+
+ return system;
+}
+
void Session::init()
{
SLANG_ASSERT(BaseTypeInfo::check());
@@ -131,6 +158,8 @@ void Session::init()
m_sharedASTBuilder = new SharedASTBuilder;
m_sharedASTBuilder->init(this);
+ m_structTagSystem = _createStructTagSystem();
+
// Use to create a ASTBuilder
RefPtr<ASTBuilder> builtinAstBuilder(new ASTBuilder(m_sharedASTBuilder, "m_builtInLinkage::m_astBuilder"));
@@ -438,42 +467,45 @@ ISlangUnknown* Session::getInterface(const Guid& guid)
}
SLANG_NO_THROW SlangResult SLANG_MCALL Session::createSession(
- slang::SessionDesc const& desc,
+ slang::SessionDesc const& inDesc,
slang::ISession** outSession)
{
+ MemoryArena arena(1024);
+
+ LazyStructTagConverter converter(getStructTagSystem(), &arena, nullptr);
+
+ const slang::SessionDesc* desc = nullptr;
+ SLANG_RETURN_ON_FAIL(converter.convertToCurrent(&inDesc, &desc));
+
RefPtr<ASTBuilder> astBuilder(new ASTBuilder(m_sharedASTBuilder, "Session::astBuilder"));
RefPtr<Linkage> linkage = new Linkage(this, astBuilder, getBuiltinLinkage());
- Int targetCount = desc.targetCount;
- const uint8_t* targetDescPtr = reinterpret_cast<const uint8_t*>(desc.targets);
- for(Int ii = 0; ii < targetCount; ++ii)
{
- slang::TargetDesc targetDesc;
- // Copy the size field first.
- memcpy(&targetDesc.structureSize, targetDescPtr, sizeof(size_t));
- // Copy the entire desc structure.
- memcpy(&targetDesc, targetDescPtr, targetDesc.structureSize);
- linkage->addTarget(targetDesc);
- targetDescPtr += targetDesc.structureSize;
+ const Index targetCount = Index(desc->targetCount);
+ for(Index ii = 0; ii < targetCount; ++ii)
+ {
+ const auto& targetDesc = desc->targets[ii];
+ linkage->addTarget(targetDesc);
+ }
}
- if(desc.flags & slang::kSessionFlag_FalcorCustomSharedKeywordSemantics)
+ if(desc->flags & slang::kSessionFlag_FalcorCustomSharedKeywordSemantics)
{
linkage->m_useFalcorCustomSharedKeywordSemantics = true;
}
- linkage->setMatrixLayoutMode(desc.defaultMatrixLayoutMode);
+ linkage->setMatrixLayoutMode(desc->defaultMatrixLayoutMode);
- Int searchPathCount = desc.searchPathCount;
+ Int searchPathCount = desc->searchPathCount;
for(Int ii = 0; ii < searchPathCount; ++ii)
{
- linkage->addSearchPath(desc.searchPaths[ii]);
+ linkage->addSearchPath(desc->searchPaths[ii]);
}
- Int macroCount = desc.preprocessorMacroCount;
+ Int macroCount = desc->preprocessorMacroCount;
for(Int ii = 0; ii < macroCount; ++ii)
{
- auto& macro = desc.preprocessorMacros[ii];
+ auto& macro = desc->preprocessorMacros[ii];
linkage->addPreprocessorDefine(macro.name, macro.value);
}