diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2021-06-18 17:09:35 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-06-18 17:09:35 -0400 |
| commit | 0afa24a3fe7d0e1787cc909f9c7641f477c30e5c (patch) | |
| tree | 08724f28641b221167a03ca2192ee8297328becd /source | |
| parent | 89051251016be7d3798c0b9586c6db7b4ed5f21d (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.h | 5 | ||||
| -rw-r--r-- | source/compiler-core/slang-struct-tag-converter.cpp | 538 | ||||
| -rw-r--r-- | source/compiler-core/slang-struct-tag-converter.h | 153 | ||||
| -rw-r--r-- | source/compiler-core/slang-struct-tag-system.cpp | 126 | ||||
| -rw-r--r-- | source/compiler-core/slang-struct-tag-system.h | 259 | ||||
| -rwxr-xr-x | source/slang/slang-compiler.h | 9 | ||||
| -rw-r--r-- | source/slang/slang.cpp | 66 |
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); } |
