├── .editorconfig ├── .gitignore ├── Examples └── hello │ ├── README.md │ ├── hello.cpp │ ├── hello.sln │ ├── hello.spire │ ├── hello.vcxproj │ └── hello.vcxproj.filters ├── LICENSE.txt ├── README.md ├── Source ├── CoreLib │ ├── Allocator.h │ ├── Array.h │ ├── ArrayView.h │ ├── Basic.h │ ├── CMakeLists.txt │ ├── CommandLineParser.cpp │ ├── CommandLineParser.h │ ├── Common.h │ ├── CoreLibBasic.vcxproj │ ├── Dictionary.h │ ├── Exception.h │ ├── Func.h │ ├── Hash.h │ ├── IntSet.h │ ├── LibIO.cpp │ ├── LibIO.h │ ├── LibMath.cpp │ ├── LibMath.h │ ├── LibString.cpp │ ├── LibString.h │ ├── Link.h │ ├── Linq.h │ ├── List.h │ ├── MemoryPool.cpp │ ├── MemoryPool.h │ ├── SecureCRT.h │ ├── SmartPointer.h │ ├── Stream.cpp │ ├── Stream.h │ ├── TextIO.cpp │ ├── TextIO.h │ ├── Tokenizer.cpp │ ├── Tokenizer.h │ ├── TypeTraits.h │ ├── VectorMath.cpp │ ├── VectorMath.h │ └── corelib.natvis ├── Spire.sln ├── SpireCompiler │ ├── D3DCompiler.cpp │ ├── D3DCompiler.h │ ├── ShaderCompilerShell.cpp │ ├── SpireCompiler.vcxproj │ └── SpireCompiler.vcxproj.filters ├── SpireCore │ ├── CLikeCodeGen.cpp │ ├── CLikeCodeGen.h │ ├── Closure.cpp │ ├── Closure.h │ ├── CodeGenBackend.h │ ├── CodeGenerator.cpp │ ├── CodeWriter.h │ ├── CompiledProgram.cpp │ ├── CompiledProgram.h │ ├── ConstantPool.cpp │ ├── DiagnosticDefs.h │ ├── Diagnostics.cpp │ ├── Diagnostics.h │ ├── GLSLCodeGen.cpp │ ├── GetDependencyVisitor.cpp │ ├── GetDependencyVisitor.h │ ├── HLSLCodeGen.cpp │ ├── IL.cpp │ ├── IL.h │ ├── InsertImplicitImportOperator.cpp │ ├── KeyHoleMatching.cpp │ ├── Lexer.cpp │ ├── Lexer.h │ ├── Naming.cpp │ ├── Naming.h │ ├── NatvisFile.natvis │ ├── NewSpirVCodeGen.cpp │ ├── Parser.cpp │ ├── Parser.h │ ├── Preprocessor.cpp │ ├── Preprocessor.h │ ├── SamplerUsageAnalysis.cpp │ ├── SamplerUsageAnalysis.h │ ├── Schedule.cpp │ ├── Schedule.h │ ├── ScopeDictionary.h │ ├── SemanticsVisitor.cpp │ ├── ShaderCompiler.cpp │ ├── ShaderCompiler.h │ ├── SpirVCodeGen.cpp │ ├── SpireCore.vcxproj │ ├── SpireCore.vcxproj.filters │ ├── StdInclude.cpp │ ├── StdInclude.h │ ├── StringObject.h │ ├── SymbolTable.cpp │ ├── SymbolTable.h │ ├── Syntax.cpp │ ├── Syntax.h │ ├── SyntaxVisitors.h │ ├── TypeLayout.cpp │ ├── TypeLayout.h │ ├── TypeTranslation.cpp │ ├── TypeTranslation.h │ ├── VariantIR.cpp │ └── VariantIR.h └── SpireLib │ ├── SpireLib.cpp │ ├── SpireLib.h │ ├── SpireLib.vcxproj │ └── SpireLib.vcxproj.filters ├── Spire.h ├── SpireAllSource.h ├── Tests ├── Diagnostics │ ├── break-outside-loop.spire │ ├── break-outside-loop.spire.expected │ ├── call-argument-type.spire │ ├── call-argument-type.spire.expected │ ├── continue-outside-loop.spire │ ├── continue-outside-loop.spire.expected │ ├── expected-token-eof.spire │ ├── expected-token-eof.spire.expected │ ├── expected-token.spire │ ├── expected-token.spire.expected │ ├── function-redefinition.spire │ ├── function-redefinition.spire.expected │ ├── hull-shader-invalid-domain.spire │ ├── hull-shader-invalid-domain.spire.expected │ ├── hull-shader-no-domain.spire │ ├── hull-shader-no-domain.spire.expected │ ├── illegal-character.spire │ ├── illegal-character.spire.expected │ ├── missing-file.spire │ ├── missing-file.spire.expected │ ├── parameter-already-defined.spire │ ├── parameter-already-defined.spire.expected │ ├── undefined-identifier.spire │ ├── undefined-identifier.spire.expected │ ├── variable-void-type.spire │ ├── variable-void-type.spire.expected │ ├── while-predicate-type.spire │ └── while-predicate-type.spire.expected ├── FrontEnd │ ├── lexer-comments.spire │ ├── parser-decls.spire │ ├── parser-empty.spire │ ├── parser-error-unclosed-curly.spire │ ├── parser-error-unclosed-curly.spire.expected │ ├── parser-using-file-a.spireh │ ├── parser-using-file.spire │ ├── pipeline-simple.spireh │ ├── struct.spire │ └── typedef.spire ├── HLSLCodeGen │ ├── DeferredLighting.fs.hlsl │ ├── DeferredLighting.vs.hlsl │ ├── StandardPipeline.spire │ ├── Utils.spire │ └── shader1.spire ├── Preprocessor │ ├── define-function-like.spire │ ├── define-function-like.spire.expected │ ├── define-simple.spire │ ├── if.spire │ ├── ifdef.spire │ ├── include-a.spireh │ └── include.spire └── SpireTestTool │ ├── SpireTestTool.vcxproj │ ├── SpireTestTool.vcxproj.filters │ ├── main.cpp │ ├── os.cpp │ └── os.h └── test.bat /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_style = tab 3 | indent_size = 4 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | DebugClang/ 2 | *.user 3 | autotuneLog.txt 4 | LibraryRelease/CodePack.exe 5 | *.exe 6 | *.exe.config 7 | *.exe.manifest 8 | *.pdb 9 | .vs 10 | *.VC.opendb 11 | *.VC.db 12 | *.spire.actual 13 | LibraryRelease/Spire.cpp 14 | *.sdf 15 | Debug/ 16 | Release/ 17 | x64/ 18 | *.TMP 19 | *.cse 20 | -------------------------------------------------------------------------------- /Examples/hello/README.md: -------------------------------------------------------------------------------- 1 | Spire "Hello World" Example 2 | =========================== 3 | 4 | The goal of this example is to demonstrate an almost minimal application that uses Spire for shading, and D3D11 for rendering. 5 | 6 | The `hello.spire` file contains a simple declaration of a Spire *shader module*, along with a *pipeline declaration* that will be used for mapping shader code to the capabilities of the "engine" (in this case, just vertex and fragment shaders). 7 | The `hello.cpp` file contains the C++ application code, showing how to use the Spire C API to load and compile the shader code, and construct a (trivial) executable shader from Spire modules. 8 | 9 | Note that this example is not intended to demonstrate good practices for integrating Spire into a production engine; the goal is merely to use the minimum amount of code possible to demonstrate a complete applicaiton that uses Spire. 10 | -------------------------------------------------------------------------------- /Examples/hello/hello.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hello", "hello.vcxproj", "{E6385042-1649-4803-9EBD-168F8B7EF131}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug|x64.ActiveCfg = Debug|x64 17 | {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug|x64.Build.0 = Debug|x64 18 | {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug|x86.ActiveCfg = Debug|Win32 19 | {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug|x86.Build.0 = Debug|Win32 20 | {E6385042-1649-4803-9EBD-168F8B7EF131}.Release|x64.ActiveCfg = Release|x64 21 | {E6385042-1649-4803-9EBD-168F8B7EF131}.Release|x64.Build.0 = Release|x64 22 | {E6385042-1649-4803-9EBD-168F8B7EF131}.Release|x86.ActiveCfg = Release|Win32 23 | {E6385042-1649-4803-9EBD-168F8B7EF131}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /Examples/hello/hello.spire: -------------------------------------------------------------------------------- 1 | // shaders.spire 2 | 3 | // TODO(tfoley): strip this down to a minimal pipeline 4 | 5 | pipeline StandardPipeline 6 | { 7 | [Pinned] 8 | input world MeshVertex; 9 | 10 | world CoarseVertex;// : "glsl(vertex:projCoord)" using projCoord export standardExport; 11 | world Fragment;// : "glsl" export fragmentExport; 12 | 13 | require @CoarseVertex vec4 projCoord; 14 | 15 | [VertexInput] 16 | extern @CoarseVertex MeshVertex vertAttribIn; 17 | import(MeshVertex->CoarseVertex) vertexImport() 18 | { 19 | return project(vertAttribIn); 20 | } 21 | 22 | extern @Fragment CoarseVertex CoarseVertexIn; 23 | import(CoarseVertex->Fragment) standardImport() 24 | // TODO(tfoley): this trait doesn't seem to be implemented on `vec3` 25 | // require trait IsTriviallyPassable(CoarseVertex) 26 | { 27 | return project(CoarseVertexIn); 28 | } 29 | 30 | stage vs : VertexShader 31 | { 32 | World: CoarseVertex; 33 | Position: projCoord; 34 | } 35 | 36 | stage fs : FragmentShader 37 | { 38 | World: Fragment; 39 | } 40 | } 41 | 42 | module HelloModule 43 | { 44 | @MeshVertex vec3 position; 45 | @MeshVertex vec3 color; 46 | 47 | param mat4 modelViewProjection; 48 | 49 | public vec4 projCoord = modelViewProjection * vec4(position, 1.0); 50 | 51 | out @Fragment vec4 colorTarget = vec4(color,1); 52 | } 53 | -------------------------------------------------------------------------------- /Examples/hello/hello.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Spire - The MIT License (MIT) 3 | Copyright (c) 2016, Carnegie Mellon University 4 | 5 | Developers: Yong He, Haomin Long, Teguh Hofstee 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a 8 | copy of this software and associated documentation files (the "Software"), 9 | to deal in the Software without restriction, including without limitation 10 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 | and/or sell copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Spire 3 | Spire is a shading language and compiler framework that facilitates modular shader authoring and rapid exploration of shader optimization choices (such as frequency reduction and algorithmic approximation) afforded by modern real-time graphics engines. The current implementation of the Spire compiler can generate either GLSL or SPIR-V output for use with OpenGL and Vulkan based engines. 4 | 5 | For an example of intergrating Spire into a game engine, head to this repository: 6 | https://github.com/csyonghe/GameEngine 7 | 8 | Note: This repository is no longer being updated. Code here is used to produce the work of our SIGGRAPH 2017 publication. Please check out our latest development of the Spire shading language at https://github.com/shader-slang/slang. 9 | 10 | # Publications 11 | 12 | [Shader Components: Modular and High Performance Shader Development](http://graphics.cs.cmu.edu/projects/shadercomp/) SIGGRAPH 2017 13 | 14 | [A System for Rapid Exploration of Shader Optimization Choices](http://graphics.cs.cmu.edu/projects/spire/) SIGGRAPH 2016 15 | -------------------------------------------------------------------------------- /Source/CoreLib/Allocator.h: -------------------------------------------------------------------------------- 1 | #ifndef CORE_LIB_ALLOCATOR_H 2 | #define CORE_LIB_ALLOCATOR_H 3 | 4 | #include 5 | 6 | namespace CoreLib 7 | { 8 | namespace Basic 9 | { 10 | inline void * AlignedAlloc(size_t size, size_t alignment) 11 | { 12 | #ifdef _MSC_VER 13 | return _aligned_malloc(size, alignment); 14 | #else 15 | void * rs = 0; 16 | int succ = posix_memalign(&rs, alignment, size); 17 | if (succ!=0) 18 | rs = 0; 19 | return rs; 20 | #endif 21 | } 22 | 23 | inline void AlignedFree(void * ptr) 24 | { 25 | #ifdef _MSC_VER 26 | _aligned_free(ptr); 27 | #else 28 | free(ptr); 29 | #endif 30 | } 31 | 32 | class StandardAllocator 33 | { 34 | public: 35 | // not really called 36 | void * Alloc(size_t size) 37 | { 38 | return malloc(size); 39 | } 40 | void Free(void * ptr) 41 | { 42 | return free(ptr); 43 | } 44 | }; 45 | 46 | template 47 | class AlignedAllocator 48 | { 49 | public: 50 | void * Alloc(size_t size) 51 | { 52 | return AlignedAlloc(size, alignment); 53 | } 54 | void Free(void * ptr) 55 | { 56 | return AlignedFree(ptr); 57 | } 58 | }; 59 | } 60 | } 61 | 62 | #endif -------------------------------------------------------------------------------- /Source/CoreLib/Array.h: -------------------------------------------------------------------------------- 1 | #ifndef CORE_LIB_ARRAY_H 2 | #define CORE_LIB_ARRAY_H 3 | 4 | #include "Exception.h" 5 | #include "ArrayView.h" 6 | 7 | namespace CoreLib 8 | { 9 | namespace Basic 10 | { 11 | template 12 | class Array 13 | { 14 | private: 15 | T _buffer[size]; 16 | int _count = 0; 17 | public: 18 | T* begin() const 19 | { 20 | return (T*)_buffer; 21 | } 22 | T* end() const 23 | { 24 | return (T*)_buffer + _count; 25 | } 26 | public: 27 | inline int GetCapacity() const 28 | { 29 | return size; 30 | } 31 | inline int Count() const 32 | { 33 | return _count; 34 | } 35 | inline T & First() const 36 | { 37 | return const_cast(_buffer[0]); 38 | } 39 | inline T & Last() const 40 | { 41 | return const_cast(_buffer[_count - 1]); 42 | } 43 | inline void SetSize(int newSize) 44 | { 45 | #ifdef _DEBUG 46 | if (newSize > size) 47 | throw IndexOutofRangeException("size too large."); 48 | #endif 49 | _count = newSize; 50 | } 51 | inline void Add(const T & item) 52 | { 53 | #ifdef _DEBUG 54 | if (_count == size) 55 | throw IndexOutofRangeException("out of range access to static array."); 56 | #endif 57 | _buffer[_count++] = item; 58 | } 59 | inline void Add(T && item) 60 | { 61 | #ifdef _DEBUG 62 | if (_count == size) 63 | throw IndexOutofRangeException("out of range access to static array."); 64 | #endif 65 | _buffer[_count++] = _Move(item); 66 | } 67 | 68 | inline T & operator [](int id) const 69 | { 70 | #if _DEBUG 71 | if (id >= _count || id < 0) 72 | throw IndexOutofRangeException("Operator[]: Index out of Range."); 73 | #endif 74 | return ((T*)_buffer)[id]; 75 | } 76 | 77 | inline T* Buffer() const 78 | { 79 | return (T*)_buffer; 80 | } 81 | 82 | inline void Clear() 83 | { 84 | _count = 0; 85 | } 86 | 87 | template 88 | int IndexOf(const T2 & val) const 89 | { 90 | for (int i = 0; i < _count; i++) 91 | { 92 | if (_buffer[i] == val) 93 | return i; 94 | } 95 | return -1; 96 | } 97 | 98 | template 99 | int LastIndexOf(const T2 & val) const 100 | { 101 | for (int i = _count - 1; i >= 0; i--) 102 | { 103 | if (_buffer[i] == val) 104 | return i; 105 | } 106 | return -1; 107 | } 108 | 109 | inline ArrayView GetArrayView() const 110 | { 111 | return ArrayView((T*)_buffer, _count); 112 | } 113 | inline ArrayView GetArrayView(int start, int count) const 114 | { 115 | return ArrayView((T*)_buffer + start, count); 116 | } 117 | }; 118 | 119 | template 120 | struct FirstType 121 | { 122 | typedef T type; 123 | }; 124 | 125 | 126 | template 127 | void InsertArray(Array &) {} 128 | 129 | template 130 | void InsertArray(Array & arr, const T & val, TArgs... args) 131 | { 132 | arr.Add(val); 133 | InsertArray(arr, args...); 134 | } 135 | 136 | template 137 | auto MakeArray(TArgs ...args) 138 | { 139 | Array::type, sizeof...(args)> rs; 140 | InsertArray(rs, args...); 141 | return rs; 142 | } 143 | } 144 | } 145 | 146 | #endif -------------------------------------------------------------------------------- /Source/CoreLib/ArrayView.h: -------------------------------------------------------------------------------- 1 | #ifndef CORE_LIB_ARRAY_VIEW_H 2 | #define CORE_LIB_ARRAY_VIEW_H 3 | 4 | #include "Exception.h" 5 | 6 | namespace CoreLib 7 | { 8 | namespace Basic 9 | { 10 | template 11 | class ArrayView 12 | { 13 | private: 14 | T * _buffer; 15 | int _count; 16 | int stride; 17 | public: 18 | T* begin() const 19 | { 20 | return _buffer; 21 | } 22 | T* end() const 23 | { 24 | return (T*)((char*)_buffer + _count*stride); 25 | } 26 | public: 27 | ArrayView() 28 | { 29 | _buffer = 0; 30 | _count = 0; 31 | } 32 | ArrayView(const T & singleObj) 33 | { 34 | SetData((T*)&singleObj, 1, sizeof(T)); 35 | } 36 | ArrayView(T * buffer, int count) 37 | { 38 | SetData(buffer, count, sizeof(T)); 39 | } 40 | ArrayView(void * buffer, int count, int _stride) 41 | { 42 | SetData(buffer, count, _stride); 43 | } 44 | void SetData(void * buffer, int count, int _stride) 45 | { 46 | this->_buffer = (T*)buffer; 47 | this->_count = count; 48 | this->stride = _stride; 49 | } 50 | inline int GetCapacity() const 51 | { 52 | return _count; 53 | } 54 | inline int Count() const 55 | { 56 | return _count; 57 | } 58 | 59 | inline T & operator [](int id) const 60 | { 61 | #if _DEBUG 62 | if (id >= _count || id < 0) 63 | throw IndexOutofRangeException("Operator[]: Index out of Range."); 64 | #endif 65 | return *(T*)((char*)_buffer+id*stride); 66 | } 67 | 68 | inline T* Buffer() const 69 | { 70 | return _buffer; 71 | } 72 | 73 | template 74 | int IndexOf(const T2 & val) const 75 | { 76 | for (int i = 0; i < _count; i++) 77 | { 78 | if (*(T*)((char*)_buffer + i*stride) == val) 79 | return i; 80 | } 81 | return -1; 82 | } 83 | 84 | template 85 | int LastIndexOf(const T2 & val) const 86 | { 87 | for (int i = _count - 1; i >= 0; i--) 88 | { 89 | if (*(T*)((char*)_buffer + i*stride) == val) 90 | return i; 91 | } 92 | return -1; 93 | } 94 | 95 | template 96 | int FindFirst(const Func & predicate) const 97 | { 98 | for (int i = 0; i < _count; i++) 99 | { 100 | if (predicate(_buffer[i])) 101 | return i; 102 | } 103 | return -1; 104 | } 105 | 106 | template 107 | int FindLast(const Func & predicate) const 108 | { 109 | for (int i = _count - 1; i >= 0; i--) 110 | { 111 | if (predicate(_buffer[i])) 112 | return i; 113 | } 114 | return -1; 115 | } 116 | }; 117 | 118 | template 119 | ArrayView MakeArrayView(const T & obj) 120 | { 121 | return ArrayView(obj); 122 | } 123 | 124 | template 125 | ArrayView MakeArrayView(T * buffer, int count) 126 | { 127 | return ArrayView(buffer, count); 128 | } 129 | } 130 | } 131 | #endif -------------------------------------------------------------------------------- /Source/CoreLib/Basic.h: -------------------------------------------------------------------------------- 1 | #ifndef CORE_LIB_BASIC_H 2 | #define CORE_LIB_BASIC_H 3 | 4 | #include "Common.h" 5 | #include "LibMath.h" 6 | #include "LibString.h" 7 | #include "Array.h" 8 | #include "List.h" 9 | #include "Link.h" 10 | #include "SmartPointer.h" 11 | #include "Exception.h" 12 | #include "Dictionary.h" 13 | #include "Func.h" 14 | #include "Linq.h" 15 | 16 | namespace CoreLib 17 | { 18 | using namespace Basic; 19 | } 20 | 21 | #endif -------------------------------------------------------------------------------- /Source/CoreLib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | project (CoreLib) 3 | 4 | add_library(CoreLib_Basic STATIC 5 | Basic.h 6 | Common.h 7 | Dictionary.h 8 | Exception.h 9 | IntSet.h 10 | LibIO.cpp 11 | LibIO.h 12 | LibMath.cpp 13 | LibMath.h 14 | LibString.cpp 15 | LibString.h 16 | Link.h 17 | List.h 18 | Parser.cpp 19 | Parser.h 20 | PerformanceCounter.cpp 21 | PerformanceCounter.h 22 | SmartPointer.h 23 | Stream.cpp 24 | Stream.h 25 | TextIO.cpp 26 | TextIO.h 27 | Threading.h 28 | VectorMath.cpp 29 | VectorMath.h 30 | WideChar.cpp 31 | WideChar.h 32 | SecureCRT.h 33 | ) 34 | add_subdirectory (Graphics) 35 | add_subdirectory (Imaging) 36 | add_subdirectory (Regex) 37 | -------------------------------------------------------------------------------- /Source/CoreLib/CommandLineParser.cpp: -------------------------------------------------------------------------------- 1 | #include "CommandLineParser.h" 2 | 3 | namespace CoreLib 4 | { 5 | namespace Text 6 | { 7 | CommandLineParser::CommandLineParser(const String & cmdLine) 8 | { 9 | stream = Split(cmdLine, L' '); 10 | } 11 | 12 | String CommandLineParser::GetFileName() 13 | { 14 | if (stream.Count()) 15 | return stream.First(); 16 | else 17 | return ""; 18 | } 19 | 20 | bool CommandLineParser::OptionExists(const String & opt) 21 | { 22 | for (auto & token : stream) 23 | { 24 | if (token.Equals(opt, false)) 25 | { 26 | return true; 27 | } 28 | } 29 | return false; 30 | } 31 | 32 | String CommandLineParser::GetOptionValue(const String & opt) 33 | { 34 | for (int i = 0; i < stream.Count(); i++) 35 | { 36 | if (stream[i].Equals(opt, false)) 37 | { 38 | if (i < stream.Count() - 1) 39 | return stream[i+1]; 40 | return ""; 41 | } 42 | } 43 | return ""; 44 | } 45 | 46 | String CommandLineParser::GetToken(int id) 47 | { 48 | return stream[id]; 49 | } 50 | 51 | int CommandLineParser::GetTokenCount() 52 | { 53 | return stream.Count(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Source/CoreLib/CommandLineParser.h: -------------------------------------------------------------------------------- 1 | #ifndef CORE_LIB_COMMANDLINE_PARSER 2 | #define CORE_LIB_COMMANDLINE_PARSER 3 | 4 | #include "Tokenizer.h" 5 | 6 | namespace CoreLib 7 | { 8 | namespace Text 9 | { 10 | class CommandLineParser : public Object 11 | { 12 | private: 13 | List stream; 14 | public: 15 | CommandLineParser(const String & cmdLine); 16 | String GetFileName(); 17 | bool OptionExists(const String & opt); 18 | String GetOptionValue(const String & opt); 19 | String GetToken(int id); 20 | int GetTokenCount(); 21 | }; 22 | } 23 | } 24 | 25 | #endif -------------------------------------------------------------------------------- /Source/CoreLib/Common.h: -------------------------------------------------------------------------------- 1 | #ifndef CORE_LIB_COMMON_H 2 | #define CORE_LIB_COMMON_H 3 | 4 | #include 5 | 6 | #ifdef __GNUC__ 7 | #define CORE_LIB_ALIGN_16(x) x __attribute__((aligned(16))) 8 | #else 9 | #define CORE_LIB_ALIGN_16(x) __declspec(align(16)) x 10 | #endif 11 | 12 | #define VARIADIC_TEMPLATE 13 | 14 | namespace CoreLib 15 | { 16 | typedef int64_t Int64; 17 | typedef unsigned short Word; 18 | #ifdef _M_X64 19 | typedef int64_t PtrInt; 20 | #else 21 | typedef int PtrInt; 22 | #endif 23 | namespace Basic 24 | { 25 | class Object 26 | { 27 | public: 28 | virtual ~Object() 29 | {} 30 | }; 31 | 32 | template 33 | inline T&& _Move(T & obj) 34 | { 35 | return static_cast(obj); 36 | } 37 | 38 | template 39 | inline void Swap(T & v0, T & v1) 40 | { 41 | T tmp = _Move(v0); 42 | v0 = _Move(v1); 43 | v1 = _Move(tmp); 44 | } 45 | } 46 | } 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /Source/CoreLib/Exception.h: -------------------------------------------------------------------------------- 1 | #ifndef CORE_LIB_EXCEPTION_H 2 | #define CORE_LIB_EXCEPTION_H 3 | 4 | #include "Common.h" 5 | #include "LibString.h" 6 | 7 | namespace CoreLib 8 | { 9 | namespace Basic 10 | { 11 | class Exception : public Object 12 | { 13 | public: 14 | String Message; 15 | Exception() 16 | {} 17 | Exception(const String & message) 18 | : Message(message) 19 | { 20 | } 21 | }; 22 | 23 | class IndexOutofRangeException : public Exception 24 | { 25 | public: 26 | IndexOutofRangeException() 27 | {} 28 | IndexOutofRangeException(const String & message) 29 | : Exception(message) 30 | { 31 | } 32 | 33 | }; 34 | 35 | class InvalidOperationException : public Exception 36 | { 37 | public: 38 | InvalidOperationException() 39 | {} 40 | InvalidOperationException(const String & message) 41 | : Exception(message) 42 | { 43 | } 44 | 45 | }; 46 | 47 | class ArgumentException : public Exception 48 | { 49 | public: 50 | ArgumentException() 51 | {} 52 | ArgumentException(const String & message) 53 | : Exception(message) 54 | { 55 | } 56 | 57 | }; 58 | 59 | class KeyNotFoundException : public Exception 60 | { 61 | public: 62 | KeyNotFoundException() 63 | {} 64 | KeyNotFoundException(const String & message) 65 | : Exception(message) 66 | { 67 | } 68 | }; 69 | class KeyExistsException : public Exception 70 | { 71 | public: 72 | KeyExistsException() 73 | {} 74 | KeyExistsException(const String & message) 75 | : Exception(message) 76 | { 77 | } 78 | }; 79 | 80 | class NotSupportedException : public Exception 81 | { 82 | public: 83 | NotSupportedException() 84 | {} 85 | NotSupportedException(const String & message) 86 | : Exception(message) 87 | { 88 | } 89 | }; 90 | 91 | class NotImplementedException : public Exception 92 | { 93 | public: 94 | NotImplementedException() 95 | {} 96 | NotImplementedException(const String & message) 97 | : Exception(message) 98 | { 99 | } 100 | }; 101 | 102 | class InvalidProgramException : public Exception 103 | { 104 | public: 105 | InvalidProgramException() 106 | {} 107 | InvalidProgramException(const String & message) 108 | : Exception(message) 109 | { 110 | } 111 | }; 112 | } 113 | } 114 | 115 | #endif -------------------------------------------------------------------------------- /Source/CoreLib/Func.h: -------------------------------------------------------------------------------- 1 | #ifndef CORELIB_FUNC_H 2 | #define CORELIB_FUNC_H 3 | 4 | #include "SmartPointer.h" 5 | 6 | namespace CoreLib 7 | { 8 | namespace Basic 9 | { 10 | template 11 | class FuncPtr 12 | { 13 | public: 14 | virtual TResult operator()(Arguments...) = 0; 15 | virtual bool operator == (const FuncPtr *) 16 | { 17 | return false; 18 | } 19 | virtual ~FuncPtr() {} 20 | }; 21 | 22 | template 23 | class CdeclFuncPtr : public FuncPtr 24 | { 25 | public: 26 | typedef TResult (*FuncType)(Arguments...); 27 | private: 28 | FuncType funcPtr; 29 | public: 30 | CdeclFuncPtr(FuncType func) 31 | :funcPtr(func) 32 | { 33 | } 34 | 35 | virtual TResult operator()(Arguments... params) override 36 | { 37 | return funcPtr(params...); 38 | } 39 | 40 | virtual bool operator == (const FuncPtr * ptr) override 41 | { 42 | auto cptr = dynamic_cast*>(ptr); 43 | if (cptr) 44 | return funcPtr == cptr->funcPtr; 45 | else 46 | return false; 47 | } 48 | }; 49 | 50 | template 51 | class MemberFuncPtr : public FuncPtr 52 | { 53 | public: 54 | typedef TResult (Class::*FuncType)(Arguments...); 55 | private: 56 | FuncType funcPtr; 57 | Class * object; 58 | public: 59 | MemberFuncPtr(Class * obj, FuncType func) 60 | : funcPtr(func), object(obj) 61 | { 62 | } 63 | 64 | virtual TResult operator()(Arguments... params) override 65 | { 66 | return (object->*funcPtr)(params...); 67 | } 68 | 69 | virtual bool operator == (const FuncPtr * ptr) override 70 | { 71 | auto cptr = dynamic_cast*>(ptr); 72 | if (cptr) 73 | return funcPtr == cptr->funcPtr && object == cptr->object; 74 | else 75 | return false; 76 | } 77 | }; 78 | 79 | template 80 | class LambdaFuncPtr : public FuncPtr 81 | { 82 | private: 83 | F func; 84 | public: 85 | LambdaFuncPtr(const F & _func) 86 | : func(_func) 87 | {} 88 | virtual TResult operator()(Arguments... params) override 89 | { 90 | return func(params...); 91 | } 92 | virtual bool operator == (const FuncPtr * /*ptr*/) override 93 | { 94 | return false; 95 | } 96 | }; 97 | 98 | template 99 | class Func 100 | { 101 | private: 102 | RefPtr> funcPtr; 103 | public: 104 | Func(){} 105 | Func(typename CdeclFuncPtr::FuncType func) 106 | { 107 | funcPtr = new CdeclFuncPtr(func); 108 | } 109 | template 110 | Func(Class * object, typename MemberFuncPtr::FuncType func) 111 | { 112 | funcPtr = new MemberFuncPtr(object, func); 113 | } 114 | template 115 | Func(const TFuncObj & func) 116 | { 117 | funcPtr = new LambdaFuncPtr(func); 118 | } 119 | Func & operator = (typename CdeclFuncPtr::FuncType func) 120 | { 121 | funcPtr = new CdeclFuncPtr(func); 122 | return *this; 123 | } 124 | template 125 | Func & operator = (const MemberFuncPtr & func) 126 | { 127 | funcPtr = new MemberFuncPtr(func); 128 | return *this; 129 | } 130 | template 131 | Func & operator = (const TFuncObj & func) 132 | { 133 | funcPtr = new LambdaFuncPtr(func); 134 | return *this; 135 | } 136 | bool operator == (const Func & f) 137 | { 138 | return *funcPtr == f.funcPtr.Ptr(); 139 | } 140 | bool operator != (const Func & f) 141 | { 142 | return !(*this == f); 143 | } 144 | TResult operator()(Arguments... params) 145 | { 146 | return (*funcPtr)(params...); 147 | } 148 | }; 149 | 150 | // template 151 | // using Procedure = Func; 152 | 153 | template 154 | class Procedure : public Func 155 | { 156 | private: 157 | RefPtr> funcPtr; 158 | public: 159 | Procedure(){} 160 | Procedure(const Procedure & proc) 161 | { 162 | funcPtr = proc.funcPtr; 163 | } 164 | Procedure(typename CdeclFuncPtr::FuncType func) 165 | { 166 | funcPtr = new CdeclFuncPtr(func); 167 | } 168 | template 169 | Procedure(Class * object, typename MemberFuncPtr::FuncType func) 170 | { 171 | funcPtr = new MemberFuncPtr(object, func); 172 | } 173 | template 174 | Procedure(const TFuncObj & func) 175 | { 176 | funcPtr = new LambdaFuncPtr(func); 177 | } 178 | Procedure & operator = (typename CdeclFuncPtr::FuncType func) 179 | { 180 | funcPtr = new CdeclFuncPtr(func); 181 | return *this; 182 | } 183 | template 184 | Procedure & operator = (const MemberFuncPtr & func) 185 | { 186 | funcPtr = new MemberFuncPtr(func); 187 | return *this; 188 | } 189 | template 190 | Procedure & operator = (const TFuncObj & func) 191 | { 192 | funcPtr = new LambdaFuncPtr(func); 193 | return *this; 194 | } 195 | Procedure & operator = (const Procedure & proc) 196 | { 197 | funcPtr = proc.funcPtr; 198 | } 199 | void Clear() 200 | { 201 | funcPtr = nullptr; 202 | } 203 | void operator()(Arguments... params) 204 | { 205 | if (funcPtr) 206 | (*funcPtr)(params...); 207 | } 208 | }; 209 | } 210 | } 211 | 212 | #endif -------------------------------------------------------------------------------- /Source/CoreLib/Hash.h: -------------------------------------------------------------------------------- 1 | #ifndef CORELIB_HASH_H 2 | #define CORELIB_HASH_H 3 | 4 | #include "LibMath.h" 5 | #include 6 | 7 | namespace CoreLib 8 | { 9 | namespace Basic 10 | { 11 | 12 | inline int GetHashCode(double key) 13 | { 14 | return FloatAsInt((float)key); 15 | } 16 | inline int GetHashCode(float key) 17 | { 18 | return FloatAsInt(key); 19 | } 20 | inline int GetHashCode(const char * buffer) 21 | { 22 | if (!buffer) 23 | return 0; 24 | int hash = 0; 25 | int c; 26 | auto str = buffer; 27 | c = *str++; 28 | while (c) 29 | { 30 | hash = c + (hash << 6) + (hash << 16) - hash; 31 | c = *str++; 32 | } 33 | return hash; 34 | } 35 | inline int GetHashCode(char * buffer) 36 | { 37 | return GetHashCode(const_cast(buffer)); 38 | } 39 | 40 | template 41 | class Hash 42 | { 43 | public: 44 | }; 45 | template<> 46 | class Hash<1> 47 | { 48 | public: 49 | template 50 | static int GetHashCode(TKey & key) 51 | { 52 | return (int)key; 53 | } 54 | }; 55 | template<> 56 | class Hash<0> 57 | { 58 | public: 59 | template 60 | static int GetHashCode(TKey & key) 61 | { 62 | return key.GetHashCode(); 63 | } 64 | }; 65 | template 66 | class PointerHash 67 | {}; 68 | template<> 69 | class PointerHash<1> 70 | { 71 | public: 72 | template 73 | static int GetHashCode(TKey & key) 74 | { 75 | return (int)((CoreLib::PtrInt)key) / sizeof(typename std::remove_pointer::type); 76 | } 77 | }; 78 | template<> 79 | class PointerHash<0> 80 | { 81 | public: 82 | template 83 | static int GetHashCode(TKey & key) 84 | { 85 | return Hash::value || std::is_enum::value>::GetHashCode(key); 86 | } 87 | }; 88 | 89 | template 90 | int GetHashCode(const TKey & key) 91 | { 92 | return PointerHash::value>::GetHashCode(key); 93 | } 94 | 95 | template 96 | int GetHashCode(TKey & key) 97 | { 98 | return PointerHash::value>::GetHashCode(key); 99 | } 100 | 101 | 102 | } 103 | } 104 | 105 | #endif -------------------------------------------------------------------------------- /Source/CoreLib/IntSet.h: -------------------------------------------------------------------------------- 1 | #ifndef BIT_VECTOR_INT_SET_H 2 | #define BIT_VECTOR_INT_SET_H 3 | 4 | #include 5 | #include "List.h" 6 | #include "LibMath.h" 7 | #include "Common.h" 8 | #include "Exception.h" 9 | 10 | namespace CoreLib 11 | { 12 | namespace Basic 13 | { 14 | class IntSet 15 | { 16 | private: 17 | List buffer; 18 | public: 19 | IntSet() 20 | {} 21 | IntSet(const IntSet & other) 22 | { 23 | buffer = other.buffer; 24 | } 25 | IntSet(IntSet && other) 26 | { 27 | *this = (_Move(other)); 28 | } 29 | IntSet & operator = (IntSet && other) 30 | { 31 | buffer = _Move(other.buffer); 32 | return *this; 33 | } 34 | IntSet & operator = (const IntSet & other) 35 | { 36 | buffer = other.buffer; 37 | return *this; 38 | } 39 | int GetHashCode() 40 | { 41 | int rs = 0; 42 | for (auto val : buffer) 43 | rs ^= val; 44 | return rs; 45 | } 46 | IntSet(int maxVal) 47 | { 48 | SetMax(maxVal); 49 | } 50 | int Size() const 51 | { 52 | return buffer.Count()*32; 53 | } 54 | void SetMax(int val) 55 | { 56 | Resize(val); 57 | Clear(); 58 | } 59 | void SetAll() 60 | { 61 | for (int i = 0; i>5); 68 | if (buffer.Count() > oldBufferSize) 69 | memset(buffer.Buffer()+oldBufferSize, 0, (buffer.Count()-oldBufferSize) * sizeof(int)); 70 | } 71 | void Clear() 72 | { 73 | for (int i = 0; i>5; 79 | if (id < buffer.Count()) 80 | buffer[id] |= (1<<(val&31)); 81 | else 82 | { 83 | int oldSize = buffer.Count(); 84 | buffer.SetSize(id+1); 85 | memset(buffer.Buffer() + oldSize, 0, (buffer.Count()-oldSize)*sizeof(int)); 86 | buffer[id] |= (1<<(val&31)); 87 | } 88 | } 89 | void Remove(int val) 90 | { 91 | if ((val>>5) < buffer.Count()) 92 | buffer[(val>>5)] &= ~(1<<(val&31)); 93 | } 94 | bool Contains(int val) const 95 | { 96 | if ((val>>5) >= buffer.Count()) 97 | return false; 98 | return (buffer[(val>>5)] & (1<<(val&31))) != 0; 99 | } 100 | void UnionWith(const IntSet & set) 101 | { 102 | for (int i = 0; i buffer.Count()) 107 | buffer.AddRange(set.buffer.Buffer()+buffer.Count(), set.buffer.Count()-buffer.Count()); 108 | } 109 | bool operator == (const IntSet & set) 110 | { 111 | if (buffer.Count() != set.buffer.Count()) 112 | return false; 113 | for (int i = 0; i 7 | #ifdef _WIN32 8 | #include 9 | #endif 10 | namespace CoreLib 11 | { 12 | namespace IO 13 | { 14 | using namespace CoreLib::Basic; 15 | 16 | CommandLineWriter * currentCommandWriter = nullptr; 17 | 18 | void SetCommandLineWriter(CommandLineWriter * writer) 19 | { 20 | currentCommandWriter = writer; 21 | } 22 | 23 | bool File::Exists(const String & fileName) 24 | { 25 | #ifdef _WIN32 26 | struct _stat32 statVar; 27 | return ::_wstat32(((String)fileName).ToWString(), &statVar) != -1; 28 | #else 29 | struct stat statVar; 30 | return ::stat(fileName.Buffer(), &statVar) == 0; 31 | #endif 32 | } 33 | 34 | String Path::TruncateExt(const String & path) 35 | { 36 | int dotPos = path.LastIndexOf('.'); 37 | if (dotPos != -1) 38 | return path.SubString(0, dotPos); 39 | else 40 | return path; 41 | } 42 | String Path::ReplaceExt(const String & path, const char * newExt) 43 | { 44 | StringBuilder sb(path.Length()+10); 45 | int dotPos = path.LastIndexOf('.'); 46 | if (dotPos == -1) 47 | dotPos = path.Length(); 48 | sb.Append(path.Buffer(), dotPos); 49 | sb.Append('.'); 50 | sb.Append(newExt); 51 | return sb.ProduceString(); 52 | } 53 | String Path::GetFileName(const String & path) 54 | { 55 | int pos = path.LastIndexOf('/'); 56 | pos = Math::Max(path.LastIndexOf('\\'), pos) + 1; 57 | return path.SubString(pos, path.Length()-pos); 58 | } 59 | String Path::GetFileNameWithoutEXT(const String & path) 60 | { 61 | int pos = path.LastIndexOf('/'); 62 | pos = Math::Max(path.LastIndexOf('\\'), pos) + 1; 63 | int dotPos = path.LastIndexOf('.'); 64 | if (dotPos <= pos) 65 | dotPos = path.Length(); 66 | return path.SubString(pos, dotPos - pos); 67 | } 68 | String Path::GetFileExt(const String & path) 69 | { 70 | int dotPos = path.LastIndexOf('.'); 71 | if (dotPos != -1) 72 | return path.SubString(dotPos+1, path.Length()-dotPos-1); 73 | else 74 | return ""; 75 | } 76 | String Path::GetDirectoryName(const String & path) 77 | { 78 | int pos = path.LastIndexOf('/'); 79 | pos = Math::Max(path.LastIndexOf('\\'), pos); 80 | if (pos != -1) 81 | return path.SubString(0, pos); 82 | else 83 | return ""; 84 | } 85 | String Path::Combine(const String & path1, const String & path2) 86 | { 87 | if (path1.Length() == 0) return path2; 88 | StringBuilder sb(path1.Length()+path2.Length()+2); 89 | sb.Append(path1); 90 | if (!path1.EndsWith('\\') && !path1.EndsWith('/')) 91 | sb.Append(PathDelimiter); 92 | sb.Append(path2); 93 | return sb.ProduceString(); 94 | } 95 | String Path::Combine(const String & path1, const String & path2, const String & path3) 96 | { 97 | StringBuilder sb(path1.Length()+path2.Length()+path3.Length()+3); 98 | sb.Append(path1); 99 | if (!path1.EndsWith('\\') && !path1.EndsWith('/')) 100 | sb.Append(PathDelimiter); 101 | sb.Append(path2); 102 | if (!path2.EndsWith('\\') && !path2.EndsWith('/')) 103 | sb.Append(PathDelimiter); 104 | sb.Append(path3); 105 | return sb.ProduceString(); 106 | } 107 | 108 | bool Path::CreateDir(const String & path) 109 | { 110 | #if defined(_WIN32) 111 | return _wmkdir(path.ToWString()) == 0; 112 | #else 113 | return mkdir(path.Buffer(), 0777) == 0; 114 | #endif 115 | } 116 | 117 | CoreLib::Basic::String File::ReadAllText(const CoreLib::Basic::String & fileName) 118 | { 119 | StreamReader reader(new FileStream(fileName, FileMode::Open, FileAccess::Read, FileShare::ReadWrite)); 120 | return reader.ReadToEnd(); 121 | } 122 | 123 | CoreLib::Basic::List File::ReadAllBytes(const CoreLib::Basic::String & fileName) 124 | { 125 | RefPtr fs = new FileStream(fileName, FileMode::Open, FileAccess::Read, FileShare::ReadWrite); 126 | List buffer; 127 | while (!fs->IsEnd()) 128 | { 129 | unsigned char ch; 130 | int read = (int)fs->Read(&ch, 1); 131 | if (read) 132 | buffer.Add(ch); 133 | else 134 | break; 135 | } 136 | return _Move(buffer); 137 | } 138 | 139 | void File::WriteAllText(const CoreLib::Basic::String & fileName, const CoreLib::Basic::String & text) 140 | { 141 | StreamWriter writer(new FileStream(fileName, FileMode::Create)); 142 | writer.Write(text); 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /Source/CoreLib/LibIO.h: -------------------------------------------------------------------------------- 1 | #ifndef CORE_LIB_IO_H 2 | #define CORE_LIB_IO_H 3 | 4 | #include "LibString.h" 5 | #include "Stream.h" 6 | #include "TextIO.h" 7 | #include "SecureCRT.h" 8 | 9 | namespace CoreLib 10 | { 11 | namespace IO 12 | { 13 | class File 14 | { 15 | public: 16 | static bool Exists(const CoreLib::Basic::String & fileName); 17 | static CoreLib::Basic::String ReadAllText(const CoreLib::Basic::String & fileName); 18 | static CoreLib::Basic::List ReadAllBytes(const CoreLib::Basic::String & fileName); 19 | static void WriteAllText(const CoreLib::Basic::String & fileName, const CoreLib::Basic::String & text); 20 | }; 21 | 22 | class Path 23 | { 24 | public: 25 | #ifdef _WIN32 26 | static const char PathDelimiter = '\\'; 27 | #else 28 | static const char PathDelimiter = '/'; 29 | #endif 30 | static String TruncateExt(const String & path); 31 | static String ReplaceExt(const String & path, const char * newExt); 32 | static String GetFileName(const String & path); 33 | static String GetFileNameWithoutEXT(const String & path); 34 | static String GetFileExt(const String & path); 35 | static String GetDirectoryName(const String & path); 36 | static String Combine(const String & path1, const String & path2); 37 | static String Combine(const String & path1, const String & path2, const String & path3); 38 | static bool CreateDir(const String & path); 39 | }; 40 | 41 | class CommandLineWriter : public Object 42 | { 43 | public: 44 | virtual void Write(const String & text) = 0; 45 | }; 46 | 47 | void SetCommandLineWriter(CommandLineWriter * writer); 48 | 49 | extern CommandLineWriter * currentCommandWriter; 50 | template 51 | void uiprintf(const wchar_t * format, Args... args) 52 | { 53 | if (currentCommandWriter) 54 | { 55 | char buffer[1024]; 56 | snprintf(buffer, 1024, format, args...); 57 | currentCommandWriter->Write(buffer); 58 | } 59 | } 60 | } 61 | } 62 | 63 | #endif -------------------------------------------------------------------------------- /Source/CoreLib/LibMath.cpp: -------------------------------------------------------------------------------- 1 | #include "LibMath.h" 2 | 3 | namespace CoreLib 4 | { 5 | namespace Basic 6 | { 7 | const float Math::Pi = 3.141592654f; 8 | } 9 | } -------------------------------------------------------------------------------- /Source/CoreLib/LibMath.h: -------------------------------------------------------------------------------- 1 | #ifndef CORE_LIB_MATH_H 2 | #define CORE_LIB_MATH_H 3 | 4 | #include 5 | 6 | namespace CoreLib 7 | { 8 | namespace Basic 9 | { 10 | class Math 11 | { 12 | public: 13 | static const float Pi; 14 | template 15 | static T Min(const T& v1, const T&v2) 16 | { 17 | return v1 20 | static T Max(const T& v1, const T&v2) 21 | { 22 | return v1>v2?v1:v2; 23 | } 24 | template 25 | static T Min(const T& v1, const T&v2, const T&v3) 26 | { 27 | return Min(v1, Min(v2, v3)); 28 | } 29 | template 30 | static T Max(const T& v1, const T&v2, const T&v3) 31 | { 32 | return Max(v1, Max(v2, v3)); 33 | } 34 | template 35 | static T Clamp(const T& val, const T& vmin, const T&vmax) 36 | { 37 | if (val < vmin) return vmin; 38 | else if (val > vmax) return vmax; 39 | else return val; 40 | } 41 | 42 | static inline int FastFloor(float x) 43 | { 44 | int i = (int)x; 45 | return i - (i > x); 46 | } 47 | 48 | static inline int FastFloor(double x) 49 | { 50 | int i = (int)x; 51 | return i - (i > x); 52 | } 53 | 54 | static inline int IsNaN(float x) 55 | { 56 | #ifdef _M_X64 57 | return _isnanf(x); 58 | #else 59 | return isnan(x); 60 | #endif 61 | } 62 | 63 | static inline int IsInf(float x) 64 | { 65 | return isinf(x); 66 | } 67 | 68 | static inline unsigned int Ones32(register unsigned int x) 69 | { 70 | /* 32-bit recursive reduction using SWAR... 71 | but first step is mapping 2-bit values 72 | into sum of 2 1-bit values in sneaky way 73 | */ 74 | x -= ((x >> 1) & 0x55555555); 75 | x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); 76 | x = (((x >> 4) + x) & 0x0f0f0f0f); 77 | x += (x >> 8); 78 | x += (x >> 16); 79 | return(x & 0x0000003f); 80 | } 81 | 82 | static inline unsigned int Log2Floor(register unsigned int x) 83 | { 84 | x |= (x >> 1); 85 | x |= (x >> 2); 86 | x |= (x >> 4); 87 | x |= (x >> 8); 88 | x |= (x >> 16); 89 | return(Ones32(x >> 1)); 90 | } 91 | 92 | static inline unsigned int Log2Ceil(register unsigned int x) 93 | { 94 | int y = (x & (x - 1)); 95 | y |= -y; 96 | y >>= (32 - 1); 97 | x |= (x >> 1); 98 | x |= (x >> 2); 99 | x |= (x >> 4); 100 | x |= (x >> 8); 101 | x |= (x >> 16); 102 | return(Ones32(x >> 1) - y); 103 | } 104 | /* 105 | static inline int Log2(float x) 106 | { 107 | unsigned int ix = (unsigned int&)x; 108 | unsigned int exp = (ix >> 23) & 0xFF; 109 | int log2 = (unsigned int)(exp) - 127; 110 | 111 | return log2; 112 | } 113 | */ 114 | }; 115 | inline int FloatAsInt(float val) 116 | { 117 | union InterCast 118 | { 119 | float fvalue; 120 | int ivalue; 121 | } cast; 122 | cast.fvalue = val; 123 | return cast.ivalue; 124 | } 125 | inline float IntAsFloat(int val) 126 | { 127 | union InterCast 128 | { 129 | float fvalue; 130 | int ivalue; 131 | } cast; 132 | cast.ivalue = val; 133 | return cast.fvalue; 134 | } 135 | 136 | inline unsigned short FloatToHalf(float val) 137 | { 138 | int x = *(int*)&val; 139 | unsigned short bits = (x >> 16) & 0x8000; 140 | unsigned short m = (x >> 12) & 0x07ff; 141 | unsigned int e = (x >> 23) & 0xff; 142 | if (e < 103) 143 | return bits; 144 | if (e > 142) 145 | { 146 | bits |= 0x7c00u; 147 | bits |= e == 255 && (x & 0x007fffffu); 148 | return bits; 149 | } 150 | if (e < 113) 151 | { 152 | m |= 0x0800u; 153 | bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1); 154 | return bits; 155 | } 156 | bits |= ((e - 112) << 10) | (m >> 1); 157 | bits += m & 1; 158 | return bits; 159 | } 160 | 161 | inline float HalfToFloat(unsigned short input) 162 | { 163 | union InterCast 164 | { 165 | float fvalue; 166 | int ivalue; 167 | InterCast() = default; 168 | InterCast(int ival) 169 | { 170 | ivalue = ival; 171 | } 172 | }; 173 | static const InterCast magic = InterCast((127 + (127 - 15)) << 23); 174 | static const InterCast was_infnan = InterCast((127 + 16) << 23); 175 | InterCast o; 176 | o.ivalue = (input & 0x7fff) << 13; // exponent/mantissa bits 177 | o.fvalue *= magic.fvalue; // exponent adjust 178 | if (o.fvalue >= was_infnan.fvalue) // make sure Inf/NaN survive 179 | o.ivalue |= 255 << 23; 180 | o.ivalue |= (input & 0x8000) << 16; // sign bit 181 | return o.fvalue; 182 | } 183 | 184 | class Random 185 | { 186 | private: 187 | unsigned int seed; 188 | public: 189 | Random(int seed) 190 | { 191 | this->seed = seed; 192 | } 193 | int Next() // random between 0 and RandMax (currently 0x7fff) 194 | { 195 | return (((seed = seed * 214013L + 2531011L) >> 16) & 0x7fff); 196 | } 197 | int Next(int min, int max) // inclusive min, exclusive max 198 | { 199 | unsigned int a = ((seed = seed * 214013L + 2531011L) & 0xFFFF0000); 200 | unsigned int b = ((seed = seed * 214013L + 2531011L) >> 16); 201 | unsigned int r = a + b; 202 | return min + r % (max - min); 203 | } 204 | float NextFloat() 205 | { 206 | return ((Next() << 15) + Next()) / ((float)(1 << 30)); 207 | } 208 | float NextFloat(float valMin, float valMax) 209 | { 210 | return valMin + (valMax - valMin) * NextFloat(); 211 | } 212 | static int RandMax() 213 | { 214 | return 0x7fff; 215 | } 216 | }; 217 | } 218 | } 219 | 220 | #endif 221 | -------------------------------------------------------------------------------- /Source/CoreLib/LibString.cpp: -------------------------------------------------------------------------------- 1 | #include "LibString.h" 2 | #include "TextIO.h" 3 | 4 | namespace CoreLib 5 | { 6 | namespace Basic 7 | { 8 | _EndLine EndLine; 9 | String StringConcat(const char * lhs, int leftLen, const char * rhs, int rightLen) 10 | { 11 | String res; 12 | res.length = leftLen + rightLen; 13 | res.buffer = new char[res.length + 1]; 14 | strcpy_s(res.buffer.Ptr(), res.length + 1, lhs); 15 | strcpy_s(res.buffer + leftLen, res.length + 1 - leftLen, rhs); 16 | return res; 17 | } 18 | String operator+(const char * op1, const String & op2) 19 | { 20 | if(!op2.buffer) // no string 2 - return first 21 | return String(op1); 22 | 23 | if (!op1) // no base string?! return the second string 24 | return op2; 25 | 26 | return StringConcat(op1, (int)strlen(op1), op2.buffer.Ptr(), op2.length); 27 | } 28 | 29 | String operator+(const String & op1, const char * op2) 30 | { 31 | if(!op1.buffer) 32 | return String(op2); 33 | 34 | return StringConcat(op1.buffer.Ptr(), op1.length, op2, (int)strlen(op2)); 35 | } 36 | 37 | String operator+(const String & op1, const String & op2) 38 | { 39 | if(!op1.buffer && !op2.buffer) 40 | return String(); 41 | else if(!op1.buffer) 42 | return String(op2); 43 | else if(!op2.buffer) 44 | return String(op1); 45 | 46 | return StringConcat(op1.buffer.Ptr(), op1.length, op2.buffer.Ptr(), op2.length); 47 | } 48 | 49 | int StringToInt(const String & str, int radix) 50 | { 51 | if (str.StartsWith("0x")) 52 | return (int)strtoll(str.Buffer(), NULL, 16); 53 | else 54 | return (int)strtoll(str.Buffer(), NULL, radix); 55 | } 56 | unsigned int StringToUInt(const String & str, int radix) 57 | { 58 | if (str.StartsWith("0x")) 59 | return (unsigned int)strtoull(str.Buffer(), NULL, 16); 60 | else 61 | return (unsigned int)strtoull(str.Buffer(), NULL, radix); 62 | } 63 | double StringToDouble(const String & str) 64 | { 65 | return (double)strtod(str.Buffer(), NULL); 66 | } 67 | float StringToFloat(const String & str) 68 | { 69 | return strtof(str.Buffer(), NULL); 70 | } 71 | 72 | String String::ReplaceAll(String src, String dst) const 73 | { 74 | String rs = *this; 75 | int index = 0; 76 | int srcLen = src.length; 77 | int len = rs.length; 78 | while ((index = rs.IndexOf(src, index)) != -1) 79 | { 80 | rs = rs.SubString(0, index) + dst + rs.SubString(index + srcLen, len - index - srcLen); 81 | len = rs.length; 82 | } 83 | return rs; 84 | } 85 | 86 | String String::FromWString(const wchar_t * wstr) 87 | { 88 | #ifdef _WIN32 89 | return CoreLib::IO::Encoding::UTF16->ToString((const char*)wstr, (int)(wcslen(wstr) * sizeof(wchar_t))); 90 | #else 91 | return CoreLib::IO::Encoding::UTF32->ToString((const char*)wstr, (int)(wcslen(wstr) * sizeof(wchar_t))); 92 | #endif 93 | } 94 | 95 | String String::FromWChar(const wchar_t ch) 96 | { 97 | #ifdef _WIN32 98 | return CoreLib::IO::Encoding::UTF16->ToString((const char*)&ch, (int)(sizeof(wchar_t))); 99 | #else 100 | return CoreLib::IO::Encoding::UTF32->ToString((const char*)&ch, (int)(sizeof(wchar_t))); 101 | #endif 102 | } 103 | 104 | String String::FromUnicodePoint(unsigned int codePoint) 105 | { 106 | char buf[6]; 107 | int len = CoreLib::IO::EncodeUnicodePointToUTF8(buf, (int)codePoint); 108 | buf[len] = 0; 109 | return String(buf); 110 | } 111 | 112 | const wchar_t * String::ToWString(int * len) const 113 | { 114 | if (!buffer) 115 | { 116 | if (len) 117 | *len = 0; 118 | return L""; 119 | } 120 | else 121 | { 122 | if (wcharBuffer) 123 | { 124 | if (len) 125 | *len = (int)wcslen(wcharBuffer); 126 | return wcharBuffer; 127 | } 128 | List buf; 129 | CoreLib::IO::Encoding::UTF16->GetBytes(buf, *this); 130 | if (len) 131 | *len = buf.Count() / sizeof(wchar_t); 132 | buf.Add(0); 133 | buf.Add(0); 134 | const_cast(this)->wcharBuffer = (wchar_t*)buf.Buffer(); 135 | buf.ReleaseBuffer(); 136 | return wcharBuffer; 137 | } 138 | } 139 | 140 | String String::PadLeft(char ch, int pLen) 141 | { 142 | StringBuilder sb; 143 | for (int i = 0; i < pLen - this->length; i++) 144 | sb << ch; 145 | for (int i = 0; i < this->length; i++) 146 | sb << buffer[i]; 147 | return sb.ProduceString(); 148 | } 149 | 150 | String String::PadRight(char ch, int pLen) 151 | { 152 | StringBuilder sb; 153 | for (int i = 0; i < this->length; i++) 154 | sb << buffer[i]; 155 | for (int i = 0; i < pLen - this->length; i++) 156 | sb << ch; 157 | return sb.ProduceString(); 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /Source/CoreLib/Link.h: -------------------------------------------------------------------------------- 1 | #ifndef CORE_LIB_LINK_H 2 | #define CORE_LIB_LINK_H 3 | 4 | #include "Common.h" 5 | #include "Exception.h" 6 | 7 | namespace CoreLib 8 | { 9 | namespace Basic 10 | { 11 | template 12 | class LinkedList; 13 | 14 | template 15 | class LinkedNode 16 | { 17 | template 18 | friend class LinkedList; 19 | private: 20 | LinkedNode *pPrev, *pNext; 21 | LinkedList * FLink; 22 | public: 23 | T Value; 24 | LinkedNode (LinkedList * lnk):FLink(lnk) 25 | { 26 | pPrev = pNext = 0; 27 | }; 28 | LinkedNode * GetPrevious() 29 | { 30 | return pPrev; 31 | }; 32 | LinkedNode * GetNext() 33 | { 34 | return pNext; 35 | }; 36 | LinkedNode * InsertAfter(const T & nData) 37 | { 38 | LinkedNode * n = new LinkedNode(FLink); 39 | n->Value = nData; 40 | n->pPrev = this; 41 | n->pNext = this->pNext; 42 | LinkedNode *npp = n->pNext; 43 | if (npp) 44 | { 45 | npp->pPrev = n; 46 | } 47 | pNext = n; 48 | if (!n->pNext) 49 | FLink->FTail = n; 50 | FLink->FCount ++; 51 | return n; 52 | }; 53 | LinkedNode * InsertBefore(const T & nData) 54 | { 55 | LinkedNode * n = new LinkedNode(FLink); 56 | n->Value = nData; 57 | n->pPrev = pPrev; 58 | n->pNext = this; 59 | pPrev = n; 60 | LinkedNode *npp = n->pPrev; 61 | if (npp) 62 | npp->pNext = n; 63 | if (!n->pPrev) 64 | FLink->FHead = n; 65 | FLink->FCount ++; 66 | return n; 67 | }; 68 | void Delete() 69 | { 70 | if (pPrev) 71 | pPrev->pNext = pNext; 72 | if (pNext) 73 | pNext->pPrev = pPrev; 74 | FLink->FCount --; 75 | if (FLink->FHead == this) 76 | { 77 | FLink->FHead = pNext; 78 | } 79 | if (FLink->FTail == this) 80 | { 81 | FLink->FTail = pPrev; 82 | } 83 | delete this; 84 | } 85 | }; 86 | template 87 | class LinkedList 88 | { 89 | template 90 | friend class LinkedNode; 91 | private: 92 | LinkedNode * FHead, *FTail; 93 | int FCount; 94 | public: 95 | class Iterator 96 | { 97 | public: 98 | LinkedNode * Current, *Next; 99 | void SetCurrent(LinkedNode * cur) 100 | { 101 | Current = cur; 102 | if (Current) 103 | Next = Current->GetNext(); 104 | else 105 | Next = 0; 106 | } 107 | Iterator() 108 | { 109 | Current = Next = 0; 110 | } 111 | Iterator(LinkedNode * cur) 112 | { 113 | SetCurrent(cur); 114 | } 115 | T & operator *() const 116 | { 117 | return Current->Value; 118 | } 119 | Iterator& operator ++() 120 | { 121 | SetCurrent(Next); 122 | return *this; 123 | } 124 | Iterator operator ++(int) 125 | { 126 | Iterator rs = *this; 127 | SetCurrent(Next); 128 | return rs; 129 | } 130 | bool operator != (const Iterator & iter) const 131 | { 132 | return Current != iter.Current; 133 | } 134 | bool operator == (const Iterator & iter) const 135 | { 136 | return Current == iter.Current; 137 | } 138 | }; 139 | Iterator begin() const 140 | { 141 | return Iterator(FHead); 142 | } 143 | Iterator end() const 144 | { 145 | return Iterator(0); 146 | } 147 | public: 148 | LinkedList() : FHead(0), FTail(0), FCount(0) 149 | { 150 | } 151 | ~LinkedList() 152 | { 153 | Clear(); 154 | } 155 | LinkedList(const LinkedList & link) : FHead(0), FTail(0), FCount(0) 156 | { 157 | this->operator=(link); 158 | } 159 | LinkedList(LinkedList && link) : FHead(0), FTail(0), FCount(0) 160 | { 161 | this->operator=(_Move(link)); 162 | } 163 | LinkedList & operator = (LinkedList && link) 164 | { 165 | if (FHead != 0) 166 | Clear(); 167 | FHead = link.FHead; 168 | FTail = link.FTail; 169 | FCount = link.FCount; 170 | link.FHead = 0; 171 | link.FTail = 0; 172 | link.FCount = 0; 173 | for (auto node = FHead; node; node = node->GetNext()) 174 | node->FLink = this; 175 | return *this; 176 | } 177 | LinkedList & operator = (const LinkedList & link) 178 | { 179 | if (FHead != 0) 180 | Clear(); 181 | auto p = link.FHead; 182 | while (p) 183 | { 184 | AddLast(p->Value); 185 | p = p->GetNext(); 186 | } 187 | return *this; 188 | } 189 | template 190 | void ForEach(const IteratorFunc & f) 191 | { 192 | auto p = FHead; 193 | while (p) 194 | { 195 | f(p->Value); 196 | p = p->GetNext(); 197 | } 198 | } 199 | LinkedNode * GetNode(int x) 200 | { 201 | LinkedNode *pCur = FHead; 202 | for (int i=0;ipNext; 206 | else 207 | throw "Index out of range"; 208 | } 209 | return pCur; 210 | }; 211 | LinkedNode * Find(const T& fData) 212 | { 213 | for (LinkedNode * pCur = FHead; pCur; pCur = pCur->pNext) 214 | { 215 | if (pCur->Value == fData) 216 | return pCur; 217 | } 218 | return 0; 219 | }; 220 | LinkedNode * FirstNode() const 221 | { 222 | return FHead; 223 | }; 224 | T & First() const 225 | { 226 | if (!FHead) 227 | throw IndexOutofRangeException("LinkedList: index out of range."); 228 | return FHead->Value; 229 | } 230 | T & Last() const 231 | { 232 | if (!FTail) 233 | throw IndexOutofRangeException("LinkedList: index out of range."); 234 | return FTail->Value; 235 | } 236 | LinkedNode * LastNode() const 237 | { 238 | return FTail; 239 | }; 240 | LinkedNode * AddLast(const T & nData) 241 | { 242 | LinkedNode * n = new LinkedNode(this); 243 | n->Value = nData; 244 | n->pPrev = FTail; 245 | if (FTail) 246 | FTail->pNext = n; 247 | n->pNext = 0; 248 | FTail = n; 249 | if (!FHead) 250 | FHead = n; 251 | FCount ++; 252 | return n; 253 | }; 254 | // Insert a blank node 255 | LinkedNode * AddLast() 256 | { 257 | LinkedNode * n = new LinkedNode(this); 258 | n->pPrev = FTail; 259 | if (FTail) 260 | FTail->pNext = n; 261 | n->pNext = 0; 262 | FTail = n; 263 | if (!FHead) 264 | FHead = n; 265 | FCount ++; 266 | return n; 267 | }; 268 | LinkedNode * AddFirst(const T& nData) 269 | { 270 | LinkedNode *n = new LinkedNode(this); 271 | n->Value = nData; 272 | n->pPrev = 0; 273 | n->pNext = FHead; 274 | if (FHead) 275 | FHead->pPrev = n; 276 | FHead = n; 277 | if (!FTail) 278 | FTail = n; 279 | FCount ++; 280 | return n; 281 | }; 282 | void Delete(LinkedNode*n, int Count = 1) 283 | { 284 | LinkedNode *n1,*n2 = 0, *tn; 285 | n1 = n->pPrev; 286 | tn = n; 287 | int numDeleted = 0; 288 | for (int i=0; ipNext; 291 | delete tn; 292 | tn = n2; 293 | numDeleted++; 294 | if (tn == 0) 295 | break; 296 | } 297 | if (n1) 298 | n1->pNext = n2; 299 | else 300 | FHead = n2; 301 | if (n2) 302 | n2->pPrev = n1; 303 | else 304 | FTail = n1; 305 | FCount -= numDeleted; 306 | } 307 | void Clear() 308 | { 309 | for (LinkedNode *n = FHead; n; ) 310 | { 311 | LinkedNode * tmp = n->pNext; 312 | delete n; 313 | n = tmp; 314 | } 315 | FHead = 0; 316 | FTail = 0; 317 | FCount = 0; 318 | } 319 | List ToList() const 320 | { 321 | List rs; 322 | rs.Reserve(FCount); 323 | for (auto & item : *this) 324 | { 325 | rs.Add(item); 326 | } 327 | return rs; 328 | } 329 | int Count() 330 | { 331 | return FCount; 332 | } 333 | }; 334 | } 335 | } 336 | #endif 337 | -------------------------------------------------------------------------------- /Source/CoreLib/MemoryPool.cpp: -------------------------------------------------------------------------------- 1 | #include "MemoryPool.h" 2 | #include 3 | 4 | namespace CoreLib 5 | { 6 | namespace Basic 7 | { 8 | MemoryPool::MemoryPool(unsigned char * pBuffer, int pLog2BlockSize, int numBlocks) 9 | { 10 | Init(pBuffer, pLog2BlockSize, numBlocks); 11 | } 12 | void MemoryPool::Init(unsigned char * pBuffer, int pLog2BlockSize, int numBlocks) 13 | { 14 | assert(pLog2BlockSize >= 1 && pLog2BlockSize <= 30); 15 | assert(numBlocks >= 4); 16 | buffer = pBuffer; 17 | blockSize = 1 << pLog2BlockSize; 18 | log2BlockSize = pLog2BlockSize; 19 | numLevels = Math::Log2Floor(numBlocks); 20 | freeList[0] = (FreeListNode*)buffer; 21 | freeList[0]->NextPtr = nullptr; 22 | freeList[0]->PrevPtr = nullptr; 23 | used.SetMax(1 << (numLevels)); 24 | for (int i = 1; i < MaxLevels; i++) 25 | { 26 | freeList[i] = nullptr; 27 | } 28 | } 29 | int MemoryPool::AllocBlock(int level) 30 | { 31 | if (level < 0) 32 | return -1; 33 | if (freeList[level] == nullptr) 34 | { 35 | auto largeBlockAddr = AllocBlock(level - 1); 36 | if (largeBlockAddr != -1) 37 | { 38 | auto block1 = (FreeListNode*)(buffer + ((largeBlockAddr ^ (1 << (numLevels - level))) << log2BlockSize)); 39 | block1->NextPtr = nullptr; 40 | block1->PrevPtr = nullptr; 41 | freeList[level] = block1; 42 | 43 | int blockIndex = (1 << level) + (largeBlockAddr >> (numLevels-level)) - 1; 44 | used.Add(blockIndex); 45 | return largeBlockAddr; 46 | } 47 | else 48 | return -1; 49 | } 50 | else 51 | { 52 | auto node = freeList[level]; 53 | if (node->NextPtr) 54 | { 55 | node->NextPtr->PrevPtr = node->PrevPtr; 56 | } 57 | freeList[level] = freeList[level]->NextPtr; 58 | int rs = (int)((unsigned char *)node - buffer) >> log2BlockSize; 59 | int blockIndex = (1 << level) + (rs >> (numLevels - level)) - 1; 60 | used.Add(blockIndex); 61 | return rs; 62 | } 63 | } 64 | unsigned char * MemoryPool::Alloc(int size) 65 | { 66 | if (size == 0) 67 | return nullptr; 68 | int originalSize = size; 69 | if (size < blockSize) 70 | size = blockSize; 71 | int order = numLevels - (Math::Log2Ceil(size) - log2BlockSize); 72 | assert(order >= 0 && order < MaxLevels); 73 | 74 | bytesAllocated += (1 << ((numLevels-order) + log2BlockSize)); 75 | bytesWasted += (1 << ((numLevels - order) + log2BlockSize)) - originalSize; 76 | 77 | int blockId = AllocBlock(order); 78 | if (blockId != -1) 79 | return buffer + (blockId << log2BlockSize); 80 | else 81 | return nullptr; 82 | } 83 | void MemoryPool::FreeBlock(unsigned char * ptr, int level) 84 | { 85 | int indexInLevel = (int)(ptr - buffer) >> (numLevels - level + log2BlockSize); 86 | int blockIndex = (1 << level) + indexInLevel - 1; 87 | assert(used.Contains(blockIndex)); 88 | int buddyIndex = (blockIndex & 1) ? blockIndex + 1 : blockIndex - 1; 89 | used.Remove(blockIndex); 90 | if (level > 0 && !used.Contains(buddyIndex)) 91 | { 92 | auto buddyPtr = (FreeListNode *)(buffer + ((((int)(ptr - buffer) >> log2BlockSize) ^ (1 << (numLevels - level))) << log2BlockSize)); 93 | if (buddyPtr->PrevPtr) 94 | { 95 | buddyPtr->PrevPtr->NextPtr = buddyPtr->NextPtr; 96 | } 97 | if (buddyPtr->NextPtr) 98 | { 99 | buddyPtr->NextPtr->PrevPtr = buddyPtr->PrevPtr; 100 | } 101 | if (freeList[level] == buddyPtr) 102 | { 103 | freeList[level] = buddyPtr->NextPtr; 104 | } 105 | // recursively free parent blocks 106 | auto parentPtr = Math::Min(buddyPtr, (FreeListNode*)ptr); 107 | if (level > 0) 108 | FreeBlock((unsigned char*)parentPtr, level - 1); 109 | } 110 | else 111 | { 112 | // insert to freelist 113 | auto freeNode = (FreeListNode *)ptr; 114 | freeNode->NextPtr = freeList[level]; 115 | freeNode->PrevPtr = nullptr; 116 | if (freeList[level]) 117 | freeList[level]->PrevPtr = freeNode; 118 | freeList[level] = freeNode; 119 | } 120 | } 121 | void MemoryPool::Free(unsigned char * ptr, int size) 122 | { 123 | if (size == 0) 124 | return; 125 | int originalSize = size; 126 | if (size < blockSize) 127 | size = blockSize; 128 | int level = numLevels - (Math::Log2Ceil(size) - log2BlockSize); 129 | bytesAllocated -= (1 << ((numLevels-level) + log2BlockSize)); 130 | bytesWasted -= (1 << ((numLevels - level) + log2BlockSize)) - originalSize; 131 | FreeBlock(ptr, level); 132 | } 133 | } 134 | } 135 | 136 | -------------------------------------------------------------------------------- /Source/CoreLib/MemoryPool.h: -------------------------------------------------------------------------------- 1 | #ifndef CORE_LIB_MEMORY_POOL_H 2 | #define CORE_LIB_MEMORY_POOL_H 3 | 4 | #include "Basic.h" 5 | #include "IntSet.h" 6 | 7 | namespace CoreLib 8 | { 9 | namespace Basic 10 | { 11 | struct MemoryBlockFields 12 | { 13 | unsigned int Occupied : 1; 14 | unsigned int Order : 31; 15 | }; 16 | struct FreeListNode 17 | { 18 | FreeListNode * PrevPtr = nullptr, *NextPtr = nullptr; 19 | }; 20 | class MemoryPool 21 | { 22 | private: 23 | static const int MaxLevels = 32; 24 | int blockSize = 0, log2BlockSize = 0; 25 | int numLevels = 0; 26 | int bytesAllocated = 0; 27 | int bytesWasted = 0; 28 | unsigned char * buffer = nullptr; 29 | FreeListNode * freeList[MaxLevels]; 30 | IntSet used; 31 | int AllocBlock(int level); 32 | void FreeBlock(unsigned char * ptr, int level); 33 | public: 34 | MemoryPool(unsigned char * buffer, int log2BlockSize, int numBlocks); 35 | MemoryPool() = default; 36 | void Init(unsigned char * buffer, int log2BlockSize, int numBlocks); 37 | unsigned char * Alloc(int size); 38 | void Free(unsigned char * ptr, int size); 39 | }; 40 | 41 | class OutofPoolMemoryException : public Exception 42 | {}; 43 | 44 | template 45 | class ObjectPool 46 | { 47 | static const int ObjectSize = sizeof(T) < 8 ? 8 : sizeof(T); 48 | private: 49 | struct FreeList 50 | { 51 | FreeList* Next; 52 | }; 53 | FreeList * freeList = nullptr; 54 | int allocPtr = 0; 55 | int poolSize = 0; 56 | void * buffer = 0; 57 | T * GetFreeObject() 58 | { 59 | if (freeList) 60 | { 61 | auto rs = (T*)freeList; 62 | freeList = freeList->Next; 63 | return rs; 64 | } 65 | return nullptr; 66 | } 67 | public: 68 | ObjectPool() 69 | { 70 | freeList = nullptr; 71 | allocPtr = 0; 72 | buffer = malloc(PoolSize * ObjectSize); 73 | } 74 | 75 | void Close() 76 | { 77 | free(buffer); 78 | } 79 | 80 | void Free(T * obj) 81 | { 82 | auto newList = (FreeList*)obj; 83 | newList->Next = freeList; 84 | freeList = newList; 85 | } 86 | 87 | void * Buffer() 88 | { 89 | return buffer; 90 | } 91 | 92 | T * Alloc() 93 | { 94 | auto rs = GetFreeObject(); 95 | if (!rs) 96 | { 97 | if (allocPtr < PoolSize) 98 | { 99 | rs = (T*)buffer + allocPtr; 100 | allocPtr++; 101 | } 102 | } 103 | if (!rs) 104 | { 105 | throw OutofPoolMemoryException(); 106 | } 107 | return rs; 108 | } 109 | }; 110 | }; 111 | 112 | #define USE_POOL_ALLOCATOR(T, PoolSize) \ 113 | private:\ 114 | static CoreLib::ObjectPool _pool;\ 115 | public:\ 116 | void * operator new(std::size_t) { return _pool.Alloc(); } \ 117 | void operator delete(void * ptr) {_pool.Free((T*)ptr); }\ 118 | int GetObjectId() { return (int)(this - (T*)_pool.Buffer()); }\ 119 | static void ClosePool(); 120 | #define IMPL_POOL_ALLOCATOR(T, PoolSize) \ 121 | CoreLib::ObjectPool T::_pool;\ 122 | void T::ClosePool() { _pool.Close(); } 123 | 124 | } 125 | 126 | #endif -------------------------------------------------------------------------------- /Source/CoreLib/SecureCRT.h: -------------------------------------------------------------------------------- 1 | #ifndef _MSC_VER 2 | #ifndef CORE_LIB_SECURE_CRT_H 3 | #define CORE_LIB_SECURE_CRT_H 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | inline void memcpy_s(void *dest, size_t numberOfElements, const void * src, size_t count) 10 | { 11 | memcpy(dest, src, count); 12 | } 13 | 14 | #define _TRUNCATE ((size_t)-1) 15 | #define _stricmp strcasecmp 16 | 17 | inline void fopen_s(FILE**f, const char * fileName, const char * mode) 18 | { 19 | *f = fopen(fileName, mode); 20 | } 21 | 22 | inline size_t fread_s(void * buffer, size_t bufferSize, size_t elementSize, size_t count, FILE * stream) 23 | { 24 | return fread(buffer, elementSize, count, stream); 25 | } 26 | 27 | inline size_t wcsnlen_s(const wchar_t * str, size_t /*numberofElements*/) 28 | { 29 | return wcslen(str); 30 | } 31 | 32 | inline size_t strnlen_s(const char * str, size_t numberofElements) 33 | { 34 | return strnlen(str, numberofElements); 35 | } 36 | 37 | inline int sprintf_s(char * buffer, size_t sizeOfBuffer, const char * format, ...) 38 | { 39 | va_list argptr; 40 | va_start(argptr, format); 41 | int rs = snprintf(buffer, sizeOfBuffer, format, argptr); 42 | va_end(argptr); 43 | return rs; 44 | } 45 | 46 | inline int swprintf_s(wchar_t * buffer, size_t sizeOfBuffer, const wchar_t * format, ...) 47 | { 48 | va_list argptr; 49 | va_start(argptr, format); 50 | int rs = swprintf(buffer, sizeOfBuffer, format, argptr); 51 | va_end(argptr); 52 | return rs; 53 | } 54 | 55 | inline void wcscpy_s(wchar_t * strDestination, size_t /*numberOfElements*/, const wchar_t * strSource) 56 | { 57 | wcscpy(strDestination, strSource); 58 | } 59 | inline void strcpy_s(char * strDestination, size_t /*numberOfElements*/, const char * strSource) 60 | { 61 | strcpy(strDestination, strSource); 62 | } 63 | 64 | inline void wcsncpy_s(wchar_t * strDestination, size_t /*numberOfElements*/, const wchar_t * strSource, size_t count) 65 | { 66 | wcscpy(strDestination, strSource); 67 | //wcsncpy(strDestination, strSource, count); 68 | } 69 | inline void strncpy_s(char * strDestination, size_t /*numberOfElements*/, const char * strSource, size_t count) 70 | { 71 | strncpy(strDestination, strSource, count); 72 | //wcsncpy(strDestination, strSource, count); 73 | } 74 | #endif 75 | #endif 76 | -------------------------------------------------------------------------------- /Source/CoreLib/Stream.cpp: -------------------------------------------------------------------------------- 1 | #include "Stream.h" 2 | #ifdef _WIN32 3 | #include 4 | #endif 5 | #include "LibIO.h" 6 | 7 | namespace CoreLib 8 | { 9 | namespace IO 10 | { 11 | using namespace CoreLib::Basic; 12 | FileStream::FileStream(const CoreLib::Basic::String & fileName, FileMode fileMode) 13 | { 14 | Init(fileName, fileMode, fileMode==FileMode::Open?FileAccess::Read:FileAccess::Write, FileShare::None); 15 | } 16 | FileStream::FileStream(const CoreLib::Basic::String & fileName, FileMode fileMode, FileAccess access, FileShare share) 17 | { 18 | Init(fileName, fileMode, access, share); 19 | } 20 | void FileStream::Init(const CoreLib::Basic::String & fileName, FileMode fileMode, FileAccess access, FileShare share) 21 | { 22 | const wchar_t * mode = L"rt"; 23 | const char* modeMBCS = "rt"; 24 | switch (fileMode) 25 | { 26 | case CoreLib::IO::FileMode::Create: 27 | if (access == FileAccess::Read) 28 | throw ArgumentException("Read-only access is incompatible with Create mode."); 29 | else if (access == FileAccess::ReadWrite) 30 | { 31 | mode = L"w+b"; 32 | modeMBCS = "w+b"; 33 | this->fileAccess = FileAccess::ReadWrite; 34 | } 35 | else 36 | { 37 | mode = L"wb"; 38 | modeMBCS = "wb"; 39 | this->fileAccess = FileAccess::Write; 40 | } 41 | break; 42 | case CoreLib::IO::FileMode::Open: 43 | if (access == FileAccess::Read) 44 | { 45 | mode = L"rb"; 46 | modeMBCS = "rb"; 47 | this->fileAccess = FileAccess::Read; 48 | } 49 | else if (access == FileAccess::ReadWrite) 50 | { 51 | mode = L"r+b"; 52 | modeMBCS = "r+b"; 53 | this->fileAccess = FileAccess::ReadWrite; 54 | } 55 | else 56 | { 57 | mode = L"wb"; 58 | modeMBCS = "wb"; 59 | this->fileAccess = FileAccess::Write; 60 | } 61 | break; 62 | case CoreLib::IO::FileMode::CreateNew: 63 | if (File::Exists(fileName)) 64 | { 65 | throw IOException("Failed opening '" + fileName + "', file already exists."); 66 | } 67 | if (access == FileAccess::Read) 68 | throw ArgumentException("Read-only access is incompatible with Create mode."); 69 | else if (access == FileAccess::ReadWrite) 70 | { 71 | mode = L"w+b"; 72 | this->fileAccess = FileAccess::ReadWrite; 73 | } 74 | else 75 | { 76 | mode = L"wb"; 77 | this->fileAccess = FileAccess::Write; 78 | } 79 | break; 80 | case CoreLib::IO::FileMode::Append: 81 | if (access == FileAccess::Read) 82 | throw ArgumentException("Read-only access is incompatible with Append mode."); 83 | else if (access == FileAccess::ReadWrite) 84 | { 85 | mode = L"a+b"; 86 | this->fileAccess = FileAccess::ReadWrite; 87 | } 88 | else 89 | { 90 | mode = L"ab"; 91 | this->fileAccess = FileAccess::Write; 92 | } 93 | break; 94 | default: 95 | break; 96 | } 97 | int shFlag; 98 | #ifdef _WIN32 99 | switch (share) 100 | { 101 | case CoreLib::IO::FileShare::None: 102 | shFlag = _SH_DENYRW; 103 | break; 104 | case CoreLib::IO::FileShare::ReadOnly: 105 | shFlag = _SH_DENYWR; 106 | break; 107 | case CoreLib::IO::FileShare::WriteOnly: 108 | shFlag = _SH_DENYRD; 109 | break; 110 | case CoreLib::IO::FileShare::ReadWrite: 111 | shFlag = _SH_DENYNO; 112 | break; 113 | default: 114 | throw ArgumentException("Invalid file share mode."); 115 | break; 116 | } 117 | handle = _wfsopen(fileName.ToWString(), mode, shFlag); 118 | #else 119 | handle = fopen(fileName.Buffer(), modeMBCS); 120 | #endif 121 | if (!handle) 122 | { 123 | throw IOException("Cannot open file '" + fileName + "'"); 124 | } 125 | } 126 | FileStream::~FileStream() 127 | { 128 | Close(); 129 | } 130 | Int64 FileStream::GetPosition() 131 | { 132 | #ifdef _WIN32 133 | fpos_t pos; 134 | fgetpos(handle, &pos); 135 | return pos; 136 | #else 137 | fpos64_t pos; 138 | fgetpos64(handle, &pos); 139 | return *(Int64*)(&pos); 140 | #endif 141 | } 142 | void FileStream::Seek(SeekOrigin origin, Int64 offset) 143 | { 144 | int _origin; 145 | switch (origin) 146 | { 147 | case CoreLib::IO::SeekOrigin::Start: 148 | _origin = SEEK_SET; 149 | endReached = false; 150 | break; 151 | case CoreLib::IO::SeekOrigin::End: 152 | _origin = SEEK_END; 153 | endReached = true; 154 | break; 155 | case CoreLib::IO::SeekOrigin::Current: 156 | _origin = SEEK_CUR; 157 | endReached = false; 158 | break; 159 | default: 160 | throw NotSupportedException("Unsupported seek origin."); 161 | break; 162 | } 163 | #ifdef _WIN32 164 | int rs = _fseeki64(handle, offset, _origin); 165 | #else 166 | int rs = fseek(handle, (int)offset, _origin); 167 | #endif 168 | if (rs != 0) 169 | { 170 | throw IOException("FileStream seek failed."); 171 | } 172 | } 173 | Int64 FileStream::Read(void * buffer, Int64 length) 174 | { 175 | auto bytes = fread_s(buffer, (size_t)length, 1, (size_t)length, handle); 176 | if (bytes == 0 && length > 0) 177 | { 178 | if (!feof(handle)) 179 | throw IOException("FileStream read failed."); 180 | else if (endReached) 181 | throw EndOfStreamException("End of file is reached."); 182 | endReached = true; 183 | } 184 | return (int)bytes; 185 | } 186 | Int64 FileStream::Write(const void * buffer, Int64 length) 187 | { 188 | auto bytes = (Int64)fwrite(buffer, 1, (size_t)length, handle); 189 | if (bytes < length) 190 | { 191 | throw IOException("FileStream write failed."); 192 | } 193 | return bytes; 194 | } 195 | bool FileStream::CanRead() 196 | { 197 | return ((int)fileAccess & (int)FileAccess::Read) != 0; 198 | } 199 | bool FileStream::CanWrite() 200 | { 201 | return ((int)fileAccess & (int)FileAccess::Write) != 0; 202 | } 203 | void FileStream::Close() 204 | { 205 | if (handle) 206 | { 207 | fclose(handle); 208 | handle = 0; 209 | } 210 | } 211 | bool FileStream::IsEnd() 212 | { 213 | return endReached; 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /Source/CoreLib/Tokenizer.h: -------------------------------------------------------------------------------- 1 | #ifndef CORELIB_TEXT_PARSER_H 2 | #define CORELIB_TEXT_PARSER_H 3 | 4 | #include "Basic.h" 5 | 6 | namespace CoreLib 7 | { 8 | namespace Text 9 | { 10 | class TextFormatException : public Exception 11 | { 12 | public: 13 | TextFormatException(String message) 14 | : Exception(message) 15 | {} 16 | }; 17 | 18 | inline bool IsLetter(char ch) 19 | { 20 | return ((ch >= 'a' && ch <= 'z') || 21 | (ch >= 'A' && ch <= 'Z') || ch == '_'); 22 | } 23 | 24 | inline bool IsDigit(char ch) 25 | { 26 | return ch >= '0' && ch <= '9'; 27 | } 28 | 29 | inline bool IsPunctuation(char ch) 30 | { 31 | return ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '%' || 32 | ch == '!' || ch == '^' || ch == '&' || ch == '(' || ch == ')' || 33 | ch == '=' || ch == '{' || ch == '}' || ch == '[' || ch == ']' || 34 | ch == '|' || ch == ';' || ch == ',' || ch == '.' || ch == '<' || 35 | ch == '>' || ch == '~' || ch == '@' || ch == ':' || ch == '?' || ch == '#'; 36 | } 37 | 38 | inline bool IsWhiteSpace(char ch) 39 | { 40 | return (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v'); 41 | } 42 | 43 | class CodePosition 44 | { 45 | public: 46 | int Line = -1, Col = -1, Pos = -1; 47 | String FileName; 48 | String ToString() 49 | { 50 | StringBuilder sb(100); 51 | sb << FileName; 52 | if (Line != -1) 53 | sb << "(" << Line << ")"; 54 | return sb.ProduceString(); 55 | } 56 | CodePosition() = default; 57 | CodePosition(int line, int col, int pos, String fileName) 58 | { 59 | Line = line; 60 | Col = col; 61 | Pos = pos; 62 | this->FileName = fileName; 63 | } 64 | bool operator < (const CodePosition & pos) const 65 | { 66 | return FileName < pos.FileName || (FileName == pos.FileName && Line < pos.Line) || 67 | (FileName == pos.FileName && Line == pos.Line && Col < pos.Col); 68 | } 69 | bool operator == (const CodePosition & pos) const 70 | { 71 | return FileName == pos.FileName && Line == pos.Line && Col == pos.Col; 72 | } 73 | }; 74 | 75 | enum class TokenType 76 | { 77 | EndOfFile = -1, 78 | // illegal 79 | Unknown, 80 | // identifier 81 | Identifier, 82 | // constant 83 | IntLiterial, DoubleLiterial, StringLiterial, CharLiterial, 84 | // operators 85 | Semicolon, Comma, Dot, LBrace, RBrace, LBracket, RBracket, LParent, RParent, 86 | OpAssign, OpAdd, OpSub, OpMul, OpDiv, OpMod, OpNot, OpBitNot, OpLsh, OpRsh, 87 | OpEql, OpNeq, OpGreater, OpLess, OpGeq, OpLeq, 88 | OpAnd, OpOr, OpBitXor, OpBitAnd, OpBitOr, 89 | OpInc, OpDec, OpAddAssign, OpSubAssign, OpMulAssign, OpDivAssign, OpModAssign, 90 | OpShlAssign, OpShrAssign, OpOrAssign, OpAndAssign, OpXorAssign, 91 | 92 | QuestionMark, Colon, RightArrow, At, Pound, PoundPound, 93 | }; 94 | 95 | String TokenTypeToString(TokenType type); 96 | 97 | enum TokenFlag : unsigned int 98 | { 99 | AtStartOfLine = 1 << 0, 100 | AfterWhitespace = 1 << 1, 101 | }; 102 | typedef unsigned int TokenFlags; 103 | 104 | class Token 105 | { 106 | public: 107 | TokenType Type = TokenType::Unknown; 108 | String Content; 109 | CodePosition Position; 110 | TokenFlags flags = 0; 111 | Token() = default; 112 | Token(TokenType type, const String & content, int line, int col, int pos, String fileName, TokenFlags flags = 0) 113 | : flags(flags) 114 | { 115 | Type = type; 116 | Content = content; 117 | Position = CodePosition(line, col, pos, fileName); 118 | } 119 | }; 120 | 121 | enum class TokenizeErrorType 122 | { 123 | InvalidCharacter, InvalidEscapeSequence 124 | }; 125 | 126 | List TokenizeText(const String & fileName, const String & text, Procedure errorHandler); 127 | List TokenizeText(const String & fileName, const String & text); 128 | List TokenizeText(const String & text); 129 | 130 | String EscapeStringLiteral(String str); 131 | String UnescapeStringLiteral(String str); 132 | 133 | class TokenReader 134 | { 135 | private: 136 | bool legal; 137 | List tokens; 138 | int tokenPtr; 139 | public: 140 | TokenReader(Basic::String text); 141 | int ReadInt() 142 | { 143 | auto token = ReadToken(); 144 | bool neg = false; 145 | if (token.Content == '-') 146 | { 147 | neg = true; 148 | token = ReadToken(); 149 | } 150 | if (token.Type == TokenType::IntLiterial) 151 | { 152 | if (neg) 153 | return -StringToInt(token.Content); 154 | else 155 | return StringToInt(token.Content); 156 | } 157 | throw TextFormatException("Text parsing error: int expected."); 158 | } 159 | unsigned int ReadUInt() 160 | { 161 | auto token = ReadToken(); 162 | if (token.Type == TokenType::IntLiterial) 163 | { 164 | return StringToUInt(token.Content); 165 | } 166 | throw TextFormatException("Text parsing error: int expected."); 167 | } 168 | double ReadDouble() 169 | { 170 | auto token = ReadToken(); 171 | bool neg = false; 172 | if (token.Content == '-') 173 | { 174 | neg = true; 175 | token = ReadToken(); 176 | } 177 | if (token.Type == TokenType::DoubleLiterial || token.Type == TokenType::IntLiterial) 178 | { 179 | if (neg) 180 | return -StringToDouble(token.Content); 181 | else 182 | return StringToDouble(token.Content); 183 | } 184 | throw TextFormatException("Text parsing error: floating point value expected."); 185 | } 186 | float ReadFloat() 187 | { 188 | return (float)ReadDouble(); 189 | } 190 | String ReadWord() 191 | { 192 | auto token = ReadToken(); 193 | if (token.Type == TokenType::Identifier) 194 | { 195 | return token.Content; 196 | } 197 | throw TextFormatException("Text parsing error: identifier expected."); 198 | } 199 | String Read(const char * expectedStr) 200 | { 201 | auto token = ReadToken(); 202 | if (token.Content == expectedStr) 203 | { 204 | return token.Content; 205 | } 206 | throw TextFormatException("Text parsing error: \'" + String(expectedStr) + "\' expected."); 207 | } 208 | String Read(String expectedStr) 209 | { 210 | auto token = ReadToken(); 211 | if (token.Content == expectedStr) 212 | { 213 | return token.Content; 214 | } 215 | throw TextFormatException("Text parsing error: \'" + expectedStr + "\' expected."); 216 | } 217 | 218 | String ReadStringLiteral() 219 | { 220 | auto token = ReadToken(); 221 | if (token.Type == TokenType::StringLiterial) 222 | { 223 | return token.Content; 224 | } 225 | throw TextFormatException("Text parsing error: string literal expected."); 226 | } 227 | void Back(int count) 228 | { 229 | tokenPtr -= count; 230 | } 231 | Token ReadToken() 232 | { 233 | if (tokenPtr < tokens.Count()) 234 | { 235 | auto &rs = tokens[tokenPtr]; 236 | tokenPtr++; 237 | return rs; 238 | } 239 | throw TextFormatException("Unexpected ending."); 240 | } 241 | Token NextToken(int offset = 0) 242 | { 243 | if (tokenPtr + offset < tokens.Count()) 244 | return tokens[tokenPtr + offset]; 245 | else 246 | { 247 | Token rs; 248 | rs.Type = TokenType::Unknown; 249 | return rs; 250 | } 251 | } 252 | bool LookAhead(String token) 253 | { 254 | if (tokenPtr < tokens.Count()) 255 | { 256 | auto next = NextToken(); 257 | return next.Content == token; 258 | } 259 | else 260 | { 261 | return false; 262 | } 263 | } 264 | bool IsEnd() 265 | { 266 | return tokenPtr == tokens.Count(); 267 | } 268 | public: 269 | bool IsLegalText() 270 | { 271 | return legal; 272 | } 273 | }; 274 | 275 | List Split(String str, char c); 276 | } 277 | } 278 | 279 | #endif -------------------------------------------------------------------------------- /Source/CoreLib/TypeTraits.h: -------------------------------------------------------------------------------- 1 | #ifndef CORELIB_TYPETRAITS_H 2 | #define CORELIB_TYPETRAITS_H 3 | 4 | namespace CoreLib 5 | { 6 | namespace Basic 7 | { 8 | struct TraitResultYes 9 | { 10 | char x; 11 | }; 12 | struct TraitResultNo 13 | { 14 | char x[2]; 15 | }; 16 | 17 | template 18 | struct IsBaseOfTraitHost 19 | { 20 | operator B*() const { return nullptr; } 21 | operator D*() { return nullptr; } 22 | }; 23 | 24 | template 25 | struct IsBaseOf 26 | { 27 | template 28 | static TraitResultYes Check(D*, T) { return TraitResultYes(); } 29 | static TraitResultNo Check(B*, int) { return TraitResultNo(); } 30 | enum { Value = sizeof(Check(IsBaseOfTraitHost(), int())) == sizeof(TraitResultYes) }; 31 | }; 32 | 33 | template 34 | struct EnableIf {}; 35 | 36 | template 37 | struct EnableIf { typedef T type; }; 38 | 39 | template 40 | struct IsConvertible 41 | { 42 | static TraitResultYes Use(B) {}; 43 | static TraitResultNo Use(...) {}; 44 | enum { Value = sizeof(Use(*(D*)(nullptr))) == sizeof(TraitResultYes) }; 45 | }; 46 | } 47 | } 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /Source/CoreLib/corelib.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {buffer.pointer,s} 7 | buffer.pointer,s 8 | 9 | 10 | 11 | {{ size={_count} }} 12 | 13 | _count 14 | 15 | _count 16 | _buffer 17 | 18 | 19 | 20 | 21 | 22 | {{ size={_count} }} 23 | 24 | _count 25 | bufferSize 26 | 27 | _count 28 | buffer 29 | 30 | 31 | 32 | 33 | 34 | 35 | {{ size={_count} }} 36 | 37 | _count 38 | 39 | _count 40 | _buffer 41 | 42 | 43 | 44 | 45 | 46 | {{ size={FCount} }} 47 | 48 | 49 | FCount 50 | FHead 51 | pNext 52 | Value 53 | 54 | 55 | 56 | 57 | 58 | {{ size={_count} }} 59 | 60 | _count 61 | bucketSizeMinusOne + 1 62 | 63 | bucketSizeMinusOne + 1 64 | hashMap 65 | 66 | 67 | 68 | 69 | 70 | {{ size={_count} }} 71 | 72 | _count 73 | bucketSizeMinusOne + 1 74 | 75 | kvPairs.FCount 76 | kvPairs.FHead 77 | pNext 78 | Value 79 | 80 | 81 | 82 | 83 | 84 | {{ size={dict._count} }} 85 | 86 | dict._count 87 | dict.bucketSizeMinusOne + 1 88 | 89 | dict.kvPairs.FCount 90 | dict.kvPairs.FHead 91 | pNext 92 | Value 93 | 94 | 95 | 96 | 97 | 98 | pointer 99 | empty 100 | RefPtr {*pointer} 101 | 102 | pointer 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /Source/SpireCompiler/D3DCompiler.cpp: -------------------------------------------------------------------------------- 1 | #include "D3DCompiler.h" 2 | #ifdef _WIN32 3 | 4 | #include 5 | 6 | using namespace CoreLib::Basic; 7 | 8 | typedef HRESULT (WINAPI *D3DCompile2Func)( 9 | LPCVOID pSrcData, 10 | SIZE_T SrcDataSize, 11 | LPCSTR pSourceName, 12 | D3D_SHADER_MACRO * pDefines, 13 | ID3DInclude * pInclude, 14 | LPCSTR pEntrypoint, 15 | LPCSTR pTarget, 16 | UINT Flags1, 17 | UINT Flags2, 18 | UINT SecondaryDataFlags, 19 | LPCVOID pSecondaryData, 20 | SIZE_T SecondaryDataSize, 21 | ID3DBlob **ppCode, 22 | ID3DBlob **ppErrorMsgs 23 | ); 24 | typedef HRESULT (WINAPI *D3DCreateBlobFunc)( 25 | SIZE_T Size, 26 | ID3DBlob **ppBlob 27 | ); 28 | 29 | 30 | class D3DCompilerImpl : public D3DCompiler 31 | { 32 | public: 33 | D3DCompile2Func D3DCompile2; 34 | D3DCreateBlobFunc D3DCreateBlob; 35 | HMODULE Lib; 36 | virtual bool Compile(CoreLib::String input, CoreLib::String stageName, CoreLib::String & errMsg) override 37 | { 38 | auto entryPoint = "main"; 39 | char * profile = "ps_5_0"; 40 | if (stageName == "vs") 41 | profile = "vs_5_0"; 42 | else if (stageName == "tes") 43 | profile = "hs_5_0"; 44 | else if (stageName == "tcs") 45 | profile = "ds_5_0"; 46 | ID3DBlob *code, *err = nullptr; 47 | D3DCompile2(input.Buffer(), input.Length(), "", nullptr, nullptr, entryPoint, profile, 0, 0, 0, 0, 0, &code, &err); 48 | if (err != nullptr) 49 | { 50 | errMsg = (char*)err->GetBufferPointer(); 51 | return false; 52 | } 53 | return true; 54 | } 55 | }; 56 | 57 | D3DCompiler * LoadD3DCompiler() 58 | { 59 | auto d3dLib = LoadLibraryW(L"D3DCompiler_47.dll"); 60 | if (d3dLib) 61 | { 62 | auto compileFunc = (D3DCompile2Func)GetProcAddress(d3dLib, "D3DCompile2"); 63 | auto createBlobFunc = (D3DCreateBlobFunc)GetProcAddress(d3dLib, "D3DCreateBlob"); 64 | if (compileFunc && createBlobFunc) 65 | { 66 | D3DCompilerImpl* result = new D3DCompilerImpl(); 67 | result->D3DCompile2 = compileFunc; 68 | result->D3DCreateBlob = createBlobFunc; 69 | result->Lib = d3dLib; 70 | return result; 71 | } 72 | else 73 | FreeLibrary(d3dLib); 74 | } 75 | return nullptr; 76 | } 77 | 78 | #else 79 | 80 | D3DCompiler * LoadD3DCompiler() 81 | { 82 | return nullptr; 83 | } 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /Source/SpireCompiler/D3DCompiler.h: -------------------------------------------------------------------------------- 1 | #ifndef SPIRE_D3D_COMPILER_H 2 | #define SPIRE_D3D_COMPILER_H 3 | 4 | #include "CoreLib/Basic.h" 5 | 6 | class D3DCompiler : public CoreLib::Object 7 | { 8 | public: 9 | virtual bool Compile(CoreLib::String input, CoreLib::String stageName, CoreLib::String & errMsg) = 0; 10 | }; 11 | 12 | D3DCompiler * LoadD3DCompiler(); 13 | 14 | #endif -------------------------------------------------------------------------------- /Source/SpireCompiler/ShaderCompilerShell.cpp: -------------------------------------------------------------------------------- 1 | #include "CoreLib/LibIO.h" 2 | #include "SpireLib.h" 3 | #include "D3DCompiler.h" 4 | 5 | using namespace CoreLib::Basic; 6 | using namespace CoreLib::IO; 7 | using namespace Spire::Compiler; 8 | 9 | // Try to read an argument for a command-line option. 10 | String tryReadCommandLineArgument(wchar_t const* option, wchar_t***ioCursor, wchar_t**end) 11 | { 12 | wchar_t**& cursor = *ioCursor; 13 | if (cursor == end) 14 | { 15 | fprintf(stderr, "expected an argument for command-line option '%S'", option); 16 | exit(1); 17 | } 18 | else 19 | { 20 | return String::FromWString(*cursor++); 21 | } 22 | } 23 | 24 | int wmain(int argc, wchar_t* argv[]) 25 | { 26 | int returnValue = -1; 27 | { 28 | // We need to parse any command-line arguments. 29 | String outputDir; 30 | CompileOptions options; 31 | 32 | // As we parse the command line, we will rewrite the 33 | // entries in `argv` to collect any "ordinary" arguments. 34 | wchar_t const** inputPaths = (wchar_t const**)&argv[1]; 35 | wchar_t const** inputPathCursor = inputPaths; 36 | 37 | wchar_t** argCursor = &argv[1]; 38 | wchar_t** argEnd = &argv[argc]; 39 | while (argCursor != argEnd) 40 | { 41 | wchar_t const* arg = *argCursor++; 42 | if (arg[0] == '-') 43 | { 44 | String argStr = String::FromWString(arg); 45 | 46 | // The argument looks like an option, so try to parse it. 47 | if (argStr == "-out") 48 | outputDir = tryReadCommandLineArgument(arg, &argCursor, argEnd); 49 | else if (argStr == "-symbo") 50 | options.SymbolToCompile = tryReadCommandLineArgument(arg, &argCursor, argEnd); 51 | else if (argStr == "-schedule") 52 | options.ScheduleFileName = tryReadCommandLineArgument(arg, &argCursor, argEnd); 53 | else if (argStr == "-backend") 54 | { 55 | String name = tryReadCommandLineArgument(arg, &argCursor, argEnd); 56 | if (name == "glsl") 57 | { 58 | options.Target = CodeGenTarget::GLSL; 59 | } 60 | else if (name == "glsl_vk") 61 | { 62 | options.Target = CodeGenTarget::GLSL_Vulkan; 63 | } 64 | else if (name == "glsl_vk_onedesc") 65 | { 66 | options.Target = CodeGenTarget::GLSL_Vulkan_OneDesc; 67 | } 68 | else if (name == "hlsl") 69 | { 70 | options.Target = CodeGenTarget::HLSL; 71 | } 72 | else if (name == "spriv") 73 | { 74 | options.Target = CodeGenTarget::SPIRV; 75 | } 76 | else 77 | { 78 | fprintf(stderr, "unknown code generation target '%S'\n", name.ToWString()); 79 | } 80 | } 81 | else if (argStr == "-genchoice") 82 | options.Mode = CompilerMode::GenerateChoice; 83 | else if (argStr == "--") 84 | { 85 | // The `--` option causes us to stop trying to parse options, 86 | // and treat the rest of the command line as input file names: 87 | while (argCursor != argEnd) 88 | { 89 | *inputPathCursor++ = *argCursor++; 90 | } 91 | break; 92 | } 93 | else 94 | { 95 | fprintf(stderr, "unknown command-line option '%S'\n", argStr.ToWString()); 96 | // TODO: print a usage message 97 | exit(1); 98 | } 99 | } 100 | else 101 | { 102 | *inputPathCursor++ = arg; 103 | } 104 | } 105 | 106 | int inputPathCount = (int)(inputPathCursor - inputPaths); 107 | if (inputPathCount == 0) 108 | { 109 | fprintf(stderr, "error: no input file specified\n"); 110 | exit(1); 111 | } 112 | else if (inputPathCount > 1) 113 | { 114 | fprintf(stderr, "error: multiple input files specified\n"); 115 | exit(1); 116 | } 117 | 118 | String fileName = String::FromWString(inputPaths[0]); 119 | 120 | // Output directory defaults to the path of the input file 121 | if (outputDir.Length() == 0) 122 | { 123 | outputDir = Path::GetDirectoryName(fileName); 124 | } 125 | 126 | auto sourceDir = Path::GetDirectoryName(fileName); 127 | String schedule; 128 | if (options.ScheduleFileName.Length()) 129 | { 130 | try 131 | { 132 | schedule = File::ReadAllText(options.ScheduleFileName); 133 | options.ScheduleSource = schedule; 134 | } 135 | catch (IOException) 136 | { 137 | printf("Cannot open schedule file '%S'.\n", options.ScheduleFileName.ToWString()); 138 | goto end; 139 | } 140 | } 141 | CompileResult result; 142 | try 143 | { 144 | auto files = SpireLib::CompileShaderSourceFromFile(result, fileName, options); 145 | for (auto & f : files) 146 | { 147 | 148 | try 149 | { 150 | f.SaveToFile(Path::Combine(outputDir, f.MetaData.ShaderName + ".cse")); 151 | } 152 | catch (Exception &) 153 | { 154 | result.GetErrorWriter()->diagnose(CodePosition(0, 0, 0, ""), Diagnostics::cannotWriteOutputFile, Path::Combine(outputDir, f.MetaData.ShaderName + ".cse")); 155 | } 156 | } 157 | 158 | if (options.Target == CodeGenTarget::HLSL) 159 | { 160 | // verify shader using D3DCompileShaderFromFile 161 | RefPtr d3dCompiler = LoadD3DCompiler(); 162 | if (d3dCompiler) 163 | { 164 | for (auto & f : files) 165 | { 166 | for (auto & stage : f.Sources) 167 | { 168 | String errMsg; 169 | d3dCompiler->Compile(stage.Value.MainCode, stage.Key, errMsg); 170 | if (errMsg.Length()) 171 | { 172 | auto dumpFileName = f.MetaData.ShaderName + "." + stage.Key + ".hlsl"; 173 | result.GetErrorWriter()->diagnose(CodePosition(0, 0, 0, dumpFileName), Diagnostics::d3dCompileInfo, errMsg); 174 | File::WriteAllText(Path::Combine(Path::GetDirectoryName(fileName), dumpFileName), stage.Value.MainCode); 175 | } 176 | } 177 | } 178 | } 179 | else 180 | { 181 | printf("failed to load d3d compiler for verification.\n"); 182 | } 183 | } 184 | } 185 | catch (Exception & e) 186 | { 187 | printf("internal compiler error: %S\n", e.Message.ToWString()); 188 | } 189 | result.PrintDiagnostics(); 190 | if (result.GetErrorCount() == 0) 191 | returnValue = 0; 192 | 193 | } 194 | end:; 195 | #ifdef _MSC_VER 196 | _CrtDumpMemoryLeaks(); 197 | #endif 198 | return returnValue; 199 | } -------------------------------------------------------------------------------- /Source/SpireCompiler/SpireCompiler.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | 26 | 27 | Header Files 28 | 29 | 30 | -------------------------------------------------------------------------------- /Source/SpireCore/Closure.h: -------------------------------------------------------------------------------- 1 | #ifndef BAKERSL_SHADER_CLOSURE_H 2 | #define BAKERSL_SHADER_CLOSURE_H 3 | #include "SymbolTable.h" 4 | 5 | namespace Spire 6 | { 7 | namespace Compiler 8 | { 9 | RefPtr CreateShaderClosure(DiagnosticSink * sink, SymbolTable * symTable, ShaderSymbol * shader); 10 | void FlattenShaderClosure(DiagnosticSink * sink, SymbolTable * symTable, ShaderClosure * shader); 11 | void InsertImplicitImportOperators(DiagnosticSink * sink, ShaderIR * shader); 12 | } 13 | } 14 | 15 | #endif -------------------------------------------------------------------------------- /Source/SpireCore/CodeGenBackend.h: -------------------------------------------------------------------------------- 1 | #ifndef CODE_GEN_BACKEND_H 2 | #define CODE_GEN_BACKEND_H 3 | 4 | #include "../CoreLib/Basic.h" 5 | #include "CompiledProgram.h" 6 | #include "SymbolTable.h" 7 | #include "TypeLayout.h" 8 | 9 | namespace Spire 10 | { 11 | namespace Compiler 12 | { 13 | class CodeGenBackend : public CoreLib::Basic::Object 14 | { 15 | public: 16 | virtual CompiledShaderSource GenerateShader(CompileResult & result, SymbolTable * symbols, ILShader * shader, DiagnosticSink * err) = 0; 17 | virtual LayoutRule GetDefaultLayoutRule() = 0; 18 | }; 19 | 20 | CodeGenBackend * CreateGLSLCodeGen(); 21 | CodeGenBackend * CreateGLSL_VulkanCodeGen(); 22 | CodeGenBackend * CreateGLSL_VulkanOneDescCodeGen(); 23 | CodeGenBackend * CreateHLSLCodeGen(); 24 | CodeGenBackend * CreateSpirVCodeGen(); 25 | } 26 | } 27 | 28 | #endif -------------------------------------------------------------------------------- /Source/SpireCore/CodeWriter.h: -------------------------------------------------------------------------------- 1 | #ifndef IL_CODE_WRITER_H 2 | #define IL_CODE_WRITER_H 3 | 4 | #include "IL.h" 5 | #include "ShaderCompiler.h" 6 | 7 | namespace Spire 8 | { 9 | namespace Compiler 10 | { 11 | class CodeWriter 12 | { 13 | private: 14 | List> cfgNode; 15 | ConstantPool * constantPool = nullptr; 16 | public: 17 | void SetConstantPool(ConstantPool * pool) 18 | { 19 | constantPool = pool; 20 | } 21 | CFGNode * GetCurrentNode() 22 | { 23 | return cfgNode.Last().Ptr(); 24 | } 25 | void PushNode() 26 | { 27 | RefPtr n = new CFGNode(); 28 | cfgNode.Add(n); 29 | } 30 | RefPtr PopNode() 31 | { 32 | auto rs = cfgNode.Last(); 33 | cfgNode.SetSize(cfgNode.Count() - 1); 34 | return rs; 35 | } 36 | void Assign(ILType * type, ILOperand * dest, ILOperand * src) // handles base type and ILArrayType assignment 37 | { 38 | auto arrType = dynamic_cast(type); 39 | if (arrType) 40 | { 41 | for (int i = 0; i < arrType->ArrayLength; i++) 42 | { 43 | auto srcAddr = Add(src, i); 44 | auto destAddr = Add(dest, i); 45 | Store(destAddr, Load(srcAddr)); 46 | } 47 | } 48 | else 49 | Store(dest, Load(src)); 50 | } 51 | ILOperand * Select(ILOperand * cond, ILOperand * v0, ILOperand * v1) 52 | { 53 | auto rs = new SelectInstruction(cond, v0, v1); 54 | cfgNode.Last()->InsertTail(rs); 55 | return rs; 56 | } 57 | ILOperand * BitAnd(ILOperand * v0, ILOperand * v1) 58 | { 59 | auto instr = new BitAndInstruction(v0, v1); 60 | cfgNode.Last()->InsertTail(instr); 61 | return instr; 62 | } 63 | ILOperand * BitAnd(ILOperand * v0, int c) 64 | { 65 | auto instr = new BitAndInstruction(v0, constantPool->CreateConstant(c)); 66 | cfgNode.Last()->InsertTail(instr); 67 | return instr; 68 | } 69 | ILOperand * Add(ILOperand * v0, ILOperand * v1) 70 | { 71 | auto instr = new AddInstruction(v0, v1); 72 | cfgNode.Last()->InsertTail(instr); 73 | return instr; 74 | } 75 | ILOperand * Add(ILOperand * v0, int v1) 76 | { 77 | auto instr = new AddInstruction(v0, constantPool->CreateConstant(v1)); 78 | cfgNode.Last()->InsertTail(instr); 79 | return instr; 80 | } 81 | ILOperand * Mul(ILOperand * v0, ILOperand * v1) 82 | { 83 | auto instr = new MulInstruction(v0, v1); 84 | cfgNode.Last()->InsertTail(instr); 85 | return instr; 86 | } 87 | ILOperand * Copy(ILOperand * src) 88 | { 89 | auto rs = new CopyInstruction(src); 90 | cfgNode.Last()->InsertTail(rs); 91 | return rs; 92 | } 93 | ILOperand * Load(ILOperand * src, int offset) 94 | { 95 | if (offset == 0) 96 | { 97 | auto instr = new LoadInstruction(src); 98 | cfgNode.Last()->InsertTail(instr); 99 | return instr; 100 | } 101 | else 102 | { 103 | auto dest = new AddInstruction(src, constantPool->CreateConstant(offset)); 104 | cfgNode.Last()->InsertTail(dest); 105 | auto instr = new LoadInstruction(dest); 106 | cfgNode.Last()->InsertTail(instr); 107 | return instr; 108 | } 109 | } 110 | ILOperand * Load(ILOperand * src) 111 | { 112 | auto instr = new LoadInstruction(src); 113 | cfgNode.Last()->InsertTail(instr); 114 | return instr; 115 | } 116 | ILOperand * Load(ILOperand * src, ILOperand * offset) 117 | { 118 | auto dest = new AddInstruction(src, offset); 119 | cfgNode.Last()->InsertTail(dest); 120 | return Load(dest); 121 | } 122 | StoreInstruction * Store(ILOperand * dest, ILOperand * value) 123 | { 124 | auto instr = new StoreInstruction(dest, value); 125 | cfgNode.Last()->InsertTail(instr); 126 | return instr; 127 | } 128 | DiscardInstruction * Discard() 129 | { 130 | auto instr = new DiscardInstruction(); 131 | cfgNode.Last()->InsertTail(instr); 132 | return instr; 133 | } 134 | MemberUpdateInstruction * Update(ILOperand * dest, ILOperand * offset, ILOperand * value) 135 | { 136 | auto instr = new MemberUpdateInstruction(dest, offset, value); 137 | cfgNode.Last()->InsertTail(instr); 138 | return instr; 139 | } 140 | MemberLoadInstruction * Retrieve(ILOperand * dest, ILOperand * offset) 141 | { 142 | auto instr = new MemberLoadInstruction(dest, offset); 143 | cfgNode.Last()->InsertTail(instr); 144 | return instr; 145 | } 146 | //AllocVarInstruction * AllocVar(ILType * type, ILOperand * size) 147 | //{ 148 | // auto arrType = dynamic_cast(type); 149 | // if (arrType) 150 | // { 151 | // // check: size must be constant 1. Do not support array of array in IL level. 152 | // auto s = dynamic_cast(size); 153 | // if (!s || s->IntValues[0] != 1) 154 | // throw ArgumentException("AllocVar(arrayType, size): size must be constant 1."); 155 | // auto instr = new AllocVarInstruction(arrType->BaseType, program.CreateConstant(arrType->ArrayLength)); 156 | // cfgNode->InsertTail(instr); 157 | // return instr; 158 | // } 159 | // else 160 | // { 161 | // auto instr = new AllocVarInstruction(type, size); 162 | // cfgNode->InsertTail(instr); 163 | // return instr; 164 | // } 165 | //} 166 | AllocVarInstruction * AllocVar(RefPtr & type, ILOperand * size) 167 | { 168 | auto arrType = dynamic_cast(type.Ptr()); 169 | if (arrType) 170 | { 171 | // check: size must be constant 1. Do not support array of array in IL level. 172 | auto s = dynamic_cast(size); 173 | if (!s || s->IntValues[0] != 1) 174 | throw ArgumentException("AllocVar(arrayType, size): size must be constant 1."); 175 | auto instr = new AllocVarInstruction(arrType->BaseType, constantPool->CreateConstant(arrType->ArrayLength)); 176 | cfgNode.Last()->InsertTail(instr); 177 | return instr; 178 | } 179 | else 180 | { 181 | auto instr = new AllocVarInstruction(type, size); 182 | cfgNode.Last()->InsertTail(instr); 183 | return instr; 184 | } 185 | } 186 | /*GLeaInstruction * GLea(ILType * type, const String & name) 187 | { 188 | auto arrType = dynamic_cast(type); 189 | auto instr = new GLeaInstruction(); 190 | if (arrType) 191 | instr->Type = new ILPointerType(arrType->BaseType); 192 | else 193 | instr->Type = new ILPointerType(type); 194 | instr->Name = name; 195 | instr->VariableName = name; 196 | cfgNode->InsertTail(instr); 197 | return instr; 198 | }*/ 199 | FetchArgInstruction * FetchArg(RefPtr type, int argId) 200 | { 201 | auto instr = new FetchArgInstruction(type); 202 | cfgNode.Last()->InsertTail(instr); 203 | instr->ArgId = argId; 204 | return instr; 205 | } 206 | 207 | void Insert(ILInstruction * instr) 208 | { 209 | cfgNode.Last()->InsertTail(instr); 210 | } 211 | }; 212 | } 213 | } 214 | 215 | #endif -------------------------------------------------------------------------------- /Source/SpireCore/CompiledProgram.cpp: -------------------------------------------------------------------------------- 1 | #include "CompiledProgram.h" 2 | 3 | namespace Spire 4 | { 5 | namespace Compiler 6 | { 7 | void IndentString(StringBuilder & sb, String src) 8 | { 9 | int indent = 0; 10 | bool beginTrim = true; 11 | for (int c = 0; c < src.Length(); c++) 12 | { 13 | auto ch = src[c]; 14 | if (ch == '\n') 15 | { 16 | sb << "\n"; 17 | 18 | beginTrim = true; 19 | } 20 | else 21 | { 22 | if (beginTrim) 23 | { 24 | while (c < src.Length() - 1 && (src[c] == '\t' || src[c] == '\n' || src[c] == '\r' || src[c] == ' ')) 25 | { 26 | c++; 27 | ch = src[c]; 28 | } 29 | for (int i = 0; i < indent - 1; i++) 30 | sb << '\t'; 31 | if (ch != '}' && indent > 0) 32 | sb << '\t'; 33 | beginTrim = false; 34 | } 35 | 36 | if (ch == '{') 37 | indent++; 38 | else if (ch == '}') 39 | indent--; 40 | if (indent < 0) 41 | indent = 0; 42 | 43 | sb << ch; 44 | } 45 | } 46 | } 47 | ShaderChoiceValue ShaderChoiceValue::Parse(String str) 48 | { 49 | return ShaderChoiceValue(str); 50 | } 51 | 52 | } 53 | } -------------------------------------------------------------------------------- /Source/SpireCore/CompiledProgram.h: -------------------------------------------------------------------------------- 1 | #ifndef BAKER_SL_COMPILED_PROGRAM_H 2 | #define BAKER_SL_COMPILED_PROGRAM_H 3 | 4 | #include "../CoreLib/Basic.h" 5 | #include "Diagnostics.h" 6 | #include "IL.h" 7 | #include "Syntax.h" 8 | 9 | namespace Spire 10 | { 11 | namespace Compiler 12 | { 13 | class ConstantPoolImpl; 14 | 15 | class ConstantPool 16 | { 17 | private: 18 | ConstantPoolImpl * impl; 19 | public: 20 | ILConstOperand * CreateConstant(ILConstOperand * c); 21 | ILConstOperand * CreateConstantIntVec(int val0, int val1); 22 | ILConstOperand * CreateConstantIntVec(int val0, int val1, int val2); 23 | ILConstOperand * CreateConstantIntVec(int val0, int val1, int val3, int val4); 24 | ILConstOperand * CreateConstant(int val, int vectorSize = 0); 25 | ILConstOperand * CreateConstant(float val, int vectorSize = 0); 26 | ILConstOperand * CreateConstant(float val, float val1); 27 | ILConstOperand * CreateConstant(float val, float val1, float val2); 28 | ILConstOperand * CreateConstant(float val, float val1, float val2, float val3); 29 | ILConstOperand * CreateConstant(bool b); 30 | ILConstOperand * CreateConstantU(unsigned int u); 31 | 32 | ILOperand * CreateDefaultValue(ILType * type); 33 | ILUndefinedOperand * GetUndefinedOperand(); 34 | ConstantPool(); 35 | ~ConstantPool(); 36 | }; 37 | 38 | class ILShader; 39 | 40 | class ILWorld : public Object 41 | { 42 | public: 43 | String Name; 44 | CodePosition Position; 45 | RefPtr OutputType; 46 | List Inputs; 47 | RefPtr Code; 48 | EnumerableDictionary Components; 49 | bool IsAbstract = false; 50 | EnumerableDictionary Attributes; 51 | EnumerableHashSet ReferencedFunctions; // internal names of referenced functions 52 | ILShader * Shader = nullptr; 53 | }; 54 | 55 | class StageAttribute 56 | { 57 | public: 58 | String Name; 59 | String Value; 60 | CodePosition Position; 61 | }; 62 | 63 | class ILStage : public Object 64 | { 65 | public: 66 | CodePosition Position; 67 | String Name; 68 | String StageType; 69 | EnumerableDictionary Attributes; 70 | }; 71 | 72 | class ILModuleParameterSet; 73 | 74 | class ILModuleParameterInstance : public ILOperand 75 | { 76 | public: 77 | ILModuleParameterSet * Module = nullptr; 78 | int BufferOffset = -1; 79 | int Size = 0; 80 | List BindingPoints; // for legacy API, usually one item. Samplers may have multiple binding points in OpenGL. 81 | virtual String ToString() 82 | { 83 | return "moduleParam<" + Name + ">"; 84 | } 85 | }; 86 | 87 | class ILModuleParameterSet : public RefObject 88 | { 89 | public: 90 | int BufferSize = 0; 91 | String BindingName; 92 | int DescriptorSetId = -1; 93 | int UniformBufferLegacyBindingPoint = -1; 94 | bool IsTopLevel = false; 95 | 96 | // for sub parameter sets: these are starting indices for each type of resource (for vk they should be the same) 97 | int TextureBindingStartIndex = 0, SamplerBindingStartIndex = 0, StorageBufferBindingStartIndex = 0, UniformBindingStartIndex = 0; 98 | 99 | // for sub parameter sets: this is the offset into parent's uniform buffer where fields of this parameter set started. 100 | int UniformBufferOffset = 0; 101 | EnumerableDictionary> Parameters; 102 | List> SubModules; 103 | }; 104 | 105 | class ILShader 106 | { 107 | public: 108 | CodePosition Position; 109 | String Name; 110 | EnumerableDictionary> ModuleParamSets; 111 | EnumerableDictionary> Worlds; 112 | EnumerableDictionary> Stages; 113 | }; 114 | 115 | class ILParameter 116 | { 117 | public: 118 | RefPtr Type; 119 | ParameterQualifier Qualifier; 120 | ILParameter() = default; 121 | ILParameter(RefPtr type, ParameterQualifier qualifier = ParameterQualifier::In) 122 | : Type(type), Qualifier(qualifier) 123 | {} 124 | }; 125 | 126 | class ILFunction 127 | { 128 | public: 129 | EnumerableDictionary Parameters; 130 | RefPtr ReturnType; 131 | RefPtr Code; 132 | String Name; 133 | }; 134 | 135 | class ILProgram 136 | { 137 | public: 138 | RefPtr ConstantPool = new Compiler::ConstantPool(); 139 | List> Shaders; 140 | EnumerableDictionary> Functions; 141 | List> Structs; 142 | }; 143 | 144 | class ShaderChoiceValue 145 | { 146 | public: 147 | String WorldName; 148 | ShaderChoiceValue() = default; 149 | ShaderChoiceValue(String world) 150 | { 151 | WorldName = world; 152 | } 153 | static ShaderChoiceValue Parse(String str); 154 | String ToString() 155 | { 156 | return WorldName; 157 | } 158 | bool operator == (const ShaderChoiceValue & val) 159 | { 160 | return WorldName == val.WorldName; 161 | } 162 | bool operator != (const ShaderChoiceValue & val) 163 | { 164 | return WorldName != val.WorldName; 165 | } 166 | int GetHashCode() 167 | { 168 | return WorldName.GetHashCode(); 169 | } 170 | }; 171 | 172 | class ShaderChoice 173 | { 174 | public: 175 | String ChoiceName; 176 | String DefaultValue; 177 | List Options; 178 | }; 179 | 180 | class ShaderMetaData 181 | { 182 | public: 183 | CoreLib::String ShaderName; 184 | CoreLib::EnumerableDictionary> ParameterSets; // bindingName->DescSet 185 | }; 186 | 187 | class StageSource 188 | { 189 | public: 190 | String MainCode; 191 | List BinaryCode; 192 | }; 193 | 194 | class CompiledShaderSource 195 | { 196 | public: 197 | EnumerableDictionary Stages; 198 | ShaderMetaData MetaData; 199 | }; 200 | 201 | void IndentString(StringBuilder & sb, String src); 202 | 203 | class CompileResult 204 | { 205 | public: 206 | DiagnosticSink sink; 207 | String ScheduleFile; 208 | RefPtr Program; 209 | List Choices; 210 | EnumerableDictionary CompiledSource; // shader -> stage -> code 211 | void PrintDiagnostics() 212 | { 213 | for (int i = 0; i < sink.diagnostics.Count(); i++) 214 | { 215 | fprintf(stderr, "%S(%d): %s %d: %S\n", 216 | sink.diagnostics[i].Position.FileName.ToWString(), 217 | sink.diagnostics[i].Position.Line, 218 | getSeverityName(sink.diagnostics[i].severity), 219 | sink.diagnostics[i].ErrorID, 220 | sink.diagnostics[i].Message.ToWString()); 221 | } 222 | } 223 | CompileResult() 224 | {} 225 | DiagnosticSink * GetErrorWriter() 226 | { 227 | return &sink; 228 | } 229 | int GetErrorCount() 230 | { 231 | return sink.GetErrorCount(); 232 | } 233 | }; 234 | 235 | } 236 | } 237 | 238 | #endif -------------------------------------------------------------------------------- /Source/SpireCore/Diagnostics.cpp: -------------------------------------------------------------------------------- 1 | // Diagnostics.cpp 2 | #include "Diagnostics.h" 3 | 4 | #include "CompiledProgram.h" 5 | #include "SymbolTable.h" 6 | #include "Syntax.h" 7 | 8 | #include 9 | 10 | namespace Spire { 11 | namespace Compiler { 12 | 13 | void printDiagnosticArg(StringBuilder& sb, char const* str) 14 | { 15 | sb << str; 16 | } 17 | 18 | void printDiagnosticArg(StringBuilder& sb, int str) 19 | { 20 | sb << str; 21 | } 22 | 23 | void printDiagnosticArg(StringBuilder& sb, CoreLib::Basic::String const& str) 24 | { 25 | sb << str; 26 | } 27 | 28 | void printDiagnosticArg(StringBuilder& sb, Decl* decl) 29 | { 30 | sb << decl->Name.Content; 31 | } 32 | 33 | void printDiagnosticArg(StringBuilder& sb, Type* type) 34 | { 35 | sb << type->DataType->ToString(); 36 | } 37 | 38 | void printDiagnosticArg(StringBuilder& sb, ExpressionType* type) 39 | { 40 | sb << type->ToString(); 41 | } 42 | 43 | void printDiagnosticArg(StringBuilder& sb, ILType* type) 44 | { 45 | sb << type->ToString(); 46 | } 47 | 48 | void printDiagnosticArg(StringBuilder& sb, CoreLib::Text::TokenType tokenType) 49 | { 50 | sb << TokenTypeToString(tokenType); 51 | } 52 | 53 | void printDiagnosticArg(StringBuilder& sb, Token const& token) 54 | { 55 | sb << token.Content; 56 | } 57 | 58 | void printDiagnosticArg(StringBuilder& sb, StageAttribute const& attr) 59 | { 60 | sb << attr.Value; 61 | } 62 | 63 | CodePosition const& getDiagnosticPos(SyntaxNode const* syntax) 64 | { 65 | return syntax->Position; 66 | } 67 | 68 | CodePosition const& getDiagnosticPos(CoreLib::Text::Token const& token) 69 | { 70 | return token.Position; 71 | } 72 | 73 | CodePosition const& getDiagnosticPos(ShaderClosure* shader) 74 | { 75 | return shader->Position; 76 | } 77 | 78 | // Take the format string for a diagnostic message, along with its arguments, and turn it into a 79 | static void formatDiagnosticMessage(StringBuilder& sb, char const* format, int argCount, DiagnosticArg const* const* args) 80 | { 81 | char const* spanBegin = format; 82 | for(;;) 83 | { 84 | char const* spanEnd = spanBegin; 85 | while (int c = *spanEnd) 86 | { 87 | if (c == '$') 88 | break; 89 | spanEnd++; 90 | } 91 | 92 | sb.Append(spanBegin, int(spanEnd - spanBegin)); 93 | if (!*spanEnd) 94 | return; 95 | 96 | assert(*spanEnd == '$'); 97 | spanEnd++; 98 | int d = *spanEnd++; 99 | switch (d) 100 | { 101 | // A double dollar sign `$$` is used to emit a single `$` 102 | case '$': 103 | sb.Append('$'); 104 | break; 105 | 106 | // A single digit means to emit the corresponding argument. 107 | // TODO: support more than 10 arguments, and add options 108 | // to control formatting, etc. 109 | case '0': case '1': case '2': case '3': case '4': 110 | case '5': case '6': case '7': case '8': case '9': 111 | { 112 | int index = d - '0'; 113 | if (index >= argCount) 114 | { 115 | // TODO(tfoley): figure out what a good policy will be for "panic" situations like this 116 | throw InvalidOperationException("too few arguments for diagnostic message"); 117 | } 118 | else 119 | { 120 | DiagnosticArg const* arg = args[index]; 121 | arg->printFunc(sb, arg->data); 122 | } 123 | } 124 | break; 125 | 126 | default: 127 | throw InvalidOperationException("invalid diagnostic message format"); 128 | break; 129 | } 130 | 131 | spanBegin = spanEnd; 132 | } 133 | } 134 | 135 | void DiagnosticSink::diagnoseImpl(CodePosition const& pos, DiagnosticInfo const& info, int argCount, DiagnosticArg const* const* args) 136 | { 137 | StringBuilder sb; 138 | formatDiagnosticMessage(sb, info.messageFormat, argCount, args); 139 | 140 | Diagnostic diagnostic; 141 | diagnostic.ErrorID = info.id; 142 | diagnostic.Message = sb.ProduceString(); 143 | diagnostic.Position = pos; 144 | diagnostic.severity = info.severity; 145 | 146 | diagnostics.Add(diagnostic); 147 | if (diagnostic.severity >= Severity::Error) 148 | { 149 | errorCount++; 150 | } 151 | if (diagnostic.severity >= Severity::Fatal) 152 | { 153 | // TODO: figure out a better policy for aborting compilation 154 | throw InvalidOperationException(); 155 | } 156 | } 157 | 158 | namespace Diagnostics 159 | { 160 | #define DIAGNOSTIC(id, severity, name, messageFormat) const DiagnosticInfo name = { id, Severity::severity, messageFormat }; 161 | #include "DiagnosticDefs.h" 162 | } 163 | 164 | 165 | }} // namespace Spire::Compiler 166 | -------------------------------------------------------------------------------- /Source/SpireCore/GetDependencyVisitor.cpp: -------------------------------------------------------------------------------- 1 | #include "GetDependencyVisitor.h" 2 | 3 | namespace Spire 4 | { 5 | namespace Compiler 6 | { 7 | EnumerableHashSet GetDependentComponents(SyntaxNode * tree) 8 | { 9 | GetDependencyVisitor visitor; 10 | tree->Accept(&visitor); 11 | return visitor.Result; 12 | } 13 | 14 | RefPtr GetDependencyVisitor::VisitImportExpression(ImportExpressionSyntaxNode * syntax) 15 | { 16 | for (auto & comp : syntax->ImportOperatorDef->Usings) 17 | Result.Add(ComponentDependency(comp, nullptr)); 18 | Result.Add(ComponentDependency(syntax->ComponentUniqueName, syntax->ImportOperatorDef.Ptr())); 19 | return SyntaxVisitor::VisitImportExpression(syntax); 20 | } 21 | RefPtr GetDependencyVisitor::VisitMemberExpression(MemberExpressionSyntaxNode * member) 22 | { 23 | RefPtr refCompObj; 24 | if (member->Tags.TryGetValue("ComponentReference", refCompObj)) 25 | { 26 | auto refComp = refCompObj.As().Ptr(); 27 | Result.Add(ComponentDependency(refComp->Content, nullptr)); 28 | } 29 | else 30 | member->BaseExpression->Accept(this); 31 | return member; 32 | } 33 | RefPtr GetDependencyVisitor::VisitVarExpression(VarExpressionSyntaxNode * var) 34 | { 35 | RefPtr refCompObj; 36 | if (var->Tags.TryGetValue("ComponentReference", refCompObj)) 37 | { 38 | auto refComp = refCompObj.As().Ptr(); 39 | Result.Add(ComponentDependency(refComp->Content, nullptr)); 40 | } 41 | return var; 42 | } 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /Source/SpireCore/GetDependencyVisitor.h: -------------------------------------------------------------------------------- 1 | #ifndef GET_DEPENDENCY_VISITOR_H 2 | #define GET_DEPENDENCY_VISITOR_H 3 | 4 | #include "VariantIR.h" 5 | #include "Closure.h" 6 | #include "StringObject.h" 7 | 8 | namespace Spire 9 | { 10 | namespace Compiler 11 | { 12 | class ComponentDependency 13 | { 14 | public: 15 | String ReferencedComponent; 16 | ImportOperatorDefSyntaxNode * ImportOperator = nullptr; 17 | ComponentDependency() = default; 18 | ComponentDependency(String compName, ImportOperatorDefSyntaxNode * impOp) 19 | : ReferencedComponent(compName), ImportOperator(impOp) 20 | {} 21 | int GetHashCode() 22 | { 23 | return ReferencedComponent.GetHashCode() ^ (int)(CoreLib::PtrInt)(void*)(ImportOperator); 24 | } 25 | bool operator == (const ComponentDependency & other) 26 | { 27 | return ReferencedComponent == other.ReferencedComponent && ImportOperator == other.ImportOperator; 28 | } 29 | }; 30 | 31 | class GetDependencyVisitor : public SyntaxVisitor 32 | { 33 | public: 34 | EnumerableHashSet Result; 35 | GetDependencyVisitor() 36 | : SyntaxVisitor(nullptr) 37 | {} 38 | 39 | RefPtr VisitVarExpression(VarExpressionSyntaxNode * var) override; 40 | 41 | RefPtr VisitMemberExpression(MemberExpressionSyntaxNode * member) override; 42 | 43 | RefPtr VisitImportExpression(ImportExpressionSyntaxNode * syntax) override; 44 | }; 45 | 46 | EnumerableHashSet GetDependentComponents(SyntaxNode * tree); 47 | } 48 | } 49 | #endif -------------------------------------------------------------------------------- /Source/SpireCore/InsertImplicitImportOperator.cpp: -------------------------------------------------------------------------------- 1 | #include "Closure.h" 2 | #include "VariantIR.h" 3 | #include "StringObject.h" 4 | #include "Naming.h" 5 | #include "GetDependencyVisitor.h" 6 | 7 | namespace Spire 8 | { 9 | namespace Compiler 10 | { 11 | class InsertImplicitImportOperatorVisitor : public SyntaxVisitor 12 | { 13 | private: 14 | ShaderIR * shaderIR; 15 | GetDependencyVisitor depVisitor; 16 | public: 17 | ComponentDefinitionIR * currentCompDef = nullptr; 18 | EnumerableDictionary> passThroughComponents; 19 | public: 20 | InsertImplicitImportOperatorVisitor(ShaderIR * ir, DiagnosticSink* err) 21 | : SyntaxVisitor(err), shaderIR(ir) 22 | {} 23 | 24 | ComponentDefinitionIR * MakeComponentAvailableAtWorld(String componentUniqueName, String world) 25 | { 26 | HashSet visitedComponents; 27 | return MakeComponentAvailableAtWorldInternal(visitedComponents, componentUniqueName, world); 28 | } 29 | 30 | ComponentDefinitionIR * MakeComponentAvailableAtWorldInternal(HashSet & visitedComponents, String componentUniqueName, String world) 31 | { 32 | RefPtr refDef; 33 | if (passThroughComponents.TryGetValue(EscapeCodeName(componentUniqueName + "_" + world), refDef)) 34 | return refDef.Ptr(); 35 | if (visitedComponents.Contains(componentUniqueName + "@" + world)) 36 | { 37 | StringBuilder refs; 38 | int count = 0; 39 | for (auto & comp : visitedComponents) 40 | { 41 | refs << comp; 42 | if (count != visitedComponents.Count() - 1) 43 | refs << ", "; 44 | count++; 45 | } 46 | getSink()->diagnose(currentCompDef->SyntaxNode, Diagnostics::cylicReference, refs.ProduceString()); 47 | return nullptr; 48 | } 49 | visitedComponents.Add(componentUniqueName); 50 | ImportPath importPath; 51 | int currentPathLength = 1 << 30; 52 | ComponentDefinitionIR * referencedDef = nullptr; 53 | for (auto & compDef : shaderIR->DefinitionsByComponent[componentUniqueName]()) 54 | { 55 | if (compDef.Value->World == world || compDef.Value->World == "") 56 | return compDef.Value; 57 | } 58 | for (auto & compDef : shaderIR->DefinitionsByComponent[componentUniqueName]()) 59 | { 60 | auto path = shaderIR->SymbolTable->FindImplicitImportOperatorChain(shaderIR->Shader->Pipeline, compDef.Value->World, world, compDef.Value->Type); 61 | if (path.Count() && path.First().Nodes.Count() < currentPathLength) 62 | { 63 | importPath = path.First(); 64 | currentPathLength = importPath.Nodes.Count(); 65 | referencedDef = compDef.Value; 66 | } 67 | } 68 | if (referencedDef) 69 | { 70 | auto & node = importPath.Nodes.Last(); 71 | RefPtr thruDef; 72 | auto thruDefName = EscapeCodeName(componentUniqueName + "_" + node.TargetWorld); 73 | if (!passThroughComponents.TryGetValue(thruDefName, thruDef)) 74 | { 75 | auto srcDef = MakeComponentAvailableAtWorldInternal(visitedComponents, componentUniqueName, node.ImportOperator->SourceWorld.Content); 76 | thruDef = new ComponentDefinitionIR(); 77 | thruDef->World = world; 78 | thruDef->Dependency.Add(srcDef); 79 | srcDef->Users.Add(thruDef.Ptr()); 80 | thruDef->OriginalName = referencedDef->OriginalName; 81 | thruDef->UniqueName = thruDefName; 82 | thruDef->UniqueKey = referencedDef->UniqueKey + "@" + node.TargetWorld; 83 | thruDef->IsEntryPoint = false; 84 | thruDef->SyntaxNode = new ComponentSyntaxNode(); 85 | thruDef->SyntaxNode->Type = thruDef->Type = srcDef->SyntaxNode->Type; 86 | thruDef->SyntaxNode->Rate = new RateSyntaxNode(); 87 | thruDef->SyntaxNode->Rate->Worlds.Add(RateWorld(node.TargetWorld)); 88 | thruDef->SyntaxNode->Name.Content = thruDefName; 89 | CloneContext cloneCtx; 90 | thruDef->SyntaxNode->TypeNode = srcDef->SyntaxNode->TypeNode->Clone(cloneCtx); 91 | auto importExpr = new ImportExpressionSyntaxNode(); 92 | importExpr->Type = thruDef->Type; 93 | importExpr->ImportOperatorDef = node.ImportOperator->Clone(cloneCtx); 94 | importExpr->ImportOperatorDef->Scope->Parent = thruDef->SyntaxNode->Scope.Ptr(); 95 | importExpr->ComponentUniqueName = srcDef->UniqueName; 96 | for (auto & arg : importExpr->Arguments) 97 | arg->Accept(this); 98 | importExpr->ImportOperatorDef->Body->Accept(this); 99 | thruDef->SyntaxNode->Expression = importExpr; 100 | passThroughComponents[thruDefName] = thruDef; 101 | } 102 | visitedComponents.Remove(componentUniqueName + "@" + world); 103 | return thruDef.Ptr(); 104 | } 105 | else 106 | { 107 | auto targetComp = shaderIR->Shader->AllComponents[componentUniqueName](); 108 | getSink()->diagnose(currentCompDef->SyntaxNode, Diagnostics::noApplicableImplicitImportOperator, targetComp.Symbol->Name, world, currentCompDef->OriginalName); 109 | getSink()->diagnose(targetComp.Symbol->Implementations.First()->SyntaxNode, Diagnostics::seeDefinitionOf, targetComp.Symbol->Name); 110 | return currentCompDef; 111 | } 112 | } 113 | 114 | RefPtr ProcessComponentReference(String componentUniqueName) 115 | { 116 | auto refDef = MakeComponentAvailableAtWorld(componentUniqueName, currentCompDef->World); 117 | auto refNode = new VarExpressionSyntaxNode(); 118 | if (refDef) 119 | { 120 | refNode->Variable = refDef->UniqueName; 121 | refNode->Type = refDef->Type; 122 | refNode->Tags["ComponentReference"] = new StringObject(refDef->UniqueName); 123 | currentCompDef->Dependency.Add(refDef); 124 | refDef->Users.Add(currentCompDef); 125 | } 126 | return refNode; 127 | } 128 | RefPtr VisitVarExpression(VarExpressionSyntaxNode * var) override 129 | { 130 | RefPtr refCompObj; 131 | if (var->Tags.TryGetValue("ComponentReference", refCompObj)) 132 | { 133 | auto refComp = refCompObj.As().Ptr(); 134 | return ProcessComponentReference(refComp->Content); 135 | } 136 | return var; 137 | } 138 | 139 | RefPtr VisitMemberExpression(MemberExpressionSyntaxNode * member) override 140 | { 141 | RefPtr refCompObj; 142 | if (member->Tags.TryGetValue("ComponentReference", refCompObj)) 143 | { 144 | auto refComp = refCompObj.As().Ptr(); 145 | return ProcessComponentReference(refComp->Content); 146 | } 147 | else 148 | member->BaseExpression = member->BaseExpression->Accept(this).As(); 149 | return member; 150 | } 151 | RefPtr VisitImportExpression(ImportExpressionSyntaxNode * import) override 152 | { 153 | auto refDef = MakeComponentAvailableAtWorld(import->ComponentUniqueName, import->ImportOperatorDef->SourceWorld.Content); 154 | if (refDef) 155 | import->ComponentUniqueName = refDef->UniqueName; 156 | depVisitor.Result.Clear(); 157 | import->ImportOperatorDef->Accept(&depVisitor); 158 | for (auto & x : depVisitor.Result) 159 | { 160 | ProcessComponentReference(x.ReferencedComponent); 161 | } 162 | return import; 163 | } 164 | }; 165 | void InsertImplicitImportOperators(DiagnosticSink * err, ShaderIR * shader) 166 | { 167 | InsertImplicitImportOperatorVisitor visitor(shader, err); 168 | for (auto & comp : shader->Definitions) 169 | { 170 | for (auto & dep : comp->Dependency) 171 | dep->Users.Remove(comp.Ptr()); 172 | comp->ClearDependency(); 173 | } 174 | for (auto & comp : shader->Definitions) 175 | { 176 | visitor.currentCompDef = comp.Ptr(); 177 | comp->SyntaxNode->Accept(&visitor); 178 | } 179 | for (auto & comp : visitor.passThroughComponents) 180 | { 181 | shader->Definitions.Add(comp.Value); 182 | EnumerableDictionary defs; 183 | defs[comp.Value->World] = comp.Value.Ptr(); 184 | shader->DefinitionsByComponent[comp.Key] = defs; 185 | } 186 | } 187 | } 188 | } -------------------------------------------------------------------------------- /Source/SpireCore/KeyHoleMatching.cpp: -------------------------------------------------------------------------------- 1 | #include "IL.h" 2 | #include "../CoreLib/Tokenizer.h" 3 | 4 | namespace Spire 5 | { 6 | namespace Compiler 7 | { 8 | RefPtr ParseInternal(CoreLib::Text::TokenReader & parser) 9 | { 10 | RefPtr result = new KeyHoleNode(); 11 | result->NodeType = parser.ReadWord(); 12 | if (parser.LookAhead("<")) 13 | { 14 | parser.ReadToken(); 15 | result->CaptureId = parser.ReadInt(); 16 | parser.ReadToken(); 17 | } 18 | if (parser.LookAhead("(")) 19 | { 20 | while (!parser.LookAhead(")")) 21 | { 22 | result->Children.Add(ParseInternal(parser)); 23 | if (parser.LookAhead(",")) 24 | parser.ReadToken(); 25 | else 26 | { 27 | break; 28 | } 29 | } 30 | parser.Read(")"); 31 | } 32 | return result; 33 | } 34 | 35 | RefPtr KeyHoleNode::Parse(String format) 36 | { 37 | CoreLib::Text::TokenReader parser(format); 38 | return ParseInternal(parser); 39 | } 40 | 41 | bool KeyHoleNode::Match(List & matchResult, ILOperand * instr) 42 | { 43 | bool matches = false; 44 | if (NodeType == "store") 45 | matches = dynamic_cast(instr) != nullptr; 46 | else if (NodeType == "op") 47 | matches = true; 48 | else if (NodeType == "load") 49 | matches = dynamic_cast(instr) != nullptr; 50 | else if (NodeType == "add") 51 | matches = dynamic_cast(instr) != nullptr; 52 | else if (NodeType == "mu") 53 | matches = dynamic_cast(instr) != nullptr; 54 | else if (NodeType == "sub") 55 | matches = dynamic_cast(instr) != nullptr; 56 | else if (NodeType == "cal") 57 | matches = dynamic_cast(instr) != nullptr; 58 | else if (NodeType == "switch") 59 | matches = dynamic_cast(instr) != nullptr; 60 | if (matches) 61 | { 62 | if (Children.Count() > 0) 63 | { 64 | ILInstruction * cinstr = dynamic_cast(instr); 65 | if (cinstr != nullptr) 66 | { 67 | int opId = 0; 68 | for (auto & op : *cinstr) 69 | { 70 | if (opId >= Children.Count()) 71 | { 72 | matches = false; 73 | break; 74 | } 75 | matches = matches && Children[opId]->Match(matchResult, &op); 76 | opId++; 77 | } 78 | if (opId != Children.Count()) 79 | matches = false; 80 | } 81 | else 82 | matches = false; 83 | } 84 | } 85 | if (matches && CaptureId != -1) 86 | { 87 | matchResult.SetSize(Math::Max(matchResult.Count(), CaptureId + 1)); 88 | matchResult[CaptureId] = instr; 89 | } 90 | return matches; 91 | } 92 | } 93 | } -------------------------------------------------------------------------------- /Source/SpireCore/Lexer.cpp: -------------------------------------------------------------------------------- 1 | #include "Lexer.h" 2 | #include "../CoreLib/Tokenizer.h" 3 | 4 | #include 5 | 6 | using namespace CoreLib::Text; 7 | 8 | namespace Spire 9 | { 10 | namespace Compiler 11 | { 12 | static Token GetEndOfFileToken() 13 | { 14 | return Token(TokenType::EndOfFile, "", 0, 0, 0, ""); 15 | } 16 | 17 | Token* TokenList::begin() const 18 | { 19 | assert(mTokens.Count()); 20 | return &mTokens[0]; 21 | } 22 | 23 | Token* TokenList::end() const 24 | { 25 | assert(mTokens.Count()); 26 | assert(mTokens[mTokens.Count()-1].Type == TokenType::EndOfFile); 27 | return &mTokens[mTokens.Count() - 1]; 28 | } 29 | 30 | TokenSpan::TokenSpan() 31 | : mBegin(NULL) 32 | , mEnd (NULL) 33 | {} 34 | 35 | TokenReader::TokenReader() 36 | : mCursor(NULL) 37 | , mEnd (NULL) 38 | {} 39 | 40 | 41 | Token TokenReader::PeekToken() const 42 | { 43 | if (!mCursor) 44 | return GetEndOfFileToken(); 45 | 46 | Token token = *mCursor; 47 | if (mCursor == mEnd) 48 | token.Type = TokenType::EndOfFile; 49 | return token; 50 | } 51 | 52 | CoreLib::Text::TokenType TokenReader::PeekTokenType() const 53 | { 54 | if (mCursor == mEnd) 55 | return TokenType::EndOfFile; 56 | assert(mCursor); 57 | return mCursor->Type; 58 | } 59 | 60 | CodePosition TokenReader::PeekLoc() const 61 | { 62 | if (!mCursor) 63 | return CodePosition(); 64 | assert(mCursor); 65 | return mCursor->Position; 66 | } 67 | 68 | Token TokenReader::AdvanceToken() 69 | { 70 | if (!mCursor) 71 | return GetEndOfFileToken(); 72 | 73 | Token token = *mCursor; 74 | if (mCursor == mEnd) 75 | token.Type = TokenType::EndOfFile; 76 | else 77 | mCursor++; 78 | return token; 79 | } 80 | 81 | 82 | TokenList Lexer::Parse(const String & fileName, const String & str, DiagnosticSink * sink) 83 | { 84 | TokenList tokenList; 85 | tokenList.mTokens = CoreLib::Text::TokenizeText(fileName, str, [&](CoreLib::Text::TokenizeErrorType errType, CoreLib::Text::CodePosition pos) 86 | { 87 | auto curChar = str[pos.Pos]; 88 | switch (errType) 89 | { 90 | case CoreLib::Text::TokenizeErrorType::InvalidCharacter: 91 | sink->diagnose(pos, Diagnostics::illegalCharacter, String((unsigned char)curChar, 16)); 92 | break; 93 | case CoreLib::Text::TokenizeErrorType::InvalidEscapeSequence: 94 | sink->diagnose(pos, Diagnostics::illegalCharacterLiteral); 95 | break; 96 | default: 97 | break; 98 | } 99 | }); 100 | 101 | // Add an end-of-file token so that we can reference it in diagnostic messages 102 | tokenList.mTokens.Add(Token(TokenType::EndOfFile, "", 0, 0, 0, fileName, TokenFlag::AtStartOfLine | TokenFlag::AfterWhitespace)); 103 | 104 | return tokenList; 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /Source/SpireCore/Lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef RASTER_RENDERER_LEXER_H 2 | #define RASTER_RENDERER_LEXER_H 3 | 4 | #include "../CoreLib/Basic.h" 5 | #include "Diagnostics.h" 6 | 7 | namespace Spire 8 | { 9 | namespace Compiler 10 | { 11 | using namespace CoreLib::Basic; 12 | 13 | struct TokenList 14 | { 15 | Token* begin() const; 16 | Token* end() const; 17 | 18 | List mTokens; 19 | }; 20 | 21 | struct TokenSpan 22 | { 23 | TokenSpan(); 24 | TokenSpan( 25 | TokenList const& tokenList) 26 | : mBegin(tokenList.begin()) 27 | , mEnd (tokenList.end ()) 28 | {} 29 | 30 | Token* begin() const { return mBegin; } 31 | Token* end () const { return mEnd ; } 32 | 33 | int GetCount() { return (int)(mEnd - mBegin); } 34 | 35 | Token* mBegin; 36 | Token* mEnd; 37 | }; 38 | 39 | struct TokenReader 40 | { 41 | TokenReader(); 42 | explicit TokenReader(TokenSpan const& tokens) 43 | : mCursor(tokens.begin()) 44 | , mEnd (tokens.end ()) 45 | {} 46 | explicit TokenReader(TokenList const& tokens) 47 | : mCursor(tokens.begin()) 48 | , mEnd (tokens.end ()) 49 | {} 50 | 51 | bool IsAtEnd() const { return mCursor == mEnd; } 52 | Token PeekToken() const; 53 | CoreLib::Text::TokenType PeekTokenType() const; 54 | CodePosition PeekLoc() const; 55 | 56 | Token AdvanceToken(); 57 | 58 | int GetCount() { return (int)(mEnd - mCursor); } 59 | 60 | Token* mCursor; 61 | Token* mEnd; 62 | }; 63 | 64 | 65 | class Lexer 66 | { 67 | public: 68 | TokenList Parse(const String & fileName, const String & str, DiagnosticSink * sink); 69 | }; 70 | } 71 | } 72 | 73 | #endif -------------------------------------------------------------------------------- /Source/SpireCore/Naming.cpp: -------------------------------------------------------------------------------- 1 | #include "Naming.h" 2 | 3 | namespace Spire 4 | { 5 | namespace Compiler 6 | { 7 | using namespace CoreLib; 8 | 9 | CoreLib::String EscapeCodeName(CoreLib::String str) 10 | { 11 | StringBuilder sb; 12 | bool isUnderScore = false; 13 | for (auto ch : str) 14 | { 15 | if (ch == '_' || !((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) 16 | { 17 | if (isUnderScore) 18 | sb << "I_"; 19 | else 20 | sb << "_"; 21 | isUnderScore = true; 22 | } 23 | else 24 | { 25 | isUnderScore = false; 26 | sb << ch; 27 | } 28 | } 29 | if (isUnderScore) 30 | sb << "I"; 31 | return sb.ProduceString(); 32 | } 33 | 34 | } 35 | } -------------------------------------------------------------------------------- /Source/SpireCore/Naming.h: -------------------------------------------------------------------------------- 1 | #ifndef SPIRE_NAMING_H 2 | #define SPIRE_NAMING_H 3 | 4 | #include "../CoreLib/Basic.h" 5 | 6 | namespace Spire 7 | { 8 | namespace Compiler 9 | { 10 | CoreLib::String EscapeCodeName(CoreLib::String str); 11 | 12 | } 13 | } 14 | 15 | #endif -------------------------------------------------------------------------------- /Source/SpireCore/NatvisFile.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{CFG Basic Block}} 5 | 6 | 7 | kvPairs.FCount 8 | kvPairs.FHead 9 | pNext 10 | Value 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Source/SpireCore/NewSpirVCodeGen.cpp: -------------------------------------------------------------------------------- 1 | #include "CodeGenBackend.h" 2 | #include "../CoreLib/Tokenizer.h" 3 | #include "IL.h" 4 | #include "Syntax.h" 5 | #include 6 | #include 7 | #include "../CoreLib/TextIO.h" 8 | #include "../CoreLib/LibIO.h" 9 | 10 | using namespace CoreLib::Basic; 11 | 12 | namespace Spire 13 | { 14 | namespace Compiler 15 | { 16 | void PrintILShader(ILShader * shader) 17 | { 18 | printf("%S\n", shader->Name.ToWString()); 19 | printf("%S\n", shader->Position.ToString().ToWString()); 20 | printf("\n---\n\n"); 21 | 22 | for (auto& stage : shader->Stages) 23 | { 24 | printf("Stage: %S\n", stage.Key.ToWString()); 25 | auto& stageIL = stage.Value; 26 | 27 | int maxAttrNameLength = 0; 28 | int maxAttrValueLength = 0; 29 | for (auto& attr : stageIL->Attributes) 30 | { 31 | if (attr.Value.Name.Length() > maxAttrNameLength) 32 | maxAttrNameLength = attr.Value.Name.Length(); 33 | 34 | if (attr.Value.Value.Length() > maxAttrValueLength) 35 | maxAttrValueLength = attr.Value.Value.Length(); 36 | } 37 | 38 | for (auto& attr : stageIL->Attributes) 39 | { 40 | printf("\t%-*S = %-*S (%S)\n", 41 | maxAttrNameLength, attr.Value.Name.ToWString(), 42 | maxAttrValueLength, attr.Value.Value.ToWString(), 43 | attr.Value.Position.ToString().ToWString()); 44 | } 45 | 46 | printf("\n"); 47 | } 48 | 49 | printf("\n---\n\n"); 50 | 51 | for (auto& world : shader->Worlds) 52 | { 53 | printf("World: %S\n", world.Key.ToWString()); 54 | //auto& worldIL = world.Value; 55 | } 56 | } 57 | 58 | class SpirVCodeGen : public CodeGenBackend 59 | { 60 | public: 61 | virtual CompiledShaderSource GenerateShader(CompileResult & /*result*/, SymbolTable * /*symbols*/, ILShader * shader, DiagnosticSink * /*err*/) override 62 | { 63 | PrintILShader(shader); 64 | system("pause"); 65 | return CompiledShaderSource(); 66 | } 67 | 68 | LayoutRule GetDefaultLayoutRule() override 69 | { 70 | return LayoutRule::Std140; 71 | } 72 | 73 | }; 74 | 75 | CodeGenBackend * CreateSpirVCodeGen() 76 | { 77 | return new SpirVCodeGen(); 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /Source/SpireCore/Parser.h: -------------------------------------------------------------------------------- 1 | #ifndef RASTER_RENDERER_PARSER_H 2 | #define RASTER_RENDERER_PARSER_H 3 | 4 | #include "Lexer.h" 5 | #include "Syntax.h" 6 | 7 | namespace Spire 8 | { 9 | namespace Compiler 10 | { 11 | RefPtr ParseProgram( 12 | TokenSpan const& tokens, 13 | DiagnosticSink* sink, 14 | String const& fileName); 15 | } 16 | } 17 | 18 | #endif -------------------------------------------------------------------------------- /Source/SpireCore/Preprocessor.h: -------------------------------------------------------------------------------- 1 | // Preprocessor.h 2 | #ifndef SPIRE_PREPROCESSOR_H_INCLUDED 3 | #define SPIRE_PREPROCESSOR_H_INCLUDED 4 | 5 | #include "../CoreLib/Basic.h" 6 | #include "../CoreLib/Tokenizer.h" 7 | #include "../SpireCore/Lexer.h" 8 | 9 | namespace Spire{ namespace Compiler { 10 | 11 | class DiagnosticSink; 12 | 13 | // Callback interface for the preprocessor to use when looking 14 | // for files in `#include` directives. 15 | struct IncludeHandler 16 | { 17 | virtual bool TryToFindIncludeFile( 18 | CoreLib::String const& pathToInclude, 19 | CoreLib::String const& pathIncludedFrom, 20 | CoreLib::String* outFoundPath, 21 | CoreLib::String* outFoundSource) = 0; 22 | }; 23 | 24 | // Take a string of source code and preprocess it into a list of tokens. 25 | TokenList PreprocessSource( 26 | CoreLib::String const& source, 27 | CoreLib::String const& fileName, 28 | DiagnosticSink* sink, 29 | IncludeHandler* includeHandler); 30 | 31 | TokenList PreprocessSource( 32 | CoreLib::String const& source, 33 | CoreLib::String const& fileName, 34 | DiagnosticSink* sink, 35 | IncludeHandler* includeHandler, 36 | CoreLib::Dictionary defines); 37 | 38 | }} 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /Source/SpireCore/SamplerUsageAnalysis.cpp: -------------------------------------------------------------------------------- 1 | #include "SamplerUsageAnalysis.h" 2 | 3 | namespace Spire 4 | { 5 | namespace Compiler 6 | { 7 | using namespace CoreLib; 8 | 9 | void AnalyzeSamplerUsageImpl(EnumerableDictionary> & samplerTextures, 10 | ILProgram * program, 11 | CFGNode * code, 12 | DiagnosticSink * sink, 13 | HashSet & processedFunctions, 14 | Dictionary & paramValues) 15 | { 16 | for (auto & instr : code->GetAllInstructions()) 17 | { 18 | auto call = dynamic_cast(&instr); 19 | if (call) 20 | { 21 | if (call->Function.StartsWith("Sample") && !program->Functions.ContainsKey(call->Function)) 22 | { 23 | auto textureOp = call->Arguments[0].Ptr(); 24 | auto samplerOp = call->Arguments[1].Ptr(); 25 | ILModuleParameterInstance * textureInstance = nullptr; 26 | ILModuleParameterInstance * samplerInstance = nullptr; 27 | if (auto texArg = dynamic_cast(textureOp)) 28 | paramValues.TryGetValue(texArg->ArgId, textureInstance); 29 | else if (auto texArg1 = dynamic_cast(textureOp)) 30 | textureInstance = texArg1; 31 | if (auto samplerArg = dynamic_cast(samplerOp)) 32 | paramValues.TryGetValue(samplerArg->ArgId, samplerInstance); 33 | else if (auto samplerArg1 = dynamic_cast(samplerOp)) 34 | samplerInstance = samplerArg1; 35 | if (textureInstance && samplerInstance) 36 | { 37 | auto list = samplerTextures.TryGetValue(samplerInstance); 38 | if (!list) 39 | { 40 | samplerTextures.Add(samplerInstance, List()); 41 | list = samplerTextures.TryGetValue(samplerInstance); 42 | } 43 | list->Add(textureInstance); 44 | } 45 | else 46 | { 47 | SPIRE_INTERNAL_ERROR(sink, instr.Position); 48 | } 49 | } 50 | else 51 | { 52 | RefPtr func = nullptr; 53 | if (program->Functions.TryGetValue(call->Function, func)) 54 | { 55 | if (!processedFunctions.Contains(func.Ptr())) 56 | { 57 | // trace into the function call 58 | Dictionary args; 59 | for (int i = 0; i < call->Arguments.Count(); i++) 60 | { 61 | if (auto arg = dynamic_cast(call->Arguments[i].Ptr())) 62 | { 63 | ILModuleParameterInstance * argValue = nullptr; 64 | if (paramValues.TryGetValue(arg->ArgId, argValue)) 65 | args[i + 1] = argValue; 66 | } 67 | else if (auto argValue = dynamic_cast(call->Arguments[i].Ptr())) 68 | { 69 | args[i + 1] = argValue; 70 | } 71 | } 72 | if (func->Code) 73 | { 74 | processedFunctions.Add(func.Ptr()); 75 | AnalyzeSamplerUsageImpl(samplerTextures, program, func->Code.Ptr(), sink, 76 | processedFunctions, args); 77 | processedFunctions.Remove(func.Ptr()); 78 | } 79 | } 80 | } 81 | } 82 | } 83 | } 84 | } 85 | 86 | void AnalyzeSamplerUsage(EnumerableDictionary> & samplerTextures, 87 | ILProgram * program, CFGNode * code, DiagnosticSink * sink) 88 | { 89 | Dictionary params; 90 | HashSet processedFuncs; 91 | AnalyzeSamplerUsageImpl(samplerTextures, program, code, sink, processedFuncs, params); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /Source/SpireCore/SamplerUsageAnalysis.h: -------------------------------------------------------------------------------- 1 | #include "IL.h" 2 | #include "CompiledProgram.h" 3 | 4 | namespace Spire 5 | { 6 | namespace Compiler 7 | { 8 | void AnalyzeSamplerUsage(CoreLib::EnumerableDictionary> & samplerTextures, 9 | ILProgram * program, CFGNode * code, DiagnosticSink * sink); 10 | } 11 | } -------------------------------------------------------------------------------- /Source/SpireCore/Schedule.cpp: -------------------------------------------------------------------------------- 1 | #include "Schedule.h" 2 | #include "Lexer.h" 3 | using namespace CoreLib::Basic; 4 | 5 | namespace Spire 6 | { 7 | namespace Compiler 8 | { 9 | class ScheduleParser 10 | { 11 | private: 12 | DiagnosticSink * sink; 13 | TokenList tokens; 14 | TokenReader reader; 15 | String fileName; 16 | Token ReadToken(const char * string) 17 | { 18 | if (reader.PeekTokenType() == TokenType::EndOfFile) 19 | { 20 | sink->diagnose(reader.PeekLoc(), Diagnostics::tokenNameExpectedButEOF, string); 21 | throw 0; 22 | } 23 | else if (reader.PeekToken().Content != string) 24 | { 25 | sink->diagnose(reader.PeekLoc(), Diagnostics::tokenNameExpected, string); 26 | throw 20001; 27 | } 28 | return reader.AdvanceToken(); 29 | } 30 | 31 | Token ReadToken(CoreLib::Text::TokenType type) 32 | { 33 | if (reader.PeekTokenType() == TokenType::EndOfFile) 34 | { 35 | sink->diagnose(reader.PeekLoc(), Diagnostics::tokenTypeExpectedButEOF, type); 36 | throw 0; 37 | } 38 | else if (reader.PeekTokenType() != type) 39 | { 40 | sink->diagnose(reader.PeekLoc(), Diagnostics::tokenTypeExpected, type); 41 | throw 20001; 42 | } 43 | return reader.AdvanceToken(); 44 | } 45 | 46 | bool LookAheadToken(const char * string) 47 | { 48 | if (reader.PeekTokenType() == TokenType::EndOfFile) 49 | { 50 | // TODO(tfoley): this error condition seems wrong 51 | // it shouldn't be an error to see EOF as out *lookahead* 52 | sink->diagnose(reader.PeekLoc(), Diagnostics::tokenNameExpectedButEOF); 53 | return false; 54 | } 55 | else 56 | { 57 | if (reader.PeekToken().Content == string) 58 | return true; 59 | else 60 | return false; 61 | } 62 | } 63 | public: 64 | ScheduleParser(DiagnosticSink * sink) 65 | : sink(sink) 66 | {} 67 | Schedule Parse(String source, String _fileName) 68 | { 69 | this->fileName = _fileName; 70 | Schedule schedule; 71 | Lexer lex; 72 | tokens = lex.Parse(fileName, source, sink); 73 | try 74 | { 75 | while (reader.PeekTokenType() != TokenType::EndOfFile) 76 | { 77 | if (LookAheadToken("attrib")) 78 | { 79 | EnumerableDictionary additionalAttributes; 80 | ReadToken("attrib"); 81 | String choiceName = ReadToken(TokenType::Identifier).Content; 82 | while (LookAheadToken(".")) 83 | { 84 | choiceName = choiceName + "."; 85 | ReadToken(TokenType::Dot); 86 | choiceName = choiceName + ReadToken(TokenType::Identifier).Content; 87 | } 88 | ReadToken(TokenType::OpAssign); 89 | 90 | while (reader.PeekTokenType() != TokenType::EndOfFile) 91 | { 92 | auto name = ReadToken(TokenType::Identifier).Content; 93 | String value; 94 | if (LookAheadToken(":")) 95 | { 96 | ReadToken(":"); 97 | value = ReadToken(TokenType::StringLiterial).Content; 98 | } 99 | additionalAttributes[name] = value; 100 | if (LookAheadToken(",")) 101 | ReadToken(TokenType::Comma); 102 | else 103 | break; 104 | } 105 | schedule.AddtionalAttributes[choiceName] = additionalAttributes; 106 | } 107 | else 108 | { 109 | String choiceName = ReadToken(TokenType::Identifier).Content; 110 | while (LookAheadToken(".")) 111 | { 112 | choiceName = choiceName + "."; 113 | ReadToken(TokenType::Dot); 114 | choiceName = choiceName + ReadToken(TokenType::Identifier).Content; 115 | } 116 | ReadToken(TokenType::OpAssign); 117 | List> worlds; 118 | while (reader.PeekTokenType() != TokenType::EndOfFile) 119 | { 120 | auto token = ReadToken(TokenType::StringLiterial); 121 | RefPtr choiceValue = new ChoiceValueSyntaxNode(); 122 | choiceValue->Position = token.Position; 123 | choiceValue->WorldName = token.Content; 124 | worlds.Add(choiceValue); 125 | if (LookAheadToken(",")) 126 | ReadToken(TokenType::Comma); 127 | else 128 | break; 129 | } 130 | schedule.Choices[choiceName] = worlds; 131 | } 132 | ReadToken(TokenType::Semicolon); 133 | } 134 | } 135 | catch (...) 136 | { 137 | } 138 | return schedule; 139 | } 140 | }; 141 | 142 | Schedule Schedule::Parse(String source, String fileName, DiagnosticSink * sink) 143 | { 144 | return ScheduleParser(sink).Parse(source, fileName); 145 | } 146 | } 147 | } -------------------------------------------------------------------------------- /Source/SpireCore/Schedule.h: -------------------------------------------------------------------------------- 1 | #ifndef BAKER_SL_SCHEDULE_H 2 | #define BAKER_SL_SCHEDULE_H 3 | 4 | #include "../CoreLib/Basic.h" 5 | #include "Diagnostics.h" 6 | #include "Syntax.h" 7 | 8 | namespace Spire 9 | { 10 | namespace Compiler 11 | { 12 | class Schedule 13 | { 14 | public: 15 | CoreLib::EnumerableDictionary>> Choices; 16 | CoreLib::EnumerableDictionary> AddtionalAttributes; 17 | static Schedule Parse(CoreLib::String source, CoreLib::String fileName, DiagnosticSink * sink); 18 | }; 19 | } 20 | } 21 | 22 | #endif -------------------------------------------------------------------------------- /Source/SpireCore/ScopeDictionary.h: -------------------------------------------------------------------------------- 1 | #ifndef RASTER_RENDERER_SCOPE_DICTIONARY_H 2 | #define RASTER_RENDERER_SCOPE_DICTIONARY_H 3 | 4 | #include "../CoreLib/Basic.h" 5 | 6 | using namespace CoreLib::Basic; 7 | 8 | namespace Spire 9 | { 10 | namespace Compiler 11 | { 12 | template 13 | class ScopeDictionary 14 | { 15 | public: 16 | LinkedList> dicts; 17 | public: 18 | void PushScope() 19 | { 20 | dicts.AddLast(); 21 | } 22 | void PopScope() 23 | { 24 | dicts.Delete(dicts.LastNode()); 25 | } 26 | bool TryGetValue(const TKey & key, TValue & value) 27 | { 28 | for (auto iter = dicts.LastNode(); iter; iter = iter->GetPrevious()) 29 | { 30 | bool rs = iter->Value.TryGetValue(key, value); 31 | if (rs) 32 | return true; 33 | } 34 | return false; 35 | } 36 | bool TryGetValueInCurrentScope(const TKey & key, TValue & value) 37 | { 38 | return dicts.Last().TryGetValue(key, value); 39 | } 40 | void Add(const TKey & key, const TValue & value) 41 | { 42 | dicts.Last().Add(key, value); 43 | } 44 | void Set(const TKey & key, const TValue & value) 45 | { 46 | dicts.Last()[key] = value; 47 | } 48 | }; 49 | } 50 | } 51 | 52 | #endif -------------------------------------------------------------------------------- /Source/SpireCore/ShaderCompiler.h: -------------------------------------------------------------------------------- 1 | #ifndef RASTER_SHADER_COMPILER_H 2 | #define RASTER_SHADER_COMPILER_H 3 | 4 | #include "../CoreLib/Basic.h" 5 | #include "Diagnostics.h" 6 | #include "CompiledProgram.h" 7 | #include "Syntax.h" 8 | #include "CodeGenBackend.h" 9 | 10 | namespace Spire 11 | { 12 | namespace Compiler 13 | { 14 | class ILConstOperand; 15 | struct IncludeHandler; 16 | 17 | enum class CompilerMode 18 | { 19 | ProduceLibrary, 20 | ProduceShader, 21 | GenerateChoice 22 | }; 23 | 24 | enum class CodeGenTarget 25 | { 26 | GLSL, GLSL_Vulkan, GLSL_Vulkan_OneDesc, HLSL, SPIRV 27 | }; 28 | 29 | class CompileOptions 30 | { 31 | public: 32 | CompilerMode Mode = CompilerMode::ProduceShader; 33 | CodeGenTarget Target = CodeGenTarget::GLSL; 34 | EnumerableDictionary BackendArguments; 35 | String ScheduleSource, ScheduleFileName; 36 | String SymbolToCompile; 37 | List TemplateShaderArguments; 38 | List SearchDirectories; 39 | Dictionary PreprocessorDefinitions; 40 | }; 41 | 42 | class CompileUnit 43 | { 44 | public: 45 | RefPtr SyntaxNode; 46 | }; 47 | 48 | class CompilationContext : public CoreLib::Basic::RefObject 49 | { 50 | public: 51 | SymbolTable Symbols; 52 | EnumerableDictionary> ShaderClosures; 53 | RefPtr Program; 54 | void MergeWith(CompilationContext * ctx); 55 | }; 56 | 57 | class ShaderCompiler : public CoreLib::Basic::Object 58 | { 59 | public: 60 | virtual CompileUnit Parse(CompileResult & result, String source, String fileName, IncludeHandler* includeHandler, Dictionary const& preprocessorDefinitions) = 0; 61 | virtual void Compile(CompileResult & result, CompilationContext & context, List & units, const CompileOptions & options) = 0; 62 | void Compile(CompileResult & result, List & units, const CompileOptions & options) 63 | { 64 | CompilationContext context; 65 | Compile(result, context, units, options); 66 | } 67 | }; 68 | 69 | ShaderCompiler * CreateShaderCompiler(); 70 | } 71 | } 72 | 73 | #endif -------------------------------------------------------------------------------- /Source/SpireCore/SpireCore.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {d1e94d15-4d7f-41dd-937e-ae0f31fe5cd8} 6 | 7 | 8 | {bf6a2182-b6c8-4870-ac96-7cd4822903bf} 9 | 10 | 11 | 12 | 13 | Front End 14 | 15 | 16 | Front End 17 | 18 | 19 | Front End 20 | 21 | 22 | Front End 23 | 24 | 25 | Front End 26 | 27 | 28 | Back End 29 | 30 | 31 | Back End 32 | 33 | 34 | Front End 35 | 36 | 37 | Front End 38 | 39 | 40 | Front End 41 | 42 | 43 | Front End 44 | 45 | 46 | Back End 47 | 48 | 49 | Back End 50 | 51 | 52 | Front End 53 | 54 | 55 | Front End 56 | 57 | 58 | Front End 59 | 60 | 61 | Front End 62 | 63 | 64 | Back End 65 | 66 | 67 | Front End 68 | 69 | 70 | Front End 71 | 72 | 73 | Front End 74 | 75 | 76 | Front End 77 | 78 | 79 | Front End 80 | 81 | 82 | Back End 83 | 84 | 85 | 86 | 87 | Front End 88 | 89 | 90 | Front End 91 | 92 | 93 | Front End 94 | 95 | 96 | Front End 97 | 98 | 99 | Back End 100 | 101 | 102 | Front End 103 | 104 | 105 | Front End 106 | 107 | 108 | Back End 109 | 110 | 111 | Front End 112 | 113 | 114 | Front End 115 | 116 | 117 | Back End 118 | 119 | 120 | Back End 121 | 122 | 123 | Back End 124 | 125 | 126 | Front End 127 | 128 | 129 | Back End 130 | 131 | 132 | Back End 133 | 134 | 135 | Front End 136 | 137 | 138 | Front End 139 | 140 | 141 | Front End 142 | 143 | 144 | Front End 145 | 146 | 147 | Back End 148 | 149 | 150 | Back End 151 | 152 | 153 | Front End 154 | 155 | 156 | Front End 157 | 158 | 159 | Front End 160 | 161 | 162 | Front End 163 | 164 | 165 | Back End 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /Source/SpireCore/StdInclude.h: -------------------------------------------------------------------------------- 1 | #ifndef SHADER_COMPILER_STD_LIB_H 2 | #define SHADER_COMPILER_STD_LIB_H 3 | 4 | #include "../CoreLib/Basic.h" 5 | 6 | namespace Spire 7 | { 8 | namespace Compiler 9 | { 10 | class SpireStdLib 11 | { 12 | private: 13 | static CoreLib::String code; 14 | public: 15 | static CoreLib::String GetCode(); 16 | static void Finalize(); 17 | }; 18 | } 19 | } 20 | 21 | #endif -------------------------------------------------------------------------------- /Source/SpireCore/StringObject.h: -------------------------------------------------------------------------------- 1 | #ifndef SPIRE_STRING_OBJECT_H 2 | #define SPIRE_STRING_OBJECT_H 3 | 4 | #include "../CoreLib/Basic.h" 5 | 6 | namespace Spire 7 | { 8 | namespace Compiler 9 | { 10 | class StringObject : public CoreLib::Object 11 | { 12 | public: 13 | CoreLib::String Content; 14 | StringObject() {} 15 | StringObject(const CoreLib::String & str) 16 | : Content(str) 17 | {} 18 | }; 19 | } 20 | } 21 | 22 | #endif -------------------------------------------------------------------------------- /Source/SpireCore/SyntaxVisitors.h: -------------------------------------------------------------------------------- 1 | #ifndef RASTER_RENDERER_SYNTAX_PRINTER_H 2 | #define RASTER_RENDERER_SYNTAX_PRINTER_H 3 | 4 | #include "Diagnostics.h" 5 | #include "Syntax.h" 6 | #include "IL.h" 7 | #include "SymbolTable.h" 8 | 9 | namespace Spire 10 | { 11 | namespace Compiler 12 | { 13 | class CodeGenBackend; 14 | class ShaderCompiler; 15 | class ShaderLinkInfo; 16 | class ShaderSymbol; 17 | 18 | class ICodeGenerator : public SyntaxVisitor 19 | { 20 | public: 21 | ICodeGenerator(DiagnosticSink * perr) 22 | : SyntaxVisitor(perr) 23 | {} 24 | virtual void ProcessFunction(FunctionSyntaxNode * func) = 0; 25 | virtual void ProcessShader(ShaderIR * shader) = 0; 26 | virtual void ProcessStruct(StructSyntaxNode * st) = 0; 27 | }; 28 | 29 | SyntaxVisitor * CreateSemanticsVisitor(SymbolTable * symbols, DiagnosticSink * err); 30 | ICodeGenerator * CreateCodeGenerator(SymbolTable * symbols, CompileResult & result, CodeGenBackend* backend); 31 | } 32 | } 33 | 34 | #endif -------------------------------------------------------------------------------- /Source/SpireCore/TypeLayout.h: -------------------------------------------------------------------------------- 1 | #ifndef SPIRE_TYPE_LAYOUT_H 2 | #define SPIRE_TYPE_LAYOUT_H 3 | 4 | #include "../CoreLib/Basic.h" 5 | #include "IL.h" 6 | 7 | namespace Spire { 8 | namespace Compiler { 9 | 10 | // Forward declarations 11 | 12 | enum class BaseType; 13 | class ExpressionType; 14 | 15 | // 16 | 17 | enum class LayoutRule 18 | { 19 | Std140, 20 | Std430, 21 | HLSL, 22 | Packed, 23 | }; 24 | 25 | 26 | struct LayoutInfo 27 | { 28 | size_t size = 0; 29 | size_t alignment = 1; 30 | bool avoid16ByteBoundary = false; 31 | LayoutInfo() = default; 32 | LayoutInfo(size_t s, size_t a) 33 | { 34 | size = s; 35 | alignment = a; 36 | } 37 | }; 38 | 39 | struct LayoutRulesImpl 40 | { 41 | // Get size and alignment for a single value of base type. 42 | virtual LayoutInfo GetScalarLayout(BaseType baseType) = 0; 43 | virtual LayoutInfo GetScalarLayout(ILBaseType baseType) = 0; 44 | 45 | // Get size and alignment for an array of elements 46 | virtual LayoutInfo GetArrayLayout(LayoutInfo elementInfo, size_t elementCount) = 0; 47 | 48 | // Get layout for a vector or matrix type 49 | virtual LayoutInfo GetVectorLayout(LayoutInfo elementInfo, size_t elementCount) = 0; 50 | virtual LayoutInfo GetMatrixLayout(LayoutInfo elementInfo, size_t rowCount, size_t columnCount) = 0; 51 | 52 | // Begin doing layout on a `struct` type 53 | virtual LayoutInfo BeginStructLayout() = 0; 54 | 55 | // Add a field to a `struct` type, and return the offset for the field 56 | virtual size_t AddStructField(LayoutInfo* ioStructInfo, LayoutInfo fieldInfo) = 0; 57 | 58 | // End layout for a struct, and finalize its size/alignment. 59 | virtual void EndStructLayout(LayoutInfo* ioStructInfo) = 0; 60 | }; 61 | 62 | LayoutRulesImpl* GetLayoutRulesImpl(LayoutRule rule); 63 | 64 | LayoutInfo GetLayout(ExpressionType* type, LayoutRulesImpl* rules); 65 | LayoutInfo GetLayout(ILType* type, LayoutRulesImpl* rules); 66 | 67 | LayoutInfo GetLayout(ExpressionType* type, LayoutRule rule = LayoutRule::Std430); 68 | LayoutInfo GetLayout(ILType* type, LayoutRule rule = LayoutRule::Std430); 69 | 70 | inline size_t GetTypeSize(ExpressionType* type, LayoutRule rule = LayoutRule::Std430) 71 | { 72 | return GetLayout(type, rule).size; 73 | } 74 | 75 | inline size_t GetTypeSize(ILType* type, LayoutRule rule = LayoutRule::Std430) 76 | { 77 | return GetLayout(type, rule).size; 78 | } 79 | 80 | inline size_t GetTypeAlignment(ExpressionType* type, LayoutRule rule = LayoutRule::Std430) 81 | { 82 | return GetLayout(type, rule).alignment; 83 | } 84 | 85 | inline size_t GetTypeAlignment(ILType* type, LayoutRule rule = LayoutRule::Std430) 86 | { 87 | return GetLayout(type, rule).alignment; 88 | } 89 | 90 | }} 91 | 92 | #endif -------------------------------------------------------------------------------- /Source/SpireCore/TypeTranslation.cpp: -------------------------------------------------------------------------------- 1 | #include "TypeTranslation.h" 2 | #include "SymbolTable.h" 3 | 4 | namespace Spire 5 | { 6 | namespace Compiler 7 | { 8 | RefPtr TranslateExpressionType(ExpressionType * type, Dictionary> * genericTypeMappings) 9 | { 10 | RefPtr resultType = 0; 11 | if (auto basicType = type->AsBasicType()) 12 | { 13 | if (basicType->BaseType == BaseType::Struct) 14 | { 15 | resultType = basicType->Struct->Type; 16 | } 17 | else if (basicType->BaseType == BaseType::Record) 18 | { 19 | if (genericTypeMappings) 20 | return (*genericTypeMappings)[basicType->RecordTypeName](); 21 | else 22 | throw InvalidProgramException("unexpected record type."); 23 | } 24 | else if (basicType->BaseType == BaseType::Generic) 25 | { 26 | if (genericTypeMappings) 27 | return (*genericTypeMappings)[basicType->GenericTypeVar](); 28 | else 29 | throw InvalidProgramException("unexpected generic type."); 30 | } 31 | else 32 | { 33 | auto base = new ILBasicType(); 34 | base->Type = (ILBaseType)basicType->BaseType; 35 | resultType = base; 36 | } 37 | } 38 | else if (auto arrType = type->AsArrayType()) 39 | { 40 | auto nArrType = new ILArrayType(); 41 | nArrType->BaseType = TranslateExpressionType(arrType->BaseType.Ptr(), genericTypeMappings); 42 | nArrType->ArrayLength = arrType->ArrayLength; 43 | resultType = nArrType; 44 | } 45 | else if (auto genType = type->AsGenericType()) 46 | { 47 | auto gType = new ILGenericType(); 48 | gType->GenericTypeName = genType->GenericTypeName; 49 | gType->BaseType = TranslateExpressionType(genType->BaseType.Ptr(), genericTypeMappings); 50 | resultType = gType; 51 | } 52 | return resultType; 53 | } 54 | 55 | RefPtr TranslateExpressionType(const RefPtr & type, Dictionary> * genericTypeMappings) 56 | { 57 | return TranslateExpressionType(type.Ptr(), genericTypeMappings); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /Source/SpireCore/TypeTranslation.h: -------------------------------------------------------------------------------- 1 | #ifndef SPIRE_TYPE_TRANSLATION_H 2 | #define SPIRE_TYPE_TRANSLATION_H 3 | 4 | #include "Syntax.h" 5 | #include "IL.h" 6 | 7 | namespace Spire 8 | { 9 | namespace Compiler 10 | { 11 | RefPtr TranslateExpressionType(ExpressionType * type, Dictionary> * genericTypeMappings = nullptr); 12 | RefPtr TranslateExpressionType(const RefPtr & type, Dictionary> * genericTypeMappings = nullptr); 13 | } 14 | } 15 | 16 | #endif -------------------------------------------------------------------------------- /Source/SpireCore/VariantIR.h: -------------------------------------------------------------------------------- 1 | #ifndef VARIANT_IR_H 2 | #define VARIANT_IR_H 3 | 4 | #include "Syntax.h" 5 | 6 | namespace Spire 7 | { 8 | namespace Compiler 9 | { 10 | class ShaderClosure; 11 | class ModuleInstanceIR : public RefObject 12 | { 13 | public: 14 | ShaderSyntaxNode * SyntaxNode; 15 | String BindingName; 16 | CodePosition UsingPosition; 17 | int BindingIndex; 18 | bool IsTopLevel = false; 19 | List SubModuleInstances; 20 | }; 21 | class ComponentDefinitionIR : public RefObject 22 | { 23 | private: 24 | EnumerableHashSet dependencyClosure; 25 | public: 26 | String OriginalName, UniqueName, UniqueKey; 27 | RefPtr SyntaxNode; 28 | RefPtr Type; 29 | ModuleInstanceIR * ModuleInstance = nullptr; 30 | String World; 31 | bool IsEntryPoint = false; 32 | EnumerableHashSet Users, Dependency; // Bidirectional dependency; 33 | EnumerableHashSet & GetComponentFunctionDependencyClosure(); 34 | void ClearDependency() 35 | { 36 | Dependency.Clear(); 37 | dependencyClosure.Clear(); 38 | } 39 | }; 40 | 41 | class ShaderIR : public RefObject 42 | { 43 | public: 44 | ShaderClosure * Shader; 45 | SymbolTable * SymbolTable; 46 | List> ModuleInstances; 47 | List> Definitions; 48 | EnumerableDictionary> DefinitionsByComponent; 49 | void EliminateDeadCode(); // returns remaining definitions in reverse dependency order 50 | void ResolveComponentReference(); // resolve reference and build dependency map 51 | List GetComponentDependencyOrder(); // returns a list of all components' unique names in dependency order 52 | template 53 | void RemoveDefinitions(const ShouldRemoveFunc &shouldRemove) 54 | { 55 | List> newDefinitions; 56 | for (auto & def : Definitions) 57 | { 58 | if (!shouldRemove(def.Ptr())) 59 | { 60 | newDefinitions.Add(def); 61 | } 62 | } 63 | Definitions = _Move(newDefinitions); 64 | for (auto & kv : DefinitionsByComponent) 65 | { 66 | for (auto & def : kv.Value) 67 | if (shouldRemove(def.Value)) 68 | kv.Value.Remove(def.Key); 69 | } 70 | } 71 | 72 | }; 73 | } 74 | } 75 | 76 | #endif -------------------------------------------------------------------------------- /Source/SpireLib/SpireLib.h: -------------------------------------------------------------------------------- 1 | #ifndef LIB_BAKER_SL_H 2 | #define LIB_BAKER_SL_H 3 | 4 | #include "../CoreLib/Basic.h" 5 | #include "../CoreLib/Tokenizer.h" 6 | #include "../SpireCore/ShaderCompiler.h" 7 | 8 | namespace SpireLib 9 | { 10 | class ShaderLibFile : public CoreLib::Basic::Object 11 | { 12 | public: 13 | CoreLib::Basic::EnumerableDictionary Sources; // indexed by world 14 | Spire::Compiler::ShaderMetaData MetaData; 15 | void AddSource(CoreLib::Basic::String source, CoreLib::Text::TokenReader & parser); 16 | void FromString(const CoreLib::String & str); 17 | CoreLib::String ToString(); 18 | void SaveToFile(CoreLib::Basic::String fileName); 19 | ShaderLibFile() = default; 20 | void Clear(); 21 | void Load(CoreLib::Basic::String fileName); 22 | }; 23 | 24 | CoreLib::Basic::List CompileShaderSourceFromFile(Spire::Compiler::CompileResult & result, 25 | const CoreLib::Basic::String & sourceFileName, 26 | Spire::Compiler::CompileOptions &options); 27 | 28 | CoreLib::Basic::List CompileShaderSource(Spire::Compiler::CompileResult & result, 29 | const CoreLib::Basic::String &source, const CoreLib::Basic::String & sourceFileName, Spire::Compiler::CompileOptions &options); 30 | 31 | class ShaderLib : public ShaderLibFile 32 | { 33 | public: 34 | Spire::Compiler::StageSource GetStageSource(CoreLib::Basic::String world); 35 | ShaderLib() = default; 36 | ShaderLib(CoreLib::Basic::String fileName); 37 | void Reload(CoreLib::Basic::String fileName); 38 | bool CompileFrom(CoreLib::Basic::String symbolName, CoreLib::Basic::String sourceFileName, CoreLib::Basic::String schedule); 39 | }; 40 | 41 | } 42 | 43 | #endif -------------------------------------------------------------------------------- /Source/SpireLib/SpireLib.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | 29 | 30 | Source Files 31 | 32 | 33 | -------------------------------------------------------------------------------- /SpireAllSource.h: -------------------------------------------------------------------------------- 1 | #ifndef SPIRE_NO_CORE_LIB 2 | #include "Source/CoreLib/CommandLineParser.cpp" 3 | #include "Source/CoreLib/LibIO.cpp" 4 | #include "Source/CoreLib/LibMath.cpp" 5 | #include "Source/CoreLib/LibString.cpp" 6 | #include "Source/CoreLib/Stream.cpp" 7 | #include "Source/CoreLib/TextIO.cpp" 8 | #include "Source/CoreLib/Tokenizer.cpp" 9 | #include "Source/CoreLib/VectorMath.cpp" 10 | #endif 11 | #include "Source/SpireCore/CLikeCodeGen.cpp" 12 | #include "Source/SpireCore/Closure.cpp" 13 | #include "Source/SpireCore/CodeGenerator.cpp" 14 | #include "Source/SpireCore/CompiledProgram.cpp" 15 | #include "Source/SpireCore/ConstantPool.cpp" 16 | #include "Source/SpireCore/Diagnostics.cpp" 17 | #include "Source/SpireCore/GetDependencyVisitor.cpp" 18 | #include "Source/SpireCore/GLSLCodeGen.cpp" 19 | #include "Source/SpireCore/HLSLCodeGen.cpp" 20 | #include "Source/SpireCore/IL.cpp" 21 | #include "Source/SpireCore/InsertImplicitImportOperator.cpp" 22 | #include "Source/SpireCore/KeyHoleMatching.cpp" 23 | #include "Source/SpireCore/Lexer.cpp" 24 | #include "Source/SpireCore/Naming.cpp" 25 | #include "Source/SpireCore/NewSpirVCodeGen.cpp" 26 | #include "Source/SpireCore/Parser.cpp" 27 | #include "Source/SpireCore/Preprocessor.cpp" 28 | #include "Source/SpireCore/Schedule.cpp" 29 | #include "Source/SpireCore/SemanticsVisitor.cpp" 30 | #include "Source/SpireCore/ShaderCompiler.cpp" 31 | #include "Source/SpireCore/SpirVCodeGen.cpp" 32 | #include "Source/SpireCore/StdInclude.cpp" 33 | #include "Source/SpireCore/SymbolTable.cpp" 34 | #include "Source/SpireCore/Syntax.cpp" 35 | #include "Source/SpireCore/TypeLayout.cpp" 36 | #include "Source/SpireCore/SamplerUsageAnalysis.cpp" 37 | #include "Source/SpireCore/VariantIR.cpp" 38 | #include "Source/SpireLib/SpireLib.cpp" 39 | -------------------------------------------------------------------------------- /Tests/Diagnostics/break-outside-loop.spire: -------------------------------------------------------------------------------- 1 | // `break` where it isn't allowed 2 | 3 | void foo() { break; } 4 | -------------------------------------------------------------------------------- /Tests/Diagnostics/break-outside-loop.spire.expected: -------------------------------------------------------------------------------- 1 | result code = -1 2 | standard error = { 3 | Tests/Diagnostics/break-outside-loop.spire(3): error 30003: 'break' must appear inside loop constructs. 4 | } 5 | standard output = { 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Diagnostics/call-argument-type.spire: -------------------------------------------------------------------------------- 1 | // call function with wrong argument type 2 | 3 | struct A {}; 4 | struct B {}; 5 | 6 | void f(A a) {} 7 | void g(B b) 8 | { 9 | f(b); 10 | } 11 | -------------------------------------------------------------------------------- /Tests/Diagnostics/call-argument-type.spire.expected: -------------------------------------------------------------------------------- 1 | result code = -1 2 | standard error = { 3 | Tests/Diagnostics/call-argument-type.spire(9): error 30021: f: no overload takes arguments () 4 | } 5 | standard output = { 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Diagnostics/continue-outside-loop.spire: -------------------------------------------------------------------------------- 1 | // `continue` where it isn't allowed 2 | 3 | void foo() { continue; } 4 | -------------------------------------------------------------------------------- /Tests/Diagnostics/continue-outside-loop.spire.expected: -------------------------------------------------------------------------------- 1 | result code = -1 2 | standard error = { 3 | Tests/Diagnostics/continue-outside-loop.spire(3): error 30004: 'continue' must appear inside loop constructs. 4 | } 5 | standard output = { 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Diagnostics/expected-token-eof.spire: -------------------------------------------------------------------------------- 1 | // expected one token, but got EOF 2 | 3 | int foo() 4 | { 5 | int a = 3 -------------------------------------------------------------------------------- /Tests/Diagnostics/expected-token-eof.spire.expected: -------------------------------------------------------------------------------- 1 | result code = -1 2 | standard error = { 3 | Tests/Diagnostics/expected-token-eof.spire(0): error 20001: unexpected end of file, expected ';' 4 | } 5 | standard output = { 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Diagnostics/expected-token.spire: -------------------------------------------------------------------------------- 1 | // expected one token, but got another 2 | 3 | int foo() 4 | { 5 | int a = 3 ] 6 | } -------------------------------------------------------------------------------- /Tests/Diagnostics/expected-token.spire.expected: -------------------------------------------------------------------------------- 1 | result code = -1 2 | standard error = { 3 | Tests/Diagnostics/expected-token.spire(5): error 20001: unexpected ']', expected ';' 4 | } 5 | standard output = { 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Diagnostics/function-redefinition.spire: -------------------------------------------------------------------------------- 1 | // redefining a function 2 | 3 | int foo(int a) { return 0; } 4 | int foo(int b) { return 1; } 5 | -------------------------------------------------------------------------------- /Tests/Diagnostics/function-redefinition.spire.expected: -------------------------------------------------------------------------------- 1 | result code = -1 2 | standard error = { 3 | Tests/Diagnostics/function-redefinition.spire(4): error 30001: 'foo(int)': function redefinition. 4 | } 5 | standard output = { 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Diagnostics/hull-shader-invalid-domain.spire: -------------------------------------------------------------------------------- 1 | // `HullShader` without `Domain` attribute 2 | 3 | pipeline P 4 | { 5 | world CoarseVertex; 6 | world ControlPoint; 7 | world CornerPoint; 8 | world TessPatch; 9 | world FineVertex; 10 | 11 | require @FineVertex vec4 RS_Position; 12 | require @ControlPoint vec2 tessLevelInner; 13 | require @ControlPoint vec4 tessLevelOuter; 14 | 15 | // implicit import operator CoarseVertex->CornerPoint 16 | extern @CornerPoint CoarseVertex[] CoarseVertex_ControlPoint; 17 | [PerCornerIterator] 18 | extern @CornerPoint int HS_CornerID; 19 | 20 | extern @ControlPoint CoarseVertex[] CoarseVertex_ControlPoint; 21 | extern @TessPatch CoarseVertex[] CoarseVertex_ControlPoint; 22 | [InvocationId] 23 | extern @ControlPoint int invocationId; 24 | extern @FineVertex ControlPoint[] ControlPoint_tes; 25 | extern @FineVertex Patch perPatch_tes; 26 | 27 | extern @FineVertex Patch perCorner_tes; 28 | [TessCoord] 29 | extern @FineVertex vec3 tessCoord; 30 | 31 | stage hs : HullShader 32 | { 33 | PatchWorld: TessPatch; 34 | ControlPointWorld: ControlPoint; 35 | CornerPointWorld: CornerPoint; 36 | InputControlPointCount: 3; 37 | ControlPointCount: 1; 38 | Domain: pentagons; 39 | TessLevelOuter: tessLevelOuter; 40 | TessLevelInner: tessLevelInner; 41 | Partitioning: integer; 42 | OutputTopology: triangle_ccw; 43 | } 44 | } 45 | 46 | shader S 47 | targets P 48 | { 49 | @FineVertex float4 RS_Position = float4(0.0); 50 | @ControlPoint float2 tessLevelInner = float2(2.0); 51 | @ControlPoint float4 tessLevelOuter = float4(2.0); 52 | } -------------------------------------------------------------------------------- /Tests/Diagnostics/hull-shader-invalid-domain.spire.expected: -------------------------------------------------------------------------------- 1 | result code = -1 2 | standard error = { 3 | Tests/Diagnostics/hull-shader-invalid-domain.spire(38): error 50053: 'Domain' should be either 'triangles' or 'quads'. 4 | } 5 | standard output = { 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Diagnostics/hull-shader-no-domain.spire: -------------------------------------------------------------------------------- 1 | // `HullShader` without `Domain` attribute 2 | 3 | pipeline P 4 | { 5 | world CoarseVertex; 6 | world ControlPoint; 7 | world CornerPoint; 8 | world TessPatch; 9 | world FineVertex; 10 | 11 | require @FineVertex vec4 RS_Position; 12 | require @ControlPoint vec2 tessLevelInner; 13 | require @ControlPoint vec4 tessLevelOuter; 14 | 15 | // implicit import operator CoarseVertex->CornerPoint 16 | extern @CornerPoint CoarseVertex[] CoarseVertex_ControlPoint; 17 | [PerCornerIterator] 18 | extern @CornerPoint int HS_CornerID; 19 | 20 | extern @ControlPoint CoarseVertex[] CoarseVertex_ControlPoint; 21 | extern @TessPatch CoarseVertex[] CoarseVertex_ControlPoint; 22 | [InvocationId] 23 | extern @ControlPoint int invocationId; 24 | extern @FineVertex ControlPoint[] ControlPoint_tes; 25 | extern @FineVertex Patch perPatch_tes; 26 | 27 | extern @FineVertex Patch perCorner_tes; 28 | [TessCoord] 29 | extern @FineVertex vec3 tessCoord; 30 | 31 | stage hs : HullShader 32 | { 33 | PatchWorld: TessPatch; 34 | ControlPointWorld: ControlPoint; 35 | CornerPointWorld: CornerPoint; 36 | InputControlPointCount: 3; 37 | ControlPointCount: 1; 38 | // Domain: triangles; 39 | TessLevelOuter: tessLevelOuter; 40 | TessLevelInner: tessLevelInner; 41 | Partitioning: integer; 42 | OutputTopology: triangle_ccw; 43 | } 44 | } 45 | 46 | shader S 47 | targets P 48 | { 49 | @FineVertex float4 RS_Position = float4(0.0); 50 | @ControlPoint float2 tessLevelInner = float2(2.0); 51 | @ControlPoint float4 tessLevelOuter = float4(2.0); 52 | } -------------------------------------------------------------------------------- /Tests/Diagnostics/hull-shader-no-domain.spire.expected: -------------------------------------------------------------------------------- 1 | result code = -1 2 | standard error = { 3 | Tests/Diagnostics/hull-shader-no-domain.spire(31): error 50052: 'HullShader' requires attribute 'Domain'. 4 | } 5 | standard output = { 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Diagnostics/illegal-character.spire: -------------------------------------------------------------------------------- 1 | // illegal character 2 | 3 | $ 4 | 5 | -------------------------------------------------------------------------------- /Tests/Diagnostics/illegal-character.spire.expected: -------------------------------------------------------------------------------- 1 | result code = -1 2 | standard error = { 3 | Tests/Diagnostics/illegal-character.spire(3): error 10000: Illegal character '\x24' 4 | } 5 | standard output = { 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Diagnostics/missing-file.spire: -------------------------------------------------------------------------------- 1 | // trying to import a non-existant file 2 | 3 | using "does-not-exist.spire" 4 | -------------------------------------------------------------------------------- /Tests/Diagnostics/missing-file.spire.expected: -------------------------------------------------------------------------------- 1 | result code = -1 2 | standard error = { 3 | Tests/Diagnostics/missing-file.spire(0): error 20001: unexpected end of file, expected ';' 4 | Tests/Diagnostics/missing-file.spire(3): error 2: cannot find file 'does-not-exist.spire'. 5 | } 6 | standard output = { 7 | } 8 | -------------------------------------------------------------------------------- /Tests/Diagnostics/parameter-already-defined.spire: -------------------------------------------------------------------------------- 1 | // re-use parameter name 2 | 3 | int foo( int a, float a ) { return 0; } 4 | -------------------------------------------------------------------------------- /Tests/Diagnostics/parameter-already-defined.spire.expected: -------------------------------------------------------------------------------- 1 | result code = -1 2 | standard error = { 3 | Tests/Diagnostics/parameter-already-defined.spire(3): error 30002: parameter 'a' already defined. 4 | } 5 | standard output = { 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Diagnostics/undefined-identifier.spire: -------------------------------------------------------------------------------- 1 | // use of undefined identifier 2 | 3 | void foo() 4 | { 5 | int a = b; 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Diagnostics/undefined-identifier.spire.expected: -------------------------------------------------------------------------------- 1 | result code = -1 2 | standard error = { 3 | Tests/Diagnostics/undefined-identifier.spire(5): error 30015: undefined identifier 'b'. 4 | } 5 | standard output = { 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Diagnostics/variable-void-type.spire: -------------------------------------------------------------------------------- 1 | // variable with `void` type 2 | 3 | void foo() 4 | { 5 | void a; 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Diagnostics/variable-void-type.spire.expected: -------------------------------------------------------------------------------- 1 | result code = -1 2 | standard error = { 3 | Tests/Diagnostics/variable-void-type.spire(5): error 30009: invalid type 'void'. 4 | } 5 | standard output = { 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Diagnostics/while-predicate-type.spire: -------------------------------------------------------------------------------- 1 | // bad type for `while` predicate 2 | 3 | struct S {}; 4 | 5 | void foo() 6 | { 7 | S s; 8 | while(s) {break;} 9 | } 10 | -------------------------------------------------------------------------------- /Tests/Diagnostics/while-predicate-type.spire.expected: -------------------------------------------------------------------------------- 1 | result code = -1 2 | standard error = { 3 | Tests/Diagnostics/while-predicate-type.spire(8): error 30010: 'while': expression must evaluate to int. 4 | } 5 | standard output = { 6 | } 7 | -------------------------------------------------------------------------------- /Tests/FrontEnd/lexer-comments.spire: -------------------------------------------------------------------------------- 1 | // confirming that the lexer handles comments correctly 2 | 3 | // line comment 4 | 5 | /* block comment 6 | */ 7 | 8 | /* block comments don't nest 9 | /* 10 | */ 11 | 12 | float f(float f) { return f; } -------------------------------------------------------------------------------- /Tests/FrontEnd/parser-decls.spire: -------------------------------------------------------------------------------- 1 | // test that we can parse all the expected kinds of declarations 2 | 3 | // global-scope `using` is another test 4 | 5 | // pipeline 6 | pipeline P 7 | { 8 | 9 | } 10 | 11 | // empty declaration 12 | ; 13 | 14 | // struct type 15 | struct Pair 16 | { 17 | int head; 18 | float tail; 19 | } 20 | 21 | // function at global scope 22 | float tail(Pair p) { return p.tail; } 23 | 24 | // module 25 | module M 26 | { 27 | // component declarations 28 | 29 | // using declarations 30 | 31 | } 32 | 33 | // a module can "inherit" from a pipeline 34 | module M2 35 | targets P 36 | { 37 | } 38 | 39 | // shader 40 | shader S 41 | { 42 | // component declarations 43 | 44 | // using declarations 45 | 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Tests/FrontEnd/parser-empty.spire: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spire-lang/Spire/e5cc9fb61cb9ae98b6a2586b43344d8ae62fa694/Tests/FrontEnd/parser-empty.spire -------------------------------------------------------------------------------- /Tests/FrontEnd/parser-error-unclosed-curly.spire: -------------------------------------------------------------------------------- 1 | shader Test { 2 | // Note: no closing curly brace 3 | -------------------------------------------------------------------------------- /Tests/FrontEnd/parser-error-unclosed-curly.spire.expected: -------------------------------------------------------------------------------- 1 | result code = -1 2 | standard error = { 3 | Tests/FrontEnd/parser-error-unclosed-curly.spire(0): error 20001: unexpected end of file, expected '}' 4 | } 5 | standard output = { 6 | } 7 | -------------------------------------------------------------------------------- /Tests/FrontEnd/parser-using-file-a.spireh: -------------------------------------------------------------------------------- 1 | // this file exists to be included by "parser-using-file.spire" 2 | 3 | float a(float x) { return x * x; } 4 | -------------------------------------------------------------------------------- /Tests/FrontEnd/parser-using-file.spire: -------------------------------------------------------------------------------- 1 | // test that we can include a file via `using` 2 | 3 | using "parser-using-file-a.spireh"; 4 | 5 | float base( float x ) { return a(x); } -------------------------------------------------------------------------------- /Tests/FrontEnd/pipeline-simple.spireh: -------------------------------------------------------------------------------- 1 | // pipeline-simple.spireh 2 | 3 | 4 | // TODO(tfoley): strip this down to a minimal pipeline 5 | 6 | pipeline StandardPipeline 7 | { 8 | [Pinned] 9 | input world MeshVertex; 10 | 11 | world CoarseVertex;// : "glsl(vertex:projCoord)" using projCoord export standardExport; 12 | world Fragment;// : "glsl" export fragmentExport; 13 | 14 | require @CoarseVertex vec4 projCoord; 15 | 16 | [VertexInput] 17 | extern @CoarseVertex MeshVertex vertAttribIn; 18 | import(MeshVertex->CoarseVertex) vertexImport() 19 | { 20 | return project(vertAttribIn); 21 | } 22 | 23 | extern @Fragment CoarseVertex CoarseVertexIn; 24 | import(CoarseVertex->Fragment) standardImport() 25 | // TODO(tfoley): this trait doesn't seem to be implemented on `vec3` 26 | // require trait IsTriviallyPassable(CoarseVertex) 27 | { 28 | return project(CoarseVertexIn); 29 | } 30 | 31 | stage vs : VertexShader 32 | { 33 | World: CoarseVertex; 34 | Position: projCoord; 35 | } 36 | 37 | stage fs : FragmentShader 38 | { 39 | World: Fragment; 40 | } 41 | } -------------------------------------------------------------------------------- /Tests/FrontEnd/struct.spire: -------------------------------------------------------------------------------- 1 | // test that `struct` decls work 2 | 3 | #include "pipeline-simple.spireh" 4 | 5 | // struct declaration 6 | struct Foo 7 | { 8 | vec3 a; 9 | vec3 b; 10 | }; 11 | 12 | // function on a struct 13 | Foo makeFoo(float x, float y) 14 | { 15 | // local of struct type 16 | Foo foo; 17 | foo.a = vec3(x); 18 | foo.b = vec3(y); 19 | return foo; 20 | } 21 | 22 | shader Test 23 | targets StandardPipeline 24 | { 25 | // Uniform of struct type 26 | param Foo foo1; 27 | 28 | @MeshVertex vec3 position; 29 | @MeshVertex vec3 color; 30 | 31 | param mat4 modelViewProjection; 32 | 33 | public vec4 projCoord = modelViewProjection * vec4(position, 1.0); 34 | 35 | // Component of struct type 36 | // Note(tfoley): use of `public` here required to work around parser limitations 37 | public Foo foo2 = makeFoo(color.x, color.y); 38 | 39 | // 40 | vec3 result = foo1.a + foo2.b; 41 | 42 | out @Fragment vec4 colorTarget = vec4(result,1); 43 | } 44 | -------------------------------------------------------------------------------- /Tests/FrontEnd/typedef.spire: -------------------------------------------------------------------------------- 1 | // test that we can `typedef` a type 2 | 3 | typedef float F32; 4 | 5 | F32 foo() 6 | { 7 | float x = 123.0; 8 | return x; 9 | } 10 | 11 | float bar() 12 | { 13 | return foo(); 14 | } 15 | -------------------------------------------------------------------------------- /Tests/HLSLCodeGen/DeferredLighting.vs.hlsl: -------------------------------------------------------------------------------- 1 | #pragma warning(disable: 3576) 2 | #pragma pack_matrix( row_major ) 3 | struct SkinningResult 4 | { 5 | float3 pos; 6 | float3 tangent; 7 | float3 binormal; 8 | }; 9 | Texture2D DeferredLightingParams_albedoTex: register(t0, space-1); 10 | Texture2D DeferredLightingParams_pbrTex: register(t1, space-1); 11 | Texture2D DeferredLightingParams_normalTex: register(t2, space-1); 12 | Texture2D DeferredLightingParams_depthTex: register(t3, space-1); 13 | SamplerState DeferredLightingParams_nearestSampler: register(s0, space-1); 14 | SamplerState ForwardBasePassParams_textureSampler: register(s1, space-1); 15 | Texture2DArray lighting_shadowMapArray: register(t4, space-1); 16 | SamplerComparisonState lighting_shadowMapSampler: register(s2, space-1); 17 | TextureCube lighting_envMap: register(t5, space-1); 18 | Texture2D layers_l0_albedoTex: register(t6, space-1); 19 | SamplerState layers_l0_samplerState: register(s3, space-1); 20 | Texture2D layers_l1_albedoTex: register(t7, space-1); 21 | SamplerState layers_l1_samplerState: register(s4, space-1); 22 | Texture2D layers_l2_albedoTex: register(t8, space-1); 23 | SamplerState layers_l2_samplerState: register(s5, space-1); 24 | Texture2D DeferredLightingParams_albedoTex: register(t0, space0); 25 | Texture2D DeferredLightingParams_pbrTex: register(t1, space0); 26 | Texture2D DeferredLightingParams_normalTex: register(t2, space0); 27 | Texture2D DeferredLightingParams_depthTex: register(t3, space0); 28 | SamplerState DeferredLightingParams_nearestSampler: register(s0, space0); 29 | cbuffer bufForwardBasePassParams : register(b1) 30 | { 31 | struct { 32 | float4x4 viewTransform; 33 | float4x4 viewProjectionTransform; 34 | float4x4 invViewTransform; 35 | float4x4 invViewProjTransform; 36 | float3 cameraPos; 37 | float time; 38 | } ForwardBasePassParams; 39 | }; 40 | SamplerState ForwardBasePassParams_textureSampler: register(s0, space1); 41 | cbuffer buflighting : register(b2) 42 | { 43 | struct { 44 | float3 lightDir; 45 | float3 lightColor; 46 | float ambient; 47 | int shadowMapId; 48 | int numCascades; 49 | float4x4 lightMatrix[8]; 50 | float4 zPlanes[2]; 51 | } lighting; 52 | }; 53 | Texture2DArray lighting_shadowMapArray: register(t0, space2); 54 | SamplerComparisonState lighting_shadowMapSampler: register(s0, space2); 55 | TextureCube lighting_envMap: register(t1, space2); 56 | Texture2D layers_l0_albedoTex: register(t0, space3); 57 | SamplerState layers_l0_samplerState: register(s0, space3); 58 | Texture2D layers_l1_albedoTex: register(t1, space3); 59 | SamplerState layers_l1_samplerState: register(s1, space3); 60 | Texture2D layers_l2_albedoTex: register(t2, space3); 61 | SamplerState layers_l2_samplerState: register(s2, space3); 62 | struct TMeshVertex 63 | { 64 | float2 vertPos : A0A; 65 | float2 vertUV : A1A; 66 | }; 67 | struct TCoarseVertex 68 | { 69 | float2 vertUV_CoarseVertex : A0A; 70 | }; 71 | struct TCoarseVertexExt 72 | { 73 | TCoarseVertex user; 74 | float4 sv_position : SV_Position; 75 | }; 76 | TCoarseVertexExt main( 77 | TMeshVertex stage_input : A) 78 | { 79 | TCoarseVertexExt stage_output; 80 | float2 vertPos; 81 | float2 vertUV; 82 | vertPos = stage_input/*standard*/.vertPos; 83 | vertUV = stage_input/*standard*/.vertUV; 84 | stage_output.user.vertUV_CoarseVertex = vertUV; 85 | stage_output.sv_position = float4(vertPos.xy, 0.000000000000e+00, 1.000000000000e+00); 86 | return stage_output; 87 | } -------------------------------------------------------------------------------- /Tests/HLSLCodeGen/StandardPipeline.spire: -------------------------------------------------------------------------------- 1 | pipeline StandardPipeline 2 | { 3 | [Pinned] 4 | input world MeshVertex; 5 | 6 | world CoarseVertex; 7 | world Fragment; 8 | 9 | require @CoarseVertex vec4 projCoord; 10 | 11 | [VertexInput] 12 | extern @CoarseVertex MeshVertex vertAttribIn; 13 | import(MeshVertex->CoarseVertex) vertexImport() 14 | { 15 | return project(vertAttribIn); 16 | } 17 | 18 | extern @Fragment CoarseVertex CoarseVertexIn; 19 | import(CoarseVertex->Fragment) standardImport() 20 | require trait IsTriviallyPassable(T) 21 | { 22 | return project(CoarseVertexIn); 23 | } 24 | 25 | stage vs : VertexShader 26 | { 27 | World: CoarseVertex; 28 | Position: projCoord; 29 | } 30 | 31 | stage fs : FragmentShader 32 | { 33 | World: Fragment; 34 | } 35 | } 36 | 37 | pipeline TessellationPipeline : StandardPipeline 38 | { 39 | [Pinned] 40 | input world MeshVertex; 41 | 42 | world CoarseVertex; 43 | world ControlPoint; 44 | world CornerPoint; 45 | world TessPatch; 46 | world FineVertex; 47 | world Fragment; 48 | 49 | require @FineVertex vec4 projCoord; 50 | require @ControlPoint vec2 tessLevelInner; 51 | require @ControlPoint vec4 tessLevelOuter; 52 | 53 | [VertexInput] 54 | extern @CoarseVertex MeshVertex vertAttribs; 55 | import(MeshVertex->CoarseVertex) vertexImport() { return project(vertAttribs); } 56 | 57 | // implicit import operator CoarseVertex->CornerPoint 58 | extern @CornerPoint CoarseVertex[] CoarseVertex_ControlPoint; 59 | [PerCornerIterator] 60 | extern @CornerPoint int sysLocalIterator; 61 | import (CoarseVertex->CornerPoint) standardImport() 62 | require trait IsTriviallyPassable(T) 63 | { 64 | return project(CoarseVertex_ControlPoint[sysLocalIterator]); 65 | } 66 | 67 | // implicit import operator FineVertex->Fragment 68 | extern @Fragment FineVertex tes_Fragment; 69 | import(FineVertex->Fragment) standardImport() 70 | require trait IsTriviallyPassable(T) 71 | { 72 | return project(tes_Fragment); 73 | } 74 | 75 | extern @ControlPoint CoarseVertex[] CoarseVertex_ControlPoint; 76 | extern @TessPatch CoarseVertex[] CoarseVertex_ControlPoint; 77 | [InvocationId] 78 | extern @ControlPoint int invocationId; 79 | import(CoarseVertex->ControlPoint) indexImport(int id) 80 | require trait IsTriviallyPassable(T) 81 | { 82 | return project(CoarseVertex_ControlPoint[id]); 83 | } 84 | import(CoarseVertex->TessPatch) indexImport(int id) 85 | require trait IsTriviallyPassable(T) 86 | { 87 | return project(CoarseVertex_ControlPoint[id]); 88 | } 89 | extern @FineVertex ControlPoint[] ControlPoint_tes; 90 | import(ControlPoint->FineVertex) indexImport(int id) 91 | require trait IsTriviallyPassable(T) 92 | { 93 | return project(ControlPoint_tes[id]); 94 | } 95 | extern @FineVertex Patch perPatch_tes; 96 | import (TessPatch->FineVertex) standardImport() 97 | require trait IsTriviallyPassable(T) 98 | { 99 | return project(perPatch_tes); 100 | } 101 | 102 | extern @FineVertex Patch perCorner_tes; 103 | [TessCoord] 104 | extern @FineVertex vec3 tessCoord; 105 | import(CornerPoint->FineVertex) standardImport() 106 | require T operator + (T, T) 107 | require T operator * (T, float) 108 | { 109 | return project(perCorner_tes[0]) * tessCoord.x + 110 | project(perCorner_tes[1]) * tessCoord.y + 111 | project(perCorner_tes[2]) * tessCoord.z; 112 | } 113 | 114 | stage vs : VertexShader 115 | { 116 | VertexInput: vertAttribs; 117 | World: CoarseVertex; 118 | } 119 | 120 | stage tcs : HullShader 121 | { 122 | PatchWorld: TessPatch; 123 | ControlPointWorld: ControlPoint; 124 | CornerPointWorld: CornerPoint; 125 | ControlPointCount: 1; 126 | Domain: triangles; 127 | TessLevelOuter: tessLevelOuter; 128 | TessLevelInner: tessLevelInner; 129 | } 130 | 131 | stage tes : DomainShader 132 | { 133 | World : FineVertex; 134 | Position : projCoord; 135 | Domain: triangles; 136 | } 137 | 138 | stage fs : FragmentShader 139 | { 140 | World: Fragment; 141 | CoarseVertexInput: vertexOutputBlock; 142 | } 143 | } -------------------------------------------------------------------------------- /Tests/HLSLCodeGen/shader1.spire: -------------------------------------------------------------------------------- 1 | using "StandardPipeline.spire"; 2 | using "Utils.spire"; 3 | 4 | struct ArrInStruct 5 | { 6 | vec3 ttt; 7 | vec3[4] points; 8 | } 9 | 10 | int Test(in vec3 b, inout ArrInStruct testOut) 11 | { 12 | testOut.ttt = b; 13 | for (int i = 0; i < 4; i++) 14 | testOut.points[i] = 0; 15 | return 0; 16 | } 17 | 18 | 19 | module DeferredLightingParams 20 | { 21 | param Texture2D albedoTex; 22 | param Texture2D pbrTex; 23 | param Texture2D normalTex; 24 | param Texture2D depthTex; 25 | param SamplerState nearestSampler; 26 | } 27 | 28 | module SubModuleWithParam 29 | { 30 | param Texture2D albedoTex; 31 | param SamplerState samplerState; 32 | vec4 Eval(vec2 uv) 33 | { 34 | return albedoTex.Sample(samplerState, uv); 35 | } 36 | } 37 | 38 | module SubModuleWithParam1 39 | { 40 | param Texture2D albedoTex; 41 | param SamplerState samplerState; 42 | vec4 Eval(vec2 uv) 43 | { 44 | return albedoTex.Sample(samplerState, uv); 45 | } 46 | } 47 | 48 | module TopModule 49 | { 50 | using l0 = SubModuleWithParam(); 51 | using l1 = SubModuleWithParam(); 52 | using l2 = SubModuleWithParam1(); 53 | vec4 shadingResult = l0.Eval(vec2(0.0)) + l1.Eval(vec2(0.0)) + l2.Eval(vec2(0.0)); 54 | } 55 | 56 | shader DeferredLighting targets StandardPipeline 57 | { 58 | [Binding: "0"] 59 | public using DeferredLightingParams; 60 | 61 | [Binding: "1"] 62 | public using ForwardBasePassParams; 63 | 64 | public @MeshVertex vec2 vertPos; 65 | public @MeshVertex vec2 vertUV; 66 | 67 | public vec4 projCoord = vec4(vertPos.xy, 0.0, 1.0); 68 | 69 | public vec4 normalSample = normalTex.Sample(nearestSampler, vertUV); 70 | public vec3 normal = normalSample.xyz * 2.0 - 1.0; 71 | public vec4 pbr = pbrTex.Sample(nearestSampler, vertUV); 72 | public float roughness = pbr.x; 73 | public float metallic = pbr.y; 74 | public float specular = pbr.z; 75 | public float ao = pbr.w; 76 | public vec3 albedo = albedoTex.Sample(nearestSampler, vertUV).xyz; 77 | public float selfShadow(vec3 x) { return 1.0; } 78 | public bool isDoubleSided = normalSample.w != 0.0; 79 | vec3 lightParam = vec3(roughness, metallic, specular); 80 | float z = depthTex.Sample(nearestSampler, vertUV).r; 81 | float x = vertUV.x*2-1; 82 | float y = vertUV.y*2-1; 83 | vec4 position = invViewProjTransform * vec4(x, y, z, 1.0f); 84 | vec3 pos = position.xyz / position.w; 85 | 86 | [Binding: "2"] 87 | using lighting = Lighting(); 88 | 89 | [Binding: "3"] 90 | using layers = TopModule(); 91 | 92 | public out @Fragment vec4 outputColor 93 | { 94 | vec4 rs = vec4(lighting.result, 1.0) + layers.shadingResult; 95 | ArrInStruct s; 96 | Test(vec3(1.0f), s); 97 | return rs; 98 | } 99 | 100 | } 101 | 102 | 103 | /* 104 | template shader ForwardBase(passParams : ForwardBasePassParams, 105 | lightingModule, 106 | geometryModule : IMaterialGeometry, 107 | materialModule : IMaterialPattern, 108 | animationModule) targets StandardPipeline 109 | { 110 | public using VertexAttributes; 111 | public using passParams; 112 | public using animationModule; 113 | public using TangentSpaceTransform; 114 | public using geometryModule; 115 | public using VertexTransform; 116 | public using materialModule; 117 | vec3 lightParam = vec3(roughness, metallic, specular); 118 | using lighting = lightingModule(TangentSpaceToWorldSpace(vec3(normal.x, -normal.y, normal.z))); 119 | public out @Fragment vec4 outputColor 120 | { 121 | ArrInStruct t; 122 | int r = Test(vec3(1.0f), t); 123 | if (opacity < 0.01f) discard; 124 | return vec4(lighting.result, opacity + (float)r); 125 | } 126 | 127 | } 128 | 129 | */ -------------------------------------------------------------------------------- /Tests/Preprocessor/define-function-like.spire: -------------------------------------------------------------------------------- 1 | // support for function-like macros 2 | 3 | #define FOO(x) 1.0 + x 4 | 5 | float foo(float y) { return FOO(y) * 2.0; } 6 | 7 | // simple token pasting 8 | 9 | #define PASTE(a,b) a##b 10 | 11 | PASTE(flo,at) bar() { return 0.0; } 12 | 13 | // no space before parens? not a function-like macro 14 | 15 | #define M (x) - (x) 16 | 17 | // Error: undefined identifier `x` 18 | float bar(float a) { return M(a); } 19 | -------------------------------------------------------------------------------- /Tests/Preprocessor/define-function-like.spire.expected: -------------------------------------------------------------------------------- 1 | result code = -1 2 | standard error = { 3 | Tests/Preprocessor/define-function-like.spire(15): error 30015: undefined identifier 'x'. 4 | Tests/Preprocessor/define-function-like.spire(15): error 30015: undefined identifier 'x'. 5 | } 6 | standard output = { 7 | } 8 | -------------------------------------------------------------------------------- /Tests/Preprocessor/define-simple.spire: -------------------------------------------------------------------------------- 1 | // #define support 2 | 3 | #define FOO 1.0f 4 | 5 | float foo() { return FOO + 2.0; } 6 | 7 | #define BAR 99 8 | 9 | #if BAR > 10 10 | int bar() { return 0; } 11 | #else 12 | BadThing shouldntCompile; 13 | #endif 14 | -------------------------------------------------------------------------------- /Tests/Preprocessor/if.spire: -------------------------------------------------------------------------------- 1 | // #ifdef support 2 | 3 | 4 | #if (1 - 1*2) < 0 5 | int foo() { return 0; } 6 | #else 7 | BadThing thatWontCompile; 8 | #endif 9 | 10 | #if (1 >> 1) && ~999 11 | AnotherError onThisLine; 12 | #else 13 | int bar() { return foo(); } 14 | #endif -------------------------------------------------------------------------------- /Tests/Preprocessor/ifdef.spire: -------------------------------------------------------------------------------- 1 | // #ifdef support 2 | 3 | #define A 4 | 5 | #ifdef A 6 | int foo() { return 0; } 7 | #else 8 | BadThing thatWontCompile; 9 | #endif 10 | 11 | #ifdef BadThing 12 | AnotherError onThisLine; 13 | #else 14 | int bar() { return foo(); } 15 | #endif -------------------------------------------------------------------------------- /Tests/Preprocessor/include-a.spireh: -------------------------------------------------------------------------------- 1 | // #include support 2 | 3 | int bar() { return foo(); } -------------------------------------------------------------------------------- /Tests/Preprocessor/include.spire: -------------------------------------------------------------------------------- 1 | // #include support 2 | 3 | int foo() { return 0; } 4 | 5 | #include "include-a.spireh" 6 | 7 | int baz() { return bar(); } -------------------------------------------------------------------------------- /Tests/SpireTestTool/SpireTestTool.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | 26 | 27 | Header Files 28 | 29 | 30 | -------------------------------------------------------------------------------- /Tests/SpireTestTool/main.cpp: -------------------------------------------------------------------------------- 1 | // main.cpp 2 | 3 | #include "../../Source/CoreLib/LibIO.h" 4 | 5 | using namespace CoreLib::Basic; 6 | using namespace CoreLib::IO; 7 | 8 | #include "os.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | // Called for an error in the test-runner (not for an error involving 16 | // a test itself). 17 | void error(char const* message, ...) 18 | { 19 | fprintf(stderr, "error: "); 20 | 21 | va_list args; 22 | va_start(args, message); 23 | vfprintf(stderr, message, args); 24 | va_end(args); 25 | 26 | fprintf(stderr, "\n"); 27 | } 28 | 29 | enum TestResult 30 | { 31 | kTestResult_Fail, 32 | kTestResult_Pass, 33 | }; 34 | 35 | TestResult runTestImpl( 36 | String filePath) 37 | { 38 | // need to execute the stand-alone Spire compiler on the file, and compare its output to what we expect 39 | 40 | OSProcessSpawner spawner; 41 | 42 | spawner.pushExecutableName("Source/Debug/SpireCompiler.exe"); 43 | spawner.pushArgument(filePath); 44 | 45 | if (spawner.spawnAndWaitForCompletion() != kOSError_None) 46 | { 47 | error("failed to run test '%S'", filePath.ToWString()); 48 | return kTestResult_Fail; 49 | } 50 | 51 | // We ignore output to stdout, and only worry about what the compiler 52 | // wrote to stderr. 53 | 54 | OSProcessSpawner::ResultCode resultCode = spawner.getResultCode(); 55 | 56 | String standardOuptut = spawner.getStandardOutput(); 57 | String standardError = spawner.getStandardError(); 58 | 59 | // We construct a single output string that captures the results 60 | StringBuilder actualOutputBuilder; 61 | actualOutputBuilder.Append("result code = "); 62 | actualOutputBuilder.Append(resultCode); 63 | actualOutputBuilder.Append("\nstandard error = {\n"); 64 | actualOutputBuilder.Append(standardError); 65 | actualOutputBuilder.Append("}\nstandard output = {\n"); 66 | actualOutputBuilder.Append(standardOuptut); 67 | actualOutputBuilder.Append("}\n"); 68 | 69 | String actualOutput = actualOutputBuilder.ProduceString(); 70 | 71 | String expectedOutputPath = filePath + ".expected"; 72 | String expectedOutput; 73 | try 74 | { 75 | expectedOutput = CoreLib::IO::File::ReadAllText(expectedOutputPath); 76 | } 77 | catch (CoreLib::IO::IOException) 78 | { 79 | } 80 | 81 | TestResult result = kTestResult_Pass; 82 | 83 | // If no expected output file was found, then we 84 | // expect everything to be empty 85 | if (expectedOutput.Length() == 0) 86 | { 87 | if (resultCode != 0) result = kTestResult_Fail; 88 | if (standardError.Length() != 0) result = kTestResult_Fail; 89 | if (standardOuptut.Length() != 0) result = kTestResult_Fail; 90 | } 91 | // Otherwise we compare to the expected output 92 | else if (actualOutput != expectedOutput) 93 | { 94 | result = kTestResult_Fail; 95 | } 96 | 97 | // If the test failed, then we write the actual output to a file 98 | // so that we can easily diff it from the command line and 99 | // diagnose the problem. 100 | if (result == kTestResult_Fail) 101 | { 102 | String actualOutputPath = filePath + ".actual"; 103 | CoreLib::IO::File::WriteAllText(actualOutputPath, actualOutput); 104 | } 105 | 106 | return result; 107 | } 108 | 109 | struct TestContext 110 | { 111 | int totalTestCount; 112 | int passedTestCount; 113 | int failedTestCount; 114 | }; 115 | 116 | void runTest( 117 | TestContext* context, 118 | String filePath) 119 | { 120 | 121 | context->totalTestCount++; 122 | TestResult result = runTestImpl(filePath); 123 | if (result == kTestResult_Pass) 124 | { 125 | printf("passed"); 126 | context->passedTestCount++; 127 | } 128 | else 129 | { 130 | printf("FAILED"); 131 | context->failedTestCount++; 132 | } 133 | 134 | printf(" test: '%S'\n", filePath.ToWString()); 135 | } 136 | 137 | void runTestsInDirectory( 138 | TestContext* context, 139 | String directoryPath) 140 | { 141 | for (auto file : osFindFilesInDirectoryMatchingPattern(directoryPath, "*.spire")) 142 | { 143 | runTest(context, file); 144 | } 145 | } 146 | 147 | // 148 | 149 | int main( 150 | int argc, 151 | char** argv) 152 | { 153 | TestContext context = { 0 }; 154 | 155 | // Enumerate test files according to policy 156 | // TODO: add more directories to this list 157 | // TODO: allow for a command-line argument to select a particular directory 158 | runTestsInDirectory(&context, "Tests/FrontEnd/"); 159 | runTestsInDirectory(&context, "Tests/Diagnostics/"); 160 | runTestsInDirectory(&context, "Tests/Preprocessor/"); 161 | 162 | if (!context.totalTestCount) 163 | { 164 | printf("no tests run"); 165 | return 0; 166 | } 167 | 168 | printf("\n===\n%d%% of tests passed (%d/%d)\n===\n\n", (context.passedTestCount*100) / context.totalTestCount, context.passedTestCount, context.totalTestCount); 169 | return context.passedTestCount == context.totalTestCount ? 0 : 1; 170 | } 171 | -------------------------------------------------------------------------------- /Tests/SpireTestTool/os.h: -------------------------------------------------------------------------------- 1 | // os.h 2 | 3 | #include "../../Source/CoreLib/LibIO.h" 4 | 5 | // This file encapsulates the platform-specific operations needed by the test 6 | // runner that are not already provided by the core Spire libs 7 | 8 | #ifdef _WIN32 9 | 10 | // Include Windows header in a way that minimized namespace pollution. 11 | // TODO: We could try to avoid including this at all, but it would 12 | // mean trying to hide certain struct layouts, which would add 13 | // more dynamic allocation. 14 | #define WIN32_LEAN_AND_MEAN 15 | #define NOMINMAX 16 | #include 17 | #undef WIN32_LEAN_AND_MEAN 18 | #undef NOMINMAX 19 | 20 | #else 21 | #endif 22 | 23 | // A simple set of error codes for possible runtime failures 24 | enum OSError 25 | { 26 | kOSError_None = 0, 27 | kOSError_InvalidArgument, 28 | kOSError_OperationFailed, 29 | kOSError_FileNotFound, 30 | }; 31 | 32 | // A helper type used during enumeration of files in a directory. 33 | struct OSFindFilesResult 34 | { 35 | CoreLib::Basic::String directoryPath_; 36 | CoreLib::Basic::String filePath_; 37 | #ifdef WIN32 38 | HANDLE findHandle_; 39 | WIN32_FIND_DATAW fileData_; 40 | OSError error_; 41 | #else 42 | #endif 43 | 44 | bool findNextFile(); 45 | 46 | struct Iterator 47 | { 48 | OSFindFilesResult* context_; 49 | 50 | bool operator!=(Iterator other) const { return context_ != other.context_; } 51 | void operator++() 52 | { 53 | if (!context_->findNextFile()) 54 | { 55 | context_ = NULL; 56 | } 57 | } 58 | CoreLib::Basic::String const& operator*() const 59 | { 60 | return context_->filePath_; 61 | } 62 | }; 63 | 64 | Iterator begin() 65 | { 66 | Iterator result = { this }; 67 | return result; 68 | } 69 | 70 | Iterator end() 71 | { 72 | Iterator result = { NULL }; 73 | return result; 74 | } 75 | }; 76 | 77 | // Enumerate files in the given `directoryPath` that match the provided 78 | // `pattern` as a simplified regex for files to return (e.g., "*.txt") 79 | // and return a logical collection of the results 80 | // that can be iterated with a range-based `for` loop: 81 | // 82 | // for( auto file : osFindFilesInDirectoryMatchingPattern(dir, "*.txt")) 83 | // { ... } 84 | // 85 | // Each element in the range is a `CoreLib::Basic::String` representing the 86 | // path to a file in the directory. 87 | OSFindFilesResult osFindFilesInDirectoryMatchingPattern( 88 | CoreLib::Basic::String directoryPath, 89 | CoreLib::Basic::String pattern); 90 | 91 | // An `OSProcessSpawner` can be used to launch a process, and handles 92 | // putting together the arguments in the form required by the target 93 | // platform, as well as capturing any output from the process (both 94 | // standard output and standard error) as strings. 95 | struct OSProcessSpawner 96 | { 97 | // Set the executable name for the process to be spawned. 98 | // Note: this call must be made before any arguments are pushed. 99 | void pushExecutableName( 100 | CoreLib::Basic::String executableName); 101 | 102 | // Append an argument for the process to be spawned. 103 | void pushArgument( 104 | CoreLib::Basic::String argument); 105 | 106 | // Attempt to spawn the process, and wait for it to complete. 107 | // Returns an error if the attempt to spawn and/or wait fails, 108 | // but returns `kOSError_None` if the process is run to completion, 109 | // whether or not the process returns "successfully" (with a zero 110 | // result code); 111 | OSError spawnAndWaitForCompletion(); 112 | 113 | // If the process is successfully spawned and completes, then 114 | // the user can query the result code that the process produce 115 | // on exit, along with the output it wrote to stdout and stderr. 116 | typedef int ResultCode; 117 | ResultCode getResultCode() { return resultCode_; } 118 | CoreLib::Basic::String const& getStandardOutput() { return standardOutput_; } 119 | CoreLib::Basic::String const& getStandardError() { return standardError_; } 120 | 121 | // "private" data follows 122 | CoreLib::Basic::String standardOutput_; 123 | CoreLib::Basic::String standardError_; 124 | ResultCode resultCode_; 125 | #ifdef WIN32 126 | CoreLib::Basic::String executableName_; 127 | CoreLib::Basic::StringBuilder commandLine_; 128 | #else 129 | #endif 130 | }; 131 | -------------------------------------------------------------------------------- /test.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | pushd %~dp0 4 | 5 | :: TODO: ensure that everything is built? 6 | 7 | .\Source\Debug\SpireTestTool.exe --------------------------------------------------------------------------------