diff --git a/BeefLibs/corlib/src/Attribute.bf b/BeefLibs/corlib/src/Attribute.bf index f3a9b056..eafc1f6c 100644 --- a/BeefLibs/corlib/src/Attribute.bf +++ b/BeefLibs/corlib/src/Attribute.bf @@ -366,10 +366,18 @@ namespace System [AttributeUsage(.Class | .Struct)] public struct PackedAttribute : Attribute { + public this() + { + } + + public this(int align) + { + + } } - [AttributeUsage(.Class | .Struct | .Alloc)] + [AttributeUsage(.Class | .Struct | .Alloc | .Field)] public struct AlignAttribute : Attribute { public this(int align) diff --git a/IDEHelper/Compiler/BfContext.cpp b/IDEHelper/Compiler/BfContext.cpp index 61affda7..48db06c2 100644 --- a/IDEHelper/Compiler/BfContext.cpp +++ b/IDEHelper/Compiler/BfContext.cpp @@ -1070,7 +1070,7 @@ void BfContext::RebuildType(BfType* type, bool deleteOnDemandTypes, bool rebuild typeInst->mLookupResults.Clear(); typeInst->mIsUnion = false; typeInst->mIsCRepr = false; - typeInst->mIsPacked = false; + typeInst->mPacking = 0; typeInst->mIsSplattable = false; typeInst->mHasUnderlyingArray = false; diff --git a/IDEHelper/Compiler/BfIRBuilder.cpp b/IDEHelper/Compiler/BfIRBuilder.cpp index 6235b13d..5944533d 100644 --- a/IDEHelper/Compiler/BfIRBuilder.cpp +++ b/IDEHelper/Compiler/BfIRBuilder.cpp @@ -2966,7 +2966,7 @@ void BfIRBuilder::CreateDbgTypeDefinition(BfType* type) llvm::SmallVector diFieldTypes; - bool isPacked = false; + int packing = 0; bool isUnion = false; bool isCRepr = false; BfType* underlyingArrayType = NULL; @@ -2980,7 +2980,7 @@ void BfIRBuilder::CreateDbgTypeDefinition(BfType* type) else { isCRepr = typeInstance->mIsCRepr; - isPacked = typeInstance->mIsPacked; + packing = typeInstance->mPacking; isUnion = typeInstance->mIsUnion; typeInstance->GetUnderlyingArray(underlyingArrayType, underlyingArraySize, underlyingArrayIsVector); // if (underlyingArrayType != NULL) @@ -3493,7 +3493,7 @@ void BfIRBuilder::CreateTypeDefinition_Data(BfModule* populateModule, BfTypeInst llvm::SmallVector diFieldTypes; - bool isPacked = false; + int packing = 0; bool isUnion = false; bool isCRepr = false; @@ -3514,7 +3514,7 @@ void BfIRBuilder::CreateTypeDefinition_Data(BfModule* populateModule, BfTypeInst else { isCRepr = typeInstance->mIsCRepr; - isPacked = typeInstance->mIsPacked; + packing = typeInstance->mPacking; isUnion = typeInstance->mIsUnion; } diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index 06bbb67f..9eed862d 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -6706,7 +6706,7 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin { // Add discriminator auto dscrType = checkTypeInstance->GetDiscriminatorType(); - if (!checkTypeInstance->mIsPacked) + if (checkTypeInstance->mPacking > 0) dataEnd = BF_ALIGN(dataEnd, dscrType->mAlign); _CheckSplat(dscrType, dataEnd); } @@ -11896,7 +11896,7 @@ void BfModule::FinishAttributeState(BfAttributeState* attributeState) Warn(0, "Unused attributes", attributeState->mSrc); } -void BfModule::ProcessTypeInstCustomAttributes(bool& isPacked, bool& isUnion, bool& isCRepr, bool& isOrdered, int& alignOverride, BfType*& underlyingArrayType, int& underlyingArraySize) +void BfModule::ProcessTypeInstCustomAttributes(int& packing, bool& isUnion, bool& isCRepr, bool& isOrdered, int& alignOverride, BfType*& underlyingArrayType, int& underlyingArraySize) { if (mCurTypeInstance->mTypeDef->mIsAlwaysInclude) mCurTypeInstance->mAlwaysIncludeFlags = (BfAlwaysIncludeFlags)(mCurTypeInstance->mAlwaysIncludeFlags | BfAlwaysIncludeFlag_Type); @@ -11907,7 +11907,17 @@ void BfModule::ProcessTypeInstCustomAttributes(bool& isPacked, bool& isUnion, bo String typeName = TypeToString(customAttribute.mType); if (typeName == "System.PackedAttribute") { - isPacked = true; + packing = 1; + if (customAttribute.mCtorArgs.size() >= 1) + { + auto alignConstant = mCurTypeInstance->mConstHolder->GetConstant(customAttribute.mCtorArgs[0]); + + int checkPacking = alignConstant->mInt32; + if (((checkPacking & (checkPacking - 1)) == 0) && (packing > 0) && (packing < 256)) + packing = checkPacking; + else + Fail("Packing must be a power of 2", customAttribute.GetRefNode()); + } } else if (typeName == "System.UnionAttribute") { @@ -11948,7 +11958,12 @@ void BfModule::ProcessTypeInstCustomAttributes(bool& isPacked, bool& isUnion, bo if (customAttribute.mCtorArgs.size() >= 1) { auto alignConstant = mCurTypeInstance->mConstHolder->GetConstant(customAttribute.mCtorArgs[0]); - alignOverride = alignConstant->mInt32; + + int checkAlign = alignConstant->mInt32; + if ((checkAlign & (checkAlign - 1)) == 0) + alignOverride = checkAlign; + else + Fail("Alignment must be a power of 2", customAttribute.GetRefNode()); } } else if (typeName == "System.UnderlyingArrayAttribute") diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index 5ac11f19..2ed68f81 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -1587,7 +1587,7 @@ public: BfCustomAttributes* GetCustomAttributes(BfAttributeDirective* attributesDirective, BfAttributeTargets attrType, BfGetCustomAttributesFlags flags = BfGetCustomAttributesFlags_None, BfCaptureInfo* captureInfo = NULL); BfCustomAttributes* GetCustomAttributes(BfTypeDef* typeDef); void FinishAttributeState(BfAttributeState* attributeState); - void ProcessTypeInstCustomAttributes(bool& isPacked, bool& isUnion, bool& isCRepr, bool& isOrdered, int& alignOverride, BfType*& underlyingArrayType, int& underlyingArraySize); + void ProcessTypeInstCustomAttributes(int& packing, bool& isUnion, bool& isCRepr, bool& isOrdered, int& alignOverride, BfType*& underlyingArrayType, int& underlyingArraySize); void ProcessCustomAttributeData(); bool TryGetConstString(BfIRConstHolder* constHolder, BfIRValue irValue, StringImpl& str); BfVariant TypedValueToVariant(BfAstNode* refNode, const BfTypedValue& value, bool allowUndef = false); diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index a45032a9..81f42025 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -2932,7 +2932,7 @@ void BfModule::DoPopulateType_FinishEnum(BfTypeInstance* typeInstance, bool unde fieldInstance->mDataOffset = unionInnerType->mSize; fieldInstance->mDataIdx = 2; // 0 = base, 1 = payload, 2 = discriminator - if (!typeInstance->mIsPacked) + if (typeInstance->mPacking == 0) { if ((fieldInstance->mDataOffset % discriminatorType->mAlign) != 0) { @@ -3890,25 +3890,25 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy typeInstance->mInstAlign = std::max(0, typeInstance->mInstAlign); ProcessCustomAttributeData(); - bool isPacked = false; + int packing = 0; bool isUnion = false; bool isCRepr = false; bool isOrdered = false; int alignOverride = 0; BfType* underlyingArrayType = NULL; int underlyingArraySize = -1; - ProcessTypeInstCustomAttributes(isPacked, isUnion, isCRepr, isOrdered, alignOverride, underlyingArrayType, underlyingArraySize); + ProcessTypeInstCustomAttributes(packing, isUnion, isCRepr, isOrdered, alignOverride, underlyingArrayType, underlyingArraySize); if (underlyingArraySize > 0) { typeInstance->mHasUnderlyingArray = true; curFieldDataIdx = 0; } - if (isPacked) // Packed infers ordered + if (packing > 0) // Packed infers ordered isOrdered = true; typeInstance->mIsUnion = isUnion; if ((typeInstance->IsEnum()) && (typeInstance->IsStruct())) typeInstance->mIsUnion = true; - typeInstance->mIsPacked = isPacked; + typeInstance->mPacking = (uint8)packing; typeInstance->mIsCRepr = isCRepr; if (typeInstance->mTypeOptionsIdx >= 0) @@ -4434,7 +4434,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy dataMemberHashCtx.Mixin(ver->mDataHash); } } - dataMemberHashCtx.Mixin(typeInstance->mIsPacked); + dataMemberHashCtx.Mixin(typeInstance->mPacking); dataMemberHashCtx.Mixin(typeInstance->mIsCRepr); dataMemberHashCtx.Mixin(typeInstance->mIsUnion); @@ -4588,12 +4588,11 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy { BF_ASSERT(resolvedFieldType->mSize >= 0); - if ((alignSize > 1) && (!isPacked)) + if (alignSize > 1) dataPos = (dataPos + (alignSize - 1)) & ~(alignSize - 1); fieldInstance->mDataOffset = dataPos; - - if (!isPacked) - typeInstance->mInstAlign = std::max(typeInstance->mInstAlign, alignSize); + + typeInstance->mInstAlign = std::max(typeInstance->mInstAlign, alignSize); dataPos += dataSize; if (dataPos > maxDataPos) @@ -4678,7 +4677,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy Array> alignBuckets; for (auto fieldInst : dataFieldVec) { - int alignBits = GetHighestBitSet(fieldInst->mResolvedType->mAlign); + int alignBits = GetHighestBitSet(fieldInst->GetAlign(packing)); while (alignBits >= alignBuckets.size()) alignBuckets.Add({}); alignBuckets[alignBits].Add(fieldInst); @@ -4711,7 +4710,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy auto fieldInst = alignBuckets[alignBits][0]; alignBuckets[alignBits].RemoveAt(0); dataFieldVec.push_back(fieldInst); - curSize = BF_ALIGN(curSize, fieldInst->mResolvedType->mAlign); + curSize = BF_ALIGN(curSize, fieldInst->GetAlign(packing)); curSize += fieldInst->mResolvedType->mSize; foundEntry = true; @@ -4732,7 +4731,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy auto fieldInst = alignBuckets[alignBits][0]; alignBuckets[alignBits].RemoveAt(0); dataFieldVec.push_back(fieldInst); - curSize = BF_ALIGN(curSize, fieldInst->mResolvedType->mAlign); + curSize = BF_ALIGN(curSize, fieldInst->GetAlign(packing)); curSize += fieldInst->mResolvedType->mSize; break; } @@ -4752,25 +4751,23 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy BF_ASSERT(resolvedFieldType->mSize >= 0); int dataSize = resolvedFieldType->mSize; - int alignSize = resolvedFieldType->mAlign; + int alignSize = fieldInstance->GetAlign(packing); fieldInstance->mDataSize = dataSize; //bool needsExplicitAlignment = !isCRepr || resolvedFieldType->NeedsExplicitAlignment(); - int nextDataPos = dataPos; - if (!isPacked) - nextDataPos = (dataPos + (alignSize - 1)) & ~(alignSize - 1); + int nextDataPos = dataPos; + nextDataPos = (dataPos + (alignSize - 1)) & ~(alignSize - 1); int padding = nextDataPos - dataPos; if ((alignSize > 1) && (needsExplicitAlignment) && (padding > 0)) { - curFieldDataIdx++; + curFieldDataIdx++; } dataPos = nextDataPos; fieldInstance->mDataOffset = dataPos; fieldInstance->mDataIdx = curFieldDataIdx++; - - if (!isPacked) - typeInstance->mInstAlign = std::max(typeInstance->mInstAlign, alignSize); + + typeInstance->mInstAlign = std::max(typeInstance->mInstAlign, alignSize); dataPos += dataSize; } @@ -4786,8 +4783,6 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy if (alignOverride > 0) typeInstance->mInstAlign = alignOverride; - else if (isPacked) - typeInstance->mInstAlign = 1; else typeInstance->mInstAlign = std::max(1, typeInstance->mInstAlign); int alignSize = typeInstance->mInstAlign; @@ -10965,7 +10960,16 @@ BfType* BfModule::ResolveTypeRef(BfTypeReference* typeRef, BfPopulateType popula refType->mElementType = elementType; resolvedEntry->mValue = refType; - BF_ASSERT(BfResolvedTypeSet::Hash(refType, &lookupCtx) == resolvedEntry->mHash); + +#ifdef _DEBUG + if (BfResolvedTypeSet::Hash(refType, &lookupCtx) != resolvedEntry->mHash) + { + int refHash = BfResolvedTypeSet::Hash(typeRef, &lookupCtx); + int typeHash = BfResolvedTypeSet::Hash(refType, &lookupCtx); + BF_ASSERT(refHash == typeHash); + } + BF_ASSERT(BfResolvedTypeSet::Equals(refType, typeRef, &lookupCtx)); +#endif populateModule->InitType(refType, populateType); return ResolveTypeResult(typeRef, refType, populateType, resolveFlags); } diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp index 8b6f91ce..db8ae513 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp @@ -495,6 +495,39 @@ void BfFieldInstance::GetDataRange(int& dataIdx, int& dataCount) dataCount = maxMergedDataIdx - minMergedDataIdx; } +int BfFieldInstance::GetAlign(int packing) +{ + int align = mResolvedType->mAlign; + if (packing > 0) + align = BF_MIN(align, packing); + if (mCustomAttributes != NULL) + { + auto module = mOwner->mModule; + for (auto& attrib : mCustomAttributes->mAttributes) + { + if (attrib.mType->IsInstanceOf(module->mCompiler->mAlignAttributeTypeDef)) + { + align = 16; // System conservative default + + if (!attrib.mCtorArgs.IsEmpty()) + { + BfIRConstHolder* constHolder = module->mCurTypeInstance->mConstHolder; + auto constant = constHolder->GetConstant(attrib.mCtorArgs[0]); + if (constant != NULL) + { + int alignOverride = (int)BF_MAX(1, constant->mInt64); + if ((alignOverride & (alignOverride - 1)) == 0) + align = alignOverride; + else + module->Fail("Alignment must be a power of 2", attrib.GetRefNode()); + } + } + } + } + } + return align; +} + ////////////////////////////////////////////////////////////////////////// int64 BfDeferredMethodCallData::GenerateMethodId(BfModule* module, int64 methodId) diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.h b/IDEHelper/Compiler/BfResolvedTypeUtils.h index 18485956..a721e183 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.h +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.h @@ -1413,6 +1413,7 @@ public: BfType* GetResolvedType(); void SetResolvedType(BfType* type); void GetDataRange(int& dataIdx, int& dataCount); + int GetAlign(int packing); }; enum BfMethodRefKind @@ -1903,7 +1904,7 @@ public: bool mIsTypedPrimitive; bool mIsCRepr; bool mIsUnion; - bool mIsPacked; + uint8 mPacking; bool mIsSplattable; bool mHasUnderlyingArray; bool mTypeIncomplete; @@ -1934,7 +1935,7 @@ public: mIsReified = true; mIsSplattable = false; mHasUnderlyingArray = false; - mIsPacked = false; + mPacking = 0; mBaseType = NULL; mCustomAttributes = NULL; mAttributeData = NULL; @@ -1990,7 +1991,7 @@ public: int GetEndingInstanceAlignment() { if (mInstSize % mInstAlign == 0) return mInstAlign; return mInstSize % mInstAlign; } virtual bool HasTypeFailed() override { return mTypeFailed; } virtual bool IsReified() override { return mIsReified; } - virtual bool NeedsExplicitAlignment() override { return !IsSizeAligned() || mIsPacked; } + virtual bool NeedsExplicitAlignment() override { return !IsSizeAligned() || (mPacking != 0); } virtual bool IsDataIncomplete() override { return ((mTypeIncomplete) || (mBaseTypeMayBeIncomplete)) && (!mNeedsMethodProcessing); } virtual bool IsFinishingType() override { return mIsFinishingType; } virtual bool IsIncomplete() override { return (mTypeIncomplete) || (mBaseTypeMayBeIncomplete); } diff --git a/IDEHelper/Tests/src/Structs.bf b/IDEHelper/Tests/src/Structs.bf index ff30c0e2..241c7602 100644 --- a/IDEHelper/Tests/src/Structs.bf +++ b/IDEHelper/Tests/src/Structs.bf @@ -191,6 +191,29 @@ namespace Tests Test.Assert(sn.mA == 123); } + [Align(16)] + struct StructP + { + int32 mA; + [Align(8)] + int8 mB; + } + + [Align(16), Ordered] + struct StructQ + { + int32 mA; + [Align(8)] + int8 mB; + } + + [Packed(2), Ordered] + struct StructR + { + int8 mA; + int32 mB; + } + [Test] static void TestLayouts() { @@ -225,6 +248,18 @@ namespace Tests Test.Assert(sizeof(StructJ) == 5); Test.Assert(alignof(StructJ) == 1); Test.Assert(strideof(StructJ) == 5); + + Test.Assert(sizeof(StructP) == 8); + Test.Assert(alignof(StructP) == 16); + Test.Assert(strideof(StructP) == 16); + + Test.Assert(sizeof(StructQ) == 9); + Test.Assert(alignof(StructQ) == 16); + Test.Assert(strideof(StructQ) == 16); + + Test.Assert(sizeof(StructR) == 6); + Test.Assert(alignof(StructR) == 2); + Test.Assert(strideof(StructR) == 6); } public int Test(T val)