summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-ast-modifier.h12
-rw-r--r--source/slang/slang-check-conversion.cpp2
-rw-r--r--source/slang/slang-check-decl.cpp187
-rw-r--r--source/slang/slang-check-impl.h2
-rw-r--r--source/slang/slang-diagnostic-defs.h4
-rw-r--r--source/slang/slang-ir-autodiff.cpp36
-rw-r--r--source/slang/slang-ir-autodiff.h2
-rw-r--r--source/slang/slang-ir-bit-field-accessors.cpp160
-rw-r--r--source/slang/slang-ir-bit-field-accessors.h7
-rw-r--r--source/slang/slang-ir-inst-defs.h6
-rw-r--r--source/slang/slang-ir-insts.h8
-rw-r--r--source/slang/slang-ir.cpp39
-rw-r--r--source/slang/slang-ir.h7
-rw-r--r--source/slang/slang-lower-to-ir.cpp25
-rw-r--r--source/slang/slang-parser.cpp9
15 files changed, 471 insertions, 35 deletions
diff --git a/source/slang/slang-ast-modifier.h b/source/slang/slang-ast-modifier.h
index 155363e0c..c5d18f4f8 100644
--- a/source/slang/slang-ast-modifier.h
+++ b/source/slang/slang-ast-modifier.h
@@ -1479,4 +1479,16 @@ class GLSLPatchModifier : public SimpleModifier
SLANG_AST_CLASS(GLSLPatchModifier)
};
+//
+class BitFieldModifier : public Modifier
+{
+ SLANG_ABSTRACT_AST_CLASS(BitFieldModifier)
+
+ IntegerLiteralValue width;
+
+ // Fields filled during semantic analysis
+ IntegerLiteralValue offset = 0;
+ DeclRef<VarDecl> backingDeclRef;
+};
+
} // namespace Slang
diff --git a/source/slang/slang-check-conversion.cpp b/source/slang/slang-check-conversion.cpp
index d76b2a80e..9dae07018 100644
--- a/source/slang/slang-check-conversion.cpp
+++ b/source/slang/slang-check-conversion.cpp
@@ -645,7 +645,7 @@ namespace Slang
}
}
- static int getTypeBitSize(Type* t)
+ int getTypeBitSize(Type* t)
{
auto basicType = as<BasicExpressionType>(t);
if (!basicType) return 0;
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index 5a9559ce6..4dfffc414 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -1,4 +1,6 @@
// slang-check-decl.cpp
+#include "slang-ast-modifier.h"
+#include "slang-ast-support-types.h"
#include "slang-check-impl.h"
// This file constaints the semantic checking logic and
@@ -34,6 +36,8 @@ namespace Slang
{
checkModifiers(decl);
}
+
+ void visitStructDecl(StructDecl* structDecl);
};
struct SemanticsDeclScopeWiringVisitor : public SemanticsDeclVisitorBase, public DeclVisitor<SemanticsDeclScopeWiringVisitor>
@@ -64,6 +68,8 @@ namespace Slang
void visitDecl(Decl*) {}
void visitDeclGroup(DeclGroup*) {}
+ void visitStructDecl(StructDecl* structDecl);
+
void visitFunctionDeclBase(FunctionDeclBase* decl);
void checkForwardDerivativeOfAttribute(FunctionDeclBase* funcDecl, ForwardDerivativeOfAttribute* attr);
@@ -1375,6 +1381,47 @@ namespace Slang
tryConstantFoldDeclRef(DeclRef<VarDeclBase>(varDecl), nullptr);
}
+ void SemanticsDeclModifiersVisitor::visitStructDecl(StructDecl* structDecl)
+ {
+ checkModifiers(structDecl);
+
+ // Replace any bitfield member with a property, do this here before
+ // name lookup to avoid the original var decl being referenced
+ for(auto& m : structDecl->members)
+ {
+ const auto bfm = m->findModifier<BitFieldModifier>();
+ if(!bfm)
+ continue;
+
+ auto property = m_astBuilder->create<PropertyDecl>();
+ property->modifiers = m->modifiers;
+ property->type = as<VarDecl>(m)->type;
+ property->loc = m->loc;
+ property->nameAndLoc = m->getNameAndLoc();
+ property->parentDecl = structDecl;
+ property->ownedScope = m_astBuilder->create<Scope>();
+ property->ownedScope->containerDecl = property;
+ property->ownedScope->parent = getScope(structDecl);
+ m = property;
+
+ const auto get = m_astBuilder->create<GetterDecl>();
+ get->ownedScope = m_astBuilder->create<Scope>();
+ get->ownedScope->containerDecl = get;
+ get->ownedScope->parent = getScope(property);
+ property->addMember(get);
+
+ const auto set = m_astBuilder->create<SetterDecl>();
+ addModifier(set, m_astBuilder->create<MutatingAttribute>());
+ set->ownedScope = m_astBuilder->create<Scope>();
+ set->ownedScope->containerDecl = set;
+ set->ownedScope->parent = getScope(property);
+ property->addMember(set);
+
+ structDecl->invalidateMemberDictionary();
+ }
+ structDecl->buildMemberDictionary();
+ }
+
void SemanticsDeclHeaderVisitor::checkDerivativeMemberAttribute(
VarDeclBase* varDecl, DerivativeMemberAttribute* derivativeMemberAttr)
{
@@ -8943,6 +8990,146 @@ namespace Slang
this, funcDecl, attr, DeclAssociationKind::PrimalSubstituteFunc);
}
+ void SemanticsDeclAttributesVisitor::visitStructDecl(StructDecl* structDecl)
+ {
+ int backingWidth = 0;
+ [[maybe_unused]]
+ int totalWidth = 0;
+ struct BitFieldInfo
+ {
+ int memberIndex;
+ int bitWidth;
+ Type* memberType;
+ BitFieldModifier* bitFieldModifier;
+ };
+ List<BitFieldInfo> groupInfo;
+
+ int memberIndex = 0;
+ int backing_nonce = 0;
+ const auto dispatchSomeBitPackedMembers = [&](){
+ SLANG_ASSERT(totalWidth <= backingWidth);
+ SLANG_ASSERT(backingWidth <= 64);
+
+ // We're going to insert a backing member to be referenced in
+ // all the bitfield properties
+ if(groupInfo.getCount())
+ {
+ const auto backingMemberBasicType
+ = backingWidth <= 8 ? BaseType::UInt8
+ : backingWidth <= 16 ? BaseType::UInt16
+ : backingWidth <= 32 ? BaseType::UInt
+ : BaseType::UInt64;
+ auto backingMember = m_astBuilder->create<VarDecl>();
+ backingMember->type.type = m_astBuilder->getBuiltinType(backingMemberBasicType);
+ backingMember->nameAndLoc.name = getName(String("$bit_field_backing_") + String(backing_nonce));
+ backing_nonce++;
+ backingMember->initExpr = nullptr;
+ backingMember->parentDecl = structDecl;
+ const auto backingMemberDeclRef = DeclRef<VarDecl>(backingMember->getDefaultDeclRef());
+
+ int bottomOfMember = 0;
+ for(const auto m : groupInfo)
+ {
+ SLANG_ASSERT(bottomOfMember <= backingWidth);
+
+ m.bitFieldModifier->backingDeclRef = backingMemberDeclRef;
+ m.bitFieldModifier->offset = bottomOfMember;
+
+ bottomOfMember += m.bitWidth;
+ }
+
+ const auto backingMemberIndex = groupInfo[0].memberIndex;
+ structDecl->members.insert(backingMemberIndex, backingMember);
+ structDecl->invalidateMemberDictionary();
+ ++memberIndex;
+ }
+ structDecl->buildMemberDictionary();
+
+ // Reset everything
+ backingWidth = 0;
+ totalWidth = 0;
+ groupInfo.clear();
+ };
+ for(; memberIndex < structDecl->members.getCount(); ++memberIndex)
+ {
+ const auto& m = structDecl->members[memberIndex];
+
+ // We can trivially skip any non-property decls
+ const auto v = as<PropertyDecl>(m);
+ if(!v)
+ {
+ // If this is a non-bitfield member then finish the current group
+ if(as<VarDecl>(m))
+ dispatchSomeBitPackedMembers();
+ continue;
+ }
+
+ const auto bfm = m->findModifier<BitFieldModifier>();
+ // If there isn't a bit field modifier, then dispatch the
+ // current group and continue
+ if(!bfm)
+ {
+ dispatchSomeBitPackedMembers();
+ continue;
+ }
+
+ // Verify that this makes sense as a bitfield
+ const auto t = v->type.type->getCanonicalType();
+ SLANG_ASSERT(t);
+ const auto b = as<BasicExpressionType>(t);
+ if(!b)
+ {
+ getSink()->diagnose(v->loc, Diagnostics::bitFieldNonIntegral, t);
+ continue;
+ }
+ const auto baseType = b->getBaseType();
+ const bool isIntegerType = isIntegerBaseType(baseType);
+ if(!isIntegerType)
+ {
+ getSink()->diagnose(v->loc, Diagnostics::bitFieldNonIntegral, t);
+ continue;
+ }
+
+ // The bit width of this member, and the member type width
+ const auto thisFieldWidth = bfm->width;
+ const auto thisFieldTypeWidth = getTypeBitSize(b);
+ SLANG_ASSERT(thisFieldTypeWidth != 0);
+ if(thisFieldWidth > thisFieldTypeWidth)
+ {
+ getSink()->diagnose(
+ v->loc,
+ Diagnostics::bitFieldTooWide,
+ thisFieldWidth,
+ t,
+ thisFieldTypeWidth
+ );
+ // Not much we can do with this field, just ignore it
+ continue;
+ }
+
+ // At this point we're sure that we have a bit field,
+ // update our bit packing state
+
+ // If there's a 0 width type, dispatch the current group
+ if(thisFieldWidth == 0)
+ dispatchSomeBitPackedMembers();
+
+ // If this member wouldn't fit into the current group, dispatch
+ // everything so far;
+ if(totalWidth + thisFieldWidth > std::max(thisFieldTypeWidth, backingWidth))
+ dispatchSomeBitPackedMembers();
+
+ // Add this member to the group,
+ // Grow the backing width if necessary
+ backingWidth = std::max(thisFieldTypeWidth, backingWidth);
+ // Grow the total width
+ totalWidth += int(thisFieldWidth);
+ groupInfo.add({memberIndex, int(thisFieldWidth), t, bfm});
+ }
+ // If the struct ended with a bitpacked member, then make sure we don't forget the last group
+ dispatchSomeBitPackedMembers();
+ }
+
void SemanticsDeclAttributesVisitor::visitFunctionDeclBase(FunctionDeclBase* decl)
{
// Run checking on attributes that can't be fully checked in header checking stage.
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index d91bbb75b..eb3f9486c 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -41,6 +41,8 @@ namespace Slang
return result;
}
+ int getTypeBitSize(Type* t);
+
// A flat representation of basic types (scalars, vectors and matrices)
// that can be used as lookup key in caches
struct BasicTypeKey
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index d638f7be6..037e441d4 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -518,6 +518,10 @@ DIAGNOSTIC(31101, Error, nonSetAccessorMustNotHaveParams, "accessors other than
DIAGNOSTIC(31102, Error, setAccessorMayNotHaveMoreThanOneParam, "a 'set' accessor may not have more than one parameter")
DIAGNOSTIC(31102, Error, setAccessorParamWrongType, "'set' parameter '$0' has type '$1' which does not match the expected type '$2'")
+// 313xx: bit fields
+DIAGNOSTIC(31300, Error, bitFieldTooWide, "bit-field size ($0) exceeds the width of its type $1 ($2)")
+DIAGNOSTIC(31301, Error, bitFieldNonIntegral, "bit-field type ($0) must be an integral type")
+
// 39999 waiting to be placed in the right range
DIAGNOSTIC(39999, Error, expectedIntegerConstantWrongType, "expected integer constant (found: '$0')")
diff --git a/source/slang/slang-ir-autodiff.cpp b/source/slang/slang-ir-autodiff.cpp
index 2cc4e3b57..14b53c0e0 100644
--- a/source/slang/slang-ir-autodiff.cpp
+++ b/source/slang/slang-ir-autodiff.cpp
@@ -96,32 +96,6 @@ IRInst* lookupForwardDerivativeReference(IRInst* primalFunction)
return nullptr;
}
-IRStructField* DifferentialPairTypeBuilder::findField(IRInst* type, IRStructKey* key)
-{
- if (auto irStructType = as<IRStructType>(type))
- {
- for (auto field : irStructType->getFields())
- {
- if (field->getKey() == key)
- {
- return field;
- }
- }
- }
- else if (auto irSpecialize = as<IRSpecialize>(type))
- {
- if (auto irGeneric = as<IRGeneric>(irSpecialize->getBase()))
- {
- if (auto irGenericStructType = as<IRStructType>(findInnerMostGenericReturnVal(irGeneric)))
- {
- return findField(irGenericStructType, key);
- }
- }
- }
-
- return nullptr;
-}
-
IRInst* DifferentialPairTypeBuilder::findSpecializationForParam(IRInst* specializeInst, IRInst* genericParam)
{
// Get base generic that's being specialized.
@@ -162,7 +136,7 @@ IRInst* DifferentialPairTypeBuilder::emitFieldAccessor(IRBuilder* builder, IRIns
if (auto basePairStructType = as<IRStructType>(pairType))
{
return as<IRFieldExtract>(builder->emitFieldExtract(
- findField(basePairStructType, key)->getFieldType(),
+ findStructField(basePairStructType, key)->getFieldType(),
baseInst,
key
));
@@ -178,7 +152,7 @@ IRInst* DifferentialPairTypeBuilder::emitFieldAccessor(IRBuilder* builder, IRIns
builder->getPtrType((IRType*)
findSpecializationForParam(
ptrInnerSpecializedType,
- findField(ptrInnerSpecializedType, key)->getFieldType())),
+ findStructField(ptrInnerSpecializedType, key)->getFieldType())),
baseInst,
key
));
@@ -188,7 +162,7 @@ IRInst* DifferentialPairTypeBuilder::emitFieldAccessor(IRBuilder* builder, IRIns
{
return as<IRFieldAddress>(builder->emitFieldAddress(
builder->getPtrType((IRType*)
- findField(ptrBaseStructType, key)->getFieldType()),
+ findStructField(ptrBaseStructType, key)->getFieldType()),
baseInst,
key));
}
@@ -204,7 +178,7 @@ IRInst* DifferentialPairTypeBuilder::emitFieldAccessor(IRBuilder* builder, IRIns
return as<IRFieldExtract>(builder->emitFieldExtract(
(IRType*)findSpecializationForParam(
specializedType,
- findField(genericBasePairStructType, key)->getFieldType()),
+ findStructField(genericBasePairStructType, key)->getFieldType()),
baseInst,
key
));
@@ -217,7 +191,7 @@ IRInst* DifferentialPairTypeBuilder::emitFieldAccessor(IRBuilder* builder, IRIns
builder->getPtrType((IRType*)
findSpecializationForParam(
specializedType,
- findField(genericPairStructType, key)->getFieldType())),
+ findStructField(genericPairStructType, key)->getFieldType())),
baseInst,
key
));
diff --git a/source/slang/slang-ir-autodiff.h b/source/slang/slang-ir-autodiff.h
index 811002091..d8f0373ac 100644
--- a/source/slang/slang-ir-autodiff.h
+++ b/source/slang/slang-ir-autodiff.h
@@ -308,8 +308,6 @@ struct DifferentialPairTypeBuilder
DifferentialPairTypeBuilder(AutoDiffSharedContext* sharedContext) : sharedContext(sharedContext) {}
- IRStructField* findField(IRInst* type, IRStructKey* key);
-
IRInst* findSpecializationForParam(IRInst* specializeInst, IRInst* genericParam);
IRInst* emitFieldAccessor(IRBuilder* builder, IRInst* baseInst, IRStructKey* key);
diff --git a/source/slang/slang-ir-bit-field-accessors.cpp b/source/slang/slang-ir-bit-field-accessors.cpp
new file mode 100644
index 000000000..3ca660a30
--- /dev/null
+++ b/source/slang/slang-ir-bit-field-accessors.cpp
@@ -0,0 +1,160 @@
+#include "slang-ir-bit-field-accessors.h"
+
+#include "slang-ir.h"
+#include "slang-ir-insts.h"
+
+namespace Slang
+{
+static IRInst* maybeUnwrapGeneric(IRInst* inst)
+{
+ if(const auto g = as<IRGeneric>(inst))
+ return findInnerMostGenericReturnVal(g);
+ return inst;
+}
+
+static IRInst* maybeUnwrapSpecialize(IRInst* inst)
+{
+ if(const auto g = as<IRSpecialize>(inst))
+ return maybeUnwrapGeneric(maybeUnwrapSpecialize(g->getBase()));
+ return inst;
+}
+
+static IRInst* shl(IRBuilder& builder, IRInst* inst, const IRIntegerValue value)
+{
+ if(value == 0)
+ return inst;
+ const auto [width, isSigned] = getIntTypeInfo(inst->getDataType());
+ if(value >= width)
+ return builder.getIntValue(inst->getDataType(), 0);
+ if(value == 0)
+ return inst;
+ return builder.emitShl(inst->getDataType(), inst, builder.getIntValue(builder.getIntType(), value));
+}
+
+static IRInst* shr(IRBuilder& builder, IRInst* inst, const IRIntegerValue value)
+{
+ if(value == 0)
+ return inst;
+ const auto [width, isSigned] = getIntTypeInfo(inst->getDataType());
+ // If it's not signed, then we just shift all the set bits away
+ if(value >= width && !isSigned)
+ return builder.getIntValue(inst->getDataType(), 0);
+ // Since on many platforms bit shifting by the number of bits in the number
+ // is undefined, correct this here assuming that the Slang IR has the same
+ // restriction
+ if(value >= width && isSigned)
+ return builder.emitShr(inst->getDataType(), inst, builder.getIntValue(builder.getIntType(), width-1));
+ if(value == 0)
+ return inst;
+ return builder.emitShr(inst->getDataType(), inst, builder.getIntValue(builder.getIntType(), value));
+}
+
+static void synthesizeBitFieldGetter(IRFunc* func, IRBitFieldAccessorDecoration* dec)
+{
+ const auto bitFieldType = func->getResultType();
+ SLANG_ASSERT(isIntegralType(bitFieldType));
+ SLANG_ASSERT(func->getParamCount() == 1);
+ const auto structParamType = func->getParamType(0);
+ const auto structType = as<IRStructType>(maybeUnwrapSpecialize(structParamType));
+ SLANG_ASSERT(structType);
+
+ const auto backingMember = findStructField(structType, dec->getBackingMemberKey());
+ const auto backingType = backingMember->getFieldType();
+ SLANG_ASSERT(isIntegralType(backingType));
+
+ IRBuilder builder{func};
+
+ const auto isSigned = getIntTypeInfo(func->getResultType()).isSigned;
+ builder.setInsertInto(func);
+ builder.emitBlock();
+ const auto s = builder.emitParam(structParamType);
+
+ // Construct the equivalent of this:
+ // Note the cast of the backing value in order to get the correct sign
+ // extension behaviour on the right shift
+ // return (int(_backing) << (backingWidth-topOfFoo)) >> (backingWidth-fooWidth);
+
+ const auto backingWidth = getIntTypeInfo(backingType).width;
+ const auto fieldWidth = dec->getFieldWidth();
+ const auto topOfField = dec->getFieldOffset() + fieldWidth;
+ const auto leftShiftAmount = backingWidth - topOfField;
+ const auto rightShiftAmount = backingWidth - fieldWidth;
+ const auto backingValue = builder.emitFieldExtract(backingType, s, dec->getBackingMemberKey());
+ const auto castBackingType = builder.getType(getIntTypeOpFromInfo({backingWidth, isSigned}));
+ const auto castedBacking = builder.emitCast(castBackingType, backingValue);
+ const auto leftShifted = shl(builder, castedBacking, leftShiftAmount);
+ const auto rightShifted = shr(builder, leftShifted, rightShiftAmount);
+ const auto castedToBitFieldType = builder.emitCast(bitFieldType, rightShifted);
+ builder.emitReturn(castedToBitFieldType);
+
+ builder.addSimpleDecoration<IRForceInlineDecoration>(func);
+}
+
+static IRIntegerValue setLowBits(IRIntegerValue bits)
+{
+ SLANG_ASSERT(bits >= 0 && bits <= 64);
+ return ~(bits >= 64 ? 0 : (~0 << bits));
+}
+
+static void synthesizeBitFieldSetter(IRFunc* func, IRBitFieldAccessorDecoration* dec)
+{
+ SLANG_ASSERT(func->getParamCount() == 2);
+ const auto ptrType = as<IRPtrTypeBase>(func->getParamType(0));
+ SLANG_ASSERT(ptrType);
+ const auto structParamType = ptrType->getValueType();
+ const auto structType = as<IRStructType>(maybeUnwrapSpecialize(structParamType));
+ SLANG_ASSERT(structType);
+ const auto bitFieldType = func->getParamType(1);
+ SLANG_ASSERT(isIntegralType(bitFieldType));
+
+ const auto backingMember = findStructField(structType, dec->getBackingMemberKey());
+ const auto backingType = backingMember->getFieldType();
+ SLANG_ASSERT(isIntegralType(backingType));
+
+ IRBuilder builder{func};
+
+ builder.setInsertInto(func);
+ builder.emitBlock();
+ const auto s = builder.emitParam(ptrType);
+ const auto v = builder.emitParam(bitFieldType);
+
+ // Construct the equivalent of this:
+ // let fooMask = 0x00000FF0;
+ // let bottomOfFoo = 4;
+ // _backing = int((_backing & ~fooMask) | ((int(x) << bottomOfFoo) & fooMask));
+
+ const auto fieldWidth = dec->getFieldWidth();
+ const auto bottomOfField = dec->getFieldOffset();
+ const auto maskBits = setLowBits(fieldWidth) << bottomOfField;
+ const auto mask = builder.getIntValue(backingType, maskBits);
+ const auto notMask = builder.getIntValue(backingType, ~maskBits);
+ const auto memberAddr = builder.emitFieldAddress(builder.getPtrType(backingType), s, dec->getBackingMemberKey());
+ const auto backingValue = builder.emitLoad(memberAddr);
+ const auto maskedOut = builder.emitBitAnd(backingType, backingValue, notMask);
+ const auto castValue = builder.emitCast(backingType, v);
+ const auto shiftedLeft = shl(builder, castValue, bottomOfField);
+ const auto maskedValue = builder.emitBitAnd(backingType, shiftedLeft, mask);
+ const auto combined = builder.emitBitOr(backingType, maskedOut, maskedValue);
+ builder.emitStore(memberAddr, combined);
+ builder.emitReturn();
+
+ builder.addSimpleDecoration<IRForceInlineDecoration>(func);
+}
+
+void synthesizeBitFieldAccessors(IRModule* module)
+{
+ for(const auto inst : module->getModuleInst()->getGlobalInsts())
+ {
+ const auto func = as<IRFunc>(maybeUnwrapGeneric(inst));
+ if(!func)
+ continue;
+ const auto bfd = func->findDecoration<IRBitFieldAccessorDecoration>();
+ if(!bfd)
+ continue;
+ if(func->getParamCount() == 1)
+ synthesizeBitFieldGetter(func, bfd);
+ else
+ synthesizeBitFieldSetter(func, bfd);
+ }
+}
+}
diff --git a/source/slang/slang-ir-bit-field-accessors.h b/source/slang/slang-ir-bit-field-accessors.h
new file mode 100644
index 000000000..f97eb8748
--- /dev/null
+++ b/source/slang/slang-ir-bit-field-accessors.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace Slang
+{
+ struct IRModule;
+ void synthesizeBitFieldAccessors(IRModule* module);
+}
diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h
index 5439a8d5b..656b3d320 100644
--- a/source/slang/slang-ir-inst-defs.h
+++ b/source/slang/slang-ir-inst-defs.h
@@ -970,7 +970,11 @@ INST(HighLevelDeclDecoration, highLevelDecl, 1, 0)
/// Recognized by SPIRV-emit pass so we can emit a SPIRV `Block` decoration.
INST(SPIRVBlockDecoration, spvBlock, 0, 0)
- INST_RANGE(Decoration, HighLevelDeclDecoration, SPIRVBlockDecoration)
+ /// Marks a function as one which access a bitfield with the specified
+ /// backing value key, width and offset
+ INST(BitFieldAccessorDecoration, BitFieldAccessorDecoration, 3, 0)
+
+ INST_RANGE(Decoration, HighLevelDeclDecoration, BitFieldAccessorDecoration)
//
diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h
index 114250bae..620695d46 100644
--- a/source/slang/slang-ir-insts.h
+++ b/source/slang/slang-ir-insts.h
@@ -1240,6 +1240,14 @@ struct IRUnpackAnyValue : IRInst
IRInst* getValue() { return getOperand(0); }
};
+struct IRBitFieldAccessorDecoration : IRDecoration
+{
+ IR_LEAF_ISA(BitFieldAccessorDecoration);
+ IRStructKey* getBackingMemberKey() { return cast<IRStructKey>(getOperand(0)); }
+ IRIntegerValue getFieldWidth() { return as<IRIntLit>(getOperand(1))->getValue(); }
+ IRIntegerValue getFieldOffset() { return as<IRIntLit>(getOperand(2))->getValue(); }
+};
+
// Layout decorations
/// A decoration that marks a field key as having been associated
diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp
index 3f10e3d3e..68b002153 100644
--- a/source/slang/slang-ir.cpp
+++ b/source/slang/slang-ir.cpp
@@ -7277,6 +7277,19 @@ namespace Slang
}
}
+ IROp getIntTypeOpFromInfo(const IntInfo info)
+ {
+ switch(info.width)
+ {
+ case 8: return info.isSigned ? kIROp_Int8Type : kIROp_UInt8Type;
+ case 16: return info.isSigned ? kIROp_Int16Type : kIROp_UInt16Type;
+ case 32: return info.isSigned ? kIROp_IntType : kIROp_UIntType;
+ case 64: return info.isSigned ? kIROp_Int64Type : kIROp_UInt64Type;
+ default:
+ SLANG_UNEXPECTED("Unhandled info passed to getIntTypeOpFromInfo");
+ }
+ }
+
FloatInfo getFloatingTypeInfo(const IRType* floatType)
{
switch(floatType->getOp())
@@ -7303,6 +7316,32 @@ namespace Slang
}
}
+ IRStructField* findStructField(IRInst* type, IRStructKey* key)
+ {
+ if (auto irStructType = as<IRStructType>(type))
+ {
+ for (auto field : irStructType->getFields())
+ {
+ if (field->getKey() == key)
+ {
+ return field;
+ }
+ }
+ }
+ else if (auto irSpecialize = as<IRSpecialize>(type))
+ {
+ if (auto irGeneric = as<IRGeneric>(irSpecialize->getBase()))
+ {
+ if (auto irGenericStructType = as<IRStructType>(findInnerMostGenericReturnVal(irGeneric)))
+ {
+ return findStructField(irGenericStructType, key);
+ }
+ }
+ }
+
+ return nullptr;
+ }
+
void findAllInstsBreadthFirst(IRInst* inst, List<IRInst*>& outInsts)
{
Index index = outInsts.getCount();
diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h
index 3a459a501..f1c079cae 100644
--- a/source/slang/slang-ir.h
+++ b/source/slang/slang-ir.h
@@ -35,6 +35,8 @@ struct IRFunc;
struct IRGlobalValueWithCode;
struct IRInst;
struct IRModule;
+struct IRStructField;
+struct IRStructKey;
typedef unsigned int IROpFlags;
enum : IROpFlags
@@ -1036,6 +1038,9 @@ struct IntInfo
IntInfo getIntTypeInfo(const IRType* intType);
+// left-inverse of getIntTypeInfo
+IROp getIntTypeOpFromInfo(const IntInfo info);
+
struct FloatInfo
{
Int width;
@@ -1046,6 +1051,8 @@ FloatInfo getFloatingTypeInfo(const IRType* floatType);
bool isIntegralScalarOrCompositeType(IRType* t);
+IRStructField* findStructField(IRInst* type, IRStructKey* key);
+
void findAllInstsBreadthFirst(IRInst* inst, List<IRInst*>& outInsts);
// Constant Instructions
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index cb9c8fc40..e4e324def 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -9,6 +9,7 @@
#include "../core/slang-performance-profiler.h"
#include "slang-check.h"
+#include "slang-ir-bit-field-accessors.h"
#include "slang-ir-loop-inversion.h"
#include "slang-ir.h"
#include "slang-ir-constexpr.h"
@@ -8872,6 +8873,25 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
}
}
+ void addBitFieldAccessorDecorations(IRInst* irFunc, Decl* decl)
+ {
+ // If this is an accessor under a property we can move the bitfield
+ // modifiers on the property to the accessor function.
+ if(as<AccessorDecl>(decl) && as<PropertyDecl>(decl->parentDecl))
+ {
+ if(const auto bfm = decl->parentDecl->findModifier<BitFieldModifier>())
+ {
+ getBuilder()->addDecoration(
+ irFunc,
+ kIROp_BitFieldAccessorDecoration,
+ getSimpleVal(context, ensureDecl(context, bfm->backingDeclRef.getDecl())),
+ getBuilder()->getIntValue(getBuilder()->getIntType(), bfm->width),
+ getBuilder()->getIntValue(getBuilder()->getIntType(), bfm->offset)
+ );
+ }
+ }
+ }
+
/// Is `decl` a member function (or effectively a member function) when considered as a stdlib declaration?
bool isStdLibMemberFuncDecl(
Decl* inDecl)
@@ -9345,6 +9365,8 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo>
bool isInline = false;
+ addBitFieldAccessorDecorations(irFunc, decl);
+
for (auto modifier : decl->modifiers)
{
if (as<RequiresNVAPIAttribute>(modifier))
@@ -10360,6 +10382,9 @@ RefPtr<IRModule> generateIRForTranslationUnit(
// normal `call` + `ifElse`, etc.
lowerErrorHandling(module, compileRequest->getSink());
+ // Synthesize some code we want to make sure is inlined and simplified
+ synthesizeBitFieldAccessors(module);
+
// Generate DebugValue insts to store values into debug variables,
// if debug symbols are enabled.
if (compileRequest->getLinkage()->m_optionSet.getEnumOption<DebugInfoLevel>(CompilerOptionName::DebugInformation) != DebugInfoLevel::None)
diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp
index 5555647f4..3f1ed3f76 100644
--- a/source/slang/slang-parser.cpp
+++ b/source/slang/slang-parser.cpp
@@ -2970,6 +2970,15 @@ namespace Slang
semantic->name = parser->ReadToken(TokenType::Identifier);
return semantic;
}
+ else if (parser->LookAheadToken(TokenType::IntegerLiteral))
+ {
+ BitFieldModifier* bitWidthMod = parser->astBuilder->create<BitFieldModifier>();
+ parser->FillPosition(bitWidthMod);
+ const auto token = parser->tokenReader.advanceToken();
+ UnownedStringSlice suffix;
+ bitWidthMod->width = getIntegerLiteralValue(token, &suffix);
+ return bitWidthMod;
+ }
else if (parser->LookAheadToken(TokenType::CompletionRequest))
{
HLSLSimpleSemantic* semantic = parser->astBuilder->create<HLSLSimpleSemantic>();