summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/command-line-slangc-reference.md5
-rw-r--r--include/slang.h3
-rw-r--r--source/slang/slang-check-decl.cpp49
-rw-r--r--source/slang/slang-options.cpp8
-rw-r--r--tests/language-feature/bitfield/msvc-mixed.slang40
-rw-r--r--tests/language-feature/bitfield/msvc-repr-mixed.slang38
-rw-r--r--tests/language-feature/bitfield/msvc-repr.slang36
-rw-r--r--tests/language-feature/bitfield/msvc-simple.slang31
-rw-r--r--tests/language-feature/bitfield/msvc-sizeof.slang43
-rw-r--r--tests/language-feature/bitfield/msvc-zero-width.slang30
-rw-r--r--tests/language-feature/bitfield/repr-mixed.slang36
-rw-r--r--tests/language-feature/bitfield/repr.slang36
12 files changed, 348 insertions, 7 deletions
diff --git a/docs/command-line-slangc-reference.md b/docs/command-line-slangc-reference.md
index 8cba2107f..650ab867a 100644
--- a/docs/command-line-slangc-reference.md
+++ b/docs/command-line-slangc-reference.md
@@ -361,6 +361,11 @@ Include additional type conformance during linking for dynamic dispatch.
Emit reflection data in JSON format to a file.
+<a id="msvc-style-bitfield-packing"></a>
+### -msvc-style-bitfield-packing
+Pack bitfields according to MSVC rules (msb first, new field when underlying type size changes) rather than gcc-style (lsb first)
+
+
<a id="Target"></a>
## Target
diff --git a/include/slang.h b/include/slang.h
index 516af9603..65449a1ff 100644
--- a/include/slang.h
+++ b/include/slang.h
@@ -1044,6 +1044,9 @@ typedef uint32_t SlangSizeT;
DenormalModeFp32,
DenormalModeFp64,
+ // Bitfield options
+ UseMSVCStyleBitfieldPacking, // bool
+
CountOf,
};
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index 02f404d88..98cdb9e61 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -14020,6 +14020,10 @@ void SemanticsDeclAttributesVisitor::visitStructDecl(StructDecl* structDecl)
checkRayPayloadStructFields(structDecl);
}
+ // Check if we should use MSVC-style bitfield packing
+ const bool useMSVCPacking =
+ getOptionSet().getBoolOption(CompilerOptionName::UseMSVCStyleBitfieldPacking);
+
int backingWidth = 0;
[[maybe_unused]] int totalWidth = 0;
struct BitFieldInfo
@@ -14055,15 +14059,32 @@ void SemanticsDeclAttributesVisitor::visitStructDecl(StructDecl* structDecl)
backingMember->parentDecl = structDecl;
const auto backingMemberDeclRef = DeclRef<VarDecl>(backingMember->getDefaultDeclRef());
- int bottomOfMember = 0;
- for (const auto m : groupInfo)
+ if (useMSVCPacking)
{
- SLANG_ASSERT(bottomOfMember <= backingWidth);
+ // MSVC packs from MSB to LSB
+ int currentBitPosition = backingWidth;
+ for (const auto& m : groupInfo)
+ {
+ currentBitPosition -= m.bitWidth;
+ SLANG_ASSERT(currentBitPosition >= 0);
- m.bitFieldModifier->backingDeclRef = backingMemberDeclRef;
- m.bitFieldModifier->offset = bottomOfMember;
+ m.bitFieldModifier->backingDeclRef = backingMemberDeclRef;
+ m.bitFieldModifier->offset = currentBitPosition;
+ }
+ }
+ else
+ {
+ // GCC/Clang pack from LSB to MSB
+ int bottomOfMember = 0;
+ for (const auto& m : groupInfo)
+ {
+ SLANG_ASSERT(bottomOfMember <= backingWidth);
- bottomOfMember += m.bitWidth;
+ m.bitFieldModifier->backingDeclRef = backingMemberDeclRef;
+ m.bitFieldModifier->offset = bottomOfMember;
+
+ bottomOfMember += m.bitWidth;
+ }
}
const auto backingMemberIndex = groupInfo[0].memberIndex;
@@ -14080,6 +14101,9 @@ void SemanticsDeclAttributesVisitor::visitStructDecl(StructDecl* structDecl)
totalWidth = 0;
groupInfo.clear();
};
+
+ int previousFieldTypeWidth = 0; // Track the type width of the previous bitfield for MSVC mode
+
for (; memberIndex < structDecl->getDirectMemberDeclCount(); ++memberIndex)
{
const auto& m = structDecl->getDirectMemberDecl(memberIndex);
@@ -14141,7 +14165,17 @@ void SemanticsDeclAttributesVisitor::visitStructDecl(StructDecl* structDecl)
// If there's a 0 width type, dispatch the current group
if (thisFieldWidth == 0)
+ {
dispatchSomeBitPackedMembers();
+ previousFieldTypeWidth = 0;
+ }
+
+ // MSVC-specific behavior: start a new backing field if the type size changes
+ if (useMSVCPacking && groupInfo.getCount() > 0 &&
+ thisFieldTypeWidth != previousFieldTypeWidth)
+ {
+ dispatchSomeBitPackedMembers();
+ }
// If this member wouldn't fit into the current group, dispatch
// everything so far;
@@ -14154,6 +14188,9 @@ void SemanticsDeclAttributesVisitor::visitStructDecl(StructDecl* structDecl)
// Grow the total width
totalWidth += int(thisFieldWidth);
groupInfo.add({memberIndex, int(thisFieldWidth), t, bfm});
+
+ // Track the type width for MSVC mode
+ previousFieldTypeWidth = thisFieldTypeWidth;
}
// If the struct ended with a bitpacked member, then make sure we don't forget the last
// group
diff --git a/source/slang/slang-options.cpp b/source/slang/slang-options.cpp
index 0ada33697..c3f2ad055 100644
--- a/source/slang/slang-options.cpp
+++ b/source/slang/slang-options.cpp
@@ -566,7 +566,12 @@ void initCommandOptions(CommandOptions& options)
{OptionKind::EmitReflectionJSON,
"-reflection-json",
"-reflection-json <path>",
- "Emit reflection data in JSON format to a file."}};
+ "Emit reflection data in JSON format to a file."},
+ {OptionKind::UseMSVCStyleBitfieldPacking,
+ "-msvc-style-bitfield-packing",
+ nullptr,
+ "Pack bitfields according to MSVC rules (msb first, new field when underlying type size "
+ "changes) rather than gcc-style (lsb first)"}};
_addOptions(makeConstArrayView(generalOpts), options);
@@ -2264,6 +2269,7 @@ SlangResult OptionsParser::_parse(int argc, char const* const* argv)
case OptionKind::LoopInversion:
case OptionKind::UnscopedEnum:
case OptionKind::PreserveParameters:
+ case OptionKind::UseMSVCStyleBitfieldPacking:
linkage->m_optionSet.set(optionKind, true);
break;
case OptionKind::MatrixLayoutRow:
diff --git a/tests/language-feature/bitfield/msvc-mixed.slang b/tests/language-feature/bitfield/msvc-mixed.slang
new file mode 100644
index 000000000..d21bdad37
--- /dev/null
+++ b/tests/language-feature/bitfield/msvc-mixed.slang
@@ -0,0 +1,40 @@
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -output-using-type -compile-arg -msvc-style-bitfield-packing
+
+// MSVC packing with mixed type sizes
+// CHECK: 15
+// CHECK-NEXT: 255
+// CHECK-NEXT: 4095
+// CHECK-NEXT: 8
+
+//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer
+RWStructuredBuffer<uint> outputBuffer;
+
+struct MixedTypes {
+ uint8_t a : 4; // Uses uint8_t backing, bits 7-4
+ uint8_t b : 4; // Same backing, bits 3-0
+ uint16_t c : 8; // New uint16_t backing due to type change, bits 15-8
+ uint16_t d : 8; // Same backing, bits 7-0
+ uint32_t e : 12; // New uint32_t backing due to type change, bits 31-20
+ uint32_t f : 12; // Same backing, bits 19-8
+ uint32_t g : 8; // Same backing, bits 7-0
+};
+
+[numthreads(1, 1, 1)]
+void computeMain()
+{
+ MixedTypes m;
+ m.a = 15; // 0xF
+ m.b = 15; // 0xF
+ m.c = 255; // 0xFF
+ m.d = 255; // 0xFF
+ m.e = 4095; // 0xFFF
+ m.f = 4095; // 0xFFF
+ m.g = 255; // 0xFF
+
+ outputBuffer[0] = m.a;
+ outputBuffer[1] = m.c;
+ outputBuffer[2] = m.e;
+ // Verify struct size includes all three backing fields
+ outputBuffer[3] = sizeof(MixedTypes);
+}
+
diff --git a/tests/language-feature/bitfield/msvc-repr-mixed.slang b/tests/language-feature/bitfield/msvc-repr-mixed.slang
new file mode 100644
index 000000000..47f03ad1d
--- /dev/null
+++ b/tests/language-feature/bitfield/msvc-repr-mixed.slang
@@ -0,0 +1,38 @@
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -compile-arg -msvc-style-bitfield-packing
+
+// MSVC creates separate backing fields for different type sizes
+// struct MixedSizes { uint8_t a:4; uint8_t b:4; uint16_t c:8; uint16_t d:8; }
+// First backing (uint8_t): a=0xA (bits 4-7), b=0xB (bits 0-3) => 0xAB
+// Second backing (uint16_t): c=0xCD (bits 8-15), d=0xEF (bits 0-7) => 0xCDEF
+// Memory layout: [0xAB, padding, 0xCDEF] or [0xAB, padding, 0xEF, 0xCD] depending on endianness
+
+// CHECK: AB
+// CHECK-NEXT: CDEF
+
+//TEST_INPUT:ubuffer(data=[0 0], stride=4):out,name=outputBuffer
+RWStructuredBuffer<uint> outputBuffer;
+
+struct MixedSizes {
+ uint8_t a : 4; // First backing field (uint8_t)
+ uint8_t b : 4; // Same backing field
+ uint16_t c : 8; // New backing field (uint16_t) due to type change
+ uint16_t d : 8; // Same backing field
+};
+
+[numthreads(1, 1, 1)]
+void computeMain()
+{
+ MixedSizes m;
+ m.a = 0xA;
+ m.b = 0xB;
+ m.c = 0xCD;
+ m.d = 0xEF;
+
+ // Read the two backing fields separately
+ uint8_t* p8 = (uint8_t*)&m;
+ uint16_t* p16 = (uint16_t*)((uint8_t*)&m + 2); // Skip uint8_t + padding
+
+ outputBuffer[0] = uint(*p8);
+ outputBuffer[1] = uint(*p16);
+}
+
diff --git a/tests/language-feature/bitfield/msvc-repr.slang b/tests/language-feature/bitfield/msvc-repr.slang
new file mode 100644
index 000000000..4edd47556
--- /dev/null
+++ b/tests/language-feature/bitfield/msvc-repr.slang
@@ -0,0 +1,36 @@
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -compile-arg -msvc-style-bitfield-packing
+
+// MSVC style packs from MSB to LSB
+// struct S { uint a:4; uint b:8; uint c:4; uint d:16; }
+// Memory layout (32-bit):
+// bits 28-31: a (0x5)
+// bits 20-27: b (0xAB)
+// bits 16-19: c (0xC)
+// bits 0-15: d (0xDEF0)
+// Expected: 0x5ABCDEF0
+
+// CHECK: 5ABCDEF0
+
+//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer
+RWStructuredBuffer<uint> outputBuffer;
+
+struct S {
+ uint a : 4; // bits 28-31 (MSB side)
+ uint b : 8; // bits 20-27
+ uint c : 4; // bits 16-19
+ uint d : 16; // bits 0-15
+};
+
+[numthreads(1, 1, 1)]
+void computeMain()
+{
+ S s;
+ s.a = 0x5;
+ s.b = 0xAB;
+ s.c = 0xC;
+ s.d = 0xDEF0;
+
+ // Write the struct to memory and read it back as uint
+ outputBuffer[0] = *((uint*)&s);
+}
+
diff --git a/tests/language-feature/bitfield/msvc-simple.slang b/tests/language-feature/bitfield/msvc-simple.slang
new file mode 100644
index 000000000..4651626b8
--- /dev/null
+++ b/tests/language-feature/bitfield/msvc-simple.slang
@@ -0,0 +1,31 @@
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -output-using-type -compile-arg -msvc-style-bitfield-packing
+
+// With MSVC packing, bitfields are packed from MSB to LSB
+// and new backing fields start when type sizes change
+// CHECK: 123
+// CHECK-NEXT: 4567
+// CHECK-NEXT: 0
+// CHECK-NEXT: 0
+
+//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer
+RWStructuredBuffer<uint> outputBuffer;
+
+struct S {
+ int foo : 8; // Packed in bits 31-24
+ uint bar : 24; // Packed in bits 23-0
+};
+
+[numthreads(1, 1, 1)]
+void computeMain()
+{
+ S s;
+ s.foo = 123;
+ s.bar = 4567;
+ outputBuffer[0] = s.foo;
+ outputBuffer[1] = s.bar;
+ s.foo = 0;
+ s.bar = 0;
+ outputBuffer[2] = s.foo;
+ outputBuffer[3] = s.bar;
+}
+
diff --git a/tests/language-feature/bitfield/msvc-sizeof.slang b/tests/language-feature/bitfield/msvc-sizeof.slang
new file mode 100644
index 000000000..9d4ccdcfc
--- /dev/null
+++ b/tests/language-feature/bitfield/msvc-sizeof.slang
@@ -0,0 +1,43 @@
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -output-using-type -compile-arg -msvc-style-bitfield-packing
+
+// MSVC starts new backing fields when type sizes change
+// CHECK: 4
+// CHECK-NEXT: 12
+// CHECK-NEXT: 8
+// CHECK-NEXT: 16
+
+//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer
+RWStructuredBuffer<uint> outputBuffer;
+
+struct S {
+ int foo : 8;
+ int bar : 24;
+};
+
+// MSVC will use separate backing fields for different sized types
+struct T {
+ int64_t foo : 33; // Uses int64_t backing
+ int bar : 24; // Uses separate int backing due to type size change
+};
+
+// This still takes two ints to store all the bits
+struct P {
+ int foo : 24;
+ int bar : 24;
+};
+
+// MSVC will use separate backing fields due to type size difference
+struct Q {
+ int8_t foo : 1; // Uses int8_t backing
+ int64_t bar : 63; // Uses separate int64_t backing
+};
+
+[numthreads(1, 1, 1)]
+void computeMain()
+{
+ outputBuffer[0] = sizeof(S);
+ outputBuffer[1] = sizeof(T);
+ outputBuffer[2] = sizeof(P);
+ outputBuffer[3] = sizeof(Q);
+}
+
diff --git a/tests/language-feature/bitfield/msvc-zero-width.slang b/tests/language-feature/bitfield/msvc-zero-width.slang
new file mode 100644
index 000000000..3363ef066
--- /dev/null
+++ b/tests/language-feature/bitfield/msvc-zero-width.slang
@@ -0,0 +1,30 @@
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -output-using-type -compile-arg -msvc-style-bitfield-packing
+
+// MSVC packing with zero-width bitfields
+// CHECK: 123
+// CHECK-NEXT: 4567
+// CHECK-NEXT: 1
+// CHECK-NEXT: 0
+
+//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer
+RWStructuredBuffer<uint> outputBuffer;
+
+struct S {
+ int foo : 8; // First backing field
+ int breaker : 0; // Forces new backing field
+ int bar : 24; // Second backing field
+};
+
+[numthreads(1, 1, 1)]
+void computeMain()
+{
+ S s;
+ s.foo = 123;
+ s.bar = 4567;
+ s.breaker = 9999; // Zero-width field, assignment has no effect
+ outputBuffer[0] = s.foo;
+ outputBuffer[1] = s.bar;
+ outputBuffer[2] = sizeof(S) == sizeof(int) * 2; // Two backing fields
+ outputBuffer[3] = s.breaker; // Always returns 0
+}
+
diff --git a/tests/language-feature/bitfield/repr-mixed.slang b/tests/language-feature/bitfield/repr-mixed.slang
new file mode 100644
index 000000000..44a3f69d9
--- /dev/null
+++ b/tests/language-feature/bitfield/repr-mixed.slang
@@ -0,0 +1,36 @@
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu
+
+// Default packing keeps all fields in one backing field when they fit
+// struct MixedSizes { uint8_t a:4; uint8_t b:4; uint16_t c:8; uint16_t d:8; }
+// All packed in single uint32_t backing:
+// bits 0-3: a (0xA)
+// bits 4-7: b (0xB)
+// bits 8-15: c (0xCD)
+// bits 16-23: d (0xEF)
+// Expected: 0x00EFCDBA (top 8 bits unused)
+
+// CHECK: EFCDBA
+
+//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer
+RWStructuredBuffer<uint> outputBuffer;
+
+struct MixedSizes {
+ uint8_t a : 4; // bits 0-3
+ uint8_t b : 4; // bits 4-7
+ uint16_t c : 8; // bits 8-15
+ uint16_t d : 8; // bits 16-23
+};
+
+[numthreads(1, 1, 1)]
+void computeMain()
+{
+ MixedSizes m;
+ m.a = 0xA;
+ m.b = 0xB;
+ m.c = 0xCD;
+ m.d = 0xEF;
+
+ // With default packing, all fields are in one backing field
+ outputBuffer[0] = *((uint*)&m) & 0xFFFFFF; // Mask to 24 bits
+}
+
diff --git a/tests/language-feature/bitfield/repr.slang b/tests/language-feature/bitfield/repr.slang
new file mode 100644
index 000000000..a199b23ff
--- /dev/null
+++ b/tests/language-feature/bitfield/repr.slang
@@ -0,0 +1,36 @@
+//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu
+
+// Default GCC/Clang style packs from LSB to MSB
+// struct S { uint a:4; uint b:8; uint c:4; uint d:16; }
+// Memory layout (32-bit):
+// bits 0-3: a (0x5)
+// bits 4-11: b (0xAB)
+// bits 12-15: c (0xC)
+// bits 16-31: d (0xDEF0)
+// Expected: 0xDEF0CAB5
+
+// CHECK: DEF0CAB5
+
+//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer
+RWStructuredBuffer<uint> outputBuffer;
+
+struct S {
+ uint a : 4; // bits 0-3
+ uint b : 8; // bits 4-11
+ uint c : 4; // bits 12-15
+ uint d : 16; // bits 16-31
+};
+
+[numthreads(1, 1, 1)]
+void computeMain()
+{
+ S s;
+ s.a = 0x5;
+ s.b = 0xAB;
+ s.c = 0xC;
+ s.d = 0xDEF0;
+
+ // Write the struct to memory and read it back as uint
+ outputBuffer[0] = *((uint*)&s);
+}
+