From d979b5048009c3909cfc13476a78a12ae5f4d61b Mon Sep 17 00:00:00 2001 From: Ellie Hermaszewska Date: Fri, 1 Mar 2024 01:50:19 +0800 Subject: Add support for bitfields (#3639) * Add support for bitfields Closes https://github.com/shader-slang/slang/issues/3559 * Set scopes for syntsized bitfield accessors * Simplify generated code for bitfield accessors * spelling * regenerate vs project * warnings --- .../platform-test/platform-test.vcxproj | 608 ++++++++++----------- .../platform-test/platform-test.vcxproj.filters | 24 +- .../slang-capability-generator.vcxproj | 590 ++++++++++---------- .../slang-capability-generator.vcxproj.filters | 40 +- build/visual-studio/slang/slang.vcxproj | 2 + build/visual-studio/slang/slang.vcxproj.filters | 6 + source/slang/slang-ast-modifier.h | 12 + source/slang/slang-check-conversion.cpp | 2 +- source/slang/slang-check-decl.cpp | 187 +++++++ source/slang/slang-check-impl.h | 2 + source/slang/slang-diagnostic-defs.h | 4 + source/slang/slang-ir-autodiff.cpp | 36 +- source/slang/slang-ir-autodiff.h | 2 - source/slang/slang-ir-bit-field-accessors.cpp | 160 ++++++ source/slang/slang-ir-bit-field-accessors.h | 7 + source/slang/slang-ir-inst-defs.h | 6 +- source/slang/slang-ir-insts.h | 8 + source/slang/slang-ir.cpp | 39 ++ source/slang/slang-ir.h | 7 + source/slang/slang-lower-to-ir.cpp | 25 + source/slang/slang-parser.cpp | 9 + tests/language-feature/bitfield/generic.slang | 29 + tests/language-feature/bitfield/non-integral.slang | 14 + tests/language-feature/bitfield/overflow.slang | 29 + tests/language-feature/bitfield/simple.slang | 74 +++ tests/language-feature/bitfield/sizeof.slang | 42 ++ tests/language-feature/bitfield/too-large.slang | 14 + tests/language-feature/bitfield/type-alias.slang | 32 ++ tests/language-feature/bitfield/zero-width.slang | 28 + 29 files changed, 1372 insertions(+), 666 deletions(-) create mode 100644 source/slang/slang-ir-bit-field-accessors.cpp create mode 100644 source/slang/slang-ir-bit-field-accessors.h create mode 100644 tests/language-feature/bitfield/generic.slang create mode 100644 tests/language-feature/bitfield/non-integral.slang create mode 100644 tests/language-feature/bitfield/overflow.slang create mode 100644 tests/language-feature/bitfield/simple.slang create mode 100644 tests/language-feature/bitfield/sizeof.slang create mode 100644 tests/language-feature/bitfield/too-large.slang create mode 100644 tests/language-feature/bitfield/type-alias.slang create mode 100644 tests/language-feature/bitfield/zero-width.slang diff --git a/build/visual-studio/platform-test/platform-test.vcxproj b/build/visual-studio/platform-test/platform-test.vcxproj index 7f9eb6013..7ea0cb675 100644 --- a/build/visual-studio/platform-test/platform-test.vcxproj +++ b/build/visual-studio/platform-test/platform-test.vcxproj @@ -1,305 +1,305 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Debug - ARM64 - - - Debug aarch64 - Win32 - - - Debug aarch64 - x64 - - - Debug aarch64 - ARM64 - - - Release - Win32 - - - Release - x64 - - - Release - ARM64 - - - Release aarch64 - Win32 - - - Release aarch64 - x64 - - - Release aarch64 - ARM64 - - - - {F385D6A7-DF6C-989F-88BD-FEBC74831106} - true - Win32Proj - platform-test - - - - Application - true - Unicode - v142 - - - Application - true - Unicode - v142 - - - Application - true - Unicode - v142 - true - - - Application - false - Unicode - v142 - - - Application - false - Unicode - v142 - - - Application - false - Unicode - v142 - true - - - - - - - - - - - - - - - - - - - - - - - - - true - ..\..\..\bin\windows-x86\debug\ - ..\..\..\intermediate\windows-x86\debug\platform-test\ - platform-test - .exe - - - true - ..\..\..\bin\windows-x64\debug\ - ..\..\..\intermediate\windows-x64\debug\platform-test\ - platform-test - .exe - - - true - ..\..\..\bin\windows-aarch64\debug\ - ..\..\..\intermediate\windows-aarch64\debug\platform-test\ - platform-test - .exe - - - false - ..\..\..\bin\windows-x86\release\ - ..\..\..\intermediate\windows-x86\release\platform-test\ - platform-test - .exe - - - false - ..\..\..\bin\windows-x64\release\ - ..\..\..\intermediate\windows-x64\release\platform-test\ - platform-test - .exe - - - false - ..\..\..\bin\windows-aarch64\release\ - ..\..\..\intermediate\windows-aarch64\release\platform-test\ - platform-test - .exe - - - - NotUsing - Level3 - _DEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) - ..\..\..;..\..\..\tools;%(AdditionalIncludeDirectories) - ProgramDatabase - Disabled - false - MultiThreadedDebug - true - stdcpp17 - - - Windows - true - - - - - NotUsing - Level3 - _DEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) - ..\..\..;..\..\..\tools;%(AdditionalIncludeDirectories) - ProgramDatabase - Disabled - false - MultiThreadedDebug - true - stdcpp17 - - - Windows - true - - - - - NotUsing - Level3 - _DEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) - ..\..\..;..\..\..\tools;%(AdditionalIncludeDirectories) - ProgramDatabase - Disabled - false - MultiThreadedDebug - true - stdcpp17 - - - Windows - true - - - - - NotUsing - Level3 - NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) - ..\..\..;..\..\..\tools;%(AdditionalIncludeDirectories) - Full - true - true - false - true - MultiThreaded - true - stdcpp17 - - - Windows - true - true - - - - - NotUsing - Level3 - NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) - ..\..\..;..\..\..\tools;%(AdditionalIncludeDirectories) - Full - true - true - false - true - MultiThreaded - true - stdcpp17 - - - Windows - true - true - - - - - NotUsing - Level3 - NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) - ..\..\..;..\..\..\tools;%(AdditionalIncludeDirectories) - Full - true - true - false - true - MultiThreaded - true - stdcpp17 - - - Windows - true - true - - - - - - - - {37BED5B5-23FA-D81F-8C0C-F1167867813A} - - - {DB00DA62-0533-4AFD-B59F-A67D5B3A0808} - - - {222F7498-B40C-4F3F-A704-DDEB91A4484A} - - - {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B} - - - {3565FE5E-4FA3-11EB-AE93-0242AC130002} - - - {F9BE7957-8399-899E-0C49-E714FDDD4B65} - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Debug + ARM64 + + + Debug aarch64 + Win32 + + + Debug aarch64 + x64 + + + Debug aarch64 + ARM64 + + + Release + Win32 + + + Release + x64 + + + Release + ARM64 + + + Release aarch64 + Win32 + + + Release aarch64 + x64 + + + Release aarch64 + ARM64 + + + + {F385D6A7-DF6C-989F-88BD-FEBC74831106} + true + Win32Proj + platform-test + + + + Application + true + Unicode + v142 + + + Application + true + Unicode + v142 + + + Application + true + Unicode + v142 + true + + + Application + false + Unicode + v142 + + + Application + false + Unicode + v142 + + + Application + false + Unicode + v142 + true + + + + + + + + + + + + + + + + + + + + + + + + + true + ..\..\..\bin\windows-x86\debug\ + ..\..\..\intermediate\windows-x86\debug\platform-test\ + platform-test + .exe + + + true + ..\..\..\bin\windows-x64\debug\ + ..\..\..\intermediate\windows-x64\debug\platform-test\ + platform-test + .exe + + + true + ..\..\..\bin\windows-aarch64\debug\ + ..\..\..\intermediate\windows-aarch64\debug\platform-test\ + platform-test + .exe + + + false + ..\..\..\bin\windows-x86\release\ + ..\..\..\intermediate\windows-x86\release\platform-test\ + platform-test + .exe + + + false + ..\..\..\bin\windows-x64\release\ + ..\..\..\intermediate\windows-x64\release\platform-test\ + platform-test + .exe + + + false + ..\..\..\bin\windows-aarch64\release\ + ..\..\..\intermediate\windows-aarch64\release\platform-test\ + platform-test + .exe + + + + NotUsing + Level3 + _DEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) + ..\..\..;..\..\..\tools;%(AdditionalIncludeDirectories) + ProgramDatabase + Disabled + false + MultiThreadedDebug + true + stdcpp17 + + + Windows + true + + + + + NotUsing + Level3 + _DEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) + ..\..\..;..\..\..\tools;%(AdditionalIncludeDirectories) + ProgramDatabase + Disabled + false + MultiThreadedDebug + true + stdcpp17 + + + Windows + true + + + + + NotUsing + Level3 + _DEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) + ..\..\..;..\..\..\tools;%(AdditionalIncludeDirectories) + ProgramDatabase + Disabled + false + MultiThreadedDebug + true + stdcpp17 + + + Windows + true + + + + + NotUsing + Level3 + NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) + ..\..\..;..\..\..\tools;%(AdditionalIncludeDirectories) + Full + true + true + false + true + MultiThreaded + true + stdcpp17 + + + Windows + true + true + + + + + NotUsing + Level3 + NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) + ..\..\..;..\..\..\tools;%(AdditionalIncludeDirectories) + Full + true + true + false + true + MultiThreaded + true + stdcpp17 + + + Windows + true + true + + + + + NotUsing + Level3 + NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) + ..\..\..;..\..\..\tools;%(AdditionalIncludeDirectories) + Full + true + true + false + true + MultiThreaded + true + stdcpp17 + + + Windows + true + true + + + + + + + + {37BED5B5-23FA-D81F-8C0C-F1167867813A} + + + {DB00DA62-0533-4AFD-B59F-A67D5B3A0808} + + + {222F7498-B40C-4F3F-A704-DDEB91A4484A} + + + {F5ADB74E-02A7-44FB-AA3B-FC02F8AC7A4B} + + + {3565FE5E-4FA3-11EB-AE93-0242AC130002} + + + {F9BE7957-8399-899E-0C49-E714FDDD4B65} + + + + + \ No newline at end of file diff --git a/build/visual-studio/platform-test/platform-test.vcxproj.filters b/build/visual-studio/platform-test/platform-test.vcxproj.filters index 5fb5eb4dc..d2de85c34 100644 --- a/build/visual-studio/platform-test/platform-test.vcxproj.filters +++ b/build/visual-studio/platform-test/platform-test.vcxproj.filters @@ -1,13 +1,13 @@ - - - - - {E9C7FDCE-D52A-8D73-7EB0-C5296AF258F6} - - - - - Source Files - - + + + + + {E9C7FDCE-D52A-8D73-7EB0-C5296AF258F6} + + + + + Source Files + + \ No newline at end of file diff --git a/build/visual-studio/slang-capability-generator/slang-capability-generator.vcxproj b/build/visual-studio/slang-capability-generator/slang-capability-generator.vcxproj index 1ab291e8d..9d052c2be 100644 --- a/build/visual-studio/slang-capability-generator/slang-capability-generator.vcxproj +++ b/build/visual-studio/slang-capability-generator/slang-capability-generator.vcxproj @@ -1,296 +1,296 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Debug - ARM64 - - - Debug aarch64 - Win32 - - - Debug aarch64 - x64 - - - Debug aarch64 - ARM64 - - - Release - Win32 - - - Release - x64 - - - Release - ARM64 - - - Release aarch64 - Win32 - - - Release aarch64 - x64 - - - Release aarch64 - ARM64 - - - - {FD16CA29-C66A-430A-822C-C09655088611} - true - Win32Proj - slang-capability-generator - - - - Application - true - Unicode - v142 - - - Application - true - Unicode - v142 - - - Application - true - Unicode - v142 - true - - - Application - false - Unicode - v142 - - - Application - false - Unicode - v142 - - - Application - false - Unicode - v142 - true - - - - - - - - - - - - - - - - - - - - - - - - - true - ..\..\..\bin\windows-x86\debug\ - ..\..\..\intermediate\windows-x86\debug\slang-capability-generator\ - slang-capability-generator - .exe - - - true - ..\..\..\bin\windows-x64\debug\ - ..\..\..\intermediate\windows-x64\debug\slang-capability-generator\ - slang-capability-generator - .exe - - - true - ..\..\..\bin\windows-aarch64\debug\ - ..\..\..\intermediate\windows-aarch64\debug\slang-capability-generator\ - slang-capability-generator - .exe - - - false - ..\..\..\bin\windows-x86\release\ - ..\..\..\intermediate\windows-x86\release\slang-capability-generator\ - slang-capability-generator - .exe - - - false - ..\..\..\bin\windows-x64\release\ - ..\..\..\intermediate\windows-x64\release\slang-capability-generator\ - slang-capability-generator - .exe - - - false - ..\..\..\bin\windows-aarch64\release\ - ..\..\..\intermediate\windows-aarch64\release\slang-capability-generator\ - slang-capability-generator - .exe - - - - NotUsing - Level3 - _DEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) - ..\..\..;%(AdditionalIncludeDirectories) - ProgramDatabase - Disabled - false - MultiThreadedDebug - true - stdcpp17 - - - Console - true - - - - - NotUsing - Level3 - _DEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) - ..\..\..;%(AdditionalIncludeDirectories) - ProgramDatabase - Disabled - false - MultiThreadedDebug - true - stdcpp17 - - - Console - true - - - - - NotUsing - Level3 - _DEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) - ..\..\..;%(AdditionalIncludeDirectories) - ProgramDatabase - Disabled - false - MultiThreadedDebug - true - stdcpp17 - - - Console - true - - - - - NotUsing - Level3 - NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) - ..\..\..;%(AdditionalIncludeDirectories) - Full - true - true - false - true - MultiThreaded - true - stdcpp17 - - - Console - true - true - - - - - NotUsing - Level3 - NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) - ..\..\..;%(AdditionalIncludeDirectories) - Full - true - true - false - true - MultiThreaded - true - stdcpp17 - - - Console - true - true - - - - - NotUsing - Level3 - NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) - ..\..\..;%(AdditionalIncludeDirectories) - Full - true - true - false - true - MultiThreaded - true - stdcpp17 - - - Console - true - true - - - - - - - - - - - {12C1E89D-F5D0-41D3-8E8D-FB3F358F8126} - - - {F9BE7957-8399-899E-0C49-E714FDDD4B65} - - - - - + + + + + Debug + Win32 + + + Debug + x64 + + + Debug + ARM64 + + + Debug aarch64 + Win32 + + + Debug aarch64 + x64 + + + Debug aarch64 + ARM64 + + + Release + Win32 + + + Release + x64 + + + Release + ARM64 + + + Release aarch64 + Win32 + + + Release aarch64 + x64 + + + Release aarch64 + ARM64 + + + + {FD16CA29-C66A-430A-822C-C09655088611} + true + Win32Proj + slang-capability-generator + + + + Application + true + Unicode + v142 + + + Application + true + Unicode + v142 + + + Application + true + Unicode + v142 + true + + + Application + false + Unicode + v142 + + + Application + false + Unicode + v142 + + + Application + false + Unicode + v142 + true + + + + + + + + + + + + + + + + + + + + + + + + + true + ..\..\..\bin\windows-x86\debug\ + ..\..\..\intermediate\windows-x86\debug\slang-capability-generator\ + slang-capability-generator + .exe + + + true + ..\..\..\bin\windows-x64\debug\ + ..\..\..\intermediate\windows-x64\debug\slang-capability-generator\ + slang-capability-generator + .exe + + + true + ..\..\..\bin\windows-aarch64\debug\ + ..\..\..\intermediate\windows-aarch64\debug\slang-capability-generator\ + slang-capability-generator + .exe + + + false + ..\..\..\bin\windows-x86\release\ + ..\..\..\intermediate\windows-x86\release\slang-capability-generator\ + slang-capability-generator + .exe + + + false + ..\..\..\bin\windows-x64\release\ + ..\..\..\intermediate\windows-x64\release\slang-capability-generator\ + slang-capability-generator + .exe + + + false + ..\..\..\bin\windows-aarch64\release\ + ..\..\..\intermediate\windows-aarch64\release\slang-capability-generator\ + slang-capability-generator + .exe + + + + NotUsing + Level3 + _DEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) + ..\..\..;%(AdditionalIncludeDirectories) + ProgramDatabase + Disabled + false + MultiThreadedDebug + true + stdcpp17 + + + Console + true + + + + + NotUsing + Level3 + _DEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) + ..\..\..;%(AdditionalIncludeDirectories) + ProgramDatabase + Disabled + false + MultiThreadedDebug + true + stdcpp17 + + + Console + true + + + + + NotUsing + Level3 + _DEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) + ..\..\..;%(AdditionalIncludeDirectories) + ProgramDatabase + Disabled + false + MultiThreadedDebug + true + stdcpp17 + + + Console + true + + + + + NotUsing + Level3 + NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) + ..\..\..;%(AdditionalIncludeDirectories) + Full + true + true + false + true + MultiThreaded + true + stdcpp17 + + + Console + true + true + + + + + NotUsing + Level3 + NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) + ..\..\..;%(AdditionalIncludeDirectories) + Full + true + true + false + true + MultiThreaded + true + stdcpp17 + + + Console + true + true + + + + + NotUsing + Level3 + NDEBUG;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;NOMINMAX;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) + ..\..\..;%(AdditionalIncludeDirectories) + Full + true + true + false + true + MultiThreaded + true + stdcpp17 + + + Console + true + true + + + + + + + + + + + {12C1E89D-F5D0-41D3-8E8D-FB3F358F8126} + + + {F9BE7957-8399-899E-0C49-E714FDDD4B65} + + + + + \ No newline at end of file diff --git a/build/visual-studio/slang-capability-generator/slang-capability-generator.vcxproj.filters b/build/visual-studio/slang-capability-generator/slang-capability-generator.vcxproj.filters index bc38eaf38..e6cf91b2c 100644 --- a/build/visual-studio/slang-capability-generator/slang-capability-generator.vcxproj.filters +++ b/build/visual-studio/slang-capability-generator/slang-capability-generator.vcxproj.filters @@ -1,21 +1,21 @@ - - - - - {21EB8090-0D4E-1035-B6D3-48EBA215DCB7} - - - {E9C7FDCE-D52A-8D73-7EB0-C5296AF258F6} - - - - - Header Files - - - - - Source Files - - + + + + + {21EB8090-0D4E-1035-B6D3-48EBA215DCB7} + + + {E9C7FDCE-D52A-8D73-7EB0-C5296AF258F6} + + + + + Header Files + + + + + Source Files + + \ No newline at end of file diff --git a/build/visual-studio/slang/slang.vcxproj b/build/visual-studio/slang/slang.vcxproj index 53ceb4caf..6314e660c 100644 --- a/build/visual-studio/slang/slang.vcxproj +++ b/build/visual-studio/slang/slang.vcxproj @@ -370,6 +370,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla + @@ -590,6 +591,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla + diff --git a/build/visual-studio/slang/slang.vcxproj.filters b/build/visual-studio/slang/slang.vcxproj.filters index 9a3f70a7e..9a029dcb7 100644 --- a/build/visual-studio/slang/slang.vcxproj.filters +++ b/build/visual-studio/slang/slang.vcxproj.filters @@ -198,6 +198,9 @@ Header Files + + Header Files + Header Files @@ -854,6 +857,9 @@ Source Files + + Source Files + Source Files 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 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(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 @@ -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(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(); + if(!bfm) + continue; + + auto property = m_astBuilder->create(); + property->modifiers = m->modifiers; + property->type = as(m)->type; + property->loc = m->loc; + property->nameAndLoc = m->getNameAndLoc(); + property->parentDecl = structDecl; + property->ownedScope = m_astBuilder->create(); + property->ownedScope->containerDecl = property; + property->ownedScope->parent = getScope(structDecl); + m = property; + + const auto get = m_astBuilder->create(); + get->ownedScope = m_astBuilder->create(); + get->ownedScope->containerDecl = get; + get->ownedScope->parent = getScope(property); + property->addMember(get); + + const auto set = m_astBuilder->create(); + addModifier(set, m_astBuilder->create()); + set->ownedScope = m_astBuilder->create(); + 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 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(); + 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(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(m); + if(!v) + { + // If this is a non-bitfield member then finish the current group + if(as(m)) + dispatchSomeBitPackedMembers(); + continue; + } + + const auto bfm = m->findModifier(); + // 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(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(type)) - { - for (auto field : irStructType->getFields()) - { - if (field->getKey() == key) - { - return field; - } - } - } - else if (auto irSpecialize = as(type)) - { - if (auto irGeneric = as(irSpecialize->getBase())) - { - if (auto irGenericStructType = as(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(pairType)) { return as(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(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(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(inst)) + return findInnerMostGenericReturnVal(g); + return inst; +} + +static IRInst* maybeUnwrapSpecialize(IRInst* inst) +{ + if(const auto g = as(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(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(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(func->getParamType(0)); + SLANG_ASSERT(ptrType); + const auto structParamType = ptrType->getValueType(); + const auto structType = as(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(func); +} + +void synthesizeBitFieldAccessors(IRModule* module) +{ + for(const auto inst : module->getModuleInst()->getGlobalInsts()) + { + const auto func = as(maybeUnwrapGeneric(inst)); + if(!func) + continue; + const auto bfd = func->findDecoration(); + 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(getOperand(0)); } + IRIntegerValue getFieldWidth() { return as(getOperand(1))->getValue(); } + IRIntegerValue getFieldOffset() { return as(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(type)) + { + for (auto field : irStructType->getFields()) + { + if (field->getKey() == key) + { + return field; + } + } + } + else if (auto irSpecialize = as(type)) + { + if (auto irGeneric = as(irSpecialize->getBase())) + { + if (auto irGenericStructType = as(findInnerMostGenericReturnVal(irGeneric))) + { + return findStructField(irGenericStructType, key); + } + } + } + + return nullptr; + } + void findAllInstsBreadthFirst(IRInst* inst, List& 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& 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 } } + 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(decl) && as(decl->parentDecl)) + { + if(const auto bfm = decl->parentDecl->findModifier()) + { + 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 bool isInline = false; + addBitFieldAccessorDecorations(irFunc, decl); + for (auto modifier : decl->modifiers) { if (as(modifier)) @@ -10360,6 +10382,9 @@ RefPtr 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(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(); + 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(); diff --git a/tests/language-feature/bitfield/generic.slang b/tests/language-feature/bitfield/generic.slang new file mode 100644 index 000000000..f1789a04c --- /dev/null +++ b/tests/language-feature/bitfield/generic.slang @@ -0,0 +1,29 @@ +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -output-using-type + +// 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 outputBuffer; + +struct S { + int foo : 8; + uint bar : 24; + T baz; +}; + +[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/non-integral.slang b/tests/language-feature/bitfield/non-integral.slang new file mode 100644 index 000000000..9d477c1f8 --- /dev/null +++ b/tests/language-feature/bitfield/non-integral.slang @@ -0,0 +1,14 @@ +//DIAGNOSTIC_TEST:SIMPLE(filecheck=CHECK): + +struct S +{ + float foo : 10; + // CHECK: non-integral.slang([[#@LINE-1]]): error 31301: bit-field type (float) must be an integral type + S bar : 10; + // CHECK: non-integral.slang([[#@LINE-1]]): error 31301: bit-field type (S) must be an integral type +}; + +[numthreads(1, 1, 1)] +void computeMain() +{ +} diff --git a/tests/language-feature/bitfield/overflow.slang b/tests/language-feature/bitfield/overflow.slang new file mode 100644 index 000000000..cd217e92a --- /dev/null +++ b/tests/language-feature/bitfield/overflow.slang @@ -0,0 +1,29 @@ +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -output-using-type + +// CHECK: 123 +// CHECK-NEXT: 4567 +// CHECK-NEXT: -10 +// CHECK-NEXT: 16772649 + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer +RWStructuredBuffer outputBuffer; + +struct S { + int foo : 8; + uint bar : 24; +}; + +[numthreads(1, 1, 1)] +void computeMain() +{ + S s; + s.foo = 123; + s.bar = 4567; + outputBuffer[0] = s.foo; + outputBuffer[1] = s.bar; + + s.foo *= 2; + s.bar *= 0xffffff; + outputBuffer[2] = s.foo; + outputBuffer[3] = s.bar; +} diff --git a/tests/language-feature/bitfield/simple.slang b/tests/language-feature/bitfield/simple.slang new file mode 100644 index 000000000..dee654259 --- /dev/null +++ b/tests/language-feature/bitfield/simple.slang @@ -0,0 +1,74 @@ +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -output-using-type + +// 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 outputBuffer; + +struct S { + int foo : 8; + uint bar : 24; +}; + +// Generates the equivalent of this: +/* +struct S { + int _backing; + + property foo : int + { + // int foo : 8; + get + { + let backingWidth = 32; + let fooWidth = 8; + let topOfFoo = 8; + // Shift left and then right to sign-extend foo properly + return (int(_backing) << (backingWidth-topOfFoo)) >> (backingWidth-fooWidth); + } + [mutating] set(int x) + { + let fooMask = 0x000000FF; + let bottomOfFoo = 0; + _backing = int((_backing & ~fooMask) | ((int(x) << bottomOfFoo) & fooMask)); + } + } + + // int bar : 24; + property bar : int + { + get + { + let backingWidth = 32; + let barWidth = 24; + let topOfBar = 32; + // Shift left and then right to sign-extend bar properly + return (uint(_backing) << (backingWidth-topOfBar)) >> (backingWidth-barWidth); + } + [mutating] set(int x) + { + let barMask = 0xFFFFFF00; + let bottomOfBar = 8; + _backing = int((_backing & ~barMask) | ((int(x) << bottomOfBar) & barMask)); + } + } +}; +*/ + +[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/sizeof.slang b/tests/language-feature/bitfield/sizeof.slang new file mode 100644 index 000000000..57056d1b2 --- /dev/null +++ b/tests/language-feature/bitfield/sizeof.slang @@ -0,0 +1,42 @@ +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -output-using-type + +// CHECK: 1 +// CHECK-NEXT: 1 +// CHECK-NEXT: 1 +// CHECK-NEXT: 1 + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer +RWStructuredBuffer outputBuffer; + +struct S { + int foo : 8; + int bar : 24; +}; + +// This packs the smaller int into the int64 +struct T { + int64_t foo : 33; + int bar : 24; +}; + +// This takes two ints to store all the bits +struct P { + int foo : 24; + int bar : 24; +}; + +// Even though the smaller field comes first, it's still packed with the larger +// one +struct Q { + int8_t foo : 1; + int64_t bar : 63; +}; + +[numthreads(1, 1, 1)] +void computeMain() +{ + outputBuffer[0] = sizeof(S) == sizeof(int); + outputBuffer[1] = sizeof(T) == sizeof(int64_t); + outputBuffer[2] = sizeof(P) == sizeof(int) * 2; + outputBuffer[3] = sizeof(Q) == sizeof(int64_t); +} diff --git a/tests/language-feature/bitfield/too-large.slang b/tests/language-feature/bitfield/too-large.slang new file mode 100644 index 000000000..0780b8206 --- /dev/null +++ b/tests/language-feature/bitfield/too-large.slang @@ -0,0 +1,14 @@ +//DIAGNOSTIC_TEST:SIMPLE(filecheck=CHECK): + +struct S +{ + int foo : 33; + // CHECK: too-large.slang([[#@LINE-1]]): error 31300: bit-field size (33) exceeds the width of its type int (32) + int64_t bar : 999; + // CHECK: too-large.slang([[#@LINE-1]]): error 31300: bit-field size (999) exceeds the width of its type int64_t (64) +}; + +[numthreads(1, 1, 1)] +void computeMain() +{ +} diff --git a/tests/language-feature/bitfield/type-alias.slang b/tests/language-feature/bitfield/type-alias.slang new file mode 100644 index 000000000..7a33e2c50 --- /dev/null +++ b/tests/language-feature/bitfield/type-alias.slang @@ -0,0 +1,32 @@ +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -output-using-type + +// 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 outputBuffer; + +typedef uint MyUInt; + +struct S { + MyInt foo : 8; + MyUInt bar : 24; +}; + +typealias MyInt = int; + +[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/zero-width.slang b/tests/language-feature/bitfield/zero-width.slang new file mode 100644 index 000000000..457b31456 --- /dev/null +++ b/tests/language-feature/bitfield/zero-width.slang @@ -0,0 +1,28 @@ +//TEST(compute):COMPARE_COMPUTE(filecheck-buffer=CHECK):-cpu -output-using-type + +// 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 outputBuffer; + +struct S { + int foo : 8; + int breaker : 0; + int bar : 24; +}; + +[numthreads(1, 1, 1)] +void computeMain() +{ + S s; + s.foo = 123; + s.bar = 4567; + s.breaker = 9999; + outputBuffer[0] = s.foo; + outputBuffer[1] = s.bar; + outputBuffer[2] = sizeof(S) == sizeof(int) * 2; + outputBuffer[3] = s.breaker; +} -- cgit v1.2.3