Repository: csyonghe/Spire Branch: master Commit: 727250ed0568 Files: 171 Total size: 1.4 MB Directory structure: gitextract_nsdljbup/ ├── .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 ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ [*] indent_style = tab indent_size = 4 ================================================ FILE: .gitignore ================================================ DebugClang/ *.user autotuneLog.txt LibraryRelease/CodePack.exe *.exe *.exe.config *.exe.manifest *.pdb .vs *.VC.opendb *.VC.db *.spire.actual LibraryRelease/Spire.cpp *.sdf Debug/ Release/ x64/ *.TMP *.cse ================================================ FILE: Examples/hello/README.md ================================================ Spire "Hello World" Example =========================== The goal of this example is to demonstrate an almost minimal application that uses Spire for shading, and D3D11 for rendering. 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). 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. 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. ================================================ FILE: Examples/hello/hello.cpp ================================================ // hello.cpp // In order to use the Spire API, we need to include its header #include // We will be rendering with Direct3D 11, so we need to include // the Windows and D3D11 headers #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include #undef WIN32_LEAN_AND_MEAN #undef NOMINMAX #include #include // We will use the C standard library just for printing error messages. #include #ifdef _MSC_VER #include #if (_MSC_VER < 1900) #define snprintf sprintf_s #endif #endif // static int gWindowWidth = 1024; static int gWindowHeight = 768; // // // For the purposes of a small example, we will define the vertex data for a // single triangle directly in the source file. It should be easy to extend // this example to load data from an external source, if desired. // struct Vertex { float position[3]; float color[3]; }; static const int kVertexCount = 3; static const Vertex kVertexData[kVertexCount] = { { { 0, 0, 0.5 }, {1, 0, 0} }, { { 0, 1, 0.5 }, {0, 0, 1} }, { { 1, 0, 0.5 }, {0, 1, 0} }, }; // // Global variabels for the various D3D11 API objects to be used for rendering ID3D11Buffer* dxConstantBuffer; ID3D11InputLayout* dxInputLayout; ID3D11Buffer* dxVertexBuffer; ID3D11VertexShader* dxVertexShader; ID3D11PixelShader* dxPixelShader; // The Spire compiler currently generates HLSL source, so we'll need a utility // routine (defined later) to translate that into D3D11 shader bytecode. ID3DBlob* compileHLSLShader( char const* source, char const* dxProfileName); // We use a utility routine to print out any diagnostic (error/warning) output // from the Spire compiler. void emitSpireDiagnostics( SpireDiagnosticSink* sink); // // At initialization time, we are going to load and compile our Spire shader // code, and then create the D3D11 API objects we need for rendering. // HRESULT initialize( ID3D11Device* dxDevice ) { // // First, we will load and compile our Spire source code. // // The argument here is an optional directory where the Spire compiler // can cache files to speed up compilation of many kernels. SpireCompilationContext* spireContext = spCreateCompilationContext(NULL); // A diagnostic sink is used to collect output messages from the Spire // compiler, so that we can easily iterate over them if an operation // fails. SpireDiagnosticSink* spireSink = spCreateDiagnosticSink(spireContext); // Instruct Spire to generate code as HLSL spSetCodeGenTarget(spireContext, SPIRE_HLSL); // Load a file of Spire source code, which defines our modules spLoadModuleLibrary(spireContext, "hello.spire", spireSink); // Inspect any error messages that got reported... emitSpireDiagnostics(spireSink); // // Once the source Spire has been loaded, we can assemble the modules // there to make one or more shaders. In our case, we really only // have one shader that we will use, so this step is kind of redundant. // // Create a shader, which will initially be empty char const* shaderName = "HelloShader"; SpireShader* spireShader = spCreateShaderFromSource(spireContext, R"( template shader HelloShader(module0) targets StandardPipeline { using module0; } )", spireSink); SpireModule * helloModule = spFindModule(spireContext, "HelloModule"); // Compile the constructed shader SpireCompilationResult* spireResult = spCompileShader(spireContext, spireShader, &helloModule, 1, nullptr, spireSink); // Inspect any error messages that got reported... emitSpireDiagnostics(spireSink); // // Once we've compiled things successfully, we can extract the HLSL kernel // code for our shader, and pass it on to the D3D API. // // TODO(tfoley): The implementation should allow `NULL` for the length // output parameter. int sourceCodeLength; char const* vertexShaderCode = spGetShaderStageSource(spireResult, nullptr, "vs", &sourceCodeLength); char const* fragmentShaderCode = spGetShaderStageSource(spireResult, nullptr, "fs", &sourceCodeLength); // TODO(tfoley): Query the required constant-buffer size int constantBufferSize = 16 * sizeof(float); // Compile the generated HLSL code ID3DBlob* dxVertexShaderBlob = compileHLSLShader(vertexShaderCode, "vs_4_0"); if(!dxVertexShaderBlob) return E_FAIL; ID3DBlob* dxPixelShaderBlob = compileHLSLShader(fragmentShaderCode, "ps_4_0"); if(!dxPixelShaderBlob) return E_FAIL; HRESULT hr = S_OK; D3D11_BUFFER_DESC dxConstantBufferDesc = { 0 }; dxConstantBufferDesc.ByteWidth = constantBufferSize; dxConstantBufferDesc.Usage = D3D11_USAGE_DYNAMIC; dxConstantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; dxConstantBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; hr = dxDevice->CreateBuffer( &dxConstantBufferDesc, NULL, &dxConstantBuffer); if(FAILED(hr)) return hr; // We clean up the Spire compilation context and result *after* // we have done the HLSL-to-bytecode compilation, because Spire // owns the memory allocation for the generated HLSL, and will // free it when we destroy the compilation result. spDestroyCompilationResult(spireResult); spDestroyCompilationContext(spireContext); // Input Assembler (IA) // In Spire-generated HLSL, all vertex shader inputs have a semantic // like: `A0`, `A1`, `A2`, etc., rather than trying to do by-name // matching. The user is thus responsibile for ensuring that the // order of their "input element descs" here matches the order // in which inputs are declared in the shader code. D3D11_INPUT_ELEMENT_DESC dxInputElements[] = { {"A", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, position), D3D11_INPUT_PER_VERTEX_DATA, 0 }, {"A", 1, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, color), D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; hr = dxDevice->CreateInputLayout( &dxInputElements[0], 2, dxVertexShaderBlob->GetBufferPointer(), dxVertexShaderBlob->GetBufferSize(), &dxInputLayout); if(FAILED(hr)) return hr; D3D11_BUFFER_DESC dxVertexBufferDesc = { 0 }; dxVertexBufferDesc.ByteWidth = kVertexCount * sizeof(Vertex); dxVertexBufferDesc.Usage = D3D11_USAGE_IMMUTABLE; dxVertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; D3D11_SUBRESOURCE_DATA dxVertexBufferInitData = { 0 }; dxVertexBufferInitData.pSysMem = &kVertexData[0]; hr = dxDevice->CreateBuffer( &dxVertexBufferDesc, &dxVertexBufferInitData, &dxVertexBuffer); if(FAILED(hr)) return hr; // Vertex Shader (VS) hr = dxDevice->CreateVertexShader( dxVertexShaderBlob->GetBufferPointer(), dxVertexShaderBlob->GetBufferSize(), NULL, &dxVertexShader); dxVertexShaderBlob->Release(); if(FAILED(hr)) return hr; // Pixel Shader (PS) hr = dxDevice->CreatePixelShader( dxPixelShaderBlob->GetBufferPointer(), dxPixelShaderBlob->GetBufferSize(), NULL, &dxPixelShader); dxPixelShaderBlob->Release(); if(FAILED(hr)) return hr; return S_OK; } void emitSpireDiagnostics( SpireDiagnosticSink* spireSink) { int diagnosticCount = spGetDiagnosticCount(spireSink); for(int jj = 0; jj < diagnosticCount; ++jj) { SpireDiagnostic diagnostic; spGetDiagnosticByIndex(spireSink, jj, &diagnostic); static const char* kSeverityNames[] = { "note", "warning", "error", "fatal error", "internal error", }; static const int kBufferSize = 1024; char buffer[kBufferSize]; snprintf(buffer, kBufferSize, "%s(%d:%d): %s %d: %s\n", diagnostic.FileName, diagnostic.Line, diagnostic.Col, kSeverityNames[diagnostic.severity], diagnostic.ErrorId, diagnostic.Message); OutputDebugStringA(buffer); } } void renderFrame(ID3D11DeviceContext* dxContext) { // We update our constant buffer per-frame, just for the purposes // of the example, but we don't actually load different data // per-frame (we always use an identity projection). D3D11_MAPPED_SUBRESOURCE mapped; HRESULT hr = dxContext->Map(dxConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); if(!FAILED(hr)) { float* data = (float*) mapped.pData; static const float kIdentity[] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }; memcpy(data, kIdentity, sizeof(kIdentity)); dxContext->Unmap(dxConstantBuffer, 0); } // Input Assembler (IA) dxContext->IASetInputLayout(dxInputLayout); dxContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); UINT dxVertexStride = sizeof(Vertex); UINT dxVertexBufferOffset = 0; dxContext->IASetVertexBuffers(0, 1, &dxVertexBuffer, &dxVertexStride, &dxVertexBufferOffset); // Vertex Shader (VS) dxContext->VSSetShader(dxVertexShader, NULL, 0); dxContext->VSSetConstantBuffers(0, 1, &dxConstantBuffer); // Pixel Shader (PS) dxContext->PSSetShader(dxPixelShader, NULL, 0); dxContext->VSSetConstantBuffers(0, 1, &dxConstantBuffer); // dxContext->Draw(3, 0); } void finalize() { } // // Definition of the HLSL-to-bytecode compilation logic. // ID3DBlob* compileHLSLShader( char const* source, char const* dxProfileName ) { // Rather than statically link against the `d3dcompile` library, we // dynamically load it. // // Note: A more realistic application would compile from HLSL text to D3D // shader bytecode as part of an offline process, rather than doing it // on-the-fly like this // static pD3DCompile D3DCompile_ = nullptr; if( !D3DCompile_ ) { // TODO(tfoley): maybe want to search for one of a few versions of the DLL HMODULE d3dcompiler = LoadLibraryA("d3dcompiler_47.dll"); if(!d3dcompiler) { fprintf(stderr, "error: failed load 'd3dcompiler_47.dll'\n"); exit(1); } D3DCompile_ = (pD3DCompile)GetProcAddress(d3dcompiler, "D3DCompile"); if( !D3DCompile_ ) { fprintf(stderr, "error: failed load symbol 'D3DCompile'\n"); exit(1); } } // For this example, we turn on debug output, and turn off all // optimization. A real application would only use these flags // when shader debugging is needed. UINT flags = 0; flags |= D3DCOMPILE_DEBUG; flags |= D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_SKIP_OPTIMIZATION; // The `D3DCompile` entry point takes a bunch of parameters, but we // don't really need most of them for Spire-generated code. ID3DBlob* dxShaderBlob = nullptr; ID3DBlob* dxErrorBlob = nullptr; HRESULT hr = D3DCompile_( source, strlen(source), "spireGeneratedCode", // TODO: proper path for error messages nullptr, nullptr, "main", dxProfileName, flags, 0, &dxShaderBlob, &dxErrorBlob); // If the HLSL-to-bytecode compilation produced any diagnostic messages // then we will print them out (whether or not the compilation failed). if( dxErrorBlob ) { OutputDebugStringA( (char const*)dxErrorBlob->GetBufferPointer()); dxErrorBlob->Release(); } if( FAILED(hr) ) { return nullptr; } return dxShaderBlob; } // // We use a bare-minimum window procedure to get things up and running. // static LRESULT CALLBACK windowProc( HWND windowHandle, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CLOSE: PostQuitMessage(0); return 0; } return DefWindowProcW(windowHandle, message, wParam, lParam); } // // Our `WinMain` handles the basic task of getting a window and rendering // context up and running. There should be nothing suprising or interesting // here. // int WINAPI WinMain( HINSTANCE instance, HINSTANCE /* prevInstance */, LPSTR /* commandLine */, int showCommand) { // First we register a window class. WNDCLASSEXW windowClassDesc; windowClassDesc.cbSize = sizeof(windowClassDesc); windowClassDesc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; windowClassDesc.lpfnWndProc = &windowProc; windowClassDesc.cbClsExtra = 0; windowClassDesc.cbWndExtra = 0; windowClassDesc.hInstance = instance; windowClassDesc.hIcon = 0; windowClassDesc.hCursor = 0; windowClassDesc.hbrBackground = 0; windowClassDesc.lpszMenuName = 0; windowClassDesc.lpszClassName = L"HelloWorld"; windowClassDesc.hIconSm = 0; ATOM windowClassAtom = RegisterClassExW(&windowClassDesc); if(!windowClassAtom) { fprintf(stderr, "error: failed to register window class\n"); return 1; } // Next, we create a window using that window class. DWORD windowExtendedStyle = 0; DWORD windowStyle = 0; LPWSTR windowName = L"Spire Hello World"; HWND windowHandle = CreateWindowExW( windowExtendedStyle, (LPWSTR)windowClassAtom, windowName, windowStyle, 0, 0, // x, y gWindowWidth, gWindowHeight, NULL, // parent NULL, // menu instance, NULL); if(!windowHandle) { fprintf(stderr, "error: failed to create window\n"); return 1; } // Rather than statically link against D3D, we load it dynamically. HMODULE d3d11 = LoadLibraryA("d3d11.dll"); if(!d3d11) { fprintf(stderr, "error: failed load 'd3d11.dll'\n"); return 1; } PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN D3D11CreateDeviceAndSwapChain_ = (PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN)GetProcAddress( d3d11, "D3D11CreateDeviceAndSwapChain"); if(!D3D11CreateDeviceAndSwapChain_) { fprintf(stderr, "error: failed load symbol 'D3D11CreateDeviceAndSwapChain'\n"); return 1; } // We create our device in debug mode, just so that we can check that the // example doesn't trigger warnings. UINT deviceFlags = 0; deviceFlags |= D3D11_CREATE_DEVICE_DEBUG; // We will ask for the highest feature level that can be supported. D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1, }; D3D_FEATURE_LEVEL dxFeatureLevel = D3D_FEATURE_LEVEL_9_1; // Our swap chain uses RGBA8 with sRGB, with double buffering. DXGI_SWAP_CHAIN_DESC dxSwapChainDesc = { 0 }; dxSwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; dxSwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; dxSwapChainDesc.SampleDesc.Count = 1; dxSwapChainDesc.SampleDesc.Quality = 0; dxSwapChainDesc.BufferCount = 2; dxSwapChainDesc.OutputWindow = windowHandle; dxSwapChainDesc.Windowed = TRUE; dxSwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; dxSwapChainDesc.Flags = 0; // On a machine that does not have an up-to-date version of D3D installed, // the `D3D11CreateDeviceAndSwapChain` call will fail with `E_INVALIDARG` // if you ask for featuer level 11_1. The workaround is to call // `D3D11CreateDeviceAndSwapChain` up to twice: the first time with 11_1 // at the start of the list of requested feature levels, and the second // time without it. IDXGISwapChain* dxSwapChain = NULL; ID3D11Device* dxDevice = NULL; ID3D11DeviceContext* dxImmediateContext = NULL; HRESULT hr = S_OK; for( int ii = 0; ii < 2; ++ii ) { hr = D3D11CreateDeviceAndSwapChain_( NULL, // adapter (use default) D3D_DRIVER_TYPE_HARDWARE, NULL, // software deviceFlags, &featureLevels[ii], (sizeof(featureLevels) / sizeof(featureLevels[0])) - 1, D3D11_SDK_VERSION, &dxSwapChainDesc, &dxSwapChain, &dxDevice, &dxFeatureLevel, &dxImmediateContext); // Failures with `E_INVALIDARG` might be due to feature level 11_1 // not being supported. Other failures are real, though. if( hr != E_INVALIDARG ) break; } if( FAILED(hr) ) { return 1; } // After we've created the swap chain, we can request a pointer to the // back buffer as a D3D11 texture, and create a render-target view from it. ID3D11Texture2D* dxBackBufferTexture = NULL; static const IID kIID_ID3D11Texture2D = { 0x6f15aaf2, 0xd208, 0x4e89, 0x9a, 0xb4, 0x48, 0x95, 0x35, 0xd3, 0x4f, 0x9c }; dxSwapChain->GetBuffer( 0, kIID_ID3D11Texture2D, (void**)&dxBackBufferTexture); ID3D11RenderTargetView* dxBackBufferRTV = NULL; dxDevice->CreateRenderTargetView( dxBackBufferTexture, NULL, &dxBackBufferRTV); // We immediately bind the back-buffer render target view, and we aren't // going to switch. We don't bother with a depth buffer. dxImmediateContext->OMSetRenderTargets( 1, &dxBackBufferRTV, NULL); // Similarly, we are going to set up a viewport once, and then never // switch, since this is a simple test app. D3D11_VIEWPORT dxViewport; dxViewport.TopLeftX = 0; dxViewport.TopLeftY = 0; dxViewport.Width = (float) gWindowWidth; dxViewport.Height = (float) gWindowHeight; dxViewport.MaxDepth = 1; // TODO(tfoley): use reversed depth dxViewport.MinDepth = 0; dxImmediateContext->RSSetViewports(1, &dxViewport); // Once we've done the general-purpose initialization, we // initialize anything specific to the "hello world" application initialize( dxDevice ); // Once initialization is all complete, we show the window... ShowWindow(windowHandle, showCommand); // ... and enter the event loop: for(;;) { MSG message; int result = PeekMessageW(&message, NULL, 0, 0, PM_REMOVE); if (result != 0) { if (message.message == WM_QUIT) { return (int)message.wParam; } TranslateMessage(&message); DispatchMessageW(&message); } else { // Whenver we don't have Windows events to process, // we render a frame. static const float kClearColor[] = { 0.25, 0.25, 0.25, 1.0 }; dxImmediateContext->ClearRenderTargetView( dxBackBufferRTV, kClearColor); renderFrame( dxImmediateContext ); dxSwapChain->Present(0, 0); } } return 0; } // // In order to actually use Spire in our application, we need to link in its // implementation. The easiest way to accomplish this is by directly inlcuding // the (concatenated) Spire source code into our app. // #include ================================================ FILE: Examples/hello/hello.sln ================================================ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hello", "hello.vcxproj", "{E6385042-1649-4803-9EBD-168F8B7EF131}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug|x64.ActiveCfg = Debug|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug|x64.Build.0 = Debug|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug|x86.ActiveCfg = Debug|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug|x86.Build.0 = Debug|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release|x64.ActiveCfg = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release|x64.Build.0 = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release|x86.ActiveCfg = Release|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: Examples/hello/hello.spire ================================================ // shaders.spire // TODO(tfoley): strip this down to a minimal pipeline pipeline StandardPipeline { [Pinned] input world MeshVertex; world CoarseVertex;// : "glsl(vertex:projCoord)" using projCoord export standardExport; world Fragment;// : "glsl" export fragmentExport; require @CoarseVertex vec4 projCoord; [VertexInput] extern @CoarseVertex MeshVertex vertAttribIn; import(MeshVertex->CoarseVertex) vertexImport() { return project(vertAttribIn); } extern @Fragment CoarseVertex CoarseVertexIn; import(CoarseVertex->Fragment) standardImport() // TODO(tfoley): this trait doesn't seem to be implemented on `vec3` // require trait IsTriviallyPassable(CoarseVertex) { return project(CoarseVertexIn); } stage vs : VertexShader { World: CoarseVertex; Position: projCoord; } stage fs : FragmentShader { World: Fragment; } } module HelloModule { @MeshVertex vec3 position; @MeshVertex vec3 color; param mat4 modelViewProjection; public vec4 projCoord = modelViewProjection * vec4(position, 1.0); out @Fragment vec4 colorTarget = vec4(color,1); } ================================================ FILE: Examples/hello/hello.vcxproj ================================================  Debug_VS2013 Win32 Debug_VS2013 x64 Debug Win32 Release_VS2013 Win32 Release_VS2013 x64 Release Win32 Debug x64 Release x64 {E6385042-1649-4803-9EBD-168F8B7EF131} Win32Proj hello 8.1 Application true v140 Unicode Application true v140 Unicode Application false v140 true Unicode Application false v140 true Unicode Application true v140 Unicode Application true v120 Unicode Application false v140 true Unicode Application false v120 true Unicode true $(ProjectDir)../../;$(IncludePath) true $(ProjectDir)../../;$(IncludePath) true $(ProjectDir)../../;$(IncludePath) true $(ProjectDir)../../;$(IncludePath) false $(ProjectDir)../../;$(IncludePath) false $(ProjectDir)../../;$(IncludePath) false $(ProjectDir)../../;$(IncludePath) false $(ProjectDir)../../;$(IncludePath) Level3 Disabled WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) Windows true Level3 Disabled WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) Windows true Level3 Disabled _DEBUG;_WINDOWS;%(PreprocessorDefinitions) Windows true Level3 Disabled _DEBUG;_WINDOWS;%(PreprocessorDefinitions) MultiThreadedDebug Windows true Level3 MaxSpeed true true WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) Windows true true true Level3 MaxSpeed true true WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) Windows true true true Level3 MaxSpeed true true NDEBUG;_WINDOWS;%(PreprocessorDefinitions) Windows true true true Level3 MaxSpeed true true NDEBUG;_WINDOWS;%(PreprocessorDefinitions) Windows true true true ================================================ FILE: Examples/hello/hello.vcxproj.filters ================================================  ================================================ FILE: LICENSE.txt ================================================ Spire - The MIT License (MIT) Copyright (c) 2016, Carnegie Mellon University Developers: Yong He, Haomin Long, Teguh Hofstee Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Spire 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. For an example of intergrating Spire into a game engine, head to this repository: https://github.com/csyonghe/GameEngine 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. # Publications [Shader Components: Modular and High Performance Shader Development](http://graphics.cs.cmu.edu/projects/shadercomp/) SIGGRAPH 2017 [A System for Rapid Exploration of Shader Optimization Choices](http://graphics.cs.cmu.edu/projects/spire/) SIGGRAPH 2016 ================================================ FILE: Source/CoreLib/Allocator.h ================================================ #ifndef CORE_LIB_ALLOCATOR_H #define CORE_LIB_ALLOCATOR_H #include namespace CoreLib { namespace Basic { inline void * AlignedAlloc(size_t size, size_t alignment) { #ifdef _MSC_VER return _aligned_malloc(size, alignment); #else void * rs = 0; int succ = posix_memalign(&rs, alignment, size); if (succ!=0) rs = 0; return rs; #endif } inline void AlignedFree(void * ptr) { #ifdef _MSC_VER _aligned_free(ptr); #else free(ptr); #endif } class StandardAllocator { public: // not really called void * Alloc(size_t size) { return malloc(size); } void Free(void * ptr) { return free(ptr); } }; template class AlignedAllocator { public: void * Alloc(size_t size) { return AlignedAlloc(size, alignment); } void Free(void * ptr) { return AlignedFree(ptr); } }; } } #endif ================================================ FILE: Source/CoreLib/Array.h ================================================ #ifndef CORE_LIB_ARRAY_H #define CORE_LIB_ARRAY_H #include "Exception.h" #include "ArrayView.h" namespace CoreLib { namespace Basic { template class Array { private: T _buffer[size]; int _count = 0; public: T* begin() const { return (T*)_buffer; } T* end() const { return (T*)_buffer + _count; } public: inline int GetCapacity() const { return size; } inline int Count() const { return _count; } inline T & First() const { return const_cast(_buffer[0]); } inline T & Last() const { return const_cast(_buffer[_count - 1]); } inline void SetSize(int newSize) { #ifdef _DEBUG if (newSize > size) throw IndexOutofRangeException("size too large."); #endif _count = newSize; } inline void Add(const T & item) { #ifdef _DEBUG if (_count == size) throw IndexOutofRangeException("out of range access to static array."); #endif _buffer[_count++] = item; } inline void Add(T && item) { #ifdef _DEBUG if (_count == size) throw IndexOutofRangeException("out of range access to static array."); #endif _buffer[_count++] = _Move(item); } inline T & operator [](int id) const { #if _DEBUG if (id >= _count || id < 0) throw IndexOutofRangeException("Operator[]: Index out of Range."); #endif return ((T*)_buffer)[id]; } inline T* Buffer() const { return (T*)_buffer; } inline void Clear() { _count = 0; } template int IndexOf(const T2 & val) const { for (int i = 0; i < _count; i++) { if (_buffer[i] == val) return i; } return -1; } template int LastIndexOf(const T2 & val) const { for (int i = _count - 1; i >= 0; i--) { if (_buffer[i] == val) return i; } return -1; } inline ArrayView GetArrayView() const { return ArrayView((T*)_buffer, _count); } inline ArrayView GetArrayView(int start, int count) const { return ArrayView((T*)_buffer + start, count); } }; template struct FirstType { typedef T type; }; template void InsertArray(Array &) {} template void InsertArray(Array & arr, const T & val, TArgs... args) { arr.Add(val); InsertArray(arr, args...); } template auto MakeArray(TArgs ...args) { Array::type, sizeof...(args)> rs; InsertArray(rs, args...); return rs; } } } #endif ================================================ FILE: Source/CoreLib/ArrayView.h ================================================ #ifndef CORE_LIB_ARRAY_VIEW_H #define CORE_LIB_ARRAY_VIEW_H #include "Exception.h" namespace CoreLib { namespace Basic { template class ArrayView { private: T * _buffer; int _count; int stride; public: T* begin() const { return _buffer; } T* end() const { return (T*)((char*)_buffer + _count*stride); } public: ArrayView() { _buffer = 0; _count = 0; } ArrayView(const T & singleObj) { SetData((T*)&singleObj, 1, sizeof(T)); } ArrayView(T * buffer, int count) { SetData(buffer, count, sizeof(T)); } ArrayView(void * buffer, int count, int _stride) { SetData(buffer, count, _stride); } void SetData(void * buffer, int count, int _stride) { this->_buffer = (T*)buffer; this->_count = count; this->stride = _stride; } inline int GetCapacity() const { return _count; } inline int Count() const { return _count; } inline T & operator [](int id) const { #if _DEBUG if (id >= _count || id < 0) throw IndexOutofRangeException("Operator[]: Index out of Range."); #endif return *(T*)((char*)_buffer+id*stride); } inline T* Buffer() const { return _buffer; } template int IndexOf(const T2 & val) const { for (int i = 0; i < _count; i++) { if (*(T*)((char*)_buffer + i*stride) == val) return i; } return -1; } template int LastIndexOf(const T2 & val) const { for (int i = _count - 1; i >= 0; i--) { if (*(T*)((char*)_buffer + i*stride) == val) return i; } return -1; } template int FindFirst(const Func & predicate) const { for (int i = 0; i < _count; i++) { if (predicate(_buffer[i])) return i; } return -1; } template int FindLast(const Func & predicate) const { for (int i = _count - 1; i >= 0; i--) { if (predicate(_buffer[i])) return i; } return -1; } }; template ArrayView MakeArrayView(const T & obj) { return ArrayView(obj); } template ArrayView MakeArrayView(T * buffer, int count) { return ArrayView(buffer, count); } } } #endif ================================================ FILE: Source/CoreLib/Basic.h ================================================ #ifndef CORE_LIB_BASIC_H #define CORE_LIB_BASIC_H #include "Common.h" #include "LibMath.h" #include "LibString.h" #include "Array.h" #include "List.h" #include "Link.h" #include "SmartPointer.h" #include "Exception.h" #include "Dictionary.h" #include "Func.h" #include "Linq.h" namespace CoreLib { using namespace Basic; } #endif ================================================ FILE: Source/CoreLib/CMakeLists.txt ================================================ cmake_minimum_required (VERSION 2.6) project (CoreLib) add_library(CoreLib_Basic STATIC Basic.h Common.h Dictionary.h Exception.h IntSet.h LibIO.cpp LibIO.h LibMath.cpp LibMath.h LibString.cpp LibString.h Link.h List.h Parser.cpp Parser.h PerformanceCounter.cpp PerformanceCounter.h SmartPointer.h Stream.cpp Stream.h TextIO.cpp TextIO.h Threading.h VectorMath.cpp VectorMath.h WideChar.cpp WideChar.h SecureCRT.h ) add_subdirectory (Graphics) add_subdirectory (Imaging) add_subdirectory (Regex) ================================================ FILE: Source/CoreLib/CommandLineParser.cpp ================================================ #include "CommandLineParser.h" namespace CoreLib { namespace Text { CommandLineParser::CommandLineParser(const String & cmdLine) { stream = Split(cmdLine, L' '); } String CommandLineParser::GetFileName() { if (stream.Count()) return stream.First(); else return ""; } bool CommandLineParser::OptionExists(const String & opt) { for (auto & token : stream) { if (token.Equals(opt, false)) { return true; } } return false; } String CommandLineParser::GetOptionValue(const String & opt) { for (int i = 0; i < stream.Count(); i++) { if (stream[i].Equals(opt, false)) { if (i < stream.Count() - 1) return stream[i+1]; return ""; } } return ""; } String CommandLineParser::GetToken(int id) { return stream[id]; } int CommandLineParser::GetTokenCount() { return stream.Count(); } } } ================================================ FILE: Source/CoreLib/CommandLineParser.h ================================================ #ifndef CORE_LIB_COMMANDLINE_PARSER #define CORE_LIB_COMMANDLINE_PARSER #include "Tokenizer.h" namespace CoreLib { namespace Text { class CommandLineParser : public Object { private: List stream; public: CommandLineParser(const String & cmdLine); String GetFileName(); bool OptionExists(const String & opt); String GetOptionValue(const String & opt); String GetToken(int id); int GetTokenCount(); }; } } #endif ================================================ FILE: Source/CoreLib/Common.h ================================================ #ifndef CORE_LIB_COMMON_H #define CORE_LIB_COMMON_H #include #ifdef __GNUC__ #define CORE_LIB_ALIGN_16(x) x __attribute__((aligned(16))) #else #define CORE_LIB_ALIGN_16(x) __declspec(align(16)) x #endif #define VARIADIC_TEMPLATE namespace CoreLib { typedef int64_t Int64; typedef unsigned short Word; #ifdef _M_X64 typedef int64_t PtrInt; #else typedef int PtrInt; #endif namespace Basic { class Object { public: virtual ~Object() {} }; template inline T&& _Move(T & obj) { return static_cast(obj); } template inline void Swap(T & v0, T & v1) { T tmp = _Move(v0); v0 = _Move(v1); v1 = _Move(tmp); } } } #endif ================================================ FILE: Source/CoreLib/CoreLibBasic.vcxproj ================================================ DebugClang ARM DebugClang Win32 DebugClang x64 Debug_VS2013 ARM Debug_VS2013 Win32 Debug_VS2013 x64 Debug ARM Debug Win32 Debug x64 Release_VS2013 ARM Release_VS2013 Win32 Release_VS2013 x64 Release ARM Release Win32 Release x64 TracingDebug ARM TracingDebug Win32 TracingDebug x64 TracingRelease ARM TracingRelease Win32 TracingRelease x64 Win32Proj CoreLib {F9BE7957-8399-899E-0C49-E714FDDD4B65} 8.1 StaticLibrary true v140 Unicode StaticLibrary true v120 Unicode StaticLibrary true v140_clang_3_7 Unicode StaticLibrary true v140 Unicode StaticLibrary true v140 Unicode StaticLibrary true v120 Unicode StaticLibrary true v140_Clang_3_7 Unicode StaticLibrary true v140 Unicode StaticLibrary true v140 Unicode StaticLibrary true v120 Unicode StaticLibrary true v140_clang_3_7 Unicode StaticLibrary true v140 Unicode StaticLibrary false v140 true Unicode StaticLibrary false v120 true Unicode StaticLibrary false v140 true Unicode StaticLibrary false v140 true Unicode StaticLibrary false v120 true Unicode StaticLibrary false v140 true Unicode StaticLibrary false v140 true Unicode StaticLibrary false v120 true Unicode StaticLibrary false v140 true Unicode Level4 Disabled WIN32;_DEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreadedDebug false Default Windows true Shlwapi.lib Level4 Disabled WIN32;_DEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreadedDebug false Default Windows true Shlwapi.lib EnableAllWarnings Disabled WIN32;_DEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreadedDebug false Default true Windows true Shlwapi.lib Level4 Disabled WIN32;_DEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreadedDebug false Windows true Shlwapi.lib Level4 Disabled WIN32;_DEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreadedDebug true false Default Windows true Shlwapi.lib true Level4 Disabled WIN32;_DEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreadedDebug true false Default Windows true Shlwapi.lib true Level4 Disabled WIN32;_DEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreadedDebug true false Default Windows true Shlwapi.lib true Level4 Disabled WIN32;_DEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreadedDebug false Windows true Shlwapi.lib Level4 Disabled WIN32;_DEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreadedDebug false Default Windows true Shlwapi.lib Level4 Disabled WIN32;_DEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreadedDebug false Default Windows true Shlwapi.lib Level4 Disabled WIN32;_DEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreadedDebug false Default Windows true Shlwapi.lib Level4 Disabled WIN32;_DEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreadedDebug false Windows true Shlwapi.lib Level4 MaxSpeed true true WIN32;NDEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreaded false Windows true true true Shlwapi.lib Level4 MaxSpeed true true WIN32;NDEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreaded false Windows true true true Shlwapi.lib Level4 MaxSpeed true true WIN32;NDEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreaded false Windows true true true Shlwapi.lib Level4 MaxSpeed true true WIN32;NDEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreaded false Windows true true true Shlwapi.lib Level4 MaxSpeed true true WIN32;NDEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreaded false Windows true true true Shlwapi.lib Level4 MaxSpeed true true WIN32;NDEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreaded false false Windows true true true Shlwapi.lib Level4 MaxSpeed true true WIN32;NDEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreaded false Windows true true true Shlwapi.lib Level4 MaxSpeed true true WIN32;NDEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreaded false Windows true true true Shlwapi.lib Level4 MaxSpeed true true WIN32;NDEBUG;_LIB;WINDOWS_PLATFORM;%(PreprocessorDefinitions);GLEW_STATIC MultiThreaded false Windows true true true Shlwapi.lib ================================================ FILE: Source/CoreLib/Dictionary.h ================================================ #ifndef CORE_LIB_DICTIONARY_H #define CORE_LIB_DICTIONARY_H #include "List.h" #include "Common.h" #include "IntSet.h" #include "Exception.h" #include "LibMath.h" #include "Hash.h" namespace CoreLib { namespace Basic { template class KeyValuePair { public: TKey Key; TValue Value; KeyValuePair() {} KeyValuePair(const TKey & key, const TValue & value) { Key = key; Value = value; } KeyValuePair(TKey && key, TValue && value) { Key = _Move(key); Value = _Move(value); } KeyValuePair(TKey && key, const TValue & value) { Key = _Move(key); Value = value; } KeyValuePair(const KeyValuePair & _that) { Key = _that.Key; Value = _that.Value; } KeyValuePair(KeyValuePair && _that) { operator=(_Move(_that)); } KeyValuePair & operator=(KeyValuePair && that) { Key = _Move(that.Key); Value = _Move(that.Value); return *this; } KeyValuePair & operator=(const KeyValuePair & that) { Key = that.Key; Value = that.Value; return *this; } int GetHashCode() { return GetHashCode(Key); } }; template inline KeyValuePair KVPair(const TKey & k, const TValue & v) { return KeyValuePair(k, v); } const float MaxLoadFactor = 0.7f; template class Dictionary { friend class Iterator; friend class ItemProxy; private: inline int GetProbeOffset(int /*probeId*/) const { // quadratic probing return 1; } private: int bucketSizeMinusOne, shiftBits; int _count; IntSet marks; KeyValuePair* hashMap; void Free() { if (hashMap) delete[] hashMap; hashMap = 0; } inline bool IsDeleted(int pos) const { return marks.Contains((pos << 1) + 1); } inline bool IsEmpty(int pos) const { return !marks.Contains((pos << 1)); } inline void SetDeleted(int pos, bool val) { if (val) marks.Add((pos << 1) + 1); else marks.Remove((pos << 1) + 1); } inline void SetEmpty(int pos, bool val) { if (val) marks.Remove((pos << 1)); else marks.Add((pos << 1)); } struct FindPositionResult { int ObjectPosition; int InsertionPosition; FindPositionResult() { ObjectPosition = -1; InsertionPosition = -1; } FindPositionResult(int objPos, int insertPos) { ObjectPosition = objPos; InsertionPosition = insertPos; } }; template inline int GetHashPos(T & key) const { return ((unsigned int)(GetHashCode(key) * 2654435761)) >> shiftBits; } template FindPositionResult FindPosition(const T & key) const { int hashPos = GetHashPos((T&)key); int insertPos = -1; int numProbes = 0; while (numProbes <= bucketSizeMinusOne) { if (IsEmpty(hashPos)) { if (insertPos == -1) return FindPositionResult(-1, hashPos); else return FindPositionResult(-1, insertPos); } else if (IsDeleted(hashPos)) { if (insertPos == -1) insertPos = hashPos; } else if (hashMap[hashPos].Key == key) { return FindPositionResult(hashPos, -1); } numProbes++; hashPos = (hashPos + GetProbeOffset(numProbes)) & bucketSizeMinusOne; } if (insertPos != -1) return FindPositionResult(-1, insertPos); throw InvalidOperationException("Hash map is full. This indicates an error in Key::Equal or Key::GetHashCode."); } TValue & _Insert(KeyValuePair && kvPair, int pos) { hashMap[pos] = _Move(kvPair); SetEmpty(pos, false); SetDeleted(pos, false); return hashMap[pos].Value; } void Rehash() { if (bucketSizeMinusOne == -1 || _count / (float)bucketSizeMinusOne >= MaxLoadFactor) { int newSize = (bucketSizeMinusOne + 1) * 2; int newShiftBits = shiftBits - 1; if (newSize == 0) { newSize = 16; newShiftBits = 28; } Dictionary newDict; newDict.shiftBits = newShiftBits; newDict.bucketSizeMinusOne = newSize - 1; newDict.hashMap = new KeyValuePair[newSize]; newDict.marks.SetMax(newSize * 2); if (hashMap) { for (auto & kvPair : *this) { newDict.Add(_Move(kvPair)); } } *this = _Move(newDict); } } bool AddIfNotExists(KeyValuePair && kvPair) { Rehash(); auto pos = FindPosition(kvPair.Key); if (pos.ObjectPosition != -1) return false; else if (pos.InsertionPosition != -1) { _count++; _Insert(_Move(kvPair), pos.InsertionPosition); return true; } else throw InvalidOperationException("Inconsistent find result returned. This is a bug in Dictionary implementation."); } void Add(KeyValuePair && kvPair) { if (!AddIfNotExists(_Move(kvPair))) throw KeyExistsException("The key already exists in Dictionary."); } TValue & Set(KeyValuePair && kvPair) { Rehash(); auto pos = FindPosition(kvPair.Key); if (pos.ObjectPosition != -1) return _Insert(_Move(kvPair), pos.ObjectPosition); else if (pos.InsertionPosition != -1) { _count++; return _Insert(_Move(kvPair), pos.InsertionPosition); } else throw InvalidOperationException("Inconsistent find result returned. This is a bug in Dictionary implementation."); } public: class Iterator { private: const Dictionary * dict; int pos; public: KeyValuePair & operator *() const { return dict->hashMap[pos]; } KeyValuePair * operator ->() const { return dict->hashMap + pos; } Iterator & operator ++() { if (pos > dict->bucketSizeMinusOne) return *this; pos++; while (pos <= dict->bucketSizeMinusOne && (dict->IsDeleted(pos) || dict->IsEmpty(pos))) { pos++; } return *this; } Iterator operator ++(int) { Iterator rs = *this; operator++(); return rs; } bool operator != (const Iterator & _that) const { return pos != _that.pos || dict != _that.dict; } bool operator == (const Iterator & _that) const { return pos == _that.pos && dict == _that.dict; } Iterator(const Dictionary * _dict, int _pos) { this->dict = _dict; this->pos = _pos; } Iterator() { this->dict = 0; this->pos = 0; } }; Iterator begin() const { int pos = 0; while (pos < bucketSizeMinusOne + 1) { if (IsEmpty(pos) || IsDeleted(pos)) pos++; else break; } return Iterator(this, pos); } Iterator end() const { return Iterator(this, bucketSizeMinusOne + 1); } public: void Add(const TKey & key, const TValue & value) { Add(KeyValuePair(key, value)); } void Add(TKey && key, TValue && value) { Add(KeyValuePair(_Move(key), _Move(value))); } bool AddIfNotExists(const TKey & key, const TValue & value) { return AddIfNotExists(KeyValuePair(key, value)); } bool AddIfNotExists(TKey && key, TValue && value) { return AddIfNotExists(KeyValuePair(_Move(key), _Move(value))); } void Remove(const TKey & key) { if (_count == 0) return; auto pos = FindPosition(key); if (pos.ObjectPosition != -1) { SetDeleted(pos.ObjectPosition, true); _count--; } } void Clear() { _count = 0; marks.Clear(); } template bool ContainsKey(const T & key) const { if (bucketSizeMinusOne == -1) return false; auto pos = FindPosition(key); return pos.ObjectPosition != -1; } template bool TryGetValue(const T & key, TValue & value) const { if (bucketSizeMinusOne == -1) return false; auto pos = FindPosition(key); if (pos.ObjectPosition != -1) { value = hashMap[pos.ObjectPosition].Value; return true; } return false; } template TValue * TryGetValue(const T & key) const { if (bucketSizeMinusOne == -1) return nullptr; auto pos = FindPosition(key); if (pos.ObjectPosition != -1) { return &hashMap[pos.ObjectPosition].Value; } return nullptr; } class ItemProxy { private: const Dictionary * dict; TKey key; public: ItemProxy(const TKey & _key, const Dictionary * _dict) { this->dict = _dict; this->key = _key; } ItemProxy(TKey && _key, const Dictionary * _dict) { this->dict = _dict; this->key = _Move(_key); } TValue & GetValue() const { auto pos = dict->FindPosition(key); if (pos.ObjectPosition != -1) { return dict->hashMap[pos.ObjectPosition].Value; } else throw KeyNotFoundException("The key does not exists in dictionary."); } inline TValue & operator()() const { return GetValue(); } operator TValue&() const { return GetValue(); } TValue & operator = (const TValue & val) const { return ((Dictionary*)dict)->Set(KeyValuePair(_Move(key), val)); } TValue & operator = (TValue && val) const { return ((Dictionary*)dict)->Set(KeyValuePair(_Move(key), _Move(val))); } }; ItemProxy operator [](const TKey & key) const { return ItemProxy(key, this); } ItemProxy operator [](TKey && key) const { return ItemProxy(_Move(key), this); } int Count() const { return _count; } private: template void Init(const KeyValuePair & kvPair, Args... args) { Add(kvPair); Init(args...); } public: Dictionary() { bucketSizeMinusOne = -1; shiftBits = 32; _count = 0; hashMap = 0; } template Dictionary(Arg arg, Args... args) { Init(arg, args...); } Dictionary(const Dictionary & other) : bucketSizeMinusOne(-1), _count(0), hashMap(0) { *this = other; } Dictionary(Dictionary && other) : bucketSizeMinusOne(-1), _count(0), hashMap(0) { *this = (_Move(other)); } Dictionary & operator = (const Dictionary & other) { if (this == &other) return *this; Free(); bucketSizeMinusOne = other.bucketSizeMinusOne; _count = other._count; shiftBits = other.shiftBits; hashMap = new KeyValuePair[other.bucketSizeMinusOne + 1]; marks = other.marks; for (int i = 0; i <= bucketSizeMinusOne; i++) hashMap[i] = other.hashMap[i]; return *this; } Dictionary & operator = (Dictionary && other) { if (this == &other) return *this; Free(); bucketSizeMinusOne = other.bucketSizeMinusOne; _count = other._count; hashMap = other.hashMap; shiftBits = other.shiftBits; marks = _Move(other.marks); other.hashMap = 0; other._count = 0; other.bucketSizeMinusOne = -1; return *this; } ~Dictionary() { Free(); } }; template class EnumerableDictionary { friend class Iterator; friend class ItemProxy; private: inline int GetProbeOffset(int /*probeIdx*/) const { // quadratic probing return 1; } private: int bucketSizeMinusOne, shiftBits; int _count; IntSet marks; // debug op struct Op { TKey key; int opType; Op() {} Op(const TKey & key, int t) { this->key = key; opType = t; } }; LinkedList> kvPairs; LinkedNode>** hashMap; void Free() { if (hashMap) delete[] hashMap; hashMap = 0; kvPairs.Clear(); } inline bool IsDeleted(int pos) const { return marks.Contains((pos << 1) + 1); } inline bool IsEmpty(int pos) const { return !marks.Contains((pos << 1)); } inline void SetDeleted(int pos, bool val) { if (val) marks.Add((pos << 1) + 1); else marks.Remove((pos << 1) + 1); } inline void SetEmpty(int pos, bool val) { if (val) marks.Remove((pos << 1)); else marks.Add((pos << 1)); } struct FindPositionResult { int ObjectPosition; int InsertionPosition; FindPositionResult() { ObjectPosition = -1; InsertionPosition = -1; } FindPositionResult(int objPos, int insertPos) { ObjectPosition = objPos; InsertionPosition = insertPos; } }; template inline int GetHashPos(T & key) const { return ((unsigned int)(GetHashCode(key) * 2654435761)) >> shiftBits; } template FindPositionResult FindPosition(const T & key) const { int hashPos = GetHashPos((T&)key); int insertPos = -1; int numProbes = 0; while (numProbes <= bucketSizeMinusOne) { if (IsEmpty(hashPos)) { if (insertPos == -1) return FindPositionResult(-1, hashPos); else return FindPositionResult(-1, insertPos); } else if (IsDeleted(hashPos)) { if (insertPos == -1) insertPos = hashPos; } else if (hashMap[hashPos]->Value.Key == key) { return FindPositionResult(hashPos, -1); } numProbes++; hashPos = (hashPos + GetProbeOffset(numProbes)) & bucketSizeMinusOne; } if (insertPos != -1) return FindPositionResult(-1, insertPos); throw InvalidOperationException("Hash map is full. This indicates an error in Key::Equal or Key::GetHashCode."); } TValue & _Insert(KeyValuePair && kvPair, int pos) { auto node = kvPairs.AddLast(); node->Value = _Move(kvPair); hashMap[pos] = node; SetEmpty(pos, false); SetDeleted(pos, false); return node->Value.Value; } void Rehash() { if (bucketSizeMinusOne == -1 || _count / (float)bucketSizeMinusOne >= MaxLoadFactor) { int newSize = (bucketSizeMinusOne + 1) * 2; int newShiftBits = shiftBits - 1; if (newSize == 0) { newSize = 16; newShiftBits = 28; } EnumerableDictionary newDict; newDict.shiftBits = newShiftBits; newDict.bucketSizeMinusOne = newSize - 1; newDict.hashMap = new LinkedNode>*[newSize]; newDict.marks.SetMax(newSize * 2); if (hashMap) { for (auto & kvPair : *this) { newDict.Add(_Move(kvPair)); } } *this = _Move(newDict); } } bool AddIfNotExists(KeyValuePair && kvPair) { Rehash(); auto pos = FindPosition(kvPair.Key); if (pos.ObjectPosition != -1) return false; else if (pos.InsertionPosition != -1) { _count++; _Insert(_Move(kvPair), pos.InsertionPosition); return true; } else throw InvalidOperationException("Inconsistent find result returned. This is a bug in Dictionary implementation."); } void Add(KeyValuePair && kvPair) { if (!AddIfNotExists(_Move(kvPair))) throw KeyExistsException("The key already exists in Dictionary."); } TValue & Set(KeyValuePair && kvPair) { Rehash(); auto pos = FindPosition(kvPair.Key); if (pos.ObjectPosition != -1) { hashMap[pos.ObjectPosition]->Delete(); return _Insert(_Move(kvPair), pos.ObjectPosition); } else if (pos.InsertionPosition != -1) { _count++; return _Insert(_Move(kvPair), pos.InsertionPosition); } else throw InvalidOperationException("Inconsistent find result returned. This is a bug in Dictionary implementation."); } public: typedef typename LinkedList>::Iterator Iterator; typename LinkedList>::Iterator begin() const { return kvPairs.begin(); } typename LinkedList>::Iterator end() const { return kvPairs.end(); } public: void Add(const TKey & key, const TValue & value) { Add(KeyValuePair(key, value)); } void Add(TKey && key, TValue && value) { Add(KeyValuePair(_Move(key), _Move(value))); } bool AddIfNotExists(const TKey & key, const TValue & value) { return AddIfNotExists(KeyValuePair(key, value)); } bool AddIfNotExists(TKey && key, TValue && value) { return AddIfNotExists(KeyValuePair(_Move(key), _Move(value))); } void Remove(const TKey & key) { if (_count > 0) { auto pos = FindPosition(key); if (pos.ObjectPosition != -1) { kvPairs.Delete(hashMap[pos.ObjectPosition]); hashMap[pos.ObjectPosition] = 0; SetDeleted(pos.ObjectPosition, true); _count--; } } } void Clear() { _count = 0; kvPairs.Clear(); marks.Clear(); } template bool ContainsKey(const T & key) const { if (bucketSizeMinusOne == -1) return false; auto pos = FindPosition(key); return pos.ObjectPosition != -1; } template TValue * TryGetValue(const T & key) const { if (bucketSizeMinusOne == -1) return nullptr; auto pos = FindPosition(key); if (pos.ObjectPosition != -1) { return &(hashMap[pos.ObjectPosition]->Value.Value); } return nullptr; } template bool TryGetValue(const T & key, TValue & value) const { if (bucketSizeMinusOne == -1) return false; auto pos = FindPosition(key); if (pos.ObjectPosition != -1) { value = hashMap[pos.ObjectPosition]->Value.Value; return true; } return false; } class ItemProxy { private: const EnumerableDictionary * dict; TKey key; public: ItemProxy(const TKey & _key, const EnumerableDictionary * _dict) { this->dict = _dict; this->key = _key; } ItemProxy(TKey && _key, const EnumerableDictionary * _dict) { this->dict = _dict; this->key = _Move(_key); } TValue & GetValue() const { auto pos = dict->FindPosition(key); if (pos.ObjectPosition != -1) { return dict->hashMap[pos.ObjectPosition]->Value.Value; } else { throw KeyNotFoundException("The key does not exists in dictionary."); } } inline TValue & operator()() const { return GetValue(); } operator TValue&() const { return GetValue(); } TValue & operator = (const TValue & val) { return ((EnumerableDictionary*)dict)->Set(KeyValuePair(_Move(key), val)); } TValue & operator = (TValue && val) { return ((EnumerableDictionary*)dict)->Set(KeyValuePair(_Move(key), _Move(val))); } }; ItemProxy operator [](const TKey & key) const { return ItemProxy(key, this); } ItemProxy operator [](TKey && key) const { return ItemProxy(_Move(key), this); } int Count() const { return _count; } KeyValuePair & First() const { return kvPairs.First(); } KeyValuePair & Last() const { return kvPairs.Last(); } private: template void Init(const KeyValuePair & kvPair, Args... args) { Add(kvPair); Init(args...); } public: EnumerableDictionary() { bucketSizeMinusOne = -1; shiftBits = 32; _count = 0; hashMap = 0; } template EnumerableDictionary(Arg arg, Args... args) { Init(arg, args...); } EnumerableDictionary(const EnumerableDictionary & other) : bucketSizeMinusOne(-1), _count(0), hashMap(0) { *this = other; } EnumerableDictionary(EnumerableDictionary && other) : bucketSizeMinusOne(-1), _count(0), hashMap(0) { *this = (_Move(other)); } EnumerableDictionary & operator = (const EnumerableDictionary & other) { if (this == &other) return *this; Clear(); for (auto & item : other) Add(item.Key, item.Value); return *this; } EnumerableDictionary & operator = (EnumerableDictionary && other) { if (this == &other) return *this; Free(); bucketSizeMinusOne = other.bucketSizeMinusOne; _count = other._count; hashMap = other.hashMap; shiftBits = other.shiftBits; marks = _Move(other.marks); other.hashMap = 0; other._count = 0; other.bucketSizeMinusOne = -1; kvPairs = _Move(other.kvPairs); return *this; } ~EnumerableDictionary() { Free(); } }; class _DummyClass {}; template class HashSetBase { protected: DictionaryType dict; private: template void Init(const T & v, Args... args) { Add(v); Init(args...); } public: HashSetBase() {} template HashSetBase(Arg arg, Args... args) { Init(arg, args...); } HashSetBase(const HashSetBase & set) { operator=(set); } HashSetBase(HashSetBase && set) { operator=(_Move(set)); } HashSetBase & operator = (const HashSetBase & set) { dict = set.dict; return *this; } HashSetBase & operator = (HashSetBase && set) { dict = _Move(set.dict); return *this; } public: class Iterator { private: typename DictionaryType::Iterator iter; public: Iterator() = default; T & operator *() const { return (*iter).Key; } T * operator ->() const { return &(*iter).Key; } Iterator & operator ++() { ++iter; return *this; } Iterator operator ++(int) { Iterator rs = *this; operator++(); return rs; } bool operator != (const Iterator & _that) const { return iter != _that.iter; } bool operator == (const Iterator & _that) const { return iter == _that.iter; } Iterator(const typename DictionaryType::Iterator & _iter) { this->iter = _iter; } }; Iterator begin() const { return Iterator(dict.begin()); } Iterator end() const { return Iterator(dict.end()); } public: int Count() const { return dict.Count(); } void Clear() { dict.Clear(); } bool Add(const T& obj) { return dict.AddIfNotExists(obj, _DummyClass()); } bool Add(T && obj) { return dict.AddIfNotExists(_Move(obj), _DummyClass()); } void Remove(const T & obj) { dict.Remove(obj); } bool Contains(const T & obj) const { return dict.ContainsKey(obj); } }; template class HashSet : public HashSetBase> {}; template class EnumerableHashSet : public HashSetBase> { public: T & First() const { return this->dict.First().Key; } T & Last() const { return this->dict.Last().Key; } }; } } #endif ================================================ FILE: Source/CoreLib/Exception.h ================================================ #ifndef CORE_LIB_EXCEPTION_H #define CORE_LIB_EXCEPTION_H #include "Common.h" #include "LibString.h" namespace CoreLib { namespace Basic { class Exception : public Object { public: String Message; Exception() {} Exception(const String & message) : Message(message) { } }; class IndexOutofRangeException : public Exception { public: IndexOutofRangeException() {} IndexOutofRangeException(const String & message) : Exception(message) { } }; class InvalidOperationException : public Exception { public: InvalidOperationException() {} InvalidOperationException(const String & message) : Exception(message) { } }; class ArgumentException : public Exception { public: ArgumentException() {} ArgumentException(const String & message) : Exception(message) { } }; class KeyNotFoundException : public Exception { public: KeyNotFoundException() {} KeyNotFoundException(const String & message) : Exception(message) { } }; class KeyExistsException : public Exception { public: KeyExistsException() {} KeyExistsException(const String & message) : Exception(message) { } }; class NotSupportedException : public Exception { public: NotSupportedException() {} NotSupportedException(const String & message) : Exception(message) { } }; class NotImplementedException : public Exception { public: NotImplementedException() {} NotImplementedException(const String & message) : Exception(message) { } }; class InvalidProgramException : public Exception { public: InvalidProgramException() {} InvalidProgramException(const String & message) : Exception(message) { } }; } } #endif ================================================ FILE: Source/CoreLib/Func.h ================================================ #ifndef CORELIB_FUNC_H #define CORELIB_FUNC_H #include "SmartPointer.h" namespace CoreLib { namespace Basic { template class FuncPtr { public: virtual TResult operator()(Arguments...) = 0; virtual bool operator == (const FuncPtr *) { return false; } virtual ~FuncPtr() {} }; template class CdeclFuncPtr : public FuncPtr { public: typedef TResult (*FuncType)(Arguments...); private: FuncType funcPtr; public: CdeclFuncPtr(FuncType func) :funcPtr(func) { } virtual TResult operator()(Arguments... params) override { return funcPtr(params...); } virtual bool operator == (const FuncPtr * ptr) override { auto cptr = dynamic_cast*>(ptr); if (cptr) return funcPtr == cptr->funcPtr; else return false; } }; template class MemberFuncPtr : public FuncPtr { public: typedef TResult (Class::*FuncType)(Arguments...); private: FuncType funcPtr; Class * object; public: MemberFuncPtr(Class * obj, FuncType func) : funcPtr(func), object(obj) { } virtual TResult operator()(Arguments... params) override { return (object->*funcPtr)(params...); } virtual bool operator == (const FuncPtr * ptr) override { auto cptr = dynamic_cast*>(ptr); if (cptr) return funcPtr == cptr->funcPtr && object == cptr->object; else return false; } }; template class LambdaFuncPtr : public FuncPtr { private: F func; public: LambdaFuncPtr(const F & _func) : func(_func) {} virtual TResult operator()(Arguments... params) override { return func(params...); } virtual bool operator == (const FuncPtr * /*ptr*/) override { return false; } }; template class Func { private: RefPtr> funcPtr; public: Func(){} Func(typename CdeclFuncPtr::FuncType func) { funcPtr = new CdeclFuncPtr(func); } template Func(Class * object, typename MemberFuncPtr::FuncType func) { funcPtr = new MemberFuncPtr(object, func); } template Func(const TFuncObj & func) { funcPtr = new LambdaFuncPtr(func); } Func & operator = (typename CdeclFuncPtr::FuncType func) { funcPtr = new CdeclFuncPtr(func); return *this; } template Func & operator = (const MemberFuncPtr & func) { funcPtr = new MemberFuncPtr(func); return *this; } template Func & operator = (const TFuncObj & func) { funcPtr = new LambdaFuncPtr(func); return *this; } bool operator == (const Func & f) { return *funcPtr == f.funcPtr.Ptr(); } bool operator != (const Func & f) { return !(*this == f); } TResult operator()(Arguments... params) { return (*funcPtr)(params...); } }; // template // using Procedure = Func; template class Procedure : public Func { private: RefPtr> funcPtr; public: Procedure(){} Procedure(const Procedure & proc) { funcPtr = proc.funcPtr; } Procedure(typename CdeclFuncPtr::FuncType func) { funcPtr = new CdeclFuncPtr(func); } template Procedure(Class * object, typename MemberFuncPtr::FuncType func) { funcPtr = new MemberFuncPtr(object, func); } template Procedure(const TFuncObj & func) { funcPtr = new LambdaFuncPtr(func); } Procedure & operator = (typename CdeclFuncPtr::FuncType func) { funcPtr = new CdeclFuncPtr(func); return *this; } template Procedure & operator = (const MemberFuncPtr & func) { funcPtr = new MemberFuncPtr(func); return *this; } template Procedure & operator = (const TFuncObj & func) { funcPtr = new LambdaFuncPtr(func); return *this; } Procedure & operator = (const Procedure & proc) { funcPtr = proc.funcPtr; } void Clear() { funcPtr = nullptr; } void operator()(Arguments... params) { if (funcPtr) (*funcPtr)(params...); } }; } } #endif ================================================ FILE: Source/CoreLib/Hash.h ================================================ #ifndef CORELIB_HASH_H #define CORELIB_HASH_H #include "LibMath.h" #include namespace CoreLib { namespace Basic { inline int GetHashCode(double key) { return FloatAsInt((float)key); } inline int GetHashCode(float key) { return FloatAsInt(key); } inline int GetHashCode(const char * buffer) { if (!buffer) return 0; int hash = 0; int c; auto str = buffer; c = *str++; while (c) { hash = c + (hash << 6) + (hash << 16) - hash; c = *str++; } return hash; } inline int GetHashCode(char * buffer) { return GetHashCode(const_cast(buffer)); } template class Hash { public: }; template<> class Hash<1> { public: template static int GetHashCode(TKey & key) { return (int)key; } }; template<> class Hash<0> { public: template static int GetHashCode(TKey & key) { return key.GetHashCode(); } }; template class PointerHash {}; template<> class PointerHash<1> { public: template static int GetHashCode(TKey & key) { return (int)((CoreLib::PtrInt)key) / sizeof(typename std::remove_pointer::type); } }; template<> class PointerHash<0> { public: template static int GetHashCode(TKey & key) { return Hash::value || std::is_enum::value>::GetHashCode(key); } }; template int GetHashCode(const TKey & key) { return PointerHash::value>::GetHashCode(key); } template int GetHashCode(TKey & key) { return PointerHash::value>::GetHashCode(key); } } } #endif ================================================ FILE: Source/CoreLib/IntSet.h ================================================ #ifndef BIT_VECTOR_INT_SET_H #define BIT_VECTOR_INT_SET_H #include #include "List.h" #include "LibMath.h" #include "Common.h" #include "Exception.h" namespace CoreLib { namespace Basic { class IntSet { private: List buffer; public: IntSet() {} IntSet(const IntSet & other) { buffer = other.buffer; } IntSet(IntSet && other) { *this = (_Move(other)); } IntSet & operator = (IntSet && other) { buffer = _Move(other.buffer); return *this; } IntSet & operator = (const IntSet & other) { buffer = other.buffer; return *this; } int GetHashCode() { int rs = 0; for (auto val : buffer) rs ^= val; return rs; } IntSet(int maxVal) { SetMax(maxVal); } int Size() const { return buffer.Count()*32; } void SetMax(int val) { Resize(val); Clear(); } void SetAll() { for (int i = 0; i>5); if (buffer.Count() > oldBufferSize) memset(buffer.Buffer()+oldBufferSize, 0, (buffer.Count()-oldBufferSize) * sizeof(int)); } void Clear() { for (int i = 0; i>5; if (id < buffer.Count()) buffer[id] |= (1<<(val&31)); else { int oldSize = buffer.Count(); buffer.SetSize(id+1); memset(buffer.Buffer() + oldSize, 0, (buffer.Count()-oldSize)*sizeof(int)); buffer[id] |= (1<<(val&31)); } } void Remove(int val) { if ((val>>5) < buffer.Count()) buffer[(val>>5)] &= ~(1<<(val&31)); } bool Contains(int val) const { if ((val>>5) >= buffer.Count()) return false; return (buffer[(val>>5)] & (1<<(val&31))) != 0; } void UnionWith(const IntSet & set) { for (int i = 0; i buffer.Count()) buffer.AddRange(set.buffer.Buffer()+buffer.Count(), set.buffer.Count()-buffer.Count()); } bool operator == (const IntSet & set) { if (buffer.Count() != set.buffer.Count()) return false; for (int i = 0; i #ifdef _WIN32 #include #endif namespace CoreLib { namespace IO { using namespace CoreLib::Basic; CommandLineWriter * currentCommandWriter = nullptr; void SetCommandLineWriter(CommandLineWriter * writer) { currentCommandWriter = writer; } bool File::Exists(const String & fileName) { #ifdef _WIN32 struct _stat32 statVar; return ::_wstat32(((String)fileName).ToWString(), &statVar) != -1; #else struct stat statVar; return ::stat(fileName.Buffer(), &statVar) == 0; #endif } String Path::TruncateExt(const String & path) { int dotPos = path.LastIndexOf('.'); if (dotPos != -1) return path.SubString(0, dotPos); else return path; } String Path::ReplaceExt(const String & path, const char * newExt) { StringBuilder sb(path.Length()+10); int dotPos = path.LastIndexOf('.'); if (dotPos == -1) dotPos = path.Length(); sb.Append(path.Buffer(), dotPos); sb.Append('.'); sb.Append(newExt); return sb.ProduceString(); } String Path::GetFileName(const String & path) { int pos = path.LastIndexOf('/'); pos = Math::Max(path.LastIndexOf('\\'), pos) + 1; return path.SubString(pos, path.Length()-pos); } String Path::GetFileNameWithoutEXT(const String & path) { int pos = path.LastIndexOf('/'); pos = Math::Max(path.LastIndexOf('\\'), pos) + 1; int dotPos = path.LastIndexOf('.'); if (dotPos <= pos) dotPos = path.Length(); return path.SubString(pos, dotPos - pos); } String Path::GetFileExt(const String & path) { int dotPos = path.LastIndexOf('.'); if (dotPos != -1) return path.SubString(dotPos+1, path.Length()-dotPos-1); else return ""; } String Path::GetDirectoryName(const String & path) { int pos = path.LastIndexOf('/'); pos = Math::Max(path.LastIndexOf('\\'), pos); if (pos != -1) return path.SubString(0, pos); else return ""; } String Path::Combine(const String & path1, const String & path2) { if (path1.Length() == 0) return path2; StringBuilder sb(path1.Length()+path2.Length()+2); sb.Append(path1); if (!path1.EndsWith('\\') && !path1.EndsWith('/')) sb.Append(PathDelimiter); sb.Append(path2); return sb.ProduceString(); } String Path::Combine(const String & path1, const String & path2, const String & path3) { StringBuilder sb(path1.Length()+path2.Length()+path3.Length()+3); sb.Append(path1); if (!path1.EndsWith('\\') && !path1.EndsWith('/')) sb.Append(PathDelimiter); sb.Append(path2); if (!path2.EndsWith('\\') && !path2.EndsWith('/')) sb.Append(PathDelimiter); sb.Append(path3); return sb.ProduceString(); } bool Path::CreateDir(const String & path) { #if defined(_WIN32) return _wmkdir(path.ToWString()) == 0; #else return mkdir(path.Buffer(), 0777) == 0; #endif } CoreLib::Basic::String File::ReadAllText(const CoreLib::Basic::String & fileName) { StreamReader reader(new FileStream(fileName, FileMode::Open, FileAccess::Read, FileShare::ReadWrite)); return reader.ReadToEnd(); } CoreLib::Basic::List File::ReadAllBytes(const CoreLib::Basic::String & fileName) { RefPtr fs = new FileStream(fileName, FileMode::Open, FileAccess::Read, FileShare::ReadWrite); List buffer; while (!fs->IsEnd()) { unsigned char ch; int read = (int)fs->Read(&ch, 1); if (read) buffer.Add(ch); else break; } return _Move(buffer); } void File::WriteAllText(const CoreLib::Basic::String & fileName, const CoreLib::Basic::String & text) { StreamWriter writer(new FileStream(fileName, FileMode::Create)); writer.Write(text); } } } ================================================ FILE: Source/CoreLib/LibIO.h ================================================ #ifndef CORE_LIB_IO_H #define CORE_LIB_IO_H #include "LibString.h" #include "Stream.h" #include "TextIO.h" #include "SecureCRT.h" namespace CoreLib { namespace IO { class File { public: static bool Exists(const CoreLib::Basic::String & fileName); static CoreLib::Basic::String ReadAllText(const CoreLib::Basic::String & fileName); static CoreLib::Basic::List ReadAllBytes(const CoreLib::Basic::String & fileName); static void WriteAllText(const CoreLib::Basic::String & fileName, const CoreLib::Basic::String & text); }; class Path { public: #ifdef _WIN32 static const char PathDelimiter = '\\'; #else static const char PathDelimiter = '/'; #endif static String TruncateExt(const String & path); static String ReplaceExt(const String & path, const char * newExt); static String GetFileName(const String & path); static String GetFileNameWithoutEXT(const String & path); static String GetFileExt(const String & path); static String GetDirectoryName(const String & path); static String Combine(const String & path1, const String & path2); static String Combine(const String & path1, const String & path2, const String & path3); static bool CreateDir(const String & path); }; class CommandLineWriter : public Object { public: virtual void Write(const String & text) = 0; }; void SetCommandLineWriter(CommandLineWriter * writer); extern CommandLineWriter * currentCommandWriter; template void uiprintf(const wchar_t * format, Args... args) { if (currentCommandWriter) { char buffer[1024]; snprintf(buffer, 1024, format, args...); currentCommandWriter->Write(buffer); } } } } #endif ================================================ FILE: Source/CoreLib/LibMath.cpp ================================================ #include "LibMath.h" namespace CoreLib { namespace Basic { const float Math::Pi = 3.141592654f; } } ================================================ FILE: Source/CoreLib/LibMath.h ================================================ #ifndef CORE_LIB_MATH_H #define CORE_LIB_MATH_H #include namespace CoreLib { namespace Basic { class Math { public: static const float Pi; template static T Min(const T& v1, const T&v2) { return v1 static T Max(const T& v1, const T&v2) { return v1>v2?v1:v2; } template static T Min(const T& v1, const T&v2, const T&v3) { return Min(v1, Min(v2, v3)); } template static T Max(const T& v1, const T&v2, const T&v3) { return Max(v1, Max(v2, v3)); } template static T Clamp(const T& val, const T& vmin, const T&vmax) { if (val < vmin) return vmin; else if (val > vmax) return vmax; else return val; } static inline int FastFloor(float x) { int i = (int)x; return i - (i > x); } static inline int FastFloor(double x) { int i = (int)x; return i - (i > x); } static inline int IsNaN(float x) { #ifdef _M_X64 return _isnanf(x); #else return isnan(x); #endif } static inline int IsInf(float x) { return isinf(x); } static inline unsigned int Ones32(register unsigned int x) { /* 32-bit recursive reduction using SWAR... but first step is mapping 2-bit values into sum of 2 1-bit values in sneaky way */ x -= ((x >> 1) & 0x55555555); x = (((x >> 2) & 0x33333333) + (x & 0x33333333)); x = (((x >> 4) + x) & 0x0f0f0f0f); x += (x >> 8); x += (x >> 16); return(x & 0x0000003f); } static inline unsigned int Log2Floor(register unsigned int x) { x |= (x >> 1); x |= (x >> 2); x |= (x >> 4); x |= (x >> 8); x |= (x >> 16); return(Ones32(x >> 1)); } static inline unsigned int Log2Ceil(register unsigned int x) { int y = (x & (x - 1)); y |= -y; y >>= (32 - 1); x |= (x >> 1); x |= (x >> 2); x |= (x >> 4); x |= (x >> 8); x |= (x >> 16); return(Ones32(x >> 1) - y); } /* static inline int Log2(float x) { unsigned int ix = (unsigned int&)x; unsigned int exp = (ix >> 23) & 0xFF; int log2 = (unsigned int)(exp) - 127; return log2; } */ }; inline int FloatAsInt(float val) { union InterCast { float fvalue; int ivalue; } cast; cast.fvalue = val; return cast.ivalue; } inline float IntAsFloat(int val) { union InterCast { float fvalue; int ivalue; } cast; cast.ivalue = val; return cast.fvalue; } inline unsigned short FloatToHalf(float val) { int x = *(int*)&val; unsigned short bits = (x >> 16) & 0x8000; unsigned short m = (x >> 12) & 0x07ff; unsigned int e = (x >> 23) & 0xff; if (e < 103) return bits; if (e > 142) { bits |= 0x7c00u; bits |= e == 255 && (x & 0x007fffffu); return bits; } if (e < 113) { m |= 0x0800u; bits |= (m >> (114 - e)) + ((m >> (113 - e)) & 1); return bits; } bits |= ((e - 112) << 10) | (m >> 1); bits += m & 1; return bits; } inline float HalfToFloat(unsigned short input) { union InterCast { float fvalue; int ivalue; InterCast() = default; InterCast(int ival) { ivalue = ival; } }; static const InterCast magic = InterCast((127 + (127 - 15)) << 23); static const InterCast was_infnan = InterCast((127 + 16) << 23); InterCast o; o.ivalue = (input & 0x7fff) << 13; // exponent/mantissa bits o.fvalue *= magic.fvalue; // exponent adjust if (o.fvalue >= was_infnan.fvalue) // make sure Inf/NaN survive o.ivalue |= 255 << 23; o.ivalue |= (input & 0x8000) << 16; // sign bit return o.fvalue; } class Random { private: unsigned int seed; public: Random(int seed) { this->seed = seed; } int Next() // random between 0 and RandMax (currently 0x7fff) { return (((seed = seed * 214013L + 2531011L) >> 16) & 0x7fff); } int Next(int min, int max) // inclusive min, exclusive max { unsigned int a = ((seed = seed * 214013L + 2531011L) & 0xFFFF0000); unsigned int b = ((seed = seed * 214013L + 2531011L) >> 16); unsigned int r = a + b; return min + r % (max - min); } float NextFloat() { return ((Next() << 15) + Next()) / ((float)(1 << 30)); } float NextFloat(float valMin, float valMax) { return valMin + (valMax - valMin) * NextFloat(); } static int RandMax() { return 0x7fff; } }; } } #endif ================================================ FILE: Source/CoreLib/LibString.cpp ================================================ #include "LibString.h" #include "TextIO.h" namespace CoreLib { namespace Basic { _EndLine EndLine; String StringConcat(const char * lhs, int leftLen, const char * rhs, int rightLen) { String res; res.length = leftLen + rightLen; res.buffer = new char[res.length + 1]; strcpy_s(res.buffer.Ptr(), res.length + 1, lhs); strcpy_s(res.buffer + leftLen, res.length + 1 - leftLen, rhs); return res; } String operator+(const char * op1, const String & op2) { if(!op2.buffer) // no string 2 - return first return String(op1); if (!op1) // no base string?! return the second string return op2; return StringConcat(op1, (int)strlen(op1), op2.buffer.Ptr(), op2.length); } String operator+(const String & op1, const char * op2) { if(!op1.buffer) return String(op2); return StringConcat(op1.buffer.Ptr(), op1.length, op2, (int)strlen(op2)); } String operator+(const String & op1, const String & op2) { if(!op1.buffer && !op2.buffer) return String(); else if(!op1.buffer) return String(op2); else if(!op2.buffer) return String(op1); return StringConcat(op1.buffer.Ptr(), op1.length, op2.buffer.Ptr(), op2.length); } int StringToInt(const String & str, int radix) { if (str.StartsWith("0x")) return (int)strtoll(str.Buffer(), NULL, 16); else return (int)strtoll(str.Buffer(), NULL, radix); } unsigned int StringToUInt(const String & str, int radix) { if (str.StartsWith("0x")) return (unsigned int)strtoull(str.Buffer(), NULL, 16); else return (unsigned int)strtoull(str.Buffer(), NULL, radix); } double StringToDouble(const String & str) { return (double)strtod(str.Buffer(), NULL); } float StringToFloat(const String & str) { return strtof(str.Buffer(), NULL); } String String::ReplaceAll(String src, String dst) const { String rs = *this; int index = 0; int srcLen = src.length; int len = rs.length; while ((index = rs.IndexOf(src, index)) != -1) { rs = rs.SubString(0, index) + dst + rs.SubString(index + srcLen, len - index - srcLen); len = rs.length; } return rs; } String String::FromWString(const wchar_t * wstr) { #ifdef _WIN32 return CoreLib::IO::Encoding::UTF16->ToString((const char*)wstr, (int)(wcslen(wstr) * sizeof(wchar_t))); #else return CoreLib::IO::Encoding::UTF32->ToString((const char*)wstr, (int)(wcslen(wstr) * sizeof(wchar_t))); #endif } String String::FromWChar(const wchar_t ch) { #ifdef _WIN32 return CoreLib::IO::Encoding::UTF16->ToString((const char*)&ch, (int)(sizeof(wchar_t))); #else return CoreLib::IO::Encoding::UTF32->ToString((const char*)&ch, (int)(sizeof(wchar_t))); #endif } String String::FromUnicodePoint(unsigned int codePoint) { char buf[6]; int len = CoreLib::IO::EncodeUnicodePointToUTF8(buf, (int)codePoint); buf[len] = 0; return String(buf); } const wchar_t * String::ToWString(int * len) const { if (!buffer) { if (len) *len = 0; return L""; } else { if (wcharBuffer) { if (len) *len = (int)wcslen(wcharBuffer); return wcharBuffer; } List buf; CoreLib::IO::Encoding::UTF16->GetBytes(buf, *this); if (len) *len = buf.Count() / sizeof(wchar_t); buf.Add(0); buf.Add(0); const_cast(this)->wcharBuffer = (wchar_t*)buf.Buffer(); buf.ReleaseBuffer(); return wcharBuffer; } } String String::PadLeft(char ch, int pLen) { StringBuilder sb; for (int i = 0; i < pLen - this->length; i++) sb << ch; for (int i = 0; i < this->length; i++) sb << buffer[i]; return sb.ProduceString(); } String String::PadRight(char ch, int pLen) { StringBuilder sb; for (int i = 0; i < this->length; i++) sb << buffer[i]; for (int i = 0; i < pLen - this->length; i++) sb << ch; return sb.ProduceString(); } } } ================================================ FILE: Source/CoreLib/LibString.h ================================================ #ifndef FUNDAMENTAL_LIB_STRING_H #define FUNDAMENTAL_LIB_STRING_H #include #include #include #include "SmartPointer.h" #include "Common.h" #include "Hash.h" #include "SecureCRT.h" namespace CoreLib { namespace Basic { class _EndLine {}; extern _EndLine EndLine; // in-place reversion, works only for ascii string inline void ReverseInternalAscii(char * buffer, int length) { int i, j; char c; for (i = 0, j = length - 1; i inline int IntToAscii(char * buffer, IntType val, int radix) { int i = 0; IntType sign; sign = val; if (sign < 0) val = (IntType)(0 - val); do { int digit = (val % radix); if (digit <= 9) buffer[i++] = (char)(digit + '0'); else buffer[i++] = (char)(digit - 10 + 'A'); } while ((val /= radix) > 0); if (sign < 0) buffer[i++] = '-'; buffer[i] = '\0'; return i; } inline bool IsUtf8LeadingByte(char ch) { return (((unsigned char)ch) & 0xC0) == 0xC0; } inline bool IsUtf8ContinuationByte(char ch) { return (((unsigned char)ch) & 0xC0) == 0x80; } /*! @brief Represents a UTF-8 encoded string. */ class String { friend class StringBuilder; private: RefPtr buffer; wchar_t * wcharBuffer = nullptr; int length = 0; void Free() { if (buffer) buffer = 0; if (wcharBuffer) delete[] wcharBuffer; buffer = 0; wcharBuffer = 0; length = 0; } public: static String FromBuffer(RefPtr buffer, int len) { String rs; rs.buffer = buffer; rs.length = len; return rs; } static String FromWString(const wchar_t * wstr); static String FromWChar(const wchar_t ch); static String FromUnicodePoint(unsigned int codePoint); String() { } const char * begin() const { return buffer.Ptr(); } const char * end() const { return buffer.Ptr() + length; } String(int val, int radix = 10) { buffer = new char[33]; length = IntToAscii(buffer.Ptr(), val, radix); ReverseInternalAscii(buffer.Ptr(), length); } String(unsigned int val, int radix = 10) { buffer = new char[33]; length = IntToAscii(buffer.Ptr(), val, radix); ReverseInternalAscii(buffer.Ptr(), length); } String(long long val, int radix = 10) { buffer = new char[65]; length = IntToAscii(buffer.Ptr(), val, radix); ReverseInternalAscii(buffer.Ptr(), length); } String(float val, const char * format = "%g") { buffer = new char[128]; sprintf_s(buffer.Ptr(), 128, format, val); length = (int)strnlen_s(buffer.Ptr(), 128); } String(double val, const char * format = "%g") { buffer = new char[128]; sprintf_s(buffer.Ptr(), 128, format, val); length = (int)strnlen_s(buffer.Ptr(), 128); } String(const char * str) { if (str) { length = (int)strlen(str); buffer = new char[length + 1]; memcpy(buffer.Ptr(), str, length + 1); } } String(char chr) { if (chr) { length = 1; buffer = new char[2]; buffer[0] = chr; buffer[1] = '\0'; } } String(const String & str) { this->operator=(str); } String(String&& other) { this->operator=(static_cast(other)); } ~String() { Free(); } String & operator=(const String & str) { if (str.buffer == buffer) return *this; Free(); if (str.buffer) { length = str.length; buffer = str.buffer; wcharBuffer = 0; } return *this; } String & operator=(String&& other) { if (this != &other) { Free(); buffer = _Move(other.buffer); length = other.length; wcharBuffer = other.wcharBuffer; other.buffer = 0; other.length = 0; other.wcharBuffer = 0; } return *this; } char operator[](int id) const { #if _DEBUG if (id < 0 || id >= length) throw "Operator[]: index out of range."; #endif return buffer.Ptr()[id]; } friend String StringConcat(const char * lhs, int leftLen, const char * rhs, int rightLen); friend String operator+(const char*op1, const String & op2); friend String operator+(const String & op1, const char * op2); friend String operator+(const String & op1, const String & op2); String TrimStart() const { if (!buffer) return *this; int startIndex = 0; while (startIndex < length && (buffer[startIndex] == ' ' || buffer[startIndex] == '\t' || buffer[startIndex] == '\r' || buffer[startIndex] == '\n')) startIndex++; return String(buffer + startIndex); } String TrimEnd() const { if (!buffer) return *this; int endIndex = length - 1; while (endIndex >= 0 && (buffer[endIndex] == ' ' || buffer[endIndex] == '\t' || buffer[endIndex] == '\r' || buffer[endIndex] == '\n')) endIndex--; String res; res.length = endIndex + 1; res.buffer = new char[endIndex + 2]; strncpy_s(res.buffer.Ptr(), endIndex + 2, buffer.Ptr(), endIndex + 1); return res; } String Trim() const { if (!buffer) return *this; int startIndex = 0; while (startIndex < length && (buffer[startIndex] == ' ' || buffer[startIndex] == '\t')) startIndex++; int endIndex = length - 1; while (endIndex >= startIndex && (buffer[endIndex] == ' ' || buffer[endIndex] == '\t')) endIndex--; String res; res.length = endIndex - startIndex + 1; res.buffer = new char[res.length + 1]; memcpy(res.buffer.Ptr(), buffer + startIndex, res.length); res.buffer[res.length] = '\0'; return res; } String SubString(int id, int len) const { if (len == 0) return ""; if (id + len > length) len = length - id; #if _DEBUG if (id < 0 || id >= length || (id + len) > length) throw "SubString: index out of range."; if (len < 0) throw "SubString: length less than zero."; #endif String res; res.buffer = new char[len + 1]; res.length = len; strncpy_s(res.buffer.Ptr(), len + 1, buffer + id, len); res.buffer[len] = 0; return res; } const char * Buffer() const { if (buffer) return buffer.Ptr(); else return ""; } const wchar_t * ToWString(int * len = 0) const; bool Equals(const String & str, bool caseSensitive = true) { if (!buffer) return (str.buffer == 0); if (caseSensitive) return (strcmp(buffer.Ptr(), str.buffer.Ptr()) == 0); else { #ifdef _MSC_VER return (_stricmp(buffer.Ptr(), str.buffer.Ptr()) == 0); #else return (strcasecmp(buffer.Ptr(), str.buffer.Ptr()) == 0); #endif } } bool operator==(const char * strbuffer) const { if (!buffer) return (strbuffer == 0 || strcmp(strbuffer, "") == 0); if (!strbuffer) return buffer == nullptr || strcmp(buffer.Ptr(), "") == 0; return (strcmp(buffer.Ptr(), strbuffer) == 0); } bool operator==(const String & str) const { if (!buffer) return (str.buffer == 0 || strcmp(str.buffer.Ptr(), "") == 0); if (!str.buffer) return buffer == nullptr || strcmp(buffer.Ptr(), "") == 0; return (strcmp(buffer.Ptr(), str.buffer.Ptr()) == 0); } bool operator!=(const char * strbuffer) const { if (!buffer) return (strbuffer != 0 && strcmp(strbuffer, "") != 0); if (strbuffer == 0) return length != 0; return (strcmp(buffer.Ptr(), strbuffer) != 0); } bool operator!=(const String & str) const { if (!buffer) return (str.buffer != 0 && strcmp(str.buffer.Ptr(), "") != 0); if (str.buffer.Ptr() == 0) return length != 0; return (strcmp(buffer.Ptr(), str.buffer.Ptr()) != 0); } bool operator>(const String & str) const { if (!buffer) return false; if (!str.buffer) return buffer.Ptr() != nullptr && length != 0; return (strcmp(buffer.Ptr(), str.buffer.Ptr()) > 0); } bool operator<(const String & str) const { if (!buffer) return (str.buffer != 0); if (!str.buffer) return false; return (strcmp(buffer.Ptr(), str.buffer.Ptr()) < 0); } bool operator>=(const String & str) const { if (!buffer) return (str.buffer == 0); if (!str.buffer) return length == 0; int res = strcmp(buffer.Ptr(), str.buffer.Ptr()); return (res > 0 || res == 0); } bool operator<=(const String & str) const { if (!buffer) return true; if (!str.buffer) return length > 0; int res = strcmp(buffer.Ptr(), str.buffer.Ptr()); return (res < 0 || res == 0); } String ToUpper() const { if (!buffer) return *this; String res; res.length = length; res.buffer = new char[length + 1]; for (int i = 0; i <= length; i++) res.buffer[i] = (buffer[i] >= 'a' && buffer[i] <= 'z') ? (buffer[i] - 'a' + 'A') : buffer[i]; return res; } String ToLower() const { if (!buffer) return *this; String res; res.length = length; res.buffer = new char[length + 1]; for (int i = 0; i <= length; i++) res.buffer[i] = (buffer[i] >= 'A' && buffer[i] <= 'Z') ? (buffer[i] - 'A' + 'a') : buffer[i]; return res; } int Length() const { return length; } int IndexOf(const char * str, int id) const // String str { if (!buffer) return -1; if (id < 0 || id >= length) return -1; auto findRs = strstr(buffer + id, str); int res = findRs ? (int)(findRs - buffer.Ptr()) : -1; if (res >= 0) return res; else return -1; } int IndexOf(const String & str, int id) const { return IndexOf(str.buffer.Ptr(), id); } int IndexOf(const char * str) const { return IndexOf(str, 0); } int IndexOf(const String & str) const { return IndexOf(str.buffer.Ptr(), 0); } int IndexOf(char ch, int id) const { #if _DEBUG if (id < 0 || id >= length) throw "SubString: index out of range."; #endif if (!buffer) return -1; for (int i = id; i < length; i++) if (buffer[i] == ch) return i; return -1; } int IndexOf(char ch) const { return IndexOf(ch, 0); } int LastIndexOf(char ch) const { for (int i = length - 1; i >= 0; i--) if (buffer[i] == ch) return i; return -1; } bool StartsWith(const char * str) const // String str { if (!buffer) return false; int strLen = (int)strlen(str); if (strLen > length) return false; for (int i = 0; i < strLen; i++) if (str[i] != buffer[i]) return false; return true; } bool StartsWith(const String & str) const { return StartsWith(str.buffer.Ptr()); } bool EndsWith(char * str) const // String str { if (!buffer) return false; int strLen = (int)strlen(str); if (strLen > length) return false; for (int i = strLen - 1; i >= 0; i--) if (str[i] != buffer[length - strLen + i]) return false; return true; } bool EndsWith(const String & str) const { return EndsWith(str.buffer.Ptr()); } bool Contains(const char * str) const // String str { if (!buffer) return false; return (IndexOf(str) >= 0) ? true : false; } bool Contains(const String & str) const { return Contains(str.buffer.Ptr()); } int GetHashCode() const { return CoreLib::Basic::GetHashCode((const char*)buffer.Ptr()); } String PadLeft(char ch, int length); String PadRight(char ch, int length); String ReplaceAll(String src, String dst) const; }; class StringBuilder { private: char * buffer; int length; int bufferSize; static const int InitialSize = 512; public: StringBuilder(int bufferSize = 1024) :buffer(0), length(0), bufferSize(0) { buffer = new char[InitialSize]; // new a larger buffer buffer[0] = '\0'; length = 0; bufferSize = InitialSize; } ~StringBuilder() { if (buffer) delete[] buffer; } void EnsureCapacity(int size) { if (bufferSize < size) { char * newBuffer = new char[size + 1]; if (buffer) { strcpy_s(newBuffer, size + 1, buffer); delete[] buffer; } buffer = newBuffer; bufferSize = size; } } StringBuilder & operator << (char ch) { Append(&ch, 1); return *this; } StringBuilder & operator << (int val) { Append(val); return *this; } StringBuilder & operator << (unsigned int val) { Append(val); return *this; } StringBuilder & operator << (long long val) { Append(val); return *this; } StringBuilder & operator << (float val) { Append(val); return *this; } StringBuilder & operator << (double val) { Append(val); return *this; } StringBuilder & operator << (const char * str) { Append(str, (int)strlen(str)); return *this; } StringBuilder & operator << (const String & str) { Append(str); return *this; } StringBuilder & operator << (const _EndLine) { Append('\n'); return *this; } void Append(char ch) { Append(&ch, 1); } void Append(float val) { char buf[128]; sprintf_s(buf, 128, "%g", val); int len = (int)strnlen_s(buf, 128); Append(buf, len); } void Append(double val) { char buf[128]; sprintf_s(buf, 128, "%g", val); int len = (int)strnlen_s(buf, 128); Append(buf, len); } void Append(unsigned int value, int radix = 10) { char vBuffer[33]; int len = IntToAscii(vBuffer, value, radix); ReverseInternalAscii(vBuffer, len); Append(vBuffer); } void Append(int value, int radix = 10) { char vBuffer[33]; int len = IntToAscii(vBuffer, value, radix); ReverseInternalAscii(vBuffer, len); Append(vBuffer); } void Append(long long value, int radix = 10) { char vBuffer[65]; int len = IntToAscii(vBuffer, value, radix); ReverseInternalAscii(vBuffer, len); Append(vBuffer); } void Append(const String & str) { Append(str.Buffer(), str.Length()); } void Append(const char * str) { Append(str, (int)strlen(str)); } void Append(const char * str, int strLen) { int newLength = length + strLen; if (bufferSize < newLength + 1) { int newBufferSize = InitialSize; while (newBufferSize < newLength + 1) newBufferSize <<= 1; char * newBuffer = new char[newBufferSize]; if (buffer) { memcpy(newBuffer, buffer, length); delete[] buffer; } memcpy(newBuffer + length, str, strLen); newBuffer[newLength] = '\0'; buffer = newBuffer; bufferSize = newBufferSize; } else { memcpy(buffer + length, str, strLen); buffer[newLength] = '\0'; } length = newLength; } int Capacity() { return bufferSize; } char * Buffer() { return buffer; } int Length() { return length; } String ToString() { return String(buffer); } String ProduceString() { String rs; rs.buffer = buffer; rs.length = length; buffer = 0; bufferSize = 0; length = 0; return rs; } String GetSubString(int start, int count) { String rs; rs.buffer = new char[count + 1]; rs.length = count; strncpy_s(rs.buffer.Ptr(), count + 1, buffer + start, count); rs.buffer[count] = 0; return rs; } void Remove(int id, int len) { #if _DEBUG if (id >= length || id < 0) throw "Remove: Index out of range."; if (len < 0) throw "Remove: remove length smaller than zero."; #endif int actualDelLength = ((id + len) >= length) ? (length - id) : len; for (int i = id + actualDelLength; i <= length; i++) buffer[i - actualDelLength] = buffer[i]; length -= actualDelLength; } void Clear() { length = 0; if (buffer) buffer[0] = 0; } }; int StringToInt(const String & str, int radix = 10); unsigned int StringToUInt(const String & str, int radix = 10); double StringToDouble(const String & str); float StringToFloat(const String & str); } } #endif ================================================ FILE: Source/CoreLib/Link.h ================================================ #ifndef CORE_LIB_LINK_H #define CORE_LIB_LINK_H #include "Common.h" #include "Exception.h" namespace CoreLib { namespace Basic { template class LinkedList; template class LinkedNode { template friend class LinkedList; private: LinkedNode *pPrev, *pNext; LinkedList * FLink; public: T Value; LinkedNode (LinkedList * lnk):FLink(lnk) { pPrev = pNext = 0; }; LinkedNode * GetPrevious() { return pPrev; }; LinkedNode * GetNext() { return pNext; }; LinkedNode * InsertAfter(const T & nData) { LinkedNode * n = new LinkedNode(FLink); n->Value = nData; n->pPrev = this; n->pNext = this->pNext; LinkedNode *npp = n->pNext; if (npp) { npp->pPrev = n; } pNext = n; if (!n->pNext) FLink->FTail = n; FLink->FCount ++; return n; }; LinkedNode * InsertBefore(const T & nData) { LinkedNode * n = new LinkedNode(FLink); n->Value = nData; n->pPrev = pPrev; n->pNext = this; pPrev = n; LinkedNode *npp = n->pPrev; if (npp) npp->pNext = n; if (!n->pPrev) FLink->FHead = n; FLink->FCount ++; return n; }; void Delete() { if (pPrev) pPrev->pNext = pNext; if (pNext) pNext->pPrev = pPrev; FLink->FCount --; if (FLink->FHead == this) { FLink->FHead = pNext; } if (FLink->FTail == this) { FLink->FTail = pPrev; } delete this; } }; template class LinkedList { template friend class LinkedNode; private: LinkedNode * FHead, *FTail; int FCount; public: class Iterator { public: LinkedNode * Current, *Next; void SetCurrent(LinkedNode * cur) { Current = cur; if (Current) Next = Current->GetNext(); else Next = 0; } Iterator() { Current = Next = 0; } Iterator(LinkedNode * cur) { SetCurrent(cur); } T & operator *() const { return Current->Value; } Iterator& operator ++() { SetCurrent(Next); return *this; } Iterator operator ++(int) { Iterator rs = *this; SetCurrent(Next); return rs; } bool operator != (const Iterator & iter) const { return Current != iter.Current; } bool operator == (const Iterator & iter) const { return Current == iter.Current; } }; Iterator begin() const { return Iterator(FHead); } Iterator end() const { return Iterator(0); } public: LinkedList() : FHead(0), FTail(0), FCount(0) { } ~LinkedList() { Clear(); } LinkedList(const LinkedList & link) : FHead(0), FTail(0), FCount(0) { this->operator=(link); } LinkedList(LinkedList && link) : FHead(0), FTail(0), FCount(0) { this->operator=(_Move(link)); } LinkedList & operator = (LinkedList && link) { if (FHead != 0) Clear(); FHead = link.FHead; FTail = link.FTail; FCount = link.FCount; link.FHead = 0; link.FTail = 0; link.FCount = 0; for (auto node = FHead; node; node = node->GetNext()) node->FLink = this; return *this; } LinkedList & operator = (const LinkedList & link) { if (FHead != 0) Clear(); auto p = link.FHead; while (p) { AddLast(p->Value); p = p->GetNext(); } return *this; } template void ForEach(const IteratorFunc & f) { auto p = FHead; while (p) { f(p->Value); p = p->GetNext(); } } LinkedNode * GetNode(int x) { LinkedNode *pCur = FHead; for (int i=0;ipNext; else throw "Index out of range"; } return pCur; }; LinkedNode * Find(const T& fData) { for (LinkedNode * pCur = FHead; pCur; pCur = pCur->pNext) { if (pCur->Value == fData) return pCur; } return 0; }; LinkedNode * FirstNode() const { return FHead; }; T & First() const { if (!FHead) throw IndexOutofRangeException("LinkedList: index out of range."); return FHead->Value; } T & Last() const { if (!FTail) throw IndexOutofRangeException("LinkedList: index out of range."); return FTail->Value; } LinkedNode * LastNode() const { return FTail; }; LinkedNode * AddLast(const T & nData) { LinkedNode * n = new LinkedNode(this); n->Value = nData; n->pPrev = FTail; if (FTail) FTail->pNext = n; n->pNext = 0; FTail = n; if (!FHead) FHead = n; FCount ++; return n; }; // Insert a blank node LinkedNode * AddLast() { LinkedNode * n = new LinkedNode(this); n->pPrev = FTail; if (FTail) FTail->pNext = n; n->pNext = 0; FTail = n; if (!FHead) FHead = n; FCount ++; return n; }; LinkedNode * AddFirst(const T& nData) { LinkedNode *n = new LinkedNode(this); n->Value = nData; n->pPrev = 0; n->pNext = FHead; if (FHead) FHead->pPrev = n; FHead = n; if (!FTail) FTail = n; FCount ++; return n; }; void Delete(LinkedNode*n, int Count = 1) { LinkedNode *n1,*n2 = 0, *tn; n1 = n->pPrev; tn = n; int numDeleted = 0; for (int i=0; ipNext; delete tn; tn = n2; numDeleted++; if (tn == 0) break; } if (n1) n1->pNext = n2; else FHead = n2; if (n2) n2->pPrev = n1; else FTail = n1; FCount -= numDeleted; } void Clear() { for (LinkedNode *n = FHead; n; ) { LinkedNode * tmp = n->pNext; delete n; n = tmp; } FHead = 0; FTail = 0; FCount = 0; } List ToList() const { List rs; rs.Reserve(FCount); for (auto & item : *this) { rs.Add(item); } return rs; } int Count() { return FCount; } }; } } #endif ================================================ FILE: Source/CoreLib/Linq.h ================================================ #ifndef FUNDAMENTAL_LIB_LINQ_H #define FUNDAMENTAL_LIB_LINQ_H #include "List.h" namespace CoreLib { namespace Basic { template T ConstructT(); template class RemoveReference { public: typedef T Type; }; template class RemoveReference { public: typedef T Type; }; template class RemoveReference { public: typedef T Type; }; template struct RemovePointer { typedef T Type; }; template struct RemovePointer { typedef T Type; }; template class ConcatQuery { private: TQueryable1 items1; TQueryable2 items2; public: ConcatQuery(const TQueryable1 & queryable1, const TQueryable2 & queryable2) : items1(queryable1), items2(queryable2) {} class Enumerator { private: TEnumerator1 ptr1; TEnumerator1 end1; TEnumerator2 ptr2; TEnumerator2 end2; public: Enumerator(const Enumerator &) = default; Enumerator(TEnumerator1 pptr, TEnumerator1 pend, TEnumerator2 pptr2, TEnumerator2 pend2) : ptr1(pptr), end1(pend), ptr2(pptr2), end2(pend2) {} T operator *() const { if (ptr1 != end1) return *(ptr1); else return *(ptr2); } Enumerator& operator ++() { if (ptr1 != end1) ++ptr1; else ++ptr2; return *this; } Enumerator operator ++(int) { Enumerator rs = *this; ++rs; return rs; } bool operator != (const Enumerator & iter) const { return ptr1 != iter.ptr1 || ptr2 != iter.ptr2; } bool operator == (const Enumerator & iter) const { return ptr1 == iter.ptr1 && ptr2 == iter.ptr2; } }; Enumerator begin() const { return Enumerator(items1.begin(), items1.end(), items2.begin(), items2.end()); } Enumerator end() const { return Enumerator(items1.end(), items1.end(), items2.end(), items2.end()); } }; template class WhereQuery { private: TQueryable items; TFunc func; public: WhereQuery(const TQueryable & queryable, const TFunc & f) : items(queryable), func(f) {} class Enumerator { private: TEnumerator ptr; TEnumerator end; const TFunc * func; public: Enumerator(const Enumerator &) = default; Enumerator(TEnumerator ptr, TEnumerator end, const TFunc & f) : ptr(ptr), end(end), func(&f) {} T operator *() const { return *(ptr); } Enumerator& operator ++() { ++ptr; while (ptr != end) { if ((*func)(*ptr)) break; else ++ptr; } return *this; } Enumerator operator ++(int) { Enumerator rs = *this; while (rs.ptr != end) { if ((*func)(*rs.ptr)) break; ++rs.ptr; } return rs; } bool operator != (const Enumerator & iter) const { return ptr != iter.ptr; } bool operator == (const Enumerator & iter) const { return ptr == iter.ptr; } }; Enumerator begin() const { auto ptr = items.begin(); auto end = items.end(); while (ptr != end) { if (func(*ptr)) break; ++ptr; } return Enumerator(ptr, end, func); } Enumerator end() const { return Enumerator(items.end(), items.end(), func); } }; template class SkipQuery { private: TQueryable items; int count = 0; public: SkipQuery(const TQueryable & queryable, int pCount) : items(queryable), count(pCount) {} class Enumerator { private: TEnumerator ptr; TEnumerator end; public: Enumerator(const Enumerator &) = default; Enumerator(TEnumerator pptr, TEnumerator pend) : ptr(pptr), end(pend) { } T operator *() const { return *(ptr); } Enumerator& operator ++() { ++ptr; return *this; } Enumerator operator ++(int) { Enumerator rs = *this; ++ptr; return rs; } bool operator != (const Enumerator & iter) const { return ptr != iter.ptr; } bool operator == (const Enumerator & iter) const { return ptr == iter.ptr; } }; Enumerator begin() const { auto ptr = items.begin(); auto end = items.end(); for (int i = 0; i < count; i++) if (ptr != end) ++ptr; return Enumerator(ptr, end); } Enumerator end() const { return Enumerator(items.end(), items.end()); } }; template class SelectQuery { private: TQueryable items; TFunc func; public: SelectQuery(const TQueryable & queryable, const TFunc & f) : items(queryable), func(f) {} class Enumerator { private: TEnumerator ptr; TEnumerator end; const TFunc * func; public: Enumerator(const Enumerator &) = default; Enumerator(TEnumerator ptr, TEnumerator end, const TFunc & f) : ptr(ptr), end(end), func(&f) {} auto operator *() const -> decltype((*func)(*ptr)) { return (*func)(*ptr); } Enumerator& operator ++() { ++ptr; return *this; } Enumerator operator ++(int) { Enumerator rs = *this; ++rs; return rs; } bool operator != (const Enumerator & iter) const { return !(ptr == iter.ptr); } bool operator == (const Enumerator & iter) const { return ptr == iter.ptr; } }; Enumerator begin() const { return Enumerator(items.begin(), items.end(), func); } Enumerator end() const { return Enumerator(items.end(), items.end(), func); } }; template class SelectManyQuery { private: TQueryable items; TFunc func; SelectManyQuery() {} public: SelectManyQuery(const TQueryable & queryable, const TFunc & f) : items(queryable), func(f) {} template class Enumerator { private: TEnumerator ptr; TEnumerator end; const TFunc * func; TItems items; TItemPtr subPtr; public: Enumerator(const Enumerator &) = default; Enumerator(TEnumerator ptr, TEnumerator end, const TFunc & f) : ptr(ptr), end(end), func(&f) { if (ptr != end) { items = f(*ptr); subPtr = items.begin(); } } auto operator *() const -> decltype(*subPtr) { return *subPtr; } Enumerator& operator ++() { ++subPtr; while (subPtr == items.end() && ptr != end) { ++ptr; if (ptr != end) { items = (*func)(*ptr); subPtr = items.begin(); } else break; } return *this; } Enumerator operator ++(int) { Enumerator rs = *this; ++rs; return rs; } bool operator != (const Enumerator & iter) const { return !operator==(iter); } bool operator == (const Enumerator & iter) const { if (ptr == iter.ptr) { if (ptr == end) return true; else return subPtr == iter.subPtr; } else return false; } }; auto begin() const ->Enumerator())), decltype(func(ConstructT()).begin())> { return Enumerator())), decltype(func(ConstructT()).begin())>(items.begin(), items.end(), func); } auto end() const ->Enumerator())), decltype(func(ConstructT()).begin())> { return Enumerator())), decltype(func(ConstructT()).begin())>(items.end(), items.end(), func); } }; template struct EnumeratorType { typedef decltype(ConstructT().begin()) Type; }; template class ExtractReturnType { public: static TFunc * f; static TArg ConstructArg() {}; typedef decltype((*f)(ConstructArg())) ReturnType; }; template class ExtractItemType { public: typedef typename RemovePointer().begin())>::Type Type; }; template class Queryable { private: TQueryable items; public: auto begin() const -> decltype(items.begin()) { return items.begin(); } auto end() const -> decltype(items.end()) { return items.end(); } public: const TQueryable & GetItems() const { return items; } Queryable(const TQueryable & items) : items(items) {} Queryable, typename SkipQuery::Enumerator, T> Skip(int count) const { return Queryable, typename SkipQuery::Enumerator, T>(SkipQuery(items, count)); } template Queryable, typename ConcatQuery::Enumerator, T> Concat(const Queryable & other) const { return Queryable, typename ConcatQuery::Enumerator, T>(ConcatQuery(this->items, other.GetItems())); } template Queryable, typename WhereQuery::Enumerator, T> Where(const TFunc & f) const { return Queryable, typename WhereQuery::Enumerator, T>(WhereQuery(items, f)); } template Queryable, typename SelectQuery::Enumerator, typename RemoveReference::ReturnType>::Type> Select(const TFunc & f) const { return Queryable, typename SelectQuery::Enumerator, typename RemoveReference::ReturnType>::Type>(SelectQuery(items, f)); } template auto SelectMany(const TFunc & f) const ->Queryable, typename EnumeratorType>::Type, typename ExtractItemType()))>::Type> { return Queryable, typename EnumeratorType>::Type, typename ExtractItemType()))>::Type>(SelectManyQuery(items, f)); } template auto Aggregate(const TAggregateResult & initial, const TFunc & f) const -> decltype(f(initial, *items.begin())) { TAggregateResult rs = initial; for (auto && x : items) rs = f(rs, x); return rs; } template bool Any(const TFunc & condition) const { for (auto && x : items) if (condition(x)) return true; return false; } template T & First(const TFunc & condition) const { for (auto && x : items) if (condition(x)) return x; } template T Max(const TFunc & selector) const { return Aggregate(*items.begin(), [&](const T & v0, const T & v1) { return selector(v0) > selector(v1) ? v0 : v1; }); } template T Min(const TFunc & selector) const { return Aggregate(*items.begin(), [&](const T & v0, const T & v1) { return selector(v0) < selector(v1) ? v0 : v1; }); } template auto Sum(const TFunc & selector) const -> decltype(selector(ConstructT())) { decltype(selector(ConstructT())) rs(0); for (auto && x : items) rs = rs + selector(x); return rs; } T Max() const { return Aggregate(*items.begin(), [](const T & v0, const T & v1) {return v0 > v1 ? v0 : v1; }); } T Min() const { return Aggregate(*items.begin(), [](const T & v0, const T & v1) {return v0 < v1 ? v0 : v1; }); } T Sum() const { T rs = T(0); for (auto && x : items) rs = rs + x; return rs; } T Avg() const { T rs = T(0); int count = 0; for (auto && x : items) { rs = rs + x; count++; } return rs / count; } int Count() const { int rs = 0; for (auto && x : items) rs++; return rs; } List ToList() const { List rs; for (auto && val : items) rs.Add(val); return rs; } }; template inline Queryable, T*, T> From(const List & list) { return Queryable, T*, T>(list.GetArrayView()); } template inline Queryable, T*, T> From(List && list) { return Queryable, T*, T>(_Move(list)); } template inline Queryable, T*, T> From(const ArrayView & list) { return Queryable, T*, T>(list); } template inline Queryable, T*, T> From(const Array & list) { return Queryable, T*, T>(list); } template inline auto From(const T & list) -> Queryable { return Queryable(list); } template inline Queryable, T*, T> FromSingle(const T & obj) { Array arr; arr.Add(obj); return From(arr); } template struct LinkedListView { typename LinkedList::Iterator start, last; typename LinkedList::Iterator begin() const { return start; } typename LinkedList::Iterator end() const { return last; } }; template inline Queryable, LinkedNode, T> From(const LinkedList & list) { LinkedListView view; view.start = list.begin(); view.last = list.end(); return Queryable, LinkedNode, T>(view); } template struct EnumerableDictView { typename EnumerableDictionary::Iterator start, last; typename EnumerableDictionary::Iterator begin() const { return start; } typename EnumerableDictionary::Iterator end() const { return last; } }; template inline Queryable, typename EnumerableDictionary::Iterator, KeyValuePair> From(const EnumerableDictionary & dict) { EnumerableDictView view; view.start = dict.begin(); view.last = dict.end(); return Queryable, typename EnumerableDictionary::Iterator, KeyValuePair>(view); } template struct EnumerableHashSetView { typename HashSetBase>::Iterator start, last; typename EnumerableHashSet::Iterator begin() const { return start; } typename EnumerableHashSet::Iterator end() const { return last; } }; template inline Queryable, typename HashSetBase>::Iterator, TKey> From(const EnumerableHashSet & dict) { EnumerableHashSetView view; view.start = dict.begin(); view.last = dict.end(); return Queryable, typename HashSetBase>::Iterator, TKey>(view); } } } #endif ================================================ FILE: Source/CoreLib/List.h ================================================ #ifndef FUNDAMENTAL_LIB_LIST_H #define FUNDAMENTAL_LIB_LIST_H #include "Allocator.h" #include #include "LibMath.h" #include #include "ArrayView.h" #include const int MIN_QSORT_SIZE = 32; namespace CoreLib { namespace Basic { template class Initializer { }; template class Initializer { public: static void Initialize(T * buffer, int size) { for (int i = 0; i class AllocateMethod { public: static inline T* Alloc(int size) { TAllocator allocator; T * rs = (T*)allocator.Alloc(size*sizeof(T)); Initializer::value>::Initialize(rs, size); return rs; } static inline void Free(T * ptr, int bufferSize) { TAllocator allocator; if (!std::is_trivially_destructible::value) { for (int i = 0; i class AllocateMethod { public: static inline T* Alloc(int size) { return new T[size]; } static inline void Free(T* ptr, int /*bufferSize*/) { delete [] ptr; } }; template class Initializer { public: static void Initialize(T * buffer, int size) { for (int i = 0; i class List { private: inline T * Allocate(int size) { return AllocateMethod::Alloc(size); } private: static const int InitialSize = 16; TAllocator allocator; private: T * buffer; int _count; int bufferSize; void FreeBuffer() { AllocateMethod::Free(buffer, bufferSize); buffer = 0; } void Free() { if (buffer) { FreeBuffer(); } buffer = 0; _count = bufferSize = 0; } public: T* begin() const { return buffer; } T* end() const { return buffer+_count; } private: template void Init(const T & val, Args... args) { Add(val); Init(args...); } public: List() : buffer(0), _count(0), bufferSize(0) { } template List(const T & val, Args... args) { Init(val, args...); } List(const List & list) : buffer(0), _count(0), bufferSize(0) { this->operator=(list); } List(List && list) : buffer(0), _count(0), bufferSize(0) { this->operator=(static_cast&&>(list)); } static List Create(const T & val, int count) { List rs; rs.SetSize(count); for (int i = 0; i < count; i++) rs[i] = val; return rs; } ~List() { Free(); } List & operator=(const List & list) { Free(); AddRange(list); return *this; } List & operator=(List && list) { Free(); _count = list._count; bufferSize = list.bufferSize; buffer = list.buffer; list.buffer = 0; list._count = 0; list.bufferSize = 0; return *this; } T & First() const { #ifdef _DEBUG if (_count == 0) throw "Index out of range."; #endif return buffer[0]; } T & Last() const { #ifdef _DEBUG if (_count == 0) throw "Index out of range."; #endif return buffer[_count-1]; } inline void SwapWith(List & other) { T* tmpBuffer = this->buffer; this->buffer = other.buffer; other.buffer = tmpBuffer; int tmpBufferSize = this->bufferSize; this->bufferSize = other.bufferSize; other.bufferSize = tmpBufferSize; int tmpCount = this->_count; this->_count = other._count; other._count = tmpCount; TAllocator tmpAlloc = _Move(this->allocator); this->allocator = _Move(other.allocator); other.allocator = _Move(tmpAlloc); } T* ReleaseBuffer() { T* rs = buffer; buffer = nullptr; _count = 0; bufferSize = 0; return rs; } inline ArrayView GetArrayView() const { return ArrayView(buffer, _count); } inline ArrayView GetArrayView(int start, int count) const { #ifdef _DEBUG if (start + count > _count || start < 0 || count < 0) throw "Index out of range."; #endif return ArrayView(buffer + start, count); } void Add(T && obj) { if (bufferSize < _count + 1) { int newBufferSize = InitialSize; if (bufferSize) newBufferSize = (bufferSize << 1); Reserve(newBufferSize); } buffer[_count++] = static_cast(obj); } void Add(const T & obj) { if (bufferSize < _count + 1) { int newBufferSize = InitialSize; if (bufferSize) newBufferSize = (bufferSize << 1); Reserve(newBufferSize); } buffer[_count++] = obj; } int Count() const { return _count; } T * Buffer() const { return buffer; } int Capacity() const { return bufferSize; } void Insert(int id, const T & val) { InsertRange(id, &val, 1); } void InsertRange(int id, const T * vals, int n) { if (bufferSize < _count + n) { int newBufferSize = InitialSize; while (newBufferSize < _count + n) newBufferSize = newBufferSize << 1; T * newBuffer = Allocate(newBufferSize); if (bufferSize) { /*if (std::has_trivial_copy_assign::value && std::has_trivial_destructor::value) { memcpy(newBuffer, buffer, sizeof(T) * id); memcpy(newBuffer + id + n, buffer + id, sizeof(T) * (_count - id)); } else*/ { for (int i = 0; i < id; i++) newBuffer[i] = buffer[i]; for (int i = id; i < _count; i++) newBuffer[i + n] = T(static_cast(buffer[i])); } FreeBuffer(); } buffer = newBuffer; bufferSize = newBufferSize; } else { /*if (std::has_trivial_copy_assign::value && std::has_trivial_destructor::value) memmove(buffer + id + n, buffer + id, sizeof(T) * (_count - id)); else*/ { for (int i = _count - 1; i >= id; i--) buffer[i + n] = static_cast(buffer[i]); } } /*if (std::has_trivial_copy_assign::value && std::has_trivial_destructor::value) memcpy(buffer + id, vals, sizeof(T) * n); else*/ for (int i = 0; i < n; i++) buffer[id + i] = vals[i]; _count += n; } //slower than original edition //void Add(const T & val) //{ // InsertRange(_count, &val, 1); //} void InsertRange(int id, const List & list) { InsertRange(id, list.buffer, list._count); } void AddRange(ArrayView list) { InsertRange(_count, list.Buffer(), list.Count()); } void AddRange(const T * vals, int n) { InsertRange(_count, vals, n); } void AddRange(const List & list) { InsertRange(_count, list.buffer, list._count); } void RemoveRange(int id, int deleteCount) { #if _DEBUG if (id >= _count || id < 0) throw "Remove: Index out of range."; if(deleteCount < 0) throw "Remove: deleteCount smaller than zero."; #endif int actualDeleteCount = ((id + deleteCount) >= _count)? (_count - id) : deleteCount; for (int i = id + actualDeleteCount; i < _count; i++) buffer[i - actualDeleteCount] = static_cast(buffer[i]); _count -= actualDeleteCount; } void RemoveAt(int id) { RemoveRange(id, 1); } void Remove(const T & val) { int idx = IndexOf(val); if (idx != -1) RemoveAt(idx); } void Reverse() { for (int i = 0; i < (_count >> 1); i++) { Swap(buffer, i, _count - i - 1); } } void FastRemove(const T & val) { int idx = IndexOf(val); FastRemoveAt(idx); } void FastRemoveAt(int idx) { if (idx != -1 && _count - 1 != idx) { buffer[idx] = _Move(buffer[_count - 1]); } _count--; } void Clear() { _count = 0; } void Reserve(int size) { if(size > bufferSize) { T * newBuffer = Allocate(size); if (bufferSize) { /*if (std::has_trivial_copy_assign::value && std::has_trivial_destructor::value) memcpy(newBuffer, buffer, _count * sizeof(T)); else*/ { for (int i = 0; i < _count; i++) newBuffer[i] = static_cast(buffer[i]); } FreeBuffer(); } buffer = newBuffer; bufferSize = size; } } void GrowToSize(int size) { int newBufferSize = 1<_count = size; } void SetSize(int size) { Reserve(size); _count = size; } void UnsafeShrinkToSize(int size) { _count = size; } void Compress() { if (bufferSize > _count && _count > 0) { T * newBuffer = Allocate(_count); for (int i = 0; i < _count; i++) newBuffer[i] = static_cast(buffer[i]); FreeBuffer(); buffer = newBuffer; bufferSize = _count; } } #ifndef FORCE_INLINE #ifdef _MSC_VER #define FORCE_INLINE __forceinline #else #define FORCE_INLINE inline #endif #endif FORCE_INLINE T & operator [](int id) const { #if _DEBUG if(id >= _count || id < 0) throw IndexOutofRangeException("Operator[]: Index out of Range."); #endif return buffer[id]; } template int FindFirst(const Func & predicate) const { for (int i = 0; i < _count; i++) { if (predicate(buffer[i])) return i; } return -1; } template int FindLast(const Func & predicate) const { for (int i = _count - 1; i >= 0; i--) { if (predicate(buffer[i])) return i; } return -1; } template int IndexOf(const T2 & val) const { for (int i = 0; i < _count; i++) { if (buffer[i] == val) return i; } return -1; } template int LastIndexOf(const T2 & val) const { for (int i = _count - 1; i >= 0; i--) { if(buffer[i] == val) return i; } return -1; } void Sort() { Sort([](T& t1, T& t2){return t1 void Sort(Comparer compare) { //InsertionSort(buffer, 0, _count - 1); //QuickSort(buffer, 0, _count - 1, compare); std::sort(buffer, buffer + _count, compare); } template void ForEach(IterateFunc f) const { for (int i = 0; i<_count; i++) f(buffer[i]); } template void QuickSort(T * vals, int startIndex, int endIndex, Comparer comparer) { if(startIndex < endIndex) { if (endIndex - startIndex < MIN_QSORT_SIZE) InsertionSort(vals, startIndex, endIndex, comparer); else { int pivotIndex = (startIndex + endIndex) >> 1; int pivotNewIndex = Partition(vals, startIndex, endIndex, pivotIndex, comparer); QuickSort(vals, startIndex, pivotNewIndex - 1, comparer); QuickSort(vals, pivotNewIndex + 1, endIndex, comparer); } } } template int Partition(T * vals, int left, int right, int pivotIndex, Comparer comparer) { T pivotValue = vals[pivotIndex]; Swap(vals, right, pivotIndex); int storeIndex = left; for (int i = left; i < right; i++) { if (comparer(vals[i], pivotValue)) { Swap(vals, i, storeIndex); storeIndex++; } } Swap(vals, storeIndex, right); return storeIndex; } template void InsertionSort(T * vals, int startIndex, int endIndex, Comparer comparer) { for (int i = startIndex + 1; i <= endIndex; i++) { T insertValue = static_cast(vals[i]); int insertIndex = i - 1; while (insertIndex >= startIndex && comparer(insertValue, vals[insertIndex])) { vals[insertIndex + 1] = static_cast(vals[insertIndex]); insertIndex--; } vals[insertIndex + 1] = static_cast(insertValue); } } inline void Swap(T * vals, int index1, int index2) { if (index1 != index2) { T tmp = static_cast(vals[index1]); vals[index1] = static_cast(vals[index2]); vals[index2] = static_cast(tmp); } } template int BinarySearch(const T2 & obj, Comparer comparer) { int imin = 0, imax = _count - 1; while (imax >= imin) { int imid = (imin + imax) >> 1; int compareResult = comparer(buffer[imid], obj); if (compareResult == 0) return imid; else if (compareResult < 0) imin = imid + 1; else imax = imid - 1; } return -1; } template int BinarySearch(const T2 & obj) { return BinarySearch(obj, [](T & curObj, const T2 & thatObj)->int { if (curObj < thatObj) return -1; else if (curObj == thatObj) return 0; else return 1; }); } }; template T Min(const List & list) { T minVal = list.First(); for (int i = 1; i T Max(const List & list) { T maxVal = list.First(); for (int i = 1; i maxVal) maxVal = list[i]; return maxVal; } } } #endif ================================================ FILE: Source/CoreLib/MemoryPool.cpp ================================================ #include "MemoryPool.h" #include namespace CoreLib { namespace Basic { MemoryPool::MemoryPool(unsigned char * pBuffer, int pLog2BlockSize, int numBlocks) { Init(pBuffer, pLog2BlockSize, numBlocks); } void MemoryPool::Init(unsigned char * pBuffer, int pLog2BlockSize, int numBlocks) { assert(pLog2BlockSize >= 1 && pLog2BlockSize <= 30); assert(numBlocks >= 4); buffer = pBuffer; blockSize = 1 << pLog2BlockSize; log2BlockSize = pLog2BlockSize; numLevels = Math::Log2Floor(numBlocks); freeList[0] = (FreeListNode*)buffer; freeList[0]->NextPtr = nullptr; freeList[0]->PrevPtr = nullptr; used.SetMax(1 << (numLevels)); for (int i = 1; i < MaxLevels; i++) { freeList[i] = nullptr; } } int MemoryPool::AllocBlock(int level) { if (level < 0) return -1; if (freeList[level] == nullptr) { auto largeBlockAddr = AllocBlock(level - 1); if (largeBlockAddr != -1) { auto block1 = (FreeListNode*)(buffer + ((largeBlockAddr ^ (1 << (numLevels - level))) << log2BlockSize)); block1->NextPtr = nullptr; block1->PrevPtr = nullptr; freeList[level] = block1; int blockIndex = (1 << level) + (largeBlockAddr >> (numLevels-level)) - 1; used.Add(blockIndex); return largeBlockAddr; } else return -1; } else { auto node = freeList[level]; if (node->NextPtr) { node->NextPtr->PrevPtr = node->PrevPtr; } freeList[level] = freeList[level]->NextPtr; int rs = (int)((unsigned char *)node - buffer) >> log2BlockSize; int blockIndex = (1 << level) + (rs >> (numLevels - level)) - 1; used.Add(blockIndex); return rs; } } unsigned char * MemoryPool::Alloc(int size) { if (size == 0) return nullptr; int originalSize = size; if (size < blockSize) size = blockSize; int order = numLevels - (Math::Log2Ceil(size) - log2BlockSize); assert(order >= 0 && order < MaxLevels); bytesAllocated += (1 << ((numLevels-order) + log2BlockSize)); bytesWasted += (1 << ((numLevels - order) + log2BlockSize)) - originalSize; int blockId = AllocBlock(order); if (blockId != -1) return buffer + (blockId << log2BlockSize); else return nullptr; } void MemoryPool::FreeBlock(unsigned char * ptr, int level) { int indexInLevel = (int)(ptr - buffer) >> (numLevels - level + log2BlockSize); int blockIndex = (1 << level) + indexInLevel - 1; assert(used.Contains(blockIndex)); int buddyIndex = (blockIndex & 1) ? blockIndex + 1 : blockIndex - 1; used.Remove(blockIndex); if (level > 0 && !used.Contains(buddyIndex)) { auto buddyPtr = (FreeListNode *)(buffer + ((((int)(ptr - buffer) >> log2BlockSize) ^ (1 << (numLevels - level))) << log2BlockSize)); if (buddyPtr->PrevPtr) { buddyPtr->PrevPtr->NextPtr = buddyPtr->NextPtr; } if (buddyPtr->NextPtr) { buddyPtr->NextPtr->PrevPtr = buddyPtr->PrevPtr; } if (freeList[level] == buddyPtr) { freeList[level] = buddyPtr->NextPtr; } // recursively free parent blocks auto parentPtr = Math::Min(buddyPtr, (FreeListNode*)ptr); if (level > 0) FreeBlock((unsigned char*)parentPtr, level - 1); } else { // insert to freelist auto freeNode = (FreeListNode *)ptr; freeNode->NextPtr = freeList[level]; freeNode->PrevPtr = nullptr; if (freeList[level]) freeList[level]->PrevPtr = freeNode; freeList[level] = freeNode; } } void MemoryPool::Free(unsigned char * ptr, int size) { if (size == 0) return; int originalSize = size; if (size < blockSize) size = blockSize; int level = numLevels - (Math::Log2Ceil(size) - log2BlockSize); bytesAllocated -= (1 << ((numLevels-level) + log2BlockSize)); bytesWasted -= (1 << ((numLevels - level) + log2BlockSize)) - originalSize; FreeBlock(ptr, level); } } } ================================================ FILE: Source/CoreLib/MemoryPool.h ================================================ #ifndef CORE_LIB_MEMORY_POOL_H #define CORE_LIB_MEMORY_POOL_H #include "Basic.h" #include "IntSet.h" namespace CoreLib { namespace Basic { struct MemoryBlockFields { unsigned int Occupied : 1; unsigned int Order : 31; }; struct FreeListNode { FreeListNode * PrevPtr = nullptr, *NextPtr = nullptr; }; class MemoryPool { private: static const int MaxLevels = 32; int blockSize = 0, log2BlockSize = 0; int numLevels = 0; int bytesAllocated = 0; int bytesWasted = 0; unsigned char * buffer = nullptr; FreeListNode * freeList[MaxLevels]; IntSet used; int AllocBlock(int level); void FreeBlock(unsigned char * ptr, int level); public: MemoryPool(unsigned char * buffer, int log2BlockSize, int numBlocks); MemoryPool() = default; void Init(unsigned char * buffer, int log2BlockSize, int numBlocks); unsigned char * Alloc(int size); void Free(unsigned char * ptr, int size); }; class OutofPoolMemoryException : public Exception {}; template class ObjectPool { static const int ObjectSize = sizeof(T) < 8 ? 8 : sizeof(T); private: struct FreeList { FreeList* Next; }; FreeList * freeList = nullptr; int allocPtr = 0; int poolSize = 0; void * buffer = 0; T * GetFreeObject() { if (freeList) { auto rs = (T*)freeList; freeList = freeList->Next; return rs; } return nullptr; } public: ObjectPool() { freeList = nullptr; allocPtr = 0; buffer = malloc(PoolSize * ObjectSize); } void Close() { free(buffer); } void Free(T * obj) { auto newList = (FreeList*)obj; newList->Next = freeList; freeList = newList; } void * Buffer() { return buffer; } T * Alloc() { auto rs = GetFreeObject(); if (!rs) { if (allocPtr < PoolSize) { rs = (T*)buffer + allocPtr; allocPtr++; } } if (!rs) { throw OutofPoolMemoryException(); } return rs; } }; }; #define USE_POOL_ALLOCATOR(T, PoolSize) \ private:\ static CoreLib::ObjectPool _pool;\ public:\ void * operator new(std::size_t) { return _pool.Alloc(); } \ void operator delete(void * ptr) {_pool.Free((T*)ptr); }\ int GetObjectId() { return (int)(this - (T*)_pool.Buffer()); }\ static void ClosePool(); #define IMPL_POOL_ALLOCATOR(T, PoolSize) \ CoreLib::ObjectPool T::_pool;\ void T::ClosePool() { _pool.Close(); } } #endif ================================================ FILE: Source/CoreLib/SecureCRT.h ================================================ #ifndef _MSC_VER #ifndef CORE_LIB_SECURE_CRT_H #define CORE_LIB_SECURE_CRT_H #include #include #include #include inline void memcpy_s(void *dest, size_t numberOfElements, const void * src, size_t count) { memcpy(dest, src, count); } #define _TRUNCATE ((size_t)-1) #define _stricmp strcasecmp inline void fopen_s(FILE**f, const char * fileName, const char * mode) { *f = fopen(fileName, mode); } inline size_t fread_s(void * buffer, size_t bufferSize, size_t elementSize, size_t count, FILE * stream) { return fread(buffer, elementSize, count, stream); } inline size_t wcsnlen_s(const wchar_t * str, size_t /*numberofElements*/) { return wcslen(str); } inline size_t strnlen_s(const char * str, size_t numberofElements) { return strnlen(str, numberofElements); } inline int sprintf_s(char * buffer, size_t sizeOfBuffer, const char * format, ...) { va_list argptr; va_start(argptr, format); int rs = snprintf(buffer, sizeOfBuffer, format, argptr); va_end(argptr); return rs; } inline int swprintf_s(wchar_t * buffer, size_t sizeOfBuffer, const wchar_t * format, ...) { va_list argptr; va_start(argptr, format); int rs = swprintf(buffer, sizeOfBuffer, format, argptr); va_end(argptr); return rs; } inline void wcscpy_s(wchar_t * strDestination, size_t /*numberOfElements*/, const wchar_t * strSource) { wcscpy(strDestination, strSource); } inline void strcpy_s(char * strDestination, size_t /*numberOfElements*/, const char * strSource) { strcpy(strDestination, strSource); } inline void wcsncpy_s(wchar_t * strDestination, size_t /*numberOfElements*/, const wchar_t * strSource, size_t count) { wcscpy(strDestination, strSource); //wcsncpy(strDestination, strSource, count); } inline void strncpy_s(char * strDestination, size_t /*numberOfElements*/, const char * strSource, size_t count) { strncpy(strDestination, strSource, count); //wcsncpy(strDestination, strSource, count); } #endif #endif ================================================ FILE: Source/CoreLib/SmartPointer.h ================================================ #ifndef FUNDAMENTAL_LIB_SMART_POINTER_H #define FUNDAMENTAL_LIB_SMART_POINTER_H #include "TypeTraits.h" namespace CoreLib { namespace Basic { class RefPtrDefaultDestructor { public: template void operator ()(T * ptr) { delete ptr; } }; class RefPtrArrayDestructor { public: template void operator() (T * ptr) { delete [] ptr; } }; class ReferenceCounted { template friend class RefPtrImpl; private: int _refCount = 0; public: ReferenceCounted() {} ReferenceCounted(const ReferenceCounted &) { _refCount = 0; } }; class RefObject : public ReferenceCounted { public: virtual ~RefObject() {} }; template class RefPtrImpl { }; template using RefPtr = RefPtrImpl::Value, Destructor>; template class RefPtrImpl { template friend class RefPtrImpl; private: T * pointer; int * refCount; public: RefPtrImpl() { pointer = 0; refCount = 0; } RefPtrImpl(T * ptr) : pointer(0), refCount(0) { this->operator=(ptr); } RefPtrImpl(const RefPtrImpl & ptr) : pointer(0), refCount(0) { this->operator=(ptr); } RefPtrImpl(RefPtrImpl && str) : pointer(0), refCount(0) { this->operator=(static_cast &&>(str)); } template RefPtrImpl(const RefPtrImpl& ptr, typename EnableIf::Value, void>::type * = 0) : pointer(0), refCount(0) { pointer = ptr.pointer; if (ptr) { refCount = ptr.refCount; (*refCount)++; } else refCount = 0; } template typename EnableIf::value, RefPtrImpl>::type& operator=(const RefPtrImpl & ptr) { Unreference(); pointer = ptr; if (ptr) { refCount = ptr.refCount; (*refCount)++; } else refCount = 0; return *this; } RefPtrImpl& operator=(const RefPtrImpl & ptr) { Unreference(); pointer = ptr.pointer; if (ptr) { refCount = ptr.refCount; (*refCount)++; } else refCount = 0; return *this; } RefPtrImpl& operator=(T * ptr) { if (ptr != pointer) { Unreference(); pointer = ptr; if (ptr) { refCount = new int; (*refCount) = 1; } else refCount = 0; } return *this; } int GetHashCode() { return (int)(long long)(void*)pointer; } bool operator == (const T * ptr) const { return pointer == ptr; } bool operator != (const T * ptr) const { return pointer != ptr; } template bool operator == (const RefPtr & ptr) const { return pointer == ptr.pointer; } template bool operator != (const RefPtr & ptr) const { return pointer != ptr.pointer; } template RefPtrImpl As() const { RefPtrImpl result; if (pointer) { result.pointer = dynamic_cast(pointer); if (result.pointer) { result.refCount = refCount; (*refCount)++; } } return result; } T* operator +(int offset) const { return pointer+offset; } T& operator [](int idx) const { return *(pointer + idx); } RefPtrImpl& operator=(RefPtrImpl && ptr) { if(ptr.pointer != pointer) { Unreference(); pointer = ptr.pointer; refCount = ptr.refCount; ptr.pointer = 0; ptr.refCount = 0; } return *this; } T* Release() { if(pointer) { if((*refCount) > 1) { (*refCount)--; } else { delete refCount; } } auto rs = pointer; refCount = 0; pointer = 0; return rs; } ~RefPtrImpl() { Unreference(); } void Unreference() { if(pointer) { if((*refCount) > 1) { (*refCount)--; } else { Destructor destructor; destructor(pointer); delete refCount; } } } T & operator *() const { return *pointer; } T * operator->() const { return pointer; } T * Ptr() const { return pointer; } public: explicit operator bool() const { if (pointer) return true; else return false; } }; template class RefPtrImpl { template friend class RefPtrImpl; private: T * pointer; public: RefPtrImpl() { pointer = 0; } RefPtrImpl(T * ptr) : pointer(0) { this->operator=(ptr); } RefPtrImpl(const RefPtrImpl & ptr) : pointer(0) { this->operator=(ptr); } RefPtrImpl(RefPtrImpl && str) : pointer(0) { this->operator=(static_cast &&>(str)); } template RefPtrImpl(const RefPtrImpl& ptr, typename EnableIf::Value, void>::type * = 0) : pointer(0) { pointer = ptr.pointer; if (ptr) { ptr->_refCount++; } } template typename EnableIf::value, RefPtrImpl&>::type operator=(const RefPtrImpl & ptr) { Unreference(); pointer = ptr.pointer; if (ptr) { ptr->_refCount++; } return *this; } RefPtrImpl& operator=(T * ptr) { if (ptr != pointer) { Unreference(); pointer = ptr; if (ptr) { ptr->_refCount++; } } return *this; } RefPtrImpl& operator=(const RefPtrImpl & ptr) { if (ptr.pointer != pointer) { Unreference(); pointer = ptr.pointer; if (pointer) pointer->_refCount++; } return *this; } int GetHashCode() { return (int)(long long)(void*)pointer; } bool operator == (const T * ptr) const { return pointer == ptr; } bool operator != (const T * ptr) const { return pointer != ptr; } template bool operator == (const RefPtr & ptr) const { return pointer == ptr.pointer; } template bool operator != (const RefPtr & ptr) const { return pointer != ptr.pointer; } template RefPtrImpl As() const { RefPtrImpl result; if (pointer) { result.pointer = dynamic_cast(pointer); if (result.pointer) { result.pointer->_refCount++; } } return result; } T* operator +(int offset) const { return pointer + offset; } T& operator [](int idx) const { return *(pointer + idx); } RefPtrImpl& operator=(RefPtrImpl && ptr) { if (ptr.pointer != pointer) { Unreference(); pointer = ptr.pointer; ptr.pointer = nullptr; } return *this; } T* Release() { if (pointer) { pointer->_refCount--; } auto rs = pointer; pointer = 0; return rs; } ~RefPtrImpl() { Unreference(); } void Unreference() { if (pointer) { if (pointer->_refCount > 1) { pointer->_refCount--; } else { Destructor destructor; destructor(pointer); } } } T & operator *() const { return *pointer; } T * operator->() const { return pointer; } T * Ptr() const { return pointer; } public: explicit operator bool() const { if (pointer) return true; else return false; } }; } } #endif ================================================ FILE: Source/CoreLib/Stream.cpp ================================================ #include "Stream.h" #ifdef _WIN32 #include #endif #include "LibIO.h" namespace CoreLib { namespace IO { using namespace CoreLib::Basic; FileStream::FileStream(const CoreLib::Basic::String & fileName, FileMode fileMode) { Init(fileName, fileMode, fileMode==FileMode::Open?FileAccess::Read:FileAccess::Write, FileShare::None); } FileStream::FileStream(const CoreLib::Basic::String & fileName, FileMode fileMode, FileAccess access, FileShare share) { Init(fileName, fileMode, access, share); } void FileStream::Init(const CoreLib::Basic::String & fileName, FileMode fileMode, FileAccess access, FileShare share) { const wchar_t * mode = L"rt"; const char* modeMBCS = "rt"; switch (fileMode) { case CoreLib::IO::FileMode::Create: if (access == FileAccess::Read) throw ArgumentException("Read-only access is incompatible with Create mode."); else if (access == FileAccess::ReadWrite) { mode = L"w+b"; modeMBCS = "w+b"; this->fileAccess = FileAccess::ReadWrite; } else { mode = L"wb"; modeMBCS = "wb"; this->fileAccess = FileAccess::Write; } break; case CoreLib::IO::FileMode::Open: if (access == FileAccess::Read) { mode = L"rb"; modeMBCS = "rb"; this->fileAccess = FileAccess::Read; } else if (access == FileAccess::ReadWrite) { mode = L"r+b"; modeMBCS = "r+b"; this->fileAccess = FileAccess::ReadWrite; } else { mode = L"wb"; modeMBCS = "wb"; this->fileAccess = FileAccess::Write; } break; case CoreLib::IO::FileMode::CreateNew: if (File::Exists(fileName)) { throw IOException("Failed opening '" + fileName + "', file already exists."); } if (access == FileAccess::Read) throw ArgumentException("Read-only access is incompatible with Create mode."); else if (access == FileAccess::ReadWrite) { mode = L"w+b"; this->fileAccess = FileAccess::ReadWrite; } else { mode = L"wb"; this->fileAccess = FileAccess::Write; } break; case CoreLib::IO::FileMode::Append: if (access == FileAccess::Read) throw ArgumentException("Read-only access is incompatible with Append mode."); else if (access == FileAccess::ReadWrite) { mode = L"a+b"; this->fileAccess = FileAccess::ReadWrite; } else { mode = L"ab"; this->fileAccess = FileAccess::Write; } break; default: break; } int shFlag; #ifdef _WIN32 switch (share) { case CoreLib::IO::FileShare::None: shFlag = _SH_DENYRW; break; case CoreLib::IO::FileShare::ReadOnly: shFlag = _SH_DENYWR; break; case CoreLib::IO::FileShare::WriteOnly: shFlag = _SH_DENYRD; break; case CoreLib::IO::FileShare::ReadWrite: shFlag = _SH_DENYNO; break; default: throw ArgumentException("Invalid file share mode."); break; } handle = _wfsopen(fileName.ToWString(), mode, shFlag); #else handle = fopen(fileName.Buffer(), modeMBCS); #endif if (!handle) { throw IOException("Cannot open file '" + fileName + "'"); } } FileStream::~FileStream() { Close(); } Int64 FileStream::GetPosition() { #ifdef _WIN32 fpos_t pos; fgetpos(handle, &pos); return pos; #else fpos64_t pos; fgetpos64(handle, &pos); return *(Int64*)(&pos); #endif } void FileStream::Seek(SeekOrigin origin, Int64 offset) { int _origin; switch (origin) { case CoreLib::IO::SeekOrigin::Start: _origin = SEEK_SET; endReached = false; break; case CoreLib::IO::SeekOrigin::End: _origin = SEEK_END; endReached = true; break; case CoreLib::IO::SeekOrigin::Current: _origin = SEEK_CUR; endReached = false; break; default: throw NotSupportedException("Unsupported seek origin."); break; } #ifdef _WIN32 int rs = _fseeki64(handle, offset, _origin); #else int rs = fseek(handle, (int)offset, _origin); #endif if (rs != 0) { throw IOException("FileStream seek failed."); } } Int64 FileStream::Read(void * buffer, Int64 length) { auto bytes = fread_s(buffer, (size_t)length, 1, (size_t)length, handle); if (bytes == 0 && length > 0) { if (!feof(handle)) throw IOException("FileStream read failed."); else if (endReached) throw EndOfStreamException("End of file is reached."); endReached = true; } return (int)bytes; } Int64 FileStream::Write(const void * buffer, Int64 length) { auto bytes = (Int64)fwrite(buffer, 1, (size_t)length, handle); if (bytes < length) { throw IOException("FileStream write failed."); } return bytes; } bool FileStream::CanRead() { return ((int)fileAccess & (int)FileAccess::Read) != 0; } bool FileStream::CanWrite() { return ((int)fileAccess & (int)FileAccess::Write) != 0; } void FileStream::Close() { if (handle) { fclose(handle); handle = 0; } } bool FileStream::IsEnd() { return endReached; } } } ================================================ FILE: Source/CoreLib/Stream.h ================================================ #ifndef CORE_LIB_STREAM_H #define CORE_LIB_STREAM_H #include "Basic.h" namespace CoreLib { namespace IO { using CoreLib::Basic::Exception; using CoreLib::Basic::String; using CoreLib::Basic::RefPtr; class IOException : public Exception { public: IOException() {} IOException(const String & message) : CoreLib::Basic::Exception(message) { } }; class EndOfStreamException : public IOException { public: EndOfStreamException() {} EndOfStreamException(const String & message) : IOException(message) { } }; enum class SeekOrigin { Start, End, Current }; class Stream : public CoreLib::Basic::Object { public: virtual Int64 GetPosition()=0; virtual void Seek(SeekOrigin origin, Int64 offset)=0; virtual Int64 Read(void * buffer, Int64 length) = 0; virtual Int64 Write(const void * buffer, Int64 length) = 0; virtual bool IsEnd() = 0; virtual bool CanRead() = 0; virtual bool CanWrite() = 0; virtual void Close() = 0; }; class BinaryReader { private: RefPtr stream; inline void Throw(Int64 val) { if (val == 0) throw IOException("read operation failed."); } public: BinaryReader(RefPtr stream) { this->stream = stream; } Stream * GetStream() { return stream.Ptr(); } void ReleaseStream() { stream.Release(); } template void Read(T * buffer, int count) { stream->Read(buffer, sizeof(T)*(Int64)count); } template void Read(T & buffer) { Throw(stream->Read(&buffer, sizeof(T))); } template void Read(List & buffer) { int count = ReadInt32(); buffer.SetSize(count); Read(buffer.Buffer(), count); } void Read(String & buffer) { buffer = ReadString(); } int ReadInt32() { int rs; Throw(stream->Read(&rs, sizeof(int))); return rs; } short ReadInt16() { short rs; Throw(stream->Read(&rs, sizeof(short))); return rs; } Int64 ReadInt64() { Int64 rs; Throw(stream->Read(&rs, sizeof(Int64))); return rs; } float ReadFloat() { float rs; Throw(stream->Read(&rs, sizeof(float))); return rs; } double ReadDouble() { double rs; Throw(stream->Read(&rs, sizeof(double))); return rs; } char ReadChar() { char rs; Throw(stream->Read(&rs, sizeof(char))); return rs; } String ReadString() { int len = ReadInt32(); char * buffer = new char[len+1]; try { Throw(stream->Read(buffer, len)); } catch(IOException & e) { delete [] buffer; throw e; } buffer[len] = 0; return String::FromBuffer(buffer, len); } }; class BinaryWriter { private: RefPtr stream; public: BinaryWriter(RefPtr stream) { this->stream = stream; } Stream * GetStream() { return stream.Ptr(); } template void Write(const T& val) { stream->Write(&val, sizeof(T)); } template void Write(T * buffer, int count) { stream->Write(buffer, sizeof(T)*(Int64)count); } template void Write(const List & list) { Write(list.Count()); stream->Write(list.Buffer(), sizeof(T)*list.Count()); } void Write(const String & str) { Write(str.Length()); Write(str.Buffer(), str.Length()); } void ReleaseStream() { stream.Release(); } void Close() { stream->Close(); } }; enum class FileMode { Create, Open, CreateNew, Append }; enum class FileAccess { Read = 1, Write = 2, ReadWrite = 3 }; enum class FileShare { None, ReadOnly, WriteOnly, ReadWrite }; class FileStream : public Stream { private: FILE * handle; FileAccess fileAccess; bool endReached = false; void Init(const CoreLib::Basic::String & fileName, FileMode fileMode, FileAccess access, FileShare share); public: FileStream(const CoreLib::Basic::String & fileName, FileMode fileMode = FileMode::Open); FileStream(const CoreLib::Basic::String & fileName, FileMode fileMode, FileAccess access, FileShare share); ~FileStream(); public: virtual Int64 GetPosition(); virtual void Seek(SeekOrigin origin, Int64 offset); virtual Int64 Read(void * buffer, Int64 length); virtual Int64 Write(const void * buffer, Int64 length); virtual bool CanRead(); virtual bool CanWrite(); virtual void Close(); virtual bool IsEnd(); }; class MemoryStream : public Stream { private: CoreLib::List writeBuffer; CoreLib::ArrayView readBuffer; int ptr = 0; bool isReadStream; public: MemoryStream() { isReadStream = false; } MemoryStream(unsigned char * mem, int length) { isReadStream = true; readBuffer = MakeArrayView(mem, length); } MemoryStream(CoreLib::ArrayView source) { isReadStream = true; readBuffer = source; } virtual Int64 GetPosition() { return ptr; } virtual void Seek(SeekOrigin origin, Int64 offset) { if (origin == SeekOrigin::Start) ptr = (int)offset; else if (origin == SeekOrigin::End) { if (isReadStream) ptr = readBuffer.Count() + (int)offset; else ptr = writeBuffer.Count() + (int)offset; } } virtual Int64 Read(void * pbuffer, Int64 length) { Int64 i; for (i = 0; i < length; i++) { if (ptr + i < readBuffer.Count()) { ((unsigned char*)pbuffer)[i] = readBuffer[(int)(ptr + i)]; } else break; } return i; } virtual Int64 Write(const void * pbuffer, Int64 length) { writeBuffer.SetSize(ptr); if (pbuffer) writeBuffer.AddRange((unsigned char *)pbuffer, (int)length); else for (auto i = 0; i < length; i++) writeBuffer.Add(0); ptr = writeBuffer.Count(); return length; } virtual bool CanRead() { return isReadStream; } virtual bool CanWrite() { return !isReadStream; } virtual void Close() { writeBuffer.SetSize(0); writeBuffer.Compress(); } virtual bool IsEnd() { if (isReadStream) return ptr >= readBuffer.Count(); else return ptr == writeBuffer.Count(); } void * GetBuffer() { if (isReadStream) return readBuffer.Buffer(); else return writeBuffer.Buffer(); } int GetBufferSize() { if (isReadStream) return readBuffer.Count(); else return writeBuffer.Count(); } }; } } #endif ================================================ FILE: Source/CoreLib/TextIO.cpp ================================================ #include "TextIO.h" #ifdef _WIN32 #include #define CONVERT_END_OF_LINE #endif namespace CoreLib { namespace IO { using namespace CoreLib::Basic; class Utf8Encoding : public Encoding { public: virtual void GetBytes(List & result, const String & str) override { result.AddRange(str.Buffer(), str.Length()); } virtual String ToString(const char * bytes, int /*length*/) override { return String(bytes); } }; class Utf32Encoding : public Encoding { public: virtual void GetBytes(List & result, const String & str) override { int ptr = 0; while (ptr < str.Length()) { int codePoint = GetUnicodePointFromUTF8([&](int) { if (ptr < str.Length()) return str[ptr++]; else return '\0'; }); result.AddRange((char*)&codePoint, 4); } } virtual String ToString(const char * bytes, int length) override { StringBuilder sb; int * content = (int*)bytes; for (int i = 0; i < (length >> 2); i++) { char buf[5]; int count = EncodeUnicodePointToUTF8(buf, content[i]); for (int j = 0; j < count; j++) sb.Append(buf[j]); } return sb.ProduceString(); } }; class Utf16Encoding : public Encoding //UTF16 { private: bool reverseOrder = false; public: Utf16Encoding(bool pReverseOrder) : reverseOrder(pReverseOrder) {} virtual void GetBytes(List & result, const String & str) override { int ptr = 0; while (ptr < str.Length()) { int codePoint = GetUnicodePointFromUTF8([&](int) { if (ptr < str.Length()) return str[ptr++]; else return '\0'; }); unsigned short buffer[2]; int count; if (!reverseOrder) count = EncodeUnicodePointToUTF16(buffer, codePoint); else count = EncodeUnicodePointToUTF16Reversed(buffer, codePoint); result.AddRange((char*)buffer, count * 2); } } virtual String ToString(const char * bytes, int length) override { int ptr = 0; StringBuilder sb; while (ptr < length) { int codePoint = GetUnicodePointFromUTF16([&](int) { if (ptr < length) return bytes[ptr++]; else return '\0'; }); char buf[5]; int count = EncodeUnicodePointToUTF8(buf, codePoint); for (int i = 0; i < count; i++) sb.Append(buf[i]); } return sb.ProduceString(); } }; Utf8Encoding __utf8Encoding; Utf16Encoding __utf16Encoding(false); Utf16Encoding __utf16EncodingReversed(true); Utf32Encoding __utf32Encoding; Encoding * Encoding::UTF8 = &__utf8Encoding; Encoding * Encoding::UTF16 = &__utf16Encoding; Encoding * Encoding::UTF16Reversed = &__utf16EncodingReversed; Encoding * Encoding::UTF32 = &__utf32Encoding; const unsigned short Utf16Header = 0xFEFF; const unsigned short Utf16ReversedHeader = 0xFFFE; StreamWriter::StreamWriter(const String & path, Encoding * encoding) { this->stream = new FileStream(path, FileMode::Create); this->encoding = encoding; if (encoding == Encoding::UTF16) { this->stream->Write(&Utf16Header, 2); } else if (encoding == Encoding::UTF16Reversed) { this->stream->Write(&Utf16ReversedHeader, 2); } } StreamWriter::StreamWriter(RefPtr stream, Encoding * encoding) { this->stream = stream; this->encoding = encoding; if (encoding == Encoding::UTF16) { this->stream->Write(&Utf16Header, 2); } else if (encoding == Encoding::UTF16Reversed) { this->stream->Write(&Utf16ReversedHeader, 2); } } void StreamWriter::Write(const String & str) { encodingBuffer.Clear(); StringBuilder sb; String newLine; #ifdef _WIN32 newLine = "\r\n"; #else newLine = "\n"; #endif for (int i = 0; i < str.Length(); i++) { if (str[i] == '\r') sb << newLine; else if (str[i] == '\n') { if (i > 0 && str[i - 1] != '\r') sb << newLine; } else sb << str[i]; } encoding->GetBytes(encodingBuffer, sb.ProduceString()); stream->Write(encodingBuffer.Buffer(), encodingBuffer.Count()); } void StreamWriter::Write(const char * str) { Write(String(str)); } StreamReader::StreamReader(const String & path) { stream = new FileStream(path, FileMode::Open); ReadBuffer(); encoding = DetermineEncoding(); if (encoding == 0) encoding = Encoding::UTF8; } StreamReader::StreamReader(RefPtr stream, Encoding * encoding) { this->stream = stream; this->encoding = encoding; ReadBuffer(); auto determinedEncoding = DetermineEncoding(); if (this->encoding == nullptr) this->encoding = determinedEncoding; } Encoding * StreamReader::DetermineEncoding() { if (buffer.Count() >= 3 && (unsigned char)(buffer[0]) == 0xEF && (unsigned char)(buffer[1]) == 0xBB && (unsigned char)(buffer[2]) == 0xBF) { ptr += 3; return Encoding::UTF8; } else if (*((unsigned short*)(buffer.Buffer())) == 0xFEFF) { ptr += 2; return Encoding::UTF16; } else if (*((unsigned short*)(buffer.Buffer())) == 0xFFFE) { ptr += 2; return Encoding::UTF16Reversed; } else { #ifdef _WIN32 int flag = IS_TEXT_UNICODE_SIGNATURE | IS_TEXT_UNICODE_REVERSE_SIGNATURE | IS_TEXT_UNICODE_STATISTICS | IS_TEXT_UNICODE_ASCII16; int rs = IsTextUnicode(buffer.Buffer(), buffer.Count(), &flag); if (rs) { if (flag & (IS_TEXT_UNICODE_SIGNATURE | IS_TEXT_UNICODE_STATISTICS)) return Encoding::UTF16; else if (flag & (IS_TEXT_UNICODE_SIGNATURE | IS_TEXT_UNICODE_STATISTICS)) return Encoding::UTF16Reversed; else if (flag & IS_TEXT_UNICODE_ASCII16) return Encoding::UTF8; } #endif return Encoding::UTF8; } } void StreamReader::ReadBuffer() { buffer.SetSize(4096); auto len = stream->Read(buffer.Buffer(), buffer.Count()); buffer.SetSize((int)len); ptr = 0; } char StreamReader::ReadBufferChar() { if (ptrIsEnd()) ReadBuffer(); if (ptr TextWriter & operator << (const T& val) { Write(val.ToString()); return *this; } TextWriter & operator << (int value) { Write(String(value)); return *this; } TextWriter & operator << (float value) { Write(String(value)); return *this; } TextWriter & operator << (double value) { Write(String(value)); return *this; } TextWriter & operator << (const char* value) { Write(value); return *this; } TextWriter & operator << (const String & val) { Write(val); return *this; } TextWriter & operator << (const _EndLine &) { #ifdef _WIN32 Write("\r\n"); #else Write("\n"); #endif return *this; } }; template int GetUnicodePointFromUTF8(const ReadCharFunc & get) { int codePoint = 0; int leading = get(0); int mask = 0x80; int count = 0; while (leading & mask) { count++; mask >>= 1; } codePoint = (leading & (mask - 1)); for (int i = 1; i <= count - 1; i++) { codePoint <<= 6; codePoint += (get(i) & 0x3F); } return codePoint; } template int GetUnicodePointFromUTF16(const ReadCharFunc & get) { int byte0 = (unsigned char)get(0); int byte1 = (unsigned char)get(1); int word0 = byte0 + (byte1 << 8); if (word0 >= 0xD800 && word0 <= 0xDFFF) { int byte2 = (unsigned char)get(2); int byte3 = (unsigned char)get(3); int word1 = byte2 + (byte3 << 8); return ((word0 & 0x3FF) << 10) + (word1 & 0x3FF) + 0x10000; } else return word0; } template int GetUnicodePointFromUTF16Reversed(const ReadCharFunc & get) { int byte0 = (unsigned char)get(0); int byte1 = (unsigned char)get(1); int word0 = (byte0 << 8) + byte1; if (word0 >= 0xD800 && word0 <= 0xDFFF) { int byte2 = (unsigned char)get(2); int byte3 = (unsigned char)get(3); int word1 = (byte2 << 8) + byte3; return ((word0 & 0x3FF) << 10) + (word1 & 0x3FF); } else return word0; } template int GetUnicodePointFromUTF32(const ReadCharFunc & get) { int byte0 = (unsigned char)get(0); int byte1 = (unsigned char)get(1); int byte2 = (unsigned char)get(2); int byte3 = (unsigned char)get(3); return byte0 + (byte1 << 8) + (byte2 << 16) + (byte3 << 24); } inline int EncodeUnicodePointToUTF8(char * buffer, int codePoint) { int count = 0; if (codePoint <= 0x7F) buffer[count++] = ((char)codePoint); else if (codePoint <= 0x7FF) { unsigned char byte = (unsigned char)(0xC0 + (codePoint >> 6)); buffer[count++] = ((char)byte); byte = 0x80 + (codePoint & 0x3F); buffer[count++] = ((char)byte); } else if (codePoint <= 0xFFFF) { unsigned char byte = (unsigned char)(0xE0 + (codePoint >> 12)); buffer[count++] = ((char)byte); byte = (unsigned char)(0x80 + ((codePoint >> 6) & (0x3F))); buffer[count++] = ((char)byte); byte = (unsigned char)(0x80 + (codePoint & 0x3F)); buffer[count++] = ((char)byte); } else { unsigned char byte = (unsigned char)(0xF0 + (codePoint >> 18)); buffer[count++] = ((char)byte); byte = (unsigned char)(0x80 + ((codePoint >> 12) & 0x3F)); buffer[count++] = ((char)byte); byte = (unsigned char)(0x80 + ((codePoint >> 6) & 0x3F)); buffer[count++] = ((char)byte); byte = (unsigned char)(0x80 + (codePoint & 0x3F)); buffer[count++] = ((char)byte); } return count; } inline int EncodeUnicodePointToUTF16(unsigned short * buffer, int codePoint) { int count = 0; if (codePoint <= 0xD7FF || (codePoint >= 0xE000 && codePoint <= 0xFFFF)) buffer[count++] = (unsigned short)codePoint; else { int sub = codePoint - 0x10000; int high = (sub >> 10) + 0xD800; int low = (sub & 0x3FF) + 0xDC00; buffer[count++] = (unsigned short)high; buffer[count++] = (unsigned short)low; } return count; } inline unsigned short ReverseBitOrder(unsigned short val) { int byte0 = val & 0xFF; int byte1 = val >> 8; return (unsigned short)(byte1 + (byte0 << 8)); } inline int EncodeUnicodePointToUTF16Reversed(unsigned short * buffer, int codePoint) { int count = 0; if (codePoint <= 0xD7FF || (codePoint >= 0xE000 && codePoint <= 0xFFFF)) buffer[count++] = ReverseBitOrder((unsigned short)codePoint); else { int sub = codePoint - 0x10000; int high = (sub >> 10) + 0xD800; int low = (sub & 0x3FF) + 0xDC00; buffer[count++] = ReverseBitOrder((unsigned short)high); buffer[count++] = ReverseBitOrder((unsigned short)low); } return count; } class Encoding { public: static Encoding * UTF8, * UTF16, *UTF16Reversed, * UTF32; virtual void GetBytes(List& buffer, const String & str) = 0; virtual String ToString(const char * buffer, int length) = 0; virtual ~Encoding() {} }; class StreamWriter : public TextWriter { private: List encodingBuffer; RefPtr stream; Encoding * encoding; public: StreamWriter(const String & path, Encoding * encoding = Encoding::UTF8); StreamWriter(RefPtr stream, Encoding * encoding = Encoding::UTF8); virtual void Write(const String & str); virtual void Write(const char * str); virtual void Close() { stream->Close(); } void ReleaseStream() { stream.Release(); } }; class StreamReader : public TextReader { private: RefPtr stream; List buffer; Encoding * encoding; int ptr; char ReadBufferChar(); void ReadBuffer(); Encoding * DetermineEncoding(); protected: virtual void ReadChar() { decodedCharPtr = 0; int codePoint = 0; if (encoding == Encoding::UTF8) codePoint = GetUnicodePointFromUTF8([&](int) {return ReadBufferChar(); }); else if (encoding == Encoding::UTF16) codePoint = GetUnicodePointFromUTF16([&](int) {return ReadBufferChar(); }); else if (encoding == Encoding::UTF16Reversed) codePoint = GetUnicodePointFromUTF16Reversed([&](int) {return ReadBufferChar(); }); else if (encoding == Encoding::UTF32) codePoint = GetUnicodePointFromUTF32([&](int) {return ReadBufferChar(); }); decodedCharSize = EncodeUnicodePointToUTF8(decodedChar, codePoint); } public: StreamReader(const String & path); StreamReader(RefPtr stream, Encoding * encoding = nullptr); virtual String ReadLine(); virtual String ReadToEnd(); virtual bool IsEnd() { return ptr == buffer.Count() && stream->IsEnd(); } virtual void Close() { stream->Close(); } void ReleaseStream() { stream.Release(); } }; } } #endif ================================================ FILE: Source/CoreLib/Tokenizer.cpp ================================================ #include "Tokenizer.h" using namespace CoreLib::Basic; namespace CoreLib { namespace Text { TokenReader::TokenReader(String text) { this->tokens = TokenizeText("", text, [&](TokenizeErrorType, CodePosition) {legal = false; }); tokenPtr = 0; } enum class State { Start, Identifier, Operator, Int, Hex, Fixed, Double, Char, String, MultiComment, SingleComment }; enum class LexDerivative { None, Line, File }; void ParseOperators(const String & str, List & tokens, TokenFlags& tokenFlags, int line, int col, int startPos, String fileName) { int pos = 0; while (pos < str.Length()) { wchar_t curChar = str[pos]; wchar_t nextChar = (pos < str.Length() - 1) ? str[pos + 1] : '\0'; wchar_t nextNextChar = (pos < str.Length() - 2) ? str[pos + 2] : '\0'; auto InsertToken = [&](TokenType type, const String & ct) { tokens.Add(Token(type, ct, line, col + pos, pos + startPos, fileName, tokenFlags)); tokenFlags = 0; }; switch (curChar) { case '+': if (nextChar == '+') { InsertToken(TokenType::OpInc, "++"); pos += 2; } else if (nextChar == '=') { InsertToken(TokenType::OpAddAssign, "+="); pos += 2; } else { InsertToken(TokenType::OpAdd, "+"); pos++; } break; case '-': if (nextChar == '-') { InsertToken(TokenType::OpDec, "--"); pos += 2; } else if (nextChar == '=') { InsertToken(TokenType::OpSubAssign, "-="); pos += 2; } else if (nextChar == '>') { InsertToken(TokenType::RightArrow, "->"); pos += 2; } else { InsertToken(TokenType::OpSub, "-"); pos++; } break; case '*': if (nextChar == '=') { InsertToken(TokenType::OpMulAssign, "*="); pos += 2; } else { InsertToken(TokenType::OpMul, "*"); pos++; } break; case '/': if (nextChar == '=') { InsertToken(TokenType::OpDivAssign, "/="); pos += 2; } else { InsertToken(TokenType::OpDiv, "/"); pos++; } break; case '%': if (nextChar == '=') { InsertToken(TokenType::OpModAssign, "%="); pos += 2; } else { InsertToken(TokenType::OpMod, "%"); pos++; } break; case '|': if (nextChar == '|') { InsertToken(TokenType::OpOr, "||"); pos += 2; } else if (nextChar == '=') { InsertToken(TokenType::OpOrAssign, "|="); pos += 2; } else { InsertToken(TokenType::OpBitOr, "|"); pos++; } break; case '&': if (nextChar == '&') { InsertToken(TokenType::OpAnd, "&&"); pos += 2; } else if (nextChar == '=') { InsertToken(TokenType::OpAndAssign, "&="); pos += 2; } else { InsertToken(TokenType::OpBitAnd, "&"); pos++; } break; case '^': if (nextChar == '=') { InsertToken(TokenType::OpXorAssign, "^="); pos += 2; } else { InsertToken(TokenType::OpBitXor, "^"); pos++; } break; case '>': if (nextChar == '>') { if (nextNextChar == '=') { InsertToken(TokenType::OpShrAssign, ">>="); pos += 3; } else { InsertToken(TokenType::OpRsh, ">>"); pos += 2; } } else if (nextChar == '=') { InsertToken(TokenType::OpGeq, ">="); pos += 2; } else { InsertToken(TokenType::OpGreater, ">"); pos++; } break; case '<': if (nextChar == '<') { if (nextNextChar == '=') { InsertToken(TokenType::OpShlAssign, "<<="); pos += 3; } else { InsertToken(TokenType::OpLsh, "<<"); pos += 2; } } else if (nextChar == '=') { InsertToken(TokenType::OpLeq, "<="); pos += 2; } else { InsertToken(TokenType::OpLess, "<"); pos++; } break; case '=': if (nextChar == '=') { InsertToken(TokenType::OpEql, "=="); pos += 2; } else { InsertToken(TokenType::OpAssign, "="); pos++; } break; case '!': if (nextChar == '=') { InsertToken(TokenType::OpNeq, "!="); pos += 2; } else { InsertToken(TokenType::OpNot, "!"); pos++; } break; case '?': InsertToken(TokenType::QuestionMark, "?"); pos++; break; case '@': InsertToken(TokenType::At, "@"); pos++; break; case '#': if (nextChar == '#') { InsertToken(TokenType::PoundPound, "##"); pos += 2; } else { InsertToken(TokenType::Pound, "#"); pos++; } pos++; break; case ':': InsertToken(TokenType::Colon, ":"); pos++; break; case '~': InsertToken(TokenType::OpBitNot, "~"); pos++; break; case ';': InsertToken(TokenType::Semicolon, ";"); pos++; break; case ',': InsertToken(TokenType::Comma, ","); pos++; break; case '.': InsertToken(TokenType::Dot, "."); pos++; break; case '{': InsertToken(TokenType::LBrace, "{"); pos++; break; case '}': InsertToken(TokenType::RBrace, "}"); pos++; break; case '[': InsertToken(TokenType::LBracket, "["); pos++; break; case ']': InsertToken(TokenType::RBracket, "]"); pos++; break; case '(': InsertToken(TokenType::LParent, "("); pos++; break; case ')': InsertToken(TokenType::RParent, ")"); pos++; break; } } } List TokenizeText(const String & fileName, const String & text, Procedure errorHandler) { int lastPos = 0, pos = 0; int line = 1, col = 0; String file = fileName; State state = State::Start; StringBuilder tokenBuilder; int tokenLine, tokenCol; List tokenList; LexDerivative derivative = LexDerivative::None; TokenFlags tokenFlags = TokenFlag::AtStartOfLine; auto InsertToken = [&](TokenType type) { derivative = LexDerivative::None; tokenList.Add(Token(type, tokenBuilder.ToString(), tokenLine, tokenCol, pos, file, tokenFlags)); tokenFlags = 0; tokenBuilder.Clear(); }; auto ProcessTransferChar = [&](char nextChar) { switch (nextChar) { case '\\': case '\"': case '\'': tokenBuilder.Append(nextChar); break; case 't': tokenBuilder.Append('\t'); break; case 's': tokenBuilder.Append(' '); break; case 'n': tokenBuilder.Append('\n'); break; case 'r': tokenBuilder.Append('\r'); break; case 'b': tokenBuilder.Append('\b'); break; } }; while (pos <= text.Length()) { char curChar = (pos < text.Length() ? text[pos] : ' '); char nextChar = (pos < text.Length() - 1) ? text[pos + 1] : '\0'; if (lastPos != pos) { if (curChar == '\n') { line++; col = 0; } else col++; lastPos = pos; } switch (state) { case State::Start: if (IsLetter(curChar)) { state = State::Identifier; tokenLine = line; tokenCol = col; } else if (IsDigit(curChar)) { state = State::Int; tokenLine = line; tokenCol = col; } else if (curChar == '\'') { state = State::Char; pos++; tokenLine = line; tokenCol = col; } else if (curChar == '"') { state = State::String; pos++; tokenLine = line; tokenCol = col; } else if (curChar == '\r' || curChar == '\n') { tokenFlags |= TokenFlag::AtStartOfLine | TokenFlag::AfterWhitespace; pos++; } else if (curChar == ' ' || curChar == '\t' || curChar == -62 || curChar== -96) // -62/-96:non-break space { tokenFlags |= TokenFlag::AfterWhitespace; pos++; } else if (curChar == '/' && nextChar == '/') { state = State::SingleComment; pos += 2; } else if (curChar == '/' && nextChar == '*') { pos += 2; state = State::MultiComment; } else if (curChar == '.' && IsDigit(nextChar)) { tokenBuilder.Append("0."); state = State::Fixed; pos++; } else if (IsPunctuation(curChar)) { state = State::Operator; tokenLine = line; tokenCol = col; } else { errorHandler(TokenizeErrorType::InvalidCharacter, CodePosition(line, col, pos, file)); pos++; } break; case State::Identifier: if (IsLetter(curChar) || IsDigit(curChar)) { tokenBuilder.Append(curChar); pos++; } else { auto tokenStr = tokenBuilder.ToString(); #if 0 if (tokenStr == "#line_reset#") { line = 0; col = 0; tokenBuilder.Clear(); } else if (tokenStr == "#line") { derivative = LexDerivative::Line; tokenBuilder.Clear(); } else if (tokenStr == "#file") { derivative = LexDerivative::File; tokenBuilder.Clear(); line = 0; col = 0; } else #endif InsertToken(TokenType::Identifier); state = State::Start; } break; case State::Operator: if (IsPunctuation(curChar) && !((curChar == '/' && nextChar == '/') || (curChar == '/' && nextChar == '*'))) { tokenBuilder.Append(curChar); pos++; } else { //do token analyze ParseOperators(tokenBuilder.ToString(), tokenList, tokenFlags, tokenLine, tokenCol, pos - tokenBuilder.Length(), file); tokenBuilder.Clear(); state = State::Start; } break; case State::Int: if (IsDigit(curChar)) { tokenBuilder.Append(curChar); pos++; } else if (curChar == '.') { state = State::Fixed; tokenBuilder.Append(curChar); pos++; } else if (curChar == 'e' || curChar == 'E') { state = State::Double; tokenBuilder.Append(curChar); if (nextChar == '-' || nextChar == '+') { tokenBuilder.Append(nextChar); pos++; } pos++; } else if (curChar == 'x') { state = State::Hex; tokenBuilder.Append(curChar); pos++; } else if (curChar == 'u') { pos++; tokenBuilder.Append(curChar); InsertToken(TokenType::IntLiterial); state = State::Start; } else { if (derivative == LexDerivative::Line) { derivative = LexDerivative::None; line = StringToInt(tokenBuilder.ToString()) - 1; col = 0; tokenBuilder.Clear(); } else { InsertToken(TokenType::IntLiterial); } state = State::Start; } break; case State::Hex: if (IsDigit(curChar) || (curChar>='a' && curChar <= 'f') || (curChar >= 'A' && curChar <= 'F')) { tokenBuilder.Append(curChar); pos++; } else { InsertToken(TokenType::IntLiterial); state = State::Start; } break; case State::Fixed: if (IsDigit(curChar)) { tokenBuilder.Append(curChar); pos++; } else if (curChar == 'e' || curChar == 'E') { state = State::Double; tokenBuilder.Append(curChar); if (nextChar == '-' || nextChar == '+') { tokenBuilder.Append(nextChar); pos++; } pos++; } else { if (curChar == 'f') pos++; InsertToken(TokenType::DoubleLiterial); state = State::Start; } break; case State::Double: if (IsDigit(curChar)) { tokenBuilder.Append(curChar); pos++; } else { if (curChar == 'f') pos++; InsertToken(TokenType::DoubleLiterial); state = State::Start; } break; case State::String: if (curChar != '"') { if (curChar == '\\') { ProcessTransferChar(nextChar); pos++; } else tokenBuilder.Append(curChar); } else { if (derivative == LexDerivative::File) { derivative = LexDerivative::None; file = tokenBuilder.ToString(); tokenBuilder.Clear(); } else { InsertToken(TokenType::StringLiterial); } state = State::Start; } pos++; break; case State::Char: if (curChar != '\'') { if (curChar == '\\') { ProcessTransferChar(nextChar); pos++; } else tokenBuilder.Append(curChar); } else { if (tokenBuilder.Length() > 1) errorHandler(TokenizeErrorType::InvalidEscapeSequence, CodePosition(line, col - tokenBuilder.Length(), pos, file)); InsertToken(TokenType::CharLiterial); state = State::Start; } pos++; break; case State::SingleComment: if( curChar == '\n' ) { state = State::Start; tokenFlags |= TokenFlag::AtStartOfLine | TokenFlag::AfterWhitespace; } pos++; break; case State::MultiComment: if (curChar == '*' && nextChar == '/') { state = State::Start; tokenFlags |= TokenFlag::AfterWhitespace; pos += 2; } else pos++; break; } } return tokenList; } List TokenizeText(const String & fileName, const String & text) { return TokenizeText(fileName, text, [](TokenizeErrorType, CodePosition) {}); } List TokenizeText(const String & text) { return TokenizeText("", text, [](TokenizeErrorType, CodePosition) {}); } String EscapeStringLiteral(String str) { StringBuilder sb; sb << "\""; for (int i = 0; i < str.Length(); i++) { switch (str[i]) { case ' ': sb << "\\s"; break; case '\n': sb << "\\n"; break; case '\r': sb << "\\r"; break; case '\t': sb << "\\t"; break; case '\v': sb << "\\v"; break; case '\'': sb << "\\\'"; break; case '\"': sb << "\\\""; break; case '\\': sb << "\\\\"; break; default: sb << str[i]; break; } } sb << "\""; return sb.ProduceString(); } String UnescapeStringLiteral(String str) { StringBuilder sb; for (int i = 0; i < str.Length(); i++) { if (str[i] == '\\' && i < str.Length() - 1) { switch (str[i + 1]) { case 's': sb << " "; break; case 't': sb << '\t'; break; case 'n': sb << '\n'; break; case 'r': sb << '\r'; break; case 'v': sb << '\v'; break; case '\'': sb << '\''; break; case '\"': sb << "\""; break; case '\\': sb << "\\"; break; default: i = i - 1; sb << str[i]; } i++; } else sb << str[i]; } return sb.ProduceString(); } String TokenTypeToString(TokenType type) { switch (type) { case TokenType::EndOfFile: return "end of file"; case TokenType::Unknown: return "UnknownToken"; case TokenType::Identifier: return "identifier"; case TokenType::IntLiterial: return "integer literal"; case TokenType::DoubleLiterial: return "floating-point literal"; case TokenType::StringLiterial: return "string literal"; case TokenType::CharLiterial: return "character literal"; case TokenType::QuestionMark: return "'?'"; case TokenType::Colon: return "':'"; case TokenType::Semicolon: return "';'"; case TokenType::Comma: return "','"; case TokenType::LBrace: return "'{'"; case TokenType::RBrace: return "'}'"; case TokenType::LBracket: return "'['"; case TokenType::RBracket: return "']'"; case TokenType::LParent: return "'('"; case TokenType::RParent: return "')'"; case TokenType::At: return "'@'"; case TokenType::OpAssign: return "'='"; case TokenType::OpAdd: return "'+'"; case TokenType::OpSub: return "'-'"; case TokenType::OpMul: return "'*'"; case TokenType::OpDiv: return "'/'"; case TokenType::OpMod: return "'%'"; case TokenType::OpNot: return "'!'"; case TokenType::OpLsh: return "'<<'"; case TokenType::OpRsh: return "'>>'"; case TokenType::OpAddAssign: return "'+='"; case TokenType::OpSubAssign: return "'-='"; case TokenType::OpMulAssign: return "'*='"; case TokenType::OpDivAssign: return "'/='"; case TokenType::OpModAssign: return "'%='"; case TokenType::OpEql: return "'=='"; case TokenType::OpNeq: return "'!='"; case TokenType::OpGreater: return "'>'"; case TokenType::OpLess: return "'<'"; case TokenType::OpGeq: return "'>='"; case TokenType::OpLeq: return "'<='"; case TokenType::OpAnd: return "'&&'"; case TokenType::OpOr: return "'||'"; case TokenType::OpBitXor: return "'^'"; case TokenType::OpBitAnd: return "'&'"; case TokenType::OpBitOr: return "'|'"; case TokenType::OpInc: return "'++'"; case TokenType::OpDec: return "'--'"; case TokenType::Pound: return "'#'"; case TokenType::PoundPound: return "'##'"; default: return ""; } } List Split(String text, char c) { List result; StringBuilder sb; for (int i = 0; i < text.Length(); i++) { if (text[i] == c) { auto str = sb.ToString(); if (str.Length() != 0) result.Add(str); sb.Clear(); } else sb << text[i]; } auto lastStr = sb.ToString(); if (lastStr.Length()) result.Add(lastStr); return result; } } } ================================================ FILE: Source/CoreLib/Tokenizer.h ================================================ #ifndef CORELIB_TEXT_PARSER_H #define CORELIB_TEXT_PARSER_H #include "Basic.h" namespace CoreLib { namespace Text { class TextFormatException : public Exception { public: TextFormatException(String message) : Exception(message) {} }; inline bool IsLetter(char ch) { return ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch == '_'); } inline bool IsDigit(char ch) { return ch >= '0' && ch <= '9'; } inline bool IsPunctuation(char ch) { return ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '%' || ch == '!' || ch == '^' || ch == '&' || ch == '(' || ch == ')' || ch == '=' || ch == '{' || ch == '}' || ch == '[' || ch == ']' || ch == '|' || ch == ';' || ch == ',' || ch == '.' || ch == '<' || ch == '>' || ch == '~' || ch == '@' || ch == ':' || ch == '?' || ch == '#'; } inline bool IsWhiteSpace(char ch) { return (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' || ch == '\v'); } class CodePosition { public: int Line = -1, Col = -1, Pos = -1; String FileName; String ToString() { StringBuilder sb(100); sb << FileName; if (Line != -1) sb << "(" << Line << ")"; return sb.ProduceString(); } CodePosition() = default; CodePosition(int line, int col, int pos, String fileName) { Line = line; Col = col; Pos = pos; this->FileName = fileName; } bool operator < (const CodePosition & pos) const { return FileName < pos.FileName || (FileName == pos.FileName && Line < pos.Line) || (FileName == pos.FileName && Line == pos.Line && Col < pos.Col); } bool operator == (const CodePosition & pos) const { return FileName == pos.FileName && Line == pos.Line && Col == pos.Col; } }; enum class TokenType { EndOfFile = -1, // illegal Unknown, // identifier Identifier, // constant IntLiterial, DoubleLiterial, StringLiterial, CharLiterial, // operators Semicolon, Comma, Dot, LBrace, RBrace, LBracket, RBracket, LParent, RParent, OpAssign, OpAdd, OpSub, OpMul, OpDiv, OpMod, OpNot, OpBitNot, OpLsh, OpRsh, OpEql, OpNeq, OpGreater, OpLess, OpGeq, OpLeq, OpAnd, OpOr, OpBitXor, OpBitAnd, OpBitOr, OpInc, OpDec, OpAddAssign, OpSubAssign, OpMulAssign, OpDivAssign, OpModAssign, OpShlAssign, OpShrAssign, OpOrAssign, OpAndAssign, OpXorAssign, QuestionMark, Colon, RightArrow, At, Pound, PoundPound, }; String TokenTypeToString(TokenType type); enum TokenFlag : unsigned int { AtStartOfLine = 1 << 0, AfterWhitespace = 1 << 1, }; typedef unsigned int TokenFlags; class Token { public: TokenType Type = TokenType::Unknown; String Content; CodePosition Position; TokenFlags flags = 0; Token() = default; Token(TokenType type, const String & content, int line, int col, int pos, String fileName, TokenFlags flags = 0) : flags(flags) { Type = type; Content = content; Position = CodePosition(line, col, pos, fileName); } }; enum class TokenizeErrorType { InvalidCharacter, InvalidEscapeSequence }; List TokenizeText(const String & fileName, const String & text, Procedure errorHandler); List TokenizeText(const String & fileName, const String & text); List TokenizeText(const String & text); String EscapeStringLiteral(String str); String UnescapeStringLiteral(String str); class TokenReader { private: bool legal; List tokens; int tokenPtr; public: TokenReader(Basic::String text); int ReadInt() { auto token = ReadToken(); bool neg = false; if (token.Content == '-') { neg = true; token = ReadToken(); } if (token.Type == TokenType::IntLiterial) { if (neg) return -StringToInt(token.Content); else return StringToInt(token.Content); } throw TextFormatException("Text parsing error: int expected."); } unsigned int ReadUInt() { auto token = ReadToken(); if (token.Type == TokenType::IntLiterial) { return StringToUInt(token.Content); } throw TextFormatException("Text parsing error: int expected."); } double ReadDouble() { auto token = ReadToken(); bool neg = false; if (token.Content == '-') { neg = true; token = ReadToken(); } if (token.Type == TokenType::DoubleLiterial || token.Type == TokenType::IntLiterial) { if (neg) return -StringToDouble(token.Content); else return StringToDouble(token.Content); } throw TextFormatException("Text parsing error: floating point value expected."); } float ReadFloat() { return (float)ReadDouble(); } String ReadWord() { auto token = ReadToken(); if (token.Type == TokenType::Identifier) { return token.Content; } throw TextFormatException("Text parsing error: identifier expected."); } String Read(const char * expectedStr) { auto token = ReadToken(); if (token.Content == expectedStr) { return token.Content; } throw TextFormatException("Text parsing error: \'" + String(expectedStr) + "\' expected."); } String Read(String expectedStr) { auto token = ReadToken(); if (token.Content == expectedStr) { return token.Content; } throw TextFormatException("Text parsing error: \'" + expectedStr + "\' expected."); } String ReadStringLiteral() { auto token = ReadToken(); if (token.Type == TokenType::StringLiterial) { return token.Content; } throw TextFormatException("Text parsing error: string literal expected."); } void Back(int count) { tokenPtr -= count; } Token ReadToken() { if (tokenPtr < tokens.Count()) { auto &rs = tokens[tokenPtr]; tokenPtr++; return rs; } throw TextFormatException("Unexpected ending."); } Token NextToken(int offset = 0) { if (tokenPtr + offset < tokens.Count()) return tokens[tokenPtr + offset]; else { Token rs; rs.Type = TokenType::Unknown; return rs; } } bool LookAhead(String token) { if (tokenPtr < tokens.Count()) { auto next = NextToken(); return next.Content == token; } else { return false; } } bool IsEnd() { return tokenPtr == tokens.Count(); } public: bool IsLegalText() { return legal; } }; List Split(String str, char c); } } #endif ================================================ FILE: Source/CoreLib/TypeTraits.h ================================================ #ifndef CORELIB_TYPETRAITS_H #define CORELIB_TYPETRAITS_H namespace CoreLib { namespace Basic { struct TraitResultYes { char x; }; struct TraitResultNo { char x[2]; }; template struct IsBaseOfTraitHost { operator B*() const { return nullptr; } operator D*() { return nullptr; } }; template struct IsBaseOf { template static TraitResultYes Check(D*, T) { return TraitResultYes(); } static TraitResultNo Check(B*, int) { return TraitResultNo(); } enum { Value = sizeof(Check(IsBaseOfTraitHost(), int())) == sizeof(TraitResultYes) }; }; template struct EnableIf {}; template struct EnableIf { typedef T type; }; template struct IsConvertible { static TraitResultYes Use(B) {}; static TraitResultNo Use(...) {}; enum { Value = sizeof(Use(*(D*)(nullptr))) == sizeof(TraitResultYes) }; }; } } #endif ================================================ FILE: Source/CoreLib/VectorMath.cpp ================================================ #include "VectorMath.h" namespace VectorMath { const __m128 Matrix4_M128::VecOne = _mm_set_ps1(1.0f); void Matrix4::Rotation(Matrix4 & rs, const Vec3 & axis, float angle) { float c = cosf(angle); float s = sinf(angle); float t = 1.0f - c; Vec3 nAxis; Vec3::Normalize(nAxis, axis); float x = nAxis.x; float y = nAxis.y; float z = nAxis.z; rs.m[0][0] = 1 + t*(x*x-1); rs.m[1][0] = z*s+t*x*y; rs.m[2][0] = -y*s+t*x*z; rs.m[3][0] = 0.0f; rs.m[0][1] = -z*s+t*x*y; rs.m[1][1] = 1+t*(y*y-1); rs.m[2][1] = x*s+t*y*z; rs.m[3][1] = 0.0f; rs.m[0][2] = y*s+t*x*z; rs.m[1][2] = -x*s+t*y*z; rs.m[2][2] = 1+t*(z*z-1); rs.m[3][2] = 0.0f; rs.m[0][3] = 0.0f; rs.m[1][3] = 0.0f; rs.m[2][3] = 0.0f; rs.m[3][3] = 1.0f; } void Matrix4::Rotation(Matrix4 & rs, float yaw, float pitch, float roll) { Matrix4 mat; Matrix4::RotationY(rs, yaw); Matrix4::RotationX(mat, pitch); Matrix4::Multiply(rs, rs, mat); Matrix4::RotationZ(mat, roll); Matrix4::Multiply(rs, rs, mat); } void Matrix4::GetNormalMatrix(Matrix4 & mOut) const { float fDet = (mi._11 * (mi._22 * mi._33 - mi._23 * mi._32) - mi._12 * (mi._21 * mi._33 - mi._23 * mi._31) + mi._13 * (mi._21 * mi._32 - mi._22 * mi._31)); float fDetInv = 1.0f / fDet; mOut.mi._11 = fDetInv * (mi._22 * mi._33 - mi._23 * mi._32); mOut.mi._21 = -fDetInv * (mi._12 * mi._33 - mi._13 * mi._32); mOut.mi._31 = fDetInv * (mi._12 * mi._23 - mi._13 * mi._22); mOut.mi._12 = -fDetInv * (mi._21 * mi._33 - mi._23 * mi._31); mOut.mi._22 = fDetInv * (mi._11 * mi._33 - mi._13 * mi._31); mOut.mi._32 = -fDetInv * (mi._11 * mi._23 - mi._13 * mi._21); mOut.mi._13 = fDetInv * (mi._21 * mi._32 - mi._22 * mi._31); mOut.mi._23 = -fDetInv * (mi._11 * mi._32 - mi._12 * mi._31); mOut.mi._33 = fDetInv * (mi._11 * mi._22 - mi._12 * mi._21); mOut.mi._14 = 0.0f; mOut.mi._24 = 0.0f; mOut.mi._34 = 0.0f; mOut.mi._41 = 0.0f; mOut.mi._42 = 0.0f; mOut.mi._43 = 0.0f; mOut.mi._44 = 1.0f; } float Matrix4::Inverse3D(Matrix4 & mOut_d) const { if(fabs(mi._44 - 1.0f) > 0.001f) return 0.0f; if(fabs(mi._14)>0.001f || fabs(mi._24)>0.001f || fabs(mi._34)>0.001f) return 0.0f; float fDet = (mi._11 * (mi._22 * mi._33 - mi._23 * mi._32) - mi._12 * (mi._21 * mi._33 - mi._23 * mi._31) + mi._13 * (mi._21 * mi._32 - mi._22 * mi._31)); float fDetInv = 1.0f / fDet; mOut_d.mi._11 = fDetInv * (mi._22 * mi._33 - mi._23 * mi._32); mOut_d.mi._12 = -fDetInv * (mi._12 * mi._33 - mi._13 * mi._32); mOut_d.mi._13 = fDetInv * (mi._12 * mi._23 - mi._13 * mi._22); mOut_d.mi._14 = 0.0f; mOut_d.mi._21 = -fDetInv * (mi._21 * mi._33 - mi._23 * mi._31); mOut_d.mi._22 = fDetInv * (mi._11 * mi._33 - mi._13 * mi._31); mOut_d.mi._23 = -fDetInv * (mi._11 * mi._23 - mi._13 * mi._21); mOut_d.mi._24 = 0.0f; mOut_d.mi._31 = fDetInv * (mi._21 * mi._32 - mi._22 * mi._31); mOut_d.mi._32 = -fDetInv * (mi._11 * mi._32 - mi._12 * mi._31); mOut_d.mi._33 = fDetInv * (mi._11 * mi._22 - mi._12 * mi._21); mOut_d.mi._34 = 0.0f; mOut_d.mi._41 = -(mi._41 * mOut_d.mi._11 + mi._42 * mOut_d.mi._21 + mi._43 * mOut_d.mi._31); mOut_d.mi._42 = -(mi._41 * mOut_d.mi._12 + mi._42 * mOut_d.mi._22 + mi._43 * mOut_d.mi._32); mOut_d.mi._43 = -(mi._41 * mOut_d.mi._13 + mi._42 * mOut_d.mi._23 + mi._43 * mOut_d.mi._33); mOut_d.mi._44 = 1.0f; return fDet; } float Matrix4::InverseFPU(Matrix4 &mOut_d) const { float succ = Inverse3D(mOut_d); if (succ != 0.0f) return succ; double Result[4][4]; double tmp[12]; double src[16]; double det; for (int i = 0; i < 4; i++) { src[i+0] = m[i][0]; src[i+4] = m[i][1]; src[i+8] = m[i][2]; src[i+12] = m[i][3]; } tmp[0] = src[10] * src[15]; tmp[1] = src[11] * src[14]; tmp[2] = src[9] * src[15]; tmp[3] = src[11] * src[13]; tmp[4] = src[9] * src[14]; tmp[5] = src[10] * src[13]; tmp[6] = src[8] * src[15]; tmp[7] = src[11] * src[12]; tmp[8] = src[8] * src[14]; tmp[9] = src[10] * src[12]; tmp[10] = src[8] * src[13]; tmp[11] = src[9] * src[12]; Result[0][0] = tmp[0]*src[5] + tmp[3]*src[6] + tmp[4]*src[7]; Result[0][0] -= tmp[1]*src[5] + tmp[2]*src[6] + tmp[5]*src[7]; Result[0][1] = tmp[1]*src[4] + tmp[6]*src[6] + tmp[9]*src[7]; Result[0][1] -= tmp[0]*src[4] + tmp[7]*src[6] + tmp[8]*src[7]; Result[0][2] = tmp[2]*src[4] + tmp[7]*src[5] + tmp[10]*src[7]; Result[0][2] -= tmp[3]*src[4] + tmp[6]*src[5] + tmp[11]*src[7]; Result[0][3] = tmp[5]*src[4] + tmp[8]*src[5] + tmp[11]*src[6]; Result[0][3] -= tmp[4]*src[4] + tmp[9]*src[5] + tmp[10]*src[6]; Result[1][0] = tmp[1]*src[1] + tmp[2]*src[2] + tmp[5]*src[3]; Result[1][0] -= tmp[0]*src[1] + tmp[3]*src[2] + tmp[4]*src[3]; Result[1][1] = tmp[0]*src[0] + tmp[7]*src[2] + tmp[8]*src[3]; Result[1][1] -= tmp[1]*src[0] + tmp[6]*src[2] + tmp[9]*src[3]; Result[1][2] = tmp[3]*src[0] + tmp[6]*src[1] + tmp[11]*src[3]; Result[1][2] -= tmp[2]*src[0] + tmp[7]*src[1] + tmp[10]*src[3]; Result[1][3] = tmp[4]*src[0] + tmp[9]*src[1] + tmp[10]*src[2]; Result[1][3] -= tmp[5]*src[0] + tmp[8]*src[1] + tmp[11]*src[2]; tmp[0] = src[2]*src[7]; tmp[1] = src[3]*src[6]; tmp[2] = src[1]*src[7]; tmp[3] = src[3]*src[5]; tmp[4] = src[1]*src[6]; tmp[5] = src[2]*src[5]; tmp[6] = src[0]*src[7]; tmp[7] = src[3]*src[4]; tmp[8] = src[0]*src[6]; tmp[9] = src[2]*src[4]; tmp[10] = src[0]*src[5]; tmp[11] = src[1]*src[4]; Result[2][0] = tmp[0]*src[13] + tmp[3]*src[14] + tmp[4]*src[15]; Result[2][0] -= tmp[1]*src[13] + tmp[2]*src[14] + tmp[5]*src[15]; Result[2][1] = tmp[1]*src[12] + tmp[6]*src[14] + tmp[9]*src[15]; Result[2][1] -= tmp[0]*src[12] + tmp[7]*src[14] + tmp[8]*src[15]; Result[2][2] = tmp[2]*src[12] + tmp[7]*src[13] + tmp[10]*src[15]; Result[2][2] -= tmp[3]*src[12] + tmp[6]*src[13] + tmp[11]*src[15]; Result[2][3] = tmp[5]*src[12] + tmp[8]*src[13] + tmp[11]*src[14]; Result[2][3] -= tmp[4]*src[12] + tmp[9]*src[13] + tmp[10]*src[14]; Result[3][0] = tmp[2]*src[10] + tmp[5]*src[11] + tmp[1]*src[9]; Result[3][0] -= tmp[4]*src[11] + tmp[0]*src[9] + tmp[3]*src[10]; Result[3][1] = tmp[8]*src[11] + tmp[0]*src[8] + tmp[7]*src[10]; Result[3][1] -= tmp[6]*src[10] + tmp[9]*src[11] + tmp[1]*src[8]; Result[3][2] = tmp[6]*src[9] + tmp[11]*src[11] + tmp[3]*src[8]; Result[3][2] -= tmp[10]*src[11] + tmp[2]*src[8] + tmp[7]*src[9]; Result[3][3] = tmp[10]*src[10] + tmp[4]*src[8] + tmp[9]*src[9]; Result[3][3] -= tmp[8]*src[9] + tmp[11]*src[10] + tmp[5]*src[8]; det=src[0]*Result[0][0]+src[1]*Result[0][1]+src[2]*Result[0][2]+src[3]*Result[0][3]; det = 1.0f / det; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { mOut_d.m[i][j] = (float)(Result[i][j] * det); } } return (float)det; } void Matrix4::LookAt(Matrix4 & rs, const Vec3 & pos, const Vec3 & center, const Vec3 & up) { Vec3 xAxis, yAxis, zAxis; Vec3::Subtract(zAxis, pos, center); Vec3::Normalize(zAxis, zAxis); Vec3::Cross(xAxis, up, zAxis); Vec3::Normalize(xAxis, xAxis); Vec3::Cross(yAxis, zAxis, xAxis); Vec3::Normalize(yAxis, yAxis); rs.m[0][0] = xAxis.x; rs.m[0][1] = yAxis.x; rs.m[0][2] = zAxis.x; rs.m[0][3] = 0.0f; rs.m[1][0] = xAxis.y; rs.m[1][1] = yAxis.y; rs.m[1][2] = zAxis.y; rs.m[1][3] = 0.0f; rs.m[2][0] = xAxis.z; rs.m[2][1] = yAxis.z; rs.m[2][2] = zAxis.z; rs.m[2][3] = 0.0f; rs.m[3][0] = -Vec3::Dot(xAxis, pos); rs.m[3][1] = -Vec3::Dot(yAxis, pos); rs.m[3][2] = -Vec3::Dot(zAxis, pos); rs.m[3][3] = 1.0f; } float Matrix4_M128::Inverse(Matrix4_M128 &mOut) const { __m128 Fac0; { __m128 Swp0a = _mm_shuffle_ps(C4, C3, _MM_SHUFFLE(3, 3, 3, 3)); __m128 Swp0b = _mm_shuffle_ps(C4, C3, _MM_SHUFFLE(2, 2, 2, 2)); __m128 Swp00 = _mm_shuffle_ps(C3, C2, _MM_SHUFFLE(2, 2, 2, 2)); __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); __m128 Swp03 = _mm_shuffle_ps(C3, C2, _MM_SHUFFLE(3, 3, 3, 3)); __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); Fac0 = _mm_sub_ps(Mul00, Mul01); } __m128 Fac1; { __m128 Swp0a = _mm_shuffle_ps(C4, C3, _MM_SHUFFLE(3, 3, 3, 3)); __m128 Swp0b = _mm_shuffle_ps(C4, C3, _MM_SHUFFLE(1, 1, 1, 1)); __m128 Swp00 = _mm_shuffle_ps(C3, C2, _MM_SHUFFLE(1, 1, 1, 1)); __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); __m128 Swp03 = _mm_shuffle_ps(C3, C2, _MM_SHUFFLE(3, 3, 3, 3)); __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); Fac1 = _mm_sub_ps(Mul00, Mul01); } __m128 Fac2; { __m128 Swp0a = _mm_shuffle_ps(C4, C3, _MM_SHUFFLE(2, 2, 2, 2)); __m128 Swp0b = _mm_shuffle_ps(C4, C3, _MM_SHUFFLE(1, 1, 1, 1)); __m128 Swp00 = _mm_shuffle_ps(C3, C2, _MM_SHUFFLE(1, 1, 1, 1)); __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); __m128 Swp03 = _mm_shuffle_ps(C3, C2, _MM_SHUFFLE(2, 2, 2, 2)); __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); Fac2 = _mm_sub_ps(Mul00, Mul01); } __m128 Fac3; { __m128 Swp0a = _mm_shuffle_ps(C4, C3, _MM_SHUFFLE(3, 3, 3, 3)); __m128 Swp0b = _mm_shuffle_ps(C4, C3, _MM_SHUFFLE(0, 0, 0, 0)); __m128 Swp00 = _mm_shuffle_ps(C3, C2, _MM_SHUFFLE(0, 0, 0, 0)); __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); __m128 Swp03 = _mm_shuffle_ps(C3, C2, _MM_SHUFFLE(3, 3, 3, 3)); __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); Fac3 = _mm_sub_ps(Mul00, Mul01); } __m128 Fac4; { __m128 Swp0a = _mm_shuffle_ps(C4, C3, _MM_SHUFFLE(2, 2, 2, 2)); __m128 Swp0b = _mm_shuffle_ps(C4, C3, _MM_SHUFFLE(0, 0, 0, 0)); __m128 Swp00 = _mm_shuffle_ps(C3, C2, _MM_SHUFFLE(0, 0, 0, 0)); __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); __m128 Swp03 = _mm_shuffle_ps(C3, C2, _MM_SHUFFLE(2, 2, 2, 2)); __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); Fac4 = _mm_sub_ps(Mul00, Mul01); } __m128 Fac5; { __m128 Swp0a = _mm_shuffle_ps(C4, C3, _MM_SHUFFLE(1, 1, 1, 1)); __m128 Swp0b = _mm_shuffle_ps(C4, C3, _MM_SHUFFLE(0, 0, 0, 0)); __m128 Swp00 = _mm_shuffle_ps(C3, C2, _MM_SHUFFLE(0, 0, 0, 0)); __m128 Swp01 = _mm_shuffle_ps(Swp0a, Swp0a, _MM_SHUFFLE(2, 0, 0, 0)); __m128 Swp02 = _mm_shuffle_ps(Swp0b, Swp0b, _MM_SHUFFLE(2, 0, 0, 0)); __m128 Swp03 = _mm_shuffle_ps(C3, C2, _MM_SHUFFLE(1, 1, 1, 1)); __m128 Mul00 = _mm_mul_ps(Swp00, Swp01); __m128 Mul01 = _mm_mul_ps(Swp02, Swp03); Fac5 = _mm_sub_ps(Mul00, Mul01); } __m128 SignA = _mm_set_ps( 1.0f,-1.0f, 1.0f,-1.0f); __m128 SignB = _mm_set_ps(-1.0f, 1.0f,-1.0f, 1.0f); __m128 Temp0 = _mm_shuffle_ps(C2, C1, _MM_SHUFFLE(0, 0, 0, 0)); __m128 Vec0 = _mm_shuffle_ps(Temp0, Temp0, _MM_SHUFFLE(2, 2, 2, 0)); __m128 Temp1 = _mm_shuffle_ps(C2, C1, _MM_SHUFFLE(1, 1, 1, 1)); __m128 Vec1 = _mm_shuffle_ps(Temp1, Temp1, _MM_SHUFFLE(2, 2, 2, 0)); __m128 Temp2 = _mm_shuffle_ps(C2, C1, _MM_SHUFFLE(2, 2, 2, 2)); __m128 Vec2 = _mm_shuffle_ps(Temp2, Temp2, _MM_SHUFFLE(2, 2, 2, 0)); __m128 Temp3 = _mm_shuffle_ps(C2, C1, _MM_SHUFFLE(3, 3, 3, 3)); __m128 Vec3 = _mm_shuffle_ps(Temp3, Temp3, _MM_SHUFFLE(2, 2, 2, 0)); __m128 Mul00 = _mm_mul_ps(Vec1, Fac0); __m128 Mul01 = _mm_mul_ps(Vec2, Fac1); __m128 Mul02 = _mm_mul_ps(Vec3, Fac2); __m128 Sub00 = _mm_sub_ps(Mul00, Mul01); __m128 Add00 = _mm_add_ps(Sub00, Mul02); __m128 Inv0 = _mm_mul_ps(SignB, Add00); __m128 Mul03 = _mm_mul_ps(Vec0, Fac0); __m128 Mul04 = _mm_mul_ps(Vec2, Fac3); __m128 Mul05 = _mm_mul_ps(Vec3, Fac4); __m128 Sub01 = _mm_sub_ps(Mul03, Mul04); __m128 Add01 = _mm_add_ps(Sub01, Mul05); __m128 Inv1 = _mm_mul_ps(SignA, Add01); __m128 Mul06 = _mm_mul_ps(Vec0, Fac1); __m128 Mul07 = _mm_mul_ps(Vec1, Fac3); __m128 Mul08 = _mm_mul_ps(Vec3, Fac5); __m128 Sub02 = _mm_sub_ps(Mul06, Mul07); __m128 Add02 = _mm_add_ps(Sub02, Mul08); __m128 Inv2 = _mm_mul_ps(SignB, Add02); __m128 Mul09 = _mm_mul_ps(Vec0, Fac2); __m128 Mul10 = _mm_mul_ps(Vec1, Fac4); __m128 Mul11 = _mm_mul_ps(Vec2, Fac5); __m128 Sub03 = _mm_sub_ps(Mul09, Mul10); __m128 Add03 = _mm_add_ps(Sub03, Mul11); __m128 Inv3 = _mm_mul_ps(SignA, Add03); __m128 Row0 = _mm_shuffle_ps(Inv0, Inv1, _MM_SHUFFLE(0, 0, 0, 0)); __m128 Row1 = _mm_shuffle_ps(Inv2, Inv3, _MM_SHUFFLE(0, 0, 0, 0)); __m128 Row2 = _mm_shuffle_ps(Row0, Row1, _MM_SHUFFLE(2, 0, 2, 0)); // Det0 = dot(C1, Row2) __m128 mul0 = _mm_mul_ps(C1, Row2); __m128 swp0 = _mm_shuffle_ps(mul0, mul0, _MM_SHUFFLE(2, 3, 0, 1)); __m128 add0 = _mm_add_ps(mul0, swp0); __m128 swp1 = _mm_shuffle_ps(add0, add0, _MM_SHUFFLE(0, 1, 2, 3)); __m128 Det0 = _mm_add_ps(add0, swp1); __m128 Rcp0 = _mm_div_ps(VecOne, Det0); mOut.C1 = _mm_mul_ps(Inv0, Rcp0); mOut.C2 = _mm_mul_ps(Inv1, Rcp0); mOut.C3 = _mm_mul_ps(Inv2, Rcp0); mOut.C4 = _mm_mul_ps(Inv3, Rcp0); float retVal; _mm_store_ss(&retVal, Det0); return retVal; } } ================================================ FILE: Source/CoreLib/VectorMath.h ================================================ #ifndef VECTOR_MATH_H #define VECTOR_MATH_H #include #include #include #include #include "LibMath.h" #ifdef _M_X64 #define NO_SIMD_ASM #endif #if !defined(_MSC_VER) || defined(__clang__) #define NO_SIMD_ASM #endif #ifndef NO_VECTOR_CONSTRUCTORS #define NO_VECTOR_CONSTRUCTORS #endif namespace VectorMath { using namespace CoreLib::Basic; const float PI = 3.1415926535f; const float Epsilon = 1e-4f; const int DefaultFloatUlps = 1024; inline float Clamp(float val, float vmin, float vmax) { return val>vmax ? vmax : valB && !FloatEquals(A, B, maxUlps); } inline bool FloatSmaller(float A, float B, int maxUlps = DefaultFloatUlps) { return AB || FloatEquals(A, B, maxUlps); } template inline T Max(T v1, T v2) { if (v1>v2) return v1; else return v2; } template inline T Min(T v1, T v2) { if (v1 inline T CatmullInterpolate(const T & p0, const T & p1, const T & p2, const T & p3, float t) { float t2 = t * t; float t3 = t2 * t; return (p1 * 2.0f + (-p0 + p2) * t + (p0 * 2.0f - p1 * 5.0f + p2 * 4.0f - p3) * t2 + (-p0 + p1 * 3.0f - p2 * 3.0f + p3) * t3) * 0.5f; } #ifdef _MSC_VER #ifndef __clang__ #ifndef M128_OPERATOR_OVERLOADS #define M128_OPERATOR_OVERLOADS inline __m128 & operator += (__m128 & v0, const __m128 &v1) { v0 = _mm_add_ps(v0, v1); return v0; } inline __m128 & operator -= (__m128 & v0, const __m128 &v1) { v0 = _mm_sub_ps(v0, v1); return v0; } inline __m128 & operator *= (__m128 & v0, const __m128 &v1) { v0 = _mm_mul_ps(v0, v1); return v0; } inline __m128 & operator /= (__m128 & v0, const __m128 &v1) { v0 = _mm_div_ps(v0, v1); return v0; } inline __m128 operator + (const __m128 & v0, const __m128 & v1) { return _mm_add_ps(v0, v1); } inline __m128 operator - (const __m128 & v0, const __m128 & v1) { return _mm_sub_ps(v0, v1); } inline __m128 operator * (const __m128 & v0, const __m128 & v1) { return _mm_mul_ps(v0, v1); } inline __m128 operator / (const __m128 & v0, const __m128 & v1) { return _mm_div_ps(v0, v1); } inline __m128 operator - (const __m128 & v0) { static const __m128 SIGNMASK = _mm_castsi128_ps(_mm_set1_epi32(0x80000000)); return _mm_xor_ps(v0, SIGNMASK); } inline __m128i & operator += (__m128i & v0, const __m128i &v1) { v0 = _mm_add_epi32(v0, v1); return v0; } inline __m128i & operator -= (__m128i & v0, const __m128i &v1) { v0 = _mm_sub_epi32(v0, v1); return v0; } inline __m128i & operator *= (__m128i & v0, const __m128i &v1) { v0 = _mm_mul_epi32(v0, v1); return v0; } inline __m128i operator + (const __m128i & v0, const __m128i & v1) { return _mm_add_epi32(v0, v1); } inline __m128i operator - (const __m128i & v0, const __m128i & v1) { return _mm_sub_epi32(v0, v1); } inline __m128i operator * (const __m128i & v0, const __m128i & v1) { return _mm_mullo_epi32(v0, v1); } inline __m128i operator - (const __m128i & v0) { return _mm_xor_si128(v0, _mm_set1_epi32(0xFFFFFFFF)); } #endif #endif _declspec(align(16)) class SSEVec3 { public: __m128 x, y, z; SSEVec3() {}; SSEVec3(__m128 x, __m128 y, __m128 z) :x(x), y(y), z(z) { } SSEVec3(const Vec3 &v) { this->x = _mm_set_ps1(v.x); this->y = _mm_set_ps1(v.y); this->z = _mm_set_ps1(v.z); } SSEVec3(float x, float y, float z) { this->x = _mm_set_ps1(x); this->y = _mm_set_ps1(y); this->z = _mm_set_ps1(z); } inline __m128 Length() { return _mm_sqrt_ps(x*x + y*y + z*z); } inline void Normalize(__m128 one) { auto s = one / Length(); x *= s; y *= s; z *= s; } inline SSEVec3 operator + (const SSEVec3 &vin) { SSEVec3 rs; rs.x = x + vin.x; rs.y = y + vin.y; rs.z = z + vin.z; return rs; } inline SSEVec3 operator - (const SSEVec3 &vin) { SSEVec3 rs; rs.x = x - vin.x; rs.y = y - vin.y; rs.z = z - vin.z; return rs; } inline SSEVec3 operator - () { SSEVec3 rs; rs.x = -x; rs.y = -y; rs.z = -z; return rs; } inline SSEVec3 operator * (__m128 scale) { SSEVec3 rs; rs.x = x * scale; rs.y = y * scale; rs.z = z * scale; return rs; } inline SSEVec3 & operator += (const SSEVec3 & vin) { x += vin.x; y += vin.y; z += vin.z; return *this; } inline SSEVec3 & operator -= (const SSEVec3 & vin) { x -= vin.x; y -= vin.y; z -= vin.z; return *this; } inline SSEVec3 & operator *= (const SSEVec3 & vin) { x *= vin.x; y *= vin.y; z *= vin.z; return *this; } inline SSEVec3 & operator *= (__m128 s) { x *= s; y *= s; z *= s; return *this; } inline SSEVec3 & operator /= (const SSEVec3 & vin) { x /= vin.x; y /= vin.y; z /= vin.z; return *this; } inline SSEVec3 & operator /= (float s) { float inv = 1.0f / s; return (*this) *= _mm_set_ps1(inv); } inline static __m128 Dot(const SSEVec3 & v1, const SSEVec3 & v2) { return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; } inline static void Cross(SSEVec3 & rs_d, const SSEVec3 & v1, const SSEVec3 & v2) { rs_d.x = v1.y*v2.z - v1.z * v2.y; rs_d.y = v1.z*v2.x - v1.x * v2.z; rs_d.z = v1.x*v2.y - v1.y * v2.x; } }; _declspec(align(16)) class SSEVec4 { public: __m128 x, y, z, w; SSEVec4() {}; SSEVec4(const __m128 & x, const __m128 & y, const __m128 & z, const __m128 & w) :x(x), y(y), z(z), w(w) { } SSEVec4(const Vec4 &v) { this->x = _mm_set_ps1(v.x); this->y = _mm_set_ps1(v.y); this->z = _mm_set_ps1(v.z); this->w = _mm_set_ps1(v.w); } SSEVec4(float x, float y, float z, float w) { this->x = _mm_set_ps1(x); this->y = _mm_set_ps1(y); this->z = _mm_set_ps1(z); this->w = _mm_set_ps1(w); } inline __m128 Length() { return _mm_sqrt_ps(x*x + y*y + z*z + w*w); } inline void Normalize(__m128 one) { auto s = one / Length(); x *= s; y *= s; z *= s; w *= s; } inline SSEVec4 operator + (const SSEVec4 &vin) { SSEVec4 rs; rs.x = x + vin.x; rs.y = y + vin.y; rs.z = z + vin.z; rs.w = w + vin.w; return rs; } inline SSEVec4 operator - (const SSEVec4 &vin) { SSEVec4 rs; rs.x = x - vin.x; rs.y = y - vin.y; rs.z = z - vin.z; rs.w = w - vin.w; return rs; } inline SSEVec4 operator - () { SSEVec4 rs; rs.x = -x; rs.y = -y; rs.z = -z; rs.w = -w; return rs; } inline SSEVec4 operator * (__m128 scale) { SSEVec4 rs; rs.x = x * scale; rs.y = y * scale; rs.z = z * scale; rs.w = w * scale; return rs; } inline SSEVec4 & operator += (const SSEVec4 & vin) { x += vin.x; y += vin.y; z += vin.z; w += vin.w; return *this; } inline SSEVec4 & operator -= (const SSEVec4 & vin) { x -= vin.x; y -= vin.y; z -= vin.z; w -= vin.w; return *this; } inline SSEVec4 & operator *= (const SSEVec4 & vin) { x *= vin.x; y *= vin.y; z *= vin.z; w *= vin.w; return *this; } inline SSEVec4 & operator *= (__m128 s) { x *= s; y *= s; z *= s; w *= s; return *this; } inline SSEVec4 & operator /= (const SSEVec4 & vin) { x /= vin.x; y /= vin.y; z /= vin.z; w /= vin.w; return *this; } inline SSEVec4 & operator /= (float s) { float inv = 1.0f / s; return (*this) *= _mm_set_ps1(inv); } inline static __m128 Dot(const SSEVec4 & v1, const SSEVec4 & v2) { return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z + v1.w*v2.w; } }; _declspec(align(16)) class SSEMatrix4 { public: __m128 values[16]; SSEMatrix4() {} SSEMatrix4(const Matrix4 & mat) { for (int i = 0; i<16; i++) values[i] = _mm_set_ps1(mat.values[i]); } inline SSEVec3 Transform(SSEVec3 & v) { SSEVec3 rs; rs.x = values[0] * v.x + values[4] * v.y + values[8] * v.z + values[12]; rs.y = values[1] * v.x + values[5] * v.y + values[9] * v.z + values[13]; rs.z = values[2] * v.x + values[6] * v.y + values[10] * v.z + values[14]; auto w = values[3] * v.x + values[7] * v.y + values[11] * v.z + values[15]; w = _mm_set_ps1(1.0f) / w; rs.x *= w; rs.y *= w; rs.z *= w; return rs; } inline SSEVec3 TransformNonPerspective(SSEVec3 & v) { SSEVec3 rs; rs.x = values[0] * v.x + values[4] * v.y + values[8] * v.z + values[12]; rs.y = values[1] * v.x + values[5] * v.y + values[9] * v.z + values[13]; rs.z = values[2] * v.x + values[6] * v.y + values[10] * v.z + values[14]; return rs; } }; #endif class Vec2i { public: int x, y; static Vec2i Create(int px, int py) { Vec2i rs; rs.x = px; rs.y = py; return rs; } }; class Vec3i { public: int x, y, z; static Vec3i Create(int px, int py, int pz) { Vec3i rs; rs.x = px; rs.y = py; rs.z = pz; return rs; } }; class Vec4i { public: int x, y, z, w; static Vec4i Create(int px, int py, int pz, int pw) { Vec4i rs; rs.x = px; rs.y = py; rs.z = pz; rs.w = pw; return rs; } }; class Quaternion { public: float x, y, z, w; Quaternion() = default; Quaternion(float px, float py, float pz, float pw) { x = px; y = py; z = pz; w = pw; } Quaternion(const Vec4& v) { x = v.x; y = v.y; z = v.z; w = v.w; } Vec4 ToVec4() const { return Vec4::Create(x, y, z, w); } Quaternion operator * (const Quaternion & q) const { Quaternion rs; rs.x = w*q.x + x*q.w + y*q.z - z*q.y; rs.y = w*q.y + y*q.w + z*q.x - x*q.z; rs.z = w*q.z + z*q.w + x*q.y - y*q.x; rs.w = w*q.w - x*q.x - y*q.y - z*q.z; return rs; } Quaternion operator + (const Quaternion & q) const { Quaternion rs; rs.x = x + q.x; rs.y = y + q.y; rs.z = z + q.z; rs.w = w + q.w; return rs; } Quaternion operator * (float s) const { Quaternion rs; rs.x = x * s; rs.y = y * s; rs.z = z * s; rs.w = w * s; return rs; } Quaternion operator *= (float s) { x = x * s; y = y * s; z = z * s; w = w * s; return *this; } Quaternion operator -() const { return Quaternion(-x, -y, -z, -w); } Quaternion Conjugate() const { return Quaternion(-x, -y, -z, w); } float Length() const { return sqrt(x*x + y*y + z*z + w*w); } float LengthSquared() const { return x*x + y*y + z*z + w*w; } Quaternion Inverse() const { auto rs = Conjugate(); rs *= (1.0f / LengthSquared()); return rs; } Vec3 Transform(const Vec3 &v) const { Quaternion V(v.x, v.y, v.z, 0.0f); auto rs = *this * V * Conjugate(); return Vec3::Create(rs.x, rs.y, rs.z); } Vec4 ToAxisAngle() const { float theta = acos(w); float invSinTheta = 1.0f / sin(theta); Vec4 rs; rs.x = x * invSinTheta; rs.y = y * invSinTheta; rs.z = z * invSinTheta; rs.w = theta * 2.0f; return rs; } Matrix3 ToMatrix3() const { Matrix3 rs; rs.values[0] = 1.0f - 2.0f * (y*y + z*z); rs.values[1] = 2.0f * (x*y + w*z); rs.values[2] = 2.0f * (x*z - w*y); rs.values[3] = 2.0f * (x*y - w*z); rs.values[4] = 1.0f - 2.0f * (x*x + z*z); rs.values[5] = 2.0f * (y*z + w*x); rs.values[6] = 2.0f * (x*z + w*y); rs.values[7] = 2.0f * (y*z - w*x); rs.values[8] = 1.0f - 2.0f * (x*x + y*y); return rs; } Matrix4 ToMatrix4() const { Matrix4 rs; rs.values[0] = 1.0f - 2.0f * (y*y + z*z); rs.values[1] = 2.0f * (x*y + w*z); rs.values[2] = 2.0f * (x*z - w*y); rs.values[3] = 0.0f; rs.values[4] = 2.0f * (x*y - w*z); rs.values[5] = 1.0f - 2.0f * (x*x + z*z); rs.values[6] = 2.0f * (y*z + w*x); rs.values[7] = 0.0f; rs.values[8] = 2.0f * (x*z + w*y); rs.values[9] = 2.0f * (y*z - w*x); rs.values[10] = 1.0f - 2.0f * (x*x + y*y); rs.values[11] = 0.0f; rs.values[12] = 0.0f; rs.values[13] = 0.0f; rs.values[14] = 0.0f; rs.values[15] = 1.0f; return rs; } static inline Quaternion FromMatrix(const Matrix3 & a) { Quaternion q; float trace = a.m[0][0] + a.m[1][1] + a.m[2][2]; // I removed + 1.0f; see discussion with Ethan if (trace > 0) { float s = 0.5f / sqrtf(trace + 1.0f); q.w = 0.25f / s; q.x = (a.m[1][2] - a.m[2][1]) * s; q.y = (a.m[2][0] - a.m[0][2]) * s; q.z = (a.m[0][1] - a.m[1][0]) * s; } else { if (a.m[0][0] > a.m[1][1] && a.m[0][0] > a.m[2][2]) { float s = 2.0f * sqrtf(1.0f + a.m[0][0] - a.m[1][1] - a.m[2][2]); q.w = (a.m[1][2] - a.m[2][1]) / s; q.x = 0.25f * s; q.y = (a.m[0][1] + a.m[1][0]) / s; q.z = (a.m[0][2] + a.m[2][0]) / s; } else if (a.m[1][1] > a.m[2][2]) { float s = 2.0f * sqrtf(1.0f + a.m[1][1] - a.m[0][0] - a.m[2][2]); q.w = (a.m[2][0] - a.m[0][2]) / s; q.x = (a.m[0][1] + a.m[1][0]) / s; q.y = 0.25f * s; q.z = (a.m[1][2] + a.m[2][1]) / s; } else { float s = 2.0f * sqrtf(1.0f + a.m[2][2] - a.m[0][0] - a.m[1][1]); q.w = (a.m[0][1] - a.m[1][0]) / s; q.x = (a.m[0][2] + a.m[2][0]) / s; q.y = (a.m[1][2] + a.m[2][1]) / s; q.z = 0.25f * s; } } return q * (1.0f / q.Length()); } // equivalent to mat(colX, colY, colZ) static inline Quaternion FromCoordinates(const Vec3 & axisX, const Vec3 & axisY, const Vec3 & axisZ) { Matrix3 a; a.values[0] = axisX.x; a.values[1] = axisX.y; a.values[2] = axisX.z; a.values[3] = axisY.x; a.values[4] = axisY.y; a.values[5] = axisY.z; a.values[6] = axisZ.x; a.values[7] = axisZ.y; a.values[8] = axisZ.z; return FromMatrix(a); } static inline Quaternion FromAxisAngle(const Vec3 & axis, float angle) { float cosAng = cos(angle * 0.5f); float sinAng = sin(angle * 0.5f); return Quaternion(axis.x * sinAng, axis.y * sinAng, axis.z * sinAng, cosAng); } static inline float Dot(const Quaternion & q1, const Quaternion & q2) { return q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; } static inline Quaternion Lerp(const Quaternion & q1, const Quaternion & q2, float t) { float invT = 1.0f - t; return Quaternion(q1.x * invT + q2.x * t, q1.y * invT + q2.y * t, q1.z * invT + q2.z * t, q1.w * invT + q2.w * t); } static inline Quaternion Slerp(const Quaternion & q1, const Quaternion & q2, float t) { Quaternion q3; float dot = Quaternion::Dot(q1, q2); if (dot < 0) { dot = -dot; q3 = -q2; } else q3 = q2; if (dot < 0.95f) { float angle = acos(dot); return (q1*sin(angle*(1 - t)) + q3*sin(angle*t)) * (1.0f / sin(angle)); } else return Lerp(q1, q3, t); } static inline void SetYawAngle(Quaternion & q, float yaw) { Matrix4 roty; Matrix4::RotationY(roty, yaw); Matrix4 original = q.ToMatrix4(); Matrix4::Multiply(original, roty, original); q = Quaternion::FromMatrix(original.GetMatrix3()); } }; } #endif ================================================ FILE: Source/CoreLib/corelib.natvis ================================================ {buffer.pointer,s} buffer.pointer,s {{ size={_count} }} _count _count _buffer {{ size={_count} }} _count bufferSize _count buffer {{ size={_count} }} _count _count _buffer {{ size={FCount} }} FCount FHead pNext Value {{ size={_count} }} _count bucketSizeMinusOne + 1 bucketSizeMinusOne + 1 hashMap {{ size={_count} }} _count bucketSizeMinusOne + 1 kvPairs.FCount kvPairs.FHead pNext Value {{ size={dict._count} }} dict._count dict.bucketSizeMinusOne + 1 dict.kvPairs.FCount dict.kvPairs.FHead pNext Value pointer empty RefPtr {*pointer} pointer ================================================ FILE: Source/Spire.sln ================================================ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 VisualStudioVersion = 14.0.25123.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpireLib", "SpireLib\SpireLib.vcxproj", "{1168C449-66A5-4D23-80E2-2C1A07E58F83}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpireCore", "SpireCore\SpireCore.vcxproj", "{DB00DA62-0533-4AFD-B59F-A67D5B3A0808}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpireCompiler", "SpireCompiler\SpireCompiler.vcxproj", "{D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Spire", "Spire", "{362538E0-CA8A-4F9A-AC99-15CC9A715A43}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SharedLib", "SharedLib", "{F37C8C78-754B-4E58-AE7E-B31C8F1C7003}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CoreLibBasic", "CoreLib\CoreLibBasic.vcxproj", "{F9BE7957-8399-899E-0C49-E714FDDD4B65}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SpireTestTool", "..\Tests\SpireTestTool\SpireTestTool.vcxproj", "{0C768A18-1D25-4000-9F37-DA5FE99E3B64}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{B625E3E2-3B0B-4A01-9D10-957F84092E10}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hello", "..\Examples\hello\hello.vcxproj", "{E6385042-1649-4803-9EBD-168F8B7EF131}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug_VS2013|Any CPU = Debug_VS2013|Any CPU Debug_VS2013|ARM = Debug_VS2013|ARM Debug_VS2013|Win32 = Debug_VS2013|Win32 Debug_VS2013|x64 = Debug_VS2013|x64 Debug|Any CPU = Debug|Any CPU Debug|ARM = Debug|ARM Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 DebugClang|Any CPU = DebugClang|Any CPU DebugClang|ARM = DebugClang|ARM DebugClang|Win32 = DebugClang|Win32 DebugClang|x64 = DebugClang|x64 Release_VS2013|Any CPU = Release_VS2013|Any CPU Release_VS2013|ARM = Release_VS2013|ARM Release_VS2013|Win32 = Release_VS2013|Win32 Release_VS2013|x64 = Release_VS2013|x64 Release|Any CPU = Release|Any CPU Release|ARM = Release|ARM Release|Win32 = Release|Win32 Release|x64 = Release|x64 TracingDebug|Any CPU = TracingDebug|Any CPU TracingDebug|ARM = TracingDebug|ARM TracingDebug|Win32 = TracingDebug|Win32 TracingDebug|x64 = TracingDebug|x64 TracingRelease|Any CPU = TracingRelease|Any CPU TracingRelease|ARM = TracingRelease|ARM TracingRelease|Win32 = TracingRelease|Win32 TracingRelease|x64 = TracingRelease|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Debug_VS2013|Any CPU.ActiveCfg = Debug_VS2013|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Debug_VS2013|ARM.ActiveCfg = Debug_VS2013|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Debug_VS2013|Win32.ActiveCfg = Debug_VS2013|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Debug_VS2013|Win32.Build.0 = Debug_VS2013|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Debug_VS2013|x64.ActiveCfg = Debug_VS2013|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Debug_VS2013|x64.Build.0 = Debug_VS2013|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Debug|Any CPU.ActiveCfg = Debug|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Debug|ARM.ActiveCfg = Debug|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Debug|Win32.ActiveCfg = Debug|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Debug|Win32.Build.0 = Debug|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Debug|x64.ActiveCfg = Debug|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Debug|x64.Build.0 = Debug|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.DebugClang|Any CPU.ActiveCfg = DebugClang|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.DebugClang|ARM.ActiveCfg = DebugClang|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.DebugClang|Win32.ActiveCfg = DebugClang|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.DebugClang|Win32.Build.0 = DebugClang|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.DebugClang|x64.ActiveCfg = DebugClang|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.DebugClang|x64.Build.0 = DebugClang|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Release_VS2013|Any CPU.ActiveCfg = Release_VS2013|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Release_VS2013|ARM.ActiveCfg = Release_VS2013|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Release_VS2013|Win32.ActiveCfg = Release_VS2013|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Release_VS2013|Win32.Build.0 = Release_VS2013|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Release_VS2013|x64.ActiveCfg = Release_VS2013|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Release_VS2013|x64.Build.0 = Release_VS2013|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Release|Any CPU.ActiveCfg = Release|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Release|ARM.ActiveCfg = Release|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Release|Win32.ActiveCfg = Release|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Release|Win32.Build.0 = Release|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Release|x64.ActiveCfg = Release|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.Release|x64.Build.0 = Release|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.TracingDebug|Any CPU.ActiveCfg = Release|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.TracingDebug|Any CPU.Build.0 = Release|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.TracingDebug|ARM.ActiveCfg = Release|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.TracingDebug|ARM.Build.0 = Release|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.TracingDebug|Win32.ActiveCfg = Debug|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.TracingDebug|Win32.Build.0 = Debug|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.TracingDebug|x64.ActiveCfg = Debug|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.TracingDebug|x64.Build.0 = Debug|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.TracingRelease|Any CPU.ActiveCfg = Release|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.TracingRelease|Any CPU.Build.0 = Release|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.TracingRelease|ARM.ActiveCfg = Release|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.TracingRelease|ARM.Build.0 = Release|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.TracingRelease|Win32.ActiveCfg = Release|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.TracingRelease|Win32.Build.0 = Release|Win32 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.TracingRelease|x64.ActiveCfg = Release|x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83}.TracingRelease|x64.Build.0 = Release|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Debug_VS2013|Any CPU.ActiveCfg = Debug_VS2013|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Debug_VS2013|ARM.ActiveCfg = Debug_VS2013|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Debug_VS2013|Win32.ActiveCfg = Debug_VS2013|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Debug_VS2013|Win32.Build.0 = Debug_VS2013|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Debug_VS2013|x64.ActiveCfg = Debug_VS2013|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Debug_VS2013|x64.Build.0 = Debug_VS2013|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Debug|Any CPU.ActiveCfg = Debug|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Debug|ARM.ActiveCfg = Debug|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Debug|Win32.ActiveCfg = Debug|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Debug|Win32.Build.0 = Debug|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Debug|x64.ActiveCfg = Debug|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Debug|x64.Build.0 = Debug|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.DebugClang|Any CPU.ActiveCfg = DebugClang|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.DebugClang|ARM.ActiveCfg = DebugClang|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.DebugClang|Win32.ActiveCfg = DebugClang|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.DebugClang|Win32.Build.0 = DebugClang|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.DebugClang|x64.ActiveCfg = DebugClang|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.DebugClang|x64.Build.0 = DebugClang|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Release_VS2013|Any CPU.ActiveCfg = Release_VS2013|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Release_VS2013|ARM.ActiveCfg = Release_VS2013|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Release_VS2013|Win32.ActiveCfg = Release_VS2013|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Release_VS2013|Win32.Build.0 = Release_VS2013|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Release_VS2013|x64.ActiveCfg = Release_VS2013|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Release_VS2013|x64.Build.0 = Release_VS2013|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Release|Any CPU.ActiveCfg = Release|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Release|ARM.ActiveCfg = Release|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Release|Win32.ActiveCfg = Release|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Release|Win32.Build.0 = Release|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Release|x64.ActiveCfg = Release|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.Release|x64.Build.0 = Release|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.TracingDebug|Any CPU.ActiveCfg = Release|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.TracingDebug|Any CPU.Build.0 = Release|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.TracingDebug|ARM.ActiveCfg = Release|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.TracingDebug|ARM.Build.0 = Release|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.TracingDebug|Win32.ActiveCfg = Debug|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.TracingDebug|Win32.Build.0 = Debug|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.TracingDebug|x64.ActiveCfg = Debug|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.TracingDebug|x64.Build.0 = Debug|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.TracingRelease|Any CPU.ActiveCfg = Release|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.TracingRelease|Any CPU.Build.0 = Release|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.TracingRelease|ARM.ActiveCfg = Release|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.TracingRelease|ARM.Build.0 = Release|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.TracingRelease|Win32.ActiveCfg = Release|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.TracingRelease|Win32.Build.0 = Release|Win32 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.TracingRelease|x64.ActiveCfg = Release|x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808}.TracingRelease|x64.Build.0 = Release|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug_VS2013|Any CPU.ActiveCfg = Debug_VS2013|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug_VS2013|ARM.ActiveCfg = Debug_VS2013|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug_VS2013|Win32.ActiveCfg = Debug_VS2013|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug_VS2013|Win32.Build.0 = Debug_VS2013|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug_VS2013|x64.ActiveCfg = Debug_VS2013|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug_VS2013|x64.Build.0 = Debug_VS2013|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|Any CPU.ActiveCfg = Debug|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|ARM.ActiveCfg = Debug|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|Win32.ActiveCfg = Debug|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|Win32.Build.0 = Debug|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|x64.ActiveCfg = Debug|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Debug|x64.Build.0 = Debug|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.DebugClang|Any CPU.ActiveCfg = DebugClang|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.DebugClang|ARM.ActiveCfg = DebugClang|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.DebugClang|Win32.ActiveCfg = DebugClang|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.DebugClang|Win32.Build.0 = DebugClang|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.DebugClang|x64.ActiveCfg = DebugClang|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.DebugClang|x64.Build.0 = DebugClang|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release_VS2013|Any CPU.ActiveCfg = Release_VS2013|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release_VS2013|ARM.ActiveCfg = Release_VS2013|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release_VS2013|Win32.ActiveCfg = Release_VS2013|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release_VS2013|Win32.Build.0 = Release_VS2013|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release_VS2013|x64.ActiveCfg = Release_VS2013|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release_VS2013|x64.Build.0 = Release_VS2013|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release|Any CPU.ActiveCfg = Release|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release|ARM.ActiveCfg = Release|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release|Win32.ActiveCfg = Release|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release|Win32.Build.0 = Release|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release|x64.ActiveCfg = Release|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.Release|x64.Build.0 = Release|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.TracingDebug|Any CPU.ActiveCfg = Release|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.TracingDebug|Any CPU.Build.0 = Release|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.TracingDebug|ARM.ActiveCfg = Release|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.TracingDebug|ARM.Build.0 = Release|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.TracingDebug|Win32.ActiveCfg = Debug|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.TracingDebug|Win32.Build.0 = Debug|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.TracingDebug|x64.ActiveCfg = Debug|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.TracingDebug|x64.Build.0 = Debug|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.TracingRelease|Any CPU.ActiveCfg = Release|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.TracingRelease|Any CPU.Build.0 = Release|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.TracingRelease|ARM.ActiveCfg = Release|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.TracingRelease|ARM.Build.0 = Release|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.TracingRelease|Win32.ActiveCfg = Release|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.TracingRelease|Win32.Build.0 = Release|Win32 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.TracingRelease|x64.ActiveCfg = Release|x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7}.TracingRelease|x64.Build.0 = Release|x64 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug_VS2013|Any CPU.ActiveCfg = Debug_VS2013|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug_VS2013|ARM.ActiveCfg = Debug_VS2013|ARM {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug_VS2013|ARM.Build.0 = Debug_VS2013|ARM {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug_VS2013|Win32.ActiveCfg = Debug_VS2013|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug_VS2013|Win32.Build.0 = Debug_VS2013|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug_VS2013|x64.ActiveCfg = Debug_VS2013|x64 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug_VS2013|x64.Build.0 = Debug_VS2013|x64 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|Any CPU.ActiveCfg = Debug|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|ARM.ActiveCfg = Debug|ARM {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|ARM.Build.0 = Debug|ARM {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|Win32.ActiveCfg = Debug|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|Win32.Build.0 = Debug|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|x64.ActiveCfg = Debug|x64 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Debug|x64.Build.0 = Debug|x64 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.DebugClang|Any CPU.ActiveCfg = DebugClang|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.DebugClang|ARM.ActiveCfg = DebugClang|ARM {F9BE7957-8399-899E-0C49-E714FDDD4B65}.DebugClang|ARM.Build.0 = DebugClang|ARM {F9BE7957-8399-899E-0C49-E714FDDD4B65}.DebugClang|Win32.ActiveCfg = DebugClang|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.DebugClang|Win32.Build.0 = DebugClang|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.DebugClang|x64.ActiveCfg = DebugClang|x64 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.DebugClang|x64.Build.0 = DebugClang|x64 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release_VS2013|Any CPU.ActiveCfg = Release_VS2013|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release_VS2013|ARM.ActiveCfg = Release_VS2013|ARM {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release_VS2013|ARM.Build.0 = Release_VS2013|ARM {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release_VS2013|Win32.ActiveCfg = Release_VS2013|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release_VS2013|Win32.Build.0 = Release_VS2013|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release_VS2013|x64.ActiveCfg = Release_VS2013|x64 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release_VS2013|x64.Build.0 = Release_VS2013|x64 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release|Any CPU.ActiveCfg = Release|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release|ARM.ActiveCfg = Release|ARM {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release|ARM.Build.0 = Release|ARM {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release|Win32.ActiveCfg = Release|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release|Win32.Build.0 = Release|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release|x64.ActiveCfg = Release|x64 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.Release|x64.Build.0 = Release|x64 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.TracingDebug|Any CPU.ActiveCfg = TracingDebug|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.TracingDebug|ARM.ActiveCfg = TracingDebug|ARM {F9BE7957-8399-899E-0C49-E714FDDD4B65}.TracingDebug|ARM.Build.0 = TracingDebug|ARM {F9BE7957-8399-899E-0C49-E714FDDD4B65}.TracingDebug|Win32.ActiveCfg = TracingDebug|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.TracingDebug|Win32.Build.0 = TracingDebug|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.TracingDebug|x64.ActiveCfg = TracingDebug|x64 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.TracingDebug|x64.Build.0 = TracingDebug|x64 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.TracingRelease|Any CPU.ActiveCfg = TracingRelease|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.TracingRelease|ARM.ActiveCfg = TracingRelease|ARM {F9BE7957-8399-899E-0C49-E714FDDD4B65}.TracingRelease|ARM.Build.0 = TracingRelease|ARM {F9BE7957-8399-899E-0C49-E714FDDD4B65}.TracingRelease|Win32.ActiveCfg = TracingRelease|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.TracingRelease|Win32.Build.0 = TracingRelease|Win32 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.TracingRelease|x64.ActiveCfg = TracingRelease|x64 {F9BE7957-8399-899E-0C49-E714FDDD4B65}.TracingRelease|x64.Build.0 = TracingRelease|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug_VS2013|Any CPU.ActiveCfg = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug_VS2013|Any CPU.Build.0 = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug_VS2013|ARM.ActiveCfg = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug_VS2013|ARM.Build.0 = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug_VS2013|Win32.ActiveCfg = Debug|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug_VS2013|Win32.Build.0 = Debug|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug_VS2013|x64.ActiveCfg = Debug_VS2013|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug_VS2013|x64.Build.0 = Debug_VS2013|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug|Any CPU.ActiveCfg = Debug|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug|ARM.ActiveCfg = Debug|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug|Win32.ActiveCfg = Debug|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug|Win32.Build.0 = Debug|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug|x64.ActiveCfg = Debug|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Debug|x64.Build.0 = Debug|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.DebugClang|Any CPU.ActiveCfg = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.DebugClang|Any CPU.Build.0 = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.DebugClang|ARM.ActiveCfg = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.DebugClang|ARM.Build.0 = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.DebugClang|Win32.ActiveCfg = Debug|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.DebugClang|Win32.Build.0 = Debug|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.DebugClang|x64.ActiveCfg = Debug|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.DebugClang|x64.Build.0 = Debug|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release_VS2013|Any CPU.ActiveCfg = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release_VS2013|Any CPU.Build.0 = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release_VS2013|ARM.ActiveCfg = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release_VS2013|ARM.Build.0 = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release_VS2013|Win32.ActiveCfg = Release|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release_VS2013|Win32.Build.0 = Release|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release_VS2013|x64.ActiveCfg = Release_VS2013|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release_VS2013|x64.Build.0 = Release_VS2013|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release|Any CPU.ActiveCfg = Release|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release|ARM.ActiveCfg = Release|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release|Win32.ActiveCfg = Release|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release|Win32.Build.0 = Release|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release|x64.ActiveCfg = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.Release|x64.Build.0 = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.TracingDebug|Any CPU.ActiveCfg = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.TracingDebug|Any CPU.Build.0 = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.TracingDebug|ARM.ActiveCfg = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.TracingDebug|ARM.Build.0 = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.TracingDebug|Win32.ActiveCfg = Debug|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.TracingDebug|Win32.Build.0 = Debug|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.TracingDebug|x64.ActiveCfg = Debug|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.TracingDebug|x64.Build.0 = Debug|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.TracingRelease|Any CPU.ActiveCfg = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.TracingRelease|Any CPU.Build.0 = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.TracingRelease|ARM.ActiveCfg = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.TracingRelease|ARM.Build.0 = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.TracingRelease|Win32.ActiveCfg = Release|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.TracingRelease|Win32.Build.0 = Release|Win32 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.TracingRelease|x64.ActiveCfg = Release|x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64}.TracingRelease|x64.Build.0 = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug_VS2013|Any CPU.ActiveCfg = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug_VS2013|Any CPU.Build.0 = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug_VS2013|ARM.ActiveCfg = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug_VS2013|ARM.Build.0 = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug_VS2013|Win32.ActiveCfg = Debug|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug_VS2013|Win32.Build.0 = Debug|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug_VS2013|x64.ActiveCfg = Debug_VS2013|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug_VS2013|x64.Build.0 = Debug_VS2013|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug|Any CPU.ActiveCfg = Debug|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug|ARM.ActiveCfg = Debug|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug|Win32.ActiveCfg = Debug|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug|Win32.Build.0 = Debug|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug|x64.ActiveCfg = Debug|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Debug|x64.Build.0 = Debug|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.DebugClang|Any CPU.ActiveCfg = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.DebugClang|Any CPU.Build.0 = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.DebugClang|ARM.ActiveCfg = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.DebugClang|ARM.Build.0 = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.DebugClang|Win32.ActiveCfg = Debug|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.DebugClang|Win32.Build.0 = Debug|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.DebugClang|x64.ActiveCfg = Debug|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.DebugClang|x64.Build.0 = Debug|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release_VS2013|Any CPU.ActiveCfg = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release_VS2013|Any CPU.Build.0 = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release_VS2013|ARM.ActiveCfg = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release_VS2013|ARM.Build.0 = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release_VS2013|Win32.ActiveCfg = Release|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release_VS2013|Win32.Build.0 = Release|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release_VS2013|x64.ActiveCfg = Release_VS2013|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release_VS2013|x64.Build.0 = Release_VS2013|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release|Any CPU.ActiveCfg = Release|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release|ARM.ActiveCfg = Release|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release|Win32.ActiveCfg = Release|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release|Win32.Build.0 = Release|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release|x64.ActiveCfg = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.Release|x64.Build.0 = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.TracingDebug|Any CPU.ActiveCfg = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.TracingDebug|Any CPU.Build.0 = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.TracingDebug|ARM.ActiveCfg = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.TracingDebug|ARM.Build.0 = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.TracingDebug|Win32.ActiveCfg = Debug|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.TracingDebug|Win32.Build.0 = Debug|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.TracingDebug|x64.ActiveCfg = Debug|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.TracingDebug|x64.Build.0 = Debug|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.TracingRelease|Any CPU.ActiveCfg = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.TracingRelease|Any CPU.Build.0 = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.TracingRelease|ARM.ActiveCfg = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.TracingRelease|ARM.Build.0 = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.TracingRelease|Win32.ActiveCfg = Release|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.TracingRelease|Win32.Build.0 = Release|Win32 {E6385042-1649-4803-9EBD-168F8B7EF131}.TracingRelease|x64.ActiveCfg = Release|x64 {E6385042-1649-4803-9EBD-168F8B7EF131}.TracingRelease|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {1168C449-66A5-4D23-80E2-2C1A07E58F83} = {362538E0-CA8A-4F9A-AC99-15CC9A715A43} {DB00DA62-0533-4AFD-B59F-A67D5B3A0808} = {362538E0-CA8A-4F9A-AC99-15CC9A715A43} {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7} = {362538E0-CA8A-4F9A-AC99-15CC9A715A43} {F9BE7957-8399-899E-0C49-E714FDDD4B65} = {F37C8C78-754B-4E58-AE7E-B31C8F1C7003} {0C768A18-1D25-4000-9F37-DA5FE99E3B64} = {362538E0-CA8A-4F9A-AC99-15CC9A715A43} {E6385042-1649-4803-9EBD-168F8B7EF131} = {B625E3E2-3B0B-4A01-9D10-957F84092E10} EndGlobalSection EndGlobal ================================================ FILE: Source/SpireCompiler/D3DCompiler.cpp ================================================ #include "D3DCompiler.h" #ifdef _WIN32 #include using namespace CoreLib::Basic; typedef HRESULT (WINAPI *D3DCompile2Func)( LPCVOID pSrcData, SIZE_T SrcDataSize, LPCSTR pSourceName, D3D_SHADER_MACRO * pDefines, ID3DInclude * pInclude, LPCSTR pEntrypoint, LPCSTR pTarget, UINT Flags1, UINT Flags2, UINT SecondaryDataFlags, LPCVOID pSecondaryData, SIZE_T SecondaryDataSize, ID3DBlob **ppCode, ID3DBlob **ppErrorMsgs ); typedef HRESULT (WINAPI *D3DCreateBlobFunc)( SIZE_T Size, ID3DBlob **ppBlob ); class D3DCompilerImpl : public D3DCompiler { public: D3DCompile2Func D3DCompile2; D3DCreateBlobFunc D3DCreateBlob; HMODULE Lib; virtual bool Compile(CoreLib::String input, CoreLib::String stageName, CoreLib::String & errMsg) override { auto entryPoint = "main"; char * profile = "ps_5_0"; if (stageName == "vs") profile = "vs_5_0"; else if (stageName == "tes") profile = "hs_5_0"; else if (stageName == "tcs") profile = "ds_5_0"; ID3DBlob *code, *err = nullptr; D3DCompile2(input.Buffer(), input.Length(), "", nullptr, nullptr, entryPoint, profile, 0, 0, 0, 0, 0, &code, &err); if (err != nullptr) { errMsg = (char*)err->GetBufferPointer(); return false; } return true; } }; D3DCompiler * LoadD3DCompiler() { auto d3dLib = LoadLibraryW(L"D3DCompiler_47.dll"); if (d3dLib) { auto compileFunc = (D3DCompile2Func)GetProcAddress(d3dLib, "D3DCompile2"); auto createBlobFunc = (D3DCreateBlobFunc)GetProcAddress(d3dLib, "D3DCreateBlob"); if (compileFunc && createBlobFunc) { D3DCompilerImpl* result = new D3DCompilerImpl(); result->D3DCompile2 = compileFunc; result->D3DCreateBlob = createBlobFunc; result->Lib = d3dLib; return result; } else FreeLibrary(d3dLib); } return nullptr; } #else D3DCompiler * LoadD3DCompiler() { return nullptr; } #endif ================================================ FILE: Source/SpireCompiler/D3DCompiler.h ================================================ #ifndef SPIRE_D3D_COMPILER_H #define SPIRE_D3D_COMPILER_H #include "CoreLib/Basic.h" class D3DCompiler : public CoreLib::Object { public: virtual bool Compile(CoreLib::String input, CoreLib::String stageName, CoreLib::String & errMsg) = 0; }; D3DCompiler * LoadD3DCompiler(); #endif ================================================ FILE: Source/SpireCompiler/ShaderCompilerShell.cpp ================================================ #include "CoreLib/LibIO.h" #include "SpireLib.h" #include "D3DCompiler.h" using namespace CoreLib::Basic; using namespace CoreLib::IO; using namespace Spire::Compiler; // Try to read an argument for a command-line option. String tryReadCommandLineArgument(wchar_t const* option, wchar_t***ioCursor, wchar_t**end) { wchar_t**& cursor = *ioCursor; if (cursor == end) { fprintf(stderr, "expected an argument for command-line option '%S'", option); exit(1); } else { return String::FromWString(*cursor++); } } int wmain(int argc, wchar_t* argv[]) { int returnValue = -1; { // We need to parse any command-line arguments. String outputDir; CompileOptions options; // As we parse the command line, we will rewrite the // entries in `argv` to collect any "ordinary" arguments. wchar_t const** inputPaths = (wchar_t const**)&argv[1]; wchar_t const** inputPathCursor = inputPaths; wchar_t** argCursor = &argv[1]; wchar_t** argEnd = &argv[argc]; while (argCursor != argEnd) { wchar_t const* arg = *argCursor++; if (arg[0] == '-') { String argStr = String::FromWString(arg); // The argument looks like an option, so try to parse it. if (argStr == "-out") outputDir = tryReadCommandLineArgument(arg, &argCursor, argEnd); else if (argStr == "-symbo") options.SymbolToCompile = tryReadCommandLineArgument(arg, &argCursor, argEnd); else if (argStr == "-schedule") options.ScheduleFileName = tryReadCommandLineArgument(arg, &argCursor, argEnd); else if (argStr == "-backend") { String name = tryReadCommandLineArgument(arg, &argCursor, argEnd); if (name == "glsl") { options.Target = CodeGenTarget::GLSL; } else if (name == "glsl_vk") { options.Target = CodeGenTarget::GLSL_Vulkan; } else if (name == "glsl_vk_onedesc") { options.Target = CodeGenTarget::GLSL_Vulkan_OneDesc; } else if (name == "hlsl") { options.Target = CodeGenTarget::HLSL; } else if (name == "spriv") { options.Target = CodeGenTarget::SPIRV; } else { fprintf(stderr, "unknown code generation target '%S'\n", name.ToWString()); } } else if (argStr == "-genchoice") options.Mode = CompilerMode::GenerateChoice; else if (argStr == "--") { // The `--` option causes us to stop trying to parse options, // and treat the rest of the command line as input file names: while (argCursor != argEnd) { *inputPathCursor++ = *argCursor++; } break; } else { fprintf(stderr, "unknown command-line option '%S'\n", argStr.ToWString()); // TODO: print a usage message exit(1); } } else { *inputPathCursor++ = arg; } } int inputPathCount = (int)(inputPathCursor - inputPaths); if (inputPathCount == 0) { fprintf(stderr, "error: no input file specified\n"); exit(1); } else if (inputPathCount > 1) { fprintf(stderr, "error: multiple input files specified\n"); exit(1); } String fileName = String::FromWString(inputPaths[0]); // Output directory defaults to the path of the input file if (outputDir.Length() == 0) { outputDir = Path::GetDirectoryName(fileName); } auto sourceDir = Path::GetDirectoryName(fileName); String schedule; if (options.ScheduleFileName.Length()) { try { schedule = File::ReadAllText(options.ScheduleFileName); options.ScheduleSource = schedule; } catch (IOException) { printf("Cannot open schedule file '%S'.\n", options.ScheduleFileName.ToWString()); goto end; } } CompileResult result; try { auto files = SpireLib::CompileShaderSourceFromFile(result, fileName, options); for (auto & f : files) { try { f.SaveToFile(Path::Combine(outputDir, f.MetaData.ShaderName + ".cse")); } catch (Exception &) { result.GetErrorWriter()->diagnose(CodePosition(0, 0, 0, ""), Diagnostics::cannotWriteOutputFile, Path::Combine(outputDir, f.MetaData.ShaderName + ".cse")); } } if (options.Target == CodeGenTarget::HLSL) { // verify shader using D3DCompileShaderFromFile RefPtr d3dCompiler = LoadD3DCompiler(); if (d3dCompiler) { for (auto & f : files) { for (auto & stage : f.Sources) { String errMsg; d3dCompiler->Compile(stage.Value.MainCode, stage.Key, errMsg); if (errMsg.Length()) { auto dumpFileName = f.MetaData.ShaderName + "." + stage.Key + ".hlsl"; result.GetErrorWriter()->diagnose(CodePosition(0, 0, 0, dumpFileName), Diagnostics::d3dCompileInfo, errMsg); File::WriteAllText(Path::Combine(Path::GetDirectoryName(fileName), dumpFileName), stage.Value.MainCode); } } } } else { printf("failed to load d3d compiler for verification.\n"); } } } catch (Exception & e) { printf("internal compiler error: %S\n", e.Message.ToWString()); } result.PrintDiagnostics(); if (result.GetErrorCount() == 0) returnValue = 0; } end:; #ifdef _MSC_VER _CrtDumpMemoryLeaks(); #endif return returnValue; } ================================================ FILE: Source/SpireCompiler/SpireCompiler.vcxproj ================================================  DebugClang Win32 DebugClang x64 Debug_VS2013 Win32 Debug_VS2013 x64 Debug Win32 Debug x64 Release_VS2013 Win32 Release_VS2013 x64 Release Win32 Release x64 {D56CBCEB-1EB5-4CA8-AEC4-48EA35ED61C7} Win32Proj SpireCompiler SpireCompiler 8.1 Application true v140 Unicode Application true v120 Unicode Application true v140_clang_3_7 Unicode Application true v140 Unicode Application true v120 Unicode Application true v140_Clang_3_7 Unicode Application false v140 true Unicode Application false v120 true Unicode Application false v140 true Unicode Application false v120 true Unicode true true true true true true false false false false Level4 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreadedDebug ../;../SpireLib false Console true Level4 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreadedDebug ../;../SpireLib false Console true EnableAllWarnings Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreadedDebug ../;../SpireLib false true Console true Level4 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreadedDebug ../;../SpireLib false Console true Level4 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreadedDebug ../;../SpireLib false Console true Level4 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreadedDebug ../;../SpireLib false Console true Level4 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded ../;../SpireLib false Console true true true Level4 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded ../;../SpireLib false Console true true true Level4 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded ../;../SpireLib false Console true true true Level4 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded ../;../SpireLib false Console true true true {f9be7957-8399-899e-0c49-e714fddd4b65} {db00da62-0533-4afd-b59f-a67d5b3a0808} {1168c449-66a5-4d23-80e2-2c1a07e58f83} ================================================ FILE: Source/SpireCompiler/SpireCompiler.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Header Files ================================================ FILE: Source/SpireCore/CLikeCodeGen.cpp ================================================ #include "CLikeCodeGen.h" #include "../CoreLib/Tokenizer.h" #include "Syntax.h" #include "Naming.h" using namespace CoreLib::Basic; namespace Spire { namespace Compiler { ILRecordType * ExtractRecordType(ILType * type) { if (auto recType = dynamic_cast(type)) return recType; else if (auto arrType = dynamic_cast(type)) return ExtractRecordType(arrType->BaseType.Ptr()); else if (auto genType = dynamic_cast(type)) return ExtractRecordType(genType->BaseType.Ptr()); else return nullptr; } String AddWorldNameSuffix(String name, String suffix) { if (name.EndsWith(suffix)) return name; else return EscapeCodeName(name + "_" + suffix); } void CLikeCodeGen::PrintType(StringBuilder & sbCode, ILType* type) { if (auto arrType = dynamic_cast(type)) { PrintType(sbCode, arrType->BaseType.Ptr()); if (arrType->ArrayLength > 0) sbCode << "[" << arrType->ArrayLength << "]"; else sbCode << "[]"; } else PrintTypeName(sbCode, type); } void CLikeCodeGen::PrintDef(StringBuilder & sbCode, ILType* type, const String & name) { if (auto arrType = dynamic_cast(type)) { PrintDef(sbCode, arrType->BaseType.Ptr(), name); if (arrType->ArrayLength > 0) sbCode << "[" << arrType->ArrayLength << "]"; else sbCode << "[]"; } else { PrintType(sbCode, type); sbCode << " "; sbCode << name; if (name.Length() == 0) throw InvalidProgramException("unnamed instruction."); } } String CLikeCodeGen::GetFuncOriginalName(const String & name) { String originalName; int splitPos = name.IndexOf('@'); if (splitPos == 0) return name; if (splitPos != -1) originalName = name.SubString(0, splitPos); else originalName = name; return originalName; } void CLikeCodeGen::PrintOp(CodeGenContext & ctx, ILOperand * op, bool forceExpression) { auto makeFloat = [](float v) { String rs(v, "%.12e"); if (!rs.Contains('.') && !rs.Contains('e') && !rs.Contains('E')) rs = rs + ".0"; if (rs.StartsWith("-")) rs = "(" + rs + ")"; return rs; }; if (auto c = dynamic_cast(op)) { auto type = c->Type.Ptr(); if (type->IsFloat()) ctx.Body << makeFloat(c->FloatValues[0]); else if (type->IsInt()) ctx.Body << (c->IntValues[0]); else if (type->IsUInt()) ctx.Body << (unsigned int)(c->IntValues[0]) << "u"; else if (type->IsBool()) ctx.Body << ((c->IntValues[0] != 0) ? "true" : "false"); else if (auto baseType = dynamic_cast(type)) { PrintType(ctx.Body, baseType); ctx.Body << "("; if (baseType->Type == ILBaseType::Float2) ctx.Body << makeFloat(c->FloatValues[0]) << ", " << makeFloat(c->FloatValues[1]); else if (baseType->Type == ILBaseType::Float3) ctx.Body << makeFloat(c->FloatValues[0]) << ", " << makeFloat(c->FloatValues[1]) << ", " << makeFloat(c->FloatValues[2]); else if (baseType->Type == ILBaseType::Float4) ctx.Body << makeFloat(c->FloatValues[0]) << ", " << makeFloat(c->FloatValues[1]) << ", " << makeFloat(c->FloatValues[2]) << ", " << makeFloat(c->FloatValues[3]); else if (baseType->Type == ILBaseType::Float3x3) { ctx.Body << "mat3("; for (int i = 0; i < 9; i++) { ctx.Body << makeFloat(c->FloatValues[i]); if (i != 8) ctx.Body << ", "; } ctx.Body; } else if (baseType->Type == ILBaseType::Float4x4) { for (int i = 0; i < 16; i++) { ctx.Body << makeFloat(c->FloatValues[i]); if (i != 15) ctx.Body << ", "; } } else if (baseType->Type == ILBaseType::Int2) ctx.Body << c->IntValues[0] << ", " << c->IntValues[1]; else if (baseType->Type == ILBaseType::Int3) ctx.Body << c->IntValues[0] << ", " << c->IntValues[1] << ", " << c->IntValues[2]; else if (baseType->Type == ILBaseType::Int4) ctx.Body << c->IntValues[0] << ", " << c->IntValues[1] << ", " << c->IntValues[2] << ", " << c->IntValues[3]; ctx.Body << ")"; } else throw InvalidOperationException("Illegal constant."); } else if (auto instr = dynamic_cast(op)) { if (AppearAsExpression(*instr, forceExpression)) { PrintInstrExpr(ctx, *instr); } else { if (forceExpression) throw InvalidProgramException("cannot generate code block as an expression."); String substituteName; if (ctx.SubstituteNames.TryGetValue(instr->Name, substituteName)) ctx.Body << substituteName; else ctx.Body << instr->Name; } } else if (auto param = dynamic_cast(op)) { PrintParameterReference(ctx.Body, param); } else throw InvalidOperationException("Unsupported operand type."); } static bool IsMatrix(ILOperand* operand) { auto type = operand->Type; // TODO(tfoley): This needs to be expanded once other matrix types are supported return type->IsFloatMatrix(); } void CLikeCodeGen::PrintMatrixMulInstrExpr(CodeGenContext & ctx, ILOperand* op0, ILOperand* op1) { ctx.Body << "("; PrintOp(ctx, op0); ctx.Body << " * "; PrintOp(ctx, op1); ctx.Body << ")"; } void CLikeCodeGen::PrintBinaryInstrExpr(CodeGenContext & ctx, BinaryInstruction * instr) { if (instr->Is()) { auto op0 = instr->Operands[0].Ptr(); auto op1 = instr->Operands[1].Ptr(); ctx.Body << "("; PrintOp(ctx, op0); ctx.Body << " = "; PrintOp(ctx, op1); ctx.Body << ")"; return; } auto op0 = instr->Operands[0].Ptr(); auto op1 = instr->Operands[1].Ptr(); if (instr->Is()) { throw InvalidOperationException("store instruction cannot appear as expression."); } if (instr->Is()) { PrintOp(ctx, op0); bool printDefault = true; if (op0->Type->GetVectorSize() <= 4 && op0->Type->IsVector()) { if (auto c = dynamic_cast(op1)) { switch (c->IntValues[0]) { case 0: ctx.Body << ".x"; break; case 1: ctx.Body << ".y"; break; case 2: ctx.Body << ".z"; break; case 3: ctx.Body << ".w"; break; default: throw InvalidOperationException("Invalid member access."); } printDefault = false; } } else if (auto structType = dynamic_cast(op0->Type.Ptr())) { if (auto c = dynamic_cast(op1)) { ctx.Body << "." << structType->Members[c->IntValues[0]].FieldName; } printDefault = false; } if (printDefault) { ctx.Body << "["; PrintOp(ctx, op1); ctx.Body << "]"; } return; } const char * op = ""; if (instr->Is()) { op = "+"; } else if (instr->Is()) { op = "-"; } else if (instr->Is()) { // For matrix-matrix, matrix-vector, and vector-matrix `*`, // GLSL performs a linear-algebraic inner product, while HLSL // always does element-wise product. We need to give the // codegen backend a change to handle this case. if(IsMatrix(op0) || IsMatrix(op1)) { PrintMatrixMulInstrExpr(ctx, op0, op1); return; } op = "*"; } else if (instr->Is()) { op = "/"; } else if (instr->Is()) { op = "%"; } else if (instr->Is()) { op = "<<"; } else if (instr->Is()) { op = ">>"; } else if (instr->Is()) { op = "=="; //ctx.Body << "int"; } else if (instr->Is()) { op = ">="; //ctx.Body << "int"; } else if (instr->Is()) { op = ">"; //ctx.Body << "int"; } else if (instr->Is()) { op = "<="; //ctx.Body << "int"; } else if (instr->Is()) { op = "<"; //ctx.Body << "int"; } else if (instr->Is()) { op = "!="; //ctx.Body << "int"; } else if (instr->Is()) { op = "&&"; } else if (instr->Is()) { op = "||"; } else if (instr->Is()) { op = "^"; } else if (instr->Is()) { op = "&"; } else if (instr->Is()) { op = "|"; } else throw InvalidProgramException("unsupported binary instruction."); ctx.Body << "("; PrintOp(ctx, op0); ctx.Body << " " << op << " "; PrintOp(ctx, op1); ctx.Body << ")"; } void CLikeCodeGen::PrintBinaryInstr(CodeGenContext & ctx, BinaryInstruction * instr) { auto op0 = instr->Operands[0].Ptr(); auto op1 = instr->Operands[1].Ptr(); if (instr->Is()) { PrintOp(ctx, op0); ctx.Body << " = "; PrintOp(ctx, op1); ctx.Body << ";\n"; return; } auto varName = ctx.DefineVariable(instr); if (instr->Is()) { ctx.Body << varName << " = "; PrintBinaryInstrExpr(ctx, instr); ctx.Body << ";\n"; return; } ctx.Body << varName << " = "; PrintBinaryInstrExpr(ctx, instr); ctx.Body << ";\n"; } void CLikeCodeGen::PrintUnaryInstrExpr(CodeGenContext & ctx, UnaryInstruction * instr) { auto op0 = instr->Operand.Ptr(); if (instr->Is()) { PrintOp(ctx, op0); return; } else if (instr->Is()) { PrintSwizzleInstrExpr(ctx, instr->As()); return; } const char * op = ""; if (instr->Is()) op = "~"; else if (instr->Is()) op = "(int)"; else if (instr->Is()) op = "(float)"; else if (instr->Is()) op = ""; else if (instr->Is()) op = "-"; else if (instr->Is()) op = "!"; else throw InvalidProgramException("unsupported unary instruction."); ctx.Body << "(" << op; PrintOp(ctx, op0); ctx.Body << ")"; } void CLikeCodeGen::PrintUnaryInstr(CodeGenContext & ctx, UnaryInstruction * instr) { auto varName = ctx.DefineVariable(instr); ctx.Body << varName << " = "; PrintUnaryInstrExpr(ctx, instr); ctx.Body << ";\n"; } void CLikeCodeGen::PrintAllocVarInstrExpr(CodeGenContext & ctx, AllocVarInstruction * instr) { ctx.Body << instr->Name; } void CLikeCodeGen::PrintAllocVarInstr(CodeGenContext & ctx, AllocVarInstruction * instr) { if (dynamic_cast(instr->Size.Ptr())) { ctx.DefineVariable(instr); } else throw InvalidProgramException("size operand of allocVar instr is not an intermediate."); } void CLikeCodeGen::PrintFetchArgInstrExpr(CodeGenContext & ctx, FetchArgInstruction * instr) { ctx.Body << instr->Name; } void CLikeCodeGen::PrintFetchArgInstr(CodeGenContext & ctx, FetchArgInstruction * instr) { if (instr->ArgId == 0) { ctx.ReturnVarName = ctx.DefineVariable(instr); } } void CLikeCodeGen::PrintSelectInstrExpr(CodeGenContext & ctx, SelectInstruction * instr) { ctx.Body << "("; PrintOp(ctx, instr->Operands[0].Ptr()); ctx.Body << "?"; PrintOp(ctx, instr->Operands[1].Ptr()); ctx.Body << ":"; PrintOp(ctx, instr->Operands[2].Ptr()); ctx.Body << ")"; } void CLikeCodeGen::PrintSelectInstr(CodeGenContext & ctx, SelectInstruction * instr) { auto varName = ctx.DefineVariable(instr); ctx.Body << varName << " = "; PrintSelectInstrExpr(ctx, instr); ctx.Body << ";\n"; } void CLikeCodeGen::PrintCallInstrExprForTarget(CodeGenContext & ctx, CallInstruction * instr, String const& name) { PrintDefaultCallInstrExpr(ctx, instr, name); } void CLikeCodeGen::PrintDefaultCallInstrArgs(CodeGenContext & ctx, CallInstruction * instr) { ctx.Body << "("; int id = 0; for (auto & arg : instr->Arguments) { PrintOp(ctx, arg.Ptr()); if (id != instr->Arguments.Count() - 1) ctx.Body << ", "; id++; } ctx.Body << ")"; } void CLikeCodeGen::PrintDefaultCallInstrExpr(CodeGenContext & ctx, CallInstruction * instr, String const& callName) { ctx.Body << callName; PrintDefaultCallInstrArgs(ctx, instr); } void CLikeCodeGen::PrintCallInstrExpr(CodeGenContext & ctx, CallInstruction * instr) { if (instr->Arguments.Count() > 0 && instr->Arguments.First()->Type->IsTexture() && intrinsicTextureFunctions.Contains(instr->Function)) { PrintTextureCall(ctx, instr); return; } String callName; callName = GetFuncOriginalName(instr->Function); PrintCallInstrExprForTarget(ctx, instr, callName); } void CLikeCodeGen::PrintCallInstr(CodeGenContext & ctx, CallInstruction * instr) { if (!instr->Type->IsVoid()) { auto varName = ctx.DefineVariable(instr); ctx.Body << varName; ctx.Body << " = "; } PrintCallInstrExpr(ctx, instr); ctx.Body << ";\n"; } void CLikeCodeGen::PrintCastF2IInstrExpr(CodeGenContext & ctx, Float2IntInstruction * instr) { ctx.Body << "((int)("; PrintOp(ctx, instr->Operand.Ptr()); ctx.Body << "))"; } void CLikeCodeGen::PrintCastF2IInstr(CodeGenContext & ctx, Float2IntInstruction * instr) { auto varName = ctx.DefineVariable(instr); ctx.Body << varName; ctx.Body << " = "; PrintCastF2IInstrExpr(ctx, instr); ctx.Body << ";\n"; } void CLikeCodeGen::PrintCastI2FInstrExpr(CodeGenContext & ctx, Int2FloatInstruction * instr) { ctx.Body << "((float)("; PrintOp(ctx, instr->Operand.Ptr()); ctx.Body << "))"; } void CLikeCodeGen::PrintCastI2FInstr(CodeGenContext & ctx, Int2FloatInstruction * instr) { auto varName = ctx.DefineVariable(instr); ctx.Body << varName; ctx.Body << " = "; PrintCastI2FInstrExpr(ctx, instr); ctx.Body << ";\n"; } bool CLikeCodeGen::AppearAsExpression(ILInstruction & instr, bool force) { if (instr.Is() || instr.Is() || instr.Is()) return true; if (auto arg = instr.As()) { if (arg->ArgId == 0) return false; } if (auto import = instr.As()) { if ((!useBindlessTexture && import->Type->IsTexture()) || import->Type.As() || import->Type->IsSamplerState() || import->Type.As()) return true; } for (auto &&usr : instr.Users) { if (auto update = dynamic_cast(usr)) { if (&instr == update->Operands[0].Ptr()) return false; } else if (dynamic_cast(usr)) return false; else if (dynamic_cast(usr)) return false; else if (dynamic_cast(usr)) return false; } if (instr.Is() && force) return true; return (instr.Users.Count() <= 1 && !instr.HasSideEffect() && !instr.Is() && !instr.Is() && !instr.Is()) || instr.Is(); } void CLikeCodeGen::PrintExportInstr(CodeGenContext &ctx, ExportInstruction * exportInstr) { outputStrategy->ProcessExportInstruction(ctx, exportInstr); } void CLikeCodeGen::PrintUpdateInstr(CodeGenContext & ctx, MemberUpdateInstruction * instr) { auto genCode = [&](String varName, ILType * srcType, ILOperand * op1, ILOperand * op2) { ctx.Body << varName; if (auto structType = dynamic_cast(srcType)) { ctx.Body << "."; ctx.Body << structType->Members[dynamic_cast(op1)->IntValues[0]].FieldName; } else { ctx.Body << "["; PrintOp(ctx, op1); ctx.Body << "]"; } ctx.Body << " = "; PrintOp(ctx, op2); ctx.Body << ";\n"; }; if (auto srcInstr = dynamic_cast(instr->Operands[0].Ptr())) { if (srcInstr->Users.Count() == 1) { auto srcName = srcInstr->Name; while (ctx.SubstituteNames.TryGetValue(srcName, srcName)); genCode(srcName, srcInstr->Type.Ptr(), instr->Operands[1].Ptr(), instr->Operands[2].Ptr()); ctx.SubstituteNames[instr->Name] = srcName; return; } } genCode(instr->Operands[0]->Name, instr->Operands[0]->Type.Ptr(), instr->Operands[1].Ptr(), instr->Operands[2].Ptr()); } void CLikeCodeGen::PrintSwizzleInstrExpr(CodeGenContext & ctx, SwizzleInstruction * swizzle) { PrintOp(ctx, swizzle->Operand.Ptr()); ctx.Body << "." << swizzle->SwizzleString; } void CLikeCodeGen::PrintImportInstr(CodeGenContext & ctx, ImportInstruction * importInstr) { currentImportInstr = importInstr; ctx.DefineVariable(importInstr); GenerateCode(ctx, importInstr->ImportOperator.Ptr()); currentImportInstr = nullptr; } void CLikeCodeGen::PrintImportInstrExpr(CodeGenContext & ctx, ImportInstruction * importInstr) { currentImportInstr = importInstr; PrintOp(ctx, importInstr->ImportOperator->GetLastInstruction()->As()->Operand.Ptr()); currentImportInstr = nullptr; } void CLikeCodeGen::PrintInstrExpr(CodeGenContext & ctx, ILInstruction & instr) { if (auto projInstr = instr.As()) PrintProjectInstrExpr(ctx, projInstr); else if (auto binInstr = instr.As()) PrintBinaryInstrExpr(ctx, binInstr); else if (auto unaryInstr = instr.As()) PrintUnaryInstrExpr(ctx, unaryInstr); else if (auto allocVar = instr.As()) PrintAllocVarInstrExpr(ctx, allocVar); else if (auto fetchArg = instr.As()) PrintFetchArgInstrExpr(ctx, fetchArg); else if (auto select = instr.As()) PrintSelectInstrExpr(ctx, select); else if (auto call = instr.As()) PrintCallInstrExpr(ctx, call); else if (auto castf2i = instr.As()) PrintCastF2IInstrExpr(ctx, castf2i); else if (auto casti2f = instr.As()) PrintCastI2FInstrExpr(ctx, casti2f); else if (auto ldInput = instr.As()) PrintLoadInputInstrExpr(ctx, ldInput); else if (auto import = instr.As()) PrintImportInstrExpr(ctx, import); else if (instr.As()) throw InvalidOperationException("member update instruction cannot appear as expression."); } void CLikeCodeGen::PrintInstr(CodeGenContext & ctx, ILInstruction & instr) { // ctx.Body << "// " << instr.ToString() << ";\n"; if (!AppearAsExpression(instr, false)) { if (auto binInstr = instr.As()) PrintBinaryInstr(ctx, binInstr); else if (auto exportInstr = instr.As()) PrintExportInstr(ctx, exportInstr); else if (auto unaryInstr = instr.As()) PrintUnaryInstr(ctx, unaryInstr); else if (auto allocVar = instr.As()) PrintAllocVarInstr(ctx, allocVar); else if (auto fetchArg = instr.As()) PrintFetchArgInstr(ctx, fetchArg); else if (auto select = instr.As()) PrintSelectInstr(ctx, select); else if (auto call = instr.As()) PrintCallInstr(ctx, call); else if (auto castf2i = instr.As()) PrintCastF2IInstr(ctx, castf2i); else if (auto casti2f = instr.As()) PrintCastI2FInstr(ctx, casti2f); else if (auto update = instr.As()) PrintUpdateInstr(ctx, update); else if (auto importInstr = instr.As()) PrintImportInstr(ctx, importInstr); } } void CLikeCodeGen::PrintLoadInputInstrExpr(CodeGenContext & ctx, LoadInputInstruction * instr) { PrintInputReference(ctx, ctx.Body, instr->InputName); } void CLikeCodeGen::GenerateCode(CodeGenContext & context, CFGNode * code) { for (auto & instr : *code) { if (auto ifInstr = instr.As()) { context.Body << "if (bool("; PrintOp(context, ifInstr->Operand.Ptr()); context.Body << "))\n{\n"; GenerateCode(context, ifInstr->TrueCode.Ptr()); context.Body << "}\n"; if (ifInstr->FalseCode) { context.Body << "else\n{\n"; GenerateCode(context, ifInstr->FalseCode.Ptr()); context.Body << "}\n"; } } else if (auto forInstr = instr.As()) { context.Body << "for ("; if (forInstr->InitialCode) PrintOp(context, forInstr->InitialCode->GetLastInstruction(), true); context.Body << "; "; if (forInstr->ConditionCode) PrintOp(context, forInstr->ConditionCode->GetLastInstruction(), true); context.Body << "; "; if (forInstr->SideEffectCode) PrintOp(context, forInstr->SideEffectCode->GetLastInstruction(), true); context.Body << ")\n{\n"; GenerateCode(context, forInstr->BodyCode.Ptr()); context.Body << "}\n"; } else if (auto doInstr = instr.As()) { context.Body << "do\n{\n"; GenerateCode(context, doInstr->BodyCode.Ptr()); context.Body << "} while (bool("; PrintOp(context, doInstr->ConditionCode->GetLastInstruction()->As()->Operand.Ptr()); context.Body << "));\n"; } else if (auto whileInstr = instr.As()) { context.Body << "while (bool("; PrintOp(context, whileInstr->ConditionCode->GetLastInstruction()->As()->Operand.Ptr()); context.Body << "))\n{\n"; GenerateCode(context, whileInstr->BodyCode.Ptr()); context.Body << "}\n"; } else if (auto ret = instr.As()) { if (currentImportInstr) { context.Body << currentImportInstr->Name << " = "; PrintOp(context, ret->Operand.Ptr()); context.Body << ";\n"; } else { context.Body << "return "; PrintOp(context, ret->Operand.Ptr()); context.Body << ";\n"; } } else if (instr.Is()) { context.Body << "break;\n"; } else if (instr.Is()) { context.Body << "continue;\n"; } else if (instr.Is()) { context.Body << "discard;\n"; } else PrintInstr(context, instr); } } CLikeCodeGen::CLikeCodeGen() { intrinsicTextureFunctions.Add("Sample"); intrinsicTextureFunctions.Add("SampleBias"); intrinsicTextureFunctions.Add("SampleGrad"); intrinsicTextureFunctions.Add("SampleLevel"); intrinsicTextureFunctions.Add("SampleCmp"); intrinsicTextureFunctions.Add("Load"); } void CLikeCodeGen::GenerateShaderMetaData(ShaderMetaData & result, ILProgram* /*program*/, ILShader * shader, DiagnosticSink * /*err*/) { result.ShaderName = shader->Name; result.ParameterSets = shader->ModuleParamSets; } CompiledShaderSource CLikeCodeGen::GenerateShader(CompileResult & result, SymbolTable *, ILShader * shader, DiagnosticSink * err) { this->errWriter = err; CompiledShaderSource rs; for (auto & stage : shader->Stages) { StageSource src; if (stage.Value->StageType == "VertexShader" || stage.Value->StageType == "FragmentShader" || stage.Value->StageType == "DomainShader") src = GenerateVertexFragmentDomainShader(result.Program.Ptr(), shader, stage.Value.Ptr()); else if (stage.Value->StageType == "ComputeShader") src = GenerateComputeShader(result.Program.Ptr(), shader, stage.Value.Ptr()); else if (stage.Value->StageType == "HullShader") src = GenerateHullShader(result.Program.Ptr(), shader, stage.Value.Ptr()); else errWriter->diagnose(stage.Value->Position, Diagnostics::unknownStageType, stage.Value->StageType); rs.Stages[stage.Key] = src; } GenerateShaderMetaData(rs.MetaData, result.Program.Ptr(), shader, err); return rs; } void CLikeCodeGen::GenerateStructs(StringBuilder & sb, ILProgram * program) { for (auto & st : program->Structs) { if (!st->IsIntrinsic) { sb << "struct " << st->TypeName << "\n{\n"; for (auto & f : st->Members) { PrintDef(sb, f.Type.Ptr(), f.FieldName); sb << ";\n"; } sb << "};\n"; } } } void CLikeCodeGen::GenerateReferencedFunctions(StringBuilder & sb, ILProgram * program, ArrayView worlds) { EnumerableHashSet refFuncs; for (auto & world : worlds) for (auto & func : world->ReferencedFunctions) refFuncs.Add(func); for (auto & func : program->Functions) { if (refFuncs.Contains(func.Value->Name)) { GenerateFunctionDeclaration(sb, func.Value.Ptr()); sb << ";\n"; } } for (auto & func : program->Functions) { if (refFuncs.Contains(func.Value->Name)) sb << GenerateFunction(func.Value.Ptr()); } } ExternComponentCodeGenInfo CLikeCodeGen::ExtractExternComponentInfo(const ILObjectDefinition & input) { auto type = input.Type.Ptr(); auto recType = ExtractRecordType(type); ExternComponentCodeGenInfo info; info.Type = type; Token bindingVal; if (input.Attributes.TryGetValue("Binding", bindingVal)) info.Binding = StringToInt(bindingVal.Content); if (recType) { if (auto genType = dynamic_cast(type)) { if (genType->GenericTypeName == "Patch") { type = genType->BaseType.Ptr(); info.DataStructure = ExternComponentCodeGenInfo::DataStructureType::Patch; } } if (auto arrType = dynamic_cast(type)) { if (info.DataStructure != ExternComponentCodeGenInfo::DataStructureType::StandardInput && info.DataStructure != ExternComponentCodeGenInfo::DataStructureType::Patch) { errWriter->diagnose(input.Position, Diagnostics::cannotGenerateCodeForExternComponentType, type); } type = arrType->BaseType.Ptr(); info.IsArray = true; info.ArrayLength = arrType->ArrayLength; } if (type != recType) { errWriter->diagnose(input.Position, Diagnostics::cannotGenerateCodeForExternComponentType, type); } } else { // check for attributes if (input.Attributes.ContainsKey("TessCoord")) { info.SystemVar = ExternComponentCodeGenInfo::SystemVarType::TessCoord; if (!(input.Type->IsFloatVector() && input.Type->GetVectorSize() <= 3)) getSink()->diagnose(input.Position, Diagnostics::invalidTessCoordType); } else if (input.Attributes.ContainsKey("FragCoord")) { info.SystemVar = ExternComponentCodeGenInfo::SystemVarType::FragCoord; if (!(input.Type->IsFloatVector() && input.Type->GetVectorSize() == 4)) getSink()->diagnose(input.Position, Diagnostics::invalidFragCoordType); } else if (input.Attributes.ContainsKey("InvocationId")) { info.SystemVar = ExternComponentCodeGenInfo::SystemVarType::InvocationId; if (!input.Type->IsInt()) getSink()->diagnose(input.Position, Diagnostics::invalidInvocationIdType); } else if (input.Attributes.ContainsKey("ThreadId")) { info.SystemVar = ExternComponentCodeGenInfo::SystemVarType::InvocationId; if (!input.Type->IsInt()) getSink()->diagnose(input.Position, Diagnostics::invalidThreadIdType); } else if (input.Attributes.ContainsKey("PrimitiveId")) { info.SystemVar = ExternComponentCodeGenInfo::SystemVarType::PrimitiveId; if (!input.Type->IsInt()) getSink()->diagnose(input.Position, Diagnostics::invalidPrimitiveIdType); } else if (input.Attributes.ContainsKey("PatchVertexCount")) { info.SystemVar = ExternComponentCodeGenInfo::SystemVarType::PatchVertexCount; if (!input.Type->IsInt()) getSink()->diagnose(input.Position, Diagnostics::invalidPatchVertexCountType); } else if (input.Attributes.ContainsKey("InstanceId")) { info.SystemVar = ExternComponentCodeGenInfo::SystemVarType::InstanceId; if (!input.Type->IsInt()) getSink()->diagnose(input.Position, Diagnostics::invalidTypeForSystemVar, "InstanceId", input.Type); } } return info; } void CLikeCodeGen::PrintInputReference(CodeGenContext & ctx, StringBuilder & sb, String input) { auto info = extCompInfo[input](); // TODO(tfoley): Is there any reason why this isn't just a `switch`? if (auto recType = ExtractRecordType(info.Type.Ptr())) { // TODO(tfoley): hoist this logic up to the top-level if chain? if(info.DataStructure == ExternComponentCodeGenInfo::DataStructureType::StandardInput) { if(info.IsArray) { PrintStandardArrayInputReference(sb, recType, input, currentImportInstr->ComponentName); } else { PrintStandardInputReference(sb, recType, input, currentImportInstr->ComponentName); } } else if(info.DataStructure == ExternComponentCodeGenInfo::DataStructureType::Patch) { PrintPatchInputReference(sb, recType, input, currentImportInstr->ComponentName); } else { // TODO(tfoley): Does this case ever actually trigger? PrintDefaultInputReference(sb, recType, input, currentImportInstr->ComponentName); } } else { PrintSystemVarReference(ctx, sb, input, info.SystemVar); } } void CLikeCodeGen::DeclareInput(CodeGenContext & sb, const ILObjectDefinition & input, bool isVertexShader) { auto info = ExtractExternComponentInfo(input); extCompInfo[input.Name] = info; auto recType = ExtractRecordType(input.Type.Ptr()); if (recType) { switch(info.DataStructure) { case ExternComponentCodeGenInfo::DataStructureType::StandardInput: DeclareStandardInputRecord(sb, input, isVertexShader); return; case ExternComponentCodeGenInfo::DataStructureType::Patch: DeclarePatchInputRecord(sb, input, isVertexShader); return; default: SPIRE_INTERNAL_ERROR(getSink(), input.Position); break; } } } void CLikeCodeGen::GenerateVertexShaderEpilog(CodeGenContext & ctx, ILWorld * world, ILStage * stage) { StageAttribute positionVar; if (stage->Attributes.TryGetValue("Position", positionVar)) { ILOperand * operand; if (world->Components.TryGetValue(positionVar.Value, operand)) { if (operand->Type->IsFloatVector() && operand->Type->GetVectorSize() == 4) { PrintRasterPositionOutputWrite(ctx, operand); } else errWriter->diagnose(positionVar.Position, Diagnostics::componentHasInvalidTypeForPositionOutput, positionVar.Value); } else errWriter->diagnose(positionVar.Position, Diagnostics::componentNotDefined, positionVar.Value); } } StageSource CLikeCodeGen::GenerateVertexFragmentDomainShader(ILProgram * program, ILShader * shader, ILStage * stage) { RefPtr world = nullptr; StageAttribute worldName; if (stage->Attributes.TryGetValue("World", worldName)) { if (!shader->Worlds.TryGetValue(worldName.Value, world)) errWriter->diagnose(worldName.Position, Diagnostics::worldIsNotDefined, worldName.Value); } outputStrategy = CreateStandardOutputStrategy(world.Ptr(), ""); return GenerateSingleWorldShader(program, shader, stage); } StageSource CLikeCodeGen::GenerateComputeShader(ILProgram * program, ILShader * shader, ILStage * stage) { RefPtr world = nullptr; StageAttribute worldName; if (stage->Attributes.TryGetValue("World", worldName)) { if (!shader->Worlds.TryGetValue(worldName.Value, world)) errWriter->diagnose(worldName.Position, Diagnostics::worldIsNotDefined, worldName); } outputStrategy = CreatePackedBufferOutputStrategy(world.Ptr()); return GenerateSingleWorldShader(program, shader, stage); } void CLikeCodeGen::GenerateFunctionDeclaration(StringBuilder & sbCode, ILFunction * function) { function->Code->NameAllInstructions(); auto retType = function->ReturnType.Ptr(); if (retType) PrintType(sbCode, retType); else sbCode << "void"; sbCode << " " << GetFuncOriginalName(function->Name) << "("; int id = 0; auto paramIter = function->Parameters.begin(); for (auto & instr : *function->Code) { if (auto arg = instr.As()) { if (arg->ArgId != 0) { if (id > 0) { sbCode << ", "; } auto qualifier = (*paramIter).Value.Qualifier; if (qualifier == ParameterQualifier::InOut) sbCode << "inout "; else if (qualifier == ParameterQualifier::Out) sbCode << "out "; else if (qualifier == ParameterQualifier::Uniform) sbCode << "uniform "; PrintDef(sbCode, arg->Type.Ptr(), arg->Name); id++; } ++paramIter; } } sbCode << ")"; } String CLikeCodeGen::GenerateFunction(ILFunction * function) { StringBuilder sbCode; CodeGenContext ctx; ctx.codeGen = this; ctx.UsedVarNames.Clear(); ctx.Body.Clear(); ctx.Header.Clear(); ctx.Arguments.Clear(); ctx.ReturnVarName = ""; ctx.VarName.Clear(); function->Code->NameAllInstructions(); GenerateFunctionDeclaration(sbCode, function); sbCode << "\n{\n"; GenerateCode(ctx, function->Code.Ptr()); sbCode << ctx.Header.ToString() << ctx.Body.ToString(); if (ctx.ReturnVarName.Length()) sbCode << "return " << ctx.ReturnVarName << ";\n"; sbCode << "}\n"; return sbCode.ProduceString(); } String CodeGenContext::DefineVariable(ILOperand * op) { String rs; if (VarName.TryGetValue(op, rs)) { return rs; } else { auto name = GenerateCodeName(op->Name, ""); codeGen->PrintDef(Header, op->Type.Ptr(), name); if (op->Type->IsInt() || op->Type->IsUInt()) { Header << " = 0"; } Header << ";\n"; VarName.Add(op, name); op->Name = name; return op->Name; } } } } ================================================ FILE: Source/SpireCore/CLikeCodeGen.h ================================================ // CLikeCodeGen.h #ifndef SPIRE_C_LIKE_CODE_GEN_H #define SPIRE_C_LIKE_CODE_GEN_H // // This file implements the shared logic for code generation in C-like // languages, such as GLSL and HLSL. // #include "CodeGenBackend.h" #include "../CoreLib/Tokenizer.h" #include "Syntax.h" #include "Naming.h" namespace Spire { namespace Compiler { using namespace CoreLib::Basic; ILRecordType * ExtractRecordType(ILType * type); String AddWorldNameSuffix(String name, String suffix); class CLikeCodeGen; class ExternComponentCodeGenInfo { public: enum class DataStructureType { StandardInput, Patch }; enum class SystemVarType { None, TessCoord, InvocationId, ThreadId, FragCoord, PatchVertexCount, PrimitiveId, InstanceId, }; DataStructureType DataStructure = DataStructureType::StandardInput; RefPtr Type; SystemVarType SystemVar = SystemVarType::None; bool IsArray = false; int ArrayLength = 0; int Binding = -1; }; class CodeGenContext { public: CLikeCodeGen * codeGen; HashSet GeneratedDefinitions; Dictionary SubstituteNames; Dictionary VarName; CompileResult * Result = nullptr; HashSet UsedVarNames; int BufferAllocator = 0; StringBuilder Body, Header, GlobalHeader; List Arguments; String ReturnVarName; HashSet UsedSystemInputs; String GenerateCodeName(String name, String prefix) { StringBuilder nameBuilder; int startPos = 0; if (name.StartsWith("_sys_")) startPos = name.IndexOf('_', 5) + 1; nameBuilder << prefix; for (int i = startPos; i < name.Length(); i++) { if ((name[i] >= 'a' && name[i] <= 'z') || (name[i] >= 'A' && name[i] <= 'Z') || name[i] == '_' || (name[i] >= '0' && name[i] <= '9')) { nameBuilder << name[i]; } else nameBuilder << '_'; } auto rs = nameBuilder.ToString(); int i = 0; while (UsedVarNames.Contains(rs)) { i++; rs = nameBuilder.ToString() + String(i); } UsedVarNames.Add(rs); return rs; } String DefineVariable(ILOperand * op); }; class OutputStrategy : public Object { protected: CLikeCodeGen * codeGen = nullptr; ILWorld * world = nullptr; public: OutputStrategy(CLikeCodeGen * pCodeGen, ILWorld * pWorld) { codeGen = pCodeGen; world = pWorld; } virtual void DeclareOutput(CodeGenContext & ctx, ILStage * stage) = 0; virtual void ProcessExportInstruction(CodeGenContext & ctx, ExportInstruction * instr) = 0; }; class CLikeCodeGen : public CodeGenBackend { protected: //ILWorld * currentWorld = nullptr; //ILRecordType * currentRecordType = nullptr; //bool exportWriteToPackedBuffer = false; RefPtr outputStrategy; Dictionary extCompInfo; HashSet intrinsicTextureFunctions; ImportInstruction * currentImportInstr = nullptr; bool useBindlessTexture = false; DiagnosticSink * errWriter; virtual OutputStrategy * CreateStandardOutputStrategy(ILWorld * world, String layoutPrefix) = 0; virtual OutputStrategy * CreatePackedBufferOutputStrategy(ILWorld * world) = 0; virtual OutputStrategy * CreateArrayOutputStrategy(ILWorld * world, bool pIsPatch, int pArraySize, String arrayIndex) = 0; // Hooks for declaring an input record based on the storage mode used (uniform, SSBO, etc.) virtual void DeclareStandardInputRecord(CodeGenContext & sb, const ILObjectDefinition & input, bool isVertexShader) = 0; virtual void DeclarePatchInputRecord(CodeGenContext & sb, const ILObjectDefinition & input, bool isVertexShader) = 0; // Hooks for generating per-stage kernels virtual StageSource GenerateSingleWorldShader(ILProgram * program, ILShader * shader, ILStage * stage) = 0; virtual StageSource GenerateHullShader(ILProgram * program, ILShader * shader, ILStage * stage) = 0; virtual void PrintParameterReference(StringBuilder& sb, ILModuleParameterInstance * param) = 0; // Print a reference to some entity that is input to a kernel virtual void PrintStandardInputReference(StringBuilder& sb, ILRecordType* recType, String inputName, String componentName) = 0; virtual void PrintStandardArrayInputReference(StringBuilder& sb, ILRecordType* recType, String inputName, String componentName) = 0; virtual void PrintPatchInputReference(StringBuilder& sb, ILRecordType* recType, String inputName, String componentName) = 0; virtual void PrintDefaultInputReference(StringBuilder& sb, ILRecordType* recType, String inputName, String componentName) = 0; virtual void PrintSystemVarReference(CodeGenContext & ctx, StringBuilder& sb, String inputName, ExternComponentCodeGenInfo::SystemVarType systemVar) = 0; // virtual void PrintTypeName(StringBuilder& sb, ILType* type) = 0; virtual void PrintCallInstrExprForTarget(CodeGenContext & ctx, CallInstruction * instr, String const& name); virtual void PrintMatrixMulInstrExpr(CodeGenContext & ctx, ILOperand* op0, ILOperand* op1); virtual void PrintRasterPositionOutputWrite(CodeGenContext & ctx, ILOperand * operand) = 0; virtual void PrintTextureCall(CodeGenContext & ctx, CallInstruction * instr) = 0; virtual void PrintProjectInstrExpr(CodeGenContext & ctx, ProjectInstruction * instr) = 0; // Helpers for printing call instructions void PrintDefaultCallInstrArgs(CodeGenContext & ctx, CallInstruction * instr); void PrintDefaultCallInstrExpr(CodeGenContext & ctx, CallInstruction * instr, String const& name); public: DiagnosticSink* getSink() { return this->errWriter; } void PrintType(StringBuilder & sbCode, ILType* type); void PrintDef(StringBuilder & sbCode, ILType* type, const String & name); String GetFuncOriginalName(const String & name); virtual void PrintOp(CodeGenContext & ctx, ILOperand * op, bool forceExpression = false); void PrintBinaryInstrExpr(CodeGenContext & ctx, BinaryInstruction * instr); void PrintBinaryInstr(CodeGenContext & ctx, BinaryInstruction * instr); void PrintUnaryInstrExpr(CodeGenContext & ctx, UnaryInstruction * instr); void PrintUnaryInstr(CodeGenContext & ctx, UnaryInstruction * instr); void PrintAllocVarInstrExpr(CodeGenContext & ctx, AllocVarInstruction * instr); void PrintAllocVarInstr(CodeGenContext & ctx, AllocVarInstruction * instr); void PrintFetchArgInstrExpr(CodeGenContext & ctx, FetchArgInstruction * instr); void PrintFetchArgInstr(CodeGenContext & ctx, FetchArgInstruction * instr); void PrintSelectInstrExpr(CodeGenContext & ctx, SelectInstruction * instr); void PrintSelectInstr(CodeGenContext & ctx, SelectInstruction * instr); void PrintCallInstrExpr(CodeGenContext & ctx, CallInstruction * instr); void PrintCallInstr(CodeGenContext & ctx, CallInstruction * instr); void PrintCastF2IInstrExpr(CodeGenContext & ctx, Float2IntInstruction * instr); void PrintCastF2IInstr(CodeGenContext & ctx, Float2IntInstruction * instr); void PrintCastI2FInstrExpr(CodeGenContext & ctx, Int2FloatInstruction * instr); void PrintCastI2FInstr(CodeGenContext & ctx, Int2FloatInstruction * instr); bool AppearAsExpression(ILInstruction & instr, bool force); void PrintExportInstr(CodeGenContext &ctx, ExportInstruction * exportInstr); void PrintUpdateInstr(CodeGenContext & ctx, MemberUpdateInstruction * instr); void PrintSwizzleInstrExpr(CodeGenContext & ctx, SwizzleInstruction * swizzle); void PrintImportInstr(CodeGenContext & ctx, ImportInstruction * importInstr); void PrintImportInstrExpr(CodeGenContext & ctx, ImportInstruction * importInstr); void PrintInstrExpr(CodeGenContext & ctx, ILInstruction & instr); void PrintInstr(CodeGenContext & ctx, ILInstruction & instr); void PrintLoadInputInstrExpr(CodeGenContext & ctx, LoadInputInstruction * instr); void GenerateCode(CodeGenContext & context, CFGNode * code); public: CLikeCodeGen(); virtual void GenerateShaderMetaData(ShaderMetaData & result, ILProgram* program, ILShader * shader, DiagnosticSink * err); virtual CompiledShaderSource GenerateShader(CompileResult & result, SymbolTable *, ILShader * shader, DiagnosticSink * err) override; void GenerateStructs(StringBuilder & sb, ILProgram * program); void GenerateReferencedFunctions(StringBuilder & sb, ILProgram * program, ArrayView worlds); ExternComponentCodeGenInfo ExtractExternComponentInfo(const ILObjectDefinition & input); void PrintInputReference(CodeGenContext & ctx, StringBuilder & sb, String input); void DeclareInput(CodeGenContext & sb, const ILObjectDefinition & input, bool isVertexShader); void GenerateVertexShaderEpilog(CodeGenContext & ctx, ILWorld * world, ILStage * stage); StageSource GenerateVertexFragmentDomainShader(ILProgram * program, ILShader * shader, ILStage * stage); StageSource GenerateComputeShader(ILProgram * program, ILShader * shader, ILStage * stage); void GenerateFunctionDeclaration(StringBuilder & sbCode, ILFunction * function); String GenerateFunction(ILFunction * function); }; } } #endif // SPIRE_C_LIKE_CODE_GEN_H ================================================ FILE: Source/SpireCore/Closure.cpp ================================================ #include "Closure.h" #include "StringObject.h" #include "Naming.h" namespace Spire { namespace Compiler { void CheckComponentRedefinition(DiagnosticSink * err, ShaderClosure * parent, ShaderClosure * child) { for (auto & comp : child->Components) { RefPtr ccomp; RefPtr su; if ((comp.Value->Implementations.First()->SyntaxNode->IsPublic() || comp.Value->Implementations.First()->SyntaxNode->IsOutput())) { if (parent->Components.TryGetValue(comp.Key, ccomp)) { err->diagnose(comp.Value->Implementations.First()->SyntaxNode, Diagnostics::nameAlreadyDefinedInCurrentScope, comp.Key); err->diagnose(ccomp->Implementations.First()->SyntaxNode, Diagnostics::seePreviousDefinition); } else if (parent->SubClosures.TryGetValue(comp.Key, su)) { err->diagnose(comp.Value->Implementations.First()->SyntaxNode->Position, Diagnostics::nameAlreadyDefinedInCurrentScope, comp.Key); err->diagnose(su->UsingPosition, Diagnostics::seePreviousDefinition); } } } for (auto & c : child->SubClosures) { if (c.Value->IsInPlace) { RefPtr ccomp; RefPtr su; if (parent->Components.TryGetValue(c.Key, ccomp)) { err->diagnose(c.Value->UsingPosition, Diagnostics::nameAlreadyDefinedInCurrentScope, c.Key); err->diagnose(ccomp->Implementations.First()->SyntaxNode, Diagnostics::seePreviousDefinition); } else if (parent->SubClosures.TryGetValue(c.Key, su)) { err->diagnose(c.Value->UsingPosition, Diagnostics::nameAlreadyDefinedInCurrentScope, c.Key); err->diagnose(su->UsingPosition, Diagnostics::seePreviousDefinition); } for (auto & sc : c.Value->SubClosures) if (sc.Value->IsInPlace) CheckComponentRedefinition(err, parent, sc.Value.Ptr()); } } } RefPtr CreateShaderClosure(DiagnosticSink * err, SymbolTable * symTable, ShaderSymbol * shader, CodePosition usingPos, ShaderClosure * rootShader, const EnumerableDictionary>& pRefMap) { RefPtr rs = new ShaderClosure(); if (rootShader == nullptr) { rootShader = rs.Ptr(); rootShader->Pipeline = shader->ParentPipeline; } rs->ModuleSyntaxNode = shader->SyntaxNode.Ptr(); rs->Name = shader->SyntaxNode->Name.Content; rs->RefMap = pRefMap; if (shader->ParentPipeline && rootShader->Pipeline) { if (shader->ParentPipeline->IsChildOf(rootShader->Pipeline)) rootShader->Pipeline = shader->ParentPipeline; else if (!rootShader->Pipeline->IsChildOf(shader->ParentPipeline)) { err->diagnose(shader->SyntaxNode->Position, Diagnostics::pipelineOfModuleIncompatibleWithPipelineOfShader, shader->ParentPipeline->SyntaxNode->Name, shader->SyntaxNode->Name.Content, rootShader->Pipeline->SyntaxNode->Name.Content, rootShader->Name); err->diagnose(shader->SyntaxNode->Position, Diagnostics::seeDefinitionOfShader, shader->SyntaxNode->Name); } } rs->Pipeline = rootShader->Pipeline; rs->UsingPosition = usingPos; rs->Position = shader->SyntaxNode->Position; for (auto & mbr : shader->SyntaxNode->Members) { if (auto import = dynamic_cast(mbr.Ptr())) { // create component for each argument EnumerableDictionary> refMap; for (auto & arg : import->Arguments) { RefPtr ccomp = new ShaderComponentSymbol(); auto compName = "arg" + String(rs->Components.Count()) + "_" + (import->ObjectName.Content.Length()==0?import->ShaderName.Content:import->ObjectName.Content) + arg->ArgumentName.Content; auto impl = new ShaderComponentImplSymbol(); auto compSyntax = new ComponentSyntaxNode(); compSyntax->Position = arg->Expression->Position; compSyntax->Name.Content = compName; CloneContext cloneCtx; compSyntax->Expression = arg->Expression->Clone(cloneCtx); compSyntax->TypeNode = new BasicTypeSyntaxNode(); compSyntax->TypeNode->Position = compSyntax->Position; impl->SyntaxNode = compSyntax; ccomp->Name = compName; ccomp->Type = new Type(); ccomp->Type->DataType = arg->Expression->Type; ccomp->Implementations.Add(impl); rs->Components[compName] = ccomp; refMap[arg->ArgumentName.Content] = ccomp; } RefPtr shaderSym; if (symTable->Shaders.TryGetValue(import->ShaderName.Content, shaderSym)) { // fill in automatic arguments for (auto & param : shaderSym->Components) { if (param.Value->IsRequire() && !refMap.ContainsKey(param.Key)) { auto arg = rs->FindComponent(param.Key); if (arg && arg->Type->DataType->Equals(param.Value->Type->DataType.Ptr())) { refMap[param.Key] = arg; } } } auto refClosure = CreateShaderClosure(err, symTable, shaderSym.Ptr(), import->Position, rootShader, refMap); refClosure->IsPublic = import->IsPublic(); refClosure->Parent = rs.Ptr(); Token bindingVal; if (import->FindSimpleAttribute("Binding", bindingVal)) { refClosure->BindingIndex = StringToInt(bindingVal.Content); } if (import->IsInplace) { refClosure->IsInPlace = true; CheckComponentRedefinition(err, rs.Ptr(), refClosure.Ptr()); rs->SubClosures["annonymousObj" + String(UniqueIdGenerator::Next())] = refClosure; } else { refClosure->Name = import->ObjectName.Content; rs->SubClosures[import->ObjectName.Content] = refClosure; } } } else if (auto compt = dynamic_cast(mbr.Ptr())) { RefPtr comp; if (shader->Components.TryGetValue(compt->Name.Content, comp) && !rs->Components.ContainsKey(compt->Name.Content)) { RefPtr ccomp = new ShaderComponentSymbol(*comp); rs->Components.Add(comp->Name, ccomp); } } } // check for unassigned arguments for (auto & comp : shader->Components) { if (comp.Value->Implementations.First()->SyntaxNode->IsRequire() && !pRefMap.ContainsKey(comp.Key)) { err->diagnose(rs->UsingPosition, Diagnostics::parameterOfModuleIsUnassigned, comp.Key, shader->SyntaxNode->Name); // try to provide more info on why it is unassigned auto arg = rootShader->FindComponent(comp.Key, true, false); if (!arg) err->diagnose(rootShader, Diagnostics::implicitParameterMatchingFailedBecauseShaderDoesNotDefineComponent, rootShader->Name, comp.Key); else { if (comp.Value->Type->DataType->Equals(arg->Type->DataType.Ptr())) { err->diagnose(rs->UsingPosition, Diagnostics::implicitParameterMatchingFailedBecauseNameNotAccessible, shader->SyntaxNode->Name); } else { err->diagnose(rs->UsingPosition, Diagnostics::implicitParameterMatchingFailedBecauseTypeMismatch, comp.Value->Type->DataType); } err->diagnose(comp.Value->Implementations.First()->SyntaxNode, Diagnostics::seeRequirementDeclaration); err->diagnose(arg->Implementations.First()->SyntaxNode, Diagnostics::seePotentialDefinitionOfComponent, comp.Key); } } } return rs; } RefPtr CreateShaderClosure(DiagnosticSink * err, SymbolTable * symTable, ShaderSymbol * shader) { return CreateShaderClosure(err, symTable, shader, shader->SyntaxNode->Position, nullptr, EnumerableDictionary>()); } class ReplaceReferenceVisitor : public SyntaxVisitor { private: ShaderClosure * shaderClosure = nullptr; ShaderComponentSymbol * currentComponent = nullptr; ImportExpressionSyntaxNode * currentImport = nullptr; void ReplaceReference(RefPtr refComp) { String targetComp; if ((*replacements).TryGetValue(refComp->Content, targetComp)) { auto oldComp = shaderClosure->AllComponents[refComp->Content]().Symbol; auto newComp = shaderClosure->AllComponents[targetComp]().Symbol; if (auto * importOps = currentComponent->DependentComponents.TryGetValue(newComp)) importOps->Add(currentImport); else { EnumerableHashSet> op; op.Add(currentImport); currentComponent->DependentComponents.Add(newComp, op); } currentComponent->DependentComponents.Remove(oldComp); if (auto * importOps = currentImpl->DependentComponents.TryGetValue(newComp)) importOps->Add(currentImport); else { EnumerableHashSet> op; op.Add(currentImport); currentImpl->DependentComponents.Add(newComp, op); } currentImpl->DependentComponents.Remove(oldComp); currentImpl->ComponentReferencePositions[newComp] = currentImpl->ComponentReferencePositions[oldComp](); refComp->Content = newComp->UniqueName; } } public: ShaderComponentImplSymbol * currentImpl = nullptr; EnumerableDictionary * replacements; ReplaceReferenceVisitor(ShaderClosure * closure, ShaderComponentSymbol * comp, EnumerableDictionary &pReplacements) : SyntaxVisitor(nullptr), shaderClosure(closure), currentComponent(comp), replacements(&pReplacements) {} RefPtr VisitImportExpression(ImportExpressionSyntaxNode * import) override { currentImport = import; import->Component->Accept(this); if (import->Component->Tags.ContainsKey("ComponentReference")) { import->ComponentUniqueName = import->Component->Tags["ComponentReference"]().As()->Content; } currentImport = nullptr; for (auto & arg : import->Arguments) arg->Accept(this); return import; } RefPtr VisitVarExpression(VarExpressionSyntaxNode * var) override { RefPtr compRef; if (var->Tags.TryGetValue("ComponentReference", compRef)) { ReplaceReference(compRef.As()); } return var; } RefPtr VisitMemberExpression(MemberExpressionSyntaxNode * member) override { member->BaseExpression->Accept(this); RefPtr compRef; if (member->Tags.TryGetValue("ComponentReference", compRef)) { ReplaceReference(compRef.As()); } return member; } }; class ResolveDependencyVisitor : public SyntaxVisitor { private: ShaderClosure * shaderClosure = nullptr, *rootShader = nullptr; ShaderComponentSymbol * currentComponent = nullptr; ImportExpressionSyntaxNode * currentImport = nullptr; void AddReference(ShaderComponentSymbol * referee, ImportExpressionSyntaxNode * importOp, CodePosition pos) { ComponentInstance referedCompInst; if (rootShader->AllComponents.TryGetValue(referee->UniqueName, referedCompInst)) referee = referedCompInst.Symbol; if (auto * importOps = currentComponent->DependentComponents.TryGetValue(referee)) importOps->Add(importOp); else { EnumerableHashSet> op; op.Add(importOp); currentComponent->DependentComponents.Add(referee, op); } if (auto * importOps = currentImpl->DependentComponents.TryGetValue(referee)) importOps->Add(importOp); else { EnumerableHashSet> op; op.Add(importOp); currentImpl->DependentComponents.Add(referee, op); } currentImpl->ComponentReferencePositions[referee] = pos; } public: ShaderComponentImplSymbol * currentImpl = nullptr; ResolveDependencyVisitor(DiagnosticSink * err, ShaderClosure * pRootShader, ShaderClosure * closure, ShaderComponentSymbol * comp) : SyntaxVisitor(err), shaderClosure(closure), rootShader(pRootShader), currentComponent(comp) {} RefPtr VisitImportExpression(ImportExpressionSyntaxNode * import) override { currentImport = import; import->Component->Accept(this); if (!import->Component->Tags.ContainsKey("ComponentReference")) { getSink()->diagnose(import->Component.Ptr(), Diagnostics::firstArgumentToImportNotComponent); } else { import->ComponentUniqueName = import->Component->Tags["ComponentReference"]().As()->Content; } currentImport = nullptr; for (auto & arg : import->Arguments) arg->Accept(this); import->ImportOperatorDef->Accept(this); return import; } RefPtr VisitVarExpression(VarExpressionSyntaxNode * var) override { if (auto decl = var->Scope->LookUp(var->Variable)) { // TODO(tfoley): wire up the variable to the declaration if (dynamic_cast(decl)) // make sure this is a ordinary variable (currently ComponentSyntaxNode is not VarDeclBase) return var; } // Otherwise look in other places... if (var->Type->AsBasicType() && var->Type->AsBasicType()->Component) { if (auto comp = shaderClosure->FindComponent(var->Type->AsBasicType()->Component->Name)) { if (comp->Implementations.First()->SyntaxNode->IsRequire()) shaderClosure->RefMap.TryGetValue(comp->Name, comp); var->Tags["ComponentReference"] = new StringObject(comp->UniqueName); AddReference(comp.Ptr(), currentImport, var->Position); } else throw InvalidProgramException("cannot resolve reference."); } if (auto comp = shaderClosure->FindComponent(var->Variable)) { if (comp->Implementations.First()->SyntaxNode->IsRequire()) shaderClosure->RefMap.TryGetValue(var->Variable, comp); var->Tags["ComponentReference"] = new StringObject(comp->UniqueName); AddReference(comp.Ptr(), currentImport, var->Position); } else if (auto closure = shaderClosure->FindClosure(var->Variable)) { ShaderSymbol * originalShader = nullptr; if (var->Type->AsBasicType()) originalShader = var->Type->AsBasicType()->Shader; var->Type = new BasicExpressionType(originalShader, closure.Ptr()); } else if (!(var->Type->AsBasicType() && var->Type->AsBasicType()->BaseType == BaseType::Function)) throw InvalidProgramException("cannot resolve reference."); return var; } RefPtr VisitMemberExpression(MemberExpressionSyntaxNode * member) override { member->BaseExpression->Accept(this); if (member->BaseExpression->Type->AsBasicType() && member->BaseExpression->Type->AsBasicType()->ShaderClosure) { String memberName = member->MemberName; // HACK(tfoley): Is this a valid fix? if(member->Type->AsBasicType()) if (auto refComp = member->Type->AsBasicType()->Component) { memberName = refComp->Name; } if (auto comp = member->BaseExpression->Type->AsBasicType()->ShaderClosure->FindComponent(memberName)) { member->Tags["ComponentReference"] = new StringObject(comp->UniqueName); AddReference(comp.Ptr(), currentImport, member->Position); } else if (auto shader = member->BaseExpression->Type->AsBasicType()->ShaderClosure->FindClosure(memberName)) { ShaderSymbol * originalShader = nullptr; if (member->Type->AsBasicType()) originalShader = member->Type->AsBasicType()->Shader; member->Type = new BasicExpressionType(originalShader, shader.Ptr()); } } else if (member->Type->AsBasicType() && member->Type->AsBasicType()->Component) { if (auto comp = shaderClosure->FindComponent(member->Type->AsBasicType()->Component->Name)) { member->Tags["ComponentReference"] = new StringObject(comp->UniqueName); AddReference(comp.Ptr(), currentImport, member->Position); } else throw InvalidProgramException("cannot resolve reference."); } return member; } }; void ResolveReference(DiagnosticSink * err, ShaderClosure * rootShader, ShaderClosure* shader) { for (auto & comp : shader->Components) { ResolveDependencyVisitor depVisitor(err, rootShader, shader, comp.Value.Ptr()); for (auto & impl : comp.Value->Implementations) { depVisitor.currentImpl = impl.Ptr(); impl->SyntaxNode->Accept(&depVisitor); } } for (auto & subClosure : shader->SubClosures) ResolveReference(err, rootShader, subClosure.Value.Ptr()); } void ReplaceRefMapReference(ShaderClosure * root, ShaderClosure * shader, EnumerableDictionary & replacements) { for (auto & map : shader->RefMap) { String newName = map.Value->UniqueName; while (replacements.TryGetValue(newName, newName)) { } if (newName != map.Value->UniqueName) map.Value = root->AllComponents[newName]().Symbol; } for (auto & subclosure : shader->SubClosures) ReplaceRefMapReference(root, subclosure.Value.Ptr(), replacements); } void ReplaceReference(ShaderClosure * shader, EnumerableDictionary & replacements) { ReplaceRefMapReference(shader, shader, replacements); for (auto & comp : shader->AllComponents) { ReplaceReferenceVisitor replaceVisitor(shader, comp.Value.Symbol, replacements); for (auto & impl : comp.Value.Symbol->Implementations) { replaceVisitor.currentImpl = impl.Ptr(); impl->SyntaxNode->Accept(&replaceVisitor); } } } bool IsInAbstractWorld(PipelineSymbol * pipeline, ShaderComponentSymbol* comp) { return comp->Implementations.First()->Worlds.Count() && !comp->Implementations.First()->SyntaxNode->IsRequire() && pipeline->IsAbstractWorld(comp->Implementations.First()->Worlds.First()); } void AssignUniqueNames(ShaderClosure * shader, String namePrefix, String publicNamePrefix) { for (auto & comp : shader->Components) { if (IsInAbstractWorld(shader->Pipeline, comp.Value.Ptr())) { comp.Value->UniqueKey = comp.Value->UniqueName = comp.Value->Name; } else { String uniqueChoiceName; if (comp.Value->Implementations.First()->SyntaxNode->IsPublic()) uniqueChoiceName = publicNamePrefix + comp.Key; else uniqueChoiceName = namePrefix + comp.Key; comp.Value->ChoiceNames.Add(uniqueChoiceName); comp.Value->UniqueKey = uniqueChoiceName; comp.Value->UniqueName = EscapeCodeName(uniqueChoiceName); } } for (auto & subClosure : shader->SubClosures) { if (subClosure.Value->IsInPlace) AssignUniqueNames(subClosure.Value.Ptr(), namePrefix + subClosure.Value->Name + ".", publicNamePrefix); else AssignUniqueNames(subClosure.Value.Ptr(), namePrefix + subClosure.Key + ".", publicNamePrefix + subClosure.Key + "."); } } bool IsConsistentGlobalComponentDefinition(ShaderComponentSymbol * comp0, ShaderComponentSymbol * comp1) { if (comp0->Type->DataType != comp1->Type->DataType) return false; if (comp0->Implementations.First()->Worlds.Count() != comp1->Implementations.First()->Worlds.Count()) return false; for (auto w : comp0->Implementations.First()->Worlds) if (!comp1->Implementations.First()->Worlds.Contains(w)) return false; return true; } void GatherComponents(DiagnosticSink * err, ShaderClosure * closure, ShaderClosure * subClosure) { for (auto & comp : subClosure->Components) { ShaderComponentSymbol* existingComp = nullptr; if (comp.Value->IsRequire()) continue; ComponentInstance existingCompInst; if (closure->AllComponents.TryGetValue(comp.Value->UniqueName, existingCompInst)) { existingComp = existingCompInst.Symbol; if (IsInAbstractWorld(closure->Pipeline, comp.Value.Ptr()) && IsInAbstractWorld(closure->Pipeline, existingComp)) { if (!IsConsistentGlobalComponentDefinition(comp.Value.Ptr(), existingComp)) { err->diagnose(comp.Value->Implementations.First()->SyntaxNode, Diagnostics::globalComponentConflictWithPreviousDeclaration, existingComp->Name); err->diagnose(existingComp->Implementations.First()->SyntaxNode, Diagnostics::seePreviousDefinition); } else { // silently ignore consistently defined global components (components in abstract worlds) err->diagnose(comp.Value->Implementations.First()->SyntaxNode, Diagnostics::componentIsAlreadyDefinedUseRequire, existingComp->Name, closure->Name); err->diagnose(existingComp->Implementations.First()->SyntaxNode, Diagnostics::seePreviousDefinition); } } else if (!comp.Value->Implementations.First()->SyntaxNode->IsComponentFunction()) { err->diagnose(comp.Value->Implementations.First()->SyntaxNode, Diagnostics::componentAlreadyDefinedWhenCompiling, comp.Value->UniqueKey, closure->Name); auto currentClosure = subClosure; while (currentClosure != nullptr && currentClosure != closure) { err->diagnose(currentClosure->UsingPosition, Diagnostics::seeInclusionOf, currentClosure->Name); currentClosure = currentClosure->Parent; } } } closure->AllComponents[comp.Value->UniqueName] = ComponentInstance(subClosure, comp.Value.Ptr()); } for (auto & sc : subClosure->SubClosures) GatherComponents(err, closure, sc.Value.Ptr()); } bool IsWorldFeasible(SymbolTable * symTable, PipelineSymbol * pipeline, ShaderComponentImplSymbol * impl, String world, ShaderComponentSymbol*& unaccessibleComp) { // shader parameter (uniform values) are available to all worlds if (impl->SyntaxNode->IsParam()) return true; bool isWFeasible = true; for (auto & dcomp : impl->DependentComponents) { if (dcomp.Value.Contains(nullptr)) { bool reachable = false; for (auto & dw : dcomp.Key->Type->FeasibleWorlds) { if (symTable->IsWorldImplicitlyReachable(pipeline, dw, world, dcomp.Key->Type->DataType)) { reachable = true; break; } } if (!reachable) { unaccessibleComp = dcomp.Key; isWFeasible = false; break; } } } return isWFeasible; } void SolveWorldConstraints(DiagnosticSink * err, SymbolTable * symTable, ShaderClosure * shader) { EnumerableHashSet allWorlds; for (auto w : shader->Pipeline->Worlds) if (!shader->Pipeline->IsAbstractWorld(w.Key)) allWorlds.Add(w.Key); auto depOrder = shader->GetDependencyOrder(); for (auto & comp : depOrder) { comp->Type->FeasibleWorlds.Clear(); for (auto & impl : comp->Implementations) { auto autoWorld = allWorlds; for (auto & w : impl->Worlds) { ShaderComponentSymbol* unaccessibleComp = nullptr; if (!IsWorldFeasible(symTable, shader->Pipeline, impl.Ptr(), w, unaccessibleComp)) { err->diagnose(impl->ComponentReferencePositions[unaccessibleComp](), Diagnostics::componentCantBeComputedAtWorldBecauseDependentNotAvailable, comp->Name, w, unaccessibleComp->Name); err->diagnose(unaccessibleComp->Implementations.First()->SyntaxNode, Diagnostics::seeDefinitionOf, unaccessibleComp->Name); } // if a world is explicitly stated in any of the component defintions, remove it from autoWorld autoWorld.Remove(w); } } for (auto & impl : comp->Implementations) { if (impl->Worlds.Count() == 0) // if this component definition is not qualified with a world { EnumerableHashSet deducedWorlds = allWorlds; EnumerableHashSet feasibleWorlds; // the auto-deduced world for this definition is all feasible worlds in autoWorld. for (auto & w : deducedWorlds) { ShaderComponentSymbol* unaccessibleComp = nullptr; bool isWFeasible = IsWorldFeasible(symTable, shader->Pipeline, impl.Ptr(), w, unaccessibleComp); if (isWFeasible) feasibleWorlds.Add(w); } impl->Worlds = feasibleWorlds; } for (auto & w : impl->Worlds) comp->Type->FeasibleWorlds.Add(w); } } for (auto & comp : depOrder) { comp->Type->ConstrainedWorlds = comp->Type->FeasibleWorlds; } auto useInWorld = [&](String comp, String world) { // comp is used in world, restrict comp.ContainedWorlds to guarantee // all candidate definitions can reach world RefPtr compSym; if (shader->Components.TryGetValue(comp, compSym)) { EnumerableHashSet newWorlds; for (auto & w : compSym->Type->ConstrainedWorlds) if (symTable->IsWorldReachable(shader->Pipeline, w, world, compSym->Type->DataType)) newWorlds.Add(w); compSym->Type->ConstrainedWorlds = _Move(newWorlds); } }; for (auto impOp : shader->Pipeline->SyntaxNode->GetImportOperators()) { for (auto comp : impOp->Usings) { useInWorld(comp, impOp->DestWorld.Content); } } } bool CheckCircularReference(DiagnosticSink * err, ShaderClosure * shader) { bool rs = false; for (auto & comp : shader->AllComponents) { for (auto & impl : comp.Value.Symbol->Implementations) { // check circular references HashSet set; List referredComponents; referredComponents.Add(comp.Value.Symbol); for (int i = 0; i < referredComponents.Count(); i++) { auto xcomp = referredComponents[i]; for (auto & xcompImpl : xcomp->Implementations) { for (auto & rcomp : xcompImpl->DependentComponents) { if (set.Add(rcomp.Key)) { referredComponents.Add(rcomp.Key); } if (rcomp.Key == comp.Value.Symbol) { err->diagnose(impl->SyntaxNode->Position, Diagnostics::circularReferenceNotAllowed, rcomp.Key->Name); rs = true; } } } } } } return rs; } void PropagateArgumentConstraints(ShaderComponentSymbol * requirement, ShaderComponentSymbol * arg) { for (auto w : requirement->Implementations.First()->ExportWorlds) { for (auto impl : arg->Implementations) { if (impl->Worlds.Contains(w)) impl->ExportWorlds.Add(w); } } for (auto w : requirement->Implementations.First()->SrcPinnedWorlds) { for (auto impl : arg->Implementations) { if (impl->Worlds.Contains(w)) impl->SrcPinnedWorlds.Add(w); } } } void VerifyAndPropagateArgumentConstraints(DiagnosticSink * err, SymbolTable * symTable, ShaderClosure * shader) { for (auto & map : shader->RefMap) { auto & arg = map.Value; RefPtr requirement; if (shader->Components.TryGetValue(map.Key, requirement) && requirement->IsRequire()) { if (requirement->Implementations.First()->SyntaxNode->Rate) { for (auto w : requirement->Implementations.First()->Worlds) { if (!symTable->IsWorldImplicitlyReachable(shader->Pipeline, arg->Type->FeasibleWorlds, w, requirement->Type->DataType)) { err->diagnose(arg->Implementations.First()->SyntaxNode->Position, Diagnostics::argumentNotAvilableInWorld, arg->Name, w, shader->Name); err->diagnose(requirement->Implementations.First()->SyntaxNode->Position, Diagnostics::seeRequirementDeclaration); } } PropagateArgumentConstraints(requirement.Ptr(), arg.Ptr()); } } } for (auto & subClosure : shader->SubClosures) VerifyAndPropagateArgumentConstraints(err, symTable, subClosure.Value.Ptr()); } void AddPipelineComponents(ShaderClosure * shader) { for (auto & comp : shader->Pipeline->Components) { if (!comp.Value->IsRequire()) shader->Components.AddIfNotExists(comp.Key, new ShaderComponentSymbol(*comp.Value)); } } void GatherArgumentMappings(EnumerableDictionary & result, ShaderClosure* shader) { for (auto & map : shader->RefMap) { result[shader->Components[map.Key]()->UniqueName] = map.Value->UniqueName; } for (auto & subShader : shader->SubClosures) GatherArgumentMappings(result, subShader.Value.Ptr()); } void RemoveTrivialComponents(ShaderClosure * shader) { // remove trivial components, e.g. if A = B, replace all references to A with B. // this is not just an optimization, it is also critical for CodeGen because // code gen does not support components that returns another function component or sampler2D etc. // i.e. function/sampler2D components must be referenced directly. EnumerableDictionary compSub; for (auto & comp : shader->AllComponents) { // if this component is required by pipeline (e.g. gl_Position), do not attempt to remove it if (shader->Pipeline->Components.ContainsKey(comp.Key)) continue; if (comp.Value.Symbol->Implementations.Count() == 1 && comp.Value.Symbol->Implementations.First()->SyntaxNode->Expression && !comp.Value.Symbol->Implementations.First()->SyntaxNode->IsOutput()) { RefPtr compRef; if (comp.Value.Symbol->Implementations.First()->SyntaxNode->Expression->Tags.TryGetValue("ComponentReference", compRef)) { compSub[comp.Key] = compRef.As()->Content; } } } // gather argument mappings EnumerableDictionary arguments; GatherArgumentMappings(arguments, shader); EnumerableDictionary replacements; for (auto & replace : compSub) { // search transitively for replaceDest; String replaceDest = replace.Key; while (compSub.ContainsKey(replaceDest)) { replaceDest = compSub[replaceDest](); arguments.TryGetValue(replaceDest, replaceDest); } if (replace.Key != replaceDest) replacements[replace.Key] = replaceDest; } ReplaceReference(shader, replacements); for (auto & r : replacements) shader->AllComponents.Remove(r.Key); } void PropagatePipelineRequirements(DiagnosticSink * err, ShaderClosure * shader) { for (auto & req : shader->Pipeline->Components) { if (req.Value->IsRequire()) { ComponentInstance comp; StringBuilder errMsg; if (shader->AllComponents.TryGetValue(req.Key, comp)) { if (!comp.Symbol->Type->DataType->Equals(req.Value->Type->DataType.Ptr())) { err->diagnose(comp.Symbol->Implementations.First()->SyntaxNode, Diagnostics::componentTypeNotWhatPipelineRequires, req.Key, comp.Symbol->Type->DataType, shader->Pipeline->SyntaxNode->Name.Content, req.Value->Type->DataType); err->diagnose(req.Value->Implementations.First()->SyntaxNode, Diagnostics::seePipelineRequirementDefinition); } } else { err->diagnose(shader->Position, Diagnostics::shaderDoesNotDefineComponentAsRequiredByPipeline, shader->Name, req.Key, shader->Pipeline->SyntaxNode->Name); err->diagnose(req.Value->Implementations.First()->SyntaxNode, Diagnostics::seePipelineRequirementDefinition); } } } } void diagnoseModuleUsingStack(DiagnosticSink* sink, ShaderClosure * shader) { if (shader->Parent) { sink->diagnose(shader, Diagnostics::seeModuleBeingUsedIn, shader->Name, shader->Parent->Name); diagnoseModuleUsingStack(sink, shader->Parent); } else { sink->diagnose(shader, Diagnostics::noteShaderIsTargetingPipeine, shader->Name, shader->Pipeline->SyntaxNode->Name); sink->diagnose(shader->Pipeline->SyntaxNode, Diagnostics::alsoSeePipelineDefinition); } } void CheckPipelineShaderConsistency(DiagnosticSink * err, ShaderClosure * shader) { for (auto & comp : shader->Components) { for (auto & impl : comp.Value->Implementations) { bool inAbstractWorld = false; if (impl->SyntaxNode->Rate) { auto & userSpecifiedWorlds = impl->SyntaxNode->Rate->Worlds; for (auto & world : userSpecifiedWorlds) { { if (!shader->Pipeline->WorldDependency.ContainsKey(world.World.Content)) { err->diagnose(world.World.Position, Diagnostics::worldIsNotDefinedInPipeline, world.World, shader->Pipeline->SyntaxNode->Name); diagnoseModuleUsingStack(err, shader); } } WorldSyntaxNode* worldDecl; if (shader->Pipeline->Worlds.TryGetValue(world.World.Content, worldDecl)) { if (worldDecl->IsAbstract()) { inAbstractWorld = true; if (userSpecifiedWorlds.Count() > 1) { err->diagnose(world.World.Position, Diagnostics::abstractWorldCannotAppearWithOthers); diagnoseModuleUsingStack(err, shader); } } } } } if (!inAbstractWorld && !impl->SyntaxNode->IsRequire() && !impl->SyntaxNode->IsInput() && !impl->SyntaxNode->IsParam() && !impl->SyntaxNode->Expression && !impl->SyntaxNode->BlockStatement) { err->diagnose(impl->SyntaxNode->Position, Diagnostics::nonAbstractComponentMustHaveImplementation); } bool isDefinedInAbstractWorld = false, isDefinedInNonAbstractWorld = false; if (impl->SyntaxNode->Rate) { for (auto & w : impl->SyntaxNode->Rate->Worlds) { auto world = shader->Pipeline->Worlds.TryGetValue(w.World.Content); if (world) { if ((*world)->IsAbstract()) isDefinedInAbstractWorld = true; else isDefinedInNonAbstractWorld = true; } } } else isDefinedInNonAbstractWorld = true; if (impl->SyntaxNode->Expression || impl->SyntaxNode->BlockStatement) { if (isDefinedInAbstractWorld) err->diagnose(impl->SyntaxNode->Position, Diagnostics::componentInInputWorldCantHaveCode, impl->SyntaxNode->Name); } } } for (auto & subShader : shader->SubClosures) CheckPipelineShaderConsistency(err, subShader.Value.Ptr()); } void FlattenShaderClosure(DiagnosticSink * err, SymbolTable * symTable, ShaderClosure * shader) { // add input(extern) components from pipeline AddPipelineComponents(shader); CheckPipelineShaderConsistency(err, shader); // assign choice names AssignUniqueNames(shader, "", ""); // traverse closures to get component list GatherComponents(err, shader, shader); PropagatePipelineRequirements(err, shader); ResolveReference(err, shader, shader); // propagate world constraints if (CheckCircularReference(err, shader)) return; if (err->GetErrorCount()) return; RemoveTrivialComponents(shader); SolveWorldConstraints(err, symTable, shader); // check pipeline constraints for (auto & requirement : shader->Pipeline->Components) { if (!requirement.Value->IsRequire()) continue; auto comp = shader->FindComponent(requirement.Key); if (!comp) { err->diagnose(shader->Position, Diagnostics::shaderDoesProvideRequirement, shader->Name, requirement.Key, shader->Pipeline->SyntaxNode->Name.Content); err->diagnose(requirement.Value->Implementations.First()->SyntaxNode, Diagnostics::seeRequirementDeclaration); } else { for (auto & impl : requirement.Value->Implementations) { for (auto w : impl->Worlds) { if (!symTable->IsWorldImplicitlyReachable(shader->Pipeline, comp->Type->FeasibleWorlds, w, requirement.Value->Type->DataType)) { err->diagnose(comp->Implementations.First()->SyntaxNode, Diagnostics::componentNotAvilableInWorld, comp->Name, w, shader->Pipeline->SyntaxNode->Name); err->diagnose(requirement.Value->Implementations.First()->SyntaxNode, Diagnostics::seeRequirementDeclaration); } } } PropagateArgumentConstraints(requirement.Value.Ptr(), comp.Ptr()); } } // check argument constraints VerifyAndPropagateArgumentConstraints(err, symTable, shader); } } } ================================================ FILE: Source/SpireCore/Closure.h ================================================ #ifndef BAKERSL_SHADER_CLOSURE_H #define BAKERSL_SHADER_CLOSURE_H #include "SymbolTable.h" namespace Spire { namespace Compiler { RefPtr CreateShaderClosure(DiagnosticSink * sink, SymbolTable * symTable, ShaderSymbol * shader); void FlattenShaderClosure(DiagnosticSink * sink, SymbolTable * symTable, ShaderClosure * shader); void InsertImplicitImportOperators(DiagnosticSink * sink, ShaderIR * shader); } } #endif ================================================ FILE: Source/SpireCore/CodeGenBackend.h ================================================ #ifndef CODE_GEN_BACKEND_H #define CODE_GEN_BACKEND_H #include "../CoreLib/Basic.h" #include "CompiledProgram.h" #include "SymbolTable.h" #include "TypeLayout.h" namespace Spire { namespace Compiler { class CodeGenBackend : public CoreLib::Basic::Object { public: virtual CompiledShaderSource GenerateShader(CompileResult & result, SymbolTable * symbols, ILShader * shader, DiagnosticSink * err) = 0; virtual LayoutRule GetDefaultLayoutRule() = 0; }; CodeGenBackend * CreateGLSLCodeGen(); CodeGenBackend * CreateGLSL_VulkanCodeGen(); CodeGenBackend * CreateGLSL_VulkanOneDescCodeGen(); CodeGenBackend * CreateHLSLCodeGen(); CodeGenBackend * CreateSpirVCodeGen(); } } #endif ================================================ FILE: Source/SpireCore/CodeGenerator.cpp ================================================ #include "SyntaxVisitors.h" #include "ScopeDictionary.h" #include "CodeWriter.h" #include "StringObject.h" #include "Naming.h" #include "TypeLayout.h" #include "../CoreLib/Tokenizer.h" #include namespace Spire { namespace Compiler { const int MaxBindingValue = 128; template class ImportNodeVisitor : public SyntaxVisitor { public: const Func * func; ImportNodeVisitor(const Func & f) : SyntaxVisitor(nullptr), func(&f) {} virtual RefPtr VisitImportExpression(ImportExpressionSyntaxNode * expr) override { (*func)(expr); return expr; } }; template void EnumerateImportExpressions(SyntaxNode * node, const Func & f) { ImportNodeVisitor visitor(f); node->Accept(&visitor); } class CodeGenerator : public ICodeGenerator { private: SymbolTable * symTable; ILWorld * currentWorld = nullptr; ComponentDefinitionIR * currentComponent = nullptr; FunctionSymbol * currentFunc = nullptr; ILOperand * returnRegister = nullptr; ImportExpressionSyntaxNode * currentImport = nullptr; ShaderIR * currentShader = nullptr; RefPtr compiledShader; CompileResult & result; List exprStack; CodeWriter codeWriter; ScopeDictionary variables; Dictionary> genericTypeMappings; Dictionary> structTypes; LayoutRule defaultLayoutRule; void PushStack(ILOperand * op) { exprStack.Add(op); } ILOperand * PopStack() { auto rs = exprStack.Last(); exprStack.SetSize(exprStack.Count() - 1); return rs; } AllocVarInstruction * AllocVar(ExpressionType * etype) { AllocVarInstruction * varOp = 0; RefPtr type = TranslateExpressionType(etype); auto arrType = dynamic_cast(type.Ptr()); if (arrType) { varOp = codeWriter.AllocVar(arrType->BaseType, result.Program->ConstantPool->CreateConstant(arrType->ArrayLength)); } else { assert(type); varOp = codeWriter.AllocVar(type, result.Program->ConstantPool->CreateConstant(0)); } return varOp; } FetchArgInstruction * FetchArg(ExpressionType * etype, int argId) { auto type = TranslateExpressionType(etype); auto arrType = dynamic_cast(type.Ptr()); FetchArgInstruction * varOp = 0; if (arrType) { auto baseType = arrType->BaseType.Release(); varOp = codeWriter.FetchArg(baseType, argId); } else { varOp = codeWriter.FetchArg(type, argId); } return varOp; } void TranslateStages(PipelineSyntaxNode * pipeline) { for (auto & stage : pipeline->GetStages()) { RefPtr ilStage = new ILStage(); ilStage->Position = stage->Position; ilStage->Name = stage->Name.Content; ilStage->StageType = stage->StageType.Content; for (auto & attrib : stage->Attributes) { StageAttribute sattrib; sattrib.Name = attrib.Key; sattrib.Position = attrib.Value.Position; sattrib.Value = attrib.Value.Content; ilStage->Attributes[attrib.Key] = sattrib; } compiledShader->Stages[stage->Name.Content] = ilStage; } } String GetComponentFunctionName(ComponentSyntaxNode * comp) { StringBuilder nameSb; nameSb << comp->ParentDecl->Name.Content << "." << comp->Name.Content; return EscapeCodeName(nameSb.ProduceString()); } public: virtual RefPtr VisitStruct(StructSyntaxNode * st) override { RefPtr structType = TranslateStructType(st); result.Program->Structs.Add(structType); return st; } virtual void ProcessFunction(FunctionSyntaxNode * func) override { VisitFunction(func); } virtual void ProcessStruct(StructSyntaxNode * st) override { VisitStruct(st); } void SetSubModuleDescriptorSetId(ILModuleParameterSet * moduleParam, int id) { moduleParam->DescriptorSetId = id; for (auto & submodule : moduleParam->SubModules) SetSubModuleDescriptorSetId(submodule.Ptr(), id); } LayoutRule GetDefaultLayoutRule() { return defaultLayoutRule; } void GenerateParameterBindingInfo(ShaderIR * shader) { Dictionary usedDescriptorSetBindings; // initialize module parameter layouts for all module instances in this shader for (auto module : shader->ModuleInstances) { if (module->BindingIndex != -1 && module->IsTopLevel) { ModuleInstanceIR * existingModule; if (usedDescriptorSetBindings.TryGetValue(module->BindingIndex, existingModule)) { getSink()->diagnose(module->UsingPosition, Diagnostics::bindingAlreadyOccupiedByModule, module->BindingIndex, existingModule->BindingName); getSink()->diagnose(existingModule->UsingPosition, Diagnostics::seeUsingOf, existingModule->SyntaxNode->Name.Content); } usedDescriptorSetBindings[module->BindingIndex] = module.Ptr(); } } // report error if shader uses a top-level module without specifying its binding for (auto & module : shader->ModuleInstances) { if (module->BindingIndex == -1 && module->IsTopLevel) { bool hasParam = false; for (auto & comp : module->SyntaxNode->GetMembersOfType()) if (comp->IsParam()) { hasParam = true; break; } if (hasParam) getSink()->diagnose(module->UsingPosition, Diagnostics::topLevelModuleUsedWithoutSpecifyingBinding, module->SyntaxNode->Name); } } shader->ModuleInstances.Sort([](RefPtr & x, RefPtr & y) {return x->BindingIndex < y->BindingIndex; }); for (auto module : shader->ModuleInstances) { auto set = new ILModuleParameterSet(); set->BindingName = module->BindingName; set->DescriptorSetId = module->BindingIndex; set->UniformBufferLegacyBindingPoint = set->DescriptorSetId; set->IsTopLevel = module->IsTopLevel; compiledShader->ModuleParamSets[module->BindingName] = set; } for (auto module : shader->ModuleInstances) { auto ilModule = compiledShader->ModuleParamSets[module->BindingName](); for (auto subModule : module->SubModuleInstances) ilModule->SubModules.Add(compiledShader->ModuleParamSets[subModule->BindingName]()); } for (auto module : compiledShader->ModuleParamSets) { if (module.Value->IsTopLevel && module.Value->DescriptorSetId != -1 && module.Key != compiledShader->Name) for (auto submodule : module.Value->SubModules) SetSubModuleDescriptorSetId(submodule.Ptr(), module.Value->DescriptorSetId); } // allocate binding slots for shader resources (textures, buffers, samplers etc.), as required by legacy APIs Dictionary usedTextureBindings, usedBufferBindings, usedSamplerBindings, usedStorageBufferBindings; int textureBindingAllcator = 0, samplerBindingAllocator = 0, storageBufferBindingAllocator = 0, bufferBindingAllocator = 0; // first pass: add components to module layout definition, and assign them user-defined binding slots (if any). for (auto def : shader->Definitions) { if (def->SyntaxNode->IsParam()) { auto module = compiledShader->ModuleParamSets[def->ModuleInstance->BindingName]().Ptr(); RefPtr param = new ILModuleParameterInstance(); param->Module = module; param->Name = def->OriginalName; param->Type = TranslateExpressionType(def->SyntaxNode->Type); auto resType = param->Type->GetBindableResourceType(); // if this parameter is ordinary-value typed, assign it a buffer range if (resType == BindableResourceType::NonBindable) { param->BindingPoints.Clear(); param->BufferOffset = (int)RoundToAlignment(module->BufferSize, (int)GetTypeAlignment(param->Type.Ptr(), defaultLayoutRule)); module->BufferSize = param->BufferOffset + (int)GetTypeSize(param->Type.Ptr(), defaultLayoutRule); } else { // otherwise, check for binding slot collision if the user assigns a binding slot explicitly param->BufferOffset = -1; Dictionary * bindingRegistry = nullptr; switch (resType) { case BindableResourceType::Texture: bindingRegistry = &usedTextureBindings; break; case BindableResourceType::Sampler: bindingRegistry = &usedSamplerBindings; break; case BindableResourceType::Buffer: bindingRegistry = &usedBufferBindings; break; case BindableResourceType::StorageBuffer: bindingRegistry = &usedStorageBufferBindings; break; } Token bindingValStr; if (def->SyntaxNode->FindSimpleAttribute("Binding", bindingValStr)) { int bindingVal = StringToInt(bindingValStr.Content); ComponentDefinitionIR * otherComp = nullptr; if (bindingRegistry->TryGetValue(bindingVal, otherComp)) { getSink()->diagnose(def->SyntaxNode->Position, Diagnostics::bindingAlreadyOccupiedByComponent, bindingVal, otherComp->OriginalName); getSink()->diagnose(otherComp->SyntaxNode->Position, Diagnostics::seeDefinitionOf, otherComp->OriginalName); } if (bindingVal < 0 || bindingVal >= MaxBindingValue) { getSink()->diagnose(def->SyntaxNode->Position, Diagnostics::invalidBindingValue, bindingVal); } (*bindingRegistry)[bindingVal] = def.Ptr(); param->BindingPoints.Clear(); param->BindingPoints.Add(bindingVal); } } module->Parameters.Add(def->OriginalName, param); } } // second pass: assign binding slots for rest of resource components whose binding is not explicitly specified by user for (auto def : shader->Definitions) { if (def->SyntaxNode->IsParam()) { auto module = compiledShader->ModuleParamSets[def->ModuleInstance->BindingName]().Ptr(); auto & param = **module->Parameters.TryGetValue(def->OriginalName); auto bindableResType = param.Type->GetBindableResourceType(); if (bindableResType != BindableResourceType::NonBindable) { Dictionary * bindingRegistry = nullptr; if (param.BindingPoints.Count()) continue; int * bindingAllocator = nullptr; switch (bindableResType) { case BindableResourceType::Texture: bindingRegistry = &usedTextureBindings; bindingAllocator = &textureBindingAllcator; break; case BindableResourceType::Sampler: bindingRegistry = &usedSamplerBindings; bindingAllocator = &samplerBindingAllocator; break; case BindableResourceType::Buffer: bindingRegistry = &usedBufferBindings; bindingAllocator = &bufferBindingAllocator; break; case BindableResourceType::StorageBuffer: bindingRegistry = &usedStorageBufferBindings; bindingAllocator = &storageBufferBindingAllocator; break; } while (bindingRegistry->ContainsKey(*bindingAllocator)) { (*bindingAllocator)++; } param.BindingPoints.Add(*bindingAllocator); int maxBinding = GetMaxResourceBindings(bindableResType); if (*bindingAllocator > maxBinding) { getSink()->diagnose(def->SyntaxNode->Position, Diagnostics::bindingExceedsLimit, *bindingAllocator, def->SyntaxNode->Name); getSink()->diagnose(def->SyntaxNode->Position, Diagnostics::seeModuleBeingUsedIn, def->ModuleInstance->SyntaxNode->Name, def->ModuleInstance->BindingName); } (*bindingAllocator)++; } } } } ParameterQualifier GetParamQualifier(ParameterSyntaxNode* paramDecl) { if ((paramDecl->modifiers.flags & ModifierFlag::InOut) == ModifierFlag::InOut) return ParameterQualifier::InOut; else if (paramDecl->modifiers.flags & ModifierFlag::Out) return ParameterQualifier::Out; else return ParameterQualifier::In; } EnumerableDictionary CopyLayoutAttributes(Decl* decl) { EnumerableDictionary attrs; for (auto attr : decl->GetLayoutAttributes()) { attrs[attr->Key] = attr->Value; } return attrs; } virtual void ProcessShader(ShaderIR * shader) override { currentShader = shader; auto pipeline = shader->Shader->Pipeline; compiledShader = new ILShader(); compiledShader->Name = EscapeCodeName(shader->Shader->Name); compiledShader->Position = shader->Shader->Position; GenerateParameterBindingInfo(shader); TranslateStages(pipeline->SyntaxNode); result.Program->Shaders.Add(compiledShader); genericTypeMappings.Clear(); // pass 1: iterating all worlds // create ILWorld and ILRecordType objects for all worlds for (auto & world : pipeline->Worlds) { auto w = new ILWorld(); auto recordType = new ILRecordType(); recordType->TypeName = world.Key; genericTypeMappings[world.Key] = recordType; w->Name = world.Key; w->OutputType = recordType; w->Attributes = CopyLayoutAttributes(world.Value); w->Shader = compiledShader.Ptr(); w->IsAbstract = world.Value->IsAbstract(); auto impOps = pipeline->GetImportOperatorsFromSourceWorld(world.Key); w->Position = world.Value->Position; compiledShader->Worlds[world.Key] = w; } // pass 2: iterating all worlds: // 1) Gather list of components for each world, and store it in worldComps dictionary. // 2) For each abstract world, add its components to record type Dictionary> worldComps; auto worlds = From(pipeline->Worlds).Select([](KeyValuePair kv) {return kv.Key; }).Concat(FromSingle(String(""))); //worlds.Add(""); for (auto world : worlds) { // gather list of components List components; for (auto & compDef : shader->Definitions) if (compDef->World == world) components.Add(compDef.Ptr()); // for abstract world, fill in record type now if (world != "" && pipeline->Worlds[world]()->IsAbstract()) { auto compiledWorld = compiledShader->Worlds[world](); for (auto & comp : components) { ILObjectDefinition compDef; compDef.Attributes = CopyLayoutAttributes(comp->SyntaxNode.Ptr()); compDef.Name = comp->UniqueName; compDef.Type = TranslateExpressionType(comp->Type.Ptr()); compDef.Position = comp->SyntaxNode->Position; compDef.Binding = -1; compiledWorld->OutputType->Members.AddIfNotExists(compDef.Name, compDef); } } // put the list in worldComps worldComps[world] = components; } // now we need to deal with import operators // create world input declarations base on input components for (auto & world : compiledShader->Worlds) { auto &components = worldComps[world.Key](); for (auto & comp : components) { if (comp->SyntaxNode->IsInput()) { ILObjectDefinition def; def.Name = comp->UniqueName; def.Type = TranslateExpressionType(comp->Type.Ptr()); def.Position = comp->SyntaxNode->Position; def.Attributes = CopyLayoutAttributes(comp->SyntaxNode.Ptr()); world.Value->Inputs.Add(def); } } } // fill in record types for (auto & comps : worldComps) { for (auto & comp : comps.Value) { // for each import operator call "import[w0->w1](x)", add x to w0's record type EnumerateImportExpressions(comp->SyntaxNode.Ptr(), [&](ImportExpressionSyntaxNode * importExpr) { auto recType = genericTypeMappings[importExpr->ImportOperatorDef->SourceWorld.Content]().As(); ILObjectDefinition entryDef; entryDef.Attributes = CopyLayoutAttributes(comp->SyntaxNode.Ptr()); entryDef.Name = importExpr->ComponentUniqueName; entryDef.Type = TranslateExpressionType(importExpr->Type.Ptr()); entryDef.Position = importExpr->Position; recType->Members.AddIfNotExists(importExpr->ComponentUniqueName, entryDef); }); // if comp is output, add comp to its world's record type if (comp->SyntaxNode->IsOutput()) { auto recType = genericTypeMappings[comp->World]().As(); ILObjectDefinition entryDef; entryDef.Attributes = CopyLayoutAttributes(comp->SyntaxNode.Ptr()); entryDef.Name = comp->UniqueName; entryDef.Type = TranslateExpressionType(comp->Type.Ptr()); entryDef.Position = comp->SyntaxNode->Position; recType->Members.AddIfNotExists(comp->UniqueName, entryDef); } } } // sort components by dependency for (auto & world : compiledShader->Worlds) { auto &components = worldComps[world.Key](); DependencySort(components, [](ComponentDefinitionIR * def) { return def->Dependency; }); } // generate component functions for (auto & comp : shader->Definitions) { currentComponent = comp.Ptr(); if (comp->SyntaxNode->IsComponentFunction()) { auto funcName = GetComponentFunctionName(comp->SyntaxNode.Ptr()); // BUG FIX: The following line must be commented out. /* we cannot really cached IL for previously generated component functions because the actual component function is dependent on component composition. example: module A, module B, module C. module A has requires void f(int) that can be provided by either B::f or C::f. because B::f and C::f may capture different environment, their actual parameter list can be different, therefore if module A has a method g that calls f, the implementation of g will differ based on wheither it is calling B::f or C::f. simply caching an IL for A::g using function name as key is therefore not sufficient. the real bug fix should factor this into the function name, but for now just disable caching. */ /*if (result.Program->Functions.ContainsKey(funcName)) continue;*/ RefPtr func = new ILFunction(); RefPtr funcSym = new FunctionSymbol(); func->Name = funcName; func->ReturnType = TranslateExpressionType(comp->Type); symTable->Functions[funcName] = funcSym; result.Program->Functions[funcName] = func; currentFunc = funcSym.Ptr(); for (auto dep : comp->GetComponentFunctionDependencyClosure()) { if (dep->SyntaxNode->IsComponentFunction()) { funcSym->ReferencedFunctions.Add(GetComponentFunctionName(dep->SyntaxNode.Ptr())); } } int id = 0; Dictionary refComponents; variables.PushScope(); codeWriter.PushNode(); for (auto & dep : comp->GetComponentFunctionDependencyClosure()) { if (!dep->SyntaxNode->IsComponentFunction()) { auto paramType = TranslateExpressionType(dep->Type); String paramName = EscapeCodeName("p" + String(id) + "_" + dep->OriginalName); func->Parameters.Add(paramName, ILParameter(paramType)); auto argInstr = codeWriter.FetchArg(paramType, id + 1); argInstr->Name = paramName; variables.Add(dep->UniqueName, argInstr); id++; } } for (auto & param : comp->SyntaxNode->GetParameters()) { auto paramType = TranslateExpressionType(param->Type); String paramName = EscapeCodeName("p" + String(id) + "_" + param->Name.Content); func->Parameters.Add(paramName, ILParameter(paramType, GetParamQualifier(param.Ptr()))); auto argInstr = codeWriter.FetchArg(paramType, id + 1); argInstr->Name = paramName; variables.Add(param->Name.Content, argInstr); id++; } if (comp->SyntaxNode->Expression) { comp->SyntaxNode->Expression->Accept(this); codeWriter.Insert(new ReturnInstruction(PopStack())); } else { comp->SyntaxNode->BlockStatement->Accept(this); } currentFunc = nullptr; variables.PopScope(); func->Code = codeWriter.PopNode(); } currentComponent = nullptr; } variables.PushScope(); // push parameter components to variables table if (auto paramComps = worldComps.TryGetValue("")) { for (auto & comp : *paramComps) { variables.Add(comp->UniqueName, compiledShader->ModuleParamSets[comp->ModuleInstance->BindingName]()->Parameters[comp->OriginalName]().Ptr()); } } for (auto & world : pipeline->Worlds) { if (world.Value->IsAbstract()) continue; NamingCounter = 0; auto & components = worldComps[world.Key].GetValue(); auto compiledWorld = compiledShader->Worlds[world.Key].GetValue().Ptr(); currentWorld = compiledWorld; codeWriter.PushNode(); variables.PushScope(); HashSet localComponents; for (auto & comp : components) localComponents.Add(comp->UniqueName); DependencySort(components, [](ComponentDefinitionIR * def) { return def->Dependency; }); for (auto & comp : components) { if (!comp->SyntaxNode->IsComponentFunction()) VisitComponent(comp); } variables.PopScope(); compiledWorld->Code = codeWriter.PopNode(); EvalReferencedFunctionClosure(compiledWorld); currentWorld = nullptr; } variables.PopScope(); currentShader = nullptr; } void EvalReferencedFunctionClosure(ILWorld * world) { List workList; for (auto & rfunc : world->ReferencedFunctions) workList.Add(rfunc); for (int i = 0; i < workList.Count(); i++) { auto rfunc = workList[i]; RefPtr funcSym; if (symTable->Functions.TryGetValue(rfunc, funcSym)) { for (auto & rrfunc : funcSym->ReferencedFunctions) { world->ReferencedFunctions.Add(rrfunc); workList.Add(rrfunc); } } } } virtual RefPtr VisitComponent(ComponentSyntaxNode *) override { throw NotImplementedException(); } void VisitComponent(ComponentDefinitionIR * comp) { currentComponent = comp; String varName = EscapeCodeName(currentComponent->OriginalName); RefPtr type = TranslateExpressionType(currentComponent->Type); if (comp->SyntaxNode->IsInput()) { auto loadInput = new LoadInputInstruction(type.Ptr(), comp->UniqueName); codeWriter.Insert(loadInput); variables.Add(currentComponent->UniqueName, loadInput); return; } else if (comp->SyntaxNode->IsParam()) { auto moduleInst = compiledShader->ModuleParamSets[comp->ModuleInstance->BindingName](); auto param = moduleInst->Parameters[comp->UniqueName]().Ptr(); variables.Add(currentComponent->UniqueName, param); return; } ILOperand * componentVar = nullptr; if (currentComponent->SyntaxNode->Expression) { currentComponent->SyntaxNode->Expression->Accept(this); componentVar = exprStack.Last(); exprStack.Clear(); } else if (currentComponent->SyntaxNode->BlockStatement) { returnRegister = nullptr; currentComponent->SyntaxNode->BlockStatement->Accept(this); componentVar = returnRegister; } if (currentWorld->OutputType->Members.ContainsKey(currentComponent->UniqueName)) { auto exp = new ExportInstruction(currentComponent->UniqueName, currentWorld, componentVar); codeWriter.Insert(exp); } /*if (!currentComponent->Type->IsTexture() && !currentComponent->Type->IsArray()) { auto vartype = TranslateExpressionType(currentComponent->Type.Ptr(), &recordTypes); auto var = codeWriter.AllocVar(vartype, result.Program->ConstantPool->CreateConstant(1)); var->Name = varName; codeWriter.Store(var, componentVar); componentVar = var; } else*/ componentVar->Name = varName; currentWorld->Components[currentComponent->UniqueName] = componentVar; variables.Add(currentComponent->UniqueName, componentVar); currentComponent = nullptr; } virtual RefPtr VisitFunction(FunctionSyntaxNode* function) override { if (function->IsExtern()) return function; RefPtr func = new ILFunction(); result.Program->Functions.Add(function->InternalName, func); func->Name = function->InternalName; func->ReturnType = TranslateExpressionType(function->ReturnType); variables.PushScope(); codeWriter.PushNode(); int id = 0; for (auto ¶m : function->GetParameters()) { func->Parameters.Add(param->Name.Content, ILParameter(TranslateExpressionType(param->Type), GetParamQualifier(param.Ptr()))); auto op = FetchArg(param->Type.Ptr(), ++id); op->Name = EscapeCodeName(String("p_") + param->Name.Content); variables.Add(param->Name.Content, op); } function->Body->Accept(this); func->Code = codeWriter.PopNode(); variables.PopScope(); return function; } virtual RefPtr VisitBlockStatement(BlockStatementSyntaxNode* stmt) override { variables.PushScope(); for (auto & subStmt : stmt->Statements) subStmt->Accept(this); variables.PopScope(); return stmt; } virtual RefPtr VisitWhileStatement(WhileStatementSyntaxNode* stmt) override { RefPtr instr = new WhileInstruction(); variables.PushScope(); codeWriter.PushNode(); stmt->Predicate->Accept(this); codeWriter.Insert(new ReturnInstruction(PopStack())); instr->ConditionCode = codeWriter.PopNode(); codeWriter.PushNode(); stmt->Statement->Accept(this); instr->BodyCode = codeWriter.PopNode(); codeWriter.Insert(instr.Release()); variables.PopScope(); return stmt; } virtual RefPtr VisitDoWhileStatement(DoWhileStatementSyntaxNode* stmt) override { RefPtr instr = new DoInstruction(); variables.PushScope(); codeWriter.PushNode(); stmt->Predicate->Accept(this); codeWriter.Insert(new ReturnInstruction(PopStack())); instr->ConditionCode = codeWriter.PopNode(); codeWriter.PushNode(); stmt->Statement->Accept(this); instr->BodyCode = codeWriter.PopNode(); codeWriter.Insert(instr.Release()); variables.PopScope(); return stmt; } virtual RefPtr VisitForStatement(ForStatementSyntaxNode* stmt) override { RefPtr instr = new ForInstruction(); variables.PushScope(); if (auto initStmt = stmt->InitialStatement.Ptr()) { // TODO(tfoley): any of this push-pop malarky needed here? initStmt->Accept(this); } if (stmt->PredicateExpression) { codeWriter.PushNode(); stmt->PredicateExpression->Accept(this); PopStack(); instr->ConditionCode = codeWriter.PopNode(); } if (stmt->SideEffectExpression) { codeWriter.PushNode(); stmt->SideEffectExpression->Accept(this); PopStack(); instr->SideEffectCode = codeWriter.PopNode(); } codeWriter.PushNode(); stmt->Statement->Accept(this); instr->BodyCode = codeWriter.PopNode(); codeWriter.Insert(instr.Release()); variables.PopScope(); return stmt; } virtual RefPtr VisitIfStatement(IfStatementSyntaxNode* stmt) override { RefPtr instr = new IfInstruction(); variables.PushScope(); stmt->Predicate->Accept(this); instr->Operand = PopStack(); codeWriter.PushNode(); stmt->PositiveStatement->Accept(this); instr->TrueCode = codeWriter.PopNode(); if (stmt->NegativeStatement) { codeWriter.PushNode(); stmt->NegativeStatement->Accept(this); instr->FalseCode = codeWriter.PopNode(); } codeWriter.Insert(instr.Release()); variables.PopScope(); return stmt; } virtual RefPtr VisitReturnStatement(ReturnStatementSyntaxNode* stmt) override { returnRegister = nullptr; if (currentWorld != nullptr && currentComponent != nullptr && !currentImport) { if (stmt->Expression) { stmt->Expression->Accept(this); returnRegister = PopStack(); if (!currentComponent->SyntaxNode->IsComponentFunction()) { if (currentWorld->OutputType->Members.ContainsKey(currentComponent->UniqueName)) { auto exp = new ExportInstruction(currentComponent->UniqueName, currentWorld, returnRegister); codeWriter.Insert(exp); } } else { codeWriter.Insert(new ReturnInstruction(returnRegister)); } } } else { if (stmt->Expression) { stmt->Expression->Accept(this); returnRegister = PopStack(); } codeWriter.Insert(new ReturnInstruction(returnRegister)); } return stmt; } virtual RefPtr VisitBreakStatement(BreakStatementSyntaxNode* stmt) override { codeWriter.Insert(new BreakInstruction()); return stmt; } virtual RefPtr VisitContinueStatement(ContinueStatementSyntaxNode* stmt) override { codeWriter.Insert(new ContinueInstruction()); return stmt; } virtual RefPtr VisitSelectExpression(SelectExpressionSyntaxNode * expr) override { expr->SelectorExpr->Accept(this); auto predOp = PopStack(); expr->Expr0->Accept(this); auto v0 = PopStack(); expr->Expr1->Accept(this); auto v1 = PopStack(); PushStack(codeWriter.Select(predOp, v0, v1)); return expr; } ILOperand * EnsureBoolType(ILOperand * op, RefPtr type) { if (!type->Equals(ExpressionType::Bool.Ptr())) { auto cmpeq = new CmpneqInstruction(); cmpeq->Operands[0] = op; cmpeq->Operands[1] = result.Program->ConstantPool->CreateConstant(0); cmpeq->Type = new ILBasicType(ILBaseType::Int); codeWriter.Insert(cmpeq); return cmpeq; } else return op; } virtual RefPtr VisitDiscardStatement(DiscardStatementSyntaxNode * stmt) override { codeWriter.Discard(); return stmt; } RefPtr VisitDeclrVariable(Variable* varDecl) { AllocVarInstruction * varOp = AllocVar(varDecl->Type.Ptr()); varOp->Name = EscapeCodeName(varDecl->Name.Content); variables.Add(varDecl->Name.Content, varOp); if (varDecl->Expr) { varDecl->Expr->Accept(this); Assign(varOp, PopStack()); } return varDecl; } virtual RefPtr VisitExpressionStatement(ExpressionStatementSyntaxNode* stmt) override { stmt->Expression->Accept(this); PopStack(); return stmt; } void Assign(ILOperand * left, ILOperand * right) { if (auto swizzle = dynamic_cast(left)) { auto baseOp = swizzle->Operand.Ptr(); int index = 0; for (int i = 0; i < swizzle->SwizzleString.Length(); i++) { switch (swizzle->SwizzleString[i]) { case 'r': case 'x': index = 0; break; case 'g': case 'y': index = 1; break; case 'b': case 'z': index = 2; break; case 'a': case 'w': index = 3; break; } codeWriter.Update(baseOp, result.Program->ConstantPool->CreateConstant(index), codeWriter.Retrieve(right, result.Program->ConstantPool->CreateConstant(i))); } swizzle->Erase(); } else codeWriter.Store(left, right); } virtual RefPtr VisitBinaryExpression(BinaryExpressionSyntaxNode* expr) override { expr->RightExpression->Accept(this); auto right = PopStack(); if (expr->Operator == Operator::Assign) { expr->LeftExpression->Access = ExpressionAccess::Write; expr->LeftExpression->Accept(this); auto left = PopStack(); Assign(left, right); PushStack(left); } else { expr->LeftExpression->Access = ExpressionAccess::Read; expr->LeftExpression->Accept(this); auto left = PopStack(); BinaryInstruction * rs = 0; switch (expr->Operator) { case Operator::Add: case Operator::AddAssign: rs = new AddInstruction(); break; case Operator::Sub: case Operator::SubAssign: rs = new SubInstruction(); break; case Operator::Mul: case Operator::MulAssign: rs = new MulInstruction(); break; case Operator::Mod: case Operator::ModAssign: rs = new ModInstruction(); break; case Operator::Div: case Operator::DivAssign: rs = new DivInstruction(); break; case Operator::And: rs = new AndInstruction(); break; case Operator::Or: rs = new OrInstruction(); break; case Operator::BitAnd: case Operator::AndAssign: rs = new BitAndInstruction(); break; case Operator::BitOr: case Operator::OrAssign: rs = new BitOrInstruction(); break; case Operator::BitXor: case Operator::XorAssign: rs = new BitXorInstruction(); break; case Operator::Lsh: case Operator::LshAssign: rs = new ShlInstruction(); break; case Operator::Rsh: case Operator::RshAssign: rs = new ShrInstruction(); break; case Operator::Eql: rs = new CmpeqlInstruction(); break; case Operator::Neq: rs = new CmpneqInstruction(); break; case Operator::Greater: rs = new CmpgtInstruction(); break; case Operator::Geq: rs = new CmpgeInstruction(); break; case Operator::Leq: rs = new CmpleInstruction(); break; case Operator::Less: rs = new CmpltInstruction(); break; default: throw NotImplementedException("Code gen not implemented for this operator."); } rs->Operands.SetSize(2); rs->Operands[0] = left; rs->Operands[1] = right; rs->Type = TranslateExpressionType(expr->Type); codeWriter.Insert(rs); switch (expr->Operator) { case Operator::AddAssign: case Operator::SubAssign: case Operator::MulAssign: case Operator::DivAssign: case Operator::ModAssign: case Operator::LshAssign: case Operator::RshAssign: case Operator::AndAssign: case Operator::OrAssign: case Operator::XorAssign: { expr->LeftExpression->Access = ExpressionAccess::Write; expr->LeftExpression->Accept(this); auto target = PopStack(); Assign(target, rs); break; } default: break; } PushStack(rs); } return expr; } virtual RefPtr VisitProject(ProjectExpressionSyntaxNode * project) override { project->BaseExpression->Accept(this); auto rs = PopStack(); auto proj = new ProjectInstruction(); proj->ComponentName = currentImport->ComponentUniqueName; proj->Operand = rs; codeWriter.Insert(proj); PushStack(proj); return project; } virtual RefPtr VisitConstantExpression(ConstantExpressionSyntaxNode* expr) override { ILConstOperand * op; if (expr->ConstType == ConstantExpressionSyntaxNode::ConstantType::Float) { op = result.Program->ConstantPool->CreateConstant(expr->FloatValue); } else if (expr->ConstType == ConstantExpressionSyntaxNode::ConstantType::Bool) { op = result.Program->ConstantPool->CreateConstant(expr->IntValue != 0); } else { if (expr->Type->Equals(ExpressionType::UInt)) op = result.Program->ConstantPool->CreateConstantU((unsigned int)(expr->IntValue)); else op = result.Program->ConstantPool->CreateConstant(expr->IntValue); } PushStack(op); return expr; } void GenerateIndexExpression(ILOperand * base, ILOperand * idx) { auto ldInstr = codeWriter.Retrieve(base, idx); ldInstr->Attribute = base->Attribute; PushStack(ldInstr); } virtual RefPtr VisitImportExpression(ImportExpressionSyntaxNode * expr) override { variables.PushScope(); List arguments; int argIndex = 0; for (auto param : expr->ImportOperatorDef->GetParameters()) { expr->Arguments[argIndex]->Accept(this); auto argOp = PopStack(); arguments.Add(argOp); variables.Add(param->Name.Content, argOp); argIndex++; } currentImport = expr; auto oldTypeMapping = genericTypeMappings.TryGetValue(expr->ImportOperatorDef->TypeName.Content); auto componentType = TranslateExpressionType(expr->Type); genericTypeMappings[expr->ImportOperatorDef->TypeName.Content] = componentType; codeWriter.PushNode(); expr->ImportOperatorDef->Body->Accept(this); currentImport = nullptr; auto impInstr = new ImportInstruction(expr->Arguments.Count()); for (int i = 0; i < expr->Arguments.Count(); i++) impInstr->Arguments[i] = arguments[i]; impInstr->ImportOperator = codeWriter.PopNode(); variables.PopScope(); if (oldTypeMapping) genericTypeMappings[expr->ImportOperatorDef->TypeName.Content] = *oldTypeMapping; else genericTypeMappings.Remove(expr->ImportOperatorDef->TypeName.Content); impInstr->ComponentName = expr->ComponentUniqueName; impInstr->Type = TranslateExpressionType(expr->Type); codeWriter.Insert(impInstr); PushStack(impInstr); return expr; } virtual RefPtr VisitIndexExpression(IndexExpressionSyntaxNode* expr) override { expr->BaseExpression->Access = expr->Access; expr->BaseExpression->Accept(this); auto base = PopStack(); expr->IndexExpression->Access = ExpressionAccess::Read; expr->IndexExpression->Accept(this); auto idx = PopStack(); GenerateIndexExpression(base, idx); return expr; } virtual RefPtr VisitMemberExpression(MemberExpressionSyntaxNode * expr) override { RefPtr refObj; if (expr->Tags.TryGetValue("ComponentReference", refObj)) { if (auto refComp = refObj.As()) { ILOperand * op; if (variables.TryGetValue(refComp->Content, op)) PushStack(op); else throw InvalidProgramException("referencing undefined component/variable. probable cause: unchecked circular reference."); } } else { expr->BaseExpression->Access = expr->Access; expr->BaseExpression->Accept(this); auto base = PopStack(); auto generateSingleMember = [&](char memberName) { int idx = 0; if (memberName == 'y' || memberName == 'g') idx = 1; else if (memberName == 'z' || memberName == 'b') idx = 2; else if (memberName == 'w' || memberName == 'a') idx = 3; GenerateIndexExpression(base, result.Program->ConstantPool->CreateConstant(idx)); }; if (expr->BaseExpression->Type->IsVectorType()) { if (expr->MemberName.Length() == 1) { generateSingleMember(expr->MemberName[0]); } else { auto rs = new SwizzleInstruction(); rs->Type = TranslateExpressionType(expr->Type.Ptr()); rs->SwizzleString = expr->MemberName; rs->Operand = base; codeWriter.Insert(rs); PushStack(rs); } } else if (expr->BaseExpression->Type->IsStruct()) { int id = expr->BaseExpression->Type->AsBasicType()->structDecl->FindFieldIndex(expr->MemberName); GenerateIndexExpression(base, result.Program->ConstantPool->CreateConstant(id)); } else throw NotImplementedException("member expression codegen"); } return expr; } virtual RefPtr VisitInvokeExpression(InvokeExpressionSyntaxNode* expr) override { List args; String funcName; bool hasSideEffect = false; if (auto basicType = expr->FunctionExpr->Type->AsBasicType()) { if (basicType->Func) { funcName = basicType->Func->SyntaxNode->IsExtern() ? basicType->Func->SyntaxNode->Name.Content : basicType->Func->SyntaxNode->InternalName; for (auto & param : basicType->Func->SyntaxNode->GetParameters()) { if (param->HasModifier(ModifierFlag::Out)) { hasSideEffect = true; break; } } } else if (basicType->Component) { auto funcCompName = expr->FunctionExpr->Tags["ComponentReference"]().As()->Content; auto funcComp = *(currentShader->DefinitionsByComponent[funcCompName]().TryGetValue(currentComponent->World)); funcName = GetComponentFunctionName(funcComp->SyntaxNode.Ptr()); for (auto & param : funcComp->SyntaxNode->GetParameters()) { if (param->HasModifier(ModifierFlag::Out)) { hasSideEffect = true; break; } } // push additional arguments for (auto & dep : funcComp->GetComponentFunctionDependencyClosure()) { if (!dep->SyntaxNode->IsComponentFunction()) { ILOperand * op = nullptr; if (variables.TryGetValue(dep->UniqueName, op)) args.Add(op); else throw InvalidProgramException("cannot resolve reference for implicit component function argument."); } } } } if (currentWorld) { currentWorld->ReferencedFunctions.Add(funcName); } if (currentFunc) currentFunc->ReferencedFunctions.Add(funcName); for (auto arg : expr->Arguments) { arg->Accept(this); args.Add(PopStack()); } auto instr = new CallInstruction(args.Count()); instr->SideEffect = hasSideEffect; instr->Function = funcName; for (int i = 0; i < args.Count(); i++) instr->Arguments[i] = args[i]; instr->Type = TranslateExpressionType(expr->Type); codeWriter.Insert(instr); PushStack(instr); return expr; } virtual RefPtr VisitTypeCastExpression(TypeCastExpressionSyntaxNode * expr) override { expr->Expression->Accept(this); auto base = PopStack(); if (expr->Expression->Type == expr->Type) { PushStack(base); } else if (expr->Expression->Type == ExpressionType::Float && expr->Type == ExpressionType::Int) { auto instr = new Float2IntInstruction(base); codeWriter.Insert(instr); PushStack(instr); } else if (expr->Expression->Type == ExpressionType::Int && expr->Type == ExpressionType::Float) { auto instr = new Int2FloatInstruction(base); codeWriter.Insert(instr); PushStack(instr); } else { getSink()->diagnose(expr, Diagnostics::invalidTypeCast, expr->Expression->Type, expr->Type); } return expr; } virtual RefPtr VisitUnaryExpression(UnaryExpressionSyntaxNode* expr) override { if (expr->Operator == Operator::PostDec || expr->Operator == Operator::PostInc || expr->Operator == Operator::PreDec || expr->Operator == Operator::PreInc) { expr->Expression->Access = ExpressionAccess::Read; expr->Expression->Accept(this); auto base = PopStack(); BinaryInstruction * instr; if (expr->Operator == Operator::PostDec) instr = new SubInstruction(); else instr = new AddInstruction(); instr->Operands.SetSize(2); instr->Operands[0] = base; if (expr->Type == ExpressionType::Float) instr->Operands[1] = result.Program->ConstantPool->CreateConstant(1.0f); else instr->Operands[1] = result.Program->ConstantPool->CreateConstant(1); instr->Type = TranslateExpressionType(expr->Type); codeWriter.Insert(instr); expr->Expression->Access = ExpressionAccess::Write; expr->Expression->Accept(this); auto dest = PopStack(); auto store = new StoreInstruction(dest, instr); codeWriter.Insert(store); PushStack(base); } else if (expr->Operator == Operator::PreDec || expr->Operator == Operator::PreInc) { expr->Expression->Access = ExpressionAccess::Read; expr->Expression->Accept(this); auto base = PopStack(); BinaryInstruction * instr; if (expr->Operator == Operator::PostDec) instr = new SubInstruction(); else instr = new AddInstruction(); instr->Operands.SetSize(2); instr->Operands[0] = base; if (expr->Type == ExpressionType::Float) instr->Operands[1] = result.Program->ConstantPool->CreateConstant(1.0f); else instr->Operands[1] = result.Program->ConstantPool->CreateConstant(1); instr->Type = TranslateExpressionType(expr->Type); codeWriter.Insert(instr); expr->Expression->Access = ExpressionAccess::Write; expr->Expression->Accept(this); auto dest = PopStack(); auto store = new StoreInstruction(dest, instr); codeWriter.Insert(store); PushStack(instr); } else { expr->Expression->Accept(this); auto base = PopStack(); auto genUnaryInstr = [&](ILOperand * input) { UnaryInstruction * rs = 0; switch (expr->Operator) { case Operator::Not: input = EnsureBoolType(input, expr->Expression->Type); rs = new NotInstruction(); break; case Operator::Neg: rs = new NegInstruction(); break; case Operator::BitNot: rs = new BitNotInstruction(); break; default: throw NotImplementedException("Code gen is not implemented for this operator."); } rs->Operand = input; rs->Type = input->Type; codeWriter.Insert(rs); return rs; }; PushStack(genUnaryInstr(base)); } return expr; } bool GenerateVarRef(String name, ExpressionAccess access) { ILOperand * var = 0; String srcName = name; if (!variables.TryGetValue(srcName, var)) { return false; } if (access == ExpressionAccess::Read) { PushStack(var); } else { PushStack(var); } return true; } virtual RefPtr VisitVarExpression(VarExpressionSyntaxNode* expr) override { RefPtr refObj; if (expr->Tags.TryGetValue("ComponentReference", refObj)) { if (auto refComp = refObj.As()) { ILOperand * op; if (variables.TryGetValue(refComp->Content, op)) PushStack(op); else throw InvalidProgramException(String("referencing undefined component/variable '") + refComp->Content + "'. probable cause: unchecked circular reference."); } } else if (!GenerateVarRef(expr->Variable, expr->Access)) { throw InvalidProgramException("identifier is neither a variable nor a recognized component."); } return expr; } private: CodeGenerator & operator = (const CodeGenerator & other) = delete; public: CodeGenerator(SymbolTable * symbols, DiagnosticSink * pErr, CompileResult & _result, LayoutRule defaultLayoutRule) : ICodeGenerator(pErr), symTable(symbols), result(_result), defaultLayoutRule(defaultLayoutRule) { result.Program = new ILProgram(); codeWriter.SetConstantPool(result.Program->ConstantPool.Ptr()); } private: RefPtr TranslateStructType(StructSyntaxNode* structDecl) { RefPtr ilStructType; if (structTypes.TryGetValue(structDecl, ilStructType)) { return ilStructType; } ilStructType = new ILStructType(); ilStructType->TypeName = structDecl->Name.Content; ilStructType->IsIntrinsic = structDecl->IsIntrinsic; for (auto field : structDecl->GetFields()) { ILStructType::ILStructField ilField; ilField.FieldName = field->Name.Content; ilField.Type = TranslateExpressionType(field->Type.Ptr()); ilStructType->Members.Add(ilField); } structTypes.Add(structDecl, ilStructType); return ilStructType; } RefPtr TranslateExpressionType(ExpressionType * type) { RefPtr resultType = 0; if (auto basicType = type->AsBasicType()) { if (basicType->BaseType == BaseType::Struct) { resultType = TranslateStructType(basicType->structDecl); } else if (basicType->BaseType == BaseType::Record) { return genericTypeMappings[basicType->RecordTypeName](); } else if (basicType->BaseType == BaseType::Generic) { return genericTypeMappings[basicType->GenericTypeVar](); } else { auto base = new ILBasicType(); base->Type = (ILBaseType)basicType->BaseType; resultType = base; } } else if (auto arrType = type->AsArrayType()) { auto nArrType = new ILArrayType(); nArrType->BaseType = TranslateExpressionType(arrType->BaseType.Ptr()); nArrType->ArrayLength = arrType->ArrayLength; resultType = nArrType; } else if (auto genType = type->AsGenericType()) { auto gType = new ILGenericType(); gType->GenericTypeName = genType->GenericTypeName; gType->BaseType = TranslateExpressionType(genType->BaseType.Ptr()); resultType = gType; } return resultType; } RefPtr TranslateExpressionType(const RefPtr & type) { return TranslateExpressionType(type.Ptr()); } }; ICodeGenerator * CreateCodeGenerator(SymbolTable * symbols, CompileResult & result, CodeGenBackend* backend) { return new CodeGenerator(symbols, result.GetErrorWriter(), result, backend->GetDefaultLayoutRule()); } } } ================================================ FILE: Source/SpireCore/CodeWriter.h ================================================ #ifndef IL_CODE_WRITER_H #define IL_CODE_WRITER_H #include "IL.h" #include "ShaderCompiler.h" namespace Spire { namespace Compiler { class CodeWriter { private: List> cfgNode; ConstantPool * constantPool = nullptr; public: void SetConstantPool(ConstantPool * pool) { constantPool = pool; } CFGNode * GetCurrentNode() { return cfgNode.Last().Ptr(); } void PushNode() { RefPtr n = new CFGNode(); cfgNode.Add(n); } RefPtr PopNode() { auto rs = cfgNode.Last(); cfgNode.SetSize(cfgNode.Count() - 1); return rs; } void Assign(ILType * type, ILOperand * dest, ILOperand * src) // handles base type and ILArrayType assignment { auto arrType = dynamic_cast(type); if (arrType) { for (int i = 0; i < arrType->ArrayLength; i++) { auto srcAddr = Add(src, i); auto destAddr = Add(dest, i); Store(destAddr, Load(srcAddr)); } } else Store(dest, Load(src)); } ILOperand * Select(ILOperand * cond, ILOperand * v0, ILOperand * v1) { auto rs = new SelectInstruction(cond, v0, v1); cfgNode.Last()->InsertTail(rs); return rs; } ILOperand * BitAnd(ILOperand * v0, ILOperand * v1) { auto instr = new BitAndInstruction(v0, v1); cfgNode.Last()->InsertTail(instr); return instr; } ILOperand * BitAnd(ILOperand * v0, int c) { auto instr = new BitAndInstruction(v0, constantPool->CreateConstant(c)); cfgNode.Last()->InsertTail(instr); return instr; } ILOperand * Add(ILOperand * v0, ILOperand * v1) { auto instr = new AddInstruction(v0, v1); cfgNode.Last()->InsertTail(instr); return instr; } ILOperand * Add(ILOperand * v0, int v1) { auto instr = new AddInstruction(v0, constantPool->CreateConstant(v1)); cfgNode.Last()->InsertTail(instr); return instr; } ILOperand * Mul(ILOperand * v0, ILOperand * v1) { auto instr = new MulInstruction(v0, v1); cfgNode.Last()->InsertTail(instr); return instr; } ILOperand * Copy(ILOperand * src) { auto rs = new CopyInstruction(src); cfgNode.Last()->InsertTail(rs); return rs; } ILOperand * Load(ILOperand * src, int offset) { if (offset == 0) { auto instr = new LoadInstruction(src); cfgNode.Last()->InsertTail(instr); return instr; } else { auto dest = new AddInstruction(src, constantPool->CreateConstant(offset)); cfgNode.Last()->InsertTail(dest); auto instr = new LoadInstruction(dest); cfgNode.Last()->InsertTail(instr); return instr; } } ILOperand * Load(ILOperand * src) { auto instr = new LoadInstruction(src); cfgNode.Last()->InsertTail(instr); return instr; } ILOperand * Load(ILOperand * src, ILOperand * offset) { auto dest = new AddInstruction(src, offset); cfgNode.Last()->InsertTail(dest); return Load(dest); } StoreInstruction * Store(ILOperand * dest, ILOperand * value) { auto instr = new StoreInstruction(dest, value); cfgNode.Last()->InsertTail(instr); return instr; } DiscardInstruction * Discard() { auto instr = new DiscardInstruction(); cfgNode.Last()->InsertTail(instr); return instr; } MemberUpdateInstruction * Update(ILOperand * dest, ILOperand * offset, ILOperand * value) { auto instr = new MemberUpdateInstruction(dest, offset, value); cfgNode.Last()->InsertTail(instr); return instr; } MemberLoadInstruction * Retrieve(ILOperand * dest, ILOperand * offset) { auto instr = new MemberLoadInstruction(dest, offset); cfgNode.Last()->InsertTail(instr); return instr; } //AllocVarInstruction * AllocVar(ILType * type, ILOperand * size) //{ // auto arrType = dynamic_cast(type); // if (arrType) // { // // check: size must be constant 1. Do not support array of array in IL level. // auto s = dynamic_cast(size); // if (!s || s->IntValues[0] != 1) // throw ArgumentException("AllocVar(arrayType, size): size must be constant 1."); // auto instr = new AllocVarInstruction(arrType->BaseType, program.CreateConstant(arrType->ArrayLength)); // cfgNode->InsertTail(instr); // return instr; // } // else // { // auto instr = new AllocVarInstruction(type, size); // cfgNode->InsertTail(instr); // return instr; // } //} AllocVarInstruction * AllocVar(RefPtr & type, ILOperand * size) { auto arrType = dynamic_cast(type.Ptr()); if (arrType) { // check: size must be constant 1. Do not support array of array in IL level. auto s = dynamic_cast(size); if (!s || s->IntValues[0] != 1) throw ArgumentException("AllocVar(arrayType, size): size must be constant 1."); auto instr = new AllocVarInstruction(arrType->BaseType, constantPool->CreateConstant(arrType->ArrayLength)); cfgNode.Last()->InsertTail(instr); return instr; } else { auto instr = new AllocVarInstruction(type, size); cfgNode.Last()->InsertTail(instr); return instr; } } /*GLeaInstruction * GLea(ILType * type, const String & name) { auto arrType = dynamic_cast(type); auto instr = new GLeaInstruction(); if (arrType) instr->Type = new ILPointerType(arrType->BaseType); else instr->Type = new ILPointerType(type); instr->Name = name; instr->VariableName = name; cfgNode->InsertTail(instr); return instr; }*/ FetchArgInstruction * FetchArg(RefPtr type, int argId) { auto instr = new FetchArgInstruction(type); cfgNode.Last()->InsertTail(instr); instr->ArgId = argId; return instr; } void Insert(ILInstruction * instr) { cfgNode.Last()->InsertTail(instr); } }; } } #endif ================================================ FILE: Source/SpireCore/CompiledProgram.cpp ================================================ #include "CompiledProgram.h" namespace Spire { namespace Compiler { void IndentString(StringBuilder & sb, String src) { int indent = 0; bool beginTrim = true; for (int c = 0; c < src.Length(); c++) { auto ch = src[c]; if (ch == '\n') { sb << "\n"; beginTrim = true; } else { if (beginTrim) { while (c < src.Length() - 1 && (src[c] == '\t' || src[c] == '\n' || src[c] == '\r' || src[c] == ' ')) { c++; ch = src[c]; } for (int i = 0; i < indent - 1; i++) sb << '\t'; if (ch != '}' && indent > 0) sb << '\t'; beginTrim = false; } if (ch == '{') indent++; else if (ch == '}') indent--; if (indent < 0) indent = 0; sb << ch; } } } ShaderChoiceValue ShaderChoiceValue::Parse(String str) { return ShaderChoiceValue(str); } } } ================================================ FILE: Source/SpireCore/CompiledProgram.h ================================================ #ifndef BAKER_SL_COMPILED_PROGRAM_H #define BAKER_SL_COMPILED_PROGRAM_H #include "../CoreLib/Basic.h" #include "Diagnostics.h" #include "IL.h" #include "Syntax.h" namespace Spire { namespace Compiler { class ConstantPoolImpl; class ConstantPool { private: ConstantPoolImpl * impl; public: ILConstOperand * CreateConstant(ILConstOperand * c); ILConstOperand * CreateConstantIntVec(int val0, int val1); ILConstOperand * CreateConstantIntVec(int val0, int val1, int val2); ILConstOperand * CreateConstantIntVec(int val0, int val1, int val3, int val4); ILConstOperand * CreateConstant(int val, int vectorSize = 0); ILConstOperand * CreateConstant(float val, int vectorSize = 0); ILConstOperand * CreateConstant(float val, float val1); ILConstOperand * CreateConstant(float val, float val1, float val2); ILConstOperand * CreateConstant(float val, float val1, float val2, float val3); ILConstOperand * CreateConstant(bool b); ILConstOperand * CreateConstantU(unsigned int u); ILOperand * CreateDefaultValue(ILType * type); ILUndefinedOperand * GetUndefinedOperand(); ConstantPool(); ~ConstantPool(); }; class ILShader; class ILWorld : public Object { public: String Name; CodePosition Position; RefPtr OutputType; List Inputs; RefPtr Code; EnumerableDictionary Components; bool IsAbstract = false; EnumerableDictionary Attributes; EnumerableHashSet ReferencedFunctions; // internal names of referenced functions ILShader * Shader = nullptr; }; class StageAttribute { public: String Name; String Value; CodePosition Position; }; class ILStage : public Object { public: CodePosition Position; String Name; String StageType; EnumerableDictionary Attributes; }; class ILModuleParameterSet; class ILModuleParameterInstance : public ILOperand { public: ILModuleParameterSet * Module = nullptr; int BufferOffset = -1; int Size = 0; List BindingPoints; // for legacy API, usually one item. Samplers may have multiple binding points in OpenGL. virtual String ToString() { return "moduleParam<" + Name + ">"; } }; class ILModuleParameterSet : public RefObject { public: int BufferSize = 0; String BindingName; int DescriptorSetId = -1; int UniformBufferLegacyBindingPoint = -1; bool IsTopLevel = false; // for sub parameter sets: these are starting indices for each type of resource (for vk they should be the same) int TextureBindingStartIndex = 0, SamplerBindingStartIndex = 0, StorageBufferBindingStartIndex = 0, UniformBindingStartIndex = 0; // for sub parameter sets: this is the offset into parent's uniform buffer where fields of this parameter set started. int UniformBufferOffset = 0; EnumerableDictionary> Parameters; List> SubModules; }; class ILShader { public: CodePosition Position; String Name; EnumerableDictionary> ModuleParamSets; EnumerableDictionary> Worlds; EnumerableDictionary> Stages; }; class ILParameter { public: RefPtr Type; ParameterQualifier Qualifier; ILParameter() = default; ILParameter(RefPtr type, ParameterQualifier qualifier = ParameterQualifier::In) : Type(type), Qualifier(qualifier) {} }; class ILFunction { public: EnumerableDictionary Parameters; RefPtr ReturnType; RefPtr Code; String Name; }; class ILProgram { public: RefPtr ConstantPool = new Compiler::ConstantPool(); List> Shaders; EnumerableDictionary> Functions; List> Structs; }; class ShaderChoiceValue { public: String WorldName; ShaderChoiceValue() = default; ShaderChoiceValue(String world) { WorldName = world; } static ShaderChoiceValue Parse(String str); String ToString() { return WorldName; } bool operator == (const ShaderChoiceValue & val) { return WorldName == val.WorldName; } bool operator != (const ShaderChoiceValue & val) { return WorldName != val.WorldName; } int GetHashCode() { return WorldName.GetHashCode(); } }; class ShaderChoice { public: String ChoiceName; String DefaultValue; List Options; }; class ShaderMetaData { public: CoreLib::String ShaderName; CoreLib::EnumerableDictionary> ParameterSets; // bindingName->DescSet }; class StageSource { public: String MainCode; List BinaryCode; }; class CompiledShaderSource { public: EnumerableDictionary Stages; ShaderMetaData MetaData; }; void IndentString(StringBuilder & sb, String src); class CompileResult { public: DiagnosticSink sink; String ScheduleFile; RefPtr Program; List Choices; EnumerableDictionary CompiledSource; // shader -> stage -> code void PrintDiagnostics() { for (int i = 0; i < sink.diagnostics.Count(); i++) { fprintf(stderr, "%S(%d): %s %d: %S\n", sink.diagnostics[i].Position.FileName.ToWString(), sink.diagnostics[i].Position.Line, getSeverityName(sink.diagnostics[i].severity), sink.diagnostics[i].ErrorID, sink.diagnostics[i].Message.ToWString()); } } CompileResult() {} DiagnosticSink * GetErrorWriter() { return &sink; } int GetErrorCount() { return sink.GetErrorCount(); } }; } } #endif ================================================ FILE: Source/SpireCore/ConstantPool.cpp ================================================ #ifndef CONSTANT_POOL_H #define CONSTANT_POOL_H #include "ShaderCompiler.h" #include "IL.h" namespace Spire { namespace Compiler { class ConstantPoolImpl { private: ILUndefinedOperand undefOperand; Dictionary, ILConstOperand*> intConsts; Dictionary, ILConstOperand*> uintConsts; Dictionary, ILConstOperand*> floatConsts; List> constants; RefPtr trueConst, falseConst; public: ILUndefinedOperand * GetUndefinedOperand() { return &undefOperand; } ILOperand * CreateDefaultValue(ILType * type) { ILOperand * value = 0; if (type->IsFloat()) value = CreateConstant(0.0f); else if (type->IsInt()) value = CreateConstant(0); else if (auto baseType = dynamic_cast(type)) { if (baseType->Type == ILBaseType::Int2) { value = CreateConstant(0, 2); } else if (baseType->Type == ILBaseType::Int3) { value = CreateConstant(0, 3); } else if (baseType->Type == ILBaseType::Int4) { value = CreateConstant(0, 4); } else if (baseType->Type == ILBaseType::Float2) { value = CreateConstant(0.0f, 2); } else if (baseType->Type == ILBaseType::Float3) { value = CreateConstant(0.0f, 3); } else if (baseType->Type == ILBaseType::Float4) { value = CreateConstant(0.0f, 4); } else if (baseType->Type == ILBaseType::Float3x3) { value = CreateConstant(0.0f, 9); } else if (baseType->Type == ILBaseType::Float4x4) { value = CreateConstant(0.0f, 16); } else throw NotImplementedException("default value for this type is not implemented."); } else throw NotImplementedException("default value for this type is not implemented."); return value; } ILConstOperand * CreateConstantIntVec(int val, int val2) { ILConstOperand * rs = 0; auto key = ConstKey::FromValues(val, val2); if (intConsts.TryGetValue(key, rs)) return rs; rs = new ILConstOperand(); rs->Type = new ILBasicType(ILBaseType::Int2); rs->IntValues[0] = val; rs->IntValues[1] = val2; intConsts[key] = rs; rs->Name = rs->ToString(); constants.Add(rs); return rs; } ILConstOperand * CreateConstantIntVec(int val, int val2, int val3) { ILConstOperand * rs = 0; auto key = ConstKey::FromValues(val, val2, val3); if (intConsts.TryGetValue(key, rs)) return rs; rs = new ILConstOperand(); rs->Type = new ILBasicType(ILBaseType::Int3); rs->IntValues[0] = val; rs->IntValues[1] = val2; rs->IntValues[2] = val3; intConsts[key] = rs; rs->Name = rs->ToString(); constants.Add(rs); return rs; } ILConstOperand * CreateConstantIntVec(int val, int val2, int val3, int val4) { ILConstOperand * rs = 0; auto key = ConstKey::FromValues(val, val2, val3, val4); if (intConsts.TryGetValue(key, rs)) return rs; rs = new ILConstOperand(); rs->Type = new ILBasicType(ILBaseType::Int4); rs->IntValues[0] = val; rs->IntValues[1] = val2; rs->IntValues[2] = val3; rs->IntValues[3] = val4; intConsts[key] = rs; rs->Name = rs->ToString(); constants.Add(rs); return rs; } ILConstOperand * CreateConstant(ILConstOperand * c) { auto baseType = dynamic_cast(c->Type.Ptr())->Type; switch (baseType) { case ILBaseType::Float: return CreateConstant(c->FloatValues[0]); case ILBaseType::Float2: return CreateConstant(c->FloatValues[0], c->FloatValues[1]); case ILBaseType::Float3: return CreateConstant(c->FloatValues[0], c->FloatValues[1], c->FloatValues[2]); case ILBaseType::Float4: return CreateConstant(c->FloatValues[0], c->FloatValues[1], c->FloatValues[2], c->FloatValues[3]); case ILBaseType::Int: return CreateConstant(c->IntValues[0]); case ILBaseType::Int2: return CreateConstantIntVec(c->IntValues[0], c->IntValues[1]); case ILBaseType::Int3: return CreateConstantIntVec(c->IntValues[0], c->IntValues[1], c->IntValues[2]); case ILBaseType::Int4: return CreateConstantIntVec(c->IntValues[0], c->IntValues[1], c->IntValues[2], c->IntValues[3]); default: if (constants.IndexOf(c) != -1) return c; else { auto rs = new ILConstOperand(*c); constants.Add(rs); return rs; } } } ILConstOperand * CreateConstantU(unsigned int val) { ILConstOperand * rs = 0; if (uintConsts.TryGetValue(ConstKey(val, 1), rs)) return rs; rs = new ILConstOperand(); ILBaseType baseType; baseType = ILBaseType::UInt; rs->Type = new ILBasicType(baseType); rs->IntValues[0] = val; uintConsts[ConstKey(val, 1)] = rs; rs->Name = rs->ToString(); constants.Add(rs); return rs; } ILConstOperand * CreateConstant(bool b) { if (b) return trueConst.Ptr(); else return falseConst.Ptr(); } ILConstOperand * CreateConstant(int val, int size = 0) { ILConstOperand * rs = 0; if (intConsts.TryGetValue(ConstKey(val, size), rs)) return rs; rs = new ILConstOperand(); ILBaseType baseType; switch (size) { case 0: case 1: baseType = ILBaseType::Int; break; case 2: baseType = ILBaseType::Int2; break; case 3: baseType = ILBaseType::Int3; break; case 4: baseType = ILBaseType::Int4; break; default: throw InvalidOperationException("Invalid vector size."); } rs->Type = new ILBasicType(baseType); rs->IntValues[0] = val; intConsts[ConstKey(val, size)] = rs; rs->Name = rs->ToString(); constants.Add(rs); return rs; } ILConstOperand * CreateConstant(float val, int size = 0) { ILConstOperand * rs = 0; if (floatConsts.TryGetValue(ConstKey(val, size), rs)) return rs; if (Math::IsNaN(val) || Math::IsInf(val)) { throw InvalidOperationException("Attempting to create NAN constant."); } rs = new ILConstOperand(); ILBaseType baseType; switch (size) { case 0: case 1: baseType = ILBaseType::Float; break; case 2: baseType = ILBaseType::Float2; break; case 3: baseType = ILBaseType::Float3; break; case 4: baseType = ILBaseType::Float4; break; case 9: baseType = ILBaseType::Float3x3; break; case 16: baseType = ILBaseType::Float4x4; break; default: throw InvalidOperationException("Invalid vector size."); } rs->Type = new ILBasicType(baseType); for (int i = 0; i < 16; i++) rs->FloatValues[i] = val; floatConsts[ConstKey(val, size)] = rs; rs->Name = rs->ToString(); constants.Add(rs); return rs; } ILConstOperand * CreateConstant(float val, float val2) { ILConstOperand * rs = 0; if (Math::IsNaN(val) || Math::IsInf(val) || Math::IsNaN(val2) || Math::IsInf(val2)) { throw InvalidOperationException("Attempting to create NAN constant."); } auto key = ConstKey::FromValues(val, val2); if (floatConsts.TryGetValue(key, rs)) return rs; rs = new ILConstOperand(); rs->Type = new ILBasicType(ILBaseType::Float2); rs->FloatValues[0] = val; rs->FloatValues[1] = val2; floatConsts[key] = rs; rs->Name = rs->ToString(); constants.Add(rs); return rs; } ILConstOperand * CreateConstant(float val, float val2, float val3) { ILConstOperand * rs = 0; if (Math::IsNaN(val) || Math::IsInf(val) || Math::IsNaN(val2) || Math::IsInf(val2) || Math::IsNaN(val3) || Math::IsInf(val3)) { throw InvalidOperationException("Attempting to create NAN constant."); } auto key = ConstKey::FromValues(val, val2, val3); if (floatConsts.TryGetValue(key, rs)) return rs; rs = new ILConstOperand(); rs->Type = new ILBasicType(ILBaseType::Float3); rs->FloatValues[0] = val; rs->FloatValues[1] = val2; rs->FloatValues[2] = val3; floatConsts[key] = rs; rs->Name = rs->ToString(); constants.Add(rs); return rs; } ILConstOperand * CreateConstant(float val, float val2, float val3, float val4) { if (Math::IsNaN(val) || Math::IsInf(val) || Math::IsNaN(val2) || Math::IsInf(val2) || Math::IsNaN(val3) || Math::IsInf(val3) || Math::IsNaN(val4) || Math::IsInf(val4)) { throw InvalidOperationException("Attempting to create NAN constant."); } ILConstOperand * rs = 0; auto key = ConstKey::FromValues(val, val2, val3, val4); if (floatConsts.TryGetValue(key, rs)) return rs; rs = new ILConstOperand(); rs->Type = new ILBasicType(ILBaseType::Float4); rs->FloatValues[0] = val; rs->FloatValues[1] = val2; rs->FloatValues[2] = val3; rs->FloatValues[3] = val4; floatConsts[key] = rs; rs->Name = rs->ToString(); constants.Add(rs); return rs; } ConstantPoolImpl() { trueConst = new ILConstOperand(); trueConst->Type = new ILBasicType(ILBaseType::Bool); trueConst->IntValues[0] = trueConst->IntValues[1] = trueConst->IntValues[2] = trueConst->IntValues[3] = 1; trueConst->Name = "true"; falseConst = new ILConstOperand(); falseConst->Type = new ILBasicType(ILBaseType::Bool); falseConst->IntValues[0] = falseConst->IntValues[1] = falseConst->IntValues[2] = falseConst->IntValues[3] = 0; trueConst->Name = "false"; } }; ConstantPool::ConstantPool() { impl = new ConstantPoolImpl(); } ConstantPool::~ConstantPool() { delete impl; } ILUndefinedOperand * ConstantPool::GetUndefinedOperand() { return impl->GetUndefinedOperand(); } ILConstOperand * ConstantPool::CreateConstant(ILConstOperand * c) { return impl->CreateConstant(c); } ILConstOperand * ConstantPool::CreateConstantIntVec(int val0, int val1) { return impl->CreateConstantIntVec(val0, val1); } ILConstOperand * ConstantPool::CreateConstantIntVec(int val0, int val1, int val2) { return impl->CreateConstantIntVec(val0, val1, val2); } ILConstOperand * ConstantPool::CreateConstantIntVec(int val0, int val1, int val3, int val4) { return impl->CreateConstantIntVec(val0, val1, val3, val4); } ILConstOperand * ConstantPool::CreateConstant(bool b) { return impl->CreateConstant(b); } ILConstOperand * ConstantPool::CreateConstantU(unsigned int u) { return impl->CreateConstantU(u); } ILConstOperand * ConstantPool::CreateConstant(int val, int vectorSize) { return impl->CreateConstant(val, vectorSize); } ILConstOperand * ConstantPool::CreateConstant(float val, int vectorSize) { return impl->CreateConstant(val, vectorSize); } ILConstOperand * ConstantPool::CreateConstant(float val, float val1) { return impl->CreateConstant(val, val1); } ILConstOperand * ConstantPool::CreateConstant(float val, float val1, float val2) { return impl->CreateConstant(val, val1, val2); } ILConstOperand * ConstantPool::CreateConstant(float val, float val1, float val2, float val3) { return impl->CreateConstant(val, val1, val2, val3); } ILOperand * ConstantPool::CreateDefaultValue(ILType * type) { return impl->CreateDefaultValue(type); } } } #endif ================================================ FILE: Source/SpireCore/DiagnosticDefs.h ================================================ // // The file is meant to be included multiple times, to produce different // pieces of declaration/definition code related to diagnostic messages // // Each diagnostic is declared here with: // // DIAGNOSTIC(id, severity, name, messageFormat) // // Where `id` is the unique diagnostic ID, `severity` is the default // severity (from the `Severity` enum), `name` is a name used to refer // to this diagnostic from code, and `messageFormat` is the default // (non-localized) message for the diagnostic, with placeholders // for any arguments. #ifndef DIAGNOSTIC #error Need to #define DIAGNOSTIC(...) before including "DiagnosticDefs.h" #define DIAGNOSTIC(id, severity, name, messageFormat) /* */ #endif // // -1 - Notes that decorate another diagnostic. // DIAGNOSTIC(-1, Note, alsoSeePipelineDefinition, "also see pipeline definition"); DIAGNOSTIC(-1, Note, implicitParameterMatchingFailedBecauseNameNotAccessible, "implicit parameter matching failed because the component of the same name is not accessible from '$0'.\ncheck if you have declared necessary requirements and properly used the 'public' qualifier.") DIAGNOSTIC(-1, Note, implicitParameterMatchingFailedBecauseShaderDoesNotDefineComponent, "implicit parameter matching failed because shader '$0' does not define component '$1'.") DIAGNOSTIC(-1, Note, implicitParameterMatchingFailedBecauseTypeMismatch, "implicit parameter matching failed because the component of the same name does not match parameter type '$0'.") DIAGNOSTIC(-1, Note, noteShaderIsTargetingPipeine, "shader '$0' is targeting pipeline '$1'") DIAGNOSTIC(-1, Note, seeDefinitionOf, "see definition of '$0'") DIAGNOSTIC(-1, Note, seeInterfaceDefinitionOf, "see interface definition of '$0'") DIAGNOSTIC(-1, Note, seeUsingOf, "see using of '$0'") DIAGNOSTIC(-1, Note, seeDefinitionOfShader, "see definition of shader '$0'") DIAGNOSTIC(-1, Note, seeInclusionOf, "see inclusion of '$0'") DIAGNOSTIC(-1, Note, seeModuleBeingUsedIn, "see module '$0' being used in '$1'") DIAGNOSTIC(-1, Note, seePipelineRequirementDefinition, "see pipeline requirement definition") DIAGNOSTIC(-1, Note, seePotentialDefinitionOfComponent, "see potential definition of component '$0'") DIAGNOSTIC(-1, Note, seePreviousDefinition, "see previous definition") DIAGNOSTIC(-1, Note, seePreviousDefinitionOf, "see previous definition of '$0'") DIAGNOSTIC(-1, Note, seeRequirementDeclaration, "see requirement declaration") DIAGNOSTIC(-1, Note, doYouForgetToMakeComponentAccessible, "do you forget to make component '$0' acessible from '$1' (missing public qualifier)?") // // 0xxxx - Command line and interaction with host platform APIs. // DIAGNOSTIC( 1, Error, cannotOpenFile, "cannot open file '$0'.") DIAGNOSTIC( 2, Error, cannotFindFile, "cannot find file '$0'.") DIAGNOSTIC( 2, Error, unsupportedCompilerMode, "unsupported compiler mode.") DIAGNOSTIC( 4, Error, cannotWriteOutputFile, "cannot write output file '$0'.") DIAGNOSTIC( 5, Note, d3dCompileInfo, "$0") // // 1xxxx - Lexical anaylsis // DIAGNOSTIC(10000, Error, illegalCharacter, "Illegal character '\\x$0'"); DIAGNOSTIC(10001, Error, illegalCharacterLiteral, "Illegal character literial."); // // 15xxx - Preprocessing // // 150xx - conditionals DIAGNOSTIC(15000, Error, endOfFileInPreprocessorConditional, "end of file encountered during preprocessor conditional") DIAGNOSTIC(15001, Error, directiveWithoutIf, "'$0' directive without '#if'") DIAGNOSTIC(15002, Error, directiveAfterElse , "'$0' directive without '#if'") DIAGNOSTIC(-1, Note, seeDirective, "see '$0' directive") // 151xx - directive parsing DIAGNOSTIC(15100, Error, expectedPreprocessorDirectiveName, "expected preprocessor directive name") DIAGNOSTIC(15101, Error, unknownPreprocessorDirective, "unknown preprocessor directive '$0'") DIAGNOSTIC(15102, Error, expectedTokenInPreprocessorDirective, "expected '$0' in '$1' directive") DIAGNOSTIC(15102, Error, expected2TokensInPreprocessorDirective, "expected '$0' or '$1' in '$2' directive") DIAGNOSTIC(15103, Error, unexpectedTokensAfterDirective, "unexpected tokens following '$0' directive") // 152xx - preprocessor expressions DIAGNOSTIC(15200, Error, expectedTokenInPreprocessorExpression, "expected '$0' in preprocessor expression"); DIAGNOSTIC(15201, Error, syntaxErrorInPreprocessorExpression, "syntax error in preprocessor expression"); DIAGNOSTIC(15202, Error, divideByZeroInPreprocessorExpression, "division by zero in preprocessor expression"); DIAGNOSTIC(15203, Error, expectedTokenInDefinedExpression, "expected '$0' in 'defined' expression"); DIAGNOSTIC(-1, Note, seeOpeningToken, "see opening '$0'") // 153xx - #include DIAGNOSTIC(15300, Error, includeFailed, "failed to find include file '$0'") DIAGNOSTIC(-1, Error, noIncludeHandlerSpecified, "no `#include` handler was specified") // 154xx - macro definition DIAGNOSTIC(15400, Warning, macroRedefinition, "redefinition of macro '$0'") DIAGNOSTIC(15401, Warning, macroNotDefined, "macro '$0' is not defined") DIAGNOSTIC(15403, Error, expectedTokenInMacroParameters, "expected '$0' in macro parameters") // 155xx - macro expansion DIAGNOSTIC(15500, Warning, expectedTokenInMacroArguments, "expected '$0' in macro invocation") // 159xx - user-defined error/warning DIAGNOSTIC(15900, Error, userDefinedError, "#error: $0") DIAGNOSTIC(15901, Warning, userDefinedWarning, "#warning: $0") // // 2xxxx - Parsing // DIAGNOSTIC(20003, Error, unexpectedToken, "unexpected $0"); DIAGNOSTIC(20001, Error, unexpectedTokenExpectedTokenType, "unexpected $0, expected $1"); DIAGNOSTIC(20001, Error, unexpectedTokenExpectedTokenName, "unexpected $0, expected '$1'"); DIAGNOSTIC(0, Error, tokenNameExpectedButEOF, "\"$0\" expected but end of file encountered."); DIAGNOSTIC(0, Error, tokenTypeExpectedButEOF, "$0 expected but end of file encountered."); DIAGNOSTIC(20001, Error, tokenNameExpected, "\"$0\" expected"); DIAGNOSTIC(20001, Error, tokenNameExpectedButEOF2, "\"$0\" expected but end of file encountered."); DIAGNOSTIC(20001, Error, tokenTypeExpected, "$0 expected"); DIAGNOSTIC(20001, Error, tokenTypeExpectedButEOF2, "$0 expected but end of file encountered."); DIAGNOSTIC(20001, Error, typeNameExpectedBut, "unexpected $0, expected type name"); DIAGNOSTIC(20001, Error, typeNameExpectedButEOF, "type name expected but end of file encountered."); DIAGNOSTIC(20001, Error, unexpectedEOF, " Unexpected end of file."); DIAGNOSTIC(20002, Error, syntaxError, "syntax error."); DIAGNOSTIC(20004, Error, unexpectedTokenExpectedComponentDefinition, "unexpected token '$0', only component definitions are allowed in a shader scope.") DIAGNOSTIC(20008, Error, invalidOperator, "invalid operator '$0'."); DIAGNOSTIC(20011, Error, unexpectedColon, "unexpected ':'.") // // 3xxxx - Semantic analysis // DIAGNOSTIC(30001, Error, functionRedefinitionWithArgList, "'$0$1': function redefinition.") DIAGNOSTIC(30002, Error, parameterAlreadyDefined, "parameter '$0' already defined.") DIAGNOSTIC(30003, Error, breakOutsideLoop, "'break' must appear inside loop constructs.") DIAGNOSTIC(30004, Error, continueOutsideLoop, "'continue' must appear inside loop constructs.") DIAGNOSTIC(30005, Error, whilePredicateTypeError, "'while': expression must evaluate to int.") DIAGNOSTIC(30006, Error, ifPredicateTypeError, "'if': expression must evaluate to int.") DIAGNOSTIC(30006, Error, returnNeedsExpression, "'return' should have an expression.") DIAGNOSTIC(30007, Error, componentReturnTypeMismatch, "expression type '$0' does not match component's type '$1'") DIAGNOSTIC(30007, Error, functionReturnTypeMismatch, "expression type '$0' does not match function's return type '$1'") DIAGNOSTIC(30008, Error, variableNameAlreadyDefined, "variable $0 already defined.") DIAGNOSTIC(30009, Error, invalidTypeVoid, "invalid type 'void'.") DIAGNOSTIC(30010, Error, whilePredicateTypeError2, "'while': expression must evaluate to int.") DIAGNOSTIC(30011, Error, assignNonLValue, "left of '=' is not an l-value.") DIAGNOSTIC(30012, Error, noApplicationUnaryOperator, "no overload found for operator $0 ($1).") DIAGNOSTIC(30012, Error, noOverloadFoundForBinOperatorOnTypes, "no overload found for operator $0 ($1, $2).") DIAGNOSTIC(30013, Error, subscriptNonArray, "'[]' can only index on arrays.") DIAGNOSTIC(30014, Error, subscriptIndexNonInteger, "index expression must evaluate to int.") DIAGNOSTIC(30015, Error, undefinedIdentifier, "'$0': undefined identifier.") DIAGNOSTIC(30015, Error, undefinedIdentifier2, "undefined identifier '$0'.") DIAGNOSTIC(30016, Error, parameterCannotBeVoid, "'void' can not be parameter type.") DIAGNOSTIC(30017, Error, componentNotAccessibleFromShader, "component '$0' is not accessible from shader '$1'.") DIAGNOSTIC(30019, Error, typeMismatch, "type mismatch '$0' and '$1'") DIAGNOSTIC(30020, Error, importOperatorReturnTypeMismatch, "import operator should return '$1', but the expression has type '$0''. do you forget 'project'?") DIAGNOSTIC(30021, Error, noApplicationFunction, "$0: no overload takes arguments ($1)") DIAGNOSTIC(30022, Error, invalidTypeCast, "invalid type cast between \"$0\" and \"$1\".") DIAGNOSTIC(30023, Error, typeHasNoPublicMemberOfName, "\"$0\" does not have public member \"$1\"."); DIAGNOSTIC(30025, Error, invalidArraySize, "array size must be larger than zero.") DIAGNOSTIC(30026, Error, returnInComponentMustComeLast, "'return' can only appear as the last statement in component definition.") DIAGNOSTIC(30027, Error, noMemberOfNameInType, "'$0' is not a member of '$1'."); DIAGNOSTIC(30028, Error, forPredicateTypeError, "'for': predicate expression must evaluate to bool.") DIAGNOSTIC(30030, Error, projectionOutsideImportOperator, "'project': invalid use outside import operator.") DIAGNOSTIC(30031, Error, projectTypeMismatch, "'project': expression must evaluate to record type '$0'.") DIAGNOSTIC(30033, Error, invalidTypeForLocalVariable, "cannot declare a local variable of this type.") DIAGNOSTIC(30035, Error, componentOverloadTypeMismatch, "'$0': type of overloaded component mismatches previous definition.") DIAGNOSTIC(30041, Error, bitOperationNonIntegral, "bit operation: operand must be integral type.") DIAGNOSTIC(30047, Error, argumentExpectedLValue, "argument passed to parameter '$0' must be l-value.") DIAGNOSTIC(30051, Error, invalidValueForArgument, "invalid value for argument '$0'") DIAGNOSTIC(30052, Error, ordinaryFunctionAsModuleArgument, "ordinary functions not allowed as argument to function-typed module parameter.") DIAGNOSTIC(30079, Error, selectPrdicateTypeMismatch, "selector must evaluate to bool."); DIAGNOSTIC(30080, Error, selectValuesTypeMismatch, "the two value expressions in a select clause must have same type."); DIAGNOSTIC(31040, Error, undefinedTypeName, "undefined type name: '$0'.") DIAGNOSTIC(32013, Error, circularReferenceNotAllowed, "'$0': circular reference is not allowed."); DIAGNOSTIC(32014, Error, shaderDoesProvideRequirement, "shader '$0' does not provide '$1' as required by '$2'.") DIAGNOSTIC(32015, Error, argumentNotAvilableInWorld, "argument '$0' is not available in world '$1' as required by '$2'.") DIAGNOSTIC(32015, Error, componentNotAvilableInWorld, "component '$0' is not available in world '$1' as required by '$2'.") DIAGNOSTIC(32047, Error, firstArgumentToImportNotComponent, "first argument of an import operator call does not resolve to a component."); DIAGNOSTIC(32051, Error, componentTypeNotWhatPipelineRequires, "component '$0' has type '$1', but pipeline '$2' requires it to be '$3'.") DIAGNOSTIC(32052, Error, shaderDoesNotDefineComponentAsRequiredByPipeline, "shader '$0' does not define '$1' as required by pipeline '$2''.") DIAGNOSTIC(33001, Error, worldNameAlreadyDefined, "world '$0' is already defined.") DIAGNOSTIC(33002, Error, explicitPipelineSpecificationRequiredForShader, "explicit pipeline specification required for shader '$0' because multiple pipelines are defined in current context.") DIAGNOSTIC(33003, Error, cannotDefineComponentsInAPipeline, "cannot define components in a pipeline.") DIAGNOSTIC(33004, Error, undefinedWorldName, "undefined world name '$0'.") DIAGNOSTIC(33005, Error, abstractWorldAsTargetOfImport, "abstract world cannot appear as target as an import operator.") // Note(tfoley): This is a duplicate of 33004 above. DIAGNOSTIC(33006, Error, undefinedWorldName2, "undefined world name '$0'.") DIAGNOSTIC(33007, Error, importOperatorCircularity, "import operator '$0' creates a circular dependency between world '$1' and '$2'") DIAGNOSTIC(33009, Error, parametersOnlyAllowedInModules, "parameters can only be defined in modules.") DIAGNOSTIC(33010, Error, undefinedPipelineName, "pipeline '$0' is undefined.") DIAGNOSTIC(33011, Error, shaderCircularity, "shader '$0' involves circular reference.") DIAGNOSTIC(33012, Error, worldIsNotDefinedInPipeline, "'$0' is not a defined world in '$1'.") DIAGNOSTIC(33013, Error, abstractWorldCannotAppearWithOthers, "abstract world cannot appear with other worlds.") DIAGNOSTIC(33014, Error, nonAbstractComponentMustHaveImplementation, "non-abstract component must have an implementation.") DIAGNOSTIC(33016, Error, usingInComponentDefinition, "'using': importing not allowed in component definition.") DIAGNOSTIC(33018, Error, nameAlreadyDefined, "'$0' is already defined.") DIAGNOSTIC(33018, Error, shaderAlreadyDefined, "shader '$0' has already been defined.") DIAGNOSTIC(33019, Error, componentMarkedExportMustHaveWorld, "component '$0': definition marked as 'export' must have an explicitly specified world.") DIAGNOSTIC(33020, Error, componentIsAlreadyDefined, "'$0' is already defined.") DIAGNOSTIC(33020, Error, componentIsAlreadyDefinedInThatWorld, "'$0' is already defined at '$1'.") DIAGNOSTIC(33021, Error, inconsistentSignatureForComponent, "'$0': inconsistent signature.") DIAGNOSTIC(33022, Error, nameAlreadyDefinedInCurrentScope, "'$0' is already defined in current scope.") DIAGNOSTIC(33022, Error, parameterNameConflictsWithExistingDefinition, "'$0': parameter name conflicts with existing definition.") DIAGNOSTIC(33023, Error, parameterOfModuleIsUnassigned, "parameter '$0' of module '$1' is unassigned.") DIAGNOSTIC(33027, Error, argumentTypeDoesNotMatchParameterType, "argument type ($0) does not match parameter type ($1)") DIAGNOSTIC(33028, Error, nameIsNotAParameterOfCallee, "'$0' is not a parameter of '$1'.") DIAGNOSTIC(33029, Error, requirementsClashWithPreviousDef, "'$0': requirement clash with previous definition.") DIAGNOSTIC(33030, Error, positionArgumentAfterNamed, "positional argument cannot appear after a named argument.") DIAGNOSTIC(33031, Error, tooManyArguments, "too many arguments.") DIAGNOSTIC(33032, Error, functionRedefinition, "'$0': function redefinition.") DIAGNOSTIC(33034, Error, recordTypeVariableInImportOperator, "cannot declare a record-typed variable in an import operator.") DIAGNOSTIC(33037, Error, componetMarkedExportCannotHaveParameters, "component '$0': definition marked as 'export' cannot have parameters.") DIAGNOSTIC(33039, Error, componentInInputWorldCantHaveCode, "'$0': no code allowed for component defined in input world.") DIAGNOSTIC(33040, Error, requireWithComputation, "'require': cannot define computation on component requirements.") DIAGNOSTIC(33042, Error, paramWithComputation, "'param': cannot define computation on parameters.") DIAGNOSTIC(33041, Error, pipelineOfModuleIncompatibleWithPipelineOfShader, "pipeline '$0' targeted by module '$1' is incompatible with pipeline '$2' targeted by shader '$3'.") DIAGNOSTIC(33070, Error, expectedFunction, "expression preceding parenthesis of apparent call must have function type.") DIAGNOSTIC(33071, Error, importOperatorCalledFromAutoPlacedComponent, "cannot call an import operator from an auto-placed component '$0'. try qualify the component with explicit worlds.") DIAGNOSTIC(33072, Error, noApplicableImportOperator, "'$0' is an import operator defined in pipeline '$1', but none of the import operator overloads converting to world '$2' matches argument list ($3).") DIAGNOSTIC(33073, Error, importOperatorCalledFromMultiWorldComponent, "cannot call an import operator from a multi-world component definition. consider qualify the component with only one explicit world.") DIAGNOSTIC(33080, Error, componentTypeDoesNotMatchInterface, "'$0': component type does not match definition in interface '$1'.") DIAGNOSTIC(33081, Error, shaderDidNotDefineComponentFunction, "shader '$0' did not define component function $1 as required by interface '$2'.") DIAGNOSTIC(33082, Error, shaderDidNotDefineComponent, "shader '$0' did not define component '$1' as required by interface '$2'.") DIAGNOSTIC(33083, Error, interfaceImplMustBePublic, "'$0': component fulfilling interface '$1' must be declared as 'public'.") DIAGNOSTIC(33084, Error, defaultParamNotAllowedInInterface, "'$0': default parameter value not allowed in interface definition.") DIAGNOSTIC(33100, Error, componentCantBeComputedAtWorldBecauseDependentNotAvailable, "'$0' cannot be computed at '$1' because the dependent component '$2' is not accessible.") DIAGNOSTIC(33101, Warning, worldIsNotAValidChoiceForKey, "'$0' is not a valid choice for '$1'.") DIAGNOSTIC(33102, Error, componentDefinitionCircularity, "component definition '$0' involves circular reference.") DIAGNOSTIC(34024, Error, componentAlreadyDefinedWhenCompiling, "component named '$0' is already defined when compiling '$1'.") DIAGNOSTIC(34025, Error, globalComponentConflictWithPreviousDeclaration, "'$0': global component conflicts with previous declaration.") DIAGNOSTIC(34026, Warning, componentIsAlreadyDefinedUseRequire, "'$0': component is already defined when compiling shader '$1'. use 'require' to declare it as a parameter.") DIAGNOSTIC(34062, Error, cylicReference, "cyclic reference: $0"); DIAGNOSTIC(34064, Error, noApplicableImplicitImportOperator, "cannot find import operator to import component '$0' to world '$1' when compiling '$2'.") DIAGNOSTIC(34065, Error, resourceTypeMustBeParamOrRequire, "'$0': resource typed component must be declared as 'param' or 'require'."); DIAGNOSTIC(34066, Error, cannotDefineComputationOnResourceType, "'$0': cannot define computation on resource typed component."); DIAGNOSTIC(35001, Error, fragDepthAttributeCanOnlyApplyToOutput, "FragDepth attribute can only apply to an output component."); DIAGNOSTIC(35002, Error, fragDepthAttributeCanOnlyApplyToFloatComponent, "FragDepth attribute can only apply to a float component."); DIAGNOSTIC(36001, Error, insufficientTemplateShaderArguments, "instantiating template shader '$0': insufficient arguments."); DIAGNOSTIC(36002, Error, tooManyTemplateShaderArguments, "instantiating template shader '$0': too many arguments."); DIAGNOSTIC(36003, Error, templateShaderArgumentIsNotDefined, "'$0' provided as template shader argument to '$1' is not a defined module."); DIAGNOSTIC(36004, Error, templateShaderArgumentDidNotImplementRequiredInterface, "module '$0' provided as template shader argument to '$1' did not implement required interface '$2'."); DIAGNOSTIC(36010, Error, specializeCanOnlyBeUsedOnParam, "'specialize' modifier can only be used on module parameters."); DIAGNOSTIC(36011, Error, specializedParameterMustBeInt, "specialized parameters must be a int, uint or bool."); DIAGNOSTIC(36012, Error, specializationValuesMustBeConstantLiterial, "specialization candidate values can only be constant literials."); // // 4xxxx - IL code generation. // DIAGNOSTIC(40001, Error, bindingAlreadyOccupiedByComponent, "resource binding location '$0' is already occupied by component '$1'.") DIAGNOSTIC(40002, Error, invalidBindingValue, "binding location '$0' is out of valid range.") DIAGNOSTIC(40003, Error, bindingExceedsLimit, "binding location '$0' assigned to component '$1' exceeds maximum limit.") DIAGNOSTIC(40004, Error, bindingAlreadyOccupiedByModule, "DescriptorSet ID '$0' is already occupied by module instance '$1'.") DIAGNOSTIC(40005, Error, topLevelModuleUsedWithoutSpecifyingBinding, "top level module '$0' is being used without specifying binding location. Use [Binding: \"index\"] attribute to provide a binding location.") // // 5xxxx - Target code generation. // DIAGNOSTIC(50020, Error, unknownStageType, "Unknown stage type '$0'.") DIAGNOSTIC(50020, Error, invalidTessCoordType, "TessCoord must have vec2 or vec3 type.") DIAGNOSTIC(50020, Error, invalidFragCoordType, "FragCoord must be a vec4.") DIAGNOSTIC(50020, Error, invalidInvocationIdType, "InvocationId must have int type.") DIAGNOSTIC(50020, Error, invalidThreadIdType, "ThreadId must have int type.") DIAGNOSTIC(50020, Error, invalidPrimitiveIdType, "PrimitiveId must have int type.") DIAGNOSTIC(50020, Error, invalidPatchVertexCountType, "PatchVertexCount must have int type.") DIAGNOSTIC(50020, Error, invalidTypeForSystemVar, "Invalid type for system value '$0'.") DIAGNOSTIC(50022, Error, worldIsNotDefined, "world '$0' is not defined."); DIAGNOSTIC(50023, Error, stageShouldProvideWorldAttribute, "'$0' should provide 'World' attribute."); DIAGNOSTIC(50040, Error, componentHasInvalidTypeForPositionOutput, "'$0': component used as 'Position' output must be of vec4 type.") DIAGNOSTIC(50041, Error, componentNotDefined, "'$0': component not defined.") DIAGNOSTIC(50052, Error, domainShaderRequiresControlPointCount, "'DomainShader' requires attribute 'ControlPointCount'."); DIAGNOSTIC(50052, Error, hullShaderRequiresControlPointCount, "'HullShader' requires attribute 'ControlPointCount'.") DIAGNOSTIC(50052, Error, hullShaderRequiresControlPointWorld, "'HullShader' requires attribute 'ControlPointWorld'."); DIAGNOSTIC(50052, Error, hullShaderRequiresCornerPointWorld, "'HullShader' requires attribute 'CornerPointWorld'."); DIAGNOSTIC(50052, Error, hullShaderRequiresDomain, "'HullShader' requires attribute 'Domain'."); DIAGNOSTIC(50052, Error, hullShaderRequiresInputControlPointCount, "'HullShader' requires attribute 'InputControlPointCount'.") DIAGNOSTIC(50052, Error, hullShaderRequiresOutputTopology, "'HullShader' requires attribute 'OutputTopology'.") DIAGNOSTIC(50052, Error, hullShaderRequiresPartitioning, "'HullShader' requires attribute 'Partitioning'.") DIAGNOSTIC(50052, Error, hullShaderRequiresPatchWorld, "'HullShader' requires attribute 'PatchWorld'."); DIAGNOSTIC(50052, Error, hullShaderRequiresTessLevelInner, "'HullShader' requires attribute 'TessLevelInner'.") DIAGNOSTIC(50052, Error, hullShaderRequiresTessLevelOuter, "'HullShader' requires attribute 'TessLevelOuter'.") DIAGNOSTIC(50053, Error, invalidTessellationDomian, "'Domain' should be either 'triangles' or 'quads'."); DIAGNOSTIC(50053, Error, invalidTessellationOutputTopology, "'OutputTopology' must be one of: 'point', 'line', 'triangle_cw', or 'triangle_ccw'."); DIAGNOSTIC(50053, Error, invalidTessellationPartitioning, "'Partitioning' must be one of: 'integer', 'pow2', 'fractional_even', or 'fractional_odd'.") DIAGNOSTIC(50053, Error, invalidTessellationDomain, "'Domain' should be either 'triangles' or 'quads'.") DIAGNOSTIC(50082, Error, importingFromPackedBufferUnsupported, "importing type '$0' from PackedBuffer is not supported by the GLSL backend.") DIAGNOSTIC(51090, Error, cannotGenerateCodeForExternComponentType, "cannot generate code for extern component type '$0'.") DIAGNOSTIC(51091, Error, typeCannotBePlacedInATexture, "type '$0' cannot be placed in a texture.") DIAGNOSTIC(51092, Error, stageDoesntHaveInputWorld, "'$0' doesn't appear to have any input world"); // 99999 - Internal compiler errors, and not-yet-classified diagnostics. DIAGNOSTIC(99999, Internal, internalCompilerError, "internal compiler error") DIAGNOSTIC(99999, Internal, unimplemented, "unimplemented feature: $0") #undef DIAGNOSTIC ================================================ FILE: Source/SpireCore/Diagnostics.cpp ================================================ // Diagnostics.cpp #include "Diagnostics.h" #include "CompiledProgram.h" #include "SymbolTable.h" #include "Syntax.h" #include namespace Spire { namespace Compiler { void printDiagnosticArg(StringBuilder& sb, char const* str) { sb << str; } void printDiagnosticArg(StringBuilder& sb, int str) { sb << str; } void printDiagnosticArg(StringBuilder& sb, CoreLib::Basic::String const& str) { sb << str; } void printDiagnosticArg(StringBuilder& sb, Decl* decl) { sb << decl->Name.Content; } void printDiagnosticArg(StringBuilder& sb, Type* type) { sb << type->DataType->ToString(); } void printDiagnosticArg(StringBuilder& sb, ExpressionType* type) { sb << type->ToString(); } void printDiagnosticArg(StringBuilder& sb, ILType* type) { sb << type->ToString(); } void printDiagnosticArg(StringBuilder& sb, CoreLib::Text::TokenType tokenType) { sb << TokenTypeToString(tokenType); } void printDiagnosticArg(StringBuilder& sb, Token const& token) { sb << token.Content; } void printDiagnosticArg(StringBuilder& sb, StageAttribute const& attr) { sb << attr.Value; } CodePosition const& getDiagnosticPos(SyntaxNode const* syntax) { return syntax->Position; } CodePosition const& getDiagnosticPos(CoreLib::Text::Token const& token) { return token.Position; } CodePosition const& getDiagnosticPos(ShaderClosure* shader) { return shader->Position; } // Take the format string for a diagnostic message, along with its arguments, and turn it into a static void formatDiagnosticMessage(StringBuilder& sb, char const* format, int argCount, DiagnosticArg const* const* args) { char const* spanBegin = format; for(;;) { char const* spanEnd = spanBegin; while (int c = *spanEnd) { if (c == '$') break; spanEnd++; } sb.Append(spanBegin, int(spanEnd - spanBegin)); if (!*spanEnd) return; assert(*spanEnd == '$'); spanEnd++; int d = *spanEnd++; switch (d) { // A double dollar sign `$$` is used to emit a single `$` case '$': sb.Append('$'); break; // A single digit means to emit the corresponding argument. // TODO: support more than 10 arguments, and add options // to control formatting, etc. case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { int index = d - '0'; if (index >= argCount) { // TODO(tfoley): figure out what a good policy will be for "panic" situations like this throw InvalidOperationException("too few arguments for diagnostic message"); } else { DiagnosticArg const* arg = args[index]; arg->printFunc(sb, arg->data); } } break; default: throw InvalidOperationException("invalid diagnostic message format"); break; } spanBegin = spanEnd; } } void DiagnosticSink::diagnoseImpl(CodePosition const& pos, DiagnosticInfo const& info, int argCount, DiagnosticArg const* const* args) { StringBuilder sb; formatDiagnosticMessage(sb, info.messageFormat, argCount, args); Diagnostic diagnostic; diagnostic.ErrorID = info.id; diagnostic.Message = sb.ProduceString(); diagnostic.Position = pos; diagnostic.severity = info.severity; diagnostics.Add(diagnostic); if (diagnostic.severity >= Severity::Error) { errorCount++; } if (diagnostic.severity >= Severity::Fatal) { // TODO: figure out a better policy for aborting compilation throw InvalidOperationException(); } } namespace Diagnostics { #define DIAGNOSTIC(id, severity, name, messageFormat) const DiagnosticInfo name = { id, Severity::severity, messageFormat }; #include "DiagnosticDefs.h" } }} // namespace Spire::Compiler ================================================ FILE: Source/SpireCore/Diagnostics.h ================================================ #ifndef RASTER_RENDERER_COMPILE_ERROR_H #define RASTER_RENDERER_COMPILE_ERROR_H #include "../CoreLib/Basic.h" #include "../CoreLib/Tokenizer.h" namespace Spire { namespace Compiler { using namespace CoreLib::Basic; using namespace CoreLib::Text; enum class Severity { Note, Warning, Error, Fatal, Internal, }; // TODO(tfoley): move this into a source file... inline const char* getSeverityName(Severity severity) { switch (severity) { case Severity::Note: return "note"; case Severity::Warning: return "warning"; case Severity::Error: return "error"; case Severity::Fatal: return "fatal error"; case Severity::Internal: return "internal error"; default: return "unknown error"; } } // A structure to be used in static data describing different // diagnostic messages. struct DiagnosticInfo { int id; Severity severity; char const* messageFormat; }; class Diagnostic { public: String Message; CodePosition Position; int ErrorID; Severity severity; Diagnostic() { ErrorID = -1; } Diagnostic( const String & msg, int id, const CodePosition & pos, Severity severity) : severity(severity) { Message = msg; ErrorID = id; Position = pos; } }; class Decl; class Type; class ExpressionType; class ILType; class StageAttribute; void printDiagnosticArg(StringBuilder& sb, char const* str); void printDiagnosticArg(StringBuilder& sb, int val); void printDiagnosticArg(StringBuilder& sb, CoreLib::Basic::String const& str); void printDiagnosticArg(StringBuilder& sb, Decl* decl); void printDiagnosticArg(StringBuilder& sb, Type* type); void printDiagnosticArg(StringBuilder& sb, ExpressionType* type); void printDiagnosticArg(StringBuilder& sb, ILType* type); void printDiagnosticArg(StringBuilder& sb, CoreLib::Text::TokenType tokenType); void printDiagnosticArg(StringBuilder& sb, Token const& token); void printDiagnosticArg(StringBuilder& sb, StageAttribute const& attr); template void printDiagnosticArg(StringBuilder& sb, RefPtr ptr) { printDiagnosticArg(sb, ptr.Ptr()); } inline CodePosition const& getDiagnosticPos(CodePosition const& pos) { return pos; } class SyntaxNode; class ShaderClosure; CodePosition const& getDiagnosticPos(SyntaxNode const* syntax); CodePosition const& getDiagnosticPos(CoreLib::Text::Token const& token); CodePosition const& getDiagnosticPos(ShaderClosure* shader); template CodePosition getDiagnosticPos(RefPtr const& ptr) { return getDiagnosticPos(ptr.Ptr()); } struct DiagnosticArg { void* data; void (*printFunc)(StringBuilder&, void*); template struct Helper { static void printFunc(StringBuilder& sb, void* data) { printDiagnosticArg(sb, *(T*)data); } }; template DiagnosticArg(T const& arg) : data((void*)&arg) , printFunc(&Helper::printFunc) {} }; class DiagnosticSink { public: List diagnostics; int errorCount = 0; /* void Error(int id, const String & msg, const CodePosition & pos) { diagnostics.Add(Diagnostic(msg, id, pos, Severity::Error)); errorCount++; } void Warning(int id, const String & msg, const CodePosition & pos) { diagnostics.Add(Diagnostic(msg, id, pos, Severity::Warning)); } */ int GetErrorCount() { return errorCount; } void diagnoseDispatch(CodePosition const& pos, DiagnosticInfo const& info) { diagnoseImpl(pos, info, 0, NULL); } void diagnoseDispatch(CodePosition const& pos, DiagnosticInfo const& info, DiagnosticArg const& arg0) { DiagnosticArg const* args[] = { &arg0 }; diagnoseImpl(pos, info, 1, args); } void diagnoseDispatch(CodePosition const& pos, DiagnosticInfo const& info, DiagnosticArg const& arg0, DiagnosticArg const& arg1) { DiagnosticArg const* args[] = { &arg0, &arg1 }; diagnoseImpl(pos, info, 2, args); } void diagnoseDispatch(CodePosition const& pos, DiagnosticInfo const& info, DiagnosticArg const& arg0, DiagnosticArg const& arg1, DiagnosticArg const& arg2) { DiagnosticArg const* args[] = { &arg0, &arg1, &arg2 }; diagnoseImpl(pos, info, 3, args); } void diagnoseDispatch(CodePosition const& pos, DiagnosticInfo const& info, DiagnosticArg const& arg0, DiagnosticArg const& arg1, DiagnosticArg const& arg2, DiagnosticArg const& arg3) { DiagnosticArg const* args[] = { &arg0, &arg1, &arg2, &arg3 }; diagnoseImpl(pos, info, 4, args); } template void diagnose(P const& pos, DiagnosticInfo const& info, Args const&... args ) { diagnoseDispatch(getDiagnosticPos(pos), info, args...); } void diagnoseImpl(CodePosition const& pos, DiagnosticInfo const& info, int argCount, DiagnosticArg const* const* args); // A stored state for the sink, which can be used to restore it later. struct State { int diagnosticCount; int errorCount; }; // Save the state of the sink so that it can be restored later. State saveState() { State state; state.diagnosticCount = diagnostics.Count(); state.errorCount = errorCount; return state; } // Restore the state of the sink to a saved state. void restoreState(State state) { errorCount = state.errorCount; diagnostics.SetSize(state.diagnosticCount); } }; namespace Diagnostics { #define DIAGNOSTIC(id, severity, name, messageFormat) extern const DiagnosticInfo name; #include "DiagnosticDefs.h" } } } #ifdef _DEBUG #define SPIRE_INTERNAL_ERROR(sink, pos) \ (sink)->diagnose(Spire::Compiler::CodePosition(__LINE__, 0, 0, __FILE__), Spire::Compiler::Diagnostics::internalCompilerError) #define SPIRE_UNIMPLEMENTED(sink, pos, what) \ (sink)->diagnose(Spire::Compiler::CodePosition(__LINE__, 0, 0, __FILE__), Spire::Compiler::Diagnostics::unimplemented, what) #else #define SPIRE_INTERNAL_ERROR(sink, pos) \ (sink)->diagnose(pos, Spire::Compiler::Diagnostics::internalCompilerError) #define SPIRE_UNIMPLEMENTED(sink, pos, what) \ (sink)->diagnose(pos, Spire::Compiler::Diagnostics::unimplemented, what) #endif #endif ================================================ FILE: Source/SpireCore/GLSLCodeGen.cpp ================================================ #include "CLikeCodeGen.h" #include "../CoreLib/Tokenizer.h" #include "Syntax.h" #include "Naming.h" #include "SamplerUsageAnalysis.h" #include using namespace CoreLib::Basic; namespace Spire { namespace Compiler { class GLSLCodeGen : public CLikeCodeGen { public: bool useVulkanBinding = false; bool useSingleDescSet = false; protected: OutputStrategy * CreateStandardOutputStrategy(ILWorld * world, String layoutPrefix) override; OutputStrategy * CreatePackedBufferOutputStrategy(ILWorld * world) override; OutputStrategy * CreateArrayOutputStrategy(ILWorld * world, bool pIsPatch, int pArraySize, String arrayIndex) override; LayoutRule GetDefaultLayoutRule() override { return LayoutRule::Std140; } void PrintOp(CodeGenContext & ctx, ILOperand * op, bool forceExpression = false) override { if (!useVulkanBinding && op->Type->IsSamplerState()) { // GLSL does not have sampler type, print 0 as placeholder ctx.Body << "0"; return; } CLikeCodeGen::PrintOp(ctx, op, forceExpression); } void PrintRasterPositionOutputWrite(CodeGenContext & ctx, ILOperand * operand) override { ctx.Body << "gl_Position = "; PrintOp(ctx, operand); ctx.Body << ";\n"; } void PrintStandardInputReference(StringBuilder& sb, ILRecordType* recType, String inputName, String componentName) override { String declName = componentName; declName = AddWorldNameSuffix(declName, recType->ToString()); sb << declName; } void PrintStandardArrayInputReference(StringBuilder& sb, ILRecordType* recType, String inputName, String componentName) override { PrintStandardInputReference(sb, recType, inputName, componentName); } void PrintPatchInputReference(StringBuilder& sb, ILRecordType* recType, String inputName, String componentName) override { String declName = componentName; declName = AddWorldNameSuffix(declName, recType->ToString()); sb << declName; } void PrintDefaultInputReference(StringBuilder& sb, ILRecordType* /*recType*/, String inputName, String componentName) override { String declName = componentName; sb << declName; } void PrintSystemVarReference(CodeGenContext & /*ctx*/, StringBuilder& sb, String inputName, ExternComponentCodeGenInfo::SystemVarType systemVar) override { switch(systemVar) { case ExternComponentCodeGenInfo::SystemVarType::FragCoord: sb << "gl_FragCoord"; break; case ExternComponentCodeGenInfo::SystemVarType::TessCoord: sb << "gl_TessCoord"; break; case ExternComponentCodeGenInfo::SystemVarType::InvocationId: sb << "gl_InvocationID"; break; case ExternComponentCodeGenInfo::SystemVarType::ThreadId: sb << "gl_GlobalInvocationID.x"; break; case ExternComponentCodeGenInfo::SystemVarType::PatchVertexCount: sb << "gl_PatchVerticesIn"; break; case ExternComponentCodeGenInfo::SystemVarType::PrimitiveId: sb << "gl_PrimitiveID"; break; default: sb << inputName; break; } } void PrintProjectInstrExpr(CodeGenContext & ctx, ProjectInstruction * proj) { if (auto memberLoadInstr = dynamic_cast(proj->Operand.Ptr())) { bool overrideBaseMemberLoad = false; auto genType = dynamic_cast(memberLoadInstr->Operands[0]->Type.Ptr()); if (genType && genType->GenericTypeName == "PackedBuffer") { // load record type from packed buffer String conversionFunction; int size = 0; if (memberLoadInstr->Type->ToString() == "int") { conversionFunction = "floatBitsToInt"; size = 1; } else if (memberLoadInstr->Type->ToString() == "ivec2") { conversionFunction = "floatBitsToInt"; size = 2; } else if (memberLoadInstr->Type->ToString() == "ivec3") { conversionFunction = "floatBitsToInt"; size = 3; } else if (memberLoadInstr->Type->ToString() == "ivec4") { conversionFunction = "floatBitsToInt"; size = 4; } else if (memberLoadInstr->Type->ToString() == "uint") { conversionFunction = "floatBitsToUint"; size = 1; } else if (memberLoadInstr->Type->ToString() == "uvec2") { conversionFunction = "floatBitsToUint"; size = 2; } else if (memberLoadInstr->Type->ToString() == "uvec3") { conversionFunction = "floatBitsToUint"; size = 3; } else if (memberLoadInstr->Type->ToString() == "uvec4") { conversionFunction = "floatBitsToUint"; size = 4; } else if (memberLoadInstr->Type->ToString() == "float") { conversionFunction = ""; size = 1; } else if (memberLoadInstr->Type->ToString() == "vec2") { conversionFunction = ""; size = 2; } else if (memberLoadInstr->Type->ToString() == "vec3") { conversionFunction = ""; size = 3; } else if (memberLoadInstr->Type->ToString() == "vec4") { conversionFunction = ""; size = 4; } else if (memberLoadInstr->Type->ToString() == "mat3") { conversionFunction = ""; size = 9; } else if (memberLoadInstr->Type->ToString() == "mat4") { conversionFunction = ""; size = 16; } else { errWriter->diagnose(CodePosition(), Diagnostics::importingFromPackedBufferUnsupported, memberLoadInstr->Type); } ctx.Body << memberLoadInstr->Type->ToString() << "("; auto recType = dynamic_cast(genType->BaseType.Ptr()); int recTypeSize = 0; EnumerableDictionary memberOffsets; for (auto & member : recType->Members) { memberOffsets[member.Key] = recTypeSize; recTypeSize += member.Value.Type->GetVectorSize(); } for (int i = 0; i < size; i++) { ctx.Body << conversionFunction << "("; PrintOp(ctx, memberLoadInstr->Operands[0].Ptr()); ctx.Body << "[("; PrintOp(ctx, memberLoadInstr->Operands[1].Ptr()); ctx.Body << ") * " << recTypeSize << " + " << memberOffsets[proj->ComponentName]() << "])"; if (i != size - 1) ctx.Body << ", "; } ctx.Body << ")"; overrideBaseMemberLoad = true; } if (!overrideBaseMemberLoad) PrintOp(ctx, memberLoadInstr, true); if (genType) { if ((genType->GenericTypeName == "StructuredBuffer" || genType->GenericTypeName == "RWStructuredBuffer") && dynamic_cast(genType->BaseType.Ptr())) ctx.Body << "." << proj->ComponentName; } } else PrintOp(ctx, proj->Operand.Ptr(), true); } const char * GetTextureType(ILType * textureType) { auto baseType = dynamic_cast(textureType)->Type; const char * textureName = nullptr; switch (baseType) { case ILBaseType::Texture2D: textureName = "texture2D"; break; case ILBaseType::Texture2DArray: textureName = "texture2DArray"; break; case ILBaseType::Texture2DArrayShadow: textureName = "texture2DArray"; break; case ILBaseType::TextureCube: textureName = "textureCube"; break; case ILBaseType::TextureCubeShadow: textureName = "textureCube"; break; case ILBaseType::Texture3D: textureName = "texture3D"; break; case ILBaseType::TextureCubeArray: textureName = "textureCubeArray"; break; case ILBaseType::TextureCubeShadowArray: textureName = "textureCubeArray"; break; default: throw NotImplementedException(); } return textureName; } const char * GetSamplerType(ILType * textureType) { auto baseType = dynamic_cast(textureType)->Type; const char * samplerName = nullptr; switch (baseType) { case ILBaseType::Texture2D: samplerName = "sampler2D"; break; case ILBaseType::Texture2DArray: samplerName = "sampler2DArray"; break; case ILBaseType::Texture2DArrayShadow: samplerName = "sampler2DArrayShadow"; break; case ILBaseType::TextureCube: samplerName = "samplerCube"; break; case ILBaseType::TextureCubeShadow: samplerName = "samplerCubeShadow"; break; case ILBaseType::Texture3D: samplerName = "sampler3D"; break; case ILBaseType::TextureCubeArray: samplerName = "samplerCubeArray"; break; case ILBaseType::TextureCubeShadowArray: samplerName = "samplerCubeArrayShadow"; break; default: throw NotImplementedException(); } return samplerName; } void PrintTypeName(StringBuilder& sb, ILType* type) override { // Currently, all types are internally named based on their GLSL equivalent, so // outputting a type for GLSL is trivial. // GLSL does not have sampler type, use int as placeholder if (type->IsSamplerState()) { if (useVulkanBinding) { if (dynamic_cast(type)->Type == ILBaseType::SamplerComparisonState) sb << "samplerShadow"; else sb << "sampler"; } else sb << "int"; } else if (type->IsTexture()) { if (useVulkanBinding) sb << GetTextureType(type); else sb << GetSamplerType(type); } else sb << type->ToString(); } void PrintTextureCall(CodeGenContext & ctx, CallInstruction * instr) { auto printSamplerArgument = [&](ILOperand * texture, ILOperand * sampler) { if (useVulkanBinding) { ctx.Body << GetSamplerType(texture->Type.Ptr()) << "("; PrintOp(ctx, texture); ctx.Body << ", "; PrintOp(ctx, sampler); ctx.Body << ")"; } else { PrintOp(ctx, texture); } ctx.Body << ", "; }; if (instr->Function == "Sample") { if (instr->Arguments.Count() == 4) ctx.Body << "textureOffset"; else ctx.Body << "texture"; ctx.Body << "("; printSamplerArgument(instr->Arguments[0].Ptr(), instr->Arguments[1].Ptr()); for (int i = 2; i < instr->Arguments.Count(); i++) { PrintOp(ctx, instr->Arguments[i].Ptr()); if (i < instr->Arguments.Count() - 1) ctx.Body << ", "; } ctx.Body << ")"; } else if (instr->Function == "SampleLevel") { ctx.Body << "textureLod"; ctx.Body << "("; printSamplerArgument(instr->Arguments[0].Ptr(), instr->Arguments[1].Ptr()); for (int i = 2; i < instr->Arguments.Count(); i++) { PrintOp(ctx, instr->Arguments[i].Ptr()); if (i < instr->Arguments.Count() - 1) ctx.Body << ", "; } ctx.Body << ")"; } else if (instr->Function == "SampleGrad") { if (instr->Arguments.Count() == 6) ctx.Body << "textureGradOffset"; else ctx.Body << "textureGrad"; ctx.Body << "("; printSamplerArgument(instr->Arguments[0].Ptr(), instr->Arguments[1].Ptr()); for (int i = 2; i < instr->Arguments.Count(); i++) { PrintOp(ctx, instr->Arguments[i].Ptr()); if (i < instr->Arguments.Count() - 1) ctx.Body << ", "; } ctx.Body << ")"; } else if (instr->Function == "SampleBias") { if (instr->Arguments.Count() == 5) // loc, bias, offset { ctx.Body << "textureOffset("; printSamplerArgument(instr->Arguments[0].Ptr(), instr->Arguments[1].Ptr()); PrintOp(ctx, instr->Arguments[2].Ptr()); ctx.Body << ", "; PrintOp(ctx, instr->Arguments[4].Ptr()); ctx.Body << ", "; PrintOp(ctx, instr->Arguments[3].Ptr()); ctx.Body << ")"; } else { ctx.Body << "texture("; printSamplerArgument(instr->Arguments[0].Ptr(), instr->Arguments[1].Ptr()); PrintOp(ctx, instr->Arguments[2].Ptr()); ctx.Body << ", "; PrintOp(ctx, instr->Arguments[3].Ptr()); ctx.Body << ")"; } } else if (instr->Function == "SampleCmp") { if (instr->Arguments.Count() == 5) ctx.Body << "textureOffset("; else ctx.Body << "texture("; printSamplerArgument(instr->Arguments[0].Ptr(), instr->Arguments[1].Ptr()); auto baseType = dynamic_cast(instr->Arguments[0]->Type.Ptr()); if (baseType) { if (baseType->Type == ILBaseType::Texture2DShadow) ctx.Body << "vec3("; else if (baseType->Type == ILBaseType::TextureCubeShadow || baseType->Type == ILBaseType::Texture2DArrayShadow) ctx.Body << "vec4("; PrintOp(ctx, instr->Arguments[2].Ptr()); ctx.Body << ", "; PrintOp(ctx, instr->Arguments[3].Ptr()); ctx.Body << ")"; if (instr->Arguments.Count() == 5) { ctx.Body << ", "; PrintOp(ctx, instr->Arguments[4].Ptr()); } } ctx.Body << ")"; } else throw NotImplementedException("CodeGen for texture function '" + instr->Function + "' is not implemented."); } void DeclareStandardInputRecord(CodeGenContext & sb, const ILObjectDefinition & input, bool isVertexShader) override { auto info = ExtractExternComponentInfo(input); auto recType = ExtractRecordType(input.Type.Ptr()); assert(recType); assert(info.DataStructure == ExternComponentCodeGenInfo::DataStructureType::StandardInput); int itemsDeclaredInBlock = 0; int index = 0; for (auto & field : recType->Members) { if (!useVulkanBinding && field.Value.Type->IsSamplerState()) continue; //if (input.Attributes.ContainsKey("VertexInput")) if (useVulkanBinding || input.Attributes.ContainsKey("VertexInput")) sb.GlobalHeader << "layout(location = " << index << ") "; if (!isVertexShader && (input.Attributes.ContainsKey("Flat") || field.Value.Type->IsIntegral())) sb.GlobalHeader << "flat "; sb.GlobalHeader << "in "; String declName = field.Key; declName = AddWorldNameSuffix(declName, recType->ToString()); PrintDef(sb.GlobalHeader, field.Value.Type.Ptr(), declName); itemsDeclaredInBlock++; if (info.IsArray) { sb.GlobalHeader << "["; if (info.ArrayLength) sb.GlobalHeader << String(info.ArrayLength); sb.GlobalHeader << "]"; } sb.GlobalHeader << ";\n"; index++; } } void DeclarePatchInputRecord(CodeGenContext & sb, const ILObjectDefinition & input, bool isVertexShader) override { auto info = ExtractExternComponentInfo(input); auto recType = ExtractRecordType(input.Type.Ptr()); assert(recType); assert(info.DataStructure == ExternComponentCodeGenInfo::DataStructureType::Patch); int itemsDeclaredInBlock = 0; int index = 0; for (auto & field : recType->Members) { if (!useVulkanBinding && field.Value.Type->IsSamplerState()) continue; if (!isVertexShader && (input.Attributes.ContainsKey("Flat"))) sb.GlobalHeader << "flat "; sb.GlobalHeader << "patch in "; String declName = field.Key; declName = AddWorldNameSuffix(declName, recType->ToString()); PrintDef(sb.GlobalHeader, field.Value.Type.Ptr(), declName); itemsDeclaredInBlock++; if (info.IsArray) { sb.GlobalHeader << "["; if (info.ArrayLength) sb.GlobalHeader << String(info.ArrayLength); sb.GlobalHeader << "]"; } sb.GlobalHeader << ";\n"; index++; } } void GenerateHeader(StringBuilder & sb, ILStage * stage) { sb << "#version 440\n"; if (stage->Attributes.ContainsKey("BindlessTexture")) sb << "#extension GL_ARB_bindless_texture: require\n#extension GL_NV_gpu_shader5 : require\n"; if (stage->Attributes.ContainsKey("NV_CommandList")) sb << "#extension GL_NV_command_list: require\n"; } void GenerateDomainShaderProlog(CodeGenContext & ctx, ILStage * stage) { ctx.GlobalHeader << "layout("; StageAttribute val; if (stage->Attributes.TryGetValue("Domain", val)) ctx.GlobalHeader << ((val.Value == "quads") ? "quads" : "triangles"); else ctx.GlobalHeader << "triangles"; if (val.Value != "triangles" && val.Value != "quads") getSink()->diagnose(val.Position, Diagnostics::invalidTessellationDomain); if (stage->Attributes.TryGetValue("Winding", val)) { if (val.Value == "cw") ctx.GlobalHeader << ", cw"; else ctx.GlobalHeader << ", ccw"; } if (stage->Attributes.TryGetValue("EqualSpacing", val)) { if (val.Value == "1" || val.Value == "true") ctx.GlobalHeader << ", equal_spacing"; } ctx.GlobalHeader << ") in;\n"; } virtual void PrintParameterReference(StringBuilder& sb, ILModuleParameterInstance * param) override { if (param->Type->GetBindableResourceType() == BindableResourceType::NonBindable) { auto bufferName = EscapeCodeName(param->Module->BindingName); sb << bufferName << "." << param->Name; } else { sb << EscapeCodeName(param->Module->BindingName + "_" + param->Name); } } void GenerateShaderParameterDefinition(CodeGenContext & ctx, ILShader * shader) { int oneDescBindingLoc = 0; for (auto module : shader->ModuleParamSets) { // generate uniform buffer declaration auto bufferName = EscapeCodeName(module.Value->BindingName); bool containsOrdinaryParams = false; for (auto param : module.Value->Parameters) if (param.Value->BufferOffset != -1) { containsOrdinaryParams = true; break; } if (containsOrdinaryParams) { if (useVulkanBinding) { if (!useSingleDescSet) { ctx.GlobalHeader << "layout(std140, set = " << module.Value->DescriptorSetId << ", binding = 0) "; } else { ctx.GlobalHeader << "layout(std140, set = 0, binding =" << oneDescBindingLoc << ") "; module.Value->UniformBufferLegacyBindingPoint = oneDescBindingLoc; oneDescBindingLoc++; } } else ctx.GlobalHeader << "layout(binding = " << module.Value->DescriptorSetId << ", std140) "; ctx.GlobalHeader << "uniform buf" << bufferName << "\n{\n"; for (auto param : module.Value->Parameters) { if (param.Value->BufferOffset != -1) { PrintType(ctx.GlobalHeader, param.Value->Type.Ptr()); ctx.GlobalHeader << " " << param.Value->Name << ";\n"; } } ctx.GlobalHeader << "} " << bufferName << ";\n"; } int slotId = containsOrdinaryParams ? 1 : 0; for (auto param : module.Value->Parameters) { auto bindableType = param.Value->Type->GetBindableResourceType(); if (bindableType != BindableResourceType::NonBindable) { switch (bindableType) { case BindableResourceType::StorageBuffer: { auto genType = param.Value->Type.As(); if (!genType) continue; String bufName = EscapeCodeName(module.Value->BindingName + "_" + param.Value->Name); if (useVulkanBinding) { if (!useSingleDescSet) ctx.GlobalHeader << "layout(std430, set = " << module.Value->DescriptorSetId << ", binding = " << slotId << ") "; else { ctx.GlobalHeader << "layout(std430, set = 0, binding = " << oneDescBindingLoc << ") "; param.Value->BindingPoints.Clear(); param.Value->BindingPoints.Add(oneDescBindingLoc); oneDescBindingLoc++; } } else ctx.GlobalHeader << "layout(std430, binding = " << param.Value->BindingPoints.First() << ") "; ctx.GlobalHeader << "buffer buf" << bufName << "\n{\n"; PrintType(ctx.GlobalHeader, genType->BaseType.Ptr()); ctx.GlobalHeader << " " << bufName << "[];\n};\n"; break; } case BindableResourceType::Texture: { if (useVulkanBinding) { if (!useSingleDescSet) ctx.GlobalHeader << "layout(set = " << module.Value->DescriptorSetId << ", binding = " << slotId << ")"; else { ctx.GlobalHeader << "layout(set = 0, binding = " << oneDescBindingLoc << ")"; param.Value->BindingPoints.Clear(); param.Value->BindingPoints.Add(oneDescBindingLoc); oneDescBindingLoc++; } } else ctx.GlobalHeader << "layout(binding = " << param.Value->BindingPoints.First() << ")"; ctx.GlobalHeader << " uniform "; PrintType(ctx.GlobalHeader, param.Value->Type.Ptr()); ctx.GlobalHeader << " " << EscapeCodeName(module.Value->BindingName + "_" + param.Value->Name) << ";\n"; break; } case BindableResourceType::Sampler: { if (useVulkanBinding) { if (!useSingleDescSet) ctx.GlobalHeader << "layout(set = " << module.Value->DescriptorSetId << ", binding = " << slotId << ")"; else { ctx.GlobalHeader << "layout(set = 0, binding = " << oneDescBindingLoc << ")"; param.Value->BindingPoints.Clear(); param.Value->BindingPoints.Add(oneDescBindingLoc); oneDescBindingLoc++; } ctx.GlobalHeader << " uniform "; PrintType(ctx.GlobalHeader, param.Value->Type.Ptr()); ctx.GlobalHeader << " " << EscapeCodeName(module.Value->BindingName + "_" + param.Value->Name) << ";\n"; } break; } break; default: continue; } slotId++; } } } } virtual void GenerateShaderMetaData(ShaderMetaData & result, ILProgram* program, ILShader* shader, DiagnosticSink* err) override { EnumerableDictionary> samplerTextures; for (auto & w : shader->Worlds) { if (w.Value->Code) AnalyzeSamplerUsage(samplerTextures, program, w.Value->Code.Ptr(), err); } if (!useVulkanBinding) { for (auto & s : program->Shaders) for (auto & pset : s->ModuleParamSets) for (auto & p : pset.Value->Parameters) if (p.Value->Type->IsSamplerState()) p.Value->BindingPoints.Clear(); for (auto & sampler : samplerTextures) { sampler.Key->BindingPoints.Clear(); for (auto & tex : sampler.Value) sampler.Key->BindingPoints.AddRange(tex->BindingPoints); } } CLikeCodeGen::GenerateShaderMetaData(result, program, shader, err); } StageSource GenerateSingleWorldShader(ILProgram * program, ILShader * shader, ILStage * stage) override { useBindlessTexture = stage->Attributes.ContainsKey("BindlessTexture"); StageSource rs; CodeGenContext ctx; GenerateHeader(ctx.GlobalHeader, stage); if (stage->StageType == "DomainShader") GenerateDomainShaderProlog(ctx, stage); GenerateStructs(ctx.GlobalHeader, program); GenerateShaderParameterDefinition(ctx, shader); StageAttribute worldName; RefPtr world = nullptr; if (stage->Attributes.TryGetValue("World", worldName)) { if (!shader->Worlds.TryGetValue(worldName.Value, world)) errWriter->diagnose(worldName.Position, Diagnostics::worldIsNotDefined, worldName.Value); } else errWriter->diagnose(stage->Position, Diagnostics::stageShouldProvideWorldAttribute, stage->StageType); if (!world) return rs; GenerateReferencedFunctions(ctx.GlobalHeader, program, MakeArrayView(world.Ptr())); extCompInfo.Clear(); for (auto & input : world->Inputs) { DeclareInput(ctx, input, stage->StageType == "VertexShader"); } outputStrategy->DeclareOutput(ctx, stage); ctx.codeGen = this; world->Code->NameAllInstructions(); GenerateCode(ctx, world->Code.Ptr()); if (stage->StageType == "VertexShader" || stage->StageType == "DomainShader") GenerateVertexShaderEpilog(ctx, world.Ptr(), stage); StringBuilder sb; sb << ctx.GlobalHeader.ProduceString(); sb << "void main()\n{\n"; sb << ctx.Header.ProduceString() << ctx.Body.ProduceString(); sb << "}"; rs.MainCode = sb.ProduceString(); return rs; } StageSource GenerateHullShader(ILProgram * program, ILShader * shader, ILStage * stage) override { useBindlessTexture = stage->Attributes.ContainsKey("BindlessTexture"); StageSource rs; StageAttribute patchWorldName, controlPointWorldName, cornerPointWorldName, domain, innerLevel, outerLevel, numControlPoints; RefPtr patchWorld, controlPointWorld, cornerPointWorld; if (!stage->Attributes.TryGetValue("PatchWorld", patchWorldName)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresPatchWorld); return rs; } if (!shader->Worlds.TryGetValue(patchWorldName.Value, patchWorld)) errWriter->diagnose(patchWorldName.Position, Diagnostics::worldIsNotDefined, patchWorldName.Value); if (!stage->Attributes.TryGetValue("ControlPointWorld", controlPointWorldName)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresControlPointWorld); return rs; } if (!shader->Worlds.TryGetValue(controlPointWorldName.Value, controlPointWorld)) errWriter->diagnose(controlPointWorldName.Position, Diagnostics::worldIsNotDefined, controlPointWorldName.Value); if (!stage->Attributes.TryGetValue("CornerPointWorld", cornerPointWorldName)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresCornerPointWorld); return rs; } if (!shader->Worlds.TryGetValue(cornerPointWorldName.Value, cornerPointWorld)) errWriter->diagnose(cornerPointWorldName.Position, Diagnostics::worldIsNotDefined, cornerPointWorldName.Value); if (!stage->Attributes.TryGetValue("Domain", domain)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresDomain); return rs; } if (domain.Value != "triangles" && domain.Value != "quads") { errWriter->diagnose(domain.Position, Diagnostics::invalidTessellationDomain); return rs; } if (!stage->Attributes.TryGetValue("TessLevelOuter", outerLevel)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresTessLevelOuter); return rs; } if (!stage->Attributes.TryGetValue("TessLevelInner", innerLevel)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresTessLevelInner); return rs; } if (!stage->Attributes.TryGetValue("ControlPointCount", numControlPoints)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresControlPointCount); return rs; } CodeGenContext ctx; ctx.codeGen = this; List worlds; worlds.Add(patchWorld.Ptr()); worlds.Add(controlPointWorld.Ptr()); worlds.Add(cornerPointWorld.Ptr()); GenerateHeader(ctx.GlobalHeader, stage); ctx.GlobalHeader << "layout(vertices = " << numControlPoints.Value << ") out;\n"; GenerateStructs(ctx.GlobalHeader, program); GenerateShaderParameterDefinition(ctx, shader); GenerateReferencedFunctions(ctx.GlobalHeader, program, worlds.GetArrayView()); extCompInfo.Clear(); HashSet declaredInputs; patchWorld->Code->NameAllInstructions(); outputStrategy = CreateStandardOutputStrategy(patchWorld.Ptr(), "patch"); for (auto & input : patchWorld->Inputs) { if (declaredInputs.Add(input.Name)) DeclareInput(ctx, input, false); } outputStrategy->DeclareOutput(ctx, stage); GenerateCode(ctx, patchWorld->Code.Ptr()); controlPointWorld->Code->NameAllInstructions(); outputStrategy = CreateArrayOutputStrategy(controlPointWorld.Ptr(), false, 0, "gl_InvocationID"); for (auto & input : controlPointWorld->Inputs) { if (declaredInputs.Add(input.Name)) DeclareInput(ctx, input, false); } outputStrategy->DeclareOutput(ctx, stage); GenerateCode(ctx, controlPointWorld->Code.Ptr()); cornerPointWorld->Code->NameAllInstructions(); outputStrategy = CreateArrayOutputStrategy(cornerPointWorld.Ptr(), true, (domain.Value == "triangles" ? 3 : 4), "sysLocalIterator"); for (auto & input : cornerPointWorld->Inputs) { if (declaredInputs.Add(input.Name)) DeclareInput(ctx, input, false); } outputStrategy->DeclareOutput(ctx, stage); ctx.Body << "for (int sysLocalIterator = 0; sysLocalIterator < gl_PatchVerticesIn; sysLocalIterator++)\n{\n"; GenerateCode(ctx, cornerPointWorld->Code.Ptr()); auto debugStr = cornerPointWorld->Code->ToString(); ctx.Body << "}\n"; // generate epilog bool found = false; for (auto & world : worlds) { ILOperand * operand; if (world->Components.TryGetValue(innerLevel.Value, operand)) { for (int i = 0; i < 2; i++) { ctx.Body << "gl_TessLevelInner[" << i << "] = "; PrintOp(ctx, operand); ctx.Body << "[" << i << "];\n"; } found = true; break; } } if (!found) errWriter->diagnose(innerLevel.Position, Diagnostics::componentNotDefined, innerLevel.Value); found = false; for (auto & world : worlds) { ILOperand * operand; if (world->Components.TryGetValue(outerLevel.Value, operand)) { for (int i = 0; i < 4; i++) { ctx.Body << "gl_TessLevelOuter[" << i << "] = "; PrintOp(ctx, operand); ctx.Body << "[" << i << "];\n"; } found = true; break; } } if (!found) errWriter->diagnose(outerLevel.Position, Diagnostics::componentNotDefined, outerLevel.Value); StringBuilder sb; sb << ctx.GlobalHeader.ProduceString(); sb << "void main()\n{\n" << ctx.Header.ProduceString() << ctx.Body.ProduceString() << "}"; rs.MainCode = sb.ProduceString(); return rs; } public: GLSLCodeGen(bool vulkanBinding, bool pUseSingleDescSet) { useVulkanBinding = vulkanBinding; useSingleDescSet = pUseSingleDescSet; } }; class StandardOutputStrategy : public OutputStrategy { private: String declPrefix; bool isVulkan; public: StandardOutputStrategy(GLSLCodeGen * pCodeGen, ILWorld * world, String prefix) : OutputStrategy(pCodeGen, world), declPrefix(prefix) { isVulkan = pCodeGen->useVulkanBinding; } virtual void DeclareOutput(CodeGenContext & ctx, ILStage * stage) override { int location = 0; for (auto & field : world->OutputType->Members) { if (field.Value.Attributes.ContainsKey("FragDepth")) continue; if (isVulkan || stage->StageType == "FragmentShader") ctx.GlobalHeader << "layout(location = " << location << ") "; if (declPrefix.Length()) ctx.GlobalHeader << declPrefix << " "; if (field.Value.Type->IsIntegral()) ctx.GlobalHeader << "flat "; ctx.GlobalHeader << "out "; String declName = field.Key; codeGen->PrintDef(ctx.GlobalHeader, field.Value.Type.Ptr(), AddWorldNameSuffix(declName, world->OutputType->TypeName)); ctx.GlobalHeader << ";\n"; location++; } } virtual void ProcessExportInstruction(CodeGenContext & ctx, ExportInstruction * instr) override { if (world->OutputType->Members[instr->ComponentName]().Attributes.ContainsKey("FragDepth")) ctx.Body << "gl_FragDepth = "; else ctx.Body << AddWorldNameSuffix(instr->ComponentName, world->OutputType->TypeName) << " = "; codeGen->PrintOp(ctx, instr->Operand.Ptr()); ctx.Body << ";\n"; } }; class ArrayOutputStrategy : public OutputStrategy { protected: bool isPatch = false; int arraySize = 0; public: String outputIndex; ArrayOutputStrategy(GLSLCodeGen * pCodeGen, ILWorld * world, bool pIsPatch, int pArraySize, String pOutputIndex) : OutputStrategy(pCodeGen, world) { isPatch = pIsPatch; arraySize = pArraySize; outputIndex = pOutputIndex; } virtual void DeclareOutput(CodeGenContext & ctx, ILStage *) override { for (auto & field : world->OutputType->Members) { if (isPatch) ctx.GlobalHeader << "patch "; ctx.GlobalHeader << "out "; codeGen->PrintDef(ctx.GlobalHeader, field.Value.Type.Ptr(), AddWorldNameSuffix(field.Key, world->Name)); ctx.GlobalHeader << "["; if (arraySize != 0) ctx.GlobalHeader << arraySize; ctx.GlobalHeader<<"]; \n"; } } virtual void ProcessExportInstruction(CodeGenContext & ctx, ExportInstruction * instr) override { ctx.Body << AddWorldNameSuffix(instr->ComponentName, world->Name) << "[" << outputIndex << "] = "; codeGen->PrintOp(ctx, instr->Operand.Ptr()); ctx.Body << ";\n"; } }; class PackedBufferOutputStrategy : public OutputStrategy { public: PackedBufferOutputStrategy(GLSLCodeGen * pCodeGen, ILWorld * world) : OutputStrategy(pCodeGen, world) {} virtual void DeclareOutput(CodeGenContext & ctx, ILStage *) override { for (auto & field : world->OutputType->Members) { ctx.GlobalHeader << "out "; codeGen->PrintDef(ctx.GlobalHeader, field.Value.Type.Ptr(), field.Key); ctx.GlobalHeader << ";\n"; } } virtual void ProcessExportInstruction(CodeGenContext & ctx, ExportInstruction * exportInstr) override { String conversionFunction; int size = 0; String typeName = exportInstr->Type->ToString(); if (typeName == "int") { conversionFunction = "intBitsToFloat"; size = 1; } else if (typeName == "ivec2") { conversionFunction = "intBitsToFloat"; size = 2; } else if (typeName == "ivec3") { conversionFunction = "intBitsToFloat"; size = 3; } else if (typeName == "ivec4") { conversionFunction = "intBitsToFloat"; size = 4; } else if (typeName == "uint") { conversionFunction = "uintBitsToFloat"; size = 1; } else if (typeName == "uvec2") { conversionFunction = "uintBitsToFloat"; size = 2; } else if (typeName == "uvec3") { conversionFunction = "uintBitsToFloat"; size = 3; } else if (typeName == "uvec4") { conversionFunction = "uintBitsToFloat"; size = 4; } else if (typeName == "float") { conversionFunction = ""; size = 1; } else if (typeName == "vec2") { conversionFunction = ""; size = 2; } else if (typeName == "vec3") { conversionFunction = ""; size = 3; } else if (typeName == "vec4") { conversionFunction = ""; size = 4; } else if (typeName == "mat3") { conversionFunction = ""; size = 9; } else if (typeName == "mat4") { conversionFunction = ""; size = 16; } else { codeGen->getSink()->diagnose(CodePosition(), Diagnostics::importingFromPackedBufferUnsupported, typeName); } auto recType = world->OutputType.Ptr(); int recTypeSize = 0; EnumerableDictionary memberOffsets; for (auto & member : recType->Members) { memberOffsets[member.Key] = recTypeSize; recTypeSize += member.Value.Type->GetVectorSize(); } for (int i = 0; i < size; i++) { ctx.Body << "sysOutputBuffer.content[gl_InvocationId.x * " << recTypeSize << " + " + memberOffsets[exportInstr->ComponentName]() << "] = " << conversionFunction << "("; codeGen->PrintOp(ctx, exportInstr->Operand.Ptr()); if (size <= 4) ctx.Body << "[" << i << "]"; else { int width = size == 9 ? 3 : 4; ctx.Body << "[" << i / width << "][" << i % width << "]"; } ctx.Body << ");\n"; } } }; OutputStrategy * GLSLCodeGen::CreateStandardOutputStrategy(ILWorld * world, String layoutPrefix) { return new StandardOutputStrategy(this, world, layoutPrefix); } OutputStrategy * GLSLCodeGen::CreatePackedBufferOutputStrategy(ILWorld * world) { return new PackedBufferOutputStrategy(this, world); } OutputStrategy * GLSLCodeGen::CreateArrayOutputStrategy(ILWorld * world, bool pIsPatch, int pArraySize, String arrayIndex) { return new ArrayOutputStrategy(this, world, pIsPatch, pArraySize, arrayIndex); } CodeGenBackend * CreateGLSLCodeGen() { return new GLSLCodeGen(false, false); } CodeGenBackend * CreateGLSL_VulkanCodeGen() { return new GLSLCodeGen(true, false); } CodeGenBackend * CreateGLSL_VulkanOneDescCodeGen() { return new GLSLCodeGen(true, true); } } } ================================================ FILE: Source/SpireCore/GetDependencyVisitor.cpp ================================================ #include "GetDependencyVisitor.h" namespace Spire { namespace Compiler { EnumerableHashSet GetDependentComponents(SyntaxNode * tree) { GetDependencyVisitor visitor; tree->Accept(&visitor); return visitor.Result; } RefPtr GetDependencyVisitor::VisitImportExpression(ImportExpressionSyntaxNode * syntax) { for (auto & comp : syntax->ImportOperatorDef->Usings) Result.Add(ComponentDependency(comp, nullptr)); Result.Add(ComponentDependency(syntax->ComponentUniqueName, syntax->ImportOperatorDef.Ptr())); return SyntaxVisitor::VisitImportExpression(syntax); } RefPtr GetDependencyVisitor::VisitMemberExpression(MemberExpressionSyntaxNode * member) { RefPtr refCompObj; if (member->Tags.TryGetValue("ComponentReference", refCompObj)) { auto refComp = refCompObj.As().Ptr(); Result.Add(ComponentDependency(refComp->Content, nullptr)); } else member->BaseExpression->Accept(this); return member; } RefPtr GetDependencyVisitor::VisitVarExpression(VarExpressionSyntaxNode * var) { RefPtr refCompObj; if (var->Tags.TryGetValue("ComponentReference", refCompObj)) { auto refComp = refCompObj.As().Ptr(); Result.Add(ComponentDependency(refComp->Content, nullptr)); } return var; } } } ================================================ FILE: Source/SpireCore/GetDependencyVisitor.h ================================================ #ifndef GET_DEPENDENCY_VISITOR_H #define GET_DEPENDENCY_VISITOR_H #include "VariantIR.h" #include "Closure.h" #include "StringObject.h" namespace Spire { namespace Compiler { class ComponentDependency { public: String ReferencedComponent; ImportOperatorDefSyntaxNode * ImportOperator = nullptr; ComponentDependency() = default; ComponentDependency(String compName, ImportOperatorDefSyntaxNode * impOp) : ReferencedComponent(compName), ImportOperator(impOp) {} int GetHashCode() { return ReferencedComponent.GetHashCode() ^ (int)(CoreLib::PtrInt)(void*)(ImportOperator); } bool operator == (const ComponentDependency & other) { return ReferencedComponent == other.ReferencedComponent && ImportOperator == other.ImportOperator; } }; class GetDependencyVisitor : public SyntaxVisitor { public: EnumerableHashSet Result; GetDependencyVisitor() : SyntaxVisitor(nullptr) {} RefPtr VisitVarExpression(VarExpressionSyntaxNode * var) override; RefPtr VisitMemberExpression(MemberExpressionSyntaxNode * member) override; RefPtr VisitImportExpression(ImportExpressionSyntaxNode * syntax) override; }; EnumerableHashSet GetDependentComponents(SyntaxNode * tree); } } #endif ================================================ FILE: Source/SpireCore/HLSLCodeGen.cpp ================================================ #include "CLikeCodeGen.h" #include "../CoreLib/Tokenizer.h" #include "Syntax.h" #include "Naming.h" #include "TypeLayout.h" #include using namespace CoreLib::Basic; namespace Spire { namespace Compiler { class HLSLCodeGen : public CLikeCodeGen { private: bool useD3D12Registers = false; protected: OutputStrategy * CreateStandardOutputStrategy(ILWorld * world, String layoutPrefix) override; OutputStrategy * CreatePackedBufferOutputStrategy(ILWorld * world) override; OutputStrategy * CreateArrayOutputStrategy(ILWorld * world, bool pIsPatch, int pArraySize, String arrayIndex) override; LayoutRule GetDefaultLayoutRule() override { return LayoutRule::HLSL; } void PrintRasterPositionOutputWrite(CodeGenContext & ctx, ILOperand * operand) override { ctx.Body << "stage_output.sv_position = "; PrintOp(ctx, operand); ctx.Body << ";\n"; } void PrintMatrixMulInstrExpr(CodeGenContext & ctx, ILOperand* op0, ILOperand* op1) override { // The matrix-vector, vector-matrix, and matrix-matrix product // operation is written with the `*` operator in GLSL, but // is handled by the built-in function `mul()` in HLSL. // // This function is called by the code generator for that op // and allows us to print it appropriately. ctx.Body << "mul("; PrintOp(ctx, op1); ctx.Body << ", "; PrintOp(ctx, op0); ctx.Body << ")"; } void PrintStandardInputReference(StringBuilder& sb, ILRecordType* /*recType*/, String inputName, String componentName) override { sb << "stage_input/*standard*/"; } void PrintStandardArrayInputReference(StringBuilder& sb, ILRecordType* /*recType*/, String inputName, String componentName) override { sb << "stage_input/*array*/"; } void PrintPatchInputReference(StringBuilder& sb, ILRecordType* /*recType*/, String inputName, String componentName) override { sb << "stage_input_patch/*patch*/." << inputName; } void PrintDefaultInputReference(StringBuilder& sb, ILRecordType* /*recType*/, String inputName, String componentName) override { String declName = componentName; sb << declName; } void PrintSystemVarReference(CodeGenContext & ctx, StringBuilder& sb, String inputName, ExternComponentCodeGenInfo::SystemVarType systemVar) override { ctx.UsedSystemInputs.Add(systemVar); switch(systemVar) { case ExternComponentCodeGenInfo::SystemVarType::FragCoord: sb << "sv_FragPosition"; break; case ExternComponentCodeGenInfo::SystemVarType::TessCoord: sb << "sv_DomainLocation"; break; case ExternComponentCodeGenInfo::SystemVarType::InvocationId: sb << "sv_ThreadID"; break; case ExternComponentCodeGenInfo::SystemVarType::ThreadId: sb << "sv_GlobalThreadID.x"; break; case ExternComponentCodeGenInfo::SystemVarType::PatchVertexCount: // TODO(tfoley): there is no equivalent of this in HLSL sb << "sv_InputControlPointCount"; break; case ExternComponentCodeGenInfo::SystemVarType::PrimitiveId: sb << "sv_PrimitiveID"; break; case ExternComponentCodeGenInfo::SystemVarType::InstanceId: sb << "sv_InstanceID"; break; default: sb << inputName; break; } } void PrintCallInstrExprForTarget(CodeGenContext & ctx, CallInstruction * instr, String const& name) override { // Currently, all types are internally named based on their GLSL equivalent, so // for HLSL output we go ahead and maintain a big table to remap the names. // // Note: for right now, this is just a linear array, with no particular sorting. // Eventually it should be turned into a hash table for performance, or at least // just be kept sorted so that we can use a binary search. // // Note 2: Well, actually, the Right Answer is for the type representation to // be better than just a string, so that we don't have to do this string->string map. static const struct { char const* glslName; char const* hlslName; } kNameRemaps[] = { { "vec2", "*float2" }, { "vec3", "*float3" }, { "vec4", "*float4" }, { "ivec2", "*int2" }, { "ivec3", "*int3" }, { "ivec4", "*int4" }, { "uvec2", "*uint2" }, { "uvec3", "*uint3" }, { "uvec4", "*uint4" }, { "mat3", "float3x3" }, { "mat4", "float4x4" }, { "sampler2D", "Texture2D" }, { "sampler2DArray", "Texture2DArray" }, { "samplerCube", "TextureCube" }, { "sampler2DShadow", "Texture2D" }, { "sampler2DArrayShadow", "Texture2DArray" }, { "samplerCubeShadow", "TextureCube" }, }; for(auto remap : kNameRemaps) { if(strcmp(name.Buffer(), remap.glslName) == 0) { char const* hlslName = remap.hlslName; if(*hlslName == '*') { hlslName++; // Note(tfoley): The specific case we are dealing with right // now is that constructing a vector from a scalar value // *must* be expressed as a cast in HLSL, while in GLSL // it *must* be expressed as a constructor call. We // intercept the call to a constructor here in the // specific case where it has one argument, and print // it differently if(instr->Arguments.Count() == 1) { ctx.Body << "((" << hlslName << ") "; PrintOp(ctx, instr->Arguments[0].Ptr()); ctx.Body << ")"; return; } } PrintDefaultCallInstrExpr(ctx, instr, hlslName); return; } } PrintDefaultCallInstrExpr(ctx, instr, name); } void PrintTextureCall(CodeGenContext & ctx, CallInstruction * instr) { // texture functions are defined based on HLSL, so this is trivial // internally, texObj.Sample(sampler_obj, uv, ..) is represented as Sample(texObj, sampler_obj, uv, ...) // so we need to lift first argument to the front PrintOp(ctx, instr->Arguments[0].Ptr(), true); ctx.Body << "." << instr->Function; ctx.Body << "("; for (int i = 1; i < instr->Arguments.Count(); i++) { PrintOp(ctx, instr->Arguments[i].Ptr()); if (i < instr->Arguments.Count() - 1) ctx.Body << ", "; } ctx.Body << ")"; } void PrintProjectInstrExpr(CodeGenContext & ctx, ProjectInstruction * proj) override { // project component out of record type. PrintOp(ctx, proj->Operand.Ptr()); ctx.Body << "." << proj->ComponentName; } void PrintTypeName(StringBuilder& sb, ILType* type) override { // Currently, all types are internally named based on their GLSL equivalent, so // for HLSL output we go ahead and maintain a big table to remap the names. // // Note: for right now, this is just a linear array, with no particular sorting. // Eventually it should be turned into a hash table for performance, or at least // just be kept sorted so that we can use a binary search. // // Note 2: Well, actually, the Right Answer is for the type representation to // be better than just a string, so that we don't have to do this string->string map. static const struct { char const* glslName; char const* hlslName; } kNameRemaps[] = { { "vec2", "float2" }, { "vec3", "float3" }, { "vec4", "float4" }, { "ivec2", "int2" }, { "ivec3", "int3" }, { "ivec4", "int4" }, { "uvec2", "uint2" }, { "uvec3", "uint3" }, { "uvec4", "uint4" }, { "mat3", "float3x3" }, { "mat4", "float4x4" }, { "sampler2D", "Texture2D" }, { "sampler2DArray", "Texture2DArray" }, { "samplerCube", "TextureCube" }, { "sampler2DShadow", "Texture2D" }, { "sampler2DArrayShadow", "Texture2DArray" }, { "samplerCubeShadow", "TextureCube" }, { "samplerCubeArray", "TextureCubeArray" }, { "samplerCubeArrayShadow", "TextureCubeArray" }, { "sampler3D", "Texture3D" } }; String typeName = type->ToString(); for(auto remap : kNameRemaps) { if(strcmp(typeName.Buffer(), remap.glslName) == 0) { sb << remap.hlslName; return; } } // If we don't find the type in our map, then that either means we missed a case, // or this is a user-defined type. I don't see an obvious way to check which of // those cases we are in, so we will just fall back to outputting the "GLSL name" here. sb << type->ToString(); } String GetLastSegment(String accessName) { int idx = accessName.LastIndexOf('.'); if (idx == -1) return accessName; return accessName.SubString(idx + 1, accessName.Length() - idx - 1); } void DefineCBufferParameterFields(CodeGenContext & sb, ILModuleParameterSet * module, int & itemsDeclaredInBlock) { // We declare an inline struct inside the `cbuffer` to ensure that // the members have an appropriate prefix on their name. sb.GlobalHeader << "struct {\n"; int index = 0; for (auto & field : module->Parameters) { auto bindableResType = field.Value->Type->GetBindableResourceType(); if (bindableResType != BindableResourceType::NonBindable) continue; String declName = field.Key; PrintDef(sb.GlobalHeader, field.Value->Type.Ptr(), declName); itemsDeclaredInBlock++; sb.GlobalHeader << ";\n"; index++; } // define sub parameter fields for (auto & subParam : module->SubModules) { int subItemsDeclaredInBlock = 0; int declarationStart = sb.GlobalHeader.Length(); DefineCBufferParameterFields(sb, subParam.Ptr(), subItemsDeclaredInBlock); if (subItemsDeclaredInBlock == 0) { sb.GlobalHeader.Remove(declarationStart, sb.GlobalHeader.Length() - declarationStart); } } sb.GlobalHeader << "} " << GetLastSegment(module->BindingName) << ";\n"; } void DefineBindableParameterFields(CodeGenContext & sb, ILModuleParameterSet * module, int descSetId, int & tCount, int & sCount, int & uCount, int & cCount) { module->TextureBindingStartIndex = tCount; module->SamplerBindingStartIndex = sCount; module->StorageBufferBindingStartIndex = uCount; module->UniformBindingStartIndex = cCount; for (auto & field : module->Parameters) { auto bindableResType = field.Value->Type->GetBindableResourceType(); if (bindableResType == BindableResourceType::NonBindable) continue; PrintDef(sb.GlobalHeader, field.Value->Type.Ptr(), EscapeCodeName(module->BindingName + "_" + field.Key)); if (field.Value->BindingPoints.Count()) { sb.GlobalHeader << ": register("; switch (bindableResType) { case BindableResourceType::Texture: sb.GlobalHeader << "t"; break; case BindableResourceType::Sampler: sb.GlobalHeader << "s"; break; case BindableResourceType::StorageBuffer: sb.GlobalHeader << "u"; break; case BindableResourceType::Buffer: sb.GlobalHeader << "c"; break; default: throw NotImplementedException(); } if (useD3D12Registers) { switch (bindableResType) { case BindableResourceType::Texture: sb.GlobalHeader << tCount; tCount++; break; case BindableResourceType::Sampler: sb.GlobalHeader << sCount; sCount++; break; case BindableResourceType::StorageBuffer: sb.GlobalHeader << uCount; uCount++; break; case BindableResourceType::Buffer: sb.GlobalHeader << cCount; cCount++; break; } sb.GlobalHeader << ", space" << descSetId; } else { sb.GlobalHeader << field.Value->BindingPoints.First(); } sb.GlobalHeader << ")"; } sb.GlobalHeader << ";\n"; } for (auto & subModule : module->SubModules) DefineBindableParameterFields(sb, subModule.Ptr(), descSetId, tCount, sCount, uCount, cCount); } bool DetermineParameterFieldOffset(ILModuleParameterSet * module, int & ptr) { bool firstFieldEncountered = false; module->UniformBufferOffset = ptr; auto layout = GetLayoutRulesImpl(LayoutRule::HLSL); auto structInfo = layout->BeginStructLayout(); for (auto & field : module->Parameters) { if (field.Value->Type->GetBindableResourceType() == BindableResourceType::NonBindable) { auto flayout = GetLayout(field.Value->Type.Ptr(), LayoutRule::HLSL); field.Value->BufferOffset = (int)(layout->AddStructField(&structInfo, flayout)); field.Value->Size = (int)flayout.size; if (!firstFieldEncountered) { module->UniformBufferOffset = field.Value->BufferOffset; firstFieldEncountered = true; } } for (auto & subModule : module->SubModules) firstFieldEncountered = DetermineParameterFieldOffset(subModule.Ptr(), ptr) | firstFieldEncountered; } layout->EndStructLayout(&structInfo); module->BufferSize = (int)structInfo.size; return firstFieldEncountered; } void GenerateShaderParameterDefinition(CodeGenContext & sb, ILShader * shader) { // first pass: figure out buffer offsets and alignments for (auto module : shader->ModuleParamSets) { if (!module.Value->IsTopLevel) continue; int ptr = 0; DetermineParameterFieldOffset(module.Value.Ptr(), ptr); } for (auto module : shader->ModuleParamSets) { if (!module.Value->IsTopLevel) continue; // TODO: this generates D3D11 style binding, should update to generate D3D12 root signature declaration auto moduleName = EscapeCodeName(module.Value->BindingName); int declarationStart = sb.GlobalHeader.Length(); int itemsDeclaredInBlock = 0; sb.GlobalHeader << "cbuffer buf" << moduleName; if (module.Value->DescriptorSetId != -1) sb.GlobalHeader << " : register(b" << module.Value->DescriptorSetId << ")"; sb.GlobalHeader << "\n{\n"; DefineCBufferParameterFields(sb, module.Value.Ptr(), itemsDeclaredInBlock); sb.GlobalHeader << "};\n"; if (itemsDeclaredInBlock == 0) { sb.GlobalHeader.Remove(declarationStart, sb.GlobalHeader.Length() - declarationStart); } int textureReg = 0; int samplerReg = 0; int uReg = 0; int cReg = 0; DefineBindableParameterFields(sb, module.Value.Ptr(), module.Value->DescriptorSetId, textureReg, samplerReg, uReg, cReg); } } virtual void PrintParameterReference(StringBuilder& sb, ILModuleParameterInstance * param) override { if (param->Type->GetBindableResourceType() == BindableResourceType::NonBindable) { sb << param->Module->BindingName << "." << param->Name; } else { sb << EscapeCodeName(param->Module->BindingName + "_" + param->Name); } } void DeclareStandardInputRecord(CodeGenContext & sb, const ILObjectDefinition & input, bool /*isVertexShader*/) override { auto info = ExtractExternComponentInfo(input); extCompInfo[input.Name] = info; auto recType = ExtractRecordType(input.Type.Ptr()); assert(recType); DeclareRecordTypeStruct(sb, recType); } void DeclarePatchInputRecord(CodeGenContext & sb, const ILObjectDefinition & input, bool isVertexShader) override { // In HLSL, both standard input/output and per-patch input/output are passed as ordinary `struct` types. DeclareStandardInputRecord(sb, input, isVertexShader); } void GenerateDomainShaderAttributes(StringBuilder & sb, ILStage * stage) { StageAttribute val; if (stage->Attributes.TryGetValue("Domain", val)) sb << "[domain(\"" << ((val.Value == "quads") ? "quad" : "tri") << "\")]\n"; else sb << "[domain(\"tri\")]\n"; if (val.Value != "triangles" && val.Value != "quads") getSink()->diagnose(val.Position, Diagnostics::invalidTessellationDomain); } void PrintHeaderBoilerplate(CodeGenContext& ctx) { // The way that we assign semantics may generate a warning, // and rather than clear it up with more complicated codegen, // we choose to just disable it (since we control all the // semantics anyway). ctx.GlobalHeader << "#pragma warning(disable: 3576)\n"; // In order to be a portable shading language, Spire needs // to make some basic decisions about the semantics of // matrix operations so they are consistent between APIs. // // The default interpretation in each language is: // GLSL: column-major storage, column-major semantics // HLSL: column-major storage, row-major semantics // // We can't change the semantics, but we *can* change // the storage layout, and making it be row-major in // HLSL ensures that the actual behavior of a shader // is consistent between APIs. ctx.GlobalHeader << "#pragma pack_matrix( row_major )\n"; } StageSource GenerateSingleWorldShader(ILProgram * program, ILShader * shader, ILStage * stage) override { // This entry point is used to generate a Vertex, Fragment, // Domain, or Compute Shader, since they all amount to // a single world. // // TODO(tfoley): This code actually doesn't work for compute, // since it currently assumes there is always going to be // "varying" stage input/output. // // TODO(tfoley): Honestly, there is almost zero value in trying // to share this code, and what little sharing there is could // be expressed just as well by having a differentry codegen // entry point per stage type, with lower-level shared routines // they can call into. // TODO(tfoley): Ther are no bindles textures in HLSL, so I'm // not sure what to do with this flag. useBindlessTexture = stage->Attributes.ContainsKey("BindlessTexture"); StageSource rs; CodeGenContext ctx; PrintHeaderBoilerplate(ctx); GenerateStructs(ctx.GlobalHeader, program); GenerateShaderParameterDefinition(ctx, shader); StageAttribute worldName; RefPtr world = nullptr; if (stage->Attributes.TryGetValue("World", worldName)) { if (!shader->Worlds.TryGetValue(worldName.Value, world)) errWriter->diagnose(worldName.Position, Diagnostics::worldIsNotDefined, worldName.Value); } else errWriter->diagnose(stage->Position, Diagnostics::stageShouldProvideWorldAttribute, stage->StageType); if (!world) return rs; GenerateReferencedFunctions(ctx.GlobalHeader, program, MakeArrayView(world.Ptr())); extCompInfo.Clear(); ILRecordType* stageInputType = nullptr; ILRecordType* dsCornerPointType = nullptr; int dsCornerPointCount = 0; ILRecordType* dsPatchType = nullptr; ILObjectDefinition* dsCornerPointInput = nullptr; ILObjectDefinition* dsPatchInput = nullptr; // We start by emitting whatever declarations the input worlds // need, and along the way we try to capture the components/ // records being used as input, so that we can refer to them // appropriately. // // Note(tfoley): It seems awkward to find these worlds/records // that we need by search, whereas the "primary" world for the // shader is passed to us more explicitly. for (auto & input : world->Inputs) { DeclareInput(ctx, input, stage->StageType == "VertexShader"); // We need to detect the world that represents the ordinary stage input... // TODO(tfoley): It seems like this is logically part of the stage definition. auto info = ExtractExternComponentInfo(input); if(info.DataStructure == ExternComponentCodeGenInfo::DataStructureType::StandardInput) { auto recType = ExtractRecordType(input.Type.Ptr()); if(recType) { stageInputType = recType; } } else if(info.DataStructure == ExternComponentCodeGenInfo::DataStructureType::Patch) { auto recType = ExtractRecordType(input.Type.Ptr()); if(recType) { if(info.IsArray) { dsCornerPointInput = &input; dsCornerPointType = recType; dsCornerPointCount = info.ArrayLength; } else { dsPatchInput = &input; dsPatchType = recType; } } } } if(!stageInputType) { errWriter->diagnose(stage->Position, Diagnostics::stageDoesntHaveInputWorld, stage->StageType); } // For a domain shader, we need to know how many corners the // domain has (triangle or quadrilateral), so that we can // declare an output array of appropriate size. StageAttribute controlPointCount; int cornerCount = 3; if(stage->StageType == "DomainShader") { if (!stage->Attributes.TryGetValue("ControlPointCount", controlPointCount)) { errWriter->diagnose(stage->Position, Diagnostics::domainShaderRequiresControlPointCount); } StageAttribute val; if(stage->Attributes.TryGetValue("Domain", val)) { if(val.Value == "quads") cornerCount = 4; else if(val.Value == "triangles") cornerCount = 3; } } outputStrategy->DeclareOutput(ctx, stage); ctx.codeGen = this; world->Code->NameAllInstructions(); GenerateCode(ctx, world->Code.Ptr()); // For shader types that might output the special `SV_Position` // output, we check if the stage in the pipeline actually // declares this output, and emit the logic as needed. if (stage->StageType == "VertexShader" || stage->StageType == "DomainShader") GenerateVertexShaderEpilog(ctx, world.Ptr(), stage); StringBuilder sb; sb << ctx.GlobalHeader.ProduceString(); // We always declare our shader entry point as outputting a // single `struct` value, for simplicity. To make this // work, we generate a combined `struct` that comprises // the user-declared outputs (in a nested `struct`) along // with any system-interpreted outputs we need. sb << "struct T" << world->OutputType->TypeName << "Ext\n{\n"; sb << "T" << world->OutputType->TypeName << " user"; // The fragment shader needs to use the specific output // semantic `SV_Target` as expected by the HLSL compiler. // Because the `user` field is a `struct` this semantic // will recursively propagate to all of its fields. // // All other stage types will just use the default semantics // already applied to the fields of the output `struct`. if(stage->StageType == "FragmentShader") { sb << " : SV_Target"; } sb << ";\n"; // We emit any required system-output semantics here. // For now we are just handling `SV_Position`, but // values like fragment shader depth output, etc. // would also go here. if(stage->Attributes.TryGetValue("Position")) { sb << "float4 sv_position : SV_Position;\n"; } sb << "};\n"; if(dsPatchType || dsCornerPointType) { // A domain shader receives two kinds of input: per-patch // and per-control-point. The per-control-point input // appears as the ordinary input, from the perspective // of the Spire front-end, so we need to declare the // per-patch input more explicitly. // // Similar to what we do with the `*Ext` contrivance // above, we are going to output a single `struct` // that combines user-defined and system inputs. sb << "struct TStageInputPatch\n{\n"; if(dsPatchType) { // In order to ensure consistent semantics, we apply // a blanket `P` semantic here to the per-patch input. // This semantic will override any per-field semantics // that got emitted for the record type itself. sb << "T" << dsPatchType->TypeName << " " << dsPatchInput->Name << " : P;\n"; } if(dsCornerPointType) { // Similar to the per-patch case, we declare an array // of records for the per-corner-point data, and // apply a single blanket semantic that will go and // recursively enumerate unique semantics for all // the array elements and fields. sb << "T" << dsCornerPointType->TypeName << " " << dsCornerPointInput->Name << "[" << cornerCount << "] : C;\n"; } // Note: HLSL requires tessellation level to be declared // as an input to the Domain Shader, even if it is unused // TODO(tfoley): This repeated matching on the `Domain` // attribute by string comparison is dangerous, and needs // to be handled more centrally and robustly. StageAttribute val; if(stage->Attributes.TryGetValue("Domain", val) && (val.Value == "quads")) { sb << " float sv_TessFactors[4] : SV_TessFactor;\n"; sb << " float sv_InsideTessFactors[2] : SV_InsideTessFactor;\n"; } else { sb << " float sv_TessFactors[3] : SV_TessFactor;\n"; sb << " float sv_InsideTessFactors[1] : SV_InsideTessFactor;\n"; } sb << "};\n"; } // The domain shader has a few required attributes that need // to be emitted in front of the declaration of `main()`. if(stage->StageType == "DomainShader") { GenerateDomainShaderAttributes(sb, stage); } sb << "T" << world->OutputType->TypeName << "Ext main("; if(stageInputType) { // We need to declare our inputs a bit differently, // depending on the stage we are emitting: // TODO(tfoley): All this string-based matching on stage type seems wrong if(stage->StageType == "DomainShader") { // A domain shader needs to declare an array of input // control points, using the special-purpose generic type // provided by HLSL. sb << "OutputPatchTypeName << ", " << controlPointCount.Value << "> stage_input"; } /* FALCOR Don't treat vertex shader specially... else if (stage->StageType == "VertexShader") { // A vertex shader can declare its input as normal, but // to make matching over vertex attributes with host // code simpler, we apply a blanket `A` semantic here, // so that the individual vertex elements in the input // layout will all get the semantic "A" with sequential // indices starting at zero. sb << "\n T" << stageInputType->TypeName << " stage_input : A"; } FALCOR */ else { // Finally, the default case just uses the semantics // that were automatically assigned to the fields // of the input record type. sb << "\n T" << stageInputType->TypeName << " stage_input"; } } if(dsPatchType || dsCornerPointType) { // For a domain shader, we also need to declare // the per-patch (and per-corner point) input. sb << ",\n TStageInputPatch stage_input_patch"; } // Next we declare any addition system inputs that ended up // being used during code generation. if(ctx.UsedSystemInputs.Contains(ExternComponentCodeGenInfo::SystemVarType::TessCoord)) { sb << ",\n "; StageAttribute val; if(stage->Attributes.TryGetValue("Domain", val)) sb << ((val.Value == "quads") ? "float2" : "float3"); else sb << "float3"; sb << " sv_DomainLocation : SV_DomainLocation"; } if(ctx.UsedSystemInputs.Contains(ExternComponentCodeGenInfo::SystemVarType::FragCoord)) { sb << ",\n float4 sv_FragPosition : SV_Position"; } if(ctx.UsedSystemInputs.Contains(ExternComponentCodeGenInfo::SystemVarType::InstanceId)) { sb << ",\n uint sv_InstanceID : SV_InstanceID"; } sb << ")\n{ \n"; sb << "T" << world->OutputType->TypeName << "Ext stage_output;\n"; sb << ctx.Header.ProduceString() << ctx.Body.ProduceString(); sb << "return stage_output;\n"; sb << "}"; rs.MainCode = sb.ProduceString(); return rs; } void DeclareRecordTypeStruct(CodeGenContext& ctx, ILRecordType* recType) { // By convention, the name of the generated `struct` is // "T" prefixed onto the name of the record type. ctx.GlobalHeader << "struct T" << recType->TypeName << "\n{\n"; int index = 0; for (auto & field : recType->Members) { // As a catch-all, we apply the `nointerpolation` // modifier to all integral types, even though // this really only affects records that flow // through rasterization/setup/interpolation. if (field.Value.Type->IsIntegral()) ctx.GlobalHeader << "nointerpolation "; // Declare the field as a `struct` member String declName = field.Key; PrintDef(ctx.GlobalHeader, field.Value.Type.Ptr(), declName); // We automatically synthesize a semantic for every // field. This will need to match any equivalent // declaration on the input side. // // The semantic must be unique across fields. // We can't simply use a convention like "A0", "A1", ... // because these semantics with a numeric suffix // will not interact nicely with fields of `struct` // or array type. // // We could use the field name to generate a unique // semantic, but this might be long and ugly, and we'd // need to decorate it to avoid accidentally having a // numeric suffix, or an "SV_" prefix. // // Ultimately, the easiest thing to do is to take the // simple "A0", "A1", ... idea and simply add another // "A" onto the end, so that it isn't techically a // numeric suffix. So: "A0A", "A1A", "A2A", ... // // In the case where the field is a `struct` or array // type, the HLSL compiler will then automatically // generate per-field/-element semantics based on // the prefix we gave it, e.g.: "A0A0", "A0A1", ... //FALCOR ctx.GlobalHeader << " : A" << index << "A"; // FALCOR: just use the name instead... ctx.GlobalHeader << " : " << declName; ctx.GlobalHeader << ";\n"; index++; } ctx.GlobalHeader << "};\n"; } // Most of our generated HLSL code can use a single simple output // strategy, which simply declares the output as a `struct` type // (to be used in the declaration of `main()`, and then output // writes so that they reference the fields of that type with // a simple prefix. struct SimpleOutputStrategy : OutputStrategy { HLSLCodeGen* hlslCodeGen; String prefix; SimpleOutputStrategy(HLSLCodeGen* hlslCodeGen, ILWorld* world, String const& prefix) : OutputStrategy(hlslCodeGen, world) , hlslCodeGen(hlslCodeGen) , prefix(prefix) {} virtual void DeclareOutput(CodeGenContext & ctx, ILStage * /*stage*/) override { hlslCodeGen->DeclareRecordTypeStruct(ctx, world->OutputType.Ptr()); } virtual void ProcessExportInstruction(CodeGenContext & ctx, ExportInstruction * instr) override { ctx.Body << prefix << "." << instr->ComponentName << " = "; codeGen->PrintOp(ctx, instr->Operand.Ptr()); ctx.Body << ";\n"; } }; StageSource GenerateHullShader(ILProgram * program, ILShader * shader, ILStage * stage) override { // As a first step, we validate the various attributes required // on a `HullShader` stage declaration. // // Note(tfoley): This logic is mostly copy-pasted from the GLSL // case, and there is a reasonable case to be made that it // should be unified. // StageSource rs; StageAttribute patchWorldName, controlPointWorldName, cornerPointWorldName, domain, innerLevel, outerLevel, numControlPoints; StageAttribute inputControlPointCount; RefPtr patchWorld, controlPointWorld, cornerPointWorld; if (!stage->Attributes.TryGetValue("PatchWorld", patchWorldName)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresPatchWorld); return rs; } if (!shader->Worlds.TryGetValue(patchWorldName.Value, patchWorld)) errWriter->diagnose(patchWorldName.Position, Diagnostics::worldIsNotDefined, patchWorldName.Value); if (!stage->Attributes.TryGetValue("ControlPointWorld", controlPointWorldName)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresControlPointWorld); return rs; } if (!shader->Worlds.TryGetValue(controlPointWorldName.Value, controlPointWorld)) errWriter->diagnose(controlPointWorldName.Position, Diagnostics::worldIsNotDefined, controlPointWorldName.Value); if (!stage->Attributes.TryGetValue("CornerPointWorld", cornerPointWorldName)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresCornerPointWorld); return rs; } if (!shader->Worlds.TryGetValue(cornerPointWorldName.Value, cornerPointWorld)) errWriter->diagnose(cornerPointWorldName.Position, Diagnostics::worldIsNotDefined, cornerPointWorldName.Value); if (!stage->Attributes.TryGetValue("Domain", domain)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresDomain); return rs; } if (domain.Value != "triangles" && domain.Value != "quads") { errWriter->diagnose(domain.Position, Diagnostics::invalidTessellationDomian); return rs; } if (!stage->Attributes.TryGetValue("TessLevelOuter", outerLevel)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresTessLevelOuter); return rs; } if (!stage->Attributes.TryGetValue("TessLevelInner", innerLevel)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresTessLevelInner); return rs; } if (!stage->Attributes.TryGetValue("InputControlPointCount", inputControlPointCount)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresInputControlPointCount); return rs; } if (!stage->Attributes.TryGetValue("ControlPointCount", numControlPoints)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresControlPointCount); return rs; } // Note(tfoley): The needs of HLSL codegen forced me to add // a few more required attributes, and we probably need to // decide whether to always require these (for portability) // or only require them when generating HLSL. // StageAttribute partitioning; if(!stage->Attributes.TryGetValue("Partitioning", partitioning)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresPartitioning); return rs; } StageAttribute outputTopology; if(!stage->Attributes.TryGetValue("OutputTopology", outputTopology)) { errWriter->diagnose(stage->Position, Diagnostics::hullShaderRequiresOutputTopology); return rs; } // TODO(tfoley): Any reason to include an optional // `maxtessfactor` attribute? CodeGenContext ctx; ctx.codeGen = this; List worlds; worlds.Add(patchWorld.Ptr()); worlds.Add(controlPointWorld.Ptr()); worlds.Add(cornerPointWorld.Ptr()); PrintHeaderBoilerplate(ctx); int cornerCount = 3; if(domain.Value == "triangles") cornerCount = 3; else if(domain.Value == "quads") cornerCount = 4; GenerateStructs(ctx.GlobalHeader, program); GenerateShaderParameterDefinition(ctx, shader); GenerateReferencedFunctions(ctx.GlobalHeader, program, worlds.GetArrayView()); // As in the single-world case, we need to emit declarations // for any inputs to the stage, but unlike that case we have // multiple worlds to deal with. // // We maintain a set of inputs encountered so far, so that // don't re-declare any given input. HashSet declaredInputs; // Similar to the single-world case, we try to capture // some information about inputs so that we can use it // to inform code generation later. String perCornerIteratorInputName = "perCornerIterator"; ILRecordType* coarseVertexType = nullptr; // for (auto & input : controlPointWorld->Inputs) { if(declaredInputs.Add(input.Name)) { DeclareInput(ctx, input, false); auto info = ExtractExternComponentInfo(input); if(info.DataStructure == ExternComponentCodeGenInfo::DataStructureType::StandardInput) { auto recType = ExtractRecordType(input.Type.Ptr()); if(recType) coarseVertexType = recType; } } } for (auto & input : patchWorld->Inputs) { if (declaredInputs.Add(input.Name)) DeclareInput(ctx, input, false); } for (auto & input : cornerPointWorld->Inputs) { if(declaredInputs.Add(input.Name)) { DeclareInput(ctx, input, false); if(input.Attributes.ContainsKey("PerCornerIterator")) { perCornerIteratorInputName = input.Name; } } } // HLSL requires two entry points for the Hull Shader: a // "patch-constant" function and the ordinary `main()` // entry point (which runs per-control-point). // // We start code generation with the "patch-constant" // function, which we use for per-patch and per-corner // computation. // Perform per-corner computation cornerPointWorld->Code->NameAllInstructions(); StringBuilder cornerPointOutputPrefix; cornerPointOutputPrefix << "stage_output.corners[" << perCornerIteratorInputName << "]"; outputStrategy = new SimpleOutputStrategy(this, cornerPointWorld.Ptr(), cornerPointOutputPrefix.ProduceString()); outputStrategy->DeclareOutput(ctx, stage); // Note(tfoley): We use the `[unroll]` attribute here, because // the HLSL compiler will end up unrolling this loop anyway, // and we'd rather not get their warning about it. ctx.Body << "[unroll] for (uint " << perCornerIteratorInputName << " = 0; " << perCornerIteratorInputName << " < " << cornerCount << "; " << perCornerIteratorInputName << "++)\n{\n"; GenerateCode(ctx, cornerPointWorld->Code.Ptr()); auto debugStr = cornerPointWorld->Code->ToString(); ctx.Body << "}\n"; outputStrategy = NULL; // Perform per-patch computation patchWorld->Code->NameAllInstructions(); outputStrategy = CreateStandardOutputStrategy(patchWorld.Ptr(), "patch"); outputStrategy->DeclareOutput(ctx, stage); GenerateCode(ctx, patchWorld->Code.Ptr()); // Compute the number of edges and interior axes we need to deal with. StageAttribute val; int tessFactorCount = 3; int insideFactorCount = 1; if(stage->Attributes.TryGetValue("Domain", val) && (val.Value == "quads")) { tessFactorCount = 4; insideFactorCount = 2; } else { tessFactorCount = 3; insideFactorCount = 1; } // Generate code to set tessellation factors. // // TODO(tfoley): This is written as a search over worlds, // whereas I would have expected the tess factors to // be expected in a fixed world (e.g., @PatchEdge, // and then @PatchInterior). This should probalby get // cleaned up. // // Note(tfoley): I swapped the order from what the GLSL // case does, so that we output the edge factors before // the interior one(s). This doesn't matter right now, // but in practice many adaptive schemes will want to // compute the interior factor(s) from the edge ones, // so this ordering would in theory be conducive to that. bool found = false; for (auto & world : worlds) { ILOperand * operand; if (world->Components.TryGetValue(outerLevel.Value, operand)) { for (int i = 0; i < tessFactorCount; i++) { // TODO(tfoley): is this needlessly re-computing the operand multiple times? ctx.Body << "stage_output.sv_TessFactors[" << i << "] = "; PrintOp(ctx, operand); ctx.Body << "[" << i << "];\n"; } found = true; break; } } if (!found) errWriter->diagnose(outerLevel.Position, Diagnostics::componentNotDefined, outerLevel.Value); found = false; for (auto & world : worlds) { ILOperand * operand; if (world->Components.TryGetValue(innerLevel.Value, operand)) { for (int i = 0; i < insideFactorCount; i++) { ctx.Body << "stage_output.sv_InsideTessFactors[" << i << "] = "; PrintOp(ctx, operand); ctx.Body << "[" << i << "];\n"; } found = true; break; } } if (!found) errWriter->diagnose(innerLevel.Position, Diagnostics::componentNotDefined, innerLevel.Value); // Now surround the code with the boilerplate needed to // make a real Hull Shader "patch constant function" StringBuilder patchMain; patchMain << "struct SPIRE_PatchOutput\n{\n"; patchMain << "T" << patchWorld->OutputType->TypeName << " user : P;\n"; patchMain << "T" << cornerPointWorld->OutputType->TypeName << " corners[" << cornerCount << "] : C;\n"; patchMain << " float sv_TessFactors[" << tessFactorCount << "] : SV_TessFactor;\n"; patchMain << " float sv_InsideTessFactors[" << insideFactorCount << "] : SV_InsideTessFactor;\n"; patchMain << "};\n"; patchMain << "SPIRE_PatchOutput SPIRE_patchOutput("; if (coarseVertexType) { patchMain << " InputPatchTypeName << ", " << inputControlPointCount.Value << "> stage_input\n"; } // TODO(tfoley): provide other input shere like SV_PrimitiveID patchMain << ")\n{\n"; patchMain << "SPIRE_PatchOutput stage_output;\n"; patchMain << ctx.Header.ProduceString() << ctx.Body.ProduceString(); patchMain << "return stage_output;\n"; patchMain << "}\n"; // After we are done outputting the per-patch entry point, // we move on to the per-control-point one. // // Note that calling `ProduceString()` on the `Header` and // `Body` builders above has cleared them out for us. controlPointWorld->Code->NameAllInstructions(); outputStrategy = new SimpleOutputStrategy(this, controlPointWorld.Ptr(), "stage_output"); outputStrategy->DeclareOutput(ctx, stage); GenerateCode(ctx, controlPointWorld->Code.Ptr()); StringBuilder controlPointMain; // HLSL requires a bunch of attributres in front of the // Hull Shader `main()` (the per-control-point function) // These are effectively binding the state of the // fixed-function tessellator (rather than have a bunch of API // state for it). // Name of the entry point to use for the patch-constant phase controlPointMain << "[patchconstantfunc(\"SPIRE_patchOutput\")]\n"; // Domain for tessellation. controlPointMain << "[domain(\""; if(domain.Value == "quads") { controlPointMain << "quad"; } else if(domain.Value == "triangles") { controlPointMain << "tri"; } else { errWriter->diagnose(domain.Position, Diagnostics::invalidTessellationDomain); return rs; } controlPointMain << "\")]\n"; // Parititoning mode (integer, fractional, etc.) controlPointMain << "[partitioning(\""; if(partitioning.Value == "integer") { controlPointMain << "integer"; } else if(partitioning.Value == "pow2") { controlPointMain << "pow2"; } else if(partitioning.Value == "fractional_even") { controlPointMain << "fractional_even"; } else if(partitioning.Value == "fractional_odd") { controlPointMain << "fractional_odd"; } else { errWriter->diagnose(partitioning.Position, Diagnostics::invalidTessellationPartitioning); return rs; } controlPointMain << "\")]\n"; // Desired output topology, including winding order // for triangles. controlPointMain << "[outputtopology(\""; if(outputTopology.Value == "point") { controlPointMain << "point"; } else if(outputTopology.Value == "line") { controlPointMain << "line"; } else if(outputTopology.Value == "triangle_cw") { controlPointMain << "triangle_cw"; } else if(outputTopology.Value == "triangle_ccw") { controlPointMain << "triangle_ccw"; } else { errWriter->diagnose(partitioning.Position, Diagnostics::invalidTessellationOutputTopology); return rs; } controlPointMain << "\")]\n"; // Number of output control points controlPointMain << "[outputcontrolpoints(" << numControlPoints.Value << ")]\n"; // With all the attributes dealt with, we can emit the actual `main()` routine controlPointMain << "T" << controlPointWorld->OutputType->TypeName << " main("; controlPointMain << " InputPatchTypeName << ", " << inputControlPointCount.Value << "> stage_input"; controlPointMain << ",\n uint sv_ControlPointID : SV_OutputControlPointID"; controlPointMain << ")\n{\n"; controlPointMain << "T" << controlPointWorld->OutputType->TypeName << " stage_output;\n"; controlPointMain << ctx.Header.ProduceString() << ctx.Body.ProduceString(); controlPointMain << "return stage_output;\n"; controlPointMain << "}\n"; StringBuilder sb; sb << ctx.GlobalHeader.ProduceString(); sb << patchMain.ProduceString(); sb << controlPointMain.ProduceString(); rs.MainCode = sb.ProduceString(); return rs; } }; // TODO(tfoley): This code has not been ported or tested. class HLSLArrayOutputStrategy : public OutputStrategy { protected: bool isPatch = false; int arraySize = 0; public: String outputIndex; HLSLArrayOutputStrategy(HLSLCodeGen * pCodeGen, ILWorld * world, bool pIsPatch, int pArraySize, String pOutputIndex) : OutputStrategy(pCodeGen, world) { isPatch = pIsPatch; arraySize = pArraySize; outputIndex = pOutputIndex; } virtual void DeclareOutput(CodeGenContext & ctx, ILStage *) override { ctx.GlobalHeader << "struct T" << world->OutputType->TypeName << "\n{\n"; for (auto & field : world->OutputType->Members) { codeGen->PrintDef(ctx.GlobalHeader, field.Value.Type.Ptr(), field.Key); ctx.GlobalHeader << ";\n"; } ctx.GlobalHeader << "};\n"; } virtual void ProcessExportInstruction(CodeGenContext & ctx, ExportInstruction * instr) override { ctx.Body << AddWorldNameSuffix(instr->ComponentName, world->Name) << "[" << outputIndex << "] = "; codeGen->PrintOp(ctx, instr->Operand.Ptr()); ctx.Body << ";\n"; } }; // TODO(tfoley): This code has not been ported or tested. class HLSLPackedBufferOutputStrategy : public OutputStrategy { public: HLSLPackedBufferOutputStrategy(HLSLCodeGen * pCodeGen, ILWorld * world) : OutputStrategy(pCodeGen, world) {} virtual void DeclareOutput(CodeGenContext & ctx, ILStage *) override { for (auto & field : world->OutputType->Members) { ctx.GlobalHeader << "out "; codeGen->PrintDef(ctx.GlobalHeader, field.Value.Type.Ptr(), field.Key); ctx.GlobalHeader << ";\n"; } } virtual void ProcessExportInstruction(CodeGenContext & ctx, ExportInstruction * exportInstr) override { String conversionFunction; int size = 0; String typeName = exportInstr->Type->ToString(); if (typeName == "int") { conversionFunction = "intBitsToFloat"; size = 1; } else if (typeName == "ivec2") { conversionFunction = "intBitsToFloat"; size = 2; } else if (typeName == "ivec3") { conversionFunction = "intBitsToFloat"; size = 3; } else if (typeName == "ivec4") { conversionFunction = "intBitsToFloat"; size = 4; } else if (typeName == "uint") { conversionFunction = "uintBitsToFloat"; size = 1; } else if (typeName == "uvec2") { conversionFunction = "uintBitsToFloat"; size = 2; } else if (typeName == "uvec3") { conversionFunction = "uintBitsToFloat"; size = 3; } else if (typeName == "uvec4") { conversionFunction = "uintBitsToFloat"; size = 4; } else if (typeName == "float") { conversionFunction = ""; size = 1; } else if (typeName == "vec2") { conversionFunction = ""; size = 2; } else if (typeName == "vec3") { conversionFunction = ""; size = 3; } else if (typeName == "vec4") { conversionFunction = ""; size = 4; } else if (typeName == "mat3") { conversionFunction = ""; size = 9; } else if (typeName == "mat4") { conversionFunction = ""; size = 16; } else { codeGen->getSink()->diagnose(CodePosition(), Diagnostics::importingFromPackedBufferUnsupported, typeName); } auto recType = world->OutputType.Ptr(); int recTypeSize = 0; EnumerableDictionary memberOffsets; for (auto & member : recType->Members) { memberOffsets[member.Key] = recTypeSize; recTypeSize += member.Value.Type->GetVectorSize(); } for (int i = 0; i < size; i++) { ctx.Body << "sysOutputBuffer.content[gl_InvocationId.x * " << recTypeSize << " + " + memberOffsets[exportInstr->ComponentName]() << "] = " << conversionFunction << "("; codeGen->PrintOp(ctx, exportInstr->Operand.Ptr()); if (size <= 4) ctx.Body << "[" << i << "]"; else { int width = size == 9 ? 3 : 4; ctx.Body << "[" << i / width << "][" << i % width << "]"; } ctx.Body << ");\n"; } } }; OutputStrategy * HLSLCodeGen::CreateStandardOutputStrategy(ILWorld * world, String layoutPrefix) { return new HLSLCodeGen::SimpleOutputStrategy(this, world, "stage_output.user"); } OutputStrategy * HLSLCodeGen::CreatePackedBufferOutputStrategy(ILWorld * world) { return new HLSLPackedBufferOutputStrategy(this, world); } OutputStrategy * HLSLCodeGen::CreateArrayOutputStrategy(ILWorld * world, bool pIsPatch, int pArraySize, String arrayIndex) { return new HLSLArrayOutputStrategy(this, world, pIsPatch, pArraySize, arrayIndex); } CodeGenBackend * CreateHLSLCodeGen() { return new HLSLCodeGen(); } } } ================================================ FILE: Source/SpireCore/IL.cpp ================================================ #include "IL.h" #include "../CoreLib/LibIO.h" #include "Syntax.h" #include "CompiledProgram.h" #include "../CoreLib/Tokenizer.h" namespace Spire { namespace Compiler { using namespace CoreLib::IO; RefPtr BaseTypeFromString(CoreLib::Text::TokenReader & parser) { if (parser.LookAhead("int")) return new ILBasicType(ILBaseType::Int); else if (parser.LookAhead("uint")) return new ILBasicType(ILBaseType::UInt); else if (parser.LookAhead("uvec2")) return new ILBasicType(ILBaseType::UInt2); else if (parser.LookAhead("uvec3")) return new ILBasicType(ILBaseType::UInt3); else if (parser.LookAhead("uvec4")) return new ILBasicType(ILBaseType::UInt4); if (parser.LookAhead("float")) return new ILBasicType(ILBaseType::Float); if (parser.LookAhead("vec2")) return new ILBasicType(ILBaseType::Float2); if (parser.LookAhead("vec3")) return new ILBasicType(ILBaseType::Float3); if (parser.LookAhead("vec4")) return new ILBasicType(ILBaseType::Float4); if (parser.LookAhead("ivec2")) return new ILBasicType(ILBaseType::Int2); if (parser.LookAhead("mat3")) return new ILBasicType(ILBaseType::Float3x3); if (parser.LookAhead("mat4")) return new ILBasicType(ILBaseType::Float4x4); if (parser.LookAhead("ivec3")) return new ILBasicType(ILBaseType::Int3); if (parser.LookAhead("ivec4")) return new ILBasicType(ILBaseType::Int4); if (parser.LookAhead("sampler2D") || parser.LookAhead("Texture2D")) return new ILBasicType(ILBaseType::Texture2D); if (parser.LookAhead("samplerCube") || parser.LookAhead("TextureCube")) return new ILBasicType(ILBaseType::TextureCube); if (parser.LookAhead("sampler2DArray") || parser.LookAhead("Texture2DArray")) return new ILBasicType(ILBaseType::Texture2DArray); if (parser.LookAhead("sampler2DShadow") || parser.LookAhead("Texture2DShadow")) return new ILBasicType(ILBaseType::Texture2DShadow); if (parser.LookAhead("samplerCubeShadow") || parser.LookAhead("TextureCubeShadow")) return new ILBasicType(ILBaseType::TextureCubeShadow); if (parser.LookAhead("sampler2DArrayShadow") || parser.LookAhead("Texture2DArrayShadow")) return new ILBasicType(ILBaseType::Texture2DArrayShadow); if (parser.LookAhead("sampler3D") || parser.LookAhead("Texture3D")) return new ILBasicType(ILBaseType::Texture3D); if (parser.LookAhead("samplerCubeArray") || parser.LookAhead("TextureCubeArray")) return new ILBasicType(ILBaseType::TextureCubeArray); if (parser.LookAhead("samplerCubeArrayShadow") || parser.LookAhead("TextureCubeShadowArray") || parser.LookAhead("TextureCubeArrayShadow")) return new ILBasicType(ILBaseType::TextureCubeShadowArray); if (parser.LookAhead("bool")) return new ILBasicType(ILBaseType::Bool); return nullptr; } int RoundToAlignment(int offset, int alignment) { int remainder = offset % alignment; if (remainder == 0) return offset; else return offset + (alignment - remainder); } int GetMaxResourceBindings(BindableResourceType type) { switch (type) { case BindableResourceType::Texture: return 32; case BindableResourceType::Sampler: return 32; case BindableResourceType::Buffer: return 16; case BindableResourceType::StorageBuffer: return 16; } return 0; } int SizeofBaseType(ILBaseType type) { if (type == ILBaseType::Int) return 4; if (type == ILBaseType::UInt) return 4; if (type == ILBaseType::UInt2) return 8; if (type == ILBaseType::UInt3) return 12; if (type == ILBaseType::UInt4) return 16; else if (type == ILBaseType::Int2) return 8; else if (type == ILBaseType::Int3) return 12; else if (type == ILBaseType::Int4) return 16; else if (type == ILBaseType::Float) return 4; else if (type == ILBaseType::Float2) return 8; else if (type == ILBaseType::Float3) return 12; else if (type == ILBaseType::Float4) return 16; else if (type == ILBaseType::Float3x3) return 48; else if (type == ILBaseType::Float4x4) return 64; else if (type == ILBaseType::Texture2D) return 8; else if (type == ILBaseType::TextureCube) return 8; else if (type == ILBaseType::Texture2DArray) return 8; else if (type == ILBaseType::Texture2DShadow) return 8; else if (type == ILBaseType::TextureCubeShadow) return 8; else if (type == ILBaseType::Texture2DArrayShadow) return 8; else if (type == ILBaseType::TextureCubeArray) return 8; else if (type == ILBaseType::TextureCubeShadowArray) return 8; else if (type == ILBaseType::Bool) return 4; else return 0; } bool ILType::IsBool() { auto basicType = dynamic_cast(this); if (basicType) return basicType->Type == ILBaseType::Bool; else return false; } bool ILType::IsInt() { auto basicType = dynamic_cast(this); if (basicType) return basicType->Type == ILBaseType::Int; else return false; } bool ILType::IsUInt() { auto basicType = dynamic_cast(this); if (basicType) return basicType->Type == ILBaseType::UInt; else return false; } bool ILType::IsIntegral() { auto basicType = dynamic_cast(this); if (basicType) return basicType->Type == ILBaseType::Int || basicType->Type == ILBaseType::Int2 || basicType->Type == ILBaseType::Int3 || basicType->Type == ILBaseType::Int4 || basicType->Type == ILBaseType::UInt || basicType->Type == ILBaseType::UInt2 || basicType->Type == ILBaseType::UInt3 || basicType->Type == ILBaseType::UInt4 || basicType->Type == ILBaseType::Bool; else return false; } bool ILType::IsVoid() { auto basicType = dynamic_cast(this); if (basicType) return basicType->Type == ILBaseType::Void; else return false; } bool ILType::IsFloat() { auto basicType = dynamic_cast(this); if (basicType) return basicType->Type == ILBaseType::Float; else return false; } bool ILType::IsBoolVector() { auto basicType = dynamic_cast(this); if (basicType) return basicType->Type == ILBaseType::Bool2 || basicType->Type == ILBaseType::Bool3 || basicType->Type == ILBaseType::Bool4; else return false; } bool ILType::IsIntVector() { auto basicType = dynamic_cast(this); if (basicType) return basicType->Type == ILBaseType::Int2 || basicType->Type == ILBaseType::Int3 || basicType->Type == ILBaseType::Int4; else return false; } bool ILType::IsUIntVector() { auto basicType = dynamic_cast(this); if (basicType) return basicType->Type == ILBaseType::UInt2 || basicType->Type == ILBaseType::UInt3 || basicType->Type == ILBaseType::UInt4; else return false; } bool ILType::IsFloatVector() { auto basicType = dynamic_cast(this); if (basicType) return basicType->Type == ILBaseType::Float2 || basicType->Type == ILBaseType::Float3 || basicType->Type == ILBaseType::Float4 || basicType->Type == ILBaseType::Float3x3 || basicType->Type == ILBaseType::Float4x4; else return false; } bool ILType::IsFloatMatrix() { auto basicType = dynamic_cast(this); if (basicType) return basicType->Type == ILBaseType::Float3x3 || basicType->Type == ILBaseType::Float4x4; else return false; } bool ILType::IsNonShadowTexture() { auto basicType = dynamic_cast(this); if (basicType) return basicType->Type == ILBaseType::Texture2D || basicType->Type == ILBaseType::TextureCube || basicType->Type == ILBaseType::Texture2DArray || basicType->Type == ILBaseType::Texture3D || basicType->Type == ILBaseType::TextureCubeArray; else return false; } bool ILType::IsTexture() { auto basicType = dynamic_cast(this); if (basicType) return basicType->Type == ILBaseType::Texture2D || basicType->Type == ILBaseType::TextureCube || basicType->Type == ILBaseType::Texture2DArray || basicType->Type == ILBaseType::Texture2DShadow || basicType->Type == ILBaseType::TextureCubeShadow || basicType->Type == ILBaseType::Texture2DArrayShadow || basicType->Type == ILBaseType::Texture3D || basicType->Type == ILBaseType::TextureCubeShadowArray || basicType->Type == ILBaseType::TextureCubeArray; else return false; } bool ILType::IsSamplerState() { auto basicType = dynamic_cast(this); if (basicType) return basicType->Type == ILBaseType::SamplerState || basicType->Type == ILBaseType::SamplerComparisonState; else return false; } int ILType::GetVectorSize() { if (auto basicType = dynamic_cast(this)) { switch (basicType->Type) { case ILBaseType::Int2: case ILBaseType::Float2: case ILBaseType::UInt2: return 2; case ILBaseType::Int3: case ILBaseType::Float3: case ILBaseType::UInt3: return 3; case ILBaseType::Int4: case ILBaseType::Float4: case ILBaseType::UInt4: return 4; case ILBaseType::Float3x3: return 9; case ILBaseType::Float4x4: return 16; default: return 1; } } return 1; } RefPtr DeserializeBasicType(CoreLib::Text::TokenReader & reader) { reader.Read("basic"); auto rs = BaseTypeFromString(reader); reader.ReadWord(); return rs; } RefPtr DeserializeStructType(CoreLib::Text::TokenReader & reader) { reader.Read("struct"); RefPtr rs = new ILStructType(); rs->TypeName = reader.ReadToken().Content; reader.Read("("); while (reader.LookAhead(")")) { ILStructType::ILStructField field; field.FieldName = reader.ReadToken().Content; reader.Read(":"); field.Type = ILType::Deserialize(reader); reader.Read(";"); } reader.Read(")"); return rs; } RefPtr DeserializeArrayType(CoreLib::Text::TokenReader & reader) { reader.Read("array"); reader.Read("("); RefPtr rs = new ILArrayType(); rs->BaseType = ILType::Deserialize(reader); reader.Read(","); rs->ArrayLength = reader.ReadInt(); reader.Read(")"); return rs; } RefPtr DeserializeGenericType(CoreLib::Text::TokenReader & reader) { reader.Read("generic"); RefPtr rs = new ILGenericType(); rs->GenericTypeName = reader.ReadWord(); reader.Read("("); rs->BaseType = ILType::Deserialize(reader); reader.Read(")"); return rs; } RefPtr DeserializeRecordType(CoreLib::Text::TokenReader & reader) { reader.Read("record"); RefPtr rs = new ILRecordType(); rs->TypeName = reader.ReadWord(); return rs; } RefPtr ILType::Deserialize(CoreLib::Text::TokenReader & reader) { if (reader.LookAhead("basic")) return DeserializeBasicType(reader); else if (reader.LookAhead("struct")) return DeserializeStructType(reader); else if (reader.LookAhead("array")) return DeserializeArrayType(reader); else if (reader.LookAhead("generic")) return DeserializeGenericType(reader); else if (reader.LookAhead("record")) return DeserializeRecordType(reader); return nullptr; } bool CFGNode::HasPhiInstruction() { return headInstr && headInstr->GetNext() && headInstr->GetNext()->Is(); } ILInstruction * CFGNode::GetFirstNonPhiInstruction() { for (auto & instr : *this) { if (!instr.Is()) return &instr; } return tailInstr; } int NamingCounter = 0; void CFGNode::NameAllInstructions() { // name all operands StringBuilder numBuilder; for (auto & instr : GetAllInstructions()) { numBuilder.Clear(); for (auto & c : instr.Name) { if (c >= '0' && c <= '9') numBuilder.Append(c); else numBuilder.Clear(); } auto num = numBuilder.ToString(); if (num.Length()) { int id = StringToInt(num); NamingCounter = Math::Max(NamingCounter, id + 1); } } HashSet existingNames; for (auto & instr : GetAllInstructions()) { if (instr.Name.Length() == 0) instr.Name = String("t") + String(NamingCounter++, 16); else { int counter = 1; String newName = instr.Name; while (existingNames.Contains(newName)) { newName = instr.Name + String(counter); counter++; } instr.Name = newName; } existingNames.Add(instr.Name); } } void CFGNode::DebugPrint() { printf("===========\n"); for (auto& instr : *this) { printf("%S\n", instr.ToString().ToWString()); } printf("===========\n"); } LoadInstruction::LoadInstruction(ILOperand * dest) { Deterministic = false; Operand = dest; Type = dest->Type->Clone(); if (!Spire::Compiler::Is(dest) && !Spire::Compiler::Is(dest)) throw "invalid address operand"; } void MemberUpdateInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitMemberUpdateInstruction(this); } void SubInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitSubInstruction(this); } void MulInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitMulInstruction(this); } void DivInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitDivInstruction(this); } void ModInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitModInstruction(this); } void AndInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitAndInstruction(this); } void OrInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitOrInstruction(this); } void BitAndInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitBitAndInstruction(this); } void BitOrInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitBitOrInstruction(this); } void BitXorInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitBitXorInstruction(this); } void ShlInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitShlInstruction(this); } void ShrInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitShrInstruction(this); } void CmpgtInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitCmpgtInstruction(this); } void CmpgeInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitCmpgeInstruction(this); } void CmpltInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitCmpltInstruction(this); } void CmpleInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitCmpleInstruction(this); } void CmpeqlInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitCmpeqlInstruction(this); } void CmpneqInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitCmpneqInstruction(this); } void Float2IntInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitFloat2IntInstruction(this); } void Int2FloatInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitInt2FloatInstruction(this); } void CopyInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitCopyInstruction(this); } void LoadInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitLoadInstruction(this); } void StoreInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitStoreInstruction(this); } void AllocVarInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitAllocVarInstruction(this); } void FetchArgInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitFetchArgInstruction(this); } void PhiInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitPhiInstruction(this); } void SelectInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitSelectInstruction(this); } void CallInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitCallInstruction(this); } void SwitchInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitSwitchInstruction(this); } void NotInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitNotInstruction(this); } void NegInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitNegInstruction(this); } void BitNotInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitBitNotInstruction(this); } void AddInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitAddInstruction(this); } void MemberLoadInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitMemberLoadInstruction(this); } AllInstructionsIterator & AllInstructionsIterator::operator++() { if (subBlockPtr < curInstr->GetSubBlockCount()) { StackItem item; item.instr = curInstr; item.subBlockPtr = subBlockPtr + 1; stack.Add(item); curInstr = curInstr->GetSubBlock(subBlockPtr)->begin().Current; subBlockPtr = 0; } else curInstr = curInstr->GetNext(); while (curInstr->GetNext() == nullptr && stack.Count() > 0) { auto item = stack.Last(); stack.RemoveAt(stack.Count() - 1); curInstr = item.instr; subBlockPtr = item.subBlockPtr; if (subBlockPtr >= curInstr->GetSubBlockCount()) { subBlockPtr = 0; curInstr = curInstr->GetNext(); } } return *this; } AllInstructionsIterator AllInstructionsCollection::begin() { return AllInstructionsIterator(node->begin().Current); } AllInstructionsIterator AllInstructionsCollection::end() { return AllInstructionsIterator(node->end().Current); } String ImportInstruction::ToString() { StringBuilder rs; rs << Name << " = import [" << ComponentName << "]("; for (auto & arg : Arguments) { rs << arg->ToString() << ", "; } rs << ")"; rs << "\n{"; rs << ImportOperator->ToString() << "}\n"; return rs.ProduceString(); } String ImportInstruction::GetOperatorString() { return "import"; } void ImportInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitImportInstruction(this); } void ExportInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitExportInstruction(this); } ILType * ILStructType::Clone() { auto rs = new ILStructType(*this); rs->Members.Clear(); for (auto & m : Members) { ILStructField f; f.FieldName = m.FieldName; f.Type = m.Type->Clone(); rs->Members.Add(f); } return rs; } String ILStructType::ToString() { return TypeName; } bool ILStructType::Equals(ILType * type) { auto st = dynamic_cast(type); if (st && st->TypeName == this->TypeName) return true; return false; } ILType * ILRecordType::Clone() { auto rs = new ILRecordType(*this); rs->Members.Clear(); for (auto & m : Members) { ILObjectDefinition f; f.Type = m.Value.Type->Clone(); f.Name = m.Value.Name; f.Attributes = m.Value.Attributes; rs->Members.Add(m.Key, f); } return rs; } String ILRecordType::ToString() { return TypeName; } bool ILRecordType::Equals(ILType * type) { auto recType = dynamic_cast(type); if (recType) return TypeName == recType->TypeName; else return false; } void DiscardInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitDiscardInstruction(this); } void LoadInputInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitLoadInputInstruction(this); } void SwizzleInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitSwizzleInstruction(this); } void ProjectInstruction::Accept(InstructionVisitor * visitor) { visitor->VisitProjectInstruction(this); } } } ================================================ FILE: Source/SpireCore/IL.h ================================================ #ifndef RASTER_RENDERER_IL_H #define RASTER_RENDERER_IL_H #include "../CoreLib/Basic.h" #include "../CoreLib/Tokenizer.h" namespace Spire { namespace Compiler { using CoreLib::Text::CodePosition; using namespace CoreLib::Basic; enum ILBaseType { Void = 0, Int = 16, Int2 = 17, Int3 = 18, Int4 = 19, Float = 32, Float2 = 33, Float3 = 34, Float4 = 35, Float3x3 = 40, Float4x4 = 47, Texture2D = 48, TextureCube = 49, Texture2DArray = 50, Texture2DShadow = 51, TextureCubeShadow = 52, Texture2DArrayShadow = 53, Texture3D = 54, TextureCubeArray = 55, TextureCubeShadowArray = 56, Bool = 128, Bool2 = 129, Bool3 = 130, Bool4 = 131, UInt = 512, UInt2 = 513, UInt3 = 514, UInt4 = 515, SamplerState = 4096, SamplerComparisonState = 4097 }; int SizeofBaseType(ILBaseType type); int RoundToAlignment(int offset, int alignment); extern int NamingCounter; enum class BindableResourceType { NonBindable, Texture, Sampler, Buffer, StorageBuffer }; int GetMaxResourceBindings(BindableResourceType type); class ILType : public RefObject { public: bool IsBool(); bool IsInt(); bool IsUInt(); bool IsIntegral(); bool IsFloat(); bool IsVoid(); bool IsScalar() { return IsInt() || IsUInt() || IsFloat() || IsBool(); } bool IsBoolVector(); bool IsIntVector(); bool IsUIntVector(); bool IsFloatVector(); bool IsFloatMatrix(); bool IsVector() { return IsIntVector() || IsUIntVector() || IsFloatVector() || IsBoolVector(); } bool IsTexture(); bool IsSamplerState(); bool IsNonShadowTexture(); int GetVectorSize(); virtual BindableResourceType GetBindableResourceType() = 0; virtual ILType * Clone() = 0; virtual String ToString() = 0; virtual bool Equals(ILType* type) = 0; virtual void Serialize(StringBuilder & sb) = 0; static RefPtr Deserialize(CoreLib::Text::TokenReader & reader); }; class ILObjectDefinition { public: RefPtr Type; String Name; EnumerableDictionary Attributes; CodePosition Position; int Binding = -1; }; class ILRecordType : public ILType { public: String TypeName; EnumerableDictionary Members; virtual ILType * Clone() override; virtual String ToString() override; virtual bool Equals(ILType* type) override; virtual void Serialize(StringBuilder & sb) override { sb << "record " << TypeName; } virtual BindableResourceType GetBindableResourceType() override { return BindableResourceType::NonBindable; } }; class ILBasicType : public ILType { public: ILBaseType Type; ILBasicType() { Type = ILBaseType::Int; } ILBasicType(ILBaseType t) { Type = t; } virtual bool Equals(ILType* type) override { auto btype = dynamic_cast(type); if (!btype) return false; return Type == btype->Type; } virtual BindableResourceType GetBindableResourceType() override { switch (Type) { case ILBaseType::Texture2D: case ILBaseType::TextureCube: case ILBaseType::Texture2DArray: case ILBaseType::Texture2DShadow: case ILBaseType::TextureCubeShadow: case ILBaseType::Texture2DArrayShadow: case ILBaseType::TextureCubeArray: case ILBaseType::TextureCubeShadowArray: case ILBaseType::Texture3D: return BindableResourceType::Texture; case ILBaseType::SamplerState: case ILBaseType::SamplerComparisonState: return BindableResourceType::Sampler; default: return BindableResourceType::NonBindable; } } virtual ILType * Clone() override { auto rs = new ILBasicType(); rs->Type = Type; return rs; } virtual void Serialize(StringBuilder & sb) override { sb << "basic " << ToString(); } virtual String ToString() override { if (Type == ILBaseType::Int) return "int"; else if (Type == ILBaseType::UInt) return "uint"; else if (Type == ILBaseType::UInt2) return "uvec2"; else if (Type == ILBaseType::UInt3) return "uvec3"; else if (Type == ILBaseType::UInt4) return "uvec4"; else if (Type == ILBaseType::Int2) return "ivec2"; else if (Type == ILBaseType::Int3) return "ivec3"; else if (Type == ILBaseType::Int4) return "ivec4"; else if (Type == ILBaseType::Float) return "float"; else if (Type == ILBaseType::Float2) return "vec2"; else if (Type == ILBaseType::Float3) return "vec3"; else if (Type == ILBaseType::Float4) return "vec4"; else if (Type == ILBaseType::Float3x3) return "mat3"; else if (Type == ILBaseType::Float4x4) return "mat4"; else if (Type == ILBaseType::Texture2D) return "sampler2D"; else if (Type == ILBaseType::TextureCube) return "samplerCube"; else if (Type == ILBaseType::Texture2DArray) return "sampler2DArray"; else if (Type == ILBaseType::Texture2DShadow) return "sampler2DShadow"; else if (Type == ILBaseType::TextureCubeShadow) return "samplerCubeShadow"; else if (Type == ILBaseType::Texture2DArrayShadow) return "sampler2DArrayShadow"; else if (Type == ILBaseType::Texture3D) return "sampler3D"; else if (Type == ILBaseType::TextureCubeArray) return "samplerCubeArray"; else if (Type == ILBaseType::TextureCubeShadowArray) return "samplerCubeArrayShadow"; else if (Type == ILBaseType::Bool) return "bool"; else if (Type == ILBaseType::Bool2) return "bvec2"; else if (Type == ILBaseType::Bool3) return "bvec3"; else if (Type == ILBaseType::Bool4) return "bvec4"; else if (Type == ILBaseType::SamplerState) return "SamplerState"; else if (Type == ILBaseType::SamplerComparisonState) return "SamplerComparisonState"; else if (Type == ILBaseType::Void) return "void"; else return "?unknown"; } }; class ILArrayType : public ILType { public: RefPtr BaseType; int ArrayLength; virtual bool Equals(ILType* type) override { auto btype = dynamic_cast(type); if (!btype) return false; return BaseType->Equals(btype->BaseType.Ptr());; } virtual ILType * Clone() override { auto rs = new ILArrayType(); rs->BaseType = BaseType->Clone(); rs->ArrayLength = ArrayLength; return rs; } virtual void Serialize(StringBuilder & sb) override { sb << "array("; BaseType->Serialize(sb); sb << ", " << ArrayLength << ")"; } virtual String ToString() override { if (ArrayLength > 0) return BaseType->ToString() + "[" + String(ArrayLength) + "]"; else return BaseType->ToString() + "[]"; } virtual BindableResourceType GetBindableResourceType() override { return BindableResourceType::NonBindable; } }; class ILGenericType : public ILType { public: RefPtr BaseType; String GenericTypeName; virtual bool Equals(ILType* type) override { auto btype = dynamic_cast(type); if (!btype) return false; return BaseType->Equals(btype->BaseType.Ptr());; } virtual ILType * Clone() override { auto rs = new ILGenericType(); rs->BaseType = BaseType->Clone(); rs->GenericTypeName = GenericTypeName; return rs; } virtual String ToString() override { return GenericTypeName + "<" + BaseType->ToString() + ">"; } virtual void Serialize(StringBuilder & sb) override { sb << "generic " << GenericTypeName << "("; BaseType->Serialize(sb); sb << ")"; } virtual BindableResourceType GetBindableResourceType() override { if (GenericTypeName == "StructuredBuffer" || GenericTypeName == "RWStructuredBuffer") return BindableResourceType::StorageBuffer; else if (GenericTypeName == "Buffer" || GenericTypeName == "RWBuffer" || GenericTypeName == "ByteAddressBuffer" || GenericTypeName == "RWByteAddressBuffer") return BindableResourceType::Buffer; return BindableResourceType::NonBindable; } }; class ILStructType : public ILType { public: String TypeName; bool IsIntrinsic = false; class ILStructField { public: RefPtr Type; String FieldName; }; List Members; virtual ILType * Clone() override; virtual String ToString() override; virtual bool Equals(ILType * type) override; virtual void Serialize(StringBuilder & sb) override { sb << "struct " << TypeName << "("; for (auto & member : Members) { sb << member.FieldName << ":"; member.Type->Serialize(sb); sb << "; "; } sb << ")"; } virtual BindableResourceType GetBindableResourceType() override { return BindableResourceType::NonBindable; } }; class ILOperand; class UserReferenceSet { private: EnumerableDictionary userRefCounts; int count; public: UserReferenceSet() { count = 0; } int Count() { return count; } int GetUseCount(ILOperand * op) { int rs = -1; userRefCounts.TryGetValue(op, rs); return rs; } void Add(ILOperand * user) { this->count++; int ncount = 0; if (userRefCounts.TryGetValue(user, ncount)) { ncount++; userRefCounts[user] = ncount; } else { userRefCounts.Add(user, 1); } } void Remove(ILOperand * user) { int ncount = 0; if (userRefCounts.TryGetValue(user, ncount)) { this->count--; ncount--; if (ncount) userRefCounts[user] = ncount; else userRefCounts.Remove(user); } } void RemoveAll(ILOperand * user) { int ncount = 0; if (userRefCounts.TryGetValue(user, ncount)) { this->count -= ncount; userRefCounts.Remove(user); } } class UserIterator { private: EnumerableDictionary::Iterator iter; public: ILOperand * operator *() { return iter.Current->Value.Key; } ILOperand ** operator ->() { return &iter.Current->Value.Key; } UserIterator & operator ++() { iter++; return *this; } UserIterator operator ++(int) { UserIterator rs = *this; operator++(); return rs; } bool operator != (const UserIterator & _that) { return iter != _that.iter; } bool operator == (const UserIterator & _that) { return iter == _that.iter; } UserIterator(const EnumerableDictionary::Iterator & iter) { this->iter = iter; } UserIterator() { } }; UserIterator begin() { return UserIterator(userRefCounts.begin()); } UserIterator end() { return UserIterator(userRefCounts.end()); } }; class ILOperand : public Object { public: String Name; RefPtr Type; UserReferenceSet Users; String Attribute; void * Tag; CodePosition Position; union VMFields { void * VMData; struct Fields { int VMDataWords[2]; } Fields; } VMFields; Procedure OnDelete; ILOperand() { Tag = nullptr; } ILOperand(const ILOperand & op) { Tag = op.Tag; Name = op.Name; Attribute = op.Attribute; if (op.Type) Type = op.Type->Clone(); //Users = op.Users; } virtual ~ILOperand() { OnDelete(this); } virtual String ToString() { return ""; } virtual bool IsUndefined() { return false; } }; class ILUndefinedOperand : public ILOperand { public: ILUndefinedOperand() { Name = ""; } virtual String ToString() override { return ""; } virtual bool IsUndefined() override { return true; } }; class UseReference { private: ILOperand * user; ILOperand * reference; public: UseReference() : user(0), reference(0) {} UseReference(const UseReference &) { user = 0; reference = 0; } UseReference(ILOperand * user) : user(user), reference(0) {} UseReference(ILOperand * user, ILOperand * ref) { this->user = user; this->reference = ref; } ~UseReference() { if (reference) reference->Users.Remove(user); } void SetUser(ILOperand * _user) { this->user = _user; } void operator = (const UseReference & ref) { if (reference) reference->Users.Remove(user); reference = ref.Ptr(); if (ref.Ptr()) { if (!user) throw InvalidOperationException("user not initialized."); ref.Ptr()->Users.Add(user); } } void operator = (ILOperand * newRef) { if (reference) reference->Users.Remove(user); reference = newRef; if (newRef) { if (!user) throw InvalidOperationException("user not initialized."); newRef->Users.Add(user); } } bool operator != (const UseReference & _that) { return reference != _that.reference || user != _that.user; } bool operator == (const UseReference & _that) { return reference == _that.reference && user == _that.user; } ILOperand * Ptr() const { return reference; } ILOperand * operator->() { return reference; } ILOperand & operator*() { return *reference; } explicit operator bool() { return (reference != 0); } String ToString() { if (reference) return reference->Name; else return ""; } }; class OperandIterator { private: UseReference * use; public: OperandIterator() { use = 0; } OperandIterator(UseReference * use) : use(use) {} ILOperand & operator *() { return use->operator*(); } ILOperand * operator ->() { return use->operator->(); } void Set(ILOperand * user, ILOperand * op) { (*use).SetUser(user); (*use) = op; } void Set(ILOperand * op) { (*use) = op; } OperandIterator & operator ++() { use++; return *this; } OperandIterator operator ++(int) { OperandIterator rs = *this; operator++(); return rs; } bool operator != (const OperandIterator & _that) { return use != _that.use; } bool operator == (const OperandIterator & _that) { return use == _that.use; } bool operator == (const ILOperand * op) { return use->Ptr() == op; } bool operator != (const ILOperand * op) { return use->Ptr() != op; } }; class ILConstOperand : public ILOperand { public: union { int IntValues[16]; float FloatValues[16]; }; virtual String ToString() override { if (Type->IsFloat()) return String(FloatValues[0]) + "f"; else if (Type->IsInt()) return String(IntValues[0]); else if (auto baseType = dynamic_cast(Type.Ptr())) { StringBuilder sb(256); if (baseType->Type == ILBaseType::Float2) sb << "vec2(" << FloatValues[0] << "f, " << FloatValues[1] << "f)"; else if (baseType->Type == ILBaseType::Float3) sb << "vec3(" << FloatValues[0] << "f, " << FloatValues[1] << "f, " << FloatValues[2] << "f)"; else if (baseType->Type == ILBaseType::Float4) sb << "vec4(" << FloatValues[0] << "f, " << FloatValues[1] << "f, " << FloatValues[2] << "f, " << FloatValues[3] << "f)"; else if (baseType->Type == ILBaseType::Float3x3) sb << "mat3(...)"; else if (baseType->Type == ILBaseType::Float4x4) sb << "mat4(...)"; else if (baseType->Type == ILBaseType::Int2) sb << "ivec2(" << IntValues[0] << ", " << IntValues[1] << ")"; else if (baseType->Type == ILBaseType::Int3) sb << "ivec3(" << IntValues[0] << ", " << IntValues[1] << ", " << IntValues[2] << ")"; else if (baseType->Type == ILBaseType::Int4) sb << "ivec4(" << IntValues[0] << ", " << IntValues[1] << ", " << IntValues[2] << ", " << IntValues[3] << ")"; else if (baseType->Type == ILBaseType::UInt2) sb << "uvec2(" << IntValues[0] << ", " << IntValues[1] << ")"; else if (baseType->Type == ILBaseType::UInt3) sb << "uvec3(" << IntValues[0] << ", " << IntValues[1] << ", " << IntValues[2] << ")"; else if (baseType->Type == ILBaseType::UInt4) sb << "uvec4(" << IntValues[0] << ", " << IntValues[1] << ", " << IntValues[2] << ", " << IntValues[3] << ")"; return sb.ToString(); } else throw InvalidOperationException("Illegal constant."); } }; class InstructionVisitor; class CFGNode; class ILInstruction : public ILOperand { private: ILInstruction *next, *prev; public: CFGNode * Parent; ILInstruction() { next = 0; prev = 0; Parent = 0; } ILInstruction(const ILInstruction & instr) : ILOperand(instr) { next = 0; prev = 0; Parent = 0; } ~ILInstruction() { } virtual ILInstruction * Clone() { return new ILInstruction(*this); } virtual String GetOperatorString() { return ""; } virtual bool HasSideEffect() { return false; } virtual bool IsDeterministic() { return true; } virtual void Accept(InstructionVisitor *) { } void InsertBefore(ILInstruction * instr) { instr->Parent = Parent; instr->prev = prev; instr->next = this; prev = instr; auto *npp = instr->prev; if (npp) npp->next = instr; } void InsertAfter(ILInstruction * instr) { instr->Parent = Parent; instr->prev = this; instr->next = this->next; next = instr; auto *npp = instr->next; if (npp) npp->prev = instr; } ILInstruction * GetNext() { return next; } ILInstruction * GetPrevious() { return prev; } void Remove() { if (prev) prev->next = next; if (next) next->prev = prev; } void Erase() { Remove(); if (Users.Count()) { throw InvalidOperationException("All uses must be removed before removing this instruction"); } delete this; } virtual OperandIterator begin() { return OperandIterator(); } virtual OperandIterator end() { return OperandIterator(); } virtual int GetSubBlockCount() { return 0; } virtual CFGNode * GetSubBlock(int) { return nullptr; } template T * As() { return dynamic_cast(this); } template bool Is() { return dynamic_cast(this) != 0; } }; template bool Is(TOperand * op) { auto ptr = dynamic_cast(op); if (ptr) return true; else return false; } class SwitchInstruction : public ILInstruction { public: List Candidates; virtual OperandIterator begin() override { return Candidates.begin(); } virtual OperandIterator end() override { return Candidates.end(); } virtual String ToString() override { StringBuilder sb(256); sb << Name; sb << " = switch "; for (auto & op : Candidates) { sb << op.ToString(); if (op != Candidates.Last()) sb << ", "; } return sb.ProduceString(); } virtual String GetOperatorString() override { return "switch"; } virtual bool HasSideEffect() override { return false; } SwitchInstruction(int argSize) { Candidates.SetSize(argSize); for (auto & use : Candidates) use.SetUser(this); } SwitchInstruction(const SwitchInstruction & other) : ILInstruction(other) { Candidates.SetSize(other.Candidates.Count()); for (int i = 0; i < other.Candidates.Count(); i++) { Candidates[i].SetUser(this); Candidates[i] = other.Candidates[i].Ptr(); } } virtual SwitchInstruction * Clone() override { return new SwitchInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class LeaInstruction : public ILInstruction {}; class ILWorld; class ImportInstruction : public LeaInstruction { public: String ComponentName; RefPtr ImportOperator; List Arguments; virtual OperandIterator begin() override { return Arguments.begin(); } virtual OperandIterator end() override { return Arguments.end(); } virtual int GetSubBlockCount() override { return 1; } virtual CFGNode * GetSubBlock(int i) override { if (i == 0) return ImportOperator.Ptr(); return nullptr; } ImportInstruction(int argSize = 0) : LeaInstruction() { Arguments.SetSize(argSize); for (auto & use : Arguments) use.SetUser(this); } ImportInstruction(const ImportInstruction & other) : LeaInstruction(other) { Arguments.SetSize(other.Arguments.Count()); for (int i = 0; i < other.Arguments.Count(); i++) { Arguments[i].SetUser(this); Arguments[i] = other.Arguments[i].Ptr(); } } ImportInstruction(int argSize, String compName, RefPtr importOp, RefPtr type) :ImportInstruction(argSize) { this->ComponentName = compName; this->ImportOperator = importOp; this->Type = type; } virtual String ToString() override; virtual String GetOperatorString() override; virtual ImportInstruction * Clone() override { return new ImportInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class LoadInputInstruction : public LeaInstruction { public: String InputName; LoadInputInstruction(RefPtr type, String name) : InputName(name) { this->Type = type; } LoadInputInstruction(const LoadInputInstruction & other) :LeaInstruction(other), InputName(other.InputName) { } virtual bool IsDeterministic() override { return true; } virtual String ToString() override { return Name + " = INPUT " + InputName; } virtual String GetOperatorString() override { return "input"; } virtual LoadInputInstruction * Clone() override { return new LoadInputInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class AllocVarInstruction : public LeaInstruction { public: UseReference Size; AllocVarInstruction(ILType * type, ILOperand * count) : Size(this) { this->Type = type; this->Size = count; } AllocVarInstruction(RefPtr & type, ILOperand * count) : Size(this) { auto ptrType = type->Clone(); if (!type) throw ArgumentException("type cannot be null."); this->Type = ptrType; this->Size = count; } AllocVarInstruction(const AllocVarInstruction & other) :LeaInstruction(other), Size(this) { Size = other.Size.Ptr(); } virtual bool IsDeterministic() override { return false; } virtual String ToString() override { return Name + " = VAR " + Type->ToString() + ", " + Size.ToString(); } virtual OperandIterator begin() override { return &Size; } virtual OperandIterator end() override { return &Size + 1; } virtual String GetOperatorString() override { return "avar"; } virtual AllocVarInstruction * Clone() override { return new AllocVarInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class FetchArgInstruction : public LeaInstruction { public: int ArgId; FetchArgInstruction(RefPtr type) { this->Type = type; ArgId = 0; } virtual String ToString() override { return Name + " = ARG " + Type->ToString(); } virtual String GetOperatorString() override { return "arg " + String(ArgId); } virtual bool IsDeterministic() override { return false; } virtual FetchArgInstruction * Clone() override { return new FetchArgInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class CFGNode; class AllInstructionsIterator { private: struct StackItem { ILInstruction* instr; int subBlockPtr; }; List stack; ILInstruction * curInstr = nullptr; int subBlockPtr = 0; public: AllInstructionsIterator(ILInstruction * instr) { curInstr = instr; } AllInstructionsIterator & operator ++(); AllInstructionsIterator operator ++(int) { AllInstructionsIterator rs = *this; operator++(); return rs; } bool operator != (const AllInstructionsIterator & _that) { return curInstr != _that.curInstr || subBlockPtr != _that.subBlockPtr; } bool operator == (const AllInstructionsIterator & _that) { return curInstr == _that.curInstr && subBlockPtr == _that.subBlockPtr; } ILOperand & operator *() { return *curInstr; } ILOperand * operator ->() { return curInstr; } }; class AllInstructionsCollection { private: CFGNode * node; public: AllInstructionsCollection(CFGNode * _node) : node(_node) {} AllInstructionsIterator begin(); AllInstructionsIterator end(); }; class CFGNode : public Object { private: ILInstruction *headInstr, *tailInstr; public: class Iterator { public: ILInstruction * Current, *Next; void SetCurrent(ILInstruction * cur) { Current = cur; if (Current) Next = Current->GetNext(); else Next = 0; } Iterator(ILInstruction * cur) { SetCurrent(cur); } ILInstruction & operator *() const { return *Current; } Iterator& operator ++() { SetCurrent(Next); return *this; } Iterator operator ++(int) { Iterator rs = *this; SetCurrent(Next); return rs; } bool operator != (const Iterator & iter) const { return Current != iter.Current; } bool operator == (const Iterator & iter) const { return Current == iter.Current; } }; String ToString() { StringBuilder sb; bool first = true; auto pintr = begin(); while (pintr != end()) { if (!first) sb << EndLine; first = false; sb << pintr.Current->ToString(); pintr++; } return sb.ToString(); } Iterator begin() const { return Iterator(headInstr->GetNext()); } Iterator end() const { return Iterator(tailInstr); } AllInstructionsCollection GetAllInstructions() { return AllInstructionsCollection(this); } ILInstruction * GetFirstNonPhiInstruction(); bool HasPhiInstruction(); ILInstruction * GetLastInstruction() { return (tailInstr->GetPrevious()); } String Name; CFGNode() { headInstr = new ILInstruction(); tailInstr = new ILInstruction(); headInstr->Parent = this; headInstr->InsertAfter(tailInstr); } ~CFGNode() { ILInstruction * instr = headInstr; while (instr) { for (auto user : instr->Users) { auto userInstr = dynamic_cast(user); if (userInstr) { for (auto iter = userInstr->begin(); iter != userInstr->end(); ++iter) if (iter == instr) iter.Set(0); } } auto next = instr->GetNext(); delete instr; instr = next; } } void InsertHead(ILInstruction * instr) { headInstr->InsertAfter(instr); } void InsertTail(ILInstruction * instr) { tailInstr->InsertBefore(instr); } void NameAllInstructions(); void DebugPrint(); }; template struct ConstKey { Array Value; int Size; ConstKey() { Value.SetSize(Value.GetCapacity()); } ConstKey(T value, int size) { if (size == 0) size = 1; Value.SetSize(Value.GetCapacity()); for (int i = 0; i < size; i++) Value[i] = value; Size = size; } static ConstKey FromValues(T value, T value1) { ConstKey result; result.Value.SetSize(result.Value.GetCapacity()); result.Size = 2; result.Value[0] = value; result.Value[1] = value1; return result; } static ConstKey FromValues(T value, T value1, T value2) { ConstKey result; result.Value.SetSize(result.Value.GetCapacity()); result.Size = 3; result.Value[0] = value; result.Value[1] = value1; result.Value[2] = value2; return result; } static ConstKey FromValues(T value, T value1, T value2, T value3) { ConstKey result; result.Value.SetSize(result.Value.GetCapacity()); result.Size = 4; result.Value[0] = value; result.Value[1] = value1; result.Value[2] = value2; result.Value[3] = value3; return result; } int GetHashCode() { int result = Size; for (int i = 0; i < Size; i++) result ^= ((*(int*)&Value) << 5); return result; } bool operator == (const ConstKey & other) { if (Size != other.Size) return false; for (int i = 0; i < Size; i++) if (Value[i] != other.Value[i]) return false; return true; } }; class PhiInstruction : public ILInstruction { public: List Operands; // Use as fixed array, no insert or resize public: PhiInstruction(int opCount) { Operands.SetSize(opCount); for (int i = 0; i < opCount; i++) Operands[i].SetUser(this); } PhiInstruction(const PhiInstruction & other) : ILInstruction(other) { Operands.SetSize(other.Operands.Count()); for (int i = 0; i < Operands.Count(); i++) { Operands[i].SetUser(this); Operands[i] = other.Operands[i].Ptr(); } } virtual String GetOperatorString() override { return "phi"; } virtual OperandIterator begin() override { return Operands.begin(); } virtual OperandIterator end() override { return Operands.end(); } virtual String ToString() override { StringBuilder sb; sb << Name << " = phi "; for (auto & op : Operands) { if (op) { sb << op.ToString(); } else sb << ""; sb << ", "; } return sb.ProduceString(); } virtual PhiInstruction * Clone() override { return new PhiInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class UnaryInstruction : public ILInstruction { public: UseReference Operand; UnaryInstruction() : Operand(this) {} UnaryInstruction(const UnaryInstruction & other) : ILInstruction(other), Operand(this) { Operand = other.Operand.Ptr(); } virtual OperandIterator begin() override { return &Operand; } virtual OperandIterator end() override { return &Operand + 1; } }; class MakeRecordInstruction : public ILInstruction { public: RefPtr RecordType; List Arguments; }; class ProjectInstruction : public UnaryInstruction { public: String ComponentName; virtual String ToString() override { StringBuilder sb; sb << Name << " = project "; sb << Operand.ToString(); sb << ", " << ComponentName; return sb.ProduceString(); } virtual ProjectInstruction * Clone() override { return new ProjectInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class ExportInstruction : public UnaryInstruction { public: String ComponentName; ILWorld * World; ExportInstruction() = default; ExportInstruction(const ExportInstruction &) = default; ExportInstruction(String compName, ILWorld * srcWorld, ILOperand * value) : UnaryInstruction() { this->Operand = value; this->ComponentName = compName; this->World = srcWorld; this->Type = value->Type; } virtual String ToString() override { return "export [" + ComponentName + "], " + Operand.ToString(); } virtual String GetOperatorString() override { return "export [" + ComponentName + "]"; } virtual ExportInstruction * Clone() override { return new ExportInstruction(*this); } virtual bool HasSideEffect() override { return true; } virtual void Accept(InstructionVisitor * visitor) override; }; class BinaryInstruction : public ILInstruction { public: Array Operands; BinaryInstruction() { Operands.SetSize(2); Operands[0].SetUser(this); Operands[1].SetUser(this); } BinaryInstruction(const BinaryInstruction & other) : ILInstruction(other) { Operands.SetSize(2); Operands[0].SetUser(this); Operands[1].SetUser(this); Operands[0] = other.Operands[0].Ptr(); Operands[1] = other.Operands[1].Ptr(); } virtual OperandIterator begin() override { return Operands.begin(); } virtual OperandIterator end() override { return Operands.end(); } }; class SelectInstruction : public ILInstruction { public: UseReference Operands[3]; SelectInstruction() { Operands[0].SetUser(this); Operands[1].SetUser(this); Operands[2].SetUser(this); } SelectInstruction(const SelectInstruction & other) : ILInstruction(other) { Operands[0].SetUser(this); Operands[1].SetUser(this); Operands[2].SetUser(this); Operands[0] = other.Operands[0].Ptr(); Operands[1] = other.Operands[1].Ptr(); Operands[2] = other.Operands[2].Ptr(); } SelectInstruction(ILOperand * mask, ILOperand * val0, ILOperand * val1) { Operands[0].SetUser(this); Operands[1].SetUser(this); Operands[2].SetUser(this); Operands[0] = mask; Operands[1] = val0; Operands[2] = val1; Type = val0->Type->Clone(); } virtual OperandIterator begin() override { return Operands; } virtual OperandIterator end() override { return Operands + 3; } virtual String ToString() override { return Name + " = select " + Operands[0].ToString() + ": " + Operands[1].ToString() + ", " + Operands[2].ToString(); } virtual String GetOperatorString() override { return "select"; } virtual SelectInstruction * Clone() override { return new SelectInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class CallInstruction : public ILInstruction { public: String Function; List Arguments; bool SideEffect = false; virtual OperandIterator begin() override { return Arguments.begin(); } virtual OperandIterator end() override { return Arguments.end(); } virtual String ToString() override { StringBuilder sb(256); sb << Name; sb << " = call " << Function << "("; for (auto & op : Arguments) { sb << op.ToString(); if (op != Arguments.Last()) sb << ", "; } sb << ")"; return sb.ProduceString(); } virtual String GetOperatorString() override { return "call " + Function; } virtual bool HasSideEffect() override { return SideEffect; } CallInstruction(int argSize) { Arguments.SetSize(argSize); for (auto & use : Arguments) use.SetUser(this); } CallInstruction(const CallInstruction & other) : ILInstruction(other) { Function = other.Function; SideEffect = other.SideEffect; Arguments.SetSize(other.Arguments.Count()); for (int i = 0; i < other.Arguments.Count(); i++) { Arguments[i].SetUser(this); Arguments[i] = other.Arguments[i].Ptr(); } } virtual CallInstruction * Clone() override { return new CallInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class NotInstruction : public UnaryInstruction { public: virtual String ToString() override { return Name + " = not " + Operand.ToString(); } virtual String GetOperatorString() override { return "not"; } virtual NotInstruction * Clone() override { return new NotInstruction(*this); } NotInstruction() = default; NotInstruction(const NotInstruction & other) = default; NotInstruction(ILOperand * op) { Operand = op; Type = op->Type->Clone(); } virtual void Accept(InstructionVisitor * visitor) override; }; class NegInstruction : public UnaryInstruction { public: virtual String ToString() override { return Name + " = neg " + Operand.ToString(); } virtual String GetOperatorString() override { return "neg"; } virtual NegInstruction * Clone() override { return new NegInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class SwizzleInstruction : public UnaryInstruction { public: String SwizzleString; virtual String ToString() override { return Name + " = " + Operand.ToString() + "." + SwizzleString; } virtual String GetOperatorString() override { return "swizzle"; } virtual SwizzleInstruction * Clone() override { return new SwizzleInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class BitNotInstruction : public UnaryInstruction { public: virtual String ToString() override { return Name + " = bnot " + Operand.ToString(); } virtual String GetOperatorString() override { return "bnot"; } virtual BitNotInstruction * Clone() override { return new BitNotInstruction(*this); } BitNotInstruction() = default; BitNotInstruction(const BitNotInstruction & instr) = default; BitNotInstruction(ILOperand * op) { Operand = op; Type = op->Type->Clone(); } virtual void Accept(InstructionVisitor * visitor) override; }; class AddInstruction : public BinaryInstruction { public: AddInstruction() = default; AddInstruction(const AddInstruction & instr) = default; AddInstruction(ILOperand * v0, ILOperand * v1) { Operands[0] = v0; Operands[1] = v1; Type = v0->Type->Clone(); } virtual String ToString() override { return Name + " = add " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "add"; } virtual AddInstruction * Clone() override { return new AddInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class MemberLoadInstruction : public BinaryInstruction { public: MemberLoadInstruction() = default; MemberLoadInstruction(const MemberLoadInstruction &) = default; MemberLoadInstruction(ILOperand * v0, ILOperand * v1) { Operands[0] = v0; Operands[1] = v1; if (auto arrType = dynamic_cast(v0->Type.Ptr())) { Type = arrType->BaseType->Clone(); } else if (auto genType = dynamic_cast(v0->Type.Ptr())) { Type = genType->BaseType->Clone(); } else if (auto baseType = dynamic_cast(v0->Type.Ptr())) { switch (baseType->Type) { case ILBaseType::Float2: case ILBaseType::Float3: case ILBaseType::Float4: Type = new ILBasicType(ILBaseType::Float); break; case ILBaseType::Float3x3: Type = new ILBasicType(ILBaseType::Float3); break; case ILBaseType::Float4x4: Type = new ILBasicType(ILBaseType::Float4); break; case ILBaseType::Int2: case ILBaseType::Int3: case ILBaseType::Int4: Type = new ILBasicType(ILBaseType::Int); break; case ILBaseType::UInt2: case ILBaseType::UInt3: case ILBaseType::UInt4: Type = new ILBasicType(ILBaseType::UInt); break; default: throw InvalidOperationException("Unsupported aggregate type."); } } else if (auto structType = dynamic_cast(v0->Type.Ptr())) { auto cv1 = dynamic_cast(v1); if (!cv1) throw InvalidProgramException("member field access offset is not constant."); if (cv1->IntValues[0] < 0 || cv1->IntValues[0] >= structType->Members.Count()) throw InvalidProgramException("member field access offset out of bounds."); Type = structType->Members[cv1->IntValues[0]].Type; } } virtual String ToString() override { return Name + " = retrieve " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "retrieve"; } virtual MemberLoadInstruction * Clone() override { return new MemberLoadInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class SubInstruction : public BinaryInstruction { public: virtual String ToString() override { return Name + " = sub " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "sub"; } virtual SubInstruction * Clone() override { return new SubInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class MulInstruction : public BinaryInstruction { public: MulInstruction(){} MulInstruction(ILOperand * v0, ILOperand * v1) { Operands[0] = v0; Operands[1] = v1; Type = v0->Type->Clone(); } MulInstruction(const MulInstruction &) = default; virtual String ToString() override { return Name + " = mul " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "mu"; } virtual MulInstruction * Clone() override { return new MulInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class DivInstruction : public BinaryInstruction { public: virtual String ToString() override { return Name + " = div " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "div"; } virtual DivInstruction * Clone() override { return new DivInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class ModInstruction : public BinaryInstruction { public: virtual String ToString() override { return Name + " = mod " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "mod"; } virtual ModInstruction * Clone() override { return new ModInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class AndInstruction : public BinaryInstruction { public: virtual String ToString() override { return Name + " = and " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "and"; } virtual AndInstruction * Clone() override { return new AndInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class OrInstruction : public BinaryInstruction { public: virtual String ToString() override { return Name + " = or " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "or"; } virtual OrInstruction * Clone() override { return new OrInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class BitAndInstruction : public BinaryInstruction { public: BitAndInstruction(){} BitAndInstruction(ILOperand * v0, ILOperand * v1) { Operands[0] = v0; Operands[1] = v1; Type = v0->Type->Clone(); } BitAndInstruction(const BitAndInstruction &) = default; virtual String ToString() override { return Name + " = band " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "band"; } virtual BitAndInstruction * Clone() override { return new BitAndInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class BitOrInstruction : public BinaryInstruction { public: virtual String ToString() override { return Name + " = bor " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "bor"; } virtual BitOrInstruction * Clone() override { return new BitOrInstruction(*this); } BitOrInstruction(){} BitOrInstruction(const BitOrInstruction &) = default; BitOrInstruction(ILOperand * v0, ILOperand * v1) { Operands[0] = v0; Operands[1] = v1; Type = v0->Type->Clone(); } virtual void Accept(InstructionVisitor * visitor) override; }; class BitXorInstruction : public BinaryInstruction { public: virtual String ToString() override { return Name + " = bxor " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "bxor"; } virtual BitXorInstruction * Clone() override { return new BitXorInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class ShlInstruction : public BinaryInstruction { public: virtual String ToString() override { return Name + " = shl " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "sh"; } virtual ShlInstruction * Clone() override { return new ShlInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class ShrInstruction : public BinaryInstruction { public: virtual String ToString() override { return Name + " = shr " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "shr"; } virtual ShrInstruction * Clone() override { return new ShrInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class CompareInstruction : public BinaryInstruction {}; class CmpgtInstruction : public CompareInstruction { public: virtual String ToString() override { return Name + " = gt " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "gt"; } virtual CmpgtInstruction * Clone() override { return new CmpgtInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class CmpgeInstruction : public CompareInstruction { public: virtual String ToString() override { return Name + " = ge " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "ge"; } virtual CmpgeInstruction * Clone() override { return new CmpgeInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class CmpltInstruction : public CompareInstruction { public: virtual String ToString() override { return Name + " = lt " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "lt"; } virtual CmpltInstruction * Clone() override { return new CmpltInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class CmpleInstruction : public CompareInstruction { public: CmpleInstruction() = default; CmpleInstruction(const CmpleInstruction &) = default; CmpleInstruction(ILOperand * v0, ILOperand * v1) { Operands[0] = v0; Operands[1] = v1; Type = v0->Type->Clone(); } virtual String ToString() override { return Name + " = le " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "le"; } virtual CmpleInstruction * Clone() override { return new CmpleInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class CmpeqlInstruction : public CompareInstruction { public: virtual String ToString() override { return Name + " = eql " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "eq"; } virtual CmpeqlInstruction * Clone() override { return new CmpeqlInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class CmpneqInstruction : public CompareInstruction { public: virtual String ToString() override { return Name + " = neq " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "neq"; } virtual CmpneqInstruction * Clone() override { return new CmpneqInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class CastInstruction : public UnaryInstruction {}; class Float2IntInstruction : public CastInstruction { public: Float2IntInstruction(){} Float2IntInstruction(const Float2IntInstruction &) = default; Float2IntInstruction(ILOperand * op) { Operand = op; Type = new ILBasicType(ILBaseType::Int); } public: virtual String ToString() override { return Name + " = f2i " + Operand.ToString(); } virtual String GetOperatorString() override { return "f2i"; } virtual Float2IntInstruction * Clone() override { return new Float2IntInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class Int2FloatInstruction : public CastInstruction { public: Int2FloatInstruction(){} Int2FloatInstruction(ILOperand * op) { Operand = op; Type = new ILBasicType(ILBaseType::Float); } Int2FloatInstruction(const Int2FloatInstruction &) = default; public: virtual String ToString() override { return Name + " = i2f " + Operand.ToString(); } virtual String GetOperatorString() override { return "i2f"; } virtual Int2FloatInstruction * Clone() override { return new Int2FloatInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class CopyInstruction : public UnaryInstruction { public: CopyInstruction(){} CopyInstruction(const CopyInstruction &) = default; CopyInstruction(ILOperand * dest) { Operand = dest; Type = dest->Type->Clone(); } public: virtual String ToString() override { return Name + " = " + Operand.ToString(); } virtual String GetOperatorString() override { return "copy"; } virtual CopyInstruction * Clone() override { return new CopyInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; // load(src) class LoadInstruction : public UnaryInstruction { public: bool Deterministic; LoadInstruction() { Deterministic = false; } LoadInstruction(const LoadInstruction & other) : UnaryInstruction(other) { Deterministic = other.Deterministic; } LoadInstruction(ILOperand * dest); public: virtual String ToString() override { return Name + " = load " + Operand.ToString(); } virtual bool IsDeterministic() override { return Deterministic; } virtual String GetOperatorString() override { return "ld"; } virtual LoadInstruction * Clone() override { auto rs = new LoadInstruction(*this); if (!rs->Type) printf("shit"); return rs; } virtual void Accept(InstructionVisitor * visitor) override; }; class DiscardInstruction : public ILInstruction { public: virtual bool IsDeterministic() override { return true; } virtual bool HasSideEffect() override { return true; } virtual String ToString() override { return "discard"; } virtual String GetOperatorString() override { return "discard"; } virtual DiscardInstruction * Clone() override { return new DiscardInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; // store(dest, value) class StoreInstruction : public BinaryInstruction { public: StoreInstruction(){} StoreInstruction(const StoreInstruction &) = default; StoreInstruction(ILOperand * dest, ILOperand * value) { Operands.SetSize(2); Operands[0] = dest; Operands[1] = value; } public: virtual String ToString() override { return "store " + Operands[0].ToString() + ", " + Operands[1].ToString(); } virtual String GetOperatorString() override { return "st"; } virtual bool HasSideEffect() override { return true; } virtual StoreInstruction * Clone() override { return new StoreInstruction(*this); } virtual void Accept(InstructionVisitor * visitor) override; }; class MemberUpdateInstruction : public ILInstruction { public: UseReference Operands[3]; MemberUpdateInstruction() { Operands[0].SetUser(this); Operands[1].SetUser(this); Operands[2].SetUser(this); } MemberUpdateInstruction(const MemberUpdateInstruction & other) : ILInstruction(other) { Operands[0].SetUser(this); Operands[1].SetUser(this); Operands[2].SetUser(this); Operands[0] = other.Operands[0].Ptr(); Operands[1] = other.Operands[1].Ptr(); Operands[2] = other.Operands[2].Ptr(); } MemberUpdateInstruction(ILOperand * var, ILOperand * offset, ILOperand * value) { Operands[0].SetUser(this); Operands[1].SetUser(this); Operands[2].SetUser(this); Operands[0] = var; Operands[1] = offset; Operands[2] = value; Type = var->Type->Clone(); } virtual OperandIterator begin() override { return Operands; } virtual OperandIterator end() override { return Operands + 3; } virtual String ToString() override { return Name + " = update " + Operands[0].ToString() + ", " + Operands[1].ToString() + "," + Operands[2].ToString(); } virtual String GetOperatorString() override { return "update"; } virtual MemberUpdateInstruction * Clone() override { return new MemberUpdateInstruction(*this); } virtual bool HasSideEffect() override { return true; } virtual void Accept(InstructionVisitor * visitor) override; }; class InstructionVisitor : public Object { public: virtual void VisitAddInstruction(AddInstruction *){} virtual void VisitSubInstruction(SubInstruction *){} virtual void VisitDivInstruction(DivInstruction *){} virtual void VisitMulInstruction(MulInstruction *){} virtual void VisitModInstruction(ModInstruction *){} virtual void VisitNegInstruction(NegInstruction *){} virtual void VisitAndInstruction(AndInstruction *){} virtual void VisitOrInstruction(OrInstruction *){} virtual void VisitBitAndInstruction(BitAndInstruction *){} virtual void VisitBitOrInstruction(BitOrInstruction *){} virtual void VisitBitXorInstruction(BitXorInstruction *){} virtual void VisitShlInstruction(ShlInstruction *){} virtual void VisitShrInstruction(ShrInstruction *){} virtual void VisitBitNotInstruction(BitNotInstruction *){} virtual void VisitNotInstruction(NotInstruction *){} virtual void VisitCmpeqlInstruction(CmpeqlInstruction *){} virtual void VisitCmpneqInstruction(CmpneqInstruction *){} virtual void VisitCmpltInstruction(CmpltInstruction *){} virtual void VisitCmpleInstruction(CmpleInstruction *){} virtual void VisitCmpgtInstruction(CmpgtInstruction *){} virtual void VisitCmpgeInstruction(CmpgeInstruction *){} virtual void VisitLoadInstruction(LoadInstruction *){} virtual void VisitStoreInstruction(StoreInstruction *){} virtual void VisitCopyInstruction(CopyInstruction *){} virtual void VisitAllocVarInstruction(AllocVarInstruction *){} virtual void VisitFetchArgInstruction(FetchArgInstruction *){} virtual void VisitCastInstruction(CastInstruction *){} virtual void VisitInt2FloatInstruction(Int2FloatInstruction *){} virtual void VisitFloat2IntInstruction(Float2IntInstruction *){} virtual void VisitMemberLoadInstruction(MemberLoadInstruction *){} virtual void VisitMemberUpdateInstruction(MemberUpdateInstruction *) {} virtual void VisitImportInstruction(ImportInstruction*) {} virtual void VisitExportInstruction(ExportInstruction*) {} virtual void VisitSelectInstruction(SelectInstruction *){} virtual void VisitCallInstruction(CallInstruction *){} virtual void VisitSwitchInstruction(SwitchInstruction *){} virtual void VisitDiscardInstruction(DiscardInstruction *) {} virtual void VisitLoadInputInstruction(LoadInputInstruction *) {} virtual void VisitPhiInstruction(PhiInstruction *){} virtual void VisitSwizzleInstruction(SwizzleInstruction*) {} virtual void VisitProjectInstruction(ProjectInstruction*) {} }; class ForInstruction : public ILInstruction { public: RefPtr InitialCode, ConditionCode, SideEffectCode, BodyCode; virtual int GetSubBlockCount() override { int count = 0; if (InitialCode) count++; if (ConditionCode) count++; if (SideEffectCode) count++; if (BodyCode) count++; return count; } virtual CFGNode * GetSubBlock(int i) override { int id = 0; if (InitialCode) { if (id == i) return InitialCode.Ptr(); id++; } if (ConditionCode) { if (id == i) return ConditionCode.Ptr(); id++; } if (SideEffectCode) { if (id == i) return SideEffectCode.Ptr(); id++; } if (BodyCode) { if (id == i) return BodyCode.Ptr(); } return nullptr; } virtual String ToString() override { StringBuilder sb; sb << "for ("; if (InitialCode) sb << InitialCode->ToString(); sb << "; "; if (ConditionCode) sb << ConditionCode->ToString(); sb << "; "; if (SideEffectCode) sb << SideEffectCode->ToString(); sb << ")" << EndLine; sb << "{" << EndLine; sb << BodyCode->ToString() << EndLine; sb << "}" << EndLine; return sb.ProduceString(); } }; class IfInstruction : public UnaryInstruction { public: RefPtr TrueCode, FalseCode; virtual int GetSubBlockCount() override { if (FalseCode) return 2; else return 1; } virtual CFGNode * GetSubBlock(int i) override { if (i == 0) return TrueCode.Ptr(); else if (i == 1) return FalseCode.Ptr(); return nullptr; } virtual String ToString() override { StringBuilder sb; sb << "if (" << Operand->ToString() << ")" << EndLine; sb << "{" << EndLine; sb << TrueCode->ToString() << EndLine; sb << "}" << EndLine; if (FalseCode) { sb << "else" << EndLine; sb << "{" << EndLine; sb << FalseCode->ToString() << EndLine; sb << "}" << EndLine; } return sb.ProduceString(); } }; class WhileInstruction : public ILInstruction { public: RefPtr ConditionCode, BodyCode; virtual int GetSubBlockCount() override { return 2; } virtual CFGNode * GetSubBlock(int i) override { if (i == 0) return ConditionCode.Ptr(); else if (i == 1) return BodyCode.Ptr(); return nullptr; } virtual String ToString() override { StringBuilder sb; sb << "while (" << ConditionCode->ToString() << ")" << EndLine; sb << "{" << EndLine; sb << BodyCode->ToString(); sb << "}" << EndLine; return sb.ProduceString(); } }; class DoInstruction : public ILInstruction { public: RefPtr ConditionCode, BodyCode; virtual int GetSubBlockCount() override { return 2; } virtual CFGNode * GetSubBlock(int i) override { if (i == 1) return ConditionCode.Ptr(); else if (i == 0) return BodyCode.Ptr(); return nullptr; } virtual String ToString() override { StringBuilder sb; sb << "{" << EndLine; sb << BodyCode->ToString(); sb << "}" << EndLine; sb << "while (" << ConditionCode->ToString() << ")" << EndLine; return sb.ProduceString(); } }; class ReturnInstruction : public UnaryInstruction { public: ReturnInstruction(ILOperand * op) :UnaryInstruction() { Operand = op; } virtual String ToString() override { return "return " + Operand->ToString() + ";"; } }; class BreakInstruction : public ILInstruction {}; class ContinueInstruction : public ILInstruction {}; class KeyHoleNode { public: String NodeType; int CaptureId = -1; List> Children; bool Match(List & matchResult, ILOperand * instr); static RefPtr Parse(String format); }; } } #endif ================================================ FILE: Source/SpireCore/InsertImplicitImportOperator.cpp ================================================ #include "Closure.h" #include "VariantIR.h" #include "StringObject.h" #include "Naming.h" #include "GetDependencyVisitor.h" namespace Spire { namespace Compiler { class InsertImplicitImportOperatorVisitor : public SyntaxVisitor { private: ShaderIR * shaderIR; GetDependencyVisitor depVisitor; public: ComponentDefinitionIR * currentCompDef = nullptr; EnumerableDictionary> passThroughComponents; public: InsertImplicitImportOperatorVisitor(ShaderIR * ir, DiagnosticSink* err) : SyntaxVisitor(err), shaderIR(ir) {} ComponentDefinitionIR * MakeComponentAvailableAtWorld(String componentUniqueName, String world) { HashSet visitedComponents; return MakeComponentAvailableAtWorldInternal(visitedComponents, componentUniqueName, world); } ComponentDefinitionIR * MakeComponentAvailableAtWorldInternal(HashSet & visitedComponents, String componentUniqueName, String world) { RefPtr refDef; if (passThroughComponents.TryGetValue(EscapeCodeName(componentUniqueName + "_" + world), refDef)) return refDef.Ptr(); if (visitedComponents.Contains(componentUniqueName + "@" + world)) { StringBuilder refs; int count = 0; for (auto & comp : visitedComponents) { refs << comp; if (count != visitedComponents.Count() - 1) refs << ", "; count++; } getSink()->diagnose(currentCompDef->SyntaxNode, Diagnostics::cylicReference, refs.ProduceString()); return nullptr; } visitedComponents.Add(componentUniqueName); ImportPath importPath; int currentPathLength = 1 << 30; ComponentDefinitionIR * referencedDef = nullptr; for (auto & compDef : shaderIR->DefinitionsByComponent[componentUniqueName]()) { if (compDef.Value->World == world || compDef.Value->World == "") return compDef.Value; } for (auto & compDef : shaderIR->DefinitionsByComponent[componentUniqueName]()) { auto path = shaderIR->SymbolTable->FindImplicitImportOperatorChain(shaderIR->Shader->Pipeline, compDef.Value->World, world, compDef.Value->Type); if (path.Count() && path.First().Nodes.Count() < currentPathLength) { importPath = path.First(); currentPathLength = importPath.Nodes.Count(); referencedDef = compDef.Value; } } if (referencedDef) { auto & node = importPath.Nodes.Last(); RefPtr thruDef; auto thruDefName = EscapeCodeName(componentUniqueName + "_" + node.TargetWorld); if (!passThroughComponents.TryGetValue(thruDefName, thruDef)) { auto srcDef = MakeComponentAvailableAtWorldInternal(visitedComponents, componentUniqueName, node.ImportOperator->SourceWorld.Content); thruDef = new ComponentDefinitionIR(); thruDef->World = world; thruDef->Dependency.Add(srcDef); srcDef->Users.Add(thruDef.Ptr()); thruDef->OriginalName = referencedDef->OriginalName; thruDef->UniqueName = thruDefName; thruDef->UniqueKey = referencedDef->UniqueKey + "@" + node.TargetWorld; thruDef->IsEntryPoint = false; thruDef->SyntaxNode = new ComponentSyntaxNode(); thruDef->SyntaxNode->Type = thruDef->Type = srcDef->SyntaxNode->Type; thruDef->SyntaxNode->Rate = new RateSyntaxNode(); thruDef->SyntaxNode->Rate->Worlds.Add(RateWorld(node.TargetWorld)); thruDef->SyntaxNode->Name.Content = thruDefName; CloneContext cloneCtx; thruDef->SyntaxNode->TypeNode = srcDef->SyntaxNode->TypeNode->Clone(cloneCtx); auto importExpr = new ImportExpressionSyntaxNode(); importExpr->Type = thruDef->Type; importExpr->ImportOperatorDef = node.ImportOperator->Clone(cloneCtx); importExpr->ImportOperatorDef->Scope->Parent = thruDef->SyntaxNode->Scope.Ptr(); importExpr->ComponentUniqueName = srcDef->UniqueName; for (auto & arg : importExpr->Arguments) arg->Accept(this); importExpr->ImportOperatorDef->Body->Accept(this); thruDef->SyntaxNode->Expression = importExpr; passThroughComponents[thruDefName] = thruDef; } visitedComponents.Remove(componentUniqueName + "@" + world); return thruDef.Ptr(); } else { auto targetComp = shaderIR->Shader->AllComponents[componentUniqueName](); getSink()->diagnose(currentCompDef->SyntaxNode, Diagnostics::noApplicableImplicitImportOperator, targetComp.Symbol->Name, world, currentCompDef->OriginalName); getSink()->diagnose(targetComp.Symbol->Implementations.First()->SyntaxNode, Diagnostics::seeDefinitionOf, targetComp.Symbol->Name); return currentCompDef; } } RefPtr ProcessComponentReference(String componentUniqueName) { auto refDef = MakeComponentAvailableAtWorld(componentUniqueName, currentCompDef->World); auto refNode = new VarExpressionSyntaxNode(); if (refDef) { refNode->Variable = refDef->UniqueName; refNode->Type = refDef->Type; refNode->Tags["ComponentReference"] = new StringObject(refDef->UniqueName); currentCompDef->Dependency.Add(refDef); refDef->Users.Add(currentCompDef); } return refNode; } RefPtr VisitVarExpression(VarExpressionSyntaxNode * var) override { RefPtr refCompObj; if (var->Tags.TryGetValue("ComponentReference", refCompObj)) { auto refComp = refCompObj.As().Ptr(); return ProcessComponentReference(refComp->Content); } return var; } RefPtr VisitMemberExpression(MemberExpressionSyntaxNode * member) override { RefPtr refCompObj; if (member->Tags.TryGetValue("ComponentReference", refCompObj)) { auto refComp = refCompObj.As().Ptr(); return ProcessComponentReference(refComp->Content); } else member->BaseExpression = member->BaseExpression->Accept(this).As(); return member; } RefPtr VisitImportExpression(ImportExpressionSyntaxNode * import) override { auto refDef = MakeComponentAvailableAtWorld(import->ComponentUniqueName, import->ImportOperatorDef->SourceWorld.Content); if (refDef) import->ComponentUniqueName = refDef->UniqueName; depVisitor.Result.Clear(); import->ImportOperatorDef->Accept(&depVisitor); for (auto & x : depVisitor.Result) { ProcessComponentReference(x.ReferencedComponent); } return import; } }; void InsertImplicitImportOperators(DiagnosticSink * err, ShaderIR * shader) { InsertImplicitImportOperatorVisitor visitor(shader, err); for (auto & comp : shader->Definitions) { for (auto & dep : comp->Dependency) dep->Users.Remove(comp.Ptr()); comp->ClearDependency(); } for (auto & comp : shader->Definitions) { visitor.currentCompDef = comp.Ptr(); comp->SyntaxNode->Accept(&visitor); } for (auto & comp : visitor.passThroughComponents) { shader->Definitions.Add(comp.Value); EnumerableDictionary defs; defs[comp.Value->World] = comp.Value.Ptr(); shader->DefinitionsByComponent[comp.Key] = defs; } } } } ================================================ FILE: Source/SpireCore/KeyHoleMatching.cpp ================================================ #include "IL.h" #include "../CoreLib/Tokenizer.h" namespace Spire { namespace Compiler { RefPtr ParseInternal(CoreLib::Text::TokenReader & parser) { RefPtr result = new KeyHoleNode(); result->NodeType = parser.ReadWord(); if (parser.LookAhead("<")) { parser.ReadToken(); result->CaptureId = parser.ReadInt(); parser.ReadToken(); } if (parser.LookAhead("(")) { while (!parser.LookAhead(")")) { result->Children.Add(ParseInternal(parser)); if (parser.LookAhead(",")) parser.ReadToken(); else { break; } } parser.Read(")"); } return result; } RefPtr KeyHoleNode::Parse(String format) { CoreLib::Text::TokenReader parser(format); return ParseInternal(parser); } bool KeyHoleNode::Match(List & matchResult, ILOperand * instr) { bool matches = false; if (NodeType == "store") matches = dynamic_cast(instr) != nullptr; else if (NodeType == "op") matches = true; else if (NodeType == "load") matches = dynamic_cast(instr) != nullptr; else if (NodeType == "add") matches = dynamic_cast(instr) != nullptr; else if (NodeType == "mu") matches = dynamic_cast(instr) != nullptr; else if (NodeType == "sub") matches = dynamic_cast(instr) != nullptr; else if (NodeType == "cal") matches = dynamic_cast(instr) != nullptr; else if (NodeType == "switch") matches = dynamic_cast(instr) != nullptr; if (matches) { if (Children.Count() > 0) { ILInstruction * cinstr = dynamic_cast(instr); if (cinstr != nullptr) { int opId = 0; for (auto & op : *cinstr) { if (opId >= Children.Count()) { matches = false; break; } matches = matches && Children[opId]->Match(matchResult, &op); opId++; } if (opId != Children.Count()) matches = false; } else matches = false; } } if (matches && CaptureId != -1) { matchResult.SetSize(Math::Max(matchResult.Count(), CaptureId + 1)); matchResult[CaptureId] = instr; } return matches; } } } ================================================ FILE: Source/SpireCore/Lexer.cpp ================================================ #include "Lexer.h" #include "../CoreLib/Tokenizer.h" #include using namespace CoreLib::Text; namespace Spire { namespace Compiler { static Token GetEndOfFileToken() { return Token(TokenType::EndOfFile, "", 0, 0, 0, ""); } Token* TokenList::begin() const { assert(mTokens.Count()); return &mTokens[0]; } Token* TokenList::end() const { assert(mTokens.Count()); assert(mTokens[mTokens.Count()-1].Type == TokenType::EndOfFile); return &mTokens[mTokens.Count() - 1]; } TokenSpan::TokenSpan() : mBegin(NULL) , mEnd (NULL) {} TokenReader::TokenReader() : mCursor(NULL) , mEnd (NULL) {} Token TokenReader::PeekToken() const { if (!mCursor) return GetEndOfFileToken(); Token token = *mCursor; if (mCursor == mEnd) token.Type = TokenType::EndOfFile; return token; } CoreLib::Text::TokenType TokenReader::PeekTokenType() const { if (mCursor == mEnd) return TokenType::EndOfFile; assert(mCursor); return mCursor->Type; } CodePosition TokenReader::PeekLoc() const { if (!mCursor) return CodePosition(); assert(mCursor); return mCursor->Position; } Token TokenReader::AdvanceToken() { if (!mCursor) return GetEndOfFileToken(); Token token = *mCursor; if (mCursor == mEnd) token.Type = TokenType::EndOfFile; else mCursor++; return token; } TokenList Lexer::Parse(const String & fileName, const String & str, DiagnosticSink * sink) { TokenList tokenList; tokenList.mTokens = CoreLib::Text::TokenizeText(fileName, str, [&](CoreLib::Text::TokenizeErrorType errType, CoreLib::Text::CodePosition pos) { auto curChar = str[pos.Pos]; switch (errType) { case CoreLib::Text::TokenizeErrorType::InvalidCharacter: sink->diagnose(pos, Diagnostics::illegalCharacter, String((unsigned char)curChar, 16)); break; case CoreLib::Text::TokenizeErrorType::InvalidEscapeSequence: sink->diagnose(pos, Diagnostics::illegalCharacterLiteral); break; default: break; } }); // Add an end-of-file token so that we can reference it in diagnostic messages tokenList.mTokens.Add(Token(TokenType::EndOfFile, "", 0, 0, 0, fileName, TokenFlag::AtStartOfLine | TokenFlag::AfterWhitespace)); return tokenList; } } } ================================================ FILE: Source/SpireCore/Lexer.h ================================================ #ifndef RASTER_RENDERER_LEXER_H #define RASTER_RENDERER_LEXER_H #include "../CoreLib/Basic.h" #include "Diagnostics.h" namespace Spire { namespace Compiler { using namespace CoreLib::Basic; struct TokenList { Token* begin() const; Token* end() const; List mTokens; }; struct TokenSpan { TokenSpan(); TokenSpan( TokenList const& tokenList) : mBegin(tokenList.begin()) , mEnd (tokenList.end ()) {} Token* begin() const { return mBegin; } Token* end () const { return mEnd ; } int GetCount() { return (int)(mEnd - mBegin); } Token* mBegin; Token* mEnd; }; struct TokenReader { TokenReader(); explicit TokenReader(TokenSpan const& tokens) : mCursor(tokens.begin()) , mEnd (tokens.end ()) {} explicit TokenReader(TokenList const& tokens) : mCursor(tokens.begin()) , mEnd (tokens.end ()) {} bool IsAtEnd() const { return mCursor == mEnd; } Token PeekToken() const; CoreLib::Text::TokenType PeekTokenType() const; CodePosition PeekLoc() const; Token AdvanceToken(); int GetCount() { return (int)(mEnd - mCursor); } Token* mCursor; Token* mEnd; }; class Lexer { public: TokenList Parse(const String & fileName, const String & str, DiagnosticSink * sink); }; } } #endif ================================================ FILE: Source/SpireCore/Naming.cpp ================================================ #include "Naming.h" namespace Spire { namespace Compiler { using namespace CoreLib; CoreLib::String EscapeCodeName(CoreLib::String str) { StringBuilder sb; bool isUnderScore = false; for (auto ch : str) { if (ch == '_' || !((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { if (isUnderScore) sb << "I_"; else sb << "_"; isUnderScore = true; } else { isUnderScore = false; sb << ch; } } if (isUnderScore) sb << "I"; return sb.ProduceString(); } } } ================================================ FILE: Source/SpireCore/Naming.h ================================================ #ifndef SPIRE_NAMING_H #define SPIRE_NAMING_H #include "../CoreLib/Basic.h" namespace Spire { namespace Compiler { CoreLib::String EscapeCodeName(CoreLib::String str); } } #endif ================================================ FILE: Source/SpireCore/NatvisFile.natvis ================================================ {{CFG Basic Block}} kvPairs.FCount kvPairs.FHead pNext Value ================================================ FILE: Source/SpireCore/NewSpirVCodeGen.cpp ================================================ #include "CodeGenBackend.h" #include "../CoreLib/Tokenizer.h" #include "IL.h" #include "Syntax.h" #include #include #include "../CoreLib/TextIO.h" #include "../CoreLib/LibIO.h" using namespace CoreLib::Basic; namespace Spire { namespace Compiler { void PrintILShader(ILShader * shader) { printf("%S\n", shader->Name.ToWString()); printf("%S\n", shader->Position.ToString().ToWString()); printf("\n---\n\n"); for (auto& stage : shader->Stages) { printf("Stage: %S\n", stage.Key.ToWString()); auto& stageIL = stage.Value; int maxAttrNameLength = 0; int maxAttrValueLength = 0; for (auto& attr : stageIL->Attributes) { if (attr.Value.Name.Length() > maxAttrNameLength) maxAttrNameLength = attr.Value.Name.Length(); if (attr.Value.Value.Length() > maxAttrValueLength) maxAttrValueLength = attr.Value.Value.Length(); } for (auto& attr : stageIL->Attributes) { printf("\t%-*S = %-*S (%S)\n", maxAttrNameLength, attr.Value.Name.ToWString(), maxAttrValueLength, attr.Value.Value.ToWString(), attr.Value.Position.ToString().ToWString()); } printf("\n"); } printf("\n---\n\n"); for (auto& world : shader->Worlds) { printf("World: %S\n", world.Key.ToWString()); //auto& worldIL = world.Value; } } class SpirVCodeGen : public CodeGenBackend { public: virtual CompiledShaderSource GenerateShader(CompileResult & /*result*/, SymbolTable * /*symbols*/, ILShader * shader, DiagnosticSink * /*err*/) override { PrintILShader(shader); system("pause"); return CompiledShaderSource(); } LayoutRule GetDefaultLayoutRule() override { return LayoutRule::Std140; } }; CodeGenBackend * CreateSpirVCodeGen() { return new SpirVCodeGen(); } } } ================================================ FILE: Source/SpireCore/Parser.cpp ================================================ #include "Parser.h" #include namespace Spire { namespace Compiler { const int MaxExprLevel = 12; // TODO: implement two pass parsing for file reference and struct type recognition class Parser { public: int anonymousParamCounter = 0; RefPtr currentScope; TokenReader tokenReader; DiagnosticSink * sink; String fileName; HashSet typeNames; HashSet classNames; bool isInImportOperator = false; // Is the parser in a "recovering" state? // During recovery we don't emit additional errors, until we find // a token that we expected, when we exit recovery. bool isRecovering = false; void FillPosition(SyntaxNode * node) { node->Position = tokenReader.PeekLoc(); node->Scope = currentScope; } void PushScope(ContainerDecl* containerDecl) { currentScope = new Scope(currentScope, containerDecl); } void PopScope() { currentScope = currentScope->Parent; } Parser(TokenSpan const& _tokens, DiagnosticSink * sink, String _fileName) : tokenReader(_tokens), sink(sink), fileName(_fileName) { typeNames.Add("int"); typeNames.Add("uint"); typeNames.Add("bool"); typeNames.Add("float"); typeNames.Add("half"); typeNames.Add("void"); typeNames.Add("ivec2"); typeNames.Add("ivec3"); typeNames.Add("ivec4"); typeNames.Add("uvec2"); typeNames.Add("uvec3"); typeNames.Add("uvec4"); typeNames.Add("vec2"); typeNames.Add("vec3"); typeNames.Add("vec4"); typeNames.Add("mat3"); typeNames.Add("mat4"); typeNames.Add("mat4x4"); typeNames.Add("mat3x3"); typeNames.Add("int2"); typeNames.Add("int3"); typeNames.Add("int4"); typeNames.Add("uint2"); typeNames.Add("uint3"); typeNames.Add("uint4"); typeNames.Add("float2"); typeNames.Add("float3"); typeNames.Add("float4"); typeNames.Add("half2"); typeNames.Add("half3"); typeNames.Add("half4"); typeNames.Add("float3x3"); typeNames.Add("float4x4"); typeNames.Add("half3x3"); typeNames.Add("half4x4"); typeNames.Add("Texture1D"); typeNames.Add("Texture2D"); typeNames.Add("Texture2DArray"); typeNames.Add("Texture2DArrayShadow"); typeNames.Add("TextureCube"); typeNames.Add("TextureCubeShadow"); typeNames.Add("TextureCubeArray"); typeNames.Add("TextureCubeShadowArray"); typeNames.Add("TextureCubeArrayShadow"); typeNames.Add("Texture3D"); typeNames.Add("texture"); typeNames.Add("Texture"); typeNames.Add("sampler"); typeNames.Add("SamplerState"); typeNames.Add("SamplerComparisonState"); typeNames.Add("sampler_state"); typeNames.Add("Uniform"); typeNames.Add("StructuredBuffer"); typeNames.Add("RWStructuredBuffer"); typeNames.Add("PackedBuffer"); typeNames.Add("StorageBuffer"); typeNames.Add("Patch"); } RefPtr Parse(); Token ReadToken(); Token ReadToken(CoreLib::Text::TokenType type); Token ReadToken(const char * string); bool LookAheadToken(CoreLib::Text::TokenType type, int offset = 0); bool LookAheadToken(const char * string, int offset = 0); Token ReadTypeKeyword(); bool IsTypeKeyword(); RefPtr ParseProgram(); RefPtr ParseShader(); RefPtr ParseTemplateShader(); RefPtr ParseTemplateShaderParameter(); RefPtr ParseInterface(); RefPtr ParsePipeline(); RefPtr ParseStage(); RefPtr ParseWorld(); RefPtr ParseRate(); RefPtr ParseImportInner(); RefPtr ParseImportStatement(); RefPtr ParseImportOperator(); RefPtr ParseFunction(bool parseBody = true); RefPtr ParseStruct(); RefPtr ParseStatement(); RefPtr ParseBlockStatement(); RefPtr ParseVarDeclrStatement(); RefPtr ParseIfStatement(); RefPtr ParseForStatement(); RefPtr ParseWhileStatement(); RefPtr ParseDoWhileStatement(); RefPtr ParseBreakStatement(); RefPtr ParseContinueStatement(); RefPtr ParseReturnStatement(); RefPtr ParseExpressionStatement(); RefPtr ParseExpression(int level = 0); RefPtr ParseLeafExpression(); RefPtr ParseParameter(); RefPtr ParseType(); Parser & operator = (const Parser &) = delete; }; static void Unexpected( Parser* parser) { // Don't emit "unexpected token" errors if we are in recovering mode if (!parser->isRecovering) { parser->sink->diagnose(parser->tokenReader.PeekLoc(), Diagnostics::unexpectedToken, parser->tokenReader.PeekTokenType()); // Switch into recovery mode, to suppress additional errors parser->isRecovering = true; } } static void Unexpected( Parser* parser, char const* expected) { // Don't emit "unexpected token" errors if we are in recovering mode if (!parser->isRecovering) { parser->sink->diagnose(parser->tokenReader.PeekLoc(), Diagnostics::unexpectedTokenExpectedTokenName, parser->tokenReader.PeekTokenType(), expected); // Switch into recovery mode, to suppress additional errors parser->isRecovering = true; } } static void Unexpected( Parser* parser, CoreLib::Text::TokenType expected) { // Don't emit "unexpected token" errors if we are in recovering mode if (!parser->isRecovering) { parser->sink->diagnose(parser->tokenReader.PeekLoc(), Diagnostics::unexpectedTokenExpectedTokenType, parser->tokenReader.PeekTokenType(), expected); // Switch into recovery mode, to suppress additional errors parser->isRecovering = true; } } static CoreLib::Text::TokenType SkipToMatchingToken(TokenReader* reader, CoreLib::Text::TokenType tokenType); // Skip a singel balanced token, which is either a single token in // the common case, or a matched pair of tokens for `()`, `[]`, and `{}` static CoreLib::Text::TokenType SkipBalancedToken( TokenReader* reader) { CoreLib::Text::TokenType tokenType = reader->AdvanceToken().Type; switch (tokenType) { default: break; case TokenType::LParent: tokenType = SkipToMatchingToken(reader, TokenType::RParent); break; case TokenType::LBrace: tokenType = SkipToMatchingToken(reader, TokenType::RBrace); break; case TokenType::LBracket: tokenType = SkipToMatchingToken(reader, TokenType::RBracket); break; } return tokenType; } // Skip balanced static CoreLib::Text::TokenType SkipToMatchingToken( TokenReader* reader, CoreLib::Text::TokenType tokenType) { for (;;) { if (reader->IsAtEnd()) return TokenType::EndOfFile; if (reader->PeekTokenType() == tokenType) { reader->AdvanceToken(); return tokenType; } SkipBalancedToken(reader); } } // Is the given token type one that is used to "close" a // balanced construct. static bool IsClosingToken(CoreLib::Text::TokenType tokenType) { switch (tokenType) { case TokenType::EndOfFile: case TokenType::RBracket: case TokenType::RParent: case TokenType::RBrace: return true; default: return false; } } // Expect an identifier token with the given content, and consume it. Token Parser::ReadToken(const char* expected) { if (tokenReader.PeekTokenType() == TokenType::Identifier && tokenReader.PeekToken().Content == expected) { isRecovering = false; return tokenReader.AdvanceToken(); } if (!isRecovering) { Unexpected(this, expected); return tokenReader.PeekToken(); } else { // Try to find a place to recover for (;;) { // The token we expected? // Then exit recovery mode and pretend like all is well. if (tokenReader.PeekTokenType() == TokenType::Identifier && tokenReader.PeekToken().Content == expected) { isRecovering = false; return tokenReader.AdvanceToken(); } // Don't skip past any "closing" tokens. if (IsClosingToken(tokenReader.PeekTokenType())) { return tokenReader.PeekToken(); } // Skip balanced tokens and try again. SkipBalancedToken(&tokenReader); } } } Token Parser::ReadToken() { return tokenReader.AdvanceToken(); } static bool TryRecover( Parser* parser, CoreLib::Text::TokenType const* recoverBefore, int recoverBeforeCount, CoreLib::Text::TokenType const* recoverAfter, int recoverAfterCount) { if (!parser->isRecovering) return true; // Determine if we are looking for a closing token at all... bool lookingForClose = false; for (int ii = 0; ii < recoverBeforeCount; ++ii) { if (IsClosingToken(recoverBefore[ii])) lookingForClose = true; } for (int ii = 0; ii < recoverAfterCount; ++ii) { if (IsClosingToken(recoverAfter[ii])) lookingForClose = true; } TokenReader* tokenReader = &parser->tokenReader; for (;;) { CoreLib::Text::TokenType peek = tokenReader->PeekTokenType(); // Is the next token in our recover-before set? // If so, then we have recovered successfully! for (int ii = 0; ii < recoverBeforeCount; ++ii) { if (peek == recoverBefore[ii]) { parser->isRecovering = false; return true; } } // If we are looking at a token in our recover-after set, // then consume it and recover for (int ii = 0; ii < recoverAfterCount; ++ii) { if (peek == recoverAfter[ii]) { tokenReader->AdvanceToken(); parser->isRecovering = false; return true; } } // Don't try to skip past end of file if (peek == TokenType::EndOfFile) return false; switch (peek) { // Don't skip past simple "closing" tokens, *unless* // we are looking for a closing token case TokenType::RParent: case TokenType::RBracket: if (!lookingForClose) return false; break; // never skip a `}`, to avoid spurious errors case TokenType::RBrace: return false; } // Skip balanced tokens and try again. CoreLib::Text::TokenType skipped = SkipBalancedToken(tokenReader); // If we happened to find a matched pair of tokens, and // the end of it was a token we were looking for, // then recover here for (int ii = 0; ii < recoverAfterCount; ++ii) { if (skipped == recoverAfter[ii]) { parser->isRecovering = false; return true; } } } } static bool TryRecoverBefore( Parser* parser, CoreLib::Text::TokenType before0) { CoreLib::Text::TokenType recoverBefore[] = { before0 }; return TryRecover(parser, recoverBefore, 1, nullptr, 0); } // Default recovery strategy, to use inside `{}`-delimeted blocks. static bool TryRecover( Parser* parser) { CoreLib::Text::TokenType recoverBefore[] = { TokenType::RBrace }; CoreLib::Text::TokenType recoverAfter[] = { TokenType::Semicolon }; return TryRecover(parser, recoverBefore, 1, recoverAfter, 1); } Token Parser::ReadToken(CoreLib::Text::TokenType expected) { if (tokenReader.PeekTokenType() == expected) { isRecovering = false; return tokenReader.AdvanceToken(); } if (!isRecovering) { Unexpected(this, expected); return tokenReader.PeekToken(); } else { // Try to find a place to recover if (TryRecoverBefore(this, expected)) { isRecovering = false; return tokenReader.AdvanceToken(); } return tokenReader.PeekToken(); } } bool Parser::LookAheadToken(const char * string, int offset) { TokenReader r = tokenReader; for (int ii = 0; ii < offset; ++ii) r.AdvanceToken(); return r.PeekTokenType() == TokenType::Identifier && r.PeekToken().Content == string; } bool Parser::LookAheadToken(CoreLib::Text::TokenType type, int offset) { TokenReader r = tokenReader; for (int ii = 0; ii < offset; ++ii) r.AdvanceToken(); return r.PeekTokenType() == type; } // Consume a token and return true it if matches, otherwise false bool AdvanceIf(Parser* parser, CoreLib::Text::TokenType tokenType) { if (parser->LookAheadToken(tokenType)) { parser->ReadToken(); return true; } return false; } // Consume a token and return true it if matches, otherwise false bool AdvanceIf(Parser* parser, char const* text) { if (parser->LookAheadToken(text)) { parser->ReadToken(); return true; } return false; } // Consume a token and return true if it matches, otherwise check // for end-of-file and expect that token (potentially producing // an error) and return true to maintain forward progress. // Otherwise return false. bool AdvanceIfMatch(Parser* parser, CoreLib::Text::TokenType tokenType) { // If we've run into a syntax error, but haven't recovered inside // the block, then try to recover here. if (parser->isRecovering) { TryRecoverBefore(parser, tokenType); } if (AdvanceIf(parser, tokenType)) return true; if (parser->tokenReader.PeekTokenType() == TokenType::EndOfFile) { parser->ReadToken(tokenType); return true; } return false; } Token Parser::ReadTypeKeyword() { if(!IsTypeKeyword()) { if (!isRecovering) { sink->diagnose(tokenReader.PeekLoc(), Diagnostics::typeNameExpectedBut, tokenReader.PeekTokenType()); } return tokenReader.PeekToken(); } return tokenReader.AdvanceToken(); } bool Parser::IsTypeKeyword() { return tokenReader.PeekTokenType() == TokenType::Identifier && typeNames.Contains(tokenReader.PeekToken().Content); } RefPtr Parser::Parse() { return ParseProgram(); } RefPtr ParseTypeDef(Parser* parser) { // Consume the `typedef` keyword parser->ReadToken("typedef"); // TODO(tfoley): parse an actual declarator auto type = parser->ParseType(); auto nameToken = parser->ReadToken(TokenType::Identifier); RefPtr typeDefDecl = new TypeDefDecl(); typeDefDecl->Name = nameToken; typeDefDecl->TypeNode = type; parser->typeNames.Add(nameToken.Content); return typeDefDecl; } static Modifiers ParseModifiers(Parser* parser) { Modifiers modifiers; RefPtr* modifierLink = &modifiers.first; for (;;) { if (AdvanceIf(parser, "in")) { modifiers.flags |= ModifierFlag::In; } else if (AdvanceIf(parser, "inout")) { modifiers.flags |= ModifierFlag::InOut; } else if (AdvanceIf(parser, "input")) { modifiers.flags |= ModifierFlag::Input; } else if (AdvanceIf(parser, "out")) { modifiers.flags |= ModifierFlag::Out; } else if (AdvanceIf(parser, "uniform")) { modifiers.flags |= ModifierFlag::Uniform; } else if (AdvanceIf(parser, "const")) { modifiers.flags |= ModifierFlag::Const; } else if (AdvanceIf(parser, "instance")) { modifiers.flags |= ModifierFlag::Instance; } else if (AdvanceIf(parser, "__builtin")) { modifiers.flags |= ModifierFlag::Builtin; } else if (AdvanceIf(parser, "layout")) { parser->ReadToken(TokenType::LParent); StringBuilder layoutSB; while (!AdvanceIfMatch(parser, TokenType::RParent)) { layoutSB.Append(parser->ReadToken(TokenType::Identifier).Content); if (parser->LookAheadToken(TokenType::OpAssign)) { layoutSB.Append(parser->ReadToken(TokenType::OpAssign).Content); layoutSB.Append(parser->ReadToken(TokenType::IntLiterial).Content); } if (AdvanceIf(parser, TokenType::RParent)) break; parser->ReadToken(TokenType::Comma); layoutSB.Append(", "); } RefPtr modifier = new LayoutModifier(); modifier->LayoutString = layoutSB.ProduceString(); *modifierLink = modifier; modifierLink = &modifier->next; } else if (AdvanceIf(parser, "specialize")) { RefPtr modifier = new SpecializeModifier(); if (AdvanceIf(parser, TokenType::LParent)) { while (!AdvanceIfMatch(parser, TokenType::RParent)) { auto expr = parser->ParseExpression(); modifier->Values.Add(expr); if (AdvanceIf(parser, TokenType::RParent)) break; parser->ReadToken(TokenType::Comma); } } *modifierLink = modifier; modifierLink = &modifier->next; } else if (AdvanceIf(parser, "inline")) { modifiers.flags |= ModifierFlag::Inline; } else if (AdvanceIf(parser, "public")) { modifiers.flags |= ModifierFlag::Public; } else if (AdvanceIf(parser, "require")) { modifiers.flags |= ModifierFlag::Require; } else if (AdvanceIf(parser, "param")) { modifiers.flags |= ModifierFlag::Param; } else if (AdvanceIf(parser, "extern")) { modifiers.flags |= ModifierFlag::Extern; } else if (AdvanceIf(parser, TokenType::LBracket)) { auto name = parser->ReadToken(TokenType::Identifier).Content; Token valueToken; if (AdvanceIf(parser, TokenType::Colon)) { valueToken = parser->ReadToken(TokenType::StringLiterial); } parser->ReadToken(TokenType::RBracket); RefPtr modifier = new SimpleAttribute(); modifier->Key = name; modifier->Value = valueToken; *modifierLink = modifier; modifierLink = &modifier->next; } else if (AdvanceIf(parser, "__intrinsic")) { modifiers.flags |= ModifierFlag::Intrinsic; } else { // Done with modifier list return modifiers; } } } static RefPtr ParseUsing( Parser* parser) { parser->ReadToken("using"); if (parser->tokenReader.PeekTokenType() == TokenType::StringLiterial) { auto usingDecl = new UsingFileDecl(); usingDecl->fileName = parser->ReadToken(TokenType::StringLiterial); parser->ReadToken(TokenType::Semicolon); return usingDecl; } else { // This is an import decl return parser->ParseImportInner(); } } static Token ParseDeclName( Parser* parser) { Token name; if (AdvanceIf(parser, "operator")) { name = parser->ReadToken(); switch (name.Type) { case TokenType::OpAdd: case TokenType::OpSub: case TokenType::OpMul: case TokenType::OpDiv: case TokenType::OpMod: case TokenType::OpNot: case TokenType::OpBitNot: case TokenType::OpLsh: case TokenType::OpRsh: case TokenType::OpEql: case TokenType::OpNeq: case TokenType::OpGreater: case TokenType::OpLess: case TokenType::OpGeq: case TokenType::OpLeq: case TokenType::OpAnd: case TokenType::OpOr: case TokenType::OpBitXor: case TokenType::OpBitAnd: case TokenType::OpBitOr: case TokenType::OpInc: case TokenType::OpDec: break; default: parser->sink->diagnose(name.Position, Diagnostics::invalidOperator, name.Content); break; } } else { name = parser->ReadToken(TokenType::Identifier); } return name; } struct DeclaratorInfo { RefPtr rate; RefPtr typeSpec; Token nameToken; }; static void ParseFuncDeclHeader( Parser* parser, DeclaratorInfo const& declaratorInfo, RefPtr decl) { parser->PushScope(decl.Ptr()); parser->anonymousParamCounter = 0; parser->FillPosition(decl.Ptr()); decl->Position = declaratorInfo.nameToken.Position; decl->Name = declaratorInfo.nameToken; decl->ReturnTypeNode = declaratorInfo.typeSpec; parser->ReadToken(TokenType::LParent); while (!AdvanceIfMatch(parser, TokenType::RParent)) { decl->Members.Add(parser->ParseParameter()); if (AdvanceIf(parser, TokenType::RParent)) break; parser->ReadToken(TokenType::Comma); } } static void ParseFuncDeclHeader( Parser* parser, DeclaratorInfo const& declaratorInfo, RefPtr decl) { parser->PushScope(decl.Ptr()); parser->anonymousParamCounter = 0; parser->FillPosition(decl.Ptr()); decl->Position = declaratorInfo.nameToken.Position; decl->Name = declaratorInfo.nameToken; decl->TypeNode = declaratorInfo.typeSpec; parser->ReadToken(TokenType::LParent); while (!AdvanceIfMatch(parser, TokenType::RParent)) { decl->Members.Add(parser->ParseParameter()); if (AdvanceIf(parser, TokenType::RParent)) break; parser->ReadToken(TokenType::Comma); } } static RefPtr ParseFuncDecl( Parser* parser, ContainerDecl* containerDecl, DeclaratorInfo const& declaratorInfo) { if (dynamic_cast(containerDecl)) { // inside a shader, we create a component decl RefPtr decl = new ComponentSyntaxNode(); ParseFuncDeclHeader(parser, declaratorInfo, decl); // decl->Rate = declaratorInfo.rate; // if (AdvanceIf(parser, TokenType::Semicolon)) { // empty body } else { decl->BlockStatement = parser->ParseBlockStatement(); } parser->PopScope(); return decl; } else { // everywhere else, we create an ordinary `FunctionSyntaxNode` RefPtr decl = new FunctionSyntaxNode(); ParseFuncDeclHeader(parser, declaratorInfo, decl); if (AdvanceIf(parser, TokenType::Semicolon)) { // empty body } else { decl->Body = parser->ParseBlockStatement(); } parser->PopScope(); return decl; } } static RefPtr CreateVarDeclForContext( ContainerDecl* containerDecl ) { if (dynamic_cast(containerDecl)) { return new StructField(); } else if (dynamic_cast(containerDecl)) { return new ParameterSyntaxNode(); } else { return new Variable(); } } static RefPtr ParseVarDecl( Parser* parser, ContainerDecl* containerDecl, DeclaratorInfo const& declaratorInfo) { if (dynamic_cast(containerDecl)) { // inside a shader, we create a component decl RefPtr decl = new ComponentSyntaxNode(); parser->FillPosition(decl.Ptr()); decl->Position = declaratorInfo.nameToken.Position; // decl->Rate = declaratorInfo.rate; // decl->Name = declaratorInfo.nameToken; decl->TypeNode = declaratorInfo.typeSpec; // Note(tfoley): this case is the one place where a component // declaration differents in any meaningful way from an // ordinary variable declaration. if (parser->tokenReader.PeekTokenType() == TokenType::LBrace) { decl->BlockStatement = parser->ParseBlockStatement(); } else { if (AdvanceIf(parser, TokenType::OpAssign)) { decl->Expression = parser->ParseExpression(); } // TODO(tfoley): support the block case here parser->ReadToken(TokenType::Semicolon); } return decl; } else { // everywhere else, we create an ordinary `VarDeclBase` RefPtr decl = CreateVarDeclForContext(containerDecl); parser->FillPosition(decl.Ptr()); decl->Position = declaratorInfo.nameToken.Position; decl->Name = declaratorInfo.nameToken; decl->TypeNode = declaratorInfo.typeSpec; if (AdvanceIf(parser, TokenType::OpAssign)) { decl->Expr = parser->ParseExpression(); } parser->ReadToken(TokenType::Semicolon); return decl; } } static RefPtr ParseDeclaratorDecl( Parser* parser, ContainerDecl* containerDecl) { DeclaratorInfo declaratorInfo; // For now we just parse // // TODO(tfoley): Actual C-style declarator-based parsed. // if (parser->tokenReader.PeekTokenType() == TokenType::At) { declaratorInfo.rate = parser->ParseRate(); } declaratorInfo.typeSpec = parser->ParseType(); declaratorInfo.nameToken = ParseDeclName(parser); // TODO(tfoley): handle array-ness here... // Look at the token after the name to disambiguate switch (parser->tokenReader.PeekTokenType()) { case TokenType::LParent: // It must be a function return ParseFuncDecl(parser, containerDecl, declaratorInfo); default: // Assume it is a variable-like declaration return ParseVarDecl(parser, containerDecl, declaratorInfo); } } static RefPtr ParseDeclWithModifiers( Parser* parser, ContainerDecl* containerDecl, Modifiers const& modifiers ) { RefPtr decl; // TODO: actual dispatch! if (parser->LookAheadToken("shader") || parser->LookAheadToken("module")) decl = parser->ParseShader(); else if (parser->LookAheadToken("template")) decl = parser->ParseTemplateShader(); else if (parser->LookAheadToken("pipeline")) decl = parser->ParsePipeline(); else if (parser->LookAheadToken("struct")) decl = parser->ParseStruct(); else if (parser->LookAheadToken("typedef")) decl = ParseTypeDef(parser); else if (parser->LookAheadToken("using")) decl = ParseUsing(parser); else if (parser->LookAheadToken("world")) decl = parser->ParseWorld(); else if (parser->LookAheadToken("import")) decl = parser->ParseImportOperator(); else if (parser->LookAheadToken("stage")) decl = parser->ParseStage(); else if (parser->LookAheadToken("interface")) decl = parser->ParseInterface(); else if (AdvanceIf(parser, TokenType::Semicolon)) { // empty declaration } else decl = ParseDeclaratorDecl(parser, containerDecl); if (decl) { decl->modifiers = modifiers; if (containerDecl) { containerDecl->Members.Add(decl); } } return decl; } static RefPtr ParseDecl( Parser* parser, ContainerDecl* containerDecl) { Modifiers modifiers = ParseModifiers(parser); return ParseDeclWithModifiers(parser, containerDecl, modifiers); } // Parse a body consisting of declarations static void ParseDeclBody( Parser* parser, ContainerDecl* containerDecl, CoreLib::Text::TokenType closingToken) { while(!AdvanceIfMatch(parser, closingToken)) { RefPtr decl = ParseDecl(parser, containerDecl); if (decl) { decl->ParentDecl = containerDecl; //containerDecl->Members.Add(decl); } TryRecover(parser); } } RefPtr Parser::ParseProgram() { RefPtr program = new ProgramSyntaxNode(); PushScope(program.Ptr()); program->Position = CodePosition(0, 0, 0, fileName); program->Scope = currentScope; ParseDeclBody(this, program.Ptr(), TokenType::EndOfFile); PopScope(); assert(!currentScope.Ptr()); currentScope = nullptr; return program; } RefPtr Parser::ParseInterface() { RefPtr node = new InterfaceSyntaxNode(); ReadToken("interface"); PushScope(node.Ptr()); FillPosition(node.Ptr()); node->Name = ReadToken(TokenType::Identifier); ReadToken(TokenType::LBrace); ParseDeclBody(this, node.Ptr(), TokenType::RBrace); PopScope(); return node; } RefPtr Parser::ParseShader() { RefPtr shader = new ShaderSyntaxNode(); if (AdvanceIf(this, "module")) { shader->IsModule = true; } else ReadToken("shader"); PushScope(shader.Ptr()); FillPosition(shader.Ptr()); shader->Name = ReadToken(TokenType::Identifier); while (LookAheadToken("targets") || LookAheadToken("implements")) { if (AdvanceIf(this, "targets")) { shader->ParentPipelineName = ReadToken(TokenType::Identifier); } if (AdvanceIf(this, "implements")) { while (!LookAheadToken("implements") && !LookAheadToken("targets") && !LookAheadToken(TokenType::LBrace)) { shader->InterfaceNames.Add(ReadToken(TokenType::Identifier)); if (!AdvanceIf(this, TokenType::Comma)) break; } } } ReadToken(TokenType::LBrace); ParseDeclBody(this, shader.Ptr(), TokenType::RBrace); PopScope(); return shader; } RefPtr Parser::ParseTemplateShader() { RefPtr shader = new TemplateShaderSyntaxNode(); ReadToken("template"); ReadToken("shader"); PushScope(shader.Ptr()); FillPosition(shader.Ptr()); shader->Name = ReadToken(TokenType::Identifier); ReadToken(TokenType::LParent); while (!AdvanceIf(this, TokenType::RParent)) { shader->Parameters.Add(ParseTemplateShaderParameter()); if (!AdvanceIf(this, TokenType::Comma)) { ReadToken(TokenType::RParent); break; } } if (AdvanceIf(this, "targets")) shader->ParentPipelineName = ReadToken(TokenType::Identifier); ReadToken(TokenType::LBrace); ParseDeclBody(this, shader.Ptr(), TokenType::RBrace); PopScope(); return shader; } RefPtr Parser::ParseTemplateShaderParameter() { RefPtr param = new TemplateShaderParameterSyntaxNode(); FillPosition(param.Ptr()); param->ModuleName = ReadToken(TokenType::Identifier); if (AdvanceIf(this, TokenType::Colon)) param->InterfaceName = ReadToken(TokenType::Identifier); return param; } RefPtr Parser::ParsePipeline() { RefPtr pipeline = new PipelineSyntaxNode(); ReadToken("pipeline"); PushScope(pipeline.Ptr()); FillPosition(pipeline.Ptr()); pipeline->Name = ReadToken(TokenType::Identifier); if (AdvanceIf(this, TokenType::Colon)) { pipeline->ParentPipelineName = ReadToken(TokenType::Identifier); } ReadToken(TokenType::LBrace); ParseDeclBody(this, pipeline.Ptr(), TokenType::RBrace); PopScope(); return pipeline; } RefPtr Parser::ParseStage() { RefPtr stage = new StageSyntaxNode(); ReadToken("stage"); stage->Name = ReadToken(TokenType::Identifier); FillPosition(stage.Ptr()); ReadToken(TokenType::Colon); stage->StageType = ReadToken(TokenType::Identifier); ReadToken(TokenType::LBrace); while (!AdvanceIfMatch(this, TokenType::RBrace)) { auto attribName = ReadToken(TokenType::Identifier); ReadToken(TokenType::Colon); Token attribValue; if (LookAheadToken(TokenType::StringLiterial) || LookAheadToken(TokenType::DoubleLiterial) || LookAheadToken(TokenType::IntLiterial)) attribValue = ReadToken(); else attribValue = ReadToken(TokenType::Identifier); stage->Attributes[attribName.Content] = attribValue; ReadToken(TokenType::Semicolon); } return stage; } RefPtr Parser::ParseWorld() { RefPtr world = new WorldSyntaxNode(); world->modifiers = ParseModifiers(this); ReadToken("world"); FillPosition(world.Ptr()); world->Name = ReadToken(TokenType::Identifier); ReadToken(TokenType::Semicolon); return world; } RefPtr Parser::ParseRate() { RefPtr rate = new RateSyntaxNode(); FillPosition(rate.Ptr()); ReadToken(TokenType::At); auto readWorldRate = [this]() { RateWorld rw; rw.World = ReadToken(TokenType::Identifier); if (AdvanceIf(this, TokenType::OpMul)) { rw.Pinned = true; } return rw; }; if (AdvanceIf(this, TokenType::LParent)) { while (!AdvanceIfMatch(this, TokenType::RParent)) { RateWorld rw = readWorldRate(); rate->Worlds.Add(rw); if (AdvanceIf(this, TokenType::RParent)) break; ReadToken(TokenType::Comma); } } else rate->Worlds.Add(readWorldRate()); return rate; } RefPtr Parser::ParseImportInner() { RefPtr rs = new ImportSyntaxNode(); rs->IsInplace = !LookAheadToken(TokenType::OpAssign, 1); if (!rs->IsInplace) { rs->ObjectName = ReadToken(TokenType::Identifier); ReadToken(TokenType::OpAssign); } FillPosition(rs.Ptr()); rs->ShaderName = ReadToken(TokenType::Identifier); if (LookAheadToken(TokenType::Semicolon)) ReadToken(TokenType::Semicolon); else { ReadToken(TokenType::LParent); while (!AdvanceIfMatch(this, TokenType::RParent)) { RefPtr arg = new ImportArgumentSyntaxNode(); FillPosition(arg.Ptr()); auto expr = ParseExpression(); if (LookAheadToken(TokenType::Colon)) { if (auto varExpr = dynamic_cast(expr.Ptr())) { arg->ArgumentName.Content = varExpr->Variable; arg->ArgumentName.Position = varExpr->Position; } else sink->diagnose(tokenReader.PeekLoc(), Diagnostics::unexpectedColon); ReadToken(TokenType::Colon); arg->Expression = ParseExpression(); } else arg->Expression = expr; rs->Arguments.Add(arg); if (AdvanceIf(this, TokenType::RParent)) break; ReadToken(TokenType::Comma); } ReadToken(TokenType::Semicolon); } return rs; } RefPtr Parser::ParseImportStatement() { RefPtr rs = new ImportStatementSyntaxNode(); FillPosition(rs.Ptr()); ReadToken("import"); rs->Import = ParseImportInner(); return rs; } RefPtr Parser::ParseImportOperator() { RefPtr op = new ImportOperatorDefSyntaxNode(); PushScope(op.Ptr()); FillPosition(op.Ptr()); ReadToken("import"); ReadToken(TokenType::LParent); op->SourceWorld = ReadToken(TokenType::Identifier); ReadToken(TokenType::RightArrow); op->DestWorld = ReadToken(TokenType::Identifier); ReadToken(TokenType::RParent); FillPosition(op.Ptr()); op->Name = ReadToken(TokenType::Identifier); if (LookAheadToken(TokenType::OpLess)) { ReadToken(TokenType::OpLess); op->TypeName = ReadToken(TokenType::Identifier); ReadToken(TokenType::OpGreater); } else { op->TypeName.Position = op->Name.Position; op->TypeName.Content = "TComponentType"; } ReadToken(TokenType::LParent); while (!AdvanceIf(this, TokenType::RParent)) { op->Members.Add(ParseParameter()); if (AdvanceIf(this, TokenType::RParent)) break; ReadToken(TokenType::Comma); } while (LookAheadToken("require")) { ReadToken("require"); op->Requirements.Add(ParseFunction(false)); } isInImportOperator = true; op->Body = ParseBlockStatement(); isInImportOperator = false; PopScope(); return op; } // TODO(tfoley): this definition is now largely redundant (only // used to parse requirements for import operators) RefPtr Parser::ParseFunction(bool parseBody) { anonymousParamCounter = 0; RefPtr function = new FunctionSyntaxNode(); function->modifiers = ParseModifiers(this); PushScope(function.Ptr()); function->ReturnTypeNode = ParseType(); FillPosition(function.Ptr()); Token name; if (LookAheadToken("operator")) { ReadToken(); name = ReadToken(); switch (name.Type) { case TokenType::OpAdd: case TokenType::OpSub: case TokenType::OpMul: case TokenType::OpDiv: case TokenType::OpMod: case TokenType::OpNot: case TokenType::OpBitNot: case TokenType::OpLsh: case TokenType::OpRsh: case TokenType::OpEql: case TokenType::OpNeq: case TokenType::OpGreater: case TokenType::OpLess: case TokenType::OpGeq: case TokenType::OpLeq: case TokenType::OpAnd: case TokenType::OpOr: case TokenType::OpBitXor: case TokenType::OpBitAnd: case TokenType::OpBitOr: case TokenType::OpInc: case TokenType::OpDec: break; default: sink->diagnose(name.Position, Diagnostics::invalidOperator, name.Content); break; } } else { name = ReadToken(TokenType::Identifier); } function->Name = name; ReadToken(TokenType::LParent); while(!AdvanceIfMatch(this, TokenType::RParent)) { function->Members.Add(ParseParameter()); if (AdvanceIf(this, TokenType::RParent)) break; ReadToken(TokenType::Comma); } if (parseBody) { if (!function->IsExtern()) function->Body = ParseBlockStatement(); else ReadToken(TokenType::Semicolon); } PopScope(); return function; } RefPtr Parser::ParseStruct() { RefPtr rs = new StructSyntaxNode(); FillPosition(rs.Ptr()); ReadToken("struct"); rs->Name = ReadToken(TokenType::Identifier); if (LookAheadToken("__intrinsic")) { ReadToken(); rs->IsIntrinsic = true; } typeNames.Add(rs->Name.Content); ReadToken(TokenType::LBrace); ParseDeclBody(this, rs.Ptr(), TokenType::RBrace); return rs; } RefPtr Parser::ParseStatement() { RefPtr statement; if (LookAheadToken(TokenType::LBrace)) statement = ParseBlockStatement(); else if (IsTypeKeyword() || LookAheadToken("const")) statement = ParseVarDeclrStatement(); else if (LookAheadToken("if")) statement = ParseIfStatement(); else if (LookAheadToken("for")) statement = ParseForStatement(); else if (LookAheadToken("while")) statement = ParseWhileStatement(); else if (LookAheadToken("do")) statement = ParseDoWhileStatement(); else if (LookAheadToken("break")) statement = ParseBreakStatement(); else if (LookAheadToken("continue")) statement = ParseContinueStatement(); else if (LookAheadToken("return")) statement = ParseReturnStatement(); else if (LookAheadToken("using") || (LookAheadToken("public") && LookAheadToken("using", 1))) statement = ParseImportStatement(); else if (LookAheadToken("discard")) { statement = new DiscardStatementSyntaxNode(); FillPosition(statement.Ptr()); ReadToken("discard"); ReadToken(TokenType::Semicolon); } else if (LookAheadToken(TokenType::Identifier)) { Token* startPos = tokenReader.mCursor; bool isVarDeclr = false; RefPtr type = ParseType(); if (LookAheadToken(TokenType::Identifier)) { type = nullptr; tokenReader.mCursor = startPos; statement = ParseVarDeclrStatement(); isVarDeclr = true; } if (!isVarDeclr) { tokenReader.mCursor = startPos; statement = ParseExpressionStatement(); } } else if (LookAheadToken(TokenType::Semicolon)) { statement = new EmptyStatementSyntaxNode(); FillPosition(statement.Ptr()); ReadToken(TokenType::Semicolon); } else { Unexpected(this); } return statement; } RefPtr Parser::ParseBlockStatement() { RefPtr scopeDecl = new ScopeDecl(); RefPtr blockStatement = new BlockStatementSyntaxNode(); blockStatement->scopeDecl = scopeDecl; PushScope(scopeDecl.Ptr()); ReadToken(TokenType::LBrace); if(!tokenReader.IsAtEnd()) { FillPosition(blockStatement.Ptr()); } while (!AdvanceIfMatch(this, TokenType::RBrace)) { auto stmt = ParseStatement(); if(stmt) { blockStatement->Statements.Add(stmt); } TryRecover(this); } PopScope(); return blockStatement; } RefPtr Parser::ParseVarDeclrStatement() { RefPtrvarDeclrStatement = new VarDeclrStatementSyntaxNode(); FillPosition(varDeclrStatement.Ptr()); auto decl = ParseDecl(this, currentScope->containerDecl); varDeclrStatement->decl = decl; return varDeclrStatement; } RefPtr Parser::ParseIfStatement() { RefPtr ifStatement = new IfStatementSyntaxNode(); FillPosition(ifStatement.Ptr()); ReadToken("if"); ReadToken(TokenType::LParent); ifStatement->Predicate = ParseExpression(); ReadToken(TokenType::RParent); ifStatement->PositiveStatement = ParseStatement(); if (LookAheadToken("else")) { ReadToken("else"); ifStatement->NegativeStatement = ParseStatement(); } return ifStatement; } RefPtr Parser::ParseForStatement() { RefPtr scopeDecl = new ScopeDecl(); RefPtr stmt = new ForStatementSyntaxNode(); stmt->scopeDecl = scopeDecl; PushScope(scopeDecl.Ptr()); FillPosition(stmt.Ptr()); ReadToken("for"); ReadToken(TokenType::LParent); if (IsTypeKeyword()) { stmt->InitialStatement = ParseVarDeclrStatement(); } else { if (!LookAheadToken(TokenType::Semicolon)) { stmt->InitialStatement = ParseExpressionStatement(); } else { ReadToken(TokenType::Semicolon); } } if (!LookAheadToken(TokenType::Semicolon)) stmt->PredicateExpression = ParseExpression(); ReadToken(TokenType::Semicolon); if (!LookAheadToken(TokenType::RParent)) stmt->SideEffectExpression = ParseExpression(); ReadToken(TokenType::RParent); stmt->Statement = ParseStatement(); PopScope(); return stmt; } RefPtr Parser::ParseWhileStatement() { RefPtr whileStatement = new WhileStatementSyntaxNode(); FillPosition(whileStatement.Ptr()); ReadToken("while"); ReadToken(TokenType::LParent); whileStatement->Predicate = ParseExpression(); ReadToken(TokenType::RParent); whileStatement->Statement = ParseStatement(); return whileStatement; } RefPtr Parser::ParseDoWhileStatement() { RefPtr doWhileStatement = new DoWhileStatementSyntaxNode(); FillPosition(doWhileStatement.Ptr()); ReadToken("do"); doWhileStatement->Statement = ParseStatement(); ReadToken("while"); ReadToken(TokenType::LParent); doWhileStatement->Predicate = ParseExpression(); ReadToken(TokenType::RParent); ReadToken(TokenType::Semicolon); return doWhileStatement; } RefPtr Parser::ParseBreakStatement() { RefPtr breakStatement = new BreakStatementSyntaxNode(); FillPosition(breakStatement.Ptr()); ReadToken("break"); ReadToken(TokenType::Semicolon); return breakStatement; } RefPtr Parser::ParseContinueStatement() { RefPtr continueStatement = new ContinueStatementSyntaxNode(); FillPosition(continueStatement.Ptr()); ReadToken("continue"); ReadToken(TokenType::Semicolon); return continueStatement; } RefPtr Parser::ParseReturnStatement() { RefPtr returnStatement = new ReturnStatementSyntaxNode(); FillPosition(returnStatement.Ptr()); ReadToken("return"); if (!LookAheadToken(TokenType::Semicolon)) returnStatement->Expression = ParseExpression(); ReadToken(TokenType::Semicolon); return returnStatement; } RefPtr Parser::ParseExpressionStatement() { RefPtr statement = new ExpressionStatementSyntaxNode(); FillPosition(statement.Ptr()); statement->Expression = ParseExpression(); ReadToken(TokenType::Semicolon); return statement; } RefPtr Parser::ParseParameter() { RefPtr parameter = new ParameterSyntaxNode(); parameter->modifiers = ParseModifiers(this); parameter->TypeNode = ParseType(); if (LookAheadToken(TokenType::Identifier)) { Token name = ReadToken(TokenType::Identifier); parameter->Name = name; } else parameter->Name.Content = "_anonymousParam" + String(anonymousParamCounter++); FillPosition(parameter.Ptr()); return parameter; } RefPtr Parser::ParseType() { Token typeName; if (LookAheadToken(TokenType::Identifier)) typeName = ReadToken(TokenType::Identifier); else typeName = ReadTypeKeyword(); RefPtr rs; if (LookAheadToken(TokenType::OpLess)) { RefPtr gtype = new GenericTypeSyntaxNode(); gtype->Position = typeName.Position; gtype->GenericTypeName = typeName.Content; ReadToken(TokenType::OpLess); gtype->BaseType = ParseType(); ReadToken(TokenType::OpGreater); rs = gtype; } else { auto basicType = new BasicTypeSyntaxNode(); basicType->Position = typeName.Position; basicType->TypeName = typeName.Content; rs = basicType; } while (LookAheadToken(TokenType::LBracket)) { RefPtr arrType = new ArrayTypeSyntaxNode(); arrType->Position = rs->Position; arrType->BaseType = rs; ReadToken(TokenType::LBracket); if (LookAheadToken(TokenType::IntLiterial)) arrType->ArrayLength = StringToInt(ReadToken(TokenType::IntLiterial).Content); else arrType->ArrayLength = 0; ReadToken(TokenType::RBracket); rs = arrType; } return rs; } enum class Associativity { Left, Right }; Associativity GetAssociativityFromLevel(int level) { if (level == 0) return Associativity::Right; else return Associativity::Left; } int GetOpLevel(CoreLib::Text::TokenType type) { switch(type) { case TokenType::OpAssign: case TokenType::OpMulAssign: case TokenType::OpDivAssign: case TokenType::OpAddAssign: case TokenType::OpSubAssign: case TokenType::OpModAssign: case TokenType::OpShlAssign: case TokenType::OpShrAssign: case TokenType::OpOrAssign: case TokenType::OpAndAssign: case TokenType::OpXorAssign: return 0; case TokenType::OpOr: return 2; case TokenType::OpAnd: return 3; case TokenType::OpBitOr: return 4; case TokenType::OpBitXor: return 5; case TokenType::OpBitAnd: return 6; case TokenType::OpEql: case TokenType::OpNeq: return 7; case TokenType::OpGeq: case TokenType::OpLeq: case TokenType::OpGreater: case TokenType::OpLess: return 8; case TokenType::OpLsh: case TokenType::OpRsh: return 9; case TokenType::OpAdd: case TokenType::OpSub: return 10; case TokenType::OpMul: case TokenType::OpDiv: case TokenType::OpMod: return 11; default: return -1; } } Operator GetOpFromToken(Token & token) { switch(token.Type) { case TokenType::OpAssign: return Operator::Assign; case TokenType::OpAddAssign: return Operator::AddAssign; case TokenType::OpSubAssign: return Operator::SubAssign; case TokenType::OpMulAssign: return Operator::MulAssign; case TokenType::OpDivAssign: return Operator::DivAssign; case TokenType::OpModAssign: return Operator::ModAssign; case TokenType::OpShlAssign: return Operator::LshAssign; case TokenType::OpShrAssign: return Operator::RshAssign; case TokenType::OpOrAssign: return Operator::OrAssign; case TokenType::OpAndAssign: return Operator::AddAssign; case TokenType::OpXorAssign: return Operator::XorAssign; case TokenType::OpOr: return Operator::Or; case TokenType::OpAnd: return Operator::And; case TokenType::OpBitOr: return Operator::BitOr; case TokenType::OpBitXor: return Operator::BitXor; case TokenType::OpBitAnd: return Operator::BitAnd; case TokenType::OpEql: return Operator::Eql; case TokenType::OpNeq: return Operator::Neq; case TokenType::OpGeq: return Operator::Geq; case TokenType::OpLeq: return Operator::Leq; case TokenType::OpGreater: return Operator::Greater; case TokenType::OpLess: return Operator::Less; case TokenType::OpLsh: return Operator::Lsh; case TokenType::OpRsh: return Operator::Rsh; case TokenType::OpAdd: return Operator::Add; case TokenType::OpSub: return Operator::Sub; case TokenType::OpMul: return Operator::Mul; case TokenType::OpDiv: return Operator::Div; case TokenType::OpMod: return Operator::Mod; case TokenType::OpInc: return Operator::PostInc; case TokenType::OpDec: return Operator::PostDec; case TokenType::OpNot: return Operator::Not; case TokenType::OpBitNot: return Operator::BitNot; default: throw "Illegal TokenType."; } } RefPtr Parser::ParseExpression(int level) { if (level == MaxExprLevel) return ParseLeafExpression(); if (level == 1) { // parse select clause auto condition = ParseExpression(level + 1); if (LookAheadToken(TokenType::QuestionMark)) { RefPtr select = new SelectExpressionSyntaxNode(); FillPosition(select.Ptr()); ReadToken(TokenType::QuestionMark); select->SelectorExpr = condition; select->Expr0 = ParseExpression(level); ReadToken(TokenType::Colon); select->Expr1 = ParseExpression(level); return select; } else return condition; } else { if (GetAssociativityFromLevel(level) == Associativity::Left) { auto left = ParseExpression(level + 1); while (GetOpLevel(tokenReader.PeekTokenType()) == level) { RefPtr tmp = new BinaryExpressionSyntaxNode(); tmp->LeftExpression = left; FillPosition(tmp.Ptr()); Token opToken = tokenReader.AdvanceToken(); tmp->Operator = GetOpFromToken(opToken); tmp->RightExpression = ParseExpression(level + 1); left = tmp; } return left; } else { auto left = ParseExpression(level + 1); if (GetOpLevel(tokenReader.PeekTokenType()) == level) { RefPtr tmp = new BinaryExpressionSyntaxNode(); tmp->LeftExpression = left; FillPosition(tmp.Ptr()); Token opToken = tokenReader.AdvanceToken(); tmp->Operator = GetOpFromToken(opToken); tmp->RightExpression = ParseExpression(level); left = tmp; } return left; } } } RefPtr Parser::ParseLeafExpression() { RefPtr rs; if (LookAheadToken("project")) { RefPtr project = new ProjectExpressionSyntaxNode(); FillPosition(project.Ptr()); ReadToken("project"); ReadToken(TokenType::LParent); project->BaseExpression = ParseExpression(); ReadToken(TokenType::RParent); return project; } if (LookAheadToken(TokenType::OpInc) || LookAheadToken(TokenType::OpDec) || LookAheadToken(TokenType::OpNot) || LookAheadToken(TokenType::OpBitNot) || LookAheadToken(TokenType::OpSub)) { RefPtr unaryExpr = new UnaryExpressionSyntaxNode(); Token token = tokenReader.AdvanceToken(); FillPosition(unaryExpr.Ptr()); unaryExpr->Operator = GetOpFromToken(token); if (unaryExpr->Operator == Operator::PostInc) unaryExpr->Operator = Operator::PreInc; else if (unaryExpr->Operator == Operator::PostDec) unaryExpr->Operator = Operator::PreDec; else if (unaryExpr->Operator == Operator::Sub) unaryExpr->Operator = Operator::Neg; unaryExpr->Expression = ParseLeafExpression(); rs = unaryExpr; return rs; } if (LookAheadToken(TokenType::LParent)) { ReadToken(TokenType::LParent); RefPtr expr; if (IsTypeKeyword() && LookAheadToken(TokenType::RParent, 1)) { RefPtr tcexpr = new TypeCastExpressionSyntaxNode(); FillPosition(tcexpr.Ptr()); tcexpr->TargetType = ParseType(); ReadToken(TokenType::RParent); tcexpr->Expression = ParseExpression(); expr = tcexpr; } else { expr = ParseExpression(); ReadToken(TokenType::RParent); } rs = expr; } else if (LookAheadToken(TokenType::IntLiterial) || LookAheadToken(TokenType::DoubleLiterial)) { RefPtr constExpr = new ConstantExpressionSyntaxNode(); auto token = tokenReader.AdvanceToken(); FillPosition(constExpr.Ptr()); if (token.Type == TokenType::IntLiterial) { if (token.Content.EndsWith("u") || token.Content.EndsWith("U")) { constExpr->ConstType = ConstantExpressionSyntaxNode::ConstantType::UInt; constExpr->IntValue = StringToUInt(token.Content); } else { constExpr->ConstType = ConstantExpressionSyntaxNode::ConstantType::Int; constExpr->IntValue = StringToInt(token.Content); } } else if (token.Type == TokenType::DoubleLiterial) { constExpr->ConstType = ConstantExpressionSyntaxNode::ConstantType::Float; constExpr->FloatValue = (float)StringToDouble(token.Content); } rs = constExpr; } else if (LookAheadToken("true") || LookAheadToken("false")) { RefPtr constExpr = new ConstantExpressionSyntaxNode(); auto token = tokenReader.AdvanceToken(); FillPosition(constExpr.Ptr()); constExpr->ConstType = ConstantExpressionSyntaxNode::ConstantType::Bool; constExpr->IntValue = token.Content == "true" ? 1 : 0; rs = constExpr; } else if (LookAheadToken(TokenType::Identifier)) { RefPtr varExpr = new VarExpressionSyntaxNode(); FillPosition(varExpr.Ptr()); auto token = ReadToken(TokenType::Identifier); varExpr->Variable = token.Content; rs = varExpr; } while (!tokenReader.IsAtEnd() && (LookAheadToken(TokenType::OpInc) || LookAheadToken(TokenType::OpDec) || LookAheadToken(TokenType::Dot) || LookAheadToken(TokenType::LBracket) || LookAheadToken(TokenType::LParent))) { if (LookAheadToken(TokenType::OpInc)) { RefPtr unaryExpr = new UnaryExpressionSyntaxNode(); FillPosition(unaryExpr.Ptr()); ReadToken(TokenType::OpInc); unaryExpr->Operator = Operator::PostInc; unaryExpr->Expression = rs; rs = unaryExpr; } else if (LookAheadToken(TokenType::OpDec)) { RefPtr unaryExpr = new UnaryExpressionSyntaxNode(); FillPosition(unaryExpr.Ptr()); ReadToken(TokenType::OpDec); unaryExpr->Operator = Operator::PostDec; unaryExpr->Expression = rs; rs = unaryExpr; } else if (LookAheadToken(TokenType::LBracket)) { RefPtr indexExpr = new IndexExpressionSyntaxNode(); indexExpr->BaseExpression = rs; FillPosition(indexExpr.Ptr()); ReadToken(TokenType::LBracket); indexExpr->IndexExpression = ParseExpression(); ReadToken(TokenType::RBracket); rs = indexExpr; } else if (LookAheadToken(TokenType::LParent)) { RefPtr invokeExpr = new InvokeExpressionSyntaxNode(); invokeExpr->FunctionExpr = rs; FillPosition(invokeExpr.Ptr()); ReadToken(TokenType::LParent); while (!tokenReader.IsAtEnd()) { if (!LookAheadToken(TokenType::RParent)) invokeExpr->Arguments.Add(ParseExpression()); else { break; } if (!LookAheadToken(TokenType::Comma)) break; ReadToken(TokenType::Comma); } ReadToken(TokenType::RParent); rs = invokeExpr; } else if (LookAheadToken(TokenType::Dot)) { RefPtr memberExpr = new MemberExpressionSyntaxNode(); FillPosition(memberExpr.Ptr()); memberExpr->BaseExpression = rs; ReadToken(TokenType::Dot); memberExpr->MemberName = ReadToken(TokenType::Identifier).Content; rs = memberExpr; } } if (!rs) { sink->diagnose(tokenReader.PeekLoc(), Diagnostics::syntaxError); } return rs; } RefPtr ParseProgram( TokenSpan const& tokens, DiagnosticSink* sink, String const& fileName) { Parser parser(tokens, sink, fileName); return parser.Parse(); } } } ================================================ FILE: Source/SpireCore/Parser.h ================================================ #ifndef RASTER_RENDERER_PARSER_H #define RASTER_RENDERER_PARSER_H #include "Lexer.h" #include "Syntax.h" namespace Spire { namespace Compiler { RefPtr ParseProgram( TokenSpan const& tokens, DiagnosticSink* sink, String const& fileName); } } #endif ================================================ FILE: Source/SpireCore/Preprocessor.cpp ================================================ // Preprocessor.cpp #include "Preprocessor.h" #include "Diagnostics.h" #include "Lexer.h" #include using namespace CoreLib; using namespace CoreLib::Text; // This file provides an implementation of a simple C-style preprocessor. // It does not aim for 100% compatibility with any particular preprocessor // specification, but the goal is to have it accept the most common // idioms for using the preprocessor, found in shader code in the wild. namespace Spire{ namespace Compiler { // State of a preprocessor conditional, which can change when // we encounter directives like `#elif` or `#endif` enum class PreprocessorConditionalState { Before, // We have not yet seen a branch with a `true` condition. During, // We are inside the branch with a `true` condition. After, // We have already seen the branch with a `true` condition. }; // Represents a preprocessor conditional that we are currently // nested inside. struct PreprocessorConditional { // The next outer conditional in the current file/stream, or NULL. PreprocessorConditional* parent; // The directive token that started the conditional (an `#if` or `#ifdef`) Token ifToken; // The `#else` directive token, if one has been seen (otherwise `TokenType::Unknown`) Token elseToken; // The state of the conditional PreprocessorConditionalState state; }; struct PreprocessorMacro; struct PreprocessorEnvironment { // The "outer" environment, to be used if lookup in this env fails PreprocessorEnvironment* parent = NULL; // Macros defined in this environment Dictionary macros; ~PreprocessorEnvironment(); }; // Input tokens can either come from source text, or from macro expansion. // In general, input streams can be nested, so we have to keep a conceptual // stack of input. // A stream of input tokens to be consumed struct PreprocessorInputStream { // The next input stream up the stack, if any. PreprocessorInputStream* parent; // The deepest preprocessor conditional active for this stream. PreprocessorConditional* conditional; // Environment to use when looking up macros PreprocessorEnvironment* environment; // Reader for pre-tokenized input TokenReader tokenReader; // Destructor is virtual so that we can clean up // after concrete subtypes. virtual ~PreprocessorInputStream() = default; }; struct SourceTextInputStream : PreprocessorInputStream { // The pre-tokenized input TokenList lexedTokens; }; struct MacroExpansion : PreprocessorInputStream { // The macro we will expand PreprocessorMacro* macro; }; struct ObjectLikeMacroExpansion : MacroExpansion { }; struct FunctionLikeMacroExpansion : MacroExpansion { // Environment for macro arguments PreprocessorEnvironment argumentEnvironment; }; // An enumeration for the diferent types of macros enum class PreprocessorMacroFlavor { ObjectLike, FunctionArg, FunctionLike, }; // In the current design (which we may want to re-consider), // a macro is a specialized flavor of input stream, that // captures the token list in its expansion, and then // can be "played back." struct PreprocessorMacro { // The name under which the macro was `#define`d Token nameToken; // Parameters of the macro, in case of a function-like macro List params; // The tokens that make up the macro body TokenList tokens; // The flavor of macro PreprocessorMacroFlavor flavor; // The environment in which this macro needs to be expanded. // For ordinary macros this will be the global environment, // while for function-like macro arguments, it will be // the environment of the macro invocation. PreprocessorEnvironment* environment; }; // State of the preprocessor struct Preprocessor { // diagnostics sink to use when writing messages DiagnosticSink* sink; // An external callback interface to use when looking // for files in a `#include` directive IncludeHandler* includeHandler; // Current input stream (top of the stack of input) PreprocessorInputStream* inputStream; // Currently-defined macros PreprocessorEnvironment globalEnv; // A pre-allocated token that can be returned to // represent end-of-input situations. Token endOfFileToken; }; // Convenience routine to access the diagnostic sink static DiagnosticSink* GetSink(Preprocessor* preprocessor) { return preprocessor->sink; } // // Forward declarations // static void DestroyConditional(PreprocessorConditional* conditional); static void DestroyMacro(Preprocessor* preprocessor, PreprocessorMacro* macro); // // Basic Input Handling // // Create a fresh input stream static void InitializeInputStream(Preprocessor* preprocessor, PreprocessorInputStream* inputStream) { inputStream->parent = NULL; inputStream->conditional = NULL; inputStream->environment = &preprocessor->globalEnv; } // Destroy an input stream static void DestroyInputStream(Preprocessor* /*preprocessor*/, PreprocessorInputStream* inputStream) { delete inputStream; } // Create an input stream to represent a pre-tokenized input file. // TODO(tfoley): pre-tokenizing files isn't going to work in the long run. static PreprocessorInputStream* CreateInputStreamForSource(Preprocessor* preprocessor, CoreLib::String const& source, CoreLib::String const& fileName) { SourceTextInputStream* inputStream = new SourceTextInputStream(); InitializeInputStream(preprocessor, inputStream); // Use existing `Lexer` to generate a token stream. Lexer lexer; inputStream->lexedTokens = lexer.Parse(fileName, source, GetSink(preprocessor)); inputStream->tokenReader = TokenReader(inputStream->lexedTokens); return inputStream; } static void PushInputStream(Preprocessor* preprocessor, PreprocessorInputStream* inputStream) { inputStream->parent = preprocessor->inputStream; preprocessor->inputStream = inputStream; } // Called when we reach the end of an input stream. // Performs some validation and then destroys the input stream if required. static void EndInputStream(Preprocessor* preprocessor, PreprocessorInputStream* inputStream) { // If there are any conditionals that weren't completed, then it is an error if (inputStream->conditional) { PreprocessorConditional* conditional = inputStream->conditional; GetSink(preprocessor)->diagnose(conditional->ifToken.Position, Diagnostics::endOfFileInPreprocessorConditional); while (conditional) { PreprocessorConditional* parent = conditional->parent; DestroyConditional(conditional); conditional = parent; } } DestroyInputStream(preprocessor, inputStream); } // Consume one token from an input stream static Token AdvanceRawToken(PreprocessorInputStream* inputStream) { return inputStream->tokenReader.AdvanceToken(); } // Peek one token from an input stream static Token PeekRawToken(PreprocessorInputStream* inputStream) { return inputStream->tokenReader.PeekToken(); } // Peek one token type from an input stream static CoreLib::Text::TokenType PeekRawTokenType(PreprocessorInputStream* inputStream) { return inputStream->tokenReader.PeekTokenType(); } // Read one token in "raw" mode (meaning don't expand macros) static Token AdvanceRawToken(Preprocessor* preprocessor) { for (;;) { // Look at the input stream on top of the stack PreprocessorInputStream* inputStream = preprocessor->inputStream; // If there isn't one, then there is no more input left to read. if (!inputStream) { return preprocessor->endOfFileToken; } // The top-most input stream may be at its end if (PeekRawTokenType(inputStream) == TokenType::EndOfFile) { // If there is another stream remaining, switch to it if (inputStream->parent) { preprocessor->inputStream = inputStream->parent; EndInputStream(preprocessor, inputStream); continue; } } // Everything worked, so read a token from the top-most stream return AdvanceRawToken(inputStream); } } // Return the next token in "raw" mode, but don't advance the // current token state. static Token PeekRawToken(Preprocessor* preprocessor) { // We need to find the strema that `advanceRawToken` would read from. PreprocessorInputStream* inputStream = preprocessor->inputStream; for (;;) { if (!inputStream) { // No more input streams left to read return preprocessor->endOfFileToken; } // The top-most input stream may be at its end, so // look one entry up the stack (don't actually pop // here, since we are just peeking) if (PeekRawTokenType(inputStream) == TokenType::EndOfFile) { if (inputStream->parent) { inputStream = inputStream->parent; continue; } } // Everything worked, so the token we just peeked is fine. return PeekRawToken(inputStream); } } // Without advancing preprocessor state, look *two* raw tokens ahead // (This is only needed in order to determine when we are possibly // expanding a function-style macro) CoreLib::Text::TokenType PeekSecondRawTokenType(Preprocessor* preprocessor) { // We need to find the strema that `advanceRawToken` would read from. PreprocessorInputStream* inputStream = preprocessor->inputStream; int count = 1; for (;;) { if (!inputStream) { // No more input streams left to read return TokenType::EndOfFile; } // The top-most input stream may be at its end, so // look one entry up the stack (don't actually pop // here, since we are just peeking) TokenReader reader = inputStream->tokenReader; if (reader.PeekTokenType() == TokenType::EndOfFile) { inputStream = inputStream->parent; continue; } if (count) { count--; // Note: we are advancing our temporary // copy of the token reader reader.AdvanceToken(); if (reader.PeekTokenType() == TokenType::EndOfFile) { inputStream = inputStream->parent; continue; } } // Everything worked, so peek a token from the top-most stream return reader.PeekTokenType(); } } // Get the location of the current (raw) token static CodePosition PeekLoc(Preprocessor* preprocessor) { return PeekRawToken(preprocessor).Position; } // Get the `TokenType` of the current (raw) token static CoreLib::Text::TokenType PeekRawTokenType(Preprocessor* preprocessor) { return PeekRawToken(preprocessor).Type; } // // Macros // // Create a macro static PreprocessorMacro* CreateMacro(Preprocessor* preprocessor) { // TODO(tfoley): Allocate these more intelligently. // For example, consider pooling them on the preprocessor. PreprocessorMacro* macro = new PreprocessorMacro(); macro->flavor = PreprocessorMacroFlavor::ObjectLike; macro->environment = &preprocessor->globalEnv; return macro; } // Destroy a macro static void DestroyMacro(Preprocessor* /*preprocessor*/, PreprocessorMacro* macro) { delete macro; } // Find the currently-defined macro of the given name, or return NULL static PreprocessorMacro* LookupMacro(PreprocessorEnvironment* environment, String const& name) { for(PreprocessorEnvironment* e = environment; e; e = e->parent) { PreprocessorMacro* macro = NULL; if (e->macros.TryGetValue(name, macro)) return macro; } return NULL; } static PreprocessorEnvironment* GetCurrentEnvironment(Preprocessor* preprocessor) { PreprocessorInputStream* inputStream = preprocessor->inputStream; return inputStream ? inputStream->environment : &preprocessor->globalEnv; } static PreprocessorMacro* LookupMacro(Preprocessor* preprocessor, String const& name) { return LookupMacro(GetCurrentEnvironment(preprocessor), name); } // A macro is "busy" if it is currently being used for expansion. // A macro cannot be expanded again while busy, to avoid infinite recursion. static bool IsMacroBusy(PreprocessorMacro* /*macro*/) { // TODO: need to implement this correctly // // The challenge here is that we are implementing expansion // for argumenst to function-like macros in a "lazy" fashion. // // The letter of the spec is that we should macro expand // each argument *before* substitution, and then go and // macro-expand the substituted body. This means that we // can invoke a macro as part of an argument to an // invocation of the same macro: // // FOO( 1, FOO(22), 333 ); // // In our implementation, the "inner" invocation of `FOO` // gets expanded at the point where it gets referenced // in the body of the "outer" invocation of `FOO`. // Doing things this way leads to greatly simplified // code for handling expansion. // // A proper implementation of `IsMacroBusy` needs to // take context into account, so that it bans recursive // use of a macro when it occurs (indirectly) through // the *body* of the expansion, but not when it occcurs // only through an *argument*. return false; } // // Reading Tokens With Expansion // static void InitializeMacroExpansion( Preprocessor* preprocessor, MacroExpansion* expansion, PreprocessorMacro* macro) { InitializeInputStream(preprocessor, expansion); expansion->environment = macro->environment; expansion->macro = macro; expansion->tokenReader = TokenReader(macro->tokens); } static void PushMacroExpansion( Preprocessor* preprocessor, MacroExpansion* expansion) { PushInputStream(preprocessor, expansion); } static void AddEndOfStreamToken( Preprocessor* preprocessor, PreprocessorMacro* macro) { Token token = PeekRawToken(preprocessor); token.Type = TokenType::EndOfFile; macro->tokens.mTokens.Add(token); } // Check whether the current token on the given input stream should be // treated as a macro invocation, and if so set up state for expanding // that macro. static void MaybeBeginMacroExpansion( Preprocessor* preprocessor ) { // We iterate because the first token in the expansion of one // macro may be another macro invocation. for (;;) { // Look at the next token ahead of us Token const& token = PeekRawToken(preprocessor); // Not an identifier? Can't be a macro. if (token.Type != TokenType::Identifier) return; // Look for a macro with the given name. String name = token.Content; PreprocessorMacro* macro = LookupMacro(preprocessor, name); // Not a macro? Can't be an invocation. if (!macro) return; // If the macro is busy (already being expanded), // don't try to trigger recursive expansion if (IsMacroBusy(macro)) return; // A function-style macro invocation should only match // if the token *after* the identifier is `(`. This // requires more lookahead than we usually have/need if (macro->flavor == PreprocessorMacroFlavor::FunctionLike) { if(PeekSecondRawTokenType(preprocessor) != TokenType::LParent) return; // Consume the token that triggered macro expansion AdvanceRawToken(preprocessor); // Consume the opening `(` Token leftParen = AdvanceRawToken(preprocessor); FunctionLikeMacroExpansion* expansion = new FunctionLikeMacroExpansion(); InitializeMacroExpansion(preprocessor, expansion, macro); expansion->argumentEnvironment.parent = &preprocessor->globalEnv; expansion->environment = &expansion->argumentEnvironment; // Try to read any arguments present. int paramCount = macro->params.Count(); int argIndex = 0; switch (PeekRawTokenType(preprocessor)) { case TokenType::EndOfFile: case TokenType::RParent: // No arguments. break; default: // At least one argument while(argIndex < paramCount) { // Read an argument // Create the argument, represented as a special flavor of macro PreprocessorMacro* arg = CreateMacro(preprocessor); arg->flavor = PreprocessorMacroFlavor::FunctionArg; arg->environment = GetCurrentEnvironment(preprocessor); // Associate the new macro with its parameter name Token paramToken = macro->params[argIndex]; String const& paramName = paramToken.Content; arg->nameToken = paramToken; expansion->argumentEnvironment.macros[paramName] = arg; argIndex++; // Read tokens for the argument // We track the nesting depth, since we don't break // arguments on a `,` nested in balanced parentheses // int nesting = 0; for (;;) { switch (PeekRawTokenType(preprocessor)) { case TokenType::EndOfFile: // if we reach the end of the file, // then we have an error, and need to // bail out AddEndOfStreamToken(preprocessor, arg); goto doneWithAllArguments; case TokenType::RParent: // If we see a right paren when we aren't nested // then we are at the end of an argument if (nesting == 0) { AddEndOfStreamToken(preprocessor, arg); goto doneWithAllArguments; } // Otherwise we decrease our nesting depth, add // the token, and keep going nesting--; break; case TokenType::Comma: // If we see a comma when we aren't nested // then we are at the end of an argument if (nesting == 0) { AddEndOfStreamToken(preprocessor, arg); AdvanceRawToken(preprocessor); goto doneWithArgument; } // Otherwise we add it as a normal token break; case TokenType::LParent: // If we see a left paren then we need to // increase our tracking of nesting nesting++; break; default: break; } // Add the token and continue parsing. arg->tokens.mTokens.Add(AdvanceRawToken(preprocessor)); } doneWithArgument: {} // We've parsed an argument and should move onto // the next one. } break; } doneWithAllArguments: // TODO: handle possible varargs // Expect closing right paren if (PeekRawTokenType(preprocessor) == TokenType::RParent) { AdvanceRawToken(preprocessor); } else { GetSink(preprocessor)->diagnose(PeekLoc(preprocessor), Diagnostics::expectedTokenInMacroArguments, TokenType::RParent, PeekRawTokenType(preprocessor)); } int argCount = argIndex; if (argCount != paramCount) { // TODO: diagnose throw 99; } // We are ready to expand. PushMacroExpansion(preprocessor, expansion); } else { // Consume the token that triggered macro expansion AdvanceRawToken(preprocessor); // Object-like macros are the easy case. ObjectLikeMacroExpansion* expansion = new ObjectLikeMacroExpansion(); InitializeMacroExpansion(preprocessor, expansion, macro); PushMacroExpansion(preprocessor, expansion); } } } // Read one token with macro-expansion enabled. static Token AdvanceToken(Preprocessor* preprocessor) { top: // Check whether we need to macro expand at the cursor. MaybeBeginMacroExpansion(preprocessor); // Read a raw token (now that expansion has been triggered) Token token = AdvanceRawToken(preprocessor); // Check if we need to perform token pasting if (PeekRawTokenType(preprocessor) != TokenType::PoundPound) { // If we aren't token pasting, then we are done return token; } else { // We are pasting tokens, which could get messy StringBuilder sb; sb << token.Content; while (PeekRawTokenType(preprocessor) == TokenType::PoundPound) { // Consume the `##` AdvanceRawToken(preprocessor); // Possibly macro-expand the next token MaybeBeginMacroExpansion(preprocessor); // Read the next raw token (now that expansion has been triggered) Token nextToken = AdvanceRawToken(preprocessor); sb << nextToken.Content; } // Now re-lex the input PreprocessorInputStream* inputStream = CreateInputStreamForSource(preprocessor, sb.ProduceString(), "token paste"); if (inputStream->tokenReader.GetCount() != 1) { // We expect a token paste to produce a single token // TODO(tfoley): emit a diagnostic here } PushInputStream(preprocessor, inputStream); goto top; } } // Read one token with macro-expansion enabled. // // Note that because triggering macro expansion may // involve changing the input-stream state, this // operation *can* have side effects. static Token PeekToken(Preprocessor* preprocessor) { // Check whether we need to macro expand at the cursor. MaybeBeginMacroExpansion(preprocessor); // Peek a raw token (now that expansion has been triggered) return PeekRawToken(preprocessor); // TODO: need a plan for how to handle token pasting // here without it being onerous. Would be nice if we // didn't have to re-do pasting on a "peek"... } // Peek the type of the next token, including macro expansion. static CoreLib::Text::TokenType PeekTokenType(Preprocessor* preprocessor) { return PeekToken(preprocessor).Type; } // // Preprocessor Directives // // When reading a preprocessor directive, we use a context // to wrap the direct preprocessor routines defines so far. // // One of the most important things the directive context // does is give us a convenient way to read tokens with // a guarantee that we won't read past the end of a line. struct PreprocessorDirectiveContext { // The preprocessor that is parsing the directive. Preprocessor* preprocessor; // The directive token (e.g., the `if` in `#if`). // Useful for reference in diagnostic messages. Token directiveToken; // Has any kind of parse error been encountered in // the directive so far? bool parseError; }; // Get the token for the preprocessor directive being parsed. inline Token const& GetDirective(PreprocessorDirectiveContext* context) { return context->directiveToken; } // Get the name of the directive being parsed. inline String const& GetDirectiveName(PreprocessorDirectiveContext* context) { return context->directiveToken.Content; } // Get the location of the directive being parsed. inline CodePosition const& GetDirectiveLoc(PreprocessorDirectiveContext* context) { return context->directiveToken.Position; } // Wrapper to get the diagnostic sink in the context of a directive. static inline DiagnosticSink* GetSink(PreprocessorDirectiveContext* context) { return GetSink(context->preprocessor); } // Wrapper to get a "current" location when parsing a directive static CodePosition PeekLoc(PreprocessorDirectiveContext* context) { return PeekLoc(context->preprocessor); } // Wrapper to look up a macro in the context of a directive. static PreprocessorMacro* LookupMacro(PreprocessorDirectiveContext* context, String const& name) { return LookupMacro(context->preprocessor, name); } // Determine if we have read everthing on the directive's line. static bool IsEndOfLine(PreprocessorDirectiveContext* context) { // Is the next token at the start of its own line? // If so, we have read to the end of the directive. return (PeekRawToken(context->preprocessor).flags & TokenFlag::AtStartOfLine) != 0; } // Read one raw token in a directive, without going past the end of the line. static Token AdvanceRawToken(PreprocessorDirectiveContext* context) { if (IsEndOfLine(context)) return context->preprocessor->endOfFileToken; return AdvanceRawToken(context->preprocessor); } // Peek one raw token in a directive, without going past the end of the line. static Token PeekRawToken(PreprocessorDirectiveContext* context) { if (IsEndOfLine(context)) return context->preprocessor->endOfFileToken; return PeekRawToken(context->preprocessor); } // Peek next raw token type, without going past the end of the line. static CoreLib::Text::TokenType PeekRawTokenType(PreprocessorDirectiveContext* context) { if (IsEndOfLine(context)) return TokenType::EndOfFile; return PeekRawTokenType(context->preprocessor); } // Read one token, with macro-expansion, without going past the end of the line. static Token AdvanceToken(PreprocessorDirectiveContext* context) { if (IsEndOfLine(context)) context->preprocessor->endOfFileToken; return AdvanceToken(context->preprocessor); } // Peek one token, with macro-expansion, without going past the end of the line. static Token PeekToken(PreprocessorDirectiveContext* context) { if (IsEndOfLine(context)) context->preprocessor->endOfFileToken; return PeekToken(context->preprocessor); } // Peek next token type, with macro-expansion, without going past the end of the line. static CoreLib::Text::TokenType PeekTokenType(PreprocessorDirectiveContext* context) { if (IsEndOfLine(context)) return TokenType::EndOfFile; return PeekTokenType(context->preprocessor); } // Skip to the end of the line (useful for recovering from errors in a directive) static void SkipToEndOfLine(PreprocessorDirectiveContext* context) { while(!IsEndOfLine(context)) { AdvanceRawToken(context); } } static bool ExpectRaw(PreprocessorDirectiveContext* context, CoreLib::Text::TokenType tokenType, DiagnosticInfo const& diagnostic, Token* outToken = NULL) { if (PeekRawTokenType(context) != tokenType) { // Only report the first parse error within a directive if (!context->parseError) { GetSink(context)->diagnose(PeekLoc(context), diagnostic, tokenType, GetDirectiveName(context)); } context->parseError = true; return false; } Token const& token = AdvanceRawToken(context); if (outToken) *outToken = token; return true; } static bool Expect(PreprocessorDirectiveContext* context, CoreLib::Text::TokenType tokenType, DiagnosticInfo const& diagnostic, Token* outToken = NULL) { if (PeekTokenType(context) != tokenType) { // Only report the first parse error within a directive if (!context->parseError) { GetSink(context)->diagnose(PeekLoc(context), diagnostic, tokenType, GetDirectiveName(context)); context->parseError = true; } return false; } Token const& token = AdvanceToken(context); if (outToken) *outToken = token; return true; } // // Preprocessor Conditionals // // Determine whether the current preprocessor state means we // should be skipping tokens. static bool IsSkipping(Preprocessor* preprocessor) { PreprocessorInputStream* inputStream = preprocessor->inputStream; if (!inputStream) return false; // If we are not inside a preprocessor conditional, then don't skip PreprocessorConditional* conditional = inputStream->conditional; if (!conditional) return false; // skip tokens unless the conditional is inside its `true` case return conditional->state != PreprocessorConditionalState::During; } // Wrapper for use inside directives static inline bool IsSkipping(PreprocessorDirectiveContext* context) { return IsSkipping(context->preprocessor); } // Create a preprocessor conditional static PreprocessorConditional* CreateConditional(Preprocessor* /*preprocessor*/) { // TODO(tfoley): allocate these more intelligently (for example, // pool them on the `Preprocessor`. return new PreprocessorConditional(); } // Destroy a preprocessor conditional. static void DestroyConditional(PreprocessorConditional* conditional) { delete conditional; } // Start a preprocessor conditional, with an initial enable/disable state. static void BeginConditional(PreprocessorDirectiveContext* context, bool enable) { Preprocessor* preprocessor = context->preprocessor; PreprocessorInputStream* inputStream = preprocessor->inputStream; assert(inputStream); PreprocessorConditional* conditional = CreateConditional(preprocessor); conditional->ifToken = context->directiveToken; // Set state of this condition appropriately. // // Default to the "haven't yet seen a `true` branch" state. PreprocessorConditionalState state = PreprocessorConditionalState::Before; // // If we are nested inside a `false` branch of another condition, then // we never want to enable, so we act as if we already *saw* the `true` branch. // if (IsSkipping(preprocessor)) state = PreprocessorConditionalState::After; // // Similarly, if we ran into any parse errors when dealing with the // opening directive, then things are probably screwy and we should just // skip all the branches. if (IsSkipping(preprocessor)) state = PreprocessorConditionalState::After; // // Otherwise, if our condition was true, then set us to be inside the `true` branch else if (enable) state = PreprocessorConditionalState::During; conditional->state = state; // Push conditional onto the stack conditional->parent = inputStream->conditional; inputStream->conditional = conditional; } // // Preprocessor Conditional Expressions // // Conditional expressions are always of type `int` typedef int PreprocessorExpressionValue; // Forward-declaretion static PreprocessorExpressionValue ParseAndEvaluateExpression(PreprocessorDirectiveContext* context); // Parse a unary (prefix) expression inside of a preprocessor directive. static PreprocessorExpressionValue ParseAndEvaluateUnaryExpression(PreprocessorDirectiveContext* context) { switch (PeekTokenType(context)) { // handle prefix unary ops case TokenType::OpSub: AdvanceToken(context); return -ParseAndEvaluateUnaryExpression(context); case TokenType::OpNot: AdvanceToken(context); return !ParseAndEvaluateUnaryExpression(context); case TokenType::OpBitNot: AdvanceToken(context); return ~ParseAndEvaluateUnaryExpression(context); // handle parenthized sub-expression case TokenType::LParent: { Token leftParen = AdvanceToken(context); PreprocessorExpressionValue value = ParseAndEvaluateExpression(context); if (!Expect(context, TokenType::RParent, Diagnostics::expectedTokenInPreprocessorExpression)) { GetSink(context)->diagnose(leftParen.Position, Diagnostics::seeOpeningToken, leftParen); } return value; } case TokenType::IntLiterial: return StringToInt(AdvanceToken(context).Content); case TokenType::Identifier: { Token token = AdvanceToken(context); if (token.Content == "defined") { // handle `defined(someName)` // Possibly parse a `(` Token leftParen; if (PeekRawTokenType(context) == TokenType::LParent) { leftParen = AdvanceRawToken(context); } // Expect an identifier Token nameToken; if (!ExpectRaw(context, TokenType::Identifier, Diagnostics::expectedTokenInDefinedExpression, &nameToken)) { return 0; } String name = nameToken.Content; // If we saw an opening `(`, then expect one to close if (leftParen.Type != TokenType::Unknown) { if(!ExpectRaw(context, TokenType::RParent, Diagnostics::expectedTokenInDefinedExpression)) { GetSink(context)->diagnose(leftParen.Position, Diagnostics::seeOpeningToken, leftParen); return 0; } } return LookupMacro(context, name) != NULL; } // An identifier here means it was not defined as a macro (or // it is defined, but as a function-like macro. These should // just evaluate to zero (possibly with a warning) return 0; } default: GetSink(context)->diagnose(PeekLoc(context), Diagnostics::syntaxErrorInPreprocessorExpression); return 0; } } // Determine the precedence level of an infix operator // for use in parsing preprocessor conditionals. static int GetInfixOpPrecedence(Token const& opToken) { // If token is on another line, it is not part of the // expression if (opToken.flags & TokenFlag::AtStartOfLine) return -1; // otherwise we look at the token type to figure // out what precednece it should be parse with switch (opToken.Type) { default: // tokens that aren't infix operators should // cause us to stop parsing an expression return -1; case TokenType::OpMul: return 10; case TokenType::OpDiv: return 10; case TokenType::OpMod: return 10; case TokenType::OpAdd: return 9; case TokenType::OpSub: return 9; case TokenType::OpLsh: return 8; case TokenType::OpRsh: return 8; case TokenType::OpLess: return 7; case TokenType::OpGreater: return 7; case TokenType::OpLeq: return 7; case TokenType::OpGeq: return 7; case TokenType::OpEql: return 6; case TokenType::OpNeq: return 6; case TokenType::OpBitAnd: return 5; case TokenType::OpBitOr: return 4; case TokenType::OpBitXor: return 3; case TokenType::OpAnd: return 2; case TokenType::OpOr: return 1; } }; // Evaluate one infix operation in a preprocessor // conditional expression static PreprocessorExpressionValue EvaluateInfixOp( PreprocessorDirectiveContext* context, Token const& opToken, PreprocessorExpressionValue left, PreprocessorExpressionValue right) { switch (opToken.Type) { default: // SPIRE_INTERNAL_ERROR(getSink(preprocessor), opToken); return 0; break; case TokenType::OpMul: return left * right; case TokenType::OpDiv: { if (right == 0) { if (!context->parseError) { GetSink(context)->diagnose(opToken.Position, Diagnostics::divideByZeroInPreprocessorExpression); } return 0; } return left / right; } case TokenType::OpMod: { if (right == 0) { if (!context->parseError) { GetSink(context)->diagnose(opToken.Position, Diagnostics::divideByZeroInPreprocessorExpression); } return 0; } return left % right; } case TokenType::OpAdd: return left + right; case TokenType::OpSub: return left - right; case TokenType::OpLsh: return left << right; case TokenType::OpRsh: return left >> right; case TokenType::OpLess: return left < right ? 1 : 0; case TokenType::OpGreater: return left > right ? 1 : 0; case TokenType::OpLeq: return left <= right ? 1 : 0; case TokenType::OpGeq: return left <= right ? 1 : 0; case TokenType::OpEql: return left == right ? 1 : 0; case TokenType::OpNeq: return left != right ? 1 : 0; case TokenType::OpBitAnd: return left & right; case TokenType::OpBitOr: return left | right; case TokenType::OpBitXor: return left ^ right; case TokenType::OpAnd: return left && right; case TokenType::OpOr: return left || right; } } // Parse the rest of an infix preprocessor expression with // precedence greater than or equal to the given `precedence` argument. // The value of the left-hand-side expression is provided as // an argument. // This is used to form a simple recursive-descent expression parser. static PreprocessorExpressionValue ParseAndEvaluateInfixExpressionWithPrecedence( PreprocessorDirectiveContext* context, PreprocessorExpressionValue left, int precedence) { for (;;) { // Look at the next token, and see if it is an operator of // high enough precedence to be included in our expression Token opToken = PeekToken(context); int opPrecedence = GetInfixOpPrecedence(opToken); // If it isn't an operator of high enough precendece, we are done. if(opPrecedence < precedence) break; // Otherwise we need to consume the operator token. AdvanceToken(context); // Next we parse a right-hand-side expression by starting with // a unary expression and absorbing and many infix operators // as possible with strictly higher precedence than the operator // we found above. PreprocessorExpressionValue right = ParseAndEvaluateUnaryExpression(context); for (;;) { // Look for an operator token Token rightOpToken = PeekToken(context); int rightOpPrecedence = GetInfixOpPrecedence(rightOpToken); // If no operator was found, or the operator wasn't high // enough precedence to fold into the right-hand-side, // exit this loop. if (rightOpPrecedence <= opPrecedence) break; // Now invoke the parser recursively, passing in our // existing right-hand side to form an even larger one. right = ParseAndEvaluateInfixExpressionWithPrecedence( context, right, rightOpPrecedence); } // Now combine the left- and right-hand sides using // the operator we found above. left = EvaluateInfixOp(context, opToken, left, right); } return left; } // Parse a complete (infix) preprocessor expression, and return its value static PreprocessorExpressionValue ParseAndEvaluateExpression(PreprocessorDirectiveContext* context) { // First read in the left-hand side (or the whole expression in the unary case) PreprocessorExpressionValue value = ParseAndEvaluateUnaryExpression(context); // Try to read in trailing infix operators with correct precedence return ParseAndEvaluateInfixExpressionWithPrecedence(context, value, 0); } // Handle a `#if` directive static void HandleIfDirective(PreprocessorDirectiveContext* context) { // Parse a preprocessor expression. PreprocessorExpressionValue value = ParseAndEvaluateExpression(context); // Begin a preprocessor block, enabled based on the expression. BeginConditional(context, value != 0); } // Handle a `#ifdef` directive static void HandleIfDefDirective(PreprocessorDirectiveContext* context) { // Expect a raw identifier, so we can check if it is defined Token nameToken; if(!ExpectRaw(context, TokenType::Identifier, Diagnostics::expectedTokenInPreprocessorDirective, &nameToken)) return; String name = nameToken.Content; // Check if the name is defined. BeginConditional(context, LookupMacro(context, name) != NULL); } // Handle a `#ifndef` directive static void HandleIfNDefDirective(PreprocessorDirectiveContext* context) { // Expect a raw identifier, so we can check if it is defined Token nameToken; if(!ExpectRaw(context, TokenType::Identifier, Diagnostics::expectedTokenInPreprocessorDirective, &nameToken)) return; String name = nameToken.Content; // Check if the name is defined. BeginConditional(context, LookupMacro(context, name) == NULL); } // Handle a `#else` directive static void HandleElseDirective(PreprocessorDirectiveContext* context) { PreprocessorInputStream* inputStream = context->preprocessor->inputStream; assert(inputStream); // if we aren't inside a conditional, then error PreprocessorConditional* conditional = inputStream->conditional; if (!conditional) { GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::directiveWithoutIf, GetDirectiveName(context)); return; } // if we've already seen a `#else`, then it is an error if (conditional->elseToken.Type != TokenType::Unknown) { GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::directiveAfterElse, GetDirectiveName(context)); GetSink(context)->diagnose(conditional->elseToken.Position, Diagnostics::seeDirective); return; } conditional->elseToken = context->directiveToken; switch (conditional->state) { case PreprocessorConditionalState::Before: conditional->state = PreprocessorConditionalState::During; break; case PreprocessorConditionalState::During: conditional->state = PreprocessorConditionalState::After; break; default: break; } } // Handle a `#elif` directive static void HandleElifDirective(PreprocessorDirectiveContext* context) { PreprocessorExpressionValue value = ParseAndEvaluateExpression(context); PreprocessorInputStream* inputStream = context->preprocessor->inputStream; assert(inputStream); // if we aren't inside a conditional, then error PreprocessorConditional* conditional = inputStream->conditional; if (!conditional) { GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::directiveWithoutIf, GetDirectiveName(context)); return; } // if we've already seen a `#else`, then it is an error if (conditional->elseToken.Type != TokenType::Unknown) { GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::directiveAfterElse, GetDirectiveName(context)); GetSink(context)->diagnose(conditional->elseToken.Position, Diagnostics::seeDirective); return; } switch (conditional->state) { case PreprocessorConditionalState::Before: if(value) conditional->state = PreprocessorConditionalState::During; break; case PreprocessorConditionalState::During: conditional->state = PreprocessorConditionalState::After; break; default: break; } } // Handle a `#endif` directive static void HandleEndIfDirective(PreprocessorDirectiveContext* context) { PreprocessorInputStream* inputStream = context->preprocessor->inputStream; assert(inputStream); // if we aren't inside a conditional, then error PreprocessorConditional* conditional = inputStream->conditional; if (!conditional) { GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::directiveWithoutIf, GetDirectiveName(context)); return; } inputStream->conditional = conditional->parent; DestroyConditional(conditional); } // Handle a `#include` directive static void HandleIncludeDirective(PreprocessorDirectiveContext* context) { Token pathToken; if(!Expect(context, TokenType::StringLiterial, Diagnostics::expectedTokenInPreprocessorDirective, &pathToken)) return; String path = pathToken.Content; // TODO(tfoley): make this robust in presence of `#line` String pathIncludedFrom = GetDirectiveLoc(context).FileName; String foundPath; String foundSource; IncludeHandler* includeHandler = context->preprocessor->includeHandler; if (!includeHandler) { GetSink(context)->diagnose(pathToken.Position, Diagnostics::includeFailed, path); GetSink(context)->diagnose(pathToken.Position, Diagnostics::noIncludeHandlerSpecified); return; } if (!includeHandler->TryToFindIncludeFile(path, pathIncludedFrom, &foundPath, &foundSource)) { GetSink(context)->diagnose(pathToken.Position, Diagnostics::includeFailed, path); return; } // Push the new file onto our stack of input streams // TODO(tfoley): check if we have made our include stack too deep PreprocessorInputStream* inputStream = CreateInputStreamForSource(context->preprocessor, foundSource, foundPath); inputStream->parent = context->preprocessor->inputStream; context->preprocessor->inputStream = inputStream; } // Handle a `#define` directive static void HandleDefineDirective(PreprocessorDirectiveContext* context) { Token nameToken; if (!Expect(context, TokenType::Identifier, Diagnostics::expectedTokenInPreprocessorDirective, &nameToken)) return; String name = nameToken.Content; PreprocessorMacro* macro = CreateMacro(context->preprocessor); macro->nameToken = nameToken; PreprocessorMacro* oldMacro = LookupMacro(&context->preprocessor->globalEnv, name); if (oldMacro) { GetSink(context)->diagnose(nameToken.Position, Diagnostics::macroRedefinition, name); GetSink(context)->diagnose(oldMacro->nameToken.Position, Diagnostics::seePreviousDefinitionOf, name); DestroyMacro(context->preprocessor, oldMacro); } context->preprocessor->globalEnv.macros[name] = macro; // If macro name is immediately followed (with no space) by `(`, // then we have a function-like macro if (PeekRawTokenType(context) == TokenType::LParent) { if (!(PeekRawToken(context).flags & TokenFlag::AfterWhitespace)) { // This is a function-like macro, so we need to remember that // and start capturing parameters macro->flavor = PreprocessorMacroFlavor::FunctionLike; AdvanceRawToken(context); // If there are any parameters, parse them if (PeekRawTokenType(context) != TokenType::RParent) { for (;;) { // TODO: handle elipsis (`...`) for varags // A macro parameter name should be a raw identifier Token paramToken; if (!ExpectRaw(context, TokenType::Identifier, Diagnostics::expectedTokenInMacroParameters, ¶mToken)) break; // TODO(tfoley): some validation on parameter name. // Certain names (e.g., `defined` and `__VA_ARGS__` // are not allowed to be used as macros or parameters). // Add the parameter to the macro being deifned macro->params.Add(paramToken); // If we see `)` then we are done with arguments if (PeekRawTokenType(context) == TokenType::RParent) break; ExpectRaw(context, TokenType::Comma, Diagnostics::expectedTokenInMacroParameters); } } ExpectRaw(context, TokenType::RParent, Diagnostics::expectedTokenInMacroParameters); } } // consume tokens until end-of-line for(;;) { Token token = AdvanceRawToken(context); macro->tokens.mTokens.Add(token); if (token.Type == TokenType::EndOfFile) break; } } // Handle a `#undef` directive static void HandleUndefDirective(PreprocessorDirectiveContext* context) { Token nameToken; if (!Expect(context, TokenType::Identifier, Diagnostics::expectedTokenInPreprocessorDirective, &nameToken)) return; String name = nameToken.Content; PreprocessorEnvironment* env = &context->preprocessor->globalEnv; PreprocessorMacro* macro = LookupMacro(env, name); if (macro != NULL) { // name was defined, so remove it env->macros.Remove(name); DestroyMacro(context->preprocessor, macro); } else { // name wasn't defined GetSink(context)->diagnose(nameToken.Position, Diagnostics::macroNotDefined, name); } } // Handle a `#warning` directive static void HandleWarningDirective(PreprocessorDirectiveContext* context) { // TODO: read rest of line without actual tokenization GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::userDefinedWarning, "user-defined warning"); SkipToEndOfLine(context); } // Handle a `#error` directive static void HandleErrorDirective(PreprocessorDirectiveContext* context) { // TODO: read rest of line without actual tokenization GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::userDefinedError, "user-defined warning"); SkipToEndOfLine(context); } // Handle a `#line` directive static void HandleLineDirective(PreprocessorDirectiveContext* context) { int line = 0; if (PeekTokenType(context) == TokenType::IntLiterial) { line = StringToInt(AdvanceToken(context).Content); } else if (PeekTokenType(context) == TokenType::Identifier && PeekToken(context).Content == "default") { AdvanceToken(context); // TODO(tfoley): reset line numbering here return; } else { GetSink(context)->diagnose(PeekLoc(context), Diagnostics::expected2TokensInPreprocessorDirective, TokenType::IntLiterial, "default", GetDirectiveName(context)); context->parseError = true; return; } if (PeekTokenType(context) == TokenType::EndOfFile) { // TODO(tfoley): set line number, but not file return; } String file; if (PeekTokenType(context) == TokenType::StringLiterial) { file = AdvanceToken(context).Content; } else if (PeekTokenType(context) == TokenType::IntLiterial) { // Note(tfoley): GLSL allows the "source string" to be indicated by an integer // TODO(tfoley): Figure out a better way to handle this, if it matters file = AdvanceToken(context).Content; } else { Expect(context, TokenType::StringLiterial, Diagnostics::expectedTokenInPreprocessorDirective); return; } // TODO(tfoley): set line number and file here } // Handle a `#pragma` directive static void HandlePragmaDirective(PreprocessorDirectiveContext* context) { // TODO(tfoley): figure out which pragmas to parse, // and which to pass along SkipToEndOfLine(context); } // Handle a `#version` directive static void HandleVersionDirective(PreprocessorDirectiveContext* context) { // TODO(tfoley): parse enough of this to spit it out again later? SkipToEndOfLine(context); } // Callback interface used by preprocessor directives typedef void (*PreprocessorDirectiveCallback)(PreprocessorDirectiveContext* context); enum PreprocessorDirectiveFlag : unsigned int { // Should this directive be handled even when skipping disbaled code? ProcessWhenSkipping = 1 << 0, }; // Information about a specific directive struct PreprocessorDirective { // Name of the directive char const* name; // Callback to handle the directive PreprocessorDirectiveCallback callback; unsigned int flags; }; // A simple array of all the directives we know how to handle. // TODO(tfoley): considering making this into a real hash map, // and then make it easy-ish for users of the codebase to add // their own directives as desired. static const PreprocessorDirective kDirectives[] = { { "if", &HandleIfDirective, ProcessWhenSkipping }, { "ifdef", &HandleIfDefDirective, ProcessWhenSkipping }, { "ifndef", &HandleIfNDefDirective, ProcessWhenSkipping }, { "else", &HandleElseDirective, ProcessWhenSkipping }, { "elif", &HandleElifDirective, ProcessWhenSkipping }, { "endif", &HandleEndIfDirective, ProcessWhenSkipping }, { "include", &HandleIncludeDirective, 0 }, { "define", &HandleDefineDirective, 0 }, { "undef", &HandleUndefDirective, 0 }, { "warning", &HandleWarningDirective, 0 }, { "error", &HandleErrorDirective, 0 }, { "line", &HandleLineDirective, 0 }, { "pragma", &HandlePragmaDirective, 0 }, { "version", &HandleVersionDirective, 0 }, { NULL, NULL }, }; // Look up the directive with the given name. static PreprocessorDirective const* FindDirective(String const& name) { char const* nameStr = name.Buffer(); for (int ii = 0; kDirectives[ii].name; ++ii) { if (strcmp(kDirectives[ii].name, nameStr) != 0) continue; return &kDirectives[ii]; } return NULL; } // Process a directive, where the preprocessor has already consumed the // `#` token that started the directive line. static void HandleDirective(PreprocessorDirectiveContext* context) { // Try to read the directive name. context->directiveToken = PeekRawToken(context); CoreLib::Text::TokenType directiveTokenType = GetDirective(context).Type; // An empty directive is allowed, and ignored. if (directiveTokenType == TokenType::EndOfFile) { return; } // Otherwise the directive name had better be an identifier else if (directiveTokenType != TokenType::Identifier) { GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::expectedPreprocessorDirectiveName); SkipToEndOfLine(context); return; } // Consume the directive name token. AdvanceRawToken(context); // Look up the handler for the directive. PreprocessorDirective const* directive = FindDirective(GetDirectiveName(context)); if (!directive) { GetSink(context)->diagnose(GetDirectiveLoc(context), Diagnostics::unknownPreprocessorDirective, GetDirectiveName(context)); SkipToEndOfLine(context); return; } // If we are skipping disabled code, and the directive is not one // of the small number that need to run even in that case, skip it. if (IsSkipping(context) && !(directive->flags & PreprocessorDirectiveFlag::ProcessWhenSkipping)) { SkipToEndOfLine(context); return; } // Apply the directive-specific callback (directive->callback)(context); // We expect the directive to consume the entire line, so if // it hasn't that is a parse error. if (!IsEndOfLine(context)) { // If we already saw a previous parse error, then don't // emit another one for the same directive. if (!context->parseError) { GetSink(context)->diagnose(PeekLoc(context), Diagnostics::unexpectedTokensAfterDirective, GetDirectiveName(context)); } SkipToEndOfLine(context); } } // Read one token using the full preprocessor, with all its behaviors. static Token ReadToken(Preprocessor* preprocessor) { for (;;) { // Look at the next raw token in the input. Token const& token = PeekRawToken(preprocessor); // If we have a directive (`#` at start of line) then handle it if ((token.Type == TokenType::Pound) && (token.flags & TokenFlag::AtStartOfLine)) { // Skip the `#` AdvanceRawToken(preprocessor); // Create a context for parsing the directive PreprocessorDirectiveContext directiveContext; directiveContext.preprocessor = preprocessor; directiveContext.parseError = false; // Parse and handle the directive HandleDirective(&directiveContext); continue; } // otherwise, if we are currently in a skipping mode, then skip tokens if (IsSkipping(preprocessor)) { if( token.Type == TokenType::EndOfFile ) { GetSink(preprocessor)->diagnose(PeekLoc(preprocessor), Diagnostics::endOfFileInPreprocessorConditional); return token; } AdvanceRawToken(preprocessor); continue; } // otherwise read a token, which may involve macro expansion return AdvanceToken(preprocessor); } } // intialize a preprocessor context, using the given sink for errros static void InitializePreprocessor( Preprocessor* preprocessor, DiagnosticSink* sink) { preprocessor->sink = sink; preprocessor->includeHandler = NULL; preprocessor->endOfFileToken.Type = TokenType::EndOfFile; preprocessor->endOfFileToken.flags = TokenFlag::AtStartOfLine; } // clean up after an environment PreprocessorEnvironment::~PreprocessorEnvironment() { for (auto pair : this->macros) { DestroyMacro(NULL, pair.Value); } } // finalize a preprocessor and free any memory still in use static void FinalizePreprocessor( Preprocessor* preprocessor) { // Clear out any waiting input streams PreprocessorInputStream* input = preprocessor->inputStream; while (input) { PreprocessorInputStream* parent = input->parent; DestroyInputStream(preprocessor, input); input = parent; } #if 0 // clean up any macros that were allocated for (auto pair : preprocessor->globalEnv.macros) { DestroyMacro(preprocessor, pair.Value); } #endif } // Add a simple macro definition from a string (e.g., for a // `-D` option passed on the command line static void DefineMacro( Preprocessor* preprocessor, String const& key, String const& value) { String fileName = "command line"; PreprocessorMacro* macro = CreateMacro(preprocessor); // Use existing `Lexer` to generate a token stream. Lexer lexer; macro->tokens = lexer.Parse(fileName, value, GetSink(preprocessor)); macro->nameToken = Token(TokenType::Identifier, key, 0, 0, 0, fileName); PreprocessorMacro* oldMacro = NULL; if (preprocessor->globalEnv.macros.TryGetValue(key, oldMacro)) { DestroyMacro(preprocessor, oldMacro); } preprocessor->globalEnv.macros[key] = macro; } // read the entire input into tokens static TokenList ReadAllTokens( Preprocessor* preprocessor) { TokenList tokens; for (;;) { Token token = ReadToken(preprocessor); tokens.mTokens.Add(token); // Note: we include the EOF token in the list, // since that is expected by the `TokenList` type. if (token.Type == TokenType::EndOfFile) break; } return tokens; } // Take a string of source code and preprocess it into a list of tokens. TokenList PreprocessSource( CoreLib::String const& source, CoreLib::String const& fileName, DiagnosticSink* sink, IncludeHandler* includeHandler) { Preprocessor preprocessor; InitializePreprocessor(&preprocessor, sink); preprocessor.includeHandler = includeHandler; // create an initial input stream based on the provided buffer preprocessor.inputStream = CreateInputStreamForSource(&preprocessor, source, fileName); TokenList tokens = ReadAllTokens(&preprocessor); FinalizePreprocessor(&preprocessor); return tokens; } TokenList PreprocessSource( CoreLib::String const& source, CoreLib::String const& fileName, DiagnosticSink* sink, IncludeHandler* includeHandler, CoreLib::Dictionary defines) { Preprocessor preprocessor; InitializePreprocessor(&preprocessor, sink); preprocessor.includeHandler = includeHandler; for (auto p : defines) { DefineMacro(&preprocessor, p.Key, p.Value); } // create an initial input stream based on the provided buffer preprocessor.inputStream = CreateInputStreamForSource(&preprocessor, source, fileName); TokenList tokens = ReadAllTokens(&preprocessor); FinalizePreprocessor(&preprocessor); // debugging: build the pre-processed source back together #if 0 StringBuilder sb; for (auto t : tokens) { if (t.flags & TokenFlag::AtStartOfLine) { sb << "\n"; } else if (t.flags & TokenFlag::AfterWhitespace) { sb << " "; } sb << t.Content; } String s = sb.ProduceString(); #endif return tokens; } }} ================================================ FILE: Source/SpireCore/Preprocessor.h ================================================ // Preprocessor.h #ifndef SPIRE_PREPROCESSOR_H_INCLUDED #define SPIRE_PREPROCESSOR_H_INCLUDED #include "../CoreLib/Basic.h" #include "../CoreLib/Tokenizer.h" #include "../SpireCore/Lexer.h" namespace Spire{ namespace Compiler { class DiagnosticSink; // Callback interface for the preprocessor to use when looking // for files in `#include` directives. struct IncludeHandler { virtual bool TryToFindIncludeFile( CoreLib::String const& pathToInclude, CoreLib::String const& pathIncludedFrom, CoreLib::String* outFoundPath, CoreLib::String* outFoundSource) = 0; }; // Take a string of source code and preprocess it into a list of tokens. TokenList PreprocessSource( CoreLib::String const& source, CoreLib::String const& fileName, DiagnosticSink* sink, IncludeHandler* includeHandler); TokenList PreprocessSource( CoreLib::String const& source, CoreLib::String const& fileName, DiagnosticSink* sink, IncludeHandler* includeHandler, CoreLib::Dictionary defines); }} #endif ================================================ FILE: Source/SpireCore/SamplerUsageAnalysis.cpp ================================================ #include "SamplerUsageAnalysis.h" namespace Spire { namespace Compiler { using namespace CoreLib; void AnalyzeSamplerUsageImpl(EnumerableDictionary> & samplerTextures, ILProgram * program, CFGNode * code, DiagnosticSink * sink, HashSet & processedFunctions, Dictionary & paramValues) { for (auto & instr : code->GetAllInstructions()) { auto call = dynamic_cast(&instr); if (call) { if (call->Function.StartsWith("Sample") && !program->Functions.ContainsKey(call->Function)) { auto textureOp = call->Arguments[0].Ptr(); auto samplerOp = call->Arguments[1].Ptr(); ILModuleParameterInstance * textureInstance = nullptr; ILModuleParameterInstance * samplerInstance = nullptr; if (auto texArg = dynamic_cast(textureOp)) paramValues.TryGetValue(texArg->ArgId, textureInstance); else if (auto texArg1 = dynamic_cast(textureOp)) textureInstance = texArg1; if (auto samplerArg = dynamic_cast(samplerOp)) paramValues.TryGetValue(samplerArg->ArgId, samplerInstance); else if (auto samplerArg1 = dynamic_cast(samplerOp)) samplerInstance = samplerArg1; if (textureInstance && samplerInstance) { auto list = samplerTextures.TryGetValue(samplerInstance); if (!list) { samplerTextures.Add(samplerInstance, List()); list = samplerTextures.TryGetValue(samplerInstance); } list->Add(textureInstance); } else { SPIRE_INTERNAL_ERROR(sink, instr.Position); } } else { RefPtr func = nullptr; if (program->Functions.TryGetValue(call->Function, func)) { if (!processedFunctions.Contains(func.Ptr())) { // trace into the function call Dictionary args; for (int i = 0; i < call->Arguments.Count(); i++) { if (auto arg = dynamic_cast(call->Arguments[i].Ptr())) { ILModuleParameterInstance * argValue = nullptr; if (paramValues.TryGetValue(arg->ArgId, argValue)) args[i + 1] = argValue; } else if (auto argValue = dynamic_cast(call->Arguments[i].Ptr())) { args[i + 1] = argValue; } } if (func->Code) { processedFunctions.Add(func.Ptr()); AnalyzeSamplerUsageImpl(samplerTextures, program, func->Code.Ptr(), sink, processedFunctions, args); processedFunctions.Remove(func.Ptr()); } } } } } } } void AnalyzeSamplerUsage(EnumerableDictionary> & samplerTextures, ILProgram * program, CFGNode * code, DiagnosticSink * sink) { Dictionary params; HashSet processedFuncs; AnalyzeSamplerUsageImpl(samplerTextures, program, code, sink, processedFuncs, params); } } } ================================================ FILE: Source/SpireCore/SamplerUsageAnalysis.h ================================================ #include "IL.h" #include "CompiledProgram.h" namespace Spire { namespace Compiler { void AnalyzeSamplerUsage(CoreLib::EnumerableDictionary> & samplerTextures, ILProgram * program, CFGNode * code, DiagnosticSink * sink); } } ================================================ FILE: Source/SpireCore/Schedule.cpp ================================================ #include "Schedule.h" #include "Lexer.h" using namespace CoreLib::Basic; namespace Spire { namespace Compiler { class ScheduleParser { private: DiagnosticSink * sink; TokenList tokens; TokenReader reader; String fileName; Token ReadToken(const char * string) { if (reader.PeekTokenType() == TokenType::EndOfFile) { sink->diagnose(reader.PeekLoc(), Diagnostics::tokenNameExpectedButEOF, string); throw 0; } else if (reader.PeekToken().Content != string) { sink->diagnose(reader.PeekLoc(), Diagnostics::tokenNameExpected, string); throw 20001; } return reader.AdvanceToken(); } Token ReadToken(CoreLib::Text::TokenType type) { if (reader.PeekTokenType() == TokenType::EndOfFile) { sink->diagnose(reader.PeekLoc(), Diagnostics::tokenTypeExpectedButEOF, type); throw 0; } else if (reader.PeekTokenType() != type) { sink->diagnose(reader.PeekLoc(), Diagnostics::tokenTypeExpected, type); throw 20001; } return reader.AdvanceToken(); } bool LookAheadToken(const char * string) { if (reader.PeekTokenType() == TokenType::EndOfFile) { // TODO(tfoley): this error condition seems wrong // it shouldn't be an error to see EOF as out *lookahead* sink->diagnose(reader.PeekLoc(), Diagnostics::tokenNameExpectedButEOF); return false; } else { if (reader.PeekToken().Content == string) return true; else return false; } } public: ScheduleParser(DiagnosticSink * sink) : sink(sink) {} Schedule Parse(String source, String _fileName) { this->fileName = _fileName; Schedule schedule; Lexer lex; tokens = lex.Parse(fileName, source, sink); try { while (reader.PeekTokenType() != TokenType::EndOfFile) { if (LookAheadToken("attrib")) { EnumerableDictionary additionalAttributes; ReadToken("attrib"); String choiceName = ReadToken(TokenType::Identifier).Content; while (LookAheadToken(".")) { choiceName = choiceName + "."; ReadToken(TokenType::Dot); choiceName = choiceName + ReadToken(TokenType::Identifier).Content; } ReadToken(TokenType::OpAssign); while (reader.PeekTokenType() != TokenType::EndOfFile) { auto name = ReadToken(TokenType::Identifier).Content; String value; if (LookAheadToken(":")) { ReadToken(":"); value = ReadToken(TokenType::StringLiterial).Content; } additionalAttributes[name] = value; if (LookAheadToken(",")) ReadToken(TokenType::Comma); else break; } schedule.AddtionalAttributes[choiceName] = additionalAttributes; } else { String choiceName = ReadToken(TokenType::Identifier).Content; while (LookAheadToken(".")) { choiceName = choiceName + "."; ReadToken(TokenType::Dot); choiceName = choiceName + ReadToken(TokenType::Identifier).Content; } ReadToken(TokenType::OpAssign); List> worlds; while (reader.PeekTokenType() != TokenType::EndOfFile) { auto token = ReadToken(TokenType::StringLiterial); RefPtr choiceValue = new ChoiceValueSyntaxNode(); choiceValue->Position = token.Position; choiceValue->WorldName = token.Content; worlds.Add(choiceValue); if (LookAheadToken(",")) ReadToken(TokenType::Comma); else break; } schedule.Choices[choiceName] = worlds; } ReadToken(TokenType::Semicolon); } } catch (...) { } return schedule; } }; Schedule Schedule::Parse(String source, String fileName, DiagnosticSink * sink) { return ScheduleParser(sink).Parse(source, fileName); } } } ================================================ FILE: Source/SpireCore/Schedule.h ================================================ #ifndef BAKER_SL_SCHEDULE_H #define BAKER_SL_SCHEDULE_H #include "../CoreLib/Basic.h" #include "Diagnostics.h" #include "Syntax.h" namespace Spire { namespace Compiler { class Schedule { public: CoreLib::EnumerableDictionary>> Choices; CoreLib::EnumerableDictionary> AddtionalAttributes; static Schedule Parse(CoreLib::String source, CoreLib::String fileName, DiagnosticSink * sink); }; } } #endif ================================================ FILE: Source/SpireCore/ScopeDictionary.h ================================================ #ifndef RASTER_RENDERER_SCOPE_DICTIONARY_H #define RASTER_RENDERER_SCOPE_DICTIONARY_H #include "../CoreLib/Basic.h" using namespace CoreLib::Basic; namespace Spire { namespace Compiler { template class ScopeDictionary { public: LinkedList> dicts; public: void PushScope() { dicts.AddLast(); } void PopScope() { dicts.Delete(dicts.LastNode()); } bool TryGetValue(const TKey & key, TValue & value) { for (auto iter = dicts.LastNode(); iter; iter = iter->GetPrevious()) { bool rs = iter->Value.TryGetValue(key, value); if (rs) return true; } return false; } bool TryGetValueInCurrentScope(const TKey & key, TValue & value) { return dicts.Last().TryGetValue(key, value); } void Add(const TKey & key, const TValue & value) { dicts.Last().Add(key, value); } void Set(const TKey & key, const TValue & value) { dicts.Last()[key] = value; } }; } } #endif ================================================ FILE: Source/SpireCore/SemanticsVisitor.cpp ================================================ #include "SyntaxVisitors.h" namespace Spire { namespace Compiler { bool IsNumeric(BaseType t) { return t == BaseType::Int || t == BaseType::Float || t == BaseType::UInt; } String GetFullComponentName(ComponentSyntaxNode * comp) { StringBuilder sb; sb << comp->Name.Content; for (auto & param : comp->GetParameters()) { sb << "@" << param->Type->ToString(); } return sb.ProduceString(); } String TranslateHLSLTypeNames(String name) { if (name == "float2" || name == "half2") return "vec2"; else if (name == "float3" || name == "half3") return "vec3"; else if (name == "float4" || name == "half4") return "vec4"; else if (name == "half") return "float"; else if (name == "int2") return "ivec2"; else if (name == "int3") return "ivec3"; else if (name == "int4") return "ivec4"; else if (name == "uint2") return "uvec2"; else if (name == "uint3") return "uvec3"; else if (name == "uint4") return "uvec4"; else if (name == "float3x3" || name == "half3x3") return "mat3"; else if (name == "float4x4" || name == "half4x4") return "mat4"; else return name; } class SemanticsVisitor : public SyntaxVisitor { ProgramSyntaxNode * program = nullptr; FunctionSyntaxNode * function = nullptr; FunctionSymbol * currentFunc = nullptr; ShaderSymbol * currentShader = nullptr; PipelineSymbol * currentPipeline = nullptr; ImportOperatorDefSyntaxNode * currentImportOperator = nullptr; ShaderComponentSymbol * currentComp = nullptr; ComponentSyntaxNode * currentCompNode = nullptr; List loops; SymbolTable * symbolTable; public: SemanticsVisitor(SymbolTable * symbols, DiagnosticSink * pErr) :SyntaxVisitor(pErr), symbolTable(symbols) { } // return true if world0 depends on world1 (there exists a series of import operators that converts world1 variables to world0) bool IsWorldDependent(PipelineSymbol * pipeline, String world0, String world1) { HashSet depWorldsSet; List depWorlds; depWorlds.Add(world0); for (int i = 0; i < depWorlds.Count(); i++) { auto & dep = pipeline->WorldDependency[world0].GetValue(); if (dep.Contains(world1)) return true; else { for (auto w : dep) if (depWorldsSet.Add(w)) depWorlds.Add(w); } } return false; } public: // Translate Types RefPtr typeResult; RefPtr TranslateTypeNode(const RefPtr & node) { node->Accept(this); return typeResult; } RefPtr VisitBasicType(BasicTypeSyntaxNode * typeNode) override { RefPtr expType = new BasicExpressionType(); if (typeNode->TypeName == "int") expType->BaseType = BaseType::Int; else if (typeNode->TypeName == "uint") expType->BaseType = BaseType::UInt; else if (typeNode->TypeName == "float" || typeNode->TypeName == "half") expType->BaseType = BaseType::Float; else if (typeNode->TypeName == "ivec2" || typeNode->TypeName == "int2") expType->BaseType = BaseType::Int2; else if (typeNode->TypeName == "ivec3" || typeNode->TypeName == "int3") expType->BaseType = BaseType::Int3; else if (typeNode->TypeName == "ivec4" || typeNode->TypeName == "int4") expType->BaseType = BaseType::Int4; else if (typeNode->TypeName == "uvec2" || typeNode->TypeName == "uint2") expType->BaseType = BaseType::UInt2; else if (typeNode->TypeName == "uvec3" || typeNode->TypeName == "uint3") expType->BaseType = BaseType::UInt3; else if (typeNode->TypeName == "uvec4" || typeNode->TypeName == "uint4") expType->BaseType = BaseType::UInt4; else if (typeNode->TypeName == "vec2" || typeNode->TypeName == "float2" || typeNode->TypeName == "half2") expType->BaseType = BaseType::Float2; else if (typeNode->TypeName == "vec3" || typeNode->TypeName == "float3" || typeNode->TypeName == "half3") expType->BaseType = BaseType::Float3; else if (typeNode->TypeName == "vec4" || typeNode->TypeName == "float4" || typeNode->TypeName == "half4") expType->BaseType = BaseType::Float4; else if (typeNode->TypeName == "mat3" || typeNode->TypeName == "mat3x3" || typeNode->TypeName == "float3x3" || typeNode->TypeName == "half3x3") expType->BaseType = BaseType::Float3x3; else if (typeNode->TypeName == "mat4" || typeNode->TypeName == "mat4x4" || typeNode->TypeName == "float4x4" || typeNode->TypeName == "half4x4") expType->BaseType = BaseType::Float4x4; else if (typeNode->TypeName == "texture" || typeNode->TypeName == "Texture" || typeNode->TypeName == "Texture2D") expType->BaseType = BaseType::Texture2D; else if (typeNode->TypeName == "TextureCUBE" || typeNode->TypeName == "TextureCube") expType->BaseType = BaseType::TextureCube; else if (typeNode->TypeName == "TextureCubeArray") expType->BaseType = BaseType::TextureCubeArray; else if (typeNode->TypeName == "TextureCubeShadowArray" || typeNode->TypeName == "TextureCubeArrayShadow") expType->BaseType = BaseType::TextureCubeShadowArray; else if (typeNode->TypeName == "Texture2DArray") expType->BaseType = BaseType::Texture2DArray; else if (typeNode->TypeName == "Texture2DArrayShadow") expType->BaseType = BaseType::Texture2DArrayShadow; else if (typeNode->TypeName == "Texture2DShadow") expType->BaseType = BaseType::Texture2DShadow; else if (typeNode->TypeName == "TextureCubeShadow") expType->BaseType = BaseType::TextureCubeShadow; else if (typeNode->TypeName == "Texture3D") expType->BaseType = BaseType::Texture3D; else if (typeNode->TypeName == "SamplerState" || typeNode->TypeName == "sampler" || typeNode->TypeName == "sampler_state") expType->BaseType = BaseType::SamplerState; else if (typeNode->TypeName == "SamplerComparisonState") expType->BaseType = BaseType::SamplerComparisonState; else if (typeNode->TypeName == "void") expType->BaseType = BaseType::Void; else if (typeNode->TypeName == "bool") expType->BaseType = BaseType::Bool; else { expType->BaseType = BaseType::Struct; if (auto decl = symbolTable->LookUp(typeNode->TypeName)) { if (auto structDecl = dynamic_cast(decl)) { expType->structDecl = structDecl; } else if (auto typeDefDecl = dynamic_cast(decl)) { RefPtr namedType = new NamedExpressionType(); namedType->decl = typeDefDecl; typeResult = namedType; return typeNode; } else { getSink()->diagnose(typeNode, Diagnostics::undefinedTypeName, typeNode->TypeName); } } else if (currentPipeline || currentShader) { PipelineSymbol * pipe = currentPipeline ? currentPipeline : currentShader->ParentPipeline; bool matched = false; if (pipe) { if (pipe->Worlds.ContainsKey(typeNode->TypeName)) { expType->BaseType = BaseType::Record; expType->RecordTypeName = typeNode->TypeName; matched = true; } } if (currentImportOperator) { if (typeNode->TypeName == currentImportOperator->TypeName.Content) { expType->BaseType = BaseType::Generic; expType->GenericTypeVar = typeNode->TypeName; matched = true; } } if (!matched) { getSink()->diagnose(typeNode, Diagnostics::undefinedTypeName, typeNode->TypeName); typeResult = ExpressionType::Error; } } else { getSink()->diagnose(typeNode, Diagnostics::undefinedTypeName, typeNode->TypeName); typeResult = ExpressionType::Error; return typeNode; } } typeResult = expType; return typeNode; } RefPtr VisitArrayType(ArrayTypeSyntaxNode * typeNode) override { RefPtr rs = new ArrayExpressionType(); rs->ArrayLength = typeNode->ArrayLength; typeNode->BaseType->Accept(this); rs->BaseType = typeResult; typeResult = rs; return typeNode; } RefPtr VisitGenericType(GenericTypeSyntaxNode * typeNode) override { RefPtr rs = new GenericExpressionType(); typeNode->BaseType->Accept(this); rs->BaseType = typeResult; rs->GenericTypeName = typeNode->GenericTypeName; if (rs->GenericTypeName != "PackedBuffer" && rs->GenericTypeName != "StructuredBuffer" && rs->GenericTypeName != "RWStructuredBuffer" && rs->GenericTypeName != "Uniform" && rs->GenericTypeName != "Patch" && rs->GenericTypeName != "PackedBuffer") { getSink()->diagnose(typeNode, Diagnostics::undefinedIdentifier, rs->GenericTypeName); } typeResult = rs; return typeNode; } public: RefPtr VisitPipeline(PipelineSyntaxNode * pipeline) override { RefPtr psymbol = new PipelineSymbol(); psymbol->SyntaxNode = pipeline; if (pipeline->ParentPipelineName.Content.Length()) { RefPtr parentPipeline; if (symbolTable->Pipelines.TryGetValue(pipeline->ParentPipelineName.Content, parentPipeline)) { psymbol->ParentPipeline = parentPipeline.Ptr(); } else { getSink()->diagnose(pipeline->ParentPipelineName, Diagnostics::undefinedPipelineName, pipeline->ParentPipelineName.Content); } } currentPipeline = psymbol.Ptr(); symbolTable->Pipelines.Add(pipeline->Name.Content, psymbol); for (auto world : pipeline->GetWorlds()) { if (!psymbol->Worlds.ContainsKey(world->Name.Content)) { psymbol->Worlds.Add(world->Name.Content, world.Ptr()); psymbol->WorldDependency.Add(world->Name.Content, EnumerableHashSet()); } else { getSink()->diagnose(world.Ptr(), Diagnostics::worldNameAlreadyDefined, world->Name.Content); } } for (auto comp : pipeline->GetAbstractComponents()) { comp->Type = TranslateTypeNode(comp->TypeNode); if (comp->IsRequire() || comp->IsInput() || (comp->Rate && comp->Rate->Worlds.Count() == 1 && psymbol->IsAbstractWorld(comp->Rate->Worlds.First().World.Content))) AddNewComponentSymbol(psymbol->Components, psymbol->FunctionComponents, comp); else { getSink()->diagnose(comp.Ptr(), Diagnostics::cannotDefineComponentsInAPipeline); } } for (auto & op : pipeline->GetImportOperators()) { psymbol->AddImportOperator(op); } // add initial world dependency edges for (auto op : pipeline->GetImportOperators()) { if (!psymbol->WorldDependency.ContainsKey(op->DestWorld.Content)) getSink()->diagnose(op->DestWorld, Diagnostics::undefinedWorldName, op->DestWorld.Content); else { if (psymbol->Worlds[op->DestWorld.Content].GetValue()->IsAbstract()) getSink()->diagnose(op->DestWorld, Diagnostics::abstractWorldAsTargetOfImport); else if (!psymbol->WorldDependency.ContainsKey(op->SourceWorld.Content)) getSink()->diagnose(op->SourceWorld, Diagnostics::undefinedWorldName2, op->SourceWorld.Content); else { if (IsWorldDependent(psymbol.Ptr(), op->SourceWorld.Content, op->DestWorld.Content)) { getSink()->diagnose(op->Name, Diagnostics::importOperatorCircularity, op->Name.Content, op->SourceWorld.Content, op->DestWorld.Content); } else { psymbol->WorldDependency[op->DestWorld.Content].GetValue().Add(op->SourceWorld.Content); } } } } // propagate world dependency graph bool changed = true; while (changed) { changed = false; for (auto world : pipeline->GetWorlds()) { EnumerableHashSet & dependentWorlds = psymbol->WorldDependency[world->Name.Content].GetValue(); List loopRange; for (auto w : dependentWorlds) loopRange.Add(w); for (auto w : loopRange) { EnumerableHashSet & ddw = psymbol->WorldDependency[w].GetValue(); for (auto ww : ddw) { if (!dependentWorlds.Contains(ww)) { dependentWorlds.Add(ww); changed = true; } } } } } for (auto & op : pipeline->GetImportOperators()) { currentImportOperator = op.Ptr(); HashSet paraNames; for (auto & para : op->GetParameters()) { if (paraNames.Contains(para->Name.Content)) getSink()->diagnose(para.Ptr(), Diagnostics::parameterAlreadyDefined, para->Name); else paraNames.Add(para->Name.Content); para->Type = TranslateTypeNode(para->TypeNode); if (para->Type->Equals(ExpressionType::Void.Ptr())) { getSink()->diagnose(para.Ptr(), Diagnostics::parameterCannotBeVoid); } } auto oldSymFuncs = symbolTable->Functions; auto oldSymFuncOverloads = symbolTable->FunctionOverloads; for (auto req : op->Requirements) { VisitFunctionDeclaration(req.Ptr()); } op->Body->Accept(this); symbolTable->Functions = oldSymFuncs; symbolTable->FunctionOverloads = oldSymFuncOverloads; currentImportOperator = nullptr; } currentPipeline = nullptr; return pipeline; } virtual CoreLib::RefPtr VisitInterface(InterfaceSyntaxNode * interfaceNode) override { for (auto & comp : interfaceNode->GetComponents()) { interfaceNode->Scope->decls.AddIfNotExists(comp->Name.Content, comp.Ptr()); for (auto & param : comp->GetParameters()) { param->Type = TranslateTypeNode(param->TypeNode); if (param->Expr) getSink()->diagnose(param->Expr->Position, Diagnostics::defaultParamNotAllowedInInterface, param->Name); } comp->Type = TranslateTypeNode(comp->TypeNode); if (comp->Expression) comp->Expression->Accept(this); if (comp->BlockStatement) comp->BlockStatement->Accept(this); } return interfaceNode; } virtual RefPtr VisitImport(ImportSyntaxNode * import) override { RefPtr refShader; symbolTable->Shaders.TryGetValue(import->ShaderName.Content, refShader); if (refShader) { // type check List paramList; for (auto & comp : refShader->Components) if (comp.Value->IsRequire()) paramList.Add(comp.Value.Ptr()); int position = 0; bool namedArgumentAppeared = false; for (auto & arg : import->Arguments) { if (arg->ArgumentName.Content.Length()) namedArgumentAppeared = true; else { if (namedArgumentAppeared) { getSink()->diagnose(arg->Expression.Ptr(), Diagnostics::positionArgumentAfterNamed); break; } if (position >= paramList.Count()) { getSink()->diagnose(arg->Expression.Ptr(), Diagnostics::tooManyArguments); break; } arg->ArgumentName.Content = paramList[position]->Name; arg->ArgumentName.Position = arg->Position; } position++; RefPtr refComp, argComp; if (auto funcs = refShader->FunctionComponents.TryGetValue(arg->ArgumentName.Content)) if (funcs->First()->IsRequire()) refComp = funcs->First(); if (!refComp) refShader->Components.TryGetValue(arg->ArgumentName.Content, refComp); if (refComp) { if (refComp->Implementations.First()->SyntaxNode->IsComponentFunction()) // this is a function parameter { arg->ArgumentName.Content = refComp->Name; // construct an invocation node to resolve overloaded component function RefPtr tempInvoke = new InvokeExpressionSyntaxNode(); tempInvoke->Position = arg->Position; tempInvoke->Scope = arg->Scope; tempInvoke->FunctionExpr = arg->Expression; for (auto & param : refComp->Implementations.First()->SyntaxNode->GetParameters()) { RefPtr tempArg = new VarExpressionSyntaxNode(); tempArg->Type = param->Type; tempInvoke->Arguments.Add(tempArg); } auto resolvedExpr = ResolveInvoke(tempInvoke.Ptr()); if (auto resolveInvoke = resolvedExpr.As()) { auto funcType = resolveInvoke->FunctionExpr->Type->AsBasicType(); if (funcType->Component) { // modify function name to resolved name if (auto memberExpr = arg->Expression.As()) { auto basicType = new BasicExpressionType(BaseType::Function); basicType->Component = funcType->Component; memberExpr->Type = basicType; //memberExpr->MemberName = funcType->Component->Name; } else if (auto varExpr = arg->Expression.As()) { varExpr->Type = funcType; //varExpr->Variable = funcType->Component->Name; } } else getSink()->diagnose(arg.Ptr(), Diagnostics::ordinaryFunctionAsModuleArgument); } else getSink()->diagnose(arg.Ptr(), Diagnostics::invalidValueForArgument, arg->ArgumentName.Content); } else { arg->Accept(this); if (!refComp->Type->DataType->Equals(arg->Expression->Type.Ptr())) { getSink()->diagnose(arg->Expression.Ptr(), Diagnostics::argumentTypeDoesNotMatchParameterType, arg->Expression->Type, refComp->Type->DataType); } if (!refComp->IsRequire()) getSink()->diagnose(arg->ArgumentName, Diagnostics::nameIsNotAParameterOfCallee, arg->ArgumentName.Content, import->ShaderName.Content); } } else getSink()->diagnose(arg->ArgumentName, Diagnostics::nameIsNotAParameterOfCallee, arg->ArgumentName.Content, import->ShaderName.Content); } } return import; } class ShaderImportVisitor : public SyntaxVisitor { private: SymbolTable * symbolTable = nullptr; ShaderSymbol * currentShader = nullptr; ShaderComponentSymbol * currentComp = nullptr; public: ShaderImportVisitor(DiagnosticSink * writer, SymbolTable * symTable) : SyntaxVisitor(writer), symbolTable(symTable) {} virtual RefPtr VisitShader(ShaderSyntaxNode * shader) override { currentShader = symbolTable->Shaders[shader->Name.Content].GetValue().Ptr(); SyntaxVisitor::VisitShader(shader); currentShader = nullptr; return shader; } virtual RefPtr VisitComponent(ComponentSyntaxNode * comp) override { RefPtr compSym; currentShader->Components.TryGetValue(comp->Name.Content, compSym); currentComp = compSym.Ptr(); SyntaxVisitor::VisitComponent(comp); if (comp->Expression || comp->BlockStatement) { if (comp->IsRequire()) getSink()->diagnose(comp, Diagnostics::requireWithComputation); if (comp->IsParam()) getSink()->diagnose(comp, Diagnostics::paramWithComputation); } if (compSym->Type->DataType->GetBindableResourceType() != BindableResourceType::NonBindable && !comp->IsParam() && !comp->IsRequire() && !dynamic_cast(comp->ParentDecl)) getSink()->diagnose(comp, Diagnostics::resourceTypeMustBeParamOrRequire, comp->Name); if (compSym->Type->DataType->GetBindableResourceType() != BindableResourceType::NonBindable && (comp->Expression || comp->BlockStatement)) getSink()->diagnose(comp, Diagnostics::cannotDefineComputationOnResourceType, comp->Name); if (comp->HasSimpleAttribute("FragDepth")) { if (!comp->IsOutput()) getSink()->diagnose(comp, Diagnostics::fragDepthAttributeCanOnlyApplyToOutput); if (!comp->Type->Equals(ExpressionType::Float)) getSink()->diagnose(comp, Diagnostics::fragDepthAttributeCanOnlyApplyToFloatComponent); } currentComp = nullptr; return comp; } virtual RefPtr VisitImport(ImportSyntaxNode * import) override { RefPtr refShader; symbolTable->Shaders.TryGetValue(import->ShaderName.Content, refShader); if (!refShader) getSink()->diagnose(import->ShaderName, Diagnostics::undefinedIdentifier, import->ShaderName.Content); currentShader->DependentShaders.Add(refShader.Ptr()); if (!currentComp) { ShaderUsing su; su.Shader = refShader.Ptr(); su.IsPublic = import->IsPublic(); if (import->IsInplace) { currentShader->ShaderUsings.Add(su); } else { if (currentShader->ShaderObjects.ContainsKey(import->ObjectName.Content) || currentShader->Components.ContainsKey(import->ObjectName.Content)) { getSink()->diagnose(import->ShaderName, Diagnostics::nameAlreadyDefined, import->ShaderName); } currentShader->ShaderObjects[import->ObjectName.Content] = su; } } if (currentComp) getSink()->diagnose(import->ShaderName, Diagnostics::usingInComponentDefinition); return import; } }; void CheckShaderInterfaceRequirements(ShaderSymbol * shaderSym) { for (auto interfaceName : shaderSym->SyntaxNode->InterfaceNames) { auto interfaceNode = dynamic_cast(symbolTable->LookUp(interfaceName.Content)); if (!interfaceNode) { getSink()->diagnose(interfaceName.Position, Diagnostics::undefinedIdentifier, interfaceName); continue; } for (auto comp : interfaceNode->GetComponents()) { auto compRef = shaderSym->ResolveComponentReference(GetFullComponentName(comp.Ptr())); if (compRef.IsAccessible) { if (!compRef.Component->Implementations.First()->SyntaxNode->IsPublic()) { getSink()->diagnose(compRef.Component->Implementations.First()->SyntaxNode->Position, Diagnostics::interfaceImplMustBePublic, comp->Name.Content, interfaceName); getSink()->diagnose(comp->Position, Diagnostics::seeInterfaceDefinitionOf, comp->Name); } if (!compRef.Component->Type->DataType->Equals(comp->Type.Ptr())) { getSink()->diagnose(compRef.Component->Implementations.First()->SyntaxNode->Position, Diagnostics::componentTypeDoesNotMatchInterface, comp->Name, interfaceName); getSink()->diagnose(comp->Position, Diagnostics::seeInterfaceDefinitionOf, comp->Name); } } else { if (!comp->Expression && !comp->BlockStatement) // interface does not define default impl, and shader does not provide impl { getSink()->diagnose(shaderSym->SyntaxNode->Position, Diagnostics::shaderDidNotDefineComponent, shaderSym->SyntaxNode->Name, comp->Name.Content, interfaceName); getSink()->diagnose(comp->Position, Diagnostics::seeInterfaceDefinitionOf, comp->Name); if (compRef.Component) getSink()->diagnose(compRef.Component->Implementations.First()->SyntaxNode->Position, Diagnostics::doYouForgetToMakeComponentAccessible, comp->Name, shaderSym->SyntaxNode->Name); } else // if interface provides default impl, add it to shader { CloneContext ctx; auto newComp = comp->Clone(ctx); shaderSym->SyntaxNode->Members.Add(newComp); newComp->modifiers.flags |= Public; AddNewComponentSymbol(shaderSym->Components, shaderSym->FunctionComponents, newComp); } } } } } // pass 1: fill components in shader symbol table void VisitShaderPass1(ShaderSyntaxNode * shader) { HashSet inheritanceSet; auto curShader = shader; inheritanceSet.Add(curShader->Name.Content); auto & shaderSymbol = symbolTable->Shaders[curShader->Name.Content].GetValue(); this->currentShader = shaderSymbol.Ptr(); if (shader->ParentPipelineName.Content.Length() == 0) // implicit pipeline { if (program->GetPipelines().Count() == 1) { shader->ParentPipelineName = shader->Name; // get line and col from shader name shader->ParentPipelineName.Content = program->GetPipelines().First()->Name.Content; } else if (!shader->IsModule) { // current compilation context has more than one pipeline defined, // in which case we do not allow implicit pipeline specification getSink()->diagnose(curShader->Name, Diagnostics::explicitPipelineSpecificationRequiredForShader, shader->Name.Content); } } auto pipelineName = shader->ParentPipelineName.Content; if (pipelineName.Length()) { auto pipeline = symbolTable->Pipelines.TryGetValue(pipelineName); if (pipeline) shaderSymbol->ParentPipeline = pipeline->Ptr(); else { getSink()->diagnose(shader->ParentPipelineName, Diagnostics::undefinedPipelineName, pipelineName); throw 0; } } if (shader->IsModule) shaderSymbol->IsAbstract = true; // add components to symbol table for (auto & mbr : shader->Members) { if (auto comp = dynamic_cast(mbr.Ptr())) { comp->Type = TranslateTypeNode(comp->TypeNode); if (comp->IsRequire()) { shaderSymbol->IsAbstract = true; if (!shaderSymbol->SyntaxNode->IsModule) { getSink()->diagnose(shaderSymbol->SyntaxNode, Diagnostics::parametersOnlyAllowedInModules); } } for (auto & param : comp->GetParameters()) param->Type = TranslateTypeNode(param->TypeNode); AddNewComponentSymbol(shaderSymbol->Components, shaderSymbol->FunctionComponents, comp); } } // add shader objects to symbol table ShaderImportVisitor importVisitor(sink, symbolTable); shader->Accept(&importVisitor); this->currentShader = nullptr; } // pass 2: type checking component definitions void VisitShaderPass2(ShaderSyntaxNode * shaderNode) { RefPtr shaderSym; if (!symbolTable->Shaders.TryGetValue(shaderNode->Name.Content, shaderSym)) return; CheckShaderInterfaceRequirements(shaderSym.Ptr()); this->currentShader = shaderSym.Ptr(); for (auto & comp : shaderNode->Members) { comp->Accept(this); } this->currentShader = nullptr; } bool MatchType_GenericType(String typeName, ExpressionType * valueType) { if (auto basicType = valueType->AsBasicType()) return basicType->GenericTypeVar == typeName; return false; } bool MatchType_ValueReceiver(ExpressionType * receiverType, ExpressionType * valueType) { if (receiverType->Equals(valueType)) return true; if (receiverType->IsIntegral() && valueType->Equals(ExpressionType::Int.Ptr())) return true; if (receiverType->Equals(ExpressionType::Float.Ptr()) && valueType->IsIntegral()) return true; if (receiverType->IsVectorType() && valueType->IsVectorType()) { auto recieverBasicType = receiverType->AsBasicType(); auto valueBasicType = valueType->AsBasicType(); if (GetVectorBaseType(recieverBasicType->BaseType) == BaseType::Float && GetVectorSize(recieverBasicType->BaseType) == GetVectorSize(valueBasicType->BaseType)) return true; if (GetVectorBaseType(recieverBasicType->BaseType) == BaseType::UInt && GetVectorBaseType(valueBasicType->BaseType) == BaseType::Int && GetVectorSize(recieverBasicType->BaseType) == GetVectorSize(valueBasicType->BaseType)) return true; } return false; } virtual RefPtr VisitComponent(ComponentSyntaxNode * comp) override { this->currentCompNode = comp; RefPtr compSym; currentShader->Components.TryGetValue(comp->Name.Content, compSym); this->currentComp = compSym.Ptr(); if (auto specialize = comp->FindSpecializeModifier()) { if (!comp->IsParam()) getSink()->diagnose(comp->Position, Diagnostics::specializeCanOnlyBeUsedOnParam); if (!compSym->Type->DataType->Equals(ExpressionType::Int) && !compSym->Type->DataType->Equals(ExpressionType::Bool) && !compSym->Type->DataType->Equals(ExpressionType::UInt)) getSink()->diagnose(comp->Position, Diagnostics::specializedParameterMustBeInt); for (auto & val : specialize->Values) { if (!dynamic_cast(val.Ptr())) { getSink()->diagnose(val->Position, Diagnostics::specializationValuesMustBeConstantLiterial); } val->Accept(this); if (!val->Type->Equals(compSym->Type->DataType)) getSink()->diagnose(val->Position, Diagnostics::typeMismatch, val->Type, currentComp->Type); } } for (auto & param : comp->GetParameters()) { param->Accept(this); } if (comp->Expression) { comp->Expression = comp->Expression->Accept(this).As(); if (!MatchType_ValueReceiver(compSym->Type->DataType.Ptr(), comp->Expression->Type.Ptr()) && !comp->Expression->Type->Equals(ExpressionType::Error.Ptr())) getSink()->diagnose(comp->Name, Diagnostics::typeMismatch, comp->Expression->Type, currentComp->Type); } if (comp->BlockStatement) comp->BlockStatement->Accept(this); this->currentComp = nullptr; this->currentCompNode = nullptr; return comp; } virtual RefPtr VisitImportStatement(ImportStatementSyntaxNode * importStmt) override { importStmt->Import->Accept(this); return importStmt; } void AddNewComponentSymbol(EnumerableDictionary> & components, EnumerableDictionary>> & funcComponents, RefPtr comp) { RefPtr compSym; RefPtr compImpl = new ShaderComponentImplSymbol(); if (comp->Rate) for (auto w : comp->Rate->Worlds) compImpl->Worlds.Add(w.World.Content); compImpl->SyntaxNode = comp; if (compImpl->SyntaxNode->Rate) { for (auto & w : compImpl->SyntaxNode->Rate->Worlds) if (w.Pinned) compImpl->SrcPinnedWorlds.Add(w.World.Content); } if (compImpl->SyntaxNode->IsOutput()) { if (compImpl->SyntaxNode->Rate) { for (auto & w : compImpl->SyntaxNode->Rate->Worlds) compImpl->ExportWorlds.Add(w.World.Content); } else { getSink()->diagnose(compImpl->SyntaxNode.Ptr(), Diagnostics::componentMarkedExportMustHaveWorld, compImpl->SyntaxNode->Name); } if (compImpl->SyntaxNode->IsComponentFunction()) getSink()->diagnose(compImpl->SyntaxNode->Name, Diagnostics::componetMarkedExportCannotHaveParameters, compImpl->SyntaxNode->Name); } auto compName = GetFullComponentName(comp.Ptr()); if (!components.TryGetValue(compName, compSym)) { compSym = new ShaderComponentSymbol(); compSym->Type = new Type(); compSym->Name = compName; compSym->Type->DataType = comp->Type; components.Add(compName, compSym); } else { if (comp->IsRequire()) getSink()->diagnose(compImpl->SyntaxNode.Ptr(), Diagnostics::requirementsClashWithPreviousDef, compImpl->SyntaxNode->Name.Content); else { if (!compSym->Type->DataType->Equals(comp->Type.Ptr())) { getSink()->diagnose(comp->Name, Diagnostics::componentOverloadTypeMismatch, comp->Name.Content); getSink()->diagnose(compSym->Implementations.First()->SyntaxNode, Diagnostics::seePreviousDefinition); } } if (compImpl->SyntaxNode->IsComponentFunction()) { getSink()->diagnose(compImpl->SyntaxNode.Ptr(), Diagnostics::functionRedefinition, compImpl->SyntaxNode->Name.Content); getSink()->diagnose(compSym->Implementations.Last()->SyntaxNode, Diagnostics::seePreviousDefinition); } symbolTable->CheckComponentImplementationConsistency(sink, compSym.Ptr(), compImpl.Ptr()); } if (compImpl->SyntaxNode->IsComponentFunction()) { auto list = funcComponents.TryGetValue(comp->Name.Content); if (!list) { funcComponents[comp->Name.Content] = List>(); list = funcComponents.TryGetValue(comp->Name.Content); } comp->Name.Content = compName; list->Add(compSym); } compSym->Implementations.Add(compImpl); } virtual RefPtr VisitProgram(ProgramSyntaxNode * programNode) override { HashSet funcNames; this->program = programNode; this->function = nullptr; for (auto & s : program->Members) { symbolTable->globalDecls.AddIfNotExists(s->Name.Content, s.Ptr()); } for (auto & s : program->GetTypeDefs()) VisitTypeDefDecl(s.Ptr()); for (auto & s : program->GetStructs()) { if (!s->SemanticallyChecked) { VisitStruct(s.Ptr()); s->SemanticallyChecked = true; } } for (auto & func : program->GetFunctions()) { if (!func->SemanticallyChecked) { VisitFunctionDeclaration(func.Ptr()); if (funcNames.Contains(func->InternalName)) { StringBuilder argList; argList << "("; bool first = true; for (auto & param : func->GetParameters()) { if (!first) argList << ", "; argList << param->Type->ToString(); first = false; } argList << ")"; getSink()->diagnose(func, Diagnostics::functionRedefinitionWithArgList, func->Name, argList.ProduceString()); } else funcNames.Add(func->InternalName); } } for (auto & func : program->GetFunctions()) { if (!func->SemanticallyChecked) { func->Accept(this); func->SemanticallyChecked = true; } } for (auto & pipeline : program->GetPipelines()) { VisitPipeline(pipeline.Ptr()); } for (auto & interfaceNode : program->GetInterfaces()) { if (!interfaceNode->SemanticallyChecked) { VisitInterface(interfaceNode.Ptr()); interfaceNode->SemanticallyChecked = true; } } // build initial symbol table for shaders for (auto & shader : program->GetShaders()) { if (!shader->SemanticallyChecked) { RefPtr shaderSym = new ShaderSymbol(); shaderSym->SyntaxNode = shader.Ptr(); if (symbolTable->Shaders.ContainsKey(shader->Name.Content)) { getSink()->diagnose(shader->Name, Diagnostics::shaderAlreadyDefined, shader->Name); } symbolTable->Shaders[shader->Name.Content] = shaderSym; } } for (auto & shader : program->GetShaders()) { if (!shader->SemanticallyChecked) { VisitShaderPass1(shader.Ptr()); shader->SemanticallyChecked = true; } } if (sink->GetErrorCount() != 0) return programNode; // shader dependency is discovered in pass 1, we can now sort the shaders if (!symbolTable->SortShaders()) { HashSet sortedShaders; for (auto & shader : symbolTable->ShaderDependenceOrder) sortedShaders.Add(shader); for (auto & shader : symbolTable->Shaders) if (!sortedShaders.Contains(shader.Value.Ptr())) { getSink()->diagnose(shader.Value->SyntaxNode->Name, Diagnostics::shaderCircularity, shader.Key); } } for (auto & shader : symbolTable->ShaderDependenceOrder) { if (!shader->SemanticallyChecked) { VisitShaderPass2(shader->SyntaxNode.Ptr()); shader->SemanticallyChecked = true; } } return programNode; } virtual RefPtr VisitStruct(StructSyntaxNode * structNode) override { for (auto field : structNode->GetFields()) { field->Type = TranslateTypeNode(field->TypeNode); } return structNode; } virtual RefPtr VisitTypeDefDecl(TypeDefDecl* decl) override { decl->Type = TranslateTypeNode(decl->TypeNode); return decl; } virtual RefPtr VisitFunction(FunctionSyntaxNode *functionNode) override { if (!functionNode->IsExtern()) { currentFunc = symbolTable->Functions.TryGetValue(functionNode->InternalName)->Ptr(); this->function = functionNode; functionNode->Body->Accept(this); this->function = NULL; currentFunc = nullptr; } return functionNode; } void VisitFunctionDeclaration(FunctionSyntaxNode *functionNode) { this->function = functionNode; auto returnType = TranslateTypeNode(functionNode->ReturnTypeNode); functionNode->ReturnType = returnType; StringBuilder internalName; internalName << functionNode->Name.Content; HashSet paraNames; for (auto & para : functionNode->GetParameters()) { if (paraNames.Contains(para->Name.Content)) getSink()->diagnose(para, Diagnostics::parameterAlreadyDefined, para->Name); else paraNames.Add(para->Name.Content); para->Type = TranslateTypeNode(para->TypeNode); if (para->Type->Equals(ExpressionType::Void.Ptr())) getSink()->diagnose(para, Diagnostics::parameterCannotBeVoid); internalName << "@" << para->Type->ToString(); } functionNode->InternalName = internalName.ProduceString(); RefPtr symbol = new FunctionSymbol(); symbol->SyntaxNode = functionNode; symbolTable->Functions[functionNode->InternalName] = symbol; auto overloadList = symbolTable->FunctionOverloads.TryGetValue(functionNode->Name.Content); if (!overloadList) { symbolTable->FunctionOverloads[functionNode->Name.Content] = List>(); overloadList = symbolTable->FunctionOverloads.TryGetValue(functionNode->Name.Content); } overloadList->Add(symbol); this->function = NULL; } virtual RefPtr VisitBlockStatement(BlockStatementSyntaxNode *stmt) override { for (auto & node : stmt->Statements) { node->Accept(this); } return stmt; } virtual RefPtr VisitBreakStatement(BreakStatementSyntaxNode *stmt) override { if (!loops.Count()) getSink()->diagnose(stmt, Diagnostics::breakOutsideLoop); return stmt; } virtual RefPtr VisitContinueStatement(ContinueStatementSyntaxNode *stmt) override { if (!loops.Count()) getSink()->diagnose(stmt, Diagnostics::continueOutsideLoop); return stmt; } virtual RefPtr VisitDoWhileStatement(DoWhileStatementSyntaxNode *stmt) override { loops.Add(stmt); if (stmt->Predicate != NULL) stmt->Predicate = stmt->Predicate->Accept(this).As(); if (!stmt->Predicate->Type->Equals(ExpressionType::Error.Ptr()) && !stmt->Predicate->Type->Equals(ExpressionType::Int.Ptr()) && !stmt->Predicate->Type->Equals(ExpressionType::Bool.Ptr())) { getSink()->diagnose(stmt, Diagnostics::whilePredicateTypeError); } stmt->Statement->Accept(this); loops.RemoveAt(loops.Count() - 1); return stmt; } virtual RefPtr VisitForStatement(ForStatementSyntaxNode *stmt) override { loops.Add(stmt); if (stmt->InitialStatement) { stmt->InitialStatement = stmt->InitialStatement->Accept(this).As(); } if (stmt->PredicateExpression) { stmt->PredicateExpression = stmt->PredicateExpression->Accept(this).As(); if (!stmt->PredicateExpression->Type->Equals(ExpressionType::Bool.Ptr()) && !stmt->PredicateExpression->Type->Equals(ExpressionType::Int.Ptr()) && !stmt->PredicateExpression->Type->Equals(ExpressionType::UInt.Ptr())) { getSink()->diagnose(stmt->PredicateExpression.Ptr(), Diagnostics::forPredicateTypeError); } } if (stmt->SideEffectExpression) { stmt->SideEffectExpression = stmt->SideEffectExpression->Accept(this).As(); } stmt->Statement->Accept(this); loops.RemoveAt(loops.Count() - 1); return stmt; } virtual RefPtr VisitIfStatement(IfStatementSyntaxNode *stmt) override { if (stmt->Predicate != NULL) stmt->Predicate = stmt->Predicate->Accept(this).As(); if (!stmt->Predicate->Type->Equals(ExpressionType::Error.Ptr()) && (!stmt->Predicate->Type->Equals(ExpressionType::Int.Ptr()) && !stmt->Predicate->Type->Equals(ExpressionType::Bool.Ptr()))) getSink()->diagnose(stmt, Diagnostics::ifPredicateTypeError); if (stmt->PositiveStatement != NULL) stmt->PositiveStatement->Accept(this); if (stmt->NegativeStatement != NULL) stmt->NegativeStatement->Accept(this); return stmt; } virtual RefPtr VisitReturnStatement(ReturnStatementSyntaxNode *stmt) override { if (currentCompNode && currentCompNode->BlockStatement->Statements.Count() && stmt != currentCompNode->BlockStatement->Statements.Last().Ptr()) { getSink()->diagnose(stmt, Diagnostics::returnInComponentMustComeLast); } if (!stmt->Expression) { if (function && !function->ReturnType->Equals(ExpressionType::Void.Ptr())) getSink()->diagnose(stmt, Diagnostics::returnNeedsExpression); } else { stmt->Expression = stmt->Expression->Accept(this).As(); if (!stmt->Expression->Type->Equals(ExpressionType::Error.Ptr())) { if (function && !MatchType_ValueReceiver(function->ReturnType.Ptr(), stmt->Expression->Type.Ptr())) getSink()->diagnose(stmt, Diagnostics::functionReturnTypeMismatch, stmt->Expression->Type, function->ReturnType); if (currentComp && !MatchType_ValueReceiver(currentComp->Type->DataType.Ptr(), stmt->Expression->Type.Ptr())) { getSink()->diagnose(stmt, Diagnostics::componentReturnTypeMismatch, stmt->Expression->Type, currentComp->Type->DataType); } if (currentImportOperator && !MatchType_GenericType(currentImportOperator->TypeName.Content, stmt->Expression->Type.Ptr())) getSink()->diagnose(stmt, Diagnostics::importOperatorReturnTypeMismatch, stmt->Expression->Type, currentImportOperator->TypeName); } } return stmt; } virtual RefPtr VisitDeclrVariable(Variable* varDecl) { RefPtr type = TranslateTypeNode(varDecl->TypeNode); if (type->IsTextureOrSampler() || type->AsGenericType()) { getSink()->diagnose(varDecl->TypeNode, Diagnostics::invalidTypeForLocalVariable); } else if (type->AsBasicType() && type->AsBasicType()->RecordTypeName.Length()) { getSink()->diagnose(varDecl->TypeNode, Diagnostics::recordTypeVariableInImportOperator); } varDecl->Type = type; if (varDecl->Type->Equals(ExpressionType::Void.Ptr())) getSink()->diagnose(varDecl, Diagnostics::invalidTypeVoid); if (varDecl->Type->IsArray() && varDecl->Type->AsArrayType()->ArrayLength <= 0) getSink()->diagnose(varDecl, Diagnostics::invalidArraySize); if (varDecl->Expr != NULL) { varDecl->Expr = varDecl->Expr->Accept(this).As(); if (!MatchType_ValueReceiver(varDecl->Type.Ptr(), varDecl->Expr->Type.Ptr()) && !varDecl->Expr->Type->Equals(ExpressionType::Error.Ptr())) { getSink()->diagnose(varDecl, Diagnostics::typeMismatch, varDecl->Expr->Type, varDecl->Type); } } return varDecl; } virtual RefPtr VisitWhileStatement(WhileStatementSyntaxNode *stmt) override { loops.Add(stmt); stmt->Predicate = stmt->Predicate->Accept(this).As(); if (!stmt->Predicate->Type->Equals(ExpressionType::Error.Ptr()) && !stmt->Predicate->Type->Equals(ExpressionType::Int.Ptr()) && !stmt->Predicate->Type->Equals(ExpressionType::Bool.Ptr())) getSink()->diagnose(stmt, Diagnostics::whilePredicateTypeError2); stmt->Statement->Accept(this); loops.RemoveAt(loops.Count() - 1); return stmt; } virtual RefPtr VisitExpressionStatement(ExpressionStatementSyntaxNode *stmt) override { stmt->Expression = stmt->Expression->Accept(this).As(); return stmt; } virtual RefPtr VisitBinaryExpression(BinaryExpressionSyntaxNode *expr) override { expr->LeftExpression = expr->LeftExpression->Accept(this).As(); expr->RightExpression = expr->RightExpression->Accept(this).As(); auto & leftType = expr->LeftExpression->Type; if (!leftType) printf("Break"); auto & rightType = expr->RightExpression->Type; RefPtr matchedType; auto checkAssign = [&]() { if (!(leftType->AsBasicType() && leftType->AsBasicType()->IsLeftValue) && !leftType->Equals(ExpressionType::Error.Ptr())) getSink()->diagnose(expr->LeftExpression.Ptr(), Diagnostics::assignNonLValue); if (expr->Operator == Operator::AndAssign || expr->Operator == Operator::OrAssign || expr->Operator == Operator::XorAssign || expr->Operator == Operator::LshAssign || expr->Operator == Operator::RshAssign) { if (!(leftType->IsIntegral() && rightType->IsIntegral())) { getSink()->diagnose(expr, Diagnostics::bitOperationNonIntegral); } } expr->LeftExpression->Access = ExpressionAccess::Write; if (MatchType_ValueReceiver(leftType.Ptr(), expr->Type.Ptr())) expr->Type = leftType; else expr->Type = ExpressionType::Error; }; if (expr->Operator == Operator::Assign) { expr->Type = rightType; checkAssign(); } else { List> argTypes; argTypes.Add(leftType); argTypes.Add(rightType); List> * operatorOverloads = symbolTable->FunctionOverloads.TryGetValue(GetOperatorFunctionName(expr->Operator)); auto overload = FindFunctionOverload(*operatorOverloads, [](RefPtr f) { return f->SyntaxNode->GetParameters(); }, argTypes); if (!overload) { expr->Type = ExpressionType::Error; if (!leftType->Equals(ExpressionType::Error.Ptr()) && !rightType->Equals(ExpressionType::Error.Ptr())) getSink()->diagnose(expr, Diagnostics::noOverloadFoundForBinOperatorOnTypes, OperatorToString(expr->Operator), leftType, rightType); } else { expr->Type = overload->SyntaxNode->ReturnType; } if (expr->Operator > Operator::Assign) checkAssign(); } return expr; } virtual RefPtr VisitConstantExpression(ConstantExpressionSyntaxNode *expr) override { switch (expr->ConstType) { case ConstantExpressionSyntaxNode::ConstantType::Int: expr->Type = ExpressionType::Int; break; case ConstantExpressionSyntaxNode::ConstantType::UInt: expr->Type = ExpressionType::UInt; break; case ConstantExpressionSyntaxNode::ConstantType::Bool: expr->Type = ExpressionType::Bool; break; case ConstantExpressionSyntaxNode::ConstantType::Float: expr->Type = ExpressionType::Float; break; default: expr->Type = ExpressionType::Error; throw "Invalid constant type."; break; } return expr; } virtual RefPtr VisitIndexExpression(IndexExpressionSyntaxNode *expr) override { expr->BaseExpression = expr->BaseExpression->Accept(this).As(); expr->IndexExpression = expr->IndexExpression->Accept(this).As(); if (expr->BaseExpression->Type->Equals(ExpressionType::Error.Ptr())) expr->Type = ExpressionType::Error; else { auto & baseExprType = expr->BaseExpression->Type; bool isValid = baseExprType->AsGenericType() && (baseExprType->AsGenericType()->GenericTypeName == "StructuredBuffer" || baseExprType->AsGenericType()->GenericTypeName == "RWStructuredBuffer" || baseExprType->AsGenericType()->GenericTypeName == "PackedBuffer"); isValid = isValid || (baseExprType->AsBasicType() && GetVectorSize(baseExprType->AsBasicType()->BaseType) != 0); isValid = isValid || baseExprType->AsArrayType(); if (!isValid) { getSink()->diagnose(expr, Diagnostics::subscriptNonArray); expr->Type = ExpressionType::Error; } if (!expr->IndexExpression->Type->Equals(ExpressionType::Int.Ptr()) && !expr->IndexExpression->Type->Equals(ExpressionType::UInt.Ptr())) { getSink()->diagnose(expr, Diagnostics::subscriptIndexNonInteger); expr->Type = ExpressionType::Error; } } if (expr->BaseExpression->Type->IsArray()) { expr->Type = expr->BaseExpression->Type->AsArrayType()->BaseType; } else if (auto genType = expr->BaseExpression->Type->AsGenericType()) { expr->Type = genType->BaseType; } else if (auto basicType = expr->BaseExpression->Type->AsBasicType()) { if (basicType->BaseType == BaseType::Float3x3) expr->Type = ExpressionType::Float3; else if (basicType->BaseType == BaseType::Float4x4) expr->Type = ExpressionType::Float4; else expr->Type = new BasicExpressionType(GetVectorBaseType(basicType->BaseType)); } expr->Type = expr->Type->Clone(); if (auto basicType = expr->Type->AsBasicType()) { basicType->IsLeftValue = true; basicType->IsReference = true; } return expr; } bool MatchArguments(FunctionSyntaxNode * functionNode, List > &args) { if (functionNode->GetParameters().Count() != args.Count()) return false; int i = 0; for (auto param : functionNode->GetParameters()) { if (!param->Type->Equals(args[i]->Type.Ptr())) return false; i++; } return true; } template PFuncT FindFunctionOverload(const List & funcs, const GetParamFunc & getParam, const List> & arguments) { int bestMatchConversions = 1 << 30; PFuncT func = nullptr; for (auto & f : funcs) { auto params = getParam(f); if (params.Count() == arguments.Count()) { int conversions = 0; bool match = true; int i = 0; for (auto param : params) { auto argType = arguments[i]; auto paramType = param->Type; if (argType->Equals(paramType.Ptr())) { } else if (MatchType_ValueReceiver(paramType.Ptr(), argType.Ptr())) { conversions++; } else { match = false; break; } i++; } if (match && conversions < bestMatchConversions) { func = f; bestMatchConversions = conversions; } } } return func; } ShaderComponentSymbol * ResolveFunctionComponent(ShaderSymbol * shader, String name, const List> & args, bool topLevel = true) { auto list = shader->FunctionComponents.TryGetValue(name); if (list) { auto func = FindFunctionOverload(*list, [](RefPtr & comp) { return comp->Implementations.First()->SyntaxNode->GetParameters(); }, args); if (func) { return func.Ptr(); } } for (auto & module : shader->ShaderUsings) { if (module.IsPublic || topLevel) { auto func = ResolveFunctionComponent(module.Shader, name, args, false); if (func) return func; } } return nullptr; } ShaderComponentSymbol * ResolveFunctionComponent(ShaderSymbol * shader, String name, const List> & args, bool topLevel = true) { return ResolveFunctionComponent(shader, name, From(args).Select([](RefPtr x) {return x->Type; }).ToList(), topLevel); } RefPtr ResolveFunctionOverload(InvokeExpressionSyntaxNode * invoke, MemberExpressionSyntaxNode* memberExpr, List> & arguments) { // TODO(tfoley): Figure out why we even need to do this here... DiagnosticSink::State savedState = sink->saveState(); memberExpr->BaseExpression->Accept(this); sink->restoreState(savedState); if (memberExpr->BaseExpression->Type->IsShader()) { auto basicType = memberExpr->BaseExpression->Type->AsBasicType(); auto func = ResolveFunctionComponent(basicType->Shader, memberExpr->MemberName, arguments); if (func) { auto funcType = new BasicExpressionType(); funcType->BaseType = BaseType::Function; funcType->Component = func; memberExpr->Type = funcType; invoke->Type = func->Implementations.First()->SyntaxNode->Type; return invoke; } else { StringBuilder argList; for (int i = 0; i < arguments.Count(); i++) { argList << arguments[i]->Type->ToString(); if (i != arguments.Count() - 1) argList << ", "; } getSink()->diagnose(invoke, Diagnostics::noApplicationFunction, memberExpr->MemberName, argList.ProduceString()); invoke->Type = ExpressionType::Error; } } else { invoke->Arguments.Insert(0, memberExpr->BaseExpression); auto funcExpr = new VarExpressionSyntaxNode(); funcExpr->Scope = invoke->Scope; funcExpr->Position = invoke->Position; funcExpr->Variable = memberExpr->MemberName; invoke->FunctionExpr = funcExpr; return ResolveFunctionOverload(invoke, funcExpr, invoke->Arguments); } return invoke; } RefPtr ResolveFunctionOverload(InvokeExpressionSyntaxNode * invoke, VarExpressionSyntaxNode* varExpr, List> & arguments) { if (currentShader) { auto func = ResolveFunctionComponent(currentShader, varExpr->Variable, arguments); if (func) { auto funcType = new BasicExpressionType(); funcType->BaseType = BaseType::Function; funcType->Component = func; varExpr->Type = funcType; invoke->Type = func->Implementations.First()->SyntaxNode->Type; return invoke; } } // check if this is an import operator call if (currentShader && currentCompNode && arguments.Count() > 0 && currentShader->ParentPipeline) { if (auto impOpList = currentShader->ParentPipeline->ImportOperators.TryGetValue(varExpr->Variable)) { // component with explicit import operator call must be qualified with explicit rate if (!currentCompNode->Rate) { getSink()->diagnose(varExpr, Diagnostics::importOperatorCalledFromAutoPlacedComponent, currentCompNode->Name); invoke->Type = ExpressionType::Error; return invoke; } // for now we do not support calling import operator from a multi-world component definition if (currentCompNode->Rate->Worlds.Count() > 1) getSink()->diagnose(varExpr, Diagnostics::importOperatorCalledFromMultiWorldComponent); auto validOverloads = From(*impOpList).Where([&](RefPtr imp) { return imp->DestWorld.Content == currentCompNode->Rate->Worlds.First().World.Content; }).ToList(); auto func = FindFunctionOverload(validOverloads, [](RefPtr imp) { return imp->GetParameters(); }, From(arguments).Skip(1).Select([](RefPtr x) {return x->Type; }).ToList()); if (func) { RefPtr importExpr = new ImportExpressionSyntaxNode(); importExpr->Position = varExpr->Position; importExpr->Component = arguments[0]; CloneContext cloneCtx; importExpr->ImportOperatorDef = func->Clone(cloneCtx); importExpr->ImportOperatorDef->Scope->Parent = varExpr->Scope->Parent; importExpr->Type = arguments[0]->Type->Clone(); importExpr->Scope = varExpr->Scope; importExpr->Access = ExpressionAccess::Read; for (int i = 1; i < arguments.Count(); i++) importExpr->Arguments.Add(arguments[i]); return importExpr; } else { StringBuilder argList; for (int i = 1; i < arguments.Count(); i++) { argList << arguments[i]->Type->ToString(); if (i != arguments.Count() - 1) argList << ", "; } getSink()->diagnose(varExpr, Diagnostics::noApplicableImportOperator, varExpr->Variable, currentShader->ParentPipeline->SyntaxNode->Name, currentCompNode->Rate->Worlds.First().World, argList.ProduceString()); invoke->Type = ExpressionType::Error; } return invoke; } } // this is not an import operator call, resolve as function call bool found = false; bool functionNameFound = false; RefPtr func; varExpr->Variable = TranslateHLSLTypeNames(varExpr->Variable); if (varExpr->Variable == "texture" && arguments.Count() > 0 && arguments[0]->Type->IsGenericType("Texture")) { if (arguments.Count() != 2) { invoke->Type = ExpressionType::Error; found = false; } else { if (auto genType = arguments[0]->Type->AsGenericType()) { invoke->Type = genType->BaseType; if (!arguments[1]->Type->Equals(ExpressionType::Float2.Ptr())) { found = false; invoke->Type = ExpressionType::Error; } } else { invoke->Type = ExpressionType::Error; found = false; } } auto funcType = new BasicExpressionType(BaseType::Function); funcType->Func = symbolTable->FunctionOverloads["texture"]().First().Ptr(); varExpr->Type = funcType; } else { // find function overload with implicit argument type conversions auto namePrefix = varExpr->Variable + "@"; List> * functionOverloads = symbolTable->FunctionOverloads.TryGetValue(varExpr->Variable); if (functionOverloads) { func = FindFunctionOverload(*functionOverloads, [](RefPtr f) { return f->SyntaxNode->GetParameters(); }, From(arguments).Select([](RefPtr x) {return x->Type; }).ToList()); functionNameFound = true; } } if (func) { if (!func->SyntaxNode->IsExtern()) { //varExpr->Variable = func->SyntaxNode->InternalName; if (currentFunc) currentFunc->ReferencedFunctions.Add(func->SyntaxNode->InternalName); } invoke->Type = func->SyntaxNode->ReturnType; auto funcType = new BasicExpressionType(); funcType->BaseType = BaseType::Function; funcType->Func = func.Ptr(); varExpr->Type = funcType; found = true; } if (!found) { invoke->Type = ExpressionType::Error; StringBuilder argList; for (int i = 0; i < arguments.Count(); i++) { argList << arguments[i]->Type->ToString(); if (i != arguments.Count() - 1) argList << ", "; } if (functionNameFound) getSink()->diagnose(varExpr, Diagnostics::noApplicationFunction, varExpr->Variable, argList.ProduceString()); else getSink()->diagnose(varExpr, Diagnostics::undefinedIdentifier2, varExpr->Variable); } return invoke; } RefPtr VisitProject(ProjectExpressionSyntaxNode * project) override { if (currentImportOperator == nullptr) { getSink()->diagnose(project, Diagnostics::projectionOutsideImportOperator); return project; } project->BaseExpression->Accept(this); auto baseType = project->BaseExpression->Type->AsBasicType(); if (!baseType || baseType->RecordTypeName != currentImportOperator->SourceWorld.Content) getSink()->diagnose(project, Diagnostics::projectTypeMismatch, currentImportOperator->SourceWorld); auto rsType = new BasicExpressionType(BaseType::Generic); project->Type = rsType; rsType->GenericTypeVar = currentImportOperator->TypeName.Content; return project; } RefPtr ResolveInvoke(InvokeExpressionSyntaxNode * expr) { if (auto varExpr = expr->FunctionExpr.As()) { return ResolveFunctionOverload(expr, varExpr.Ptr(), expr->Arguments); } else if (auto memberExpr = expr->FunctionExpr.As()) { return ResolveFunctionOverload(expr, memberExpr.Ptr(), expr->Arguments); } else { getSink()->diagnose(expr->FunctionExpr, Diagnostics::expectedFunction); expr->Type = ExpressionType::Error; } return expr; } virtual RefPtr VisitInvokeExpression(InvokeExpressionSyntaxNode *expr) override { for (auto & arg : expr->Arguments) arg = arg->Accept(this).As(); auto rs = ResolveInvoke(expr); if (auto invoke = dynamic_cast(rs.Ptr())) { // if this is still an invoke expression, test arguments passed to inout/out parameter are LValues if (auto basicType = dynamic_cast(invoke->FunctionExpr->Type.Ptr())) { List> paramsStorage; List> * params = nullptr; if (basicType->Func) { paramsStorage = basicType->Func->SyntaxNode->GetParameters().ToArray(); params = ¶msStorage; } else if (basicType->Component) { paramsStorage = basicType->Component->Implementations.First()->SyntaxNode->GetParameters().ToArray(); params = ¶msStorage; } if (params) { for (int i = 0; i < (*params).Count(); i++) { if ((*params)[i]->HasModifier(ModifierFlag::Out)) { if (i < expr->Arguments.Count() && expr->Arguments[i]->Type->AsBasicType() && !expr->Arguments[i]->Type->AsBasicType()->IsLeftValue) { getSink()->diagnose(expr->Arguments[i], Diagnostics::argumentExpectedLValue, (*params)[i]->Name); } } } } } } return rs; } String OperatorToString(Operator op) { switch (op) { case Spire::Compiler::Operator::Neg: return "-"; case Spire::Compiler::Operator::Not: return "!"; case Spire::Compiler::Operator::PreInc: return "++"; case Spire::Compiler::Operator::PreDec: return "--"; case Spire::Compiler::Operator::PostInc: return "++"; case Spire::Compiler::Operator::PostDec: return "--"; case Spire::Compiler::Operator::Mul: case Spire::Compiler::Operator::MulAssign: return "*"; case Spire::Compiler::Operator::Div: case Spire::Compiler::Operator::DivAssign: return "/"; case Spire::Compiler::Operator::Mod: case Spire::Compiler::Operator::ModAssign: return "%"; case Spire::Compiler::Operator::Add: case Spire::Compiler::Operator::AddAssign: return "+"; case Spire::Compiler::Operator::Sub: case Spire::Compiler::Operator::SubAssign: return "-"; case Spire::Compiler::Operator::Lsh: case Spire::Compiler::Operator::LshAssign: return "<<"; case Spire::Compiler::Operator::Rsh: case Spire::Compiler::Operator::RshAssign: return ">>"; case Spire::Compiler::Operator::Eql: return "=="; case Spire::Compiler::Operator::Neq: return "!="; case Spire::Compiler::Operator::Greater: return ">"; case Spire::Compiler::Operator::Less: return "<"; case Spire::Compiler::Operator::Geq: return ">="; case Spire::Compiler::Operator::Leq: return "<="; case Spire::Compiler::Operator::BitAnd: case Spire::Compiler::Operator::AndAssign: return "&"; case Spire::Compiler::Operator::BitXor: case Spire::Compiler::Operator::XorAssign: return "^"; case Spire::Compiler::Operator::BitOr: case Spire::Compiler::Operator::OrAssign: return "|"; case Spire::Compiler::Operator::And: return "&&"; case Spire::Compiler::Operator::Or: return "||"; case Spire::Compiler::Operator::Assign: return "="; default: return "ERROR"; } } virtual RefPtr VisitUnaryExpression(UnaryExpressionSyntaxNode *expr) override { expr->Expression = expr->Expression->Accept(this).As(); List> argTypes; argTypes.Add(expr->Expression->Type); List> * operatorOverloads = symbolTable->FunctionOverloads.TryGetValue(GetOperatorFunctionName(expr->Operator)); auto overload = FindFunctionOverload(*operatorOverloads, [](RefPtr f) { return f->SyntaxNode->GetParameters(); }, argTypes); if (!overload) { expr->Type = ExpressionType::Error; if (!expr->Expression->Type->Equals(ExpressionType::Error.Ptr())) getSink()->diagnose(expr, Diagnostics::noApplicationUnaryOperator, OperatorToString(expr->Operator), expr->Expression->Type); } else { expr->Type = overload->SyntaxNode->ReturnType; } return expr; } virtual RefPtr VisitVarExpression(VarExpressionSyntaxNode *expr) override { ShaderUsing shaderObj; expr->Type = ExpressionType::Error; auto decl = expr->Scope->LookUp(expr->Variable); auto varDecl = dynamic_cast(decl); if (varDecl) { expr->Type = varDecl->Type; if (auto basicType = expr->Type->AsBasicType()) basicType->IsLeftValue = !(dynamic_cast(varDecl)); } else if (currentShader && currentShader->ShaderObjects.TryGetValue(expr->Variable, shaderObj)) { auto basicType = new BasicExpressionType(BaseType::Shader); basicType->Shader = shaderObj.Shader; basicType->IsLeftValue = false; expr->Type = basicType; } else if (currentPipeline && currentImportOperator) { RefPtr comp; if (currentPipeline->Components.TryGetValue(expr->Variable, comp)) { currentImportOperator->Usings.Add(comp->Name); expr->Type = comp->Type->DataType; } else getSink()->diagnose(expr, Diagnostics::undefinedIdentifier2, expr->Variable); } else if (currentShader) { auto compRef = currentShader->ResolveComponentReference(expr->Variable); if (compRef.IsAccessible) { expr->Type = compRef.Component->Type->DataType->Clone(); if (auto basicType = expr->Type->AsBasicType()) basicType->IsLeftValue = false; } else if (compRef.Component) { getSink()->diagnose(expr, Diagnostics::componentNotAccessibleFromShader, expr->Variable, currentShader->SyntaxNode->Name); } else getSink()->diagnose(expr, Diagnostics::undefinedIdentifier2, expr->Variable); } else if (auto compDecl = dynamic_cast(decl)) // interface decl { expr->Type = compDecl->Type; if (auto basicType = expr->Type->AsBasicType()) basicType->IsLeftValue = false; } else getSink()->diagnose(expr, Diagnostics::undefinedIdentifier2, expr->Variable); if (expr->Type->IsGenericType("Uniform") || expr->Type->IsGenericType("Patch") || expr->Type->IsGenericType("StorageBuffer")) expr->Type = expr->Type->AsGenericType()->BaseType; return expr; } virtual RefPtr VisitTypeCastExpression(TypeCastExpressionSyntaxNode * expr) override { expr->Expression = expr->Expression->Accept(this).As(); auto targetType = TranslateTypeNode(expr->TargetType); if (!expr->Expression->Type->Equals(ExpressionType::Error.Ptr()) && targetType->AsBasicType()) { if (!expr->Expression->Type->AsBasicType()) expr->Type = ExpressionType::Error; else if (!IsNumeric(GetVectorBaseType(expr->Expression->Type->AsBasicType()->BaseType)) || !IsNumeric(GetVectorBaseType(targetType->AsBasicType()->BaseType))) expr->Type = ExpressionType::Error; else if (targetType->AsBasicType()->BaseType == BaseType::Void || expr->Expression->Type->AsBasicType()->BaseType == BaseType::Void) expr->Type = ExpressionType::Error; else expr->Type = targetType; } else expr->Type = ExpressionType::Error; if (expr->Type->Equals(ExpressionType::Error.Ptr()) && !expr->Expression->Type->Equals(ExpressionType::Error.Ptr())) { getSink()->diagnose(expr, Diagnostics::invalidTypeCast, expr->Expression->Type, targetType->ToString()); } return expr; } virtual RefPtr VisitSelectExpression(SelectExpressionSyntaxNode * expr) override { expr->SelectorExpr = expr->SelectorExpr->Accept(this).As(); if (!expr->SelectorExpr->Type->Equals(ExpressionType::Int.Ptr()) && !expr->SelectorExpr->Type->Equals(ExpressionType::Bool.Ptr()) && !expr->SelectorExpr->Type->Equals(ExpressionType::Error.Ptr())) { expr->Type = ExpressionType::Error; getSink()->diagnose(expr, Diagnostics::selectPrdicateTypeMismatch); } expr->Expr0 = expr->Expr0->Accept(this).As(); expr->Expr1 = expr->Expr1->Accept(this).As(); if (!expr->Expr0->Type->Equals(expr->Expr1->Type.Ptr())) { getSink()->diagnose(expr, Diagnostics::selectValuesTypeMismatch); } expr->Type = expr->Expr0->Type; return expr; } virtual RefPtr VisitMemberExpression(MemberExpressionSyntaxNode * expr) override { expr->BaseExpression = expr->BaseExpression->Accept(this).As(); auto & baseType = expr->BaseExpression->Type; if (!baseType->AsBasicType()) expr->Type = ExpressionType::Error; else if (IsVector(baseType->AsBasicType()->BaseType)) { Array children; if (expr->MemberName.Length() > 4) expr->Type = ExpressionType::Error; else { bool error = false; for (int i = 0; i < expr->MemberName.Length(); i++) { auto ch = expr->MemberName[i]; switch (ch) { case 'x': case 'r': children.Add(0); break; case 'y': case 'g': children.Add(1); break; case 'z': case 'b': children.Add(2); break; case 'w': case 'a': children.Add(3); break; default: error = true; expr->Type = ExpressionType::Error; break; } } int vecLen = GetVectorSize(baseType->AsBasicType()->BaseType); for (auto m : children) { if (m >= vecLen) { error = true; expr->Type = ExpressionType::Error; break; } } if ((vecLen == 9 || vecLen == 16) && children.Count() > 1) { error = true; expr->Type = ExpressionType::Error; } if (!error) { if (vecLen == 9) expr->Type = new BasicExpressionType((BaseType)((int)GetVectorBaseType(baseType->AsBasicType()->BaseType) + 2)); else if (vecLen == 16) expr->Type = new BasicExpressionType((BaseType)((int)GetVectorBaseType(baseType->AsBasicType()->BaseType) + 3)); else { expr->Type = new BasicExpressionType((BaseType)((int)GetVectorBaseType(baseType->AsBasicType()->BaseType) + children.Count() - 1)); } expr->Type->AsBasicType()->IsMaskedVector = true; } if (auto bt = expr->Type->AsBasicType()) { bt->IsLeftValue = !baseType->AsBasicType()->IsMaskedVector; if (children.Count() > vecLen || children.Count() == 0) bt->IsLeftValue = false; int curMax = children[0]; for (int i = 0; i < children.Count(); i++) if (children[i] < curMax) { bt->IsLeftValue = false; curMax = children[i]; } } } } else if (baseType->AsBasicType()->BaseType == BaseType::Shader) { ShaderUsing shaderObj; auto refComp = baseType->AsBasicType()->Shader->ResolveComponentReference(expr->MemberName); if (refComp.IsAccessible) expr->Type = refComp.Component->Type->DataType; else if (baseType->AsBasicType()->Shader->ShaderObjects.TryGetValue(expr->MemberName, shaderObj)) { if (shaderObj.IsPublic) { auto shaderType = new BasicExpressionType(BaseType::Shader); shaderType->Shader = shaderObj.Shader; expr->Type = shaderType; } else expr->Type = ExpressionType::Error; } else expr->Type = ExpressionType::Error; } else if (baseType->IsStruct()) { StructField* field = baseType->AsBasicType()->structDecl->FindField(expr->MemberName); if (!field) { expr->Type = ExpressionType::Error; getSink()->diagnose(expr, Diagnostics::noMemberOfNameInType, expr->MemberName, baseType->AsBasicType()->structDecl); } else expr->Type = field->Type; if (auto bt = expr->Type->AsBasicType()) { bt->IsLeftValue = baseType->AsBasicType()->IsLeftValue; } } else expr->Type = ExpressionType::Error; if (!baseType->Equals(ExpressionType::Error.Ptr()) && expr->Type->Equals(ExpressionType::Error.Ptr())) { getSink()->diagnose(expr, Diagnostics::typeHasNoPublicMemberOfName, baseType, expr->MemberName); } return expr; } SemanticsVisitor & operator = (const SemanticsVisitor &) = delete; }; SyntaxVisitor * CreateSemanticsVisitor(SymbolTable * symbols, DiagnosticSink * err) { return new SemanticsVisitor(symbols, err); } } } ================================================ FILE: Source/SpireCore/ShaderCompiler.cpp ================================================ // Compiler.cpp : Defines the entry point for the console application. // #include "../CoreLib/Basic.h" #include "../CoreLib/LibIO.h" #include "ShaderCompiler.h" #include "Lexer.h" #include "Parser.h" #include "Preprocessor.h" #include "SyntaxVisitors.h" #include "StdInclude.h" #include "Schedule.h" #include "CodeGenBackend.h" #include "../CoreLib/Tokenizer.h" #include "Closure.h" #include "VariantIR.h" #include "Naming.h" #ifdef CreateDirectory #undef CreateDirectory #endif using namespace CoreLib::Basic; using namespace CoreLib::IO; using namespace Spire::Compiler; namespace Spire { namespace Compiler { int compilerInstances = 0; class ShaderCompilerImpl : public ShaderCompiler { private: Dictionary> backends; void ResolveAttributes(SymbolTable * symTable) { for (auto & shader : symTable->ShaderDependenceOrder) { auto comps = shader->GetComponentDependencyOrder(); for (auto & comp : comps) { for (auto & impl : comp->Implementations) for (auto attrib : impl->SyntaxNode->GetLayoutAttributes()) { try { if (attrib->GetValue().StartsWith("%")) { CoreLib::Text::TokenReader parser(attrib->GetValue().SubString(1, attrib->GetValue().Length() - 1)); auto compName = parser.ReadWord(); parser.Read("."); auto compAttrib = parser.ReadWord(); RefPtr compSym; if (shader->Components.TryGetValue(compName, compSym)) { for (auto & timpl : compSym->Implementations) { Token attribValue; if (timpl->SyntaxNode->FindSimpleAttribute(compAttrib, attribValue)) attrib->Value = attribValue; } } } } catch (Exception) { } } } } } /* Generate a shader variant by applying mechanic choice rules and the choice file. The choice file provides "preferred" definitions, as represented in ShaderComponentSymbol::Type::PinnedWorlds The process resolves the component references by picking a pinned definition if one is available, or a definition with the preferred import path as defined by import operator ordering. After all references are resolved, all unreferenced definitions (dead code) are eliminated, resulting a shader variant ready for code generation. */ RefPtr GenerateShaderVariantIR(CompileResult & cresult, ShaderClosure * shader, Schedule & schedule, SymbolTable * symbolTable) { RefPtr result = new ShaderIR(); result->Shader = shader; result->SymbolTable = symbolTable; // mark pinned worlds for (auto & comp : shader->Components) { for (auto & impl : comp.Value->Implementations) { for (auto & w : impl->Worlds) { if (impl->SrcPinnedWorlds.Contains(w) || impl->SyntaxNode->IsInline() || impl->ExportWorlds.Contains(w) || impl->SyntaxNode->IsInput()) { comp.Value->Type->PinnedWorlds.Add(w); } } } } // apply choices Dictionary choiceComps; for (auto & comp : shader->AllComponents) { for (auto & choiceName : comp.Value.Symbol->ChoiceNames) choiceComps[choiceName] = comp.Value.Symbol; } HashSet pinnedImpl; for (auto & choice : schedule.Choices) { ShaderComponentSymbol * comp = nullptr; if (choiceComps.TryGetValue(choice.Key, comp)) { comp->Type->PinnedWorlds.Clear(); for (auto & selectedDef : choice.Value) { if (comp->Type->ConstrainedWorlds.Contains(selectedDef->WorldName)) { comp->Type->PinnedWorlds.Add(selectedDef->WorldName); // find specified impl for (auto & impl : comp->Implementations) { if (impl->Worlds.Contains(selectedDef->WorldName)) pinnedImpl.Add(impl.Ptr()); } } else { cresult.GetErrorWriter()->diagnose(selectedDef.Ptr()->Position, Diagnostics::worldIsNotAValidChoiceForKey, selectedDef->WorldName, choice.Key); } } } } for (auto & attribs : schedule.AddtionalAttributes) { ShaderComponentSymbol * comp = nullptr; if (choiceComps.TryGetValue(attribs.Key, comp)) { // apply attributes for (auto & impl : comp->Implementations) { for (auto & attrib : attribs.Value) { auto modifier = new SimpleAttribute(); modifier->Key = attrib.Key; modifier->Value.Content = attrib.Value; modifier->next = impl->SyntaxNode->modifiers.first; impl->SyntaxNode->modifiers.first = modifier; } } } } // generate definitions EnumerableDictionary moduleInstanceMap; auto createModuleInstance = [&](ShaderClosure * closure) { ModuleInstanceIR * inst; if (moduleInstanceMap.TryGetValue(closure, inst)) return inst; List namePath; auto parent = closure; while (parent) { if (parent->Name.Length()) namePath.Add(parent->Name); else namePath.Add(parent->ModuleSyntaxNode->Name.Content); parent = parent->Parent; } StringBuilder sbBindingName; for (int i = namePath.Count() - 2; i >= 0; i--) { sbBindingName << namePath[i]; if (i > 0) sbBindingName << "."; } // if this is the root module, its binding name is module name. if (namePath.Count() == 1) sbBindingName << namePath[0]; inst = new ModuleInstanceIR(); inst->IsTopLevel = namePath.Count() <= 2; inst->SyntaxNode = closure->ModuleSyntaxNode; inst->BindingIndex = closure->BindingIndex; inst->UsingPosition = closure->UsingPosition; result->ModuleInstances.Add(inst); inst->BindingName = sbBindingName.ProduceString(); moduleInstanceMap[closure] = inst; return inst; }; for (auto & comp : shader->AllComponents) { EnumerableDictionary defs; Dictionary impls; for (auto & impl : comp.Value.Symbol->Implementations) { auto createComponentDef = [&](const String & w) { RefPtr def = new ComponentDefinitionIR(); def->OriginalName = comp.Value.Symbol->Name; def->UniqueKey = comp.Value.Symbol->UniqueKey; def->UniqueName = comp.Value.Symbol->UniqueName; def->Type = comp.Value.Symbol->Type->DataType; def->IsEntryPoint = (impl->ExportWorlds.Contains(w) || impl->SyntaxNode->IsParam() || (shader->Pipeline->IsAbstractWorld(w) && (impl->SyntaxNode->HasSimpleAttribute("Pinned") || shader->Pipeline->Worlds[w]()->HasSimpleAttribute("Pinned")))); CloneContext cloneCtx; def->SyntaxNode = impl->SyntaxNode->Clone(cloneCtx); def->World = w; def->ModuleInstance = createModuleInstance(comp.Value.Closure); return def; }; // parameter component will only have one defintion that is shared by all worlds if (impl->SyntaxNode->IsParam()) { auto def = createComponentDef(""); result->Definitions.Add(def); defs[""] = def.Ptr(); } else { for (auto & w : impl->Worlds) { auto def = createComponentDef(w); result->Definitions.Add(def); bool existingDefIsPinned = false; if (defs.ContainsKey(w)) existingDefIsPinned = pinnedImpl.Contains(impls[w]()); if (!existingDefIsPinned) { defs[w] = def.Ptr(); impls[w] = impl.Ptr(); } } } } result->DefinitionsByComponent[comp.Key] = defs; } // now that all module instances are generated, sort out sub module instances for (auto & module : moduleInstanceMap) { auto closure = module.Key; // find first parent of this module, and add the module to the parent's children list while (closure->Parent) { closure = closure->Parent; ModuleInstanceIR * parentModule; if (closure->Parent && moduleInstanceMap.TryGetValue(closure, parentModule)) { parentModule->SubModuleInstances.Add(module.Value); break; } } } bool changed = true; while (changed) { changed = false; result->ResolveComponentReference(); result->EliminateDeadCode(); // check circular references for (auto & def : result->Definitions) { if (def->Dependency.Contains(def.Ptr())) { cresult.GetErrorWriter()->diagnose(def->SyntaxNode->Position, Diagnostics::componentDefinitionCircularity, def->OriginalName); return nullptr; } } /* // eliminate redundant (downstream) definitions, one at a time auto comps = result->GetComponentDependencyOrder(); for (int i = comps.Count() - 1; i >= 0; i--) { auto comp = comps[i]; auto & defs = result->DefinitionsByComponent[comp->UniqueName](); EnumerableHashSet removedDefs; for (auto & def : defs) if (!def.Value->IsEntryPoint && !comp->Type->PinnedWorlds.Contains(def.Value->World)) { for (auto & otherDef : defs) { if (otherDef.Value != def.Value && !removedDefs.Contains(otherDef.Value) && shader->Pipeline->IsWorldReachable(otherDef.Value->World, def.Value->World)) { removedDefs.Add(def.Value); break; } } } if (removedDefs.Count()) { result->RemoveDefinitions([&](ComponentDefinitionIR* def) {return removedDefs.Contains(def); }); changed = true; } } */ } return result; } ShaderSyntaxNode * InstantiateShaderTemplate(DiagnosticSink* sink, SymbolTable* symTable, TemplateShaderSyntaxNode* ts, const List& args) { if (ts->Parameters.Count() > args.Count()) { sink->diagnose(ts->Position, Diagnostics::insufficientTemplateShaderArguments, ts->Name); return nullptr; } if (ts->Parameters.Count() < args.Count()) { sink->diagnose(ts->Position, Diagnostics::tooManyTemplateShaderArguments, ts->Name); return nullptr; } // check semantics bool hasErrors = false; for (int i = 0; i < args.Count(); i++) { auto name = args[i]; if (auto module = symTable->Shaders.TryGetValue(name)) { if (ts->Parameters[i]->InterfaceName.Content.Length()) { if ((*module)->SyntaxNode->Name.Content != ts->Parameters[i]->InterfaceName.Content && (*module)->SyntaxNode->InterfaceNames.FindFirst([&](const Token & t) { return t.Content == ts->Parameters[i]->InterfaceName.Content; }) == -1) { hasErrors = true; sink->diagnose(ts->Parameters[i]->Position, Diagnostics::templateShaderArgumentDidNotImplementRequiredInterface, name, ts->Parameters[i]->ModuleName, ts->Parameters[i]->InterfaceName); sink->diagnose((*module)->SyntaxNode->Position, Diagnostics::seeDefinitionOf, (*module)->SyntaxNode->Name); } } } else { hasErrors = true; sink->diagnose(ts->Parameters[i]->Position, Diagnostics::templateShaderArgumentIsNotDefined, args[i], ts->Parameters[i]->ModuleName.Content); } } if (hasErrors) return nullptr; ShaderSyntaxNode * result = new ShaderSyntaxNode(); result->Name = ts->Name; StringBuilder nameBuilder; nameBuilder << ts->Name.Content << "<"; for (int i = 0; i < args.Count(); i++) { nameBuilder << args[i]; if (i < args.Count()-1) nameBuilder << ","; } nameBuilder << ">"; result->Name.Content = nameBuilder.ProduceString(); result->ParentPipelineName = ts->ParentPipelineName; for (auto & member : ts->Members) { if (auto import = member.As()) { int index = ts->Parameters.FindFirst([&](RefPtr p) { return p->ModuleName.Content == import->ShaderName.Content; }); if (index != -1) { CloneContext cloneCtx; auto newImport = import->Clone(cloneCtx); auto attribModifier = new SimpleAttribute(); attribModifier->Key = "Binding"; attribModifier->Value.Content = String(index); newImport->modifiers.first = attribModifier; newImport->Scope->Parent = result->Scope; newImport->ShaderName.Content = args[index]; result->Members.Add(newImport); continue; } } result->Members.Add(member); } result->IsModule = false; return result; } public: virtual CompileUnit Parse(CompileResult & result, String source, String fileName, IncludeHandler* includeHandler, Dictionary const& preprocesorDefinitions) override { auto tokens = PreprocessSource(source, fileName, result.GetErrorWriter(), includeHandler, preprocesorDefinitions); CompileUnit rs; rs.SyntaxNode = ParseProgram(tokens, result.GetErrorWriter(), fileName); return rs; } virtual void Compile(CompileResult & result, CompilationContext & context, List & units, const CompileOptions & options) override { RefPtr programSyntaxNode = new ProgramSyntaxNode(); for (auto & unit : units) { programSyntaxNode->Include(unit.SyntaxNode.Ptr()); } SymbolTable & symTable = context.Symbols; auto & shaderClosures = context.ShaderClosures; RefPtr visitor = CreateSemanticsVisitor(&symTable, result.GetErrorWriter()); try { programSyntaxNode->Accept(visitor.Ptr()); if (result.GetErrorCount() > 0) return; // if user specified a template shader symbol, instantiate the template now String symbolToCompile = options.SymbolToCompile; if (symbolToCompile.Length()) { auto templateShaders = programSyntaxNode->GetMembersOfType(); for (auto & ts : templateShaders) if (ts->Name.Content == symbolToCompile) { auto shader = InstantiateShaderTemplate(result.GetErrorWriter(), &symTable, ts.Ptr(), options.TemplateShaderArguments); if (shader) { programSyntaxNode->Members.Add(shader); symbolToCompile = shader->Name.Content; programSyntaxNode->Accept(visitor.Ptr()); } break; } } visitor = nullptr; symTable.EvalFunctionReferenceClosure(); if (result.GetErrorCount() > 0) return; for (auto & shader : symTable.ShaderDependenceOrder) { if (shader->IsAbstract) continue; if (!shaderClosures.ContainsKey(shader->SyntaxNode->Name.Content)) { auto shaderClosure = CreateShaderClosure(result.GetErrorWriter(), &symTable, shader); FlattenShaderClosure(result.GetErrorWriter(), &symTable, shaderClosure.Ptr()); shaderClosures.Add(shader->SyntaxNode->Name.Content, shaderClosure); } } ResolveAttributes(&symTable); if (result.GetErrorCount() > 0) return; CodeGenBackend * backend = nullptr; switch(options.Target) { case CodeGenTarget::SPIRV: backend = backends["spirv"]().Ptr(); break; case CodeGenTarget::GLSL: backend = backends["glsl"]().Ptr(); break; case CodeGenTarget::GLSL_Vulkan: backend = backends["glsl_vk"]().Ptr(); break; case CodeGenTarget::GLSL_Vulkan_OneDesc: backend = backends["glsl_vk_onedesc"]().Ptr(); break; case CodeGenTarget::HLSL: backend = backends["hlsl"]().Ptr(); break; default: // TODO: emit an appropriate diagnostic return; } Schedule schedule; if (options.ScheduleSource != "") { schedule = Schedule::Parse(options.ScheduleSource, options.ScheduleFileName, result.GetErrorWriter()); } for (auto shader : shaderClosures) { // generate shader variant from schedule file, and also apply mechanic deduction rules if (!shader.Value->IR) shader.Value->IR = GenerateShaderVariantIR(result, shader.Value.Ptr(), schedule, &symTable); } if (options.Mode == CompilerMode::ProduceShader) { if (result.GetErrorWriter()->GetErrorCount() > 0) return; // generate IL code RefPtr codeGen = CreateCodeGenerator(&symTable, result, backend); if (context.Program) { result.Program->Functions = context.Program->Functions; result.Program->Shaders = context.Program->Shaders; result.Program->Structs = context.Program->Structs; result.Program->ConstantPool = context.Program->ConstantPool; } for (auto & s : programSyntaxNode->GetStructs()) codeGen->ProcessStruct(s.Ptr()); for (auto & func : programSyntaxNode->GetFunctions()) codeGen->ProcessFunction(func.Ptr()); for (auto & shader : shaderClosures) { InsertImplicitImportOperators(result.GetErrorWriter(), shader.Value->IR.Ptr()); } if (result.GetErrorCount() > 0) return; for (auto & shader : shaderClosures) { codeGen->ProcessShader(shader.Value->IR.Ptr()); } if (result.GetErrorCount() > 0) return; // emit target code EnumerableHashSet symbolsToGen; for (auto & unit : units) { for (auto & shader : unit.SyntaxNode->GetShaders()) if (!shader->IsModule) symbolsToGen.Add(shader->Name.Content); for (auto & func : unit.SyntaxNode->GetFunctions()) symbolsToGen.Add(func->Name.Content); } auto IsSymbolToGen = [&](String & shaderName) { if (symbolsToGen.Contains(shaderName)) return true; for (auto & symbol : symbolsToGen) if (shaderName.StartsWith(symbol)) return true; return false; }; for (auto & shader : result.Program->Shaders) { if ((symbolToCompile.Length() == 0 && IsSymbolToGen(shader->Name)) || EscapeCodeName(symbolToCompile) == shader->Name) { StringBuilder glslBuilder; Dictionary targetCode; result.CompiledSource[shader->Name] = backend->GenerateShader(result, &symTable, shader.Ptr(), result.GetErrorWriter()); } } } else if (options.Mode == CompilerMode::GenerateChoice) { for (auto shader : shaderClosures) { if (options.SymbolToCompile.Length() == 0 || shader.Value->Name == options.SymbolToCompile) { auto &worldOrder = shader.Value->Pipeline->GetWorldTopologyOrder(); for (auto & comp : shader.Value->AllComponents) { ShaderChoice choice; if (comp.Value.Symbol->ChoiceNames.Count() == 0) continue; if (comp.Value.Symbol->IsRequire()) continue; choice.ChoiceName = comp.Value.Symbol->ChoiceNames.First(); for (auto & impl : comp.Value.Symbol->Implementations) { for (auto w : impl->Worlds) if (comp.Value.Symbol->Type->ConstrainedWorlds.Contains(w)) choice.Options.Add(ShaderChoiceValue(w)); } if (auto defs = shader.Value->IR->DefinitionsByComponent.TryGetValue(comp.Key)) { int latestWorldOrder = -1; for (auto & def : *defs) { int order = worldOrder.IndexOf(def.Key); if (latestWorldOrder < order) { choice.DefaultValue = def.Key; latestWorldOrder = order; } } } result.Choices.Add(choice); } } } } else { result.GetErrorWriter()->diagnose(CodePosition(), Diagnostics::unsupportedCompilerMode); return; } context.Program = result.Program; } catch (int) { } catch (...) { throw; } return; } ShaderCompilerImpl() { if (compilerInstances == 0) { BasicExpressionType::Init(); } compilerInstances++; backends.Add("glsl", CreateGLSLCodeGen()); backends.Add("hlsl", CreateHLSLCodeGen()); backends.Add("spirv", CreateSpirVCodeGen()); backends.Add("glsl_vk", CreateGLSL_VulkanCodeGen()); backends.Add("glsl_vk_onedesc", CreateGLSL_VulkanOneDescCodeGen()); } ~ShaderCompilerImpl() { compilerInstances--; if (compilerInstances == 0) { BasicExpressionType::Finalize(); SpireStdLib::Finalize(); } } }; ShaderCompiler * CreateShaderCompiler() { return new ShaderCompilerImpl(); } void CompilationContext::MergeWith(CompilationContext * ctx) { Symbols.MergeWith(ctx->Symbols); if (ctx->Program != Program) { for (auto & f : ctx->Program->Functions) Program->Functions[f.Key] = f.Value; HashSet existingStructs; for (auto & s : Program->Structs) existingStructs.Add(s.Ptr()); for (auto & s : ctx->Program->Structs) if (existingStructs.Add(s.Ptr())) Program->Structs.Add(s); HashSet existingShaders; for (auto & s : Program->Shaders) existingShaders.Add(s.Ptr()); for (auto & s : ctx->Program->Shaders) if (existingShaders.Add(s.Ptr())) Program->Shaders.Add(s); } for (auto & s : ctx->ShaderClosures) ShaderClosures[s.Key] = s.Value; } } } ================================================ FILE: Source/SpireCore/ShaderCompiler.h ================================================ #ifndef RASTER_SHADER_COMPILER_H #define RASTER_SHADER_COMPILER_H #include "../CoreLib/Basic.h" #include "Diagnostics.h" #include "CompiledProgram.h" #include "Syntax.h" #include "CodeGenBackend.h" namespace Spire { namespace Compiler { class ILConstOperand; struct IncludeHandler; enum class CompilerMode { ProduceLibrary, ProduceShader, GenerateChoice }; enum class CodeGenTarget { GLSL, GLSL_Vulkan, GLSL_Vulkan_OneDesc, HLSL, SPIRV }; class CompileOptions { public: CompilerMode Mode = CompilerMode::ProduceShader; CodeGenTarget Target = CodeGenTarget::GLSL; EnumerableDictionary BackendArguments; String ScheduleSource, ScheduleFileName; String SymbolToCompile; List TemplateShaderArguments; List SearchDirectories; Dictionary PreprocessorDefinitions; }; class CompileUnit { public: RefPtr SyntaxNode; }; class CompilationContext : public CoreLib::Basic::RefObject { public: SymbolTable Symbols; EnumerableDictionary> ShaderClosures; RefPtr Program; void MergeWith(CompilationContext * ctx); }; class ShaderCompiler : public CoreLib::Basic::Object { public: virtual CompileUnit Parse(CompileResult & result, String source, String fileName, IncludeHandler* includeHandler, Dictionary const& preprocessorDefinitions) = 0; virtual void Compile(CompileResult & result, CompilationContext & context, List & units, const CompileOptions & options) = 0; void Compile(CompileResult & result, List & units, const CompileOptions & options) { CompilationContext context; Compile(result, context, units, options); } }; ShaderCompiler * CreateShaderCompiler(); } } #endif ================================================ FILE: Source/SpireCore/SpirVCodeGen.cpp ================================================ #if 0 #include "CodeGenBackend.h" #include "../CoreLib/Tokenizer.h" #include "IL.h" #include "Syntax.h" #include #include #include "../CoreLib/TextIO.h" #include "../CoreLib/LibIO.h" using namespace CoreLib::Basic; namespace Spire { namespace Compiler { enum class ExecutionModel { Invalid = 777, Vertex = 0, TessellationControl = 1, TessellationEvaluation = 2, Geometry = 3, Fragment = 4, GLCompute = 5, Kernel = 6 }; String ExecutionModelToString(ExecutionModel em) { switch (em) { case ExecutionModel::Invalid: return "invalid"; case ExecutionModel::Vertex: return "Vertex"; case ExecutionModel::TessellationControl: return "TessellationContro"; case ExecutionModel::TessellationEvaluation: return "TessellationEvaluation"; case ExecutionModel::Geometry: return "Geometry"; case ExecutionModel::Fragment: return "Fragment"; case ExecutionModel::GLCompute: return "GLCompute"; case ExecutionModel::Kernel: return "Kerne"; default: throw NotImplementedException("unknown ExecutionMode"); } } enum class ExecutionMode { Invalid = 777, Invocations = 0, PixelCenterInteger = 6, OriginUpperLeft = 7, OriginLowerLeft = 8, EarlyFragmentTests = 9, DepthReplacing = 12, DepthGreater = 14, DepthLess = 15, DepthUnchanged = 16, LocalSize = 17 }; String ExecutionModeToString(ExecutionMode em) { switch (em) { case ExecutionMode::Invalid: return "invalid"; case ExecutionMode::Invocations: return "Invocations"; case ExecutionMode::PixelCenterInteger: return "PixelCenterInteger"; case ExecutionMode::OriginUpperLeft: return "OriginUpperLeft"; case ExecutionMode::OriginLowerLeft: return "OriginLowerLeft"; case ExecutionMode::EarlyFragmentTests: return "EarlyFragmentTests"; case ExecutionMode::DepthReplacing: return "DepthReplacing"; case ExecutionMode::DepthGreater: return "DepthGreater"; case ExecutionMode::DepthLess: return "DepthLess"; case ExecutionMode::DepthUnchanged: return "DepthUnchanged"; case ExecutionMode::LocalSize: return "LocalSize"; default: throw NotImplementedException("unknown ExecutionMode"); } } enum class StorageClass { Invalid = 777, UniformConstant = 0, Input = 1, Uniform = 2, Output = 3, Workgroup = 4, CrossWorkGroup = 5, Private = 6, Function = 7, Generic = 8, PushConstant = 9, AtomicCounter = 10, Image = 11 }; String StorageClassToString(StorageClass store) { switch (store) { case StorageClass::UniformConstant: return "UniformConstant"; case StorageClass::Input: return "Input"; case StorageClass::Uniform: return "Uniform"; case StorageClass::Output: return "Output"; case StorageClass::Workgroup: return "Workgroup"; case StorageClass::CrossWorkGroup: return "CrossWorkGroup"; case StorageClass::Private: return "Private"; case StorageClass::Function: return "Function"; case StorageClass::Generic: return "Generic"; case StorageClass::PushConstant: return "PushConstant"; case StorageClass::AtomicCounter: return "AtomicCounter"; case StorageClass::Image: return "Image"; default: throw NotImplementedException("Unknown StorageClass: "); } } enum class MemoryAccess { None = 0, //0x0 Volatile = 1, //0x1 Aligned = 2, //0x2 Nontemporal = 4 //0x4 }; String MemoryAccessToString(MemoryAccess ma) { switch (ma) { case MemoryAccess::None: return "None"; case MemoryAccess::Volatile: return "Volatile"; case MemoryAccess::Aligned: return "Aligned"; case MemoryAccess::Nontemporal: return "Nontempora"; default: throw NotImplementedException("Unknown MemoryAccess"); } } enum class Decoration { Invalid = 777, Block = 2, BufferBlock = 3, RowMajor = 4, ColMajor = 5, ArrayStride = 6, MatrixStride = 7, BuiltIn = 11, Flat = 14, Constant = 22, Location = 30, Component = 31, Index = 32, Binding = 33, DescriptorSet = 34, Offset = 35 }; String DecorationToString(Decoration d) { switch (d) { case Decoration::Invalid: return "invalid"; case Decoration::Block: return "Block"; case Decoration::BufferBlock: return "BufferBlock"; case Decoration::RowMajor: return "RowMajor"; case Decoration::ColMajor: return "ColMajor"; case Decoration::ArrayStride: return "ArrayStride"; case Decoration::MatrixStride: return "MatrixStride"; case Decoration::BuiltIn: return "BuiltIn"; case Decoration::Flat: return "Flat"; case Decoration::Constant: return "Constant"; case Decoration::Location: return "Location"; case Decoration::Component: return "Component"; case Decoration::Index: return "Index"; case Decoration::Binding: return "Binding"; case Decoration::DescriptorSet: return "DescriptorSet"; case Decoration::Offset: return "Offset"; default: throw NotImplementedException("unknown Decoration"); } } enum class BuiltIn { Invalid = 777, Position = 0, PointSize = 1, ClipDistance = 3, CullDistance = 4, FragDepth = 22, WorkgroupSize = 25, GlobalInvocationId = 28 }; String BuiltinToString(BuiltIn b) { switch (b) { case BuiltIn::Invalid: return "invalid"; case BuiltIn::Position: return "Position"; case BuiltIn::PointSize: return "PointSize"; case BuiltIn::ClipDistance: return "ClipDistance"; case BuiltIn::CullDistance: return "CullDistance"; case BuiltIn::FragDepth: return "FragDepth"; case BuiltIn::WorkgroupSize: return "WorkgroupSize"; case BuiltIn::GlobalInvocationId: return "GlobalInvocationId"; default: throw NotImplementedException("unknown Builtin"); } } enum class Dim { e1D = 0, e2D = 1, e3D = 2, eCube = 3, eRect = 4, eBuffer = 5, eSubpassData = 6 }; String DimToString(Dim b) { switch (b) { case Dim::e1D: return "1D"; case Dim::e2D: return "2D"; case Dim::e3D: return "3D"; case Dim::eCube: return "Cube"; case Dim::eRect: return "Rect"; case Dim::eBuffer: return "Buffer"; case Dim::eSubpassData: return "SubpassData"; default: throw NotImplementedException("unknown Builtin"); } } enum class ImageOperands { None = 0, Bias = 0x1, Lod = 0x2, Grad = 0x4, ConstOffset = 0x8, Offset = 0x10, ConstOffsets = 0x20, Sample = 0x40, MinLod = 0x80 }; String ImageOperandsToString(ImageOperands io) { switch (io) { case ImageOperands::None: return "None"; case ImageOperands::Bias: return "Bias"; case ImageOperands::Lod: return "Lod"; case ImageOperands::Grad: return "Grad"; case ImageOperands::ConstOffset: return "ConstOffset"; case ImageOperands::Offset: return "Offset"; case ImageOperands::ConstOffsets: return "ConstOffsets"; case ImageOperands::Sample: return "Sample"; case ImageOperands::MinLod: return "MinLod"; default: throw NotImplementedException("unknown Image Operands"); } } Dictionary GenGLSLstd450InstructionSet() //https://www.khronos.org/registry/spir-v/specs/1.0/GLSL.std.450.html { Dictionary ret; ret["abs"] = 4; //fabs, actually :( ret["sign"] = 6; //fsign, actually :( ret["floor"] = 8; ret["cei"] = 9; ret["fract"] = 10; ret["sin"] = 13; ret["cos"] = 14; ret["tan"] = 15; ret["asin"] = 16; ret["acos"] = 17; ret["atan"] = 18; ret["atan2"] = 25; ret["pow"] = 26; ret["exp"] = 27; ret["log"] = 28; ret["exp2"] = 29; ret["log2"] = 30; ret["sqrt"] = 31; ret["min"] = 37; ret["max"] = 40; ret["clamp"] = 43; ret["mix"] = 46; ret["step"] = 48; ret["smoothstep"] = 49; ret["length"] = 66; ret["cross"] = 68; ret["normalize"] = 69; ret["reflect"] = 71; ret["refract"] = 72; return ret; } String GetFuncOriginalName(const String & name) { String originalName; int splitPos = name.IndexOf('@'); if (splitPos == 0) return name; if (splitPos != -1) originalName = name.SubString(0, splitPos); else originalName = name; return originalName; } String SpirVFloatToString(float v) { String rs(v, "%.12e"); if (!rs.Contains('.') && !rs.Contains('e') && !rs.Contains('E')) rs = rs + ".0"; return rs; }; String SpirVUIntToString(unsigned int i) { String s; if (i >> 31) s = s + "1"; s = s + int(i & 0x7fffffff); return s; } RefPtr GetTypeFromString(String s) { CoreLib::Text::Parser parser(s); return TypeFromString(parser); } //UniformOrBuffer - 0: none; 1: uniform; 2: buffer int GetBaseAlignment(ILType* Type, int UniformOrBuffer) { auto RoundUpTo = [](int x, int r) { if (x%r) x += r - x%r; return x; }; if (auto basicType = dynamic_cast(Type)) { return Type->GetAlignment(); } else if (auto arrayType = dynamic_cast(Type)) { int elementAlignment = GetBaseAlignment(arrayType->BaseType.Ptr(), UniformOrBuffer); if (UniformOrBuffer == 1) elementAlignment = RoundUpTo(elementAlignment, 16); return elementAlignment; } else if (auto structType = dynamic_cast(Type)) { int maxAlignment = -1; for (auto &member : structType->Members) { int memberAlignment = GetBaseAlignment(member.Type.Ptr(), UniformOrBuffer); maxAlignment = std::max(maxAlignment, memberAlignment); } if (UniformOrBuffer == 1) maxAlignment = RoundUpTo(maxAlignment, 16); return maxAlignment; } return -1; } int GetSize(ILType* Type, int UniformOrBuffer) { auto RoundUpTo = [](int x, int r) { if (x%r) x += r - x%r; return x; }; if (auto basicType = dynamic_cast(Type)) { return Type->GetSize(); } else if (auto arrayType = dynamic_cast(Type)) { return GetSize(arrayType->BaseType.Ptr(), UniformOrBuffer) * arrayType->ArrayLength; } else if (auto structType = dynamic_cast(Type)) { int rs = 0; for (auto &member : structType->Members) { int memberAlignment = GetBaseAlignment(member.Type.Ptr(), UniformOrBuffer); rs = RoundUpTo(rs, memberAlignment); rs += GetSize(member.Type.Ptr(), UniformOrBuffer); } return rs; } return 0; } enum class IDClass { None, TypeofValue, TypeofPointer, Pointer, Value, Function }; class IDInfo { private: bool available; IDClass idClass; int ID; String variableName; // only available for Class:Pointer String typeName; int typeID; int baseTypeID; RefPtr typeIL; StorageClass store; CompiledFunction * func = nullptr; ILOperand *op; public: IDInfo() :available(false) { } // static IDInfo CreateIDInfoForTypeofValue(int ID, RefPtr typeIL, int UniformOrBuffer = 0) { IDInfo ret; ret.available = true; ret.idClass = IDClass::TypeofValue; ret.ID = ID; ret.typeName = ""; if (typeIL) { ret.typeName = typeIL->ToString(); if (UniformOrBuffer) ret.typeName = ret.typeName + "#" + UniformOrBuffer; } ret.typeID = ID; ret.typeIL = typeIL; return ret; } static IDInfo CreateIDInfoForValue(int ID, RefPtr typeIL, ILOperand *op, int typeID) { IDInfo ret; ret.available = true; ret.idClass = IDClass::Value; ret.ID = ID; if (op) ret.variableName = op->Name; else ret.variableName = ""; ret.op = op; ret.typeName = typeIL->ToString(); ret.typeID = typeID; ret.typeIL = typeIL; return ret; } static IDInfo CreateIDInfoForPointer(int ID, ILOperand *op, int typeID, RefPtr basetypeIL, int basetypeID, StorageClass store) { IDInfo ret; ret.available = true; ret.idClass = IDClass::Pointer; ret.ID = ID; ret.op = op; if (op) ret.variableName = op->Name; else ret.variableName = ""; ret.typeName = basetypeIL->ToString(); ret.typeID = typeID; ret.baseTypeID = basetypeID; ret.typeIL = basetypeIL; ret.store = store; return ret; } static IDInfo CreateIDInfoForTypeofPointer(int ID, RefPtr baseTypeIL, int baseTypeID, StorageClass store) { IDInfo ret; ret.available = true; ret.idClass = IDClass::TypeofPointer; ret.ID = ID; ret.typeName = baseTypeIL->ToString(); ret.typeID = ID; ret.baseTypeID = baseTypeID; ret.typeIL = baseTypeIL; ret.store = store; return ret; } static IDInfo CreateIDInfoForFunction(int ID, CompiledFunction * func) { IDInfo ret; ret.available = true; ret.idClass = IDClass::Function; ret.ID = ID; ret.func = func; return ret; } bool IsAvailable() { return available; } int GetID() { if (!available) return -1; return ID; } IDClass GetClass() { if (!available) return IDClass::None; return idClass; } bool IsTypeofValue() { if (!available) return false; return idClass == IDClass::TypeofValue; } bool IsValue() { if (!available) return false; return idClass == IDClass::Value; } bool IsPointer() { if (!available) return false; return idClass == IDClass::Pointer; } bool IsTypeofPointer() { if (!available) return false; return idClass == IDClass::TypeofPointer; } bool IsFunction() { if (!available) return false; return idClass == IDClass::Function; } String GetVariableName() { if (!available) return ""; return variableName; } String GetTypeName() { if (!available) return ""; return typeName; } int GetTypeID() { if (!available) return -1; return typeID; } int GetBaseTypeID() { if (!available) return -1; return baseTypeID; } RefPtr GetILType() { if (!available) return nullptr; return typeIL; } StorageClass GetStorageClass() { if (!available) return StorageClass::Invalid; return store; } CompiledFunction * GetFunc() { if (!available || idClass != IDClass::Function) return nullptr; return func; } ILOperand* GetOp() { if (!available) return nullptr; return op; } }; class SpirVCodeBuilder { List streamHeader; List streamDebug; List streamAnnotation; List streamTypeDefinition; List streamFunctionHeader; List streamFunctionVariable; List streamFunctionBody; List streamProcessedFunctions; StringBuilder sbTextHeader; //OpCapability, OpExtension, OpExtInstImport, OpMemoryModel, OpEntryPoint, OpExecutionMode StringBuilder sbDebug; //OpName, OpMemberName StringBuilder sbTextAnnotation; //OpDecorate, OpMemberDecorate, OpGroupDecorate, OpGroupMemberDecorate, OpDecoration Group StringBuilder sbTextTypeDefinition; //OpTypeXXXX, OpConstant, global variable declarations(all OpVariable instructions whose storage class is not Function) StringBuilder sbTextFunctionDefinitions; StringBuilder sbTextFunctionHeader; StringBuilder sbTextFunctionVariable; StringBuilder sbTextFunctionBody; public: void Clear() { streamHeader.Clear(); streamDebug.Clear(); streamAnnotation.Clear(); streamTypeDefinition.Clear(); streamFunctionHeader.Clear(); streamFunctionVariable.Clear(); streamFunctionBody.Clear(); streamProcessedFunctions.Clear(); sbTextHeader.Clear(); sbDebug.Clear(); sbTextAnnotation.Clear(); sbTextTypeDefinition.Clear(); sbTextFunctionDefinitions.Clear(); sbTextFunctionHeader.Clear(); sbTextFunctionVariable.Clear(); sbTextFunctionBody.Clear(); } void Initiate() { Clear(); streamHeader.Add(0x07230203); // magic number streamHeader.Add(0x00010000); // version streamHeader.Add(0x00080001); // register number streamHeader.Add(0x0000ffff); // ID bound streamHeader.Add(0x00000000); // reserved } void ProduceFunction() { //---------------- for binary code ---------------- streamProcessedFunctions.AddRange(streamFunctionHeader); streamFunctionHeader.Clear(); streamProcessedFunctions.AddRange(streamFunctionVariable); streamFunctionVariable.Clear(); streamProcessedFunctions.AddRange(streamFunctionBody); streamFunctionBody.Clear(); //---------------- for text code ---------------- sbTextFunctionDefinitions << sbTextFunctionHeader.ToString() << sbTextFunctionVariable.ToString() << sbTextFunctionBody.ToString(); sbTextFunctionHeader.Clear(); sbTextFunctionVariable.Clear(); sbTextFunctionBody.Clear(); } List ProduceWordStream(int IDBound) { streamHeader[3] = IDBound + 5; List ret; ret.AddRange(streamHeader); ret.AddRange(streamDebug); ret.AddRange(streamAnnotation); ret.AddRange(streamTypeDefinition); ret.AddRange(streamProcessedFunctions); return ret; } String ProduceTextCode() { String ret; ret = ret + sbTextHeader.ToString(); ret = ret + sbDebug.ToString(); ret = ret + sbTextAnnotation.ToString(); ret = ret + sbTextTypeDefinition.ToString(); ret = ret + sbTextFunctionDefinitions.ToString(); return ret; } void ProgramHeader() { sbTextHeader << LR"(OpCapability Shader)" << EndLine; sbTextHeader << LR"(%1 = OpExtInstImport "GLSL.std.450")" << EndLine; sbTextHeader << LR"(OpMemoryModel Logical GLSL450)" << EndLine; streamHeader.Add(17 + (2 << 16)); //wordCount and opCode streamHeader.Add(1); //Shader //hardcoded streamHeader.Add(0x0006000B); streamHeader.Add(0x00000001); streamHeader.Add(0x4c534c47); streamHeader.Add(0x6474732E); streamHeader.Add(0x3035342E); streamHeader.Add(0); streamHeader.Add(14 + (3 << 16)); streamHeader.Add(0); streamHeader.Add(1); } void OpFunction(const int funcID, const int returnTypeID, const int functionTypeID) { sbTextFunctionHeader << LR"(%)" << funcID << LR"( = OpFunction )"; sbTextFunctionHeader << LR"(%)" << returnTypeID; sbTextFunctionHeader << LR"( None)"; sbTextFunctionHeader << LR"( %)" << functionTypeID; sbTextFunctionHeader << EndLine; streamFunctionHeader.Add(54 + (5 << 16)); streamFunctionHeader.Add(returnTypeID); streamFunctionHeader.Add(funcID); streamFunctionHeader.Add(0); // function control - 0 streamFunctionHeader.Add(functionTypeID); } void OpFunctionParameter(const int paramID, const int typeID) { sbTextFunctionHeader << LR"(%)" << paramID << LR"( = OpFunctionParameter %)" << typeID << EndLine; streamFunctionHeader.Add(55 + (3 << 16)); streamFunctionHeader.Add(typeID); streamFunctionHeader.Add(paramID); } void OpTypeFunction(const int functionTypeID, const int returnTypeID, const List &argIDList) { sbTextTypeDefinition << LR"(%)" << functionTypeID << LR"( = OpTypeFunction %)" << returnTypeID; for (auto & arg : argIDList) sbTextTypeDefinition << LR"( %)" << arg; sbTextTypeDefinition << EndLine; streamTypeDefinition.Add(33 + ((3 + argIDList.Count()) << 16)); streamTypeDefinition.Add(functionTypeID); streamTypeDefinition.Add(returnTypeID); for (auto & arg : argIDList) streamTypeDefinition.Add(arg); } void OpLabel_AtFunctionHeader(const int label) { sbTextFunctionHeader << LR"(%)" << label << LR"( = OpLabel)" << EndLine; streamFunctionHeader.Add(248 + (2 << 16)); streamFunctionHeader.Add(label); } void OpLabel_AtFunctionBody(const int label) { sbTextFunctionBody << LR"(%)" << label << LR"( = OpLabel)" << EndLine; streamFunctionBody.Add(248 + (2 << 16)); streamFunctionBody.Add(label); } void OpBranch(const int ID) { sbTextFunctionBody << LR"(OpBranch %)" << ID << EndLine; streamFunctionBody.Add(249 + (2 << 16)); streamFunctionBody.Add(ID); } void OpBranchConditional(const int cond, const int tb, const int fb) { sbTextFunctionBody << LR"(OpBranchConditional %)" << cond << LR"( %)" << tb << LR"( %)" << fb << EndLine; streamFunctionBody.Add(250 + (4 << 16)); streamFunctionBody.Add(cond); streamFunctionBody.Add(tb); streamFunctionBody.Add(fb); } void OpLoopMerge(const int merge, const int cont) { sbTextFunctionBody << LR"(OpLoopMerge %)" << merge << LR"( %)" << cont << LR"( None)" << EndLine; streamFunctionBody.Add(246 + (4 << 16)); streamFunctionBody.Add(merge); streamFunctionBody.Add(cont); streamFunctionBody.Add(0); //loop control: none } void OpSelectionMerge(const int merge) { sbTextFunctionBody << LR"(OpSelectionMerge %)" << merge << LR"( None)" << EndLine; streamFunctionBody.Add(247 + (3 << 16)); streamFunctionBody.Add(merge); streamFunctionBody.Add(0); //selection control: none } void OpPhi(const int ID, const int typeID, const List branches) { //: (variable1, parent branch1), (variable2, parent branch2), ... sbTextFunctionBody << LR"(%)" << ID << LR"( = OpPhi %)" << typeID; for (const auto & x : branches) sbTextFunctionBody << LR"( %)" << x; sbTextFunctionBody << EndLine; streamFunctionBody.Add(245 + ((3 + branches.Count()) << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); for (const auto & x : branches) streamFunctionBody.Add(x); } void OpFunctionCall(const int ID, const int typeID, const int funcID, const List &args) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpFunctionCall %)" << typeID << LR"( %)" << funcID; for (auto & arg : args) sbTextFunctionBody << LR"( %)" << arg; sbTextFunctionBody << EndLine; streamFunctionBody.Add(57 + ((4 + args.Count()) << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(funcID); for (auto & arg : args) streamFunctionBody.Add(arg); } void OpKill() { sbTextFunctionBody << LR"(OpKill)" << EndLine; streamFunctionBody.Add(252 + (1<<16)); } void OpReturn() { sbTextFunctionBody << LR"(OpReturn)" << EndLine; streamFunctionBody.Add(253 + (1 << 16)); } void OpReturnValue(const int ID) { sbTextFunctionBody << LR"(OpReturnValue %)" << ID << EndLine; streamFunctionBody.Add(254 + (2 << 16)); streamFunctionBody.Add(ID); } void OpFunctionEnd() { sbTextFunctionBody << LR"(OpFunctionEnd)" << EndLine; streamFunctionBody.Add(56 + (1 << 16)); } int EncodeString(List & stream, String S) { auto encoder = CoreLib::IO::Encoding::UTF8; List bytes; encoder->GetBytes(bytes, S); int padding = (4 - (bytes.Count() & 3)) & 3; for (int i = 0; i < padding; i++) bytes.Add(0); int oldSize = stream.Count(); stream.SetSize(oldSize + (bytes.Count() >> 2)); memcpy(stream.Buffer() + oldSize, bytes.Buffer(), bytes.Count()); if (padding==0) stream.Add(0); return stream.Count() - oldSize; } void OpEntryPoint(const ExecutionModel currentExecutionModel, const int entryID, const List &interfaceIDs) { sbTextHeader << LR"(OpEntryPoint )"; sbTextHeader << ExecutionModelToString(currentExecutionModel) << LR"( )"; sbTextHeader << LR"(%)" << entryID << LR"( "main" )"; for (auto & id : interfaceIDs) sbTextHeader << LR"( %)" << id; sbTextHeader << EndLine; int len_i = streamHeader.Count(); streamHeader.Add(0); streamHeader.Add((int)currentExecutionModel); streamHeader.Add(entryID); int NameLen = EncodeString(streamHeader, "main"); for (auto & id : interfaceIDs) streamHeader.Add(id); streamHeader[len_i] = (15) + ((1 + 1 + NameLen + 1 + interfaceIDs.Count()) << 16); } void OpExecutionMode(const int entryID, const ExecutionMode mode, const int op1, const int op2, const int op3) { bool LocalSize = false; if (mode == ExecutionMode::LocalSize) LocalSize = true; sbTextHeader << LR"(OpExecutionMode %)" << entryID << LR"( )" << ExecutionModeToString(mode); if (LocalSize) sbTextHeader << op1 << op2 << op3; sbTextHeader << EndLine; int len = 3; if (LocalSize) len += 3; streamHeader.Add(16 + (len << 16)); streamHeader.Add(entryID); streamHeader.Add((int)mode); if (LocalSize) { streamHeader.Add(op1); streamHeader.Add(op2); streamHeader.Add(op3); } } int Decorate(const Decoration deco, int op1 = 0) { int len = 0; sbTextAnnotation << " " << DecorationToString(deco); streamAnnotation.Add((int)deco); len++; if (deco == Decoration::Location || deco == Decoration::Offset || deco == Decoration::MatrixStride || deco == Decoration::ArrayStride || deco == Decoration::DescriptorSet || deco == Decoration::Binding) { sbTextAnnotation << LR"( )" << op1; streamAnnotation.Add(op1); len++; } if (deco == Decoration::BuiltIn) { BuiltIn builtin = static_cast(op1); sbTextAnnotation << LR"( )" << BuiltinToString(builtin); streamAnnotation.Add(op1); len++; } return len; } void OpDecorate(const int ID, const Decoration deco, int op1 = 0) { sbTextAnnotation << LR"(OpDecorate %)" << ID; int len_i = streamAnnotation.Count(); streamAnnotation.Add(0); streamAnnotation.Add(ID); int deco_len = Decorate(deco, op1); streamAnnotation[len_i] = 71 + ((2 + deco_len) << 16); sbTextAnnotation << EndLine; } void OpMemberDecorate(const int ID, const int memberIndex, const Decoration deco, int op1 = 0) { sbTextAnnotation << LR"(OpMemberDecorate %)" << ID << LR"( )" << memberIndex; int len_i = streamAnnotation.Count(); streamAnnotation.Add(0); streamAnnotation.Add(ID); streamAnnotation.Add(memberIndex); int deco_len = Decorate(deco, op1); streamAnnotation[len_i] = 72 + ((3 + deco_len) << 16); sbTextAnnotation << EndLine; } void OpSNegate(const int ID, const int typeID, const int valueID) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpSNegate %)" << typeID << LR"( %)" << valueID << EndLine; streamFunctionBody.Add(126 + (4 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(valueID); } void OpFNegate(const int ID, const int typeID, const int valueID) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpFNegate %)" << typeID << LR"( %)" << valueID << EndLine; streamFunctionBody.Add(127 + (4 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(valueID); } void OpFAdd(const int ID, const int typeID, const int op1, const int op2) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpFAdd %)" << typeID << LR"( %)" << op1 << LR"( %)" << op2 << EndLine; streamFunctionBody.Add(129 + (5 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(op1); streamFunctionBody.Add(op2); } void OpFMul(const int ID, const int typeID, const int op1, const int op2) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpFMul %)" << typeID << LR"( %)" << op1 << LR"( %)" << op2 << EndLine; streamFunctionBody.Add(133 + (5 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(op1); streamFunctionBody.Add(op2); } void OpINotEqual(const int ID, const int typeID, const int id0, const int id1) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpINotEqual %)" << typeID << LR"( %)" << id0 << LR"( %)" << id1 << EndLine; streamFunctionBody.Add(171 + (5 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(id0); streamFunctionBody.Add(id1); } void OpNot(const int ID, const int typeID, const int valueID) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpNot %)" << typeID << LR"( %)" << valueID << EndLine; streamFunctionBody.Add(200 + (4 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(valueID); } void OpLogicalNot(const int ID, const int typeID, const int valueID) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpLogicalNot %)" << typeID << LR"( %)" << valueID << EndLine; streamFunctionBody.Add(168 + (4 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(valueID); } void OpBinaryInstr(const int ID, String opStr, const int typeID, const int ID0, const int ID1) { sbTextFunctionBody << LR"(%)" << ID << LR"( = )"; sbTextFunctionBody << opStr; sbTextFunctionBody << LR"( %)" << typeID << LR"( %)" << ID0 << LR"( %)" << ID1 << EndLine; int opCode = -1; String opStr_prefix = opStr.SubString(0, 2); opStr = opStr.SubString(2, opStr.Length() - 2); if (opStr == "FMu") opCode = 133; else if (opStr == "IMu") opCode = 132; else if (opStr == "FAdd") opCode = 129; else if (opStr == "IAdd") opCode = 128; else if (opStr == "UDiv") opCode = 134; else if (opStr == "SDiv") opCode = 135; else if (opStr == "FDiv") opCode = 136; else if (opStr == "FSub") opCode = 131; else if (opStr == "ISub") opCode = 130; else if (opStr == "UMod") opCode = 137; else if (opStr == "SMod") opCode = 139; else if (opStr == "FMod") opCode = 141; else if (opStr == "ShiftLeftLogica") opCode = 196; else if (opStr == "ShiftRightArithmetic") opCode = 195; else if (opStr == "ShiftRightLogica") opCode = 194; else if (opStr == "BitwiseXor") opCode = 198; else if (opStr == "BitwiseAnd") opCode = 199; else if (opStr == "BitwiseOr") opCode = 197; else if (opStr == "LogicalAnd") opCode = 167; else if (opStr == "LogicalOr") opCode = 166; else if (opStr == "INotEqua") opCode = 171; else if (opStr == "FOrdNotEqua") opCode = 182; else if (opStr == "IEqua") opCode = 170; else if (opStr == "FOrdEqua") opCode = 180; else if (opStr == "SGreaterThanEqua") opCode = 175; else if (opStr == "FOrdGreaterThanEqua") opCode = 190; else if (opStr == "SGreaterThan") opCode = 173; else if (opStr == "FOrdGreaterThan") opCode = 186; else if (opStr == "SLessThanEqua") opCode = 179; else if (opStr == "FOrdLessThanEqua") opCode = 188; else if (opStr == "SLessThan") opCode = 177; else if (opStr == "FOrdLessThan") opCode = 184; else if (opStr == "UGreaterThan") opCode = 172; else if (opStr == "UGreaterThanEqua") opCode = 174; else if (opStr == "ULessThan") opCode = 176; else if (opStr == "ULessThanEqua") opCode = 178; if (opCode == -1) throw InvalidOperationException("unrecognized op string in CodeGenerator::OpBinaryInstr(): " + opStr); streamFunctionBody.Add(opCode + (5 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(ID0); streamFunctionBody.Add(ID1); } void OpMatrixTimesScalar(const int ID, const int typeID, const int ID0, const int ID1) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpMatrixTimesScalar %)" << typeID << LR"( %)" << ID0 << LR"( %)" << ID1 << EndLine; streamFunctionBody.Add(143 + (5 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(ID0); streamFunctionBody.Add(ID1); } void OpVectorTimesMatrix(const int ID, const int typeID, const int ID0, const int ID1) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpVectorTimesMatrix %)" << typeID << LR"( %)" << ID0 << LR"( %)" << ID1 << EndLine; streamFunctionBody.Add(144 + (5 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(ID0); streamFunctionBody.Add(ID1); } void OpMatrixTimesVector(const int ID, const int typeID, const int ID0, const int ID1) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpMatrixTimesVector %)" << typeID << LR"( %)" << ID0 << LR"( %)" << ID1 << EndLine; streamFunctionBody.Add(145 + (5 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(ID0); streamFunctionBody.Add(ID1); } void OpMatrixTimesMatrix(const int ID, const int typeID, const int ID0, const int ID1) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpMatrixTimesMatrix %)" << typeID << LR"( %)" << ID0 << LR"( %)" << ID1 << EndLine; streamFunctionBody.Add(146 + (5 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(ID0); streamFunctionBody.Add(ID1); } void OpConstantBool(const int typeID, const int ID, const bool b) { if (b) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpConstantTrue%)" << EndLine; streamTypeDefinition.Add(41 + (3 << 16)); streamTypeDefinition.Add(typeID); streamTypeDefinition.Add(ID); } else { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpConstantFalse%)" << EndLine; streamTypeDefinition.Add(42 + (3 << 16)); streamTypeDefinition.Add(typeID); streamTypeDefinition.Add(ID); } } void OpConstantFloat(const int ID, const int typeID, float f) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpConstant %)" << typeID << LR"( )" << SpirVFloatToString(f) << EndLine; streamTypeDefinition.Add(43 + (4 << 16)); streamTypeDefinition.Add(typeID); streamTypeDefinition.Add(ID); streamTypeDefinition.Add(*reinterpret_cast(&f)); //printf("%.8f -> %x\n", f, streamTypeDefinition[streamTypeDefinition.Count() - 1]); } void OpConstantInt(const int ID, const int typeID, int i) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpConstant %)" << typeID << LR"( )" << i << EndLine; streamTypeDefinition.Add(43 + (4 << 16)); streamTypeDefinition.Add(typeID); streamTypeDefinition.Add(ID); streamTypeDefinition.Add(*reinterpret_cast(&i)); } void OpConstantUInt(const int ID, const int typeID, const unsigned int i) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpConstant %)" << typeID << LR"( )"; sbTextTypeDefinition << SpirVUIntToString(i); sbTextTypeDefinition << EndLine; streamTypeDefinition.Add(43 + (4 << 16)); streamTypeDefinition.Add(typeID); streamTypeDefinition.Add(ID); streamTypeDefinition.Add(i); } void OpConstantComposite(const int ID, const int typeID, const List &args) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpConstantComposite %)" << typeID; for (auto & id : args) sbTextTypeDefinition << LR"( %)" << id; sbTextTypeDefinition << EndLine; streamTypeDefinition.Add(44 + ((3 + args.Count()) << 16)); streamTypeDefinition.Add(typeID); streamTypeDefinition.Add(ID); for (auto & id : args) streamTypeDefinition.Add(id); } void OpCompositeConstruct(const int ID, const int typeID, const List &args) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpCompositeConstruct %)" << typeID; for (auto & id : args) sbTextFunctionBody << LR"( %)" << id; sbTextFunctionBody << EndLine; streamFunctionBody.Add(80 + ((3 + args.Count()) << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); for (auto & id : args) streamFunctionBody.Add(id); } void OpCompositeExtract(const int ID, const int baseTypeID, const int compositeID, const int index) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpCompositeExtract %)" << baseTypeID << LR"( %)" << compositeID << LR"( )" << index << EndLine; streamFunctionBody.Add(81 + (5 << 16)); streamFunctionBody.Add(baseTypeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(compositeID); streamFunctionBody.Add(index); } void OpCompositeInsert(const int ID, const int typeID, const int objectID, const int compositeID, const int index) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpCompositeInsert %)" << typeID << LR"( %)" << objectID << LR"( %)" << compositeID << LR"( )" << index << EndLine; streamFunctionBody.Add(82 + (6 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(objectID); streamFunctionBody.Add(compositeID); streamFunctionBody.Add(index); } void OpExtInst(const int ID, const int typeID, const int instrNumber, const List &Arguments) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpExtInst %)" << typeID << LR"( %1 )"; sbTextFunctionBody << instrNumber; for (auto & arg : Arguments) sbTextFunctionBody << LR"( %)" << arg; sbTextFunctionBody << EndLine; streamFunctionBody.Add(12 + ((5 + Arguments.Count()) << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(1); //instruction set **** streamFunctionBody.Add(instrNumber); for (auto & arg : Arguments) streamFunctionBody.Add(arg); } void OpStore(const int op0, const int op1) { sbTextFunctionBody << LR"(OpStore %)" << op0 << LR"( %)" << op1 << EndLine; streamFunctionBody.Add(62 + (3 << 16)); streamFunctionBody.Add(op0); streamFunctionBody.Add(op1); } void OpLoad(const int ID, const int typeID, const int variableID, const MemoryAccess ma) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpLoad %)" << typeID << LR"( %)" << variableID << LR"( )" << MemoryAccessToString(ma) << EndLine; if (ma != MemoryAccess::None) throw NotImplementedException("not support memory access in CodeGenerator::OpLoad(): " + MemoryAccessToString(ma)); streamFunctionBody.Add(61 + (4 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(variableID); } void OpVariable(const int ID, const int typeID, StorageClass store) { StringBuilder instrBuilder; instrBuilder << LR"(%)" << ID << LR"( = OpVariable %)" << typeID << LR"( )" << StorageClassToString(store) << EndLine; if (store == StorageClass::Function) sbTextFunctionVariable << instrBuilder.ProduceString() << EndLine; else sbTextTypeDefinition << instrBuilder.ProduceString() << EndLine; if (store == StorageClass::Function) { streamFunctionVariable.Add(59 + (4 << 16)); streamFunctionVariable.Add(typeID); streamFunctionVariable.Add(ID); streamFunctionVariable.Add((int)store); } else { streamTypeDefinition.Add(59 + (4 << 16)); streamTypeDefinition.Add(typeID); streamTypeDefinition.Add(ID); streamTypeDefinition.Add((int)store); } } void OpAccessChain(const int ID, const int typeID, const int structID, const int indexID) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpAccessChain %)" << typeID << LR"( %)" << structID << LR"( %)" << indexID << EndLine; streamFunctionBody.Add(65 + (5 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(structID); streamFunctionBody.Add(indexID); } void OpImageSampleImplicitLod( const int ID, const int typeID, const int textureID, const int coordinateID, const int Bias = -1) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpImageSampleImplicitLod %)" << typeID << LR"( %)" << textureID << LR"( %)" << coordinateID; if (Bias != -1) sbTextFunctionBody << LR"( Bias %)" << Bias; sbTextFunctionBody << EndLine; int len = 5; int IO = 0; if (Bias != -1) { len++; IO |= (int)ImageOperands::Bias; } if (IO) len++; streamFunctionBody.Add(87 + (len << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(textureID); streamFunctionBody.Add(coordinateID); if (IO) streamFunctionBody.Add(IO); if (Bias != -1) streamFunctionBody.Add(Bias); } void OpImageSampleExplicitLod( const int ID, const int typeID, const int textureID, const int coordinateID, const int LodID, const int Bias = -1, const int GradX = -1, const int GradY = -1) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpImageSampleExplicitLod %)" << typeID << LR"( %)" << textureID << LR"( %)" << coordinateID; if (Bias != -1) sbTextFunctionBody << LR"( Bias %)" << Bias; if (GradX != -1) sbTextFunctionBody << LR"( Grad %)" << GradX << LR"( %)" << GradY; else sbTextFunctionBody << LR"( Lod %)" << LodID; sbTextFunctionBody << EndLine; int IO = 0; int len = 5; if (Bias != -1) { IO |= (int)ImageOperands::Bias; len++; } if (GradX != -1) { IO |= (int)ImageOperands::Grad; len += 2; } else { IO |= (int)ImageOperands::Lod; len++; } if (IO) len++; streamFunctionBody.Add(88 + (len << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(textureID); streamFunctionBody.Add(coordinateID); if (IO) streamFunctionBody.Add(IO); if (Bias != -1) streamFunctionBody.Add(Bias); if (GradX != -1) { streamFunctionBody.Add(GradX); streamFunctionBody.Add(GradY); } else streamFunctionBody.Add(LodID); } void OpImageSampleDrefImplicitLod( const int ID, const int typeID, const int textureID, const int coordinateID, const int DrefID) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpImageSampleDrefImplicitLod %)" << typeID << LR"( %)" << textureID << LR"( %)" << coordinateID << LR"( %)" << DrefID; sbTextFunctionBody << EndLine; int len = 6; streamFunctionBody.Add(89 + (len << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(textureID); streamFunctionBody.Add(coordinateID); streamFunctionBody.Add(DrefID); } void OpImageSampleDrefExplicitLod( const int ID, const int typeID, const int textureID, const int coordinateID, const int DrefID, const int LodID) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpImageSampleDrefExplicitLod %)" << typeID << LR"( %)" << textureID << LR"( %)" << coordinateID << LR"( %)" << DrefID; sbTextFunctionBody << LR"( Lod %)" << LodID; sbTextFunctionBody << EndLine; int len = 8; streamFunctionBody.Add(90 + (len << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(textureID); streamFunctionBody.Add(coordinateID); streamFunctionBody.Add(DrefID); streamFunctionBody.Add((int)ImageOperands::Lod); streamFunctionBody.Add(LodID); } void OpImageSampleProjDrefImplicitLod( const int ID, const int typeID, const int textureID, const int coordinateID, const int DrefID) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpImageSampleProjDrefImplicitLod %)" << typeID << LR"( %)" << textureID << LR"( %)" << coordinateID << LR"( %)" << DrefID; sbTextFunctionBody << EndLine; int len = 6; streamFunctionBody.Add(93 + (len << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(textureID); streamFunctionBody.Add(coordinateID); streamFunctionBody.Add(DrefID); } void OpImageSampleProjDrefExplicitLod( const int ID, const int typeID, const int textureID, const int coordinateID, const int DrefID, const int LodID) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpImageSampleProjDrefExplicitLod %)" << typeID << LR"( %)" << textureID << LR"( %)" << coordinateID << LR"( %)" << DrefID; sbTextFunctionBody << LR"( Lod %)" << LodID; sbTextFunctionBody << EndLine; int len = 8; streamFunctionBody.Add(94 + (len << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(textureID); streamFunctionBody.Add(coordinateID); streamFunctionBody.Add(DrefID); streamFunctionBody.Add((int)ImageOperands::Lod); streamFunctionBody.Add(LodID); } void OpConvertSToF(const int ID, const int typeID, const int operandID) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpConvertSToF %)" << typeID << LR"( %)" << operandID << EndLine; streamFunctionBody.Add(111 + (4 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(operandID); } void OpConvertFToS(const int ID, const int typeID, const int operandID) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpConvertFToS %)" << typeID << LR"( %)" << operandID << EndLine; streamFunctionBody.Add(110 + (4 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(operandID); } void OpConvertFToU(const int ID, const int typeID, const int operandID) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpConvertFToU %)" << typeID << LR"( %)" << operandID << EndLine; streamFunctionBody.Add(109 + (4 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(operandID); } void OpConvertUToF(const int ID, const int typeID, const int operandID) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpConvertUToF %)" << typeID << LR"( %)" << operandID << EndLine; streamFunctionBody.Add(112 + (4 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(operandID); } void OpBitCast(const int ID, const int typeID, const int operandID) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpBitcast %)" << typeID << LR"( %)" << operandID << EndLine; streamFunctionBody.Add(124 + (4 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(operandID); } void OpTypeVoid(const int ID) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpTypeVoid)" << EndLine; streamTypeDefinition.Add(19 + (2 << 16)); streamTypeDefinition.Add(ID); } void OpTypeBool(const int ID) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpTypeBool)" << EndLine; streamTypeDefinition.Add(20 + (2 << 16)); streamTypeDefinition.Add(ID); } void OpTypeInt(const int ID, const int width, const int signedness) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpTypeInt )" << width << LR"( )" << signedness << EndLine; streamTypeDefinition.Add(21 + (4 << 16)); streamTypeDefinition.Add(ID); streamTypeDefinition.Add(width); streamTypeDefinition.Add(signedness); } void OpTypeFloat(const int ID, const int width) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpTypeFloat )" << width << EndLine; streamTypeDefinition.Add(22 + (3 << 16)); streamTypeDefinition.Add(ID); streamTypeDefinition.Add(width); } void OpTypeVector(const int ID, const int eleTypeID, const int vecLen) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpTypeVector %)" << eleTypeID << LR"( )" << vecLen << EndLine; streamTypeDefinition.Add(23 + (4 << 16)); streamTypeDefinition.Add(ID); streamTypeDefinition.Add(eleTypeID); streamTypeDefinition.Add(vecLen); } void OpTypeMatrix(const int ID, const int colTypeID, const int Dim) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpTypeMatrix %)" << colTypeID << LR"( )" << Dim << EndLine; streamTypeDefinition.Add(24 + (4 << 16)); streamTypeDefinition.Add(ID); streamTypeDefinition.Add(colTypeID); streamTypeDefinition.Add(Dim); } void OpTypeImage(const int ID, const int sampledTypeID, const Dim d, const int depth) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpTypeImage %)" << sampledTypeID << LR"( )" << DimToString(d) << LR"( )" << depth << LR"( 0 0 1 Unknown)" << EndLine; streamTypeDefinition.Add(25 + (9 << 16)); streamTypeDefinition.Add(ID); streamTypeDefinition.Add(sampledTypeID); streamTypeDefinition.Add((int)d); //Dim streamTypeDefinition.Add(depth); //depth streamTypeDefinition.Add(0); //arrayed streamTypeDefinition.Add(0); //MS streamTypeDefinition.Add(1); //sampled: will be used with sampler streamTypeDefinition.Add(0); //image format: Unknown } void OpTypeSampledImage(const int ID, const int imageTypeID) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpTypeSampledImage %)" << imageTypeID << EndLine; streamTypeDefinition.Add(27 + (3 << 16)); streamTypeDefinition.Add(ID); streamTypeDefinition.Add(imageTypeID); } void OpTypePointer(const int ID, const StorageClass store, const int baseTypeID) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpTypePointer )" << StorageClassToString(store) << LR"( %)" << baseTypeID << EndLine; streamTypeDefinition.Add(32 + (4 << 16)); streamTypeDefinition.Add(ID); streamTypeDefinition.Add((int)store); streamTypeDefinition.Add(baseTypeID); } void OpTypeArray(const int ID, const int elementTypeID, const int lengthID) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpTypeArray %)" << elementTypeID << LR"( %)" << lengthID << EndLine; streamTypeDefinition.Add(28 + (4 << 16)); streamTypeDefinition.Add(ID); streamTypeDefinition.Add(elementTypeID); streamTypeDefinition.Add(lengthID); } void OpTypeRuntimeArray(const int ID, const int elementTypeID) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpTypeRuntimeArray %)" << elementTypeID << EndLine; streamTypeDefinition.Add(29 + (3 << 16)); streamTypeDefinition.Add(ID); streamTypeDefinition.Add(elementTypeID); } void OpTypeStruct(const int ID, const List & memberIDList) { sbTextTypeDefinition << LR"(%)" << ID << LR"( = OpTypeStruct)"; for (auto & member : memberIDList) sbTextTypeDefinition << LR"( %)" << member; sbTextTypeDefinition << EndLine; streamTypeDefinition.Add(30 + ((2 + memberIDList.Count()) << 16)); streamTypeDefinition.Add(ID); for (auto & member : memberIDList) streamTypeDefinition.Add(member); } void OpDot(const int ID, const int typeID, const int ID0, const int ID1) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpDot %)" << typeID << LR"( %)" << ID0 << LR"( %)" << ID1 << EndLine; streamFunctionBody.Add(148 + (5 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(ID0); streamFunctionBody.Add(ID1); } void OpTranspose(const int ID, const int typeID, const int Op0) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpTranspose %)" << typeID << LR"( %)" << Op0 << EndLine; streamFunctionBody.Add(84 + (4 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(Op0); } void OpDPdx(const int ID, const int typeID, const int Op0) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpDPdx %)" << typeID << LR"( %)" << Op0 << EndLine; streamFunctionBody.Add(207 + (4 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(Op0); } void OpDPdy(const int ID, const int typeID, const int Op0) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpDPdy %)" << typeID << LR"( %)" << Op0 << EndLine; streamFunctionBody.Add(208 + (4 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(Op0); } void OpFwidth(const int ID, const int typeID, const int Op0) { sbTextFunctionBody << LR"(%)" << ID << LR"( = OpFwidth %)" << typeID << LR"( %)" << Op0 << EndLine; streamFunctionBody.Add(209 + (4 << 16)); streamFunctionBody.Add(typeID); streamFunctionBody.Add(ID); streamFunctionBody.Add(Op0); } void OpName(int ID, String Name) { sbDebug << LR"(OpName %)" << ID << LR"( ")" << Name << LR"(")" << EndLine; int len_i = streamDebug.Count(); streamDebug.Add(0); streamDebug.Add(ID); int len = EncodeString(streamDebug, Name); streamDebug[len_i] = 5 + ((2 + len) << 16); } void OpMemberName(int ID, int index, String Name) { sbDebug << LR"(OpMemberName %)" << ID << LR"( )" << index << LR"( ")" << Name << LR"(")" << EndLine; int len_i = streamDebug.Count(); streamDebug.Add(0); streamDebug.Add(ID); streamDebug.Add(index); int len = EncodeString(streamDebug, Name); streamDebug[len_i] = 6 + ((3 + len) << 16); } }; class SpirVCodeGenContext { public: CompileResult * Result = nullptr; int CurrentID = 0; int MainFunctionID = 0; int MainFunctionReturnTypeID = 0; int MainFunctionTypeID = 0; int ReturnID = -1; Dictionary TypeNameToID; // 'int' - 1, 'vec4' - 2 Dictionary TypeStorageToTypePointerID; // 'uint Function' - 5, 'mat3 Uniform' - 6 Dictionary FunctionNameToFunctionTypeID; Dictionary FunctionNameToFunctionID; Dictionary ParameterNameToID; List> StackVariableNameToStorageID; List> StackVariableNameToValueID; Dictionary InterfaceNameToID; Dictionary Dictionary_ConstantIntToID; Dictionary Dictionary_ConstantUIntToID; Dictionary Dictionary_ConstantBoolToID; Dictionary IDInfos; List StackMergeBlock; List StackContinueBlock; SpirVCodeBuilder CodeGen; void Clear() { Result = nullptr; CurrentID = 0; MainFunctionID = 0; MainFunctionReturnTypeID = 0; MainFunctionTypeID = 0; ReturnID = -1; StackVariableNameToStorageID.Clear(); StackVariableNameToValueID.Clear(); TypeNameToID.Clear(); TypeStorageToTypePointerID.Clear(); FunctionNameToFunctionTypeID.Clear(); FunctionNameToFunctionID.Clear(); ParameterNameToID.Clear(); Dictionary_ConstantIntToID.Clear(); Dictionary_ConstantUIntToID.Clear(); Dictionary_ConstantBoolToID.Clear(); IDInfos.Clear(); StackMergeBlock.Clear(); StackContinueBlock.Clear(); CodeGen.Clear(); InterfaceNameToID.Clear(); } void ClearBuffer() { StackVariableNameToStorageID.Clear(); StackVariableNameToValueID.Clear(); ParameterNameToID.Clear(); ReturnID = -1; } void PushScope() { StackVariableNameToStorageID.Add(Dictionary()); StackVariableNameToValueID.Add(Dictionary()); } void PopScope() { StackVariableNameToStorageID.RemoveAt(StackVariableNameToStorageID.Count() - 1); StackVariableNameToValueID.RemoveAt(StackVariableNameToValueID.Count() - 1); } void UpdateVariable(ILOperand* op, int id) { if (op == nullptr) return; StackVariableNameToStorageID.Last()[op] = id; } int FindVariableID(ILOperand* op) { auto it = StackVariableNameToStorageID.end(); while (it != StackVariableNameToStorageID.begin()) { it--; if (it->ContainsKey(op)) return (*it)[op]; } return -1; } // ***NOTICE***: // There is no relation between VariableNameToStorageID and ValueIDToVariableNames. void UpdateValue(ILOperand *op, int id) { if (op == nullptr) return; StackVariableNameToValueID.Last()[op] = id; } int FindValueID(ILOperand *op) { auto it = StackVariableNameToValueID.end(); while (it != StackVariableNameToValueID.begin()) { it--; if (it->ContainsKey(op)) return (*it)[op]; } return -1; } void InvalidateValue(ILOperand *op) { if (StackVariableNameToValueID.Last().ContainsKey(op)) StackVariableNameToValueID.Last()[op] = -1; } int DefineBasicType(RefPtr Type) { String typeName = Type->ToString(); if (TypeNameToID.ContainsKey(typeName)) return TypeNameToID[typeName]; TypeNameToID[typeName] = -1; //marked as visited if (typeName == "int" || typeName.StartsWith("ivec")) { DefineBasicType(new ILBasicType(ILBaseType::Int)); if (typeName == "int") { ++CurrentID; CodeGen.OpTypeInt(CurrentID, 32, 1); TypeNameToID[typeName] = CurrentID; } if (typeName.StartsWith("ivec")) { ++CurrentID; CodeGen.OpTypeVector(CurrentID, TypeNameToID["int"](), StringToInt(typeName[4])); TypeNameToID[typeName] = CurrentID; } } if (typeName == "uint" || typeName.StartsWith("uvec")) { DefineBasicType(new ILBasicType(ILBaseType::UInt)); if (typeName == "uint") { ++CurrentID; CodeGen.OpTypeInt(CurrentID, 32, 0); TypeNameToID[typeName] = CurrentID; } if (typeName.StartsWith("uvec")) { ++CurrentID; CodeGen.OpTypeVector(CurrentID, TypeNameToID["uint"](), StringToInt(typeName[4])); TypeNameToID[typeName] = CurrentID; } } if (typeName == "float" || typeName.StartsWith("vec") || typeName.StartsWith("mat")) { DefineBasicType(new ILBasicType(ILBaseType::Float)); if (typeName == "float") { ++CurrentID; CodeGen.OpTypeFloat(CurrentID, 32); TypeNameToID[typeName] = CurrentID; } if (typeName.StartsWith("vec")) { ++CurrentID; CodeGen.OpTypeVector(CurrentID, TypeNameToID["float"](), StringToInt(typeName[3])); TypeNameToID[typeName] = CurrentID; } if (typeName == "mat3") { DefineBasicType(new ILBasicType(ILBaseType::Float3)); ++CurrentID; CodeGen.OpTypeMatrix(CurrentID, TypeNameToID["vec3"](), 3); TypeNameToID[typeName] = CurrentID; } if (typeName == "mat4") { DefineBasicType(new ILBasicType(ILBaseType::Float4)); ++CurrentID; CodeGen.OpTypeMatrix(CurrentID, TypeNameToID["vec4"](), 4); TypeNameToID[typeName] = CurrentID; } } if (typeName == "sampler2D") { //according to vulkan specification // Resource Descriptors, Descriptor Types, Sampled Image DefineBasicType(new ILBasicType(ILBaseType::Float)); ++CurrentID; CodeGen.OpTypeImage(CurrentID, TypeNameToID["float"](), Dim::e2D, 0); int tmp = CurrentID; ++CurrentID; CodeGen.OpTypeSampledImage(CurrentID, tmp); TypeNameToID[typeName] = CurrentID; } if (typeName == "samplerCube") { DefineBasicType(new ILBasicType(ILBaseType::Float)); ++CurrentID; CodeGen.OpTypeImage(CurrentID, TypeNameToID["float"](), Dim::eCube, 0); int tmp = CurrentID; ++CurrentID; CodeGen.OpTypeSampledImage(CurrentID, tmp); TypeNameToID[typeName] = CurrentID; } if (typeName == "samplerCubeShadow") { DefineBasicType(new ILBasicType(ILBaseType::Float)); ++CurrentID; CodeGen.OpTypeImage(CurrentID, TypeNameToID["float"](), Dim::eCube, 1); int tmp = CurrentID; ++CurrentID; CodeGen.OpTypeSampledImage(CurrentID, tmp); TypeNameToID[typeName] = CurrentID; } if (typeName == "sampler2DShadow") { DefineBasicType(new ILBasicType(ILBaseType::Float)); ++CurrentID; CodeGen.OpTypeImage(CurrentID, TypeNameToID["float"](), Dim::e2D, 1); int tmp = CurrentID; ++CurrentID; CodeGen.OpTypeSampledImage(CurrentID, tmp); TypeNameToID[typeName] = CurrentID; } if (typeName == "bool") { ++CurrentID; CodeGen.OpTypeBool(CurrentID); TypeNameToID[typeName] = CurrentID; } if (TypeNameToID[typeName] == -1) { throw InvalidProgramException("fail to generate type definition for: " + typeName); } int id = TypeNameToID[typeName]; IDInfos[id] = IDInfo::CreateIDInfoForTypeofValue(id, Type); return id; } //UniformOrBuffer - 0: none; 1: uniform; 2: buffer int DefineType(RefPtr Type, int UniformOrBuffer = 0) { if (!Type) { if (TypeNameToID.ContainsKey("void")) return TypeNameToID["void"]; ++CurrentID; //TypeDefinition << LR"(%)" << << LR"( = OpTypeVoid)" << EndLine; CodeGen.OpTypeVoid(CurrentID); TypeNameToID["void"] = CurrentID; IDInfos[CurrentID] = IDInfo::CreateIDInfoForTypeofValue(CurrentID, nullptr); return CurrentID; } int RetID = -1; if (auto ArrayType = dynamic_cast(Type.Ptr())) { String IndexName = Type->ToString(); if (UniformOrBuffer != 0) IndexName = IndexName + "#" + UniformOrBuffer; if (TypeNameToID.ContainsKey(IndexName)) return TypeNameToID[IndexName]; if (ArrayType->ArrayLength != 0) { //normal constant-length array int lengthID = AddInstrConstantInt(ArrayType->ArrayLength); int elementTypeID = DefineType(ArrayType->BaseType, UniformOrBuffer); ++CurrentID; CodeGen.OpTypeArray(CurrentID, elementTypeID, lengthID); TypeNameToID[IndexName] = CurrentID; IDInfos[CurrentID] = IDInfo::CreateIDInfoForTypeofValue(CurrentID, Type, UniformOrBuffer); RetID = CurrentID; } else { //dynamic array int elementTypeID = DefineType(ArrayType->BaseType, UniformOrBuffer); ++CurrentID; CodeGen.OpTypeRuntimeArray(CurrentID, elementTypeID); TypeNameToID[IndexName] = CurrentID; IDInfos[CurrentID] = IDInfo::CreateIDInfoForTypeofValue(CurrentID, Type, UniformOrBuffer); RetID = CurrentID; } if (UniformOrBuffer != 0) { int Stride = GetSize(ArrayType->BaseType.Ptr(), UniformOrBuffer); CodeGen.OpDecorate(RetID, Decoration::ArrayStride, Stride); } } if (auto StructType = dynamic_cast(Type.Ptr())) { String IndexName = Type->ToString(); if (UniformOrBuffer != 0) IndexName = IndexName + "#" + UniformOrBuffer; if (TypeNameToID.ContainsKey(IndexName)) return TypeNameToID[IndexName]; List memberIDList; for (auto & member : StructType->Members) memberIDList.Add(DefineType(member.Type, UniformOrBuffer)); ++CurrentID; CodeGen.OpTypeStruct(CurrentID, memberIDList); TypeNameToID[IndexName] = CurrentID; IDInfos[CurrentID] = IDInfo::CreateIDInfoForTypeofValue(CurrentID, Type, UniformOrBuffer); RetID = CurrentID; //generate decoration for struct layout if (UniformOrBuffer != 0) { int Offset = 0; int Index = 0; for (auto & MemberTypeID : memberIDList) { RefPtr MemberType = IDInfos[MemberTypeID]().GetILType(); int BaseAlignment = GetBaseAlignment(MemberType.Ptr(), UniformOrBuffer); //round up to baseAlignment if (Offset % BaseAlignment) Offset += BaseAlignment - Offset % BaseAlignment; AddInstrMemberDecorate(RetID, Index, Decoration::Offset, Offset); if (MemberType->IsFloatMatrix()) { AddInstrMemberDecorate(RetID, Index, Decoration::ColMajor); AddInstrMemberDecorate(RetID, Index, Decoration::MatrixStride, 16); } Offset += GetSize(MemberType.Ptr(), UniformOrBuffer); Index++; } } //generate debug information CodeGen.OpName(RetID, Type->ToString()); int index = 0; for (auto &member : StructType->Members) { CodeGen.OpMemberName(RetID, index, member.FieldName); index++; } } if (auto BasicType = dynamic_cast(Type.Ptr())) { if (TypeNameToID.ContainsKey(Type->ToString())) return TypeNameToID[Type->ToString()]; RetID = DefineBasicType(Type); } return RetID; } int DefineTypePointer(RefPtr Type, StorageClass store, int UniformOrBuffer = 0) { String PointerName = Type->ToString() + "$" + StorageClassToString(store) + "#" + UniformOrBuffer; if (TypeStorageToTypePointerID.ContainsKey(PointerName)) return TypeStorageToTypePointerID[PointerName](); int basetypeID = DefineType(Type, UniformOrBuffer); ++CurrentID; CodeGen.OpTypePointer(CurrentID, store, basetypeID); TypeStorageToTypePointerID[PointerName] = CurrentID; IDInfos[CurrentID] = IDInfo::CreateIDInfoForTypeofPointer(CurrentID, Type, basetypeID, store); return CurrentID; } int AddInstrTypeFunction(CompiledFunction * func, const List> & argTypes, RefPtr returnType) { int returnTypeID = DefineType(returnType); for (auto & arg : argTypes) DefineTypePointer(arg, StorageClass::Function); int functionTypeID = ++CurrentID; List argIDList; for (auto & arg : argTypes) argIDList.Add(DefineTypePointer(arg, StorageClass::Function)); CodeGen.OpTypeFunction(functionTypeID, returnTypeID, argIDList); IDInfos[functionTypeID] = IDInfo::CreateIDInfoForFunction(functionTypeID, func); if (func) FunctionNameToFunctionTypeID[func->Name] = functionTypeID; else FunctionNameToFunctionTypeID["main"] = functionTypeID; return functionTypeID; } int AddInstrConstantBool(int value) { if (Dictionary_ConstantBoolToID.ContainsKey(value != 0)) return Dictionary_ConstantBoolToID[value != 0].GetValue(); auto Type = GetTypeFromString("bool"); int typeID = DefineType(Type); ++CurrentID; CodeGen.OpConstantBool(typeID, CurrentID, value != 0); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue(CurrentID, Type, 0, typeID); Dictionary_ConstantBoolToID[value != 0] = CurrentID; return CurrentID; } int AddInstrConstantFloat(float f) { auto Type = GetTypeFromString("float"); int typeID = DefineType(Type); ++CurrentID; CodeGen.OpConstantFloat(CurrentID, typeID, f); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue(CurrentID, Type, 0, typeID); return CurrentID; } int AddInstrConstantInt(int i) { auto Type = GetTypeFromString("int"); if (Dictionary_ConstantIntToID.ContainsKey(i)) return Dictionary_ConstantIntToID[i].GetValue(); int typeID = DefineType(Type); ++CurrentID; CodeGen.OpConstantInt(CurrentID, typeID, i); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue(CurrentID, Type, 0, typeID); Dictionary_ConstantIntToID[i] = CurrentID; return CurrentID; } int AddInstrConstantUInt(unsigned int i) { auto Type = GetTypeFromString("uint"); if (Dictionary_ConstantUIntToID.ContainsKey(i)) return Dictionary_ConstantUIntToID[i].GetValue(); int typeID = DefineType(Type); ++CurrentID; CodeGen.OpConstantUInt(CurrentID, typeID, i); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue(CurrentID, Type, 0, typeID); Dictionary_ConstantUIntToID[i] = CurrentID; return CurrentID; } int AddInstrConstantCompositeFloat(float *f, int len) { RefPtr Type; if (len == 2) Type = GetTypeFromString("vec2"); else if (len == 3) Type = GetTypeFromString("vec3"); else if (len == 4) Type = GetTypeFromString("vec4"); else throw InvalidOperationException("Invalid type in AddInstrConstantCompositeFloat(): vec"+len); int typeID = DefineType(Type); List elementIDs; for (int i = 0; i < len; i++) elementIDs.Add(AddInstrConstantFloat(f[i])); ++CurrentID; CodeGen.OpConstantComposite(CurrentID, typeID, elementIDs); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue(CurrentID, Type, 0, typeID); return CurrentID; } int AddInstrConstantCompositeInt(int *v, int len) { RefPtr Type; if (len == 2) Type = GetTypeFromString("ivec2"); else if (len == 3) Type = GetTypeFromString("ivec3"); else if (len == 4) Type = GetTypeFromString("ivec4"); else throw InvalidOperationException("Invalid type in AddInstrConstantCompositeInt(): ivec" + len); int typeID = DefineType(Type); List elementIDs; for (int i = 0; i < len; i++) elementIDs.Add(AddInstrConstantInt(v[i])); ++CurrentID; CodeGen.OpConstantComposite(CurrentID, typeID, elementIDs); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue(CurrentID, Type, 0, typeID); return CurrentID; } int AddInstrConstantCompositeUInt(unsigned int *v, int len) { RefPtr Type; if (len == 2) Type = GetTypeFromString("uvec2"); else if (len == 3) Type = GetTypeFromString("uvec3"); else if (len == 4) Type = GetTypeFromString("uvec4"); else throw InvalidOperationException("Invalid type in AddInstrConstantCompositeUInt(): uvec" + len); int typeID = DefineType(Type); List elementIDs; for (int i = 0; i < len; i++) elementIDs.Add(AddInstrConstantUInt(*(v + i))); ++CurrentID; CodeGen.OpConstantComposite(CurrentID, typeID, elementIDs); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue(CurrentID, Type, 0, typeID); return CurrentID; } int AddInstrConstantMatrix(float *f, int n) { RefPtr Type; if (n == 3) Type = GetTypeFromString("mat3"); else if (n == 4) Type = GetTypeFromString("mat4"); else throw InvalidOperationException("Invalid type in AddInstrConstantMatrix(): mat" + n); int typeID = DefineType(Type); List vectorIDs; for (int i = 0; i < n; i++) vectorIDs.Add(AddInstrConstantCompositeFloat(f + (i * n), n)); ++CurrentID; CodeGen.OpConstantComposite(CurrentID, typeID, vectorIDs); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue(CurrentID, Type, 0, typeID); return CurrentID; } int AddInstrCompositeConstruct(ILOperand* op, RefPtr Type, List Arguments) // return ID of this instruction { int typeID = DefineType(Type); ++CurrentID; CodeGen.OpCompositeConstruct(CurrentID, typeID, Arguments); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue(CurrentID, Type, op, typeID); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrCompositeExtract(int ID, RefPtr baseType, int index) { int baseTypeID = DefineType(baseType); ++CurrentID; CodeGen.OpCompositeExtract(CurrentID, baseTypeID, ID, index); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue(CurrentID, baseType, 0, baseTypeID); return CurrentID; } int AddInstrCompositeInsert(RefPtr Type, int ID, int index, int op) { //ID[index] = op int typeID = DefineType(Type); ++CurrentID; CodeGen.OpCompositeInsert(CurrentID, typeID, op, ID, index); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue(CurrentID, Type, 0, typeID); return CurrentID; } int AddInstrExtInst(ILOperand* op, RefPtr Type, int instrNumber, List Arguments) { int typeID = DefineType(Type); ++CurrentID; CodeGen.OpExtInst(CurrentID, typeID, instrNumber, Arguments); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue(CurrentID, Type, op, typeID); UpdateValue(op, CurrentID); return CurrentID; } void AddInstrStore(ILOperand *op, int op0, int op1) { CodeGen.OpStore(op0, op1); UpdateValue(op, op1); return; } int AddInstrVariableDeclaration(ILOperand *op, RefPtr typeIL, StorageClass store, String DebugName = "", int UniformOrBuffer = 0) { int typeID = DefineTypePointer(typeIL, store, UniformOrBuffer); ++CurrentID; CodeGen.OpVariable(CurrentID, typeID, store); UpdateVariable(op, CurrentID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForPointer(CurrentID, op, typeID, typeIL, IDInfos[typeID]().GetBaseTypeID(), store); //Debug Information CodeGen.OpName(CurrentID, DebugName!=""? DebugName : (op?op->Name:"")); return CurrentID; } int AddInstrAccessChain_VectorMember(ILOperand *op, int ID, int indexID, int index) { String variableName = ""; if (index == -1 && indexID == -1) throw InvalidOperationException("indexID=-1 && index=-1 in AddInstrAccessChain_VectorMember()"); if (indexID == -1) { //indexID == -1 && index != -1 indexID = AddInstrConstantInt(index); } if (index != -1) { variableName = IDInfos[ID]().GetVariableName() + "[" + index + "]"; } RefPtr TypeIL = IDInfos[ID]().GetILType(); RefPtr memberTypeIL = nullptr; if (TypeIL->IsFloatMatrix()) { if (TypeIL->ToString() == "mat3") memberTypeIL = GetTypeFromString("vec3"); else if (TypeIL->ToString() == "mat4") memberTypeIL = GetTypeFromString("vec4"); } else if (TypeIL->IsFloatVector()) { memberTypeIL = GetTypeFromString("float"); } else if (TypeIL->IsIntVector()) { memberTypeIL = GetTypeFromString("int"); } else if (TypeIL->IsUIntVector()) { memberTypeIL = GetTypeFromString("uint"); } else throw InvalidOperationException("invalid operand type for access chain: " + TypeIL->ToString()); int memberTypeID = DefineTypePointer(memberTypeIL, IDInfos[ID]().GetStorageClass()); ++CurrentID; /*FunctionBody << LR"(%)" << CurrentID << LR"( = OpAccessChain %)" << memberTypeID << LR"( %)" << ID << LR"( %)" << indexID << EndLine;*/ CodeGen.OpAccessChain(CurrentID, memberTypeID, ID, indexID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForPointer( CurrentID, op, memberTypeID, memberTypeIL, IDInfos[memberTypeID]().GetBaseTypeID(), IDInfos[ID]().GetStorageClass() ); UpdateVariable(op, CurrentID); return CurrentID; } int AddInstrAccessChain_StructMember(ILOperand *op, int ID, int indexID, int index) { if (indexID == -1) indexID = AddInstrConstantInt(index); RefPtr Type = IDInfos[ID]().GetILType(); ILStructType * structType = dynamic_cast(Type.Ptr()); RefPtr memberBasetypeIL = structType->Members[index].Type; int memberTypeID = DefineTypePointer(memberBasetypeIL, IDInfos[ID]().GetStorageClass()); ++CurrentID; /* FunctionBody << LR"(%)" << CurrentID << LR"( = OpAccessChain %)" << memberTypeID << LR"( %)" << ID << LR"( %)" << indexID << EndLine; */ CodeGen.OpAccessChain(CurrentID, memberTypeID, ID, indexID); UpdateVariable(op, CurrentID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForPointer( CurrentID, op, memberTypeID, memberBasetypeIL, IDInfos[memberTypeID]().GetBaseTypeID(), IDInfos[ID]().GetStorageClass() ); return CurrentID; } int AddInstrAccessChain_StructMember(ILOperand *op, String memberName) { int structID = FindVariableID(op); ILStructType* structIL = dynamic_cast(IDInfos[structID]().GetILType().Ptr()); if (!structIL) throw InvalidProgramException("can not convert to ILStruct in AddInstrAccessChain_StructMember()"); int index = structIL->Members.FindFirst([&](ILStructType::ILStructField member) { return member.FieldName == memberName; }); int indexID = AddInstrConstantInt(index); return AddInstrAccessChain_StructMember(op, structID, indexID, index); } int AddInstrAccessChain_StructMember(ILOperand *op, int structID, String memberName) { ILStructType* structIL = dynamic_cast(IDInfos[structID]().GetILType().Ptr()); if (!structIL) throw InvalidProgramException("can not convert to ILStruct in AddInstrAccessChain_StructMember()"); int index = structIL->Members.FindFirst([&](ILStructType::ILStructField member) { return member.FieldName == memberName; }); int indexID = AddInstrConstantInt(index); return AddInstrAccessChain_StructMember(op, structID, indexID, index); } int AddInstrAccessChain_ArrayMember(ILOperand *op, RefPtr Type, int ID, int indexID) { if (!Type) throw InvalidProgramException("empty type in AddInstrAccessChain_ArrayMember()"); auto arrayType = dynamic_cast(Type.Ptr()); int baseTypeID = DefineTypePointer(arrayType->BaseType, IDInfos[ID]().GetStorageClass()); //it's a pointer ++CurrentID; CodeGen.OpAccessChain(CurrentID, baseTypeID, ID, indexID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForPointer( CurrentID, op, baseTypeID, arrayType->BaseType, IDInfos[baseTypeID]().GetBaseTypeID(), IDInfos[ID]().GetStorageClass() ); UpdateVariable(op, CurrentID); return CurrentID; } int AddInstrLoad(int variableID, MemoryAccess ma) { ++CurrentID; RefPtr Type = IDInfos[variableID]().GetILType(); int TypeID = IDInfos[variableID]().GetBaseTypeID(); CodeGen.OpLoad(CurrentID, TypeID, variableID, ma); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, Type, 0, TypeID); return CurrentID; } int AddInstrLoad(ILOperand *op, ILOperand *targetOp, MemoryAccess ma) { int targetID = FindVariableID(targetOp); if (targetID == -1) return -1; int typeID = IDInfos[targetID]().GetBaseTypeID(); ++CurrentID; CodeGen.OpLoad(CurrentID, typeID, targetID, ma); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[targetID]().GetILType(), op, typeID); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrINotEqual(int id0, int id1) { RefPtr typeIL = GetTypeFromString("bool"); int typeID = DefineType(typeIL); ++CurrentID; CodeGen.OpINotEqual(CurrentID, typeID, id0, id1); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, typeIL, 0, typeID ); return CurrentID; } int AddInstrTexture( ILOperand *op, int textureID, int coordinateID, ExecutionModel currentExecutionModel, int Bias = -1, int GradX = -1, int GradY = -1) { RefPtr typeIL = GetTypeFromString("vec4"); int typeID = DefineType(typeIL); ++CurrentID; if (currentExecutionModel == ExecutionModel::Fragment && GradX == -1) { //implicit LOD CodeGen.OpImageSampleImplicitLod(CurrentID, typeID, textureID, coordinateID, Bias); } else { //explicit LOD int zeroID = AddInstrConstantInt(0); CodeGen.OpImageSampleExplicitLod(CurrentID, typeID, textureID, coordinateID, zeroID, Bias, GradX, GradY); } IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, typeIL, op, typeID ); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrTextureShadow( ILOperand *op, int textureID, int coordinateID, ExecutionModel currentExecutionModel) { RefPtr typeIL = GetTypeFromString("vec4"); int typeID = DefineType(typeIL); int veclen = IDInfos[coordinateID]().GetILType()->GetVectorSize(); int DrefID = AddInstrCompositeExtract(coordinateID, GetTypeFromString("float"), veclen-1); ++CurrentID; if (currentExecutionModel == ExecutionModel::Fragment) { //implicit LOD CodeGen.OpImageSampleDrefImplicitLod(CurrentID, typeID, textureID, coordinateID, DrefID); } else { //explicit LOD int zeroID = AddInstrConstantInt(0); CodeGen.OpImageSampleDrefExplicitLod(CurrentID, typeID, textureID, coordinateID, DrefID, zeroID); } IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, typeIL, op, typeID ); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrTexture2DShadowProj( ILOperand *op, int textureID, int coordinateID, ////coordinateID: u, v, depth, q ExecutionModel currentExecutionModel) { RefPtr typeIL = GetTypeFromString("vec4"); int typeID = DefineType(typeIL); int DrefID = AddInstrCompositeExtract(coordinateID, GetTypeFromString("float"), 2); int qID = AddInstrCompositeExtract(coordinateID, GetTypeFromString("float"), 3); int NewCoordinateID = AddInstrCompositeInsert(IDInfos[coordinateID]().GetILType(), coordinateID, 2, qID); ++CurrentID; if (currentExecutionModel == ExecutionModel::Fragment) { //implicit LOD CodeGen.OpImageSampleProjDrefImplicitLod(CurrentID, typeID, textureID, NewCoordinateID, DrefID); } else { //explicit LOD int zeroID = AddInstrConstantInt(0); CodeGen.OpImageSampleProjDrefExplicitLod(CurrentID, typeID, textureID, NewCoordinateID, DrefID, zeroID); } IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, typeIL, op, typeID ); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrConvertSToF(int destTypeID, int operandID) { ++CurrentID; CodeGen.OpConvertSToF(CurrentID, destTypeID, operandID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[destTypeID]().GetILType(), 0, destTypeID ); return CurrentID; } int AddInstrConvertFToS(int destTypeID, int operandID) { ++CurrentID; CodeGen.OpConvertFToS(CurrentID, destTypeID, operandID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[destTypeID]().GetILType(), 0, destTypeID ); return CurrentID; } int AddInstrConvertUToF(int destTypeID, int operandID) { ++CurrentID; CodeGen.OpConvertUToF(CurrentID, destTypeID, operandID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[destTypeID]().GetILType(), 0, destTypeID ); return CurrentID; } int AddInstrConvertFToU(int destTypeID, int operandID) { ++CurrentID; CodeGen.OpConvertFToU(CurrentID, destTypeID, operandID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[destTypeID]().GetILType(), 0, destTypeID ); return CurrentID; } int AddInstrConvertSToU(int destTypeID, int operandID) { ++CurrentID; CodeGen.OpBitCast(CurrentID, destTypeID, operandID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[destTypeID]().GetILType(), 0, destTypeID ); return CurrentID; } int AddInstrConvertUToS(int destTypeID, int operandID) { ++CurrentID; CodeGen.OpBitCast(CurrentID, destTypeID, operandID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[destTypeID]().GetILType(), 0, destTypeID ); return CurrentID; } int AddInstrBitcast(int destTypeID, int operandID) { ++CurrentID; CodeGen.OpBitCast(CurrentID, destTypeID, operandID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[destTypeID]().GetILType(), 0, destTypeID ); return CurrentID; } int AddInstrFunctionCall(ILOperand *op, int typeID, int funcID, List &args) { ++CurrentID; CodeGen.OpFunctionCall(CurrentID, typeID, funcID, args); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[typeID]().GetILType(), op, typeID ); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrFnegate(ILOperand *op, int typeID, int valueID) { ++CurrentID; CodeGen.OpFNegate(CurrentID, typeID, valueID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[typeID]().GetILType(), op, typeID ); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrSnegate(ILOperand *op, int typeID, int valueID) { ++CurrentID; CodeGen.OpSNegate(CurrentID, typeID, valueID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[typeID]().GetILType(), op, typeID ); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrNot(ILOperand *op, int typeID, int valueID) { ++CurrentID; CodeGen.OpNot(CurrentID, typeID, valueID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[typeID]().GetILType(), op, typeID ); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrLogicalNot(ILOperand *op, int typeID, int valueID) { ++CurrentID; CodeGen.OpLogicalNot(CurrentID, typeID, valueID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[typeID]().GetILType(), op, typeID ); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrMatrixTimesScalar(ILOperand *op, int ID0, int ID1) { ++CurrentID; CodeGen.OpMatrixTimesScalar(CurrentID, IDInfos[ID0]().GetTypeID(), ID0, ID1); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[ID0]().GetILType(), op, IDInfos[ID0]().GetTypeID() ); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrVectorTimesMatrix(ILOperand *op, int ID0, int ID1) { ++CurrentID; CodeGen.OpVectorTimesMatrix(CurrentID, IDInfos[ID0]().GetTypeID(), ID0, ID1); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[ID0]().GetILType(), op, IDInfos[ID0]().GetTypeID() ); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrMatrixTimesVector(ILOperand *op, int ID0, int ID1) { ++CurrentID; CodeGen.OpMatrixTimesVector(CurrentID, IDInfos[ID1]().GetTypeID(), ID0, ID1); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[ID1]().GetILType(), op, IDInfos[ID1]().GetTypeID() ); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrMatrixTimesMatrix(ILOperand *op, int ID0, int ID1) { ++CurrentID; CodeGen.OpMatrixTimesMatrix(CurrentID, IDInfos[ID0]().GetTypeID(), ID0, ID1); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[ID0]().GetILType(), op, IDInfos[ID0]().GetTypeID() ); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrBinaryInstr(ILOperand *op, RefPtr instrType, const String &opStr, int ID0, int ID1) { int instrTypeID = DefineType(instrType); CurrentID++; CodeGen.OpBinaryInstr(CurrentID, opStr, instrTypeID, ID0, ID1); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue(CurrentID, instrType, op, instrTypeID); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrMulAdd(int operandID, float mul, float add) { //return 0.5*op+0.5 int typeID = IDInfos[operandID]().GetTypeID(); //shoudl be float RefPtr typeIL = IDInfos[operandID]().GetILType(); int mul_ID = AddInstrConstantFloat(mul); mul_ID = ConvertBasicType(mul_ID, IDInfos[mul_ID]().GetILType(), IDInfos[operandID]().GetILType()); int add_ID = AddInstrConstantFloat(add); add_ID = ConvertBasicType(add_ID, IDInfos[add_ID]().GetILType(), IDInfos[operandID]().GetILType()); ++CurrentID; //ctx.FunctionBody << LR"(%)" << ctx.CurrentID << LR"( = OpFMul %)" << typeID << LR"( %)" << operandID << LR"( %)" << mul_ID << EndLine; CodeGen.OpFMul(CurrentID, typeID, operandID, mul_ID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue(CurrentID, typeIL, 0, typeID); ++CurrentID; //ctx.FunctionBody << LR"(%)" << ctx.CurrentID << LR"( = OpFAdd %)" << typeID << LR"( %)" << ctx.CurrentID-1 << LR"( %)" << add_ID << EndLine; CodeGen.OpFAdd(CurrentID, typeID, CurrentID - 1, add_ID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue(CurrentID, typeIL, 0, typeID); return CurrentID; } int ConvertBasicType(int operandID, RefPtr srcType, RefPtr dstType) { String srcStr = srcType->ToString(); String dstStr = dstType->ToString(); if (srcStr == dstStr) return operandID; if (dstType->IsBool()) { if (srcType->IsInt()) return AddInstrINotEqual(operandID, AddInstrConstantInt(0)); if (srcType->IsUInt()) return AddInstrINotEqual(operandID, AddInstrConstantUInt(0)); throw NotImplementedException("only convert int to bool in ConvertBasicType(): " + srcType->ToString()); } //from column vector to column vector bool srcIsColumnVector = (srcType->IsFloat() || srcType->IsFloatVector() || srcType->IsIntegral()) && !srcType->IsFloatMatrix(); bool dstIsColumnVector = (dstType->IsFloat() || dstType->IsFloatVector() || dstType->IsIntegral()) && !dstType->IsFloatMatrix(); if (srcIsColumnVector && dstIsColumnVector && srcType->GetVectorSize() != dstType->GetVectorSize()) { //make src ID have length equal to destType RefPtr elementType; if (srcType->IsInt() || srcType->IsUInt()) elementType = new ILBasicType(ILBaseType::Int); else if (srcType->IsUInt() || srcType->IsUIntVector()) elementType = new ILBasicType(ILBaseType::UInt); else elementType = new ILBasicType(ILBaseType::Float); List arguments; int extraID = -1; if (srcType->GetVectorSize() == 1) { for (int i = 0; i < dstType->GetVectorSize(); i++) { arguments.Add(operandID); } } else { for (int i = 0; i < dstType->GetVectorSize(); i++) { if (i >= srcType->GetVectorSize()) { if (i == srcType->GetVectorSize()) { if (srcType->IsInt() || srcType->IsIntVector()) extraID = AddInstrConstantInt(0); else if (srcType->IsUInt() || srcType->IsUIntVector()) extraID = AddInstrConstantUInt(0); else extraID = AddInstrConstantFloat(0.0); } arguments.Add(extraID); } else { if (srcType->GetVectorSize() == 1) arguments.Add(operandID); else arguments.Add(AddInstrCompositeExtract(operandID, elementType, i)); } } } int BaseType = (dynamic_cast(srcType.Ptr()))->Type; int newBaseType = BaseType & ~15; // int or float newBaseType += dstType->GetVectorSize() - 1; RefPtr newSrcType = new ILBasicType(ILBaseType(newBaseType)); operandID = AddInstrCompositeConstruct(0, newSrcType, arguments); srcType = newSrcType; } srcStr = srcType->ToString(); dstStr = dstType->ToString(); if (srcStr == dstStr) return operandID; //from scalar to matrix if ((srcStr == "float" || srcStr == "int" || srcStr == "uint") && dstType->IsFloatMatrix()) { throw NotImplementedException("scalar to matrix conversion is not supported yet."); } //from matrix to matrix if (srcType->IsFloatMatrix() && dstType->IsFloatMatrix()) { throw NotImplementedException("matrix to matrix conversion is not supported yet."); } if (srcType->GetVectorSize() != dstType->GetVectorSize()) { throw NotImplementedException("can not convert " + srcType->ToString() + " to " + dstType->ToString()); } //component-wise conversion bool srcFloat = srcType->IsFloat() || srcType->IsFloatVector(); bool srcInt = srcType->IsInt() || srcType->IsIntVector(); bool srcUint = srcType->IsUInt() || srcType->IsUIntVector(); bool dstFloat = dstType->IsFloat() || dstType->IsFloatVector(); bool dstInt = dstType->IsInt() || dstType->IsIntVector(); bool dstUint = dstType->IsUInt() || dstType->IsUIntVector(); int destTypeID = DefineType(dstType); if (srcInt && dstFloat) return AddInstrConvertSToF(destTypeID, operandID); else if (srcFloat && dstInt) return AddInstrConvertFToS(destTypeID, operandID); else if (srcUint && dstFloat) return AddInstrConvertUToF(destTypeID, operandID); else if (srcFloat && dstUint) return AddInstrConvertFToU(destTypeID, operandID); else if (srcInt && dstUint) return AddInstrConvertSToU(destTypeID, operandID); else if (srcUint && dstInt) return AddInstrConvertUToS(destTypeID, operandID); else throw NotImplementedException("can not convert " + srcType->ToString() + " to " + dstType->ToString()); } void AddInstrSelectionMerge(int MergeLabel) { CodeGen.OpSelectionMerge(MergeLabel); } void AddInstrBranchConditional(int ID, int TrueLabel, int FalseLabel) { CodeGen.OpBranchConditional(ID, TrueLabel, FalseLabel); } void AddInstrLabel_AtFunctionBody(int Label) { CodeGen.OpLabel_AtFunctionBody(Label); } void AddInstrLabel_AtFunctionHeader(int Label) { CodeGen.OpLabel_AtFunctionHeader(Label); } void AddInstrBranch(int Target) { CodeGen.OpBranch(Target);; } int AddInstrPhi(ILOperand *op, int ID1, int Label1, int ID2, int Label2) { List branches; branches.Add(ID1); branches.Add(Label1); branches.Add(ID2); branches.Add(Label2); ++CurrentID; CodeGen.OpPhi(CurrentID, IDInfos[ID1]().GetTypeID(), branches); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, IDInfos[ID1]().GetILType(), op, IDInfos[ID1]().GetTypeID() ); UpdateValue(op, CurrentID); return CurrentID; } void AddInstrLoopMerge(int MergeLabel, int ContinueLabel) { CodeGen.OpLoopMerge(MergeLabel, ContinueLabel); } void AddInstrReturnValue(int operandID) { CodeGen.OpReturnValue(operandID); } void AddInstrKill() { CodeGen.OpKill(); } void AddInstrDecorate(int ID, Decoration deco, int ID1 = 0) { CodeGen.OpDecorate(ID, deco, ID1); } void AddInstrMemberDecorate(int ID, int index, Decoration deco, int ID1 = 0) { CodeGen.OpMemberDecorate(ID, index, deco, ID1); } void AddInstrFunction(int funcID, int returnTypeID, int funcTypeID, String funcName) { CodeGen.OpName(funcID, funcName); CodeGen.OpFunction(funcID, returnTypeID, funcTypeID); } void AddInstrFunctionParameter(ILOperand *op, int typeID, String DebugName) { int paramID = ++CurrentID; CodeGen.OpName(paramID, DebugName); CodeGen.OpFunctionParameter(paramID, typeID); IDInfos[paramID] = IDInfo::CreateIDInfoForPointer( paramID, op, typeID, IDInfos[typeID]().GetILType(), IDInfos[typeID]().GetBaseTypeID(), StorageClass::Function ); ParameterNameToID[op] = paramID; UpdateVariable(op, paramID); } void AddInstrReturn() { CodeGen.OpReturn(); } void AddInstrFunctionEnd() { CodeGen.OpFunctionEnd(); } void AddInstrEntryPoint(ExecutionModel EM, int entryID, const List& interfaceIDs) { CodeGen.OpEntryPoint(EM, entryID, interfaceIDs); } void AddInstrExecutionMode(int ID, ExecutionMode mode, int op1 = -1, int op2 = -1, int op3 = -1) { CodeGen.OpExecutionMode(ID, mode, op1, op2, op3); } int AddInstrDot(ILOperand *op, RefPtr typeIL, int ID0, int ID1) { int typeID = DefineType(typeIL); ++CurrentID; CodeGen.OpDot(CurrentID, typeID, ID0, ID1); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, typeIL, op, typeID ); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrTranspose(ILOperand *op, RefPtr typeIL, int ID) { int typeID = DefineType(typeIL); ++CurrentID; CodeGen.OpTranspose(CurrentID, typeID, ID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, typeIL, op, typeID ); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrDFdx(ILOperand *op, RefPtr typeIL, int ID) { int typeID = DefineType(typeIL); ++CurrentID; CodeGen.OpDPdx(CurrentID, typeID, ID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, typeIL, op, typeID ); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrDFdy(ILOperand *op, RefPtr typeIL, int ID) { int typeID = DefineType(typeIL); ++CurrentID; CodeGen.OpDPdy(CurrentID, typeID, ID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, typeIL, op, typeID ); UpdateValue(op, CurrentID); return CurrentID; } int AddInstrFwidth(ILOperand *op, RefPtr typeIL, int ID) { int typeID = DefineType(typeIL); ++CurrentID; CodeGen.OpFwidth(CurrentID, typeID, ID); IDInfos[CurrentID] = IDInfo::CreateIDInfoForValue( CurrentID, typeIL, op, typeID ); UpdateValue(op, CurrentID); return CurrentID; } void ProduceFunction() { CodeGen.ProduceFunction(); } List ProduceWordStream() { return CodeGen.ProduceWordStream(CurrentID); } String ProduceTextCode() { return CodeGen.ProduceTextCode(); } }; class SpirvModule { private: SpirVCodeGenContext ctx; ExecutionModel currentExecutionModel; List interfaceIDs; bool DepthReplacing; bool LocalSize; bool BufferImportOrExport; CompiledWorld * currentWorld; int GetOperandValue(ILOperand * op) { int id = -1; if (auto c = dynamic_cast(op)) { auto type = c->Type.Ptr(); if (type->IsFloat()) { id = ctx.AddInstrConstantFloat(c->FloatValues[0]); } else if (type->IsInt()) { id = ctx.AddInstrConstantInt(c->IntValues[0]); } else if (type->IsUInt()) { id = ctx.AddInstrConstantUInt(*((unsigned int*)(&c->IntValues[0]))); } else if (auto baseType = dynamic_cast(type)) { if (baseType->Type == ILBaseType::Float2) { id = ctx.AddInstrConstantCompositeFloat(c->FloatValues, 2); } else if (baseType->Type == ILBaseType::Float3) { id = ctx.AddInstrConstantCompositeFloat(c->FloatValues, 3); } else if (baseType->Type == ILBaseType::Float4) { id = ctx.AddInstrConstantCompositeFloat(c->FloatValues, 4); } else if (baseType->Type == ILBaseType::Float3x3) { id = ctx.AddInstrConstantMatrix(c->FloatValues, 3); } else if (baseType->Type == ILBaseType::Float4x4) { id = ctx.AddInstrConstantMatrix(c->FloatValues, 4); } else if (baseType->Type == ILBaseType::Int2) { id = ctx.AddInstrConstantCompositeInt(c->IntValues, 2); } else if (baseType->Type == ILBaseType::Int3) { id = ctx.AddInstrConstantCompositeInt(c->IntValues, 3); } else if (baseType->Type == ILBaseType::Int4) { id = ctx.AddInstrConstantCompositeInt(c->IntValues, 4); } else if (baseType->Type == ILBaseType::UInt2) { id = ctx.AddInstrConstantCompositeUInt((unsigned int*)c->IntValues, 2); } else if (baseType->Type == ILBaseType::UInt3) { id = ctx.AddInstrConstantCompositeUInt((unsigned int*)c->IntValues, 3); } else if (baseType->Type == ILBaseType::UInt4) { id = ctx.AddInstrConstantCompositeUInt((unsigned int*)c->IntValues, 4); } else if (baseType->Type == ILBaseType::Bool) { id = ctx.AddInstrConstantBool(c->IntValues[0]); } } else throw InvalidOperationException("Illegal constant."); } else if (auto instr = dynamic_cast(op)) { id = ctx.FindValueID(op); if (id == -1) { //need to load it from storage id = ctx.AddInstrLoad(op, op, MemoryAccess::None); } } else throw InvalidOperationException("Unsupported operand type."); return id; } int GetOperandPointer(ILOperand * op) { int id = -1; //RefPtr result_type; if (auto c = dynamic_cast(op)) { int valueID = GetOperandValue(op); id = ctx.AddInstrVariableDeclaration(op, op->Type, StorageClass::Function); ctx.AddInstrStore(op, id, valueID); } else if (auto instr = dynamic_cast(op)) { id = ctx.FindVariableID(op); if (id == -1) { int valueID = ctx.FindValueID(op); if (valueID == -1) throw InvalidOperationException("can not find variable ID in Get OperandPointer(): " + op->ToString()); id = ctx.AddInstrVariableDeclaration(op, instr->Type, StorageClass::Function); ctx.AddInstrStore(op, id, valueID); } } else throw InvalidOperationException("Unsupported operand type."); return id; } void PrintAllocVarInstr(AllocVarInstruction * instr, StorageClass store) { if (dynamic_cast(instr->Size.Ptr())) { ctx.AddInstrVariableDeclaration((ILOperand*)instr, instr->Type, store); } else throw InvalidProgramException("size operand of allocVar instr is not an intermediate."); } Dictionary GLSLstd450InstructionSet = GenGLSLstd450InstructionSet(); void PrintCallInstr(CallInstruction * instr) // return ID of this instruction { String callName = GetFuncOriginalName(instr->Function); //------------------------- texture instructions ------------------------- if (callName == "texture") { if (instr->Arguments[0]->Type->IsNonShadowTexture()) { if (instr->Arguments[0]->Type->ToString() == "sampler2D") { //*** no bias!!! //__intrinsic vec4 texture(sampler2D tex, vec2 coord); ctx.AddInstrTexture( (ILOperand*)instr, GetOperandValue(instr->Arguments[0].Ptr()), GetOperandValue(instr->Arguments[1].Ptr()), currentExecutionModel ); return; } else if (instr->Arguments[0]->Type->ToString() == "samplerCube") { if (instr->Arguments.Count() == 2) { //__intrinsic vec4 texture(samplerCube tex, vec3 coord); ctx.AddInstrTexture( (ILOperand*)instr, GetOperandValue(instr->Arguments[0].Ptr()), GetOperandValue(instr->Arguments[1].Ptr()), currentExecutionModel ); return; } else { //__intrinsic vec4 texture(samplerCube tex, vec3 coord, float bias); ctx.AddInstrTexture( (ILOperand*)instr, GetOperandValue(instr->Arguments[0].Ptr()), GetOperandValue(instr->Arguments[1].Ptr()), currentExecutionModel, GetOperandValue(instr->Arguments[2].Ptr()) ); return; } } } else { //instr->Arguments[0]->Type->IsShadowTexture //__intrinsic float texture(sampler2DShadow tex, vec3 coord); //__intrinsic float texture(samplerCubeShadow tex, vec4 coord); ctx.AddInstrTextureShadow( (ILOperand*)instr, GetOperandValue(instr->Arguments[0].Ptr()), GetOperandValue(instr->Arguments[1].Ptr()), currentExecutionModel ); return; } } if (callName == "textureGrad") { //__intrinsic vec4 textureGrad(sampler2D tex, vec2 coord, vec2 dPdx, vec2 dPdy); //__intrinsic vec4 textureGrad(samplerCube tex, vec3 coord, vec3 dPdx, vec3 dPdy); ctx.AddInstrTexture( (ILOperand*)instr, GetOperandValue(instr->Arguments[0].Ptr()), GetOperandValue(instr->Arguments[1].Ptr()), currentExecutionModel, -1, //Bias GetOperandValue(instr->Arguments[2].Ptr()), GetOperandValue(instr->Arguments[3].Ptr()) ); return; } if (callName == "textureProj") { if (instr->Arguments[0]->Type->ToString() == "sampler2DShadow") { //__intrinsic float textureProj(sampler2DShadow tex, vec4 coord); ctx.AddInstrTexture2DShadowProj( (ILOperand*)instr, GetOperandValue(instr->Arguments[0].Ptr()), GetOperandValue(instr->Arguments[1].Ptr()), currentExecutionModel ); return; } } //------------------------- Dot Instruction ------------------------------ if (callName == "dot" && instr->Arguments.Count() == 2 && instr->Arguments[0]->Type->ToString() == instr->Arguments[1]->Type->ToString() && instr->Arguments[0]->Type->IsFloatVector() && !instr->Arguments[0]->Type->IsFloatMatrix()) { ctx.AddInstrDot( (ILOperand*)instr, instr->Type, GetOperandValue(instr->Arguments[0].Ptr()), GetOperandValue(instr->Arguments[1].Ptr()) ); return; } //------------------------- Transpose Instruction ------------------------------ if (callName == "transpose" && instr->Arguments.Count() == 1 && instr->Arguments[0]->Type->IsFloatMatrix()) { ctx.AddInstrTranspose((ILOperand*)instr, instr->Type, GetOperandValue(instr->Arguments[0].Ptr())); return; } //------------------------- Derivative Instruction ----------------------------- if (callName == "dFdx") { ctx.AddInstrDFdx((ILOperand*)instr, instr->Type, GetOperandValue(instr->Arguments[0].Ptr())); return; } else if (callName == "dFdy") { ctx.AddInstrDFdy((ILOperand*)instr, instr->Type, GetOperandValue(instr->Arguments[0].Ptr())); return; } else if (callName == "fwidth") { ctx.AddInstrFwidth((ILOperand*)instr, instr->Type, GetOperandValue(instr->Arguments[0].Ptr())); return; } //------------------------- user-defined instructions ------------------------- int funcID; if (ctx.FunctionNameToFunctionID.TryGetValue(instr->Function, funcID)) { RefPtr returnType = ctx.IDInfos[ ctx.FunctionNameToFunctionTypeID[instr->Function]() ]().GetFunc()->ReturnType; int typeID = ctx.DefineType(returnType); List args; for (auto & arg : instr->Arguments) { int valueID = GetOperandValue(arg.Ptr()); int paramID = ctx.AddInstrVariableDeclaration(0, arg->Type, StorageClass::Function, "param"); // the name of the parameter must be empty; or may conflict with non-param variables ctx.AddInstrStore(0, paramID, valueID); args.Add(paramID); } ctx.AddInstrFunctionCall((ILOperand*)instr, typeID, funcID, args); return; } //------------------------- ext-import instructions ----------------------------- List Arguments; for (auto & arg : instr->Arguments) { int valueID = GetOperandValue(arg.Ptr()); if (callName == "mix") { //the mix instruction in spirv only accept mix(vec_, vec_, vec_); //however, front end of SPIRE can accept mix(vec_, vec_, float); valueID = ctx.ConvertBasicType(valueID, arg->Type, instr->Type); } Arguments.Add(valueID); } if (GLSLstd450InstructionSet.ContainsKey(callName)) { ctx.AddInstrExtInst((ILOperand*)instr, instr->Type, GLSLstd450InstructionSet[callName](), Arguments); return; } //------------------------- built-in constructors ----------------------------- RefPtr dstType = GetTypeFromString(callName); if (dstType == nullptr) throw InvalidOperationException("can not call: " + callName); RefPtr dstBasicType = dstType; if (instr->Arguments.Count() > 1) { //composite for (auto & ID : Arguments) { auto argBasicType = dynamic_cast((ctx.IDInfos[ID]().GetILType()).Ptr()); if (argBasicType) if (argBasicType->IsIntegral() != dstType->IsIntegral()) { RefPtr argDstType = new ILBasicType(ILBaseType((dstBasicType->Type & ~15) + (argBasicType->Type & 15))); ID = ctx.ConvertBasicType(ID, ctx.IDInfos[ID]().GetILType(), argDstType); } } if (instr->Type->IsFloatMatrix() && (Arguments.Count() == 0 || (Arguments.Count() > 0 && ctx.IDInfos[Arguments[0]]().GetILType()->IsScalar()))) { //need to arrange scalars into vectors int n = (int)sqrt((float)instr->Type->GetVectorSize() + 1e-6); int diff = n * n - Arguments.Count(); if (diff > 0) for (int i = 0; i < diff; i++) Arguments.Add(ctx.AddInstrConstantFloat(0.0)); List newArguments; RefPtr vectorType = new ILBasicType(ILBaseType(ILBaseType::Float + n - 1)); for (int i = 0; i < n; i++) { List subArguments; for (int j = 0; j < n; j++) subArguments.Add(Arguments[i*n + j]); newArguments.Add(ctx.AddInstrCompositeConstruct(0, vectorType, subArguments)); } Arguments = newArguments; } ctx.AddInstrCompositeConstruct((ILOperand*)instr, instr->Type, Arguments); return; } else { //need conversion int ID = Arguments[0]; ID = ctx.ConvertBasicType(ID, ctx.IDInfos[ID]().GetILType(), dstType); ctx.UpdateValue((ILOperand*)instr, ID); return; } } void PrintUnaryInstr(UnaryInstruction * instr) { auto op0 = instr->Operand.Ptr(); if (instr->Is()) { ctx.AddInstrLoad((ILOperand*)instr, op0, MemoryAccess::None); return; } if (instr->Is()) instr->Type = GetTypeFromString("bool"); int op0ValueID = GetOperandValue(op0); op0ValueID = ctx.ConvertBasicType(op0ValueID, ctx.IDInfos[op0ValueID]().GetILType(), instr->Type); RefPtr op0ILType = ctx.IDInfos[op0ValueID]().GetILType(); if (instr->Is() || instr->Is() || instr->Is()) { ctx.UpdateValue((ILOperand*)instr, op0ValueID); return; } int instrTypeID = ctx.DefineType(instr->Type); if (instr->Is()) { if (op0ILType->IsFloat() || op0ILType->IsFloatVector()) ctx.AddInstrFnegate((ILOperand*)instr, instrTypeID, op0ValueID); else if (op0ILType->IsInt() || op0ILType->IsIntVector()) ctx.AddInstrSnegate((ILOperand*)instr, instrTypeID, op0ValueID); else if (op0ILType->IsUInt() || op0ILType->IsUIntVector()) throw InvalidOperationException("trying to negate a uint in PrintUnaryInstruction(): " + instr->ToString()); } else if (instr->Is()) ctx.AddInstrNot((ILOperand*)instr, instrTypeID, op0ValueID); else if (instr->Is()) ctx.AddInstrLogicalNot((ILOperand*)instr, instrTypeID, op0ValueID); else throw InvalidProgramException("unsupported unary instruction."); } void PrintBinaryInstr(BinaryInstruction * instr) { auto op0 = instr->Operands[0].Ptr(); auto op1 = instr->Operands[1].Ptr(); //-------------------------------------Store Instruction------------------------------------------ if (instr->Is()) { if (auto structType = dynamic_cast(op0->Type.Ptr())) { int op0ID = ctx.FindVariableID(op0); int op1ID = ctx.FindVariableID(op1); int index = 0; for (int i = 0; i < structType->Members.Count(); i++) { int indexID = ctx.AddInstrConstantInt(index); int dest = ctx.AddInstrAccessChain_StructMember(0, op0ID, indexID, index); int pSrc = ctx.AddInstrAccessChain_StructMember(0, op1ID, indexID, index); int vSrc = ctx.AddInstrLoad(pSrc, MemoryAccess::None); ctx.AddInstrStore(0, dest, vSrc); index++; } return; } int op0ID = ctx.FindVariableID(op0); // should be a pointer int op1ID = GetOperandValue(op1); op1ID = ctx.ConvertBasicType(op1ID, ctx.IDInfos[op1ID]().GetILType(), ctx.IDInfos[op0ID]().GetILType()); ctx.AddInstrStore(instr, op0ID, op1ID); //TO FIX return; } //----------------------------------Member Load Instruction--------------------------------------- if (instr->Is()) { int fatherID = GetOperandPointer(op0); if (op0->Type->IsVector()) { if (auto c = dynamic_cast(op1)) { //if op1 is constant, take that as index of vector int memberID = ctx.AddInstrAccessChain_VectorMember((ILOperand*)instr, fatherID, -1, c->IntValues[0]); int retID = ctx.AddInstrLoad(memberID, MemoryAccess::None); ctx.UpdateValue((ILOperand*)instr, retID); return; } else { //if op1 is not constant, compute it int memberID = ctx.AddInstrAccessChain_VectorMember((ILOperand*)instr, fatherID, GetOperandValue(op1), -1); int retID = ctx.AddInstrLoad(memberID, MemoryAccess::None); ctx.UpdateValue((ILOperand*)instr, retID); return; } } else if (auto structType = dynamic_cast(op0->Type.Ptr())) { if (auto c = dynamic_cast(op1)) { //index of struct must be constant int indexID = GetOperandValue(c); int memberID = ctx.AddInstrAccessChain_StructMember((ILOperand*)instr, fatherID, indexID, c->IntValues[0]); int retID = ctx.AddInstrLoad(memberID, MemoryAccess::None); ctx.UpdateValue((ILOperand*)instr, retID); return; } else throw InvalidOperationException("wrong: " + instr->ToString()); } else if (auto arrayType = dynamic_cast(op0->Type.Ptr())) { int memberID = ctx.AddInstrAccessChain_ArrayMember((ILOperand*)instr, op0->Type, fatherID, GetOperandValue(op1)); int retID = ctx.AddInstrLoad(memberID, MemoryAccess::None); ctx.UpdateValue((ILOperand*)instr, retID); return; } else throw InvalidOperationException("wrong op0 type for MemberLoadInstruction(): " + op0->Type->ToString()); } int ID0 = GetOperandValue(op0); int ID1 = GetOperandValue(op1); RefPtr ID0Type = ctx.IDInfos[ID0]().GetILType(); RefPtr ID1Type = ctx.IDInfos[ID1]().GetILType(); //----------------------------------Vec/Mat Multiplication--------------------------------------- if (instr->Is()) { //scalar X matrix or matrix X scalar if (ID0Type->IsScalar() && ID1Type->IsFloatMatrix() || ID1Type->IsScalar() && ID0Type->IsFloatMatrix()) { if (ID1Type->IsFloatMatrix()) Swap(ID0, ID1); //now ID0 is matrix, ID1 is scalar, ID1 = ctx.ConvertBasicType(ID1, ctx.IDInfos[ID1]().GetILType(), GetTypeFromString("float")); ctx.AddInstrMatrixTimesScalar((ILOperand*)instr, ID0, ID1); return; } //vector X matrix if (ID0Type->IsFloatVector() && !ID0Type->IsFloatMatrix() && ID1Type->IsFloatMatrix()) { ctx.AddInstrVectorTimesMatrix((ILOperand*)instr, ID0, ID1); return; } //matrix X vector if (ID1Type->IsFloatVector() && !ID1Type->IsFloatMatrix() && ID0Type->IsFloatMatrix()) { ctx.AddInstrMatrixTimesVector((ILOperand*)instr, ID0, ID1); return; } //matrix X matrix if (ID0Type->IsFloatMatrix() && ID1Type->IsFloatMatrix()) { ctx.AddInstrMatrixTimesMatrix((ILOperand*)instr, ID0, ID1); return; } } //---------------------------------Boolean-Related Instruction--------------------------------------- bool ResultIsLogical = instr->Is() || instr->Is() || instr->Is() || instr->Is() || instr->Is() || instr->Is() || instr->Is() || instr->Is(); if (dynamic_cast(instr->Type.Ptr())->Type == 0) { instr->Type = ID0Type; } if (ResultIsLogical) { RefPtr OperandType = nullptr; if (instr->Is() || instr->Is()) { OperandType = GetTypeFromString("bool"); } else { if (ID0Type->IsFloat() || ID1Type->IsFloat()) OperandType = GetTypeFromString("float"); else if (ID0Type->IsUInt() || ID1Type->IsUInt()) OperandType = GetTypeFromString("uint"); else if (ID0Type->IsInt() || ID1Type->IsInt()) OperandType = GetTypeFromString("int"); } ID0 = ctx.ConvertBasicType(ID0, ID0Type, OperandType); ID1 = ctx.ConvertBasicType(ID1, ID1Type, OperandType); instr->Type = GetTypeFromString("bool"); ID0Type = ctx.IDInfos[ID0]().GetILType(); ID1Type = ctx.IDInfos[ID1]().GetILType(); } else { ID0 = ctx.ConvertBasicType(ID0, ID0Type, instr->Type); ID1 = ctx.ConvertBasicType(ID1, ID1Type, instr->Type); ID0Type = ctx.IDInfos[ID0]().GetILType(); ID1Type = ctx.IDInfos[ID1]().GetILType(); } //--------------------------------------Get Binary Operator String--------------------------------------- String opStr; bool needPrefix = false; bool Signed = false; if (instr->Is()) { opStr = "Mu"; needPrefix = true; } else if (instr->Is()) { opStr = "Add"; needPrefix = true; } else if (instr->Is()) { opStr = "Div"; needPrefix = true; Signed = true; } else if (instr->Is()) { opStr = "Sub"; needPrefix = true; } else if (instr->Is()) { opStr = "Mod"; needPrefix = true; Signed = true; } else if (instr->Is()) { opStr = "ShiftLeftLogica"; } else if (instr->Is()) { if (ID0Type->IsUInt() || ID0Type->IsUIntVector()) opStr = "ShiftRightLogica"; else opStr = "ShiftRightArithmetic"; } else if (instr->Is()) { opStr = "BitwiseXor"; } else if (instr->Is()) { opStr = "BitwiseAnd"; } else if (instr->Is()) { opStr = "BitwiseOr"; } else if (instr->Is()) { opStr = "LogicalAnd"; } else if (instr->Is()) { opStr = "LogicalOr"; } else if (instr->Is()) { if (ID0Type->IsIntegral()) opStr = "INotEqua"; else opStr = "FOrdNotEqua"; } else if (instr->Is()) { if (ID0Type->IsIntegral()) opStr = "IEqua"; else opStr = "FOrdEqua"; } else if (instr->Is()) { if (ID0Type->IsIntegral()) { if (ID0Type->IsUInt() || ID0Type->IsUIntVector()) opStr = "UGreaterThanEqua"; else opStr = "SGreaterThanEqua"; } else opStr = "FOrdGreaterThanEqua"; } else if (instr->Is()) { if (ID0Type->IsIntegral()) { if (ID0Type->IsUInt() || ID0Type->IsUIntVector()) opStr = "UGreaterThan"; else opStr = "SGreaterThan"; } else opStr = "FOrdGreaterThan"; } else if (instr->Is()) { if (ID0Type->IsIntegral()) { if (ID0Type->IsUInt() || ID0Type->IsUIntVector()) opStr = "ULessThanEqua"; else opStr = "SLessThanEqua"; } else opStr = "FOrdLessThanEqua"; } else if (instr->Is()) { if (ID0Type->IsIntegral()) { if (ID0Type->IsUInt() || ID0Type->IsUIntVector()) opStr = "ULessThan"; else opStr = "SLessThan"; } else opStr = "FOrdLessThan"; } else throw InvalidProgramException("unsupported binary instruction: " + instr->ToString()); //---------------------------------------Generate Instrction--------------------------------------- String finalOpStr = LR"(Op)"; if (needPrefix) { if (ID0Type->IsFloat() || ID0Type->IsFloatVector()) finalOpStr = finalOpStr + LR"(F)"; else if (ID0Type->IsIntegral()) { if (Signed) finalOpStr = finalOpStr + LR"(S)"; else finalOpStr = finalOpStr + LR"(I)"; } } finalOpStr = finalOpStr + opStr; ctx.AddInstrBinaryInstr((ILOperand*)instr, instr->Type, finalOpStr, ID0, ID1); } void PrintUpdateInstr(MemberUpdateInstruction * instr) { int variableID = ctx.AddInstrVariableDeclaration((ILOperand*)instr, instr->Operands[0]->Type, StorageClass::Function); ctx.AddInstrStore((ILOperand*)instr, variableID, GetOperandValue(instr->Operands[0].Ptr())); auto typeIL = ctx.IDInfos[variableID]().GetILType().Ptr(); int memberID = -1; int indexID = GetOperandValue(instr->Operands[1].Ptr()); if (indexID == -1) throw InvalidOperationException("bad index in PrintUpdateInstr(): " + instr->Operands[1]->ToString()); if (auto structType = dynamic_cast(typeIL)) { auto c = dynamic_cast(instr->Operands[1].Ptr()); if (c) memberID = ctx.AddInstrAccessChain_StructMember(0, variableID, indexID, c->IntValues[0]); else throw InvalidOperationException("index of struct must be const in PrintUpdateInstr(): " + instr->Operands[1]->ToString()); } else if (auto arrayType = dynamic_cast(typeIL)) { memberID = ctx.AddInstrAccessChain_ArrayMember(0, typeIL, variableID, indexID); } else if (auto vecType = dynamic_cast(typeIL)) { if (!typeIL->IsVector()) throw InvalidOperationException("unable to update members of type: " + typeIL->ToString()); memberID = ctx.AddInstrAccessChain_VectorMember(0, variableID, indexID, -1); } else throw InvalidOperationException("not supported type in PrintUpdateInstr(): " + typeIL->ToString()); ctx.AddInstrStore( 0, memberID, GetOperandValue(instr->Operands[2].Ptr()) ); ctx.InvalidateValue((ILOperand*)instr); } void PrintSelectInstr(SelectInstruction * instr) { int ID0 = GetOperandValue(instr->Operands[0].Ptr()); ID0 = ctx.ConvertBasicType( ID0, ctx.IDInfos[ID0]().GetILType(), GetTypeFromString("bool")); int TrueLabel = ++ctx.CurrentID; int FalseLabel = ++ctx.CurrentID; int MergeLabel = ++ctx.CurrentID; ctx.AddInstrSelectionMerge(MergeLabel); ctx.AddInstrBranchConditional(ID0, TrueLabel, FalseLabel); ctx.AddInstrLabel_AtFunctionBody(TrueLabel); int ID1 = GetOperandValue(instr->Operands[1].Ptr()); ID1 = ctx.ConvertBasicType(ID1, ctx.IDInfos[ID1]().GetILType(), instr->Type); ctx.AddInstrBranch(MergeLabel); ctx.AddInstrLabel_AtFunctionBody(FalseLabel); int ID2 = GetOperandValue(instr->Operands[2].Ptr()); ID2 = ctx.ConvertBasicType(ID2, ctx.IDInfos[ID2]().GetILType(), instr->Type); ctx.AddInstrBranch(MergeLabel); ctx.AddInstrLabel_AtFunctionBody(MergeLabel); ctx.AddInstrPhi((ILOperand*)instr, ID1, TrueLabel, ID2, FalseLabel); } void PrintFetchArgInstr(FetchArgInstruction * instr) { if (instr->ArgId == 0) { ctx.ReturnID = ctx.AddInstrVariableDeclaration((ILOperand*)instr, instr->Type, StorageClass::Function); } } void PrintExportInstr(ExportInstruction * instr) { String exportOpName = instr->ExportOperator; if (exportOpName == "fragmentExport") { CompiledComponent ccomp; bool isNormal = false; bool isDepthOutput = false; if (currentWorld->LocalComponents.TryGetValue(instr->ComponentName, ccomp)) { if (ccomp.Attributes.ContainsKey("Norma")) isNormal = true; if (ccomp.Attributes.ContainsKey("DepthOutput")) isDepthOutput = true; } String exportName; if (isDepthOutput) exportName = "gl_FragDepth"; else exportName = instr->ComponentName; int exportID = ctx.InterfaceNameToID[exportName]; if (exportID == -1) throw InvalidOperationException("can not find component for export instruction for fragmentExport in PrintExportInstr(): " + exportName); int operandID = GetOperandValue(instr->Operand.Ptr()); if (isNormal) operandID = ctx.AddInstrMulAdd(operandID, 0.5, 0.5); ctx.AddInstrStore((ILOperand*)instr, exportID, operandID); } else if (exportOpName == "standardExport") { int storeID = ctx.AddInstrAccessChain_StructMember(0, ctx.InterfaceNameToID["blk" + currentWorld->WorldOutput->Name], instr->ComponentName); ctx.AddInstrStore((ILOperand*)instr, storeID, GetOperandValue(instr->Operand.Ptr())); } else if (exportOpName == "bufferExport") { auto & comp = currentWorld->WorldOutput->Entries[instr->ComponentName](); auto UIntType = GetTypeFromString("uint"); auto FloatType = GetTypeFromString("float"); int GIIDx = ctx.AddInstrLoad( ctx.AddInstrAccessChain_VectorMember(0, ctx.InterfaceNameToID["gl_GlobalInvocationID"], -1, 0), MemoryAccess::None ); //GlobalInvocationID.x int baseIndex = ctx.AddInstrBinaryInstr( 0, UIntType, "OpIMu", GIIDx, ctx.AddInstrConstantUInt(currentWorld->WorldOutput->Size / 4) ); baseIndex = ctx.AddInstrBinaryInstr( 0, UIntType, "OpIAdd", baseIndex, ctx.AddInstrConstantUInt(comp.Offset / 4) ); RefPtr compElementType = (!comp.Type->IsIntegral()) ? FloatType : ((comp.Type->IsUInt() || comp.Type->IsUIntVector()) ? UIntType : GetTypeFromString("int")); for (int i = 0; i < comp.Type->GetVectorSize(); i++) { int index = ctx.AddInstrBinaryInstr( 0, UIntType, "OpIAdd", baseIndex, ctx.AddInstrConstantUInt(i) ); int blockID = ctx.InterfaceNameToID["blk" + currentWorld->WorldOutput->Name]; int arrayID = ctx.AddInstrAccessChain_StructMember(0, blockID, -1, 0); int storeID = ctx.AddInstrAccessChain_ArrayMember( 0, ctx.IDInfos[arrayID]().GetILType(), arrayID, index ); int valueID = -1; if (instr->Operand->Type->GetVectorSize() > 1) valueID = ctx.AddInstrLoad( ctx.AddInstrAccessChain_VectorMember(0, GetOperandPointer(instr->Operand.Ptr()), -1, i), MemoryAccess::None ); else valueID = ctx.AddInstrLoad( GetOperandPointer(instr->Operand.Ptr()), MemoryAccess::None ); valueID = ctx.ConvertBasicType(valueID, ctx.IDInfos[valueID]().GetILType(), compElementType); if (ctx.IDInfos[valueID]().GetILType()->IsIntegral()) valueID = ctx.AddInstrBitcast(ctx.DefineType(FloatType), valueID); ctx.AddInstrStore(0, storeID, valueID); } } else throw InvalidOperationException("not valid export operator in PrintExportInstr(): " + exportOpName); } void PrintImportInstr(ImportInstruction * instr) { auto block = instr->SourceWorld->WorldOutput; if (instr->ImportOperator->Name.Content == "standardImport") { ctx.AddInstrAccessChain_StructMember(instr, ctx.InterfaceNameToID["blk" + block->Name], instr->ComponentName); } else if (instr->ImportOperator->Name.Content == "vertexImport") { int componentID = ctx.InterfaceNameToID[instr->ComponentName]; if (componentID == -1) throw InvalidOperationException("can not find import component for vertexImport in PrintImportInstr(): " + instr->ComponentName); ctx.UpdateVariable(instr, componentID); } else if (instr->ImportOperator->Name.Content == "uniformImport") { if (instr->Type->IsTexture()) { int pointerID = ctx.InterfaceNameToID[instr->ComponentName]; if (pointerID == -1) throw InvalidOperationException("can not find import component for uniformImport in PrintImportInstr(): " + instr->ComponentName); ctx.UpdateVariable(instr, pointerID); } else { ctx.AddInstrAccessChain_StructMember(instr, ctx.InterfaceNameToID["blk" + block->Name], instr->ComponentName); } } else if (instr->ImportOperator->Name.Content == "textureImport") { int textureStorageID = ctx.InterfaceNameToID[instr->ComponentName]; int textureValueID = ctx.AddInstrLoad(textureStorageID, MemoryAccess::None); int operandID = -1; operandID = ctx.AddInstrTexture( 0, textureValueID, GetOperandValue(instr->Arguments[0].Ptr()), currentExecutionModel ); operandID = ctx.ConvertBasicType( operandID, ctx.IDInfos[operandID]().GetILType(), instr->Type); CompiledComponent ccomp; if (instr->SourceWorld->LocalComponents.TryGetValue(instr->ComponentName, ccomp)) { if (ccomp.Attributes.ContainsKey("Norma")) operandID = ctx.AddInstrMulAdd(operandID, 2.0, -1.0); } int storeID = ctx.AddInstrVariableDeclaration((ILOperand*)instr, instr->Type, StorageClass::Function); ctx.AddInstrStore((ILOperand*)instr, storeID, operandID); } else if (instr->ImportOperator->Name.Content == "bufferImport") { //instr->Name[][] = // *(int*/float*) // ( block->Name + block->Entries[instr->ComponentName].GetValue().Offset / 4 + i + gl_GlobalInvocationID.x * block->Size / 4 ) auto UIntType = GetTypeFromString("uint"); auto FloatType = GetTypeFromString("float"); int GIIDx = ctx.AddInstrLoad( ctx.AddInstrAccessChain_VectorMember(0, ctx.InterfaceNameToID["gl_GlobalInvocationID"], -1, 0), MemoryAccess::None ); //GlobalInvocationID.x int baseIndex = ctx.AddInstrBinaryInstr( 0, UIntType, "OpIMu", GIIDx, ctx.AddInstrConstantUInt(block->Size / 4) ); baseIndex = ctx.AddInstrBinaryInstr( 0, UIntType, "OpIAdd", baseIndex, ctx.AddInstrConstantUInt(block->Entries[instr->ComponentName]().Offset / 4) ); RefPtr instrElementType = (!instr->Type->IsIntegral()) ? FloatType : ((instr->Type->IsUInt() || instr->Type->IsUIntVector()) ? UIntType : GetTypeFromString("int")); int vecSize = instr->Type->GetVectorSize(); int srcIDs[16]; for (int i = 0; i < vecSize; i++) { int index = ctx.AddInstrBinaryInstr( 0, UIntType, "OpIAdd", baseIndex, ctx.AddInstrConstantUInt(i) ); int blockID = ctx.InterfaceNameToID["blk" + block->Name]; int arrayID = ctx.AddInstrAccessChain_StructMember(0, blockID, -1, 0); int srcID = ctx.AddInstrLoad( ctx.AddInstrAccessChain_ArrayMember(0, ctx.IDInfos[arrayID]().GetILType(), arrayID, index), MemoryAccess::None ); srcIDs[i] = ctx.AddInstrBitcast(ctx.DefineType(instrElementType), srcID); } int valueID = -1; if (instr->Type->IsFloatMatrix()) { int n = 3; auto colType = GetTypeFromString("vec3"); if (instr->Type->GetVectorSize() == 16) { n = 4; colType = GetTypeFromString("vec4"); } int colIDs[4]; for (int i = 0; i < n; i++) { List args; for (int j = 0; j < n; j++) args.Add(srcIDs[i*n + j]); colIDs[i] = ctx.AddInstrCompositeConstruct(0, colType, args); } List args; for (int i = 0; i < n; i++) args.Add(colIDs[i]); valueID = ctx.AddInstrCompositeConstruct(0, instr->Type, args); } else if (instr->Type->IsVector()) { int n = 3; if (instr->Type->GetVectorSize() == 16) n = 4; List args; for (int i = 0; i < n; i++) args.Add(srcIDs[i]); valueID = ctx.AddInstrCompositeConstruct(0, instr->Type, args); } else //scalar { valueID = srcIDs[0]; } int storeID = ctx.AddInstrVariableDeclaration(instr, instr->Type, StorageClass::Function); ctx.AddInstrStore(instr, storeID, valueID); } else throw NotImplementedException("import in PrintImportInstr(): " + instr->ImportOperator->Name.Content); } void PrintInstr(ILInstruction & instr) { if (auto binInstr = instr.As()) PrintBinaryInstr(binInstr); else if (auto allocVar = instr.As()) PrintAllocVarInstr(allocVar, StorageClass::Function); else if (auto call = instr.As()) PrintCallInstr(call); else if (auto exportInstr = instr.As()) { PrintExportInstr(exportInstr); //throw InvalidOperationException("export instruction not supported"); } else if (auto import = instr.As()) { PrintImportInstr(import); //throw InvalidOperationException("import instruction not supported"); } else if (auto update = instr.As()) PrintUpdateInstr(update); else if (auto unaryInstr = instr.As()) PrintUnaryInstr(unaryInstr); else if (auto select = instr.As()) PrintSelectInstr(select); else if (auto fetchArg = instr.As()) //for function: return instruction PrintFetchArgInstr(fetchArg); else throw NotImplementedException("unsupported instruction in PrintInstr()" + instr.ToString()); } void PrintIf(IfInstruction * instr) { int operandID = GetOperandValue(instr->Operand.Ptr()); operandID = ctx.ConvertBasicType( operandID, ctx.IDInfos[operandID]().GetILType(), GetTypeFromString("bool")); int TrueLabel = ++ctx.CurrentID; int FalseLabel = ++ctx.CurrentID; int MergeLabel = ++ctx.CurrentID; ctx.AddInstrSelectionMerge(MergeLabel); ctx.AddInstrBranchConditional(operandID, TrueLabel, FalseLabel); ctx.PushScope(); ctx.AddInstrLabel_AtFunctionBody(TrueLabel); GenerateCode(instr->TrueCode.Ptr(), TrueLabel); ctx.AddInstrBranch(MergeLabel); ctx.PopScope(); ctx.PushScope(); ctx.AddInstrLabel_AtFunctionBody(FalseLabel); if (instr->FalseCode) GenerateCode(instr->FalseCode.Ptr(), FalseLabel); ctx.AddInstrBranch(MergeLabel); ctx.PopScope(); ctx.AddInstrLabel_AtFunctionBody(MergeLabel); } void PrintFor(ForInstruction * instr) { int HeaderBlockLabel = ++ctx.CurrentID; int ConditionBlockLabel = ++ctx.CurrentID; int BodyBlockLabel = ++ctx.CurrentID; int UpdateBlockLabel = ++ctx.CurrentID; int MergeBlockLabel = ++ctx.CurrentID; ctx.AddInstrBranch(HeaderBlockLabel); ctx.AddInstrLabel_AtFunctionBody(HeaderBlockLabel); ctx.AddInstrLoopMerge(MergeBlockLabel, UpdateBlockLabel); ctx.AddInstrBranch(ConditionBlockLabel); // condition block ctx.PushScope(); ctx.AddInstrLabel_AtFunctionBody(ConditionBlockLabel); GenerateCode(instr->ConditionCode.Ptr(), ConditionBlockLabel); int conditionID = GetOperandValue(instr->ConditionCode->GetLastInstruction()); conditionID = ctx.ConvertBasicType( conditionID, ctx.IDInfos[conditionID]().GetILType(), GetTypeFromString("bool")); ctx.AddInstrBranchConditional(conditionID, BodyBlockLabel, MergeBlockLabel); ctx.PopScope(); // body block ctx.PushScope(); ctx.AddInstrLabel_AtFunctionBody(BodyBlockLabel); ctx.StackMergeBlock.Add(MergeBlockLabel); ctx.StackContinueBlock.Add(UpdateBlockLabel); GenerateCode(instr->BodyCode.Ptr(), BodyBlockLabel); ctx.StackMergeBlock.RemoveAt(ctx.StackMergeBlock.Count() - 1); ctx.StackContinueBlock.RemoveAt(ctx.StackContinueBlock.Count() - 1); ctx.AddInstrBranch(UpdateBlockLabel); ctx.PopScope(); // update block ctx.PushScope(); ctx.AddInstrLabel_AtFunctionBody(UpdateBlockLabel); GenerateCode(instr->SideEffectCode.Ptr(), UpdateBlockLabel); ctx.AddInstrBranch(HeaderBlockLabel); ctx.PopScope(); // merge block ctx.AddInstrLabel_AtFunctionBody(MergeBlockLabel); } void PrintWhileDo(WhileInstruction * instr) { int HeaderBlockLabel = ++ctx.CurrentID; int ConditionBlockLabel = ++ctx.CurrentID; int BodyBlockLabel = ++ctx.CurrentID; int UpdateBlockLabel = ++ctx.CurrentID; int MergeBlockLabel = ++ctx.CurrentID; ctx.AddInstrBranch(HeaderBlockLabel); // header block ctx.AddInstrLabel_AtFunctionBody(HeaderBlockLabel); ctx.AddInstrLoopMerge(MergeBlockLabel, UpdateBlockLabel); ctx.AddInstrBranch(ConditionBlockLabel); // condition block ctx.PushScope(); ctx.AddInstrLabel_AtFunctionBody(ConditionBlockLabel); GenerateCode(instr->ConditionCode.Ptr(), ConditionBlockLabel, true); int conditionID = GetOperandValue(instr->ConditionCode->GetLastInstruction()->As()->Operand.Ptr()); conditionID = ctx.ConvertBasicType( conditionID, ctx.IDInfos[conditionID]().GetILType(), GetTypeFromString("bool") ); ctx.AddInstrBranchConditional(conditionID, BodyBlockLabel, MergeBlockLabel); ctx.PopScope(); // body block ctx.PushScope(); ctx.AddInstrLabel_AtFunctionBody(BodyBlockLabel); ctx.StackMergeBlock.Add(MergeBlockLabel); ctx.StackContinueBlock.Add(UpdateBlockLabel); GenerateCode(instr->BodyCode.Ptr(), BodyBlockLabel); ctx.StackMergeBlock.RemoveAt(ctx.StackMergeBlock.Count() - 1); ctx.StackContinueBlock.RemoveAt(ctx.StackContinueBlock.Count() - 1); ctx.AddInstrBranch(UpdateBlockLabel); ctx.PopScope(); // update block (empty) ctx.AddInstrLabel_AtFunctionBody(UpdateBlockLabel); ctx.AddInstrBranch(HeaderBlockLabel); // merge block ctx.AddInstrLabel_AtFunctionBody(MergeBlockLabel); } void PrintDoWhile(DoInstruction * instr) { int HeaderBlockLabel = ++ctx.CurrentID; int BodyBlockLabel = ++ctx.CurrentID; int ConditionBlockLabel = ++ctx.CurrentID; int MergeBlockLabel = ++ctx.CurrentID; //ctx.FunctionBody << LR"(OpBranch %)" << HeaderBlockLabel << EndLine; ctx.AddInstrBranch(HeaderBlockLabel); // header block ctx.AddInstrLabel_AtFunctionBody(HeaderBlockLabel); ctx.AddInstrLoopMerge(MergeBlockLabel, ConditionBlockLabel); ctx.AddInstrBranch(BodyBlockLabel); // body block ctx.PushScope(); ctx.AddInstrLabel_AtFunctionBody(BodyBlockLabel); ctx.StackMergeBlock.Add(MergeBlockLabel); ctx.StackContinueBlock.Add(ConditionBlockLabel); GenerateCode(instr->BodyCode.Ptr(), BodyBlockLabel); ctx.StackMergeBlock.RemoveAt(ctx.StackMergeBlock.Count() - 1); ctx.StackContinueBlock.RemoveAt(ctx.StackContinueBlock.Count() - 1); ctx.AddInstrBranch(ConditionBlockLabel); ctx.PopScope(); // condition block ctx.PushScope(); ctx.AddInstrLabel_AtFunctionBody(ConditionBlockLabel); GenerateCode(instr->ConditionCode.Ptr(), ConditionBlockLabel, true); int conditionID = GetOperandValue(instr->ConditionCode->GetLastInstruction()->As()->Operand.Ptr()); conditionID = ctx.ConvertBasicType( conditionID, ctx.IDInfos[conditionID]().GetILType(), GetTypeFromString("bool") ); ctx.AddInstrBranchConditional(conditionID, HeaderBlockLabel, MergeBlockLabel); ctx.PopScope(); // merge block ctx.AddInstrLabel_AtFunctionBody(MergeBlockLabel); } void GenerateCode(CFGNode * code, int givenLabel = -1, bool LoopReturn = false) { if (givenLabel == -1) { ++ctx.CurrentID; //label ID ctx.AddInstrLabel_AtFunctionBody(ctx.CurrentID); } List usedID; for (auto & instr : *code) { if (auto ifInstr = instr.As()) { PrintIf(ifInstr); } else if (auto forInstr = instr.As()) { PrintFor(forInstr); } else if (auto doInstr = instr.As()) { PrintDoWhile(doInstr); } else if (auto whileInstr = instr.As()) { PrintWhileDo(whileInstr); } else if (auto ret = instr.As()) { if (!LoopReturn) { if (ret->Operand) ctx.AddInstrReturnValue(GetOperandValue(ret->Operand.Ptr())); else ctx.AddInstrReturn(); ctx.AddInstrLabel_AtFunctionBody(++ctx.CurrentID); } } else if (instr.Is()) { ctx.AddInstrBranch(ctx.StackMergeBlock.Last()); ctx.AddInstrLabel_AtFunctionBody(++ctx.CurrentID); } else if (instr.Is()) { ctx.AddInstrBranch(ctx.StackContinueBlock.Last()); ctx.AddInstrLabel_AtFunctionBody(++ctx.CurrentID); } else if (instr.Is()) { ctx.AddInstrKill(); ctx.AddInstrLabel_AtFunctionBody(++ctx.CurrentID); } else { PrintInstr(instr); } } } public: bool BufferPreProcessed; void Initiate(ExecutionModel model, CompiledWorld * shaderWorld) { ctx.Clear(); ctx.CodeGen.Initiate(); ctx.CurrentID = 1; // reserved for extinst ctx.CodeGen.ProgramHeader(); ctx.PushScope(); interfaceIDs.Clear(); DepthReplacing = false; LocalSize = false; BufferImportOrExport = false; BufferPreProcessed = false; currentWorld = shaderWorld; currentExecutionModel = model; } int GenerateFunctionDeclaration(CompiledFunction *func) //return the ID of the Function Type { List> args; for (auto & instr : *func->Code) if (auto arg = instr.As()) if (arg->ArgId != 0) { args.Add(arg->Type); } int funcTypeID = ctx.AddInstrTypeFunction(func, args, func->ReturnType); ctx.FunctionNameToFunctionID[func->Name] = ++ctx.CurrentID; return funcTypeID; } int GenerateFunctionDefinition(CompiledFunction *func, ILOperand *vertexOutput = nullptr) //return the ID of the Function { ctx.ClearBuffer(); ctx.PushScope(); int funcID = ctx.FunctionNameToFunctionID[func->Name](); int funcTypeID = ctx.FunctionNameToFunctionTypeID[func->Name](); ctx.AddInstrFunction(funcID, ctx.DefineType(func->ReturnType), funcTypeID, GetFuncOriginalName(func->Name)); for (auto & instr : *func->Code) if (auto arg = instr.As()) if (arg->ArgId != 0) { if (!ctx.ParameterNameToID.ContainsKey((ILOperand*)&instr)) { ctx.DefineType(arg->Type); int typeID = ctx.DefineTypePointer(arg->Type, StorageClass::Function); ctx.AddInstrFunctionParameter((ILOperand*)&instr, typeID, arg->Name); } } ++ctx.CurrentID; ctx.AddInstrLabel_AtFunctionHeader(ctx.CurrentID); func->Code->NameAllInstructions(); GenerateCode(func->Code.Ptr(), ctx.CurrentID); if (vertexOutput) { int valueID = ctx.AddInstrLoad(nullptr, vertexOutput, MemoryAccess::None); int gl_PositionID = ctx.AddInstrAccessChain_StructMember( 0, ctx.InterfaceNameToID["variable_gl_PerVertex"], "gl_Position" ); ctx.AddInstrStore(0, gl_PositionID, valueID); } ctx.AddInstrReturn(); ctx.AddInstrFunctionEnd(); ctx.ProduceFunction(); ctx.PopScope(); return funcID; } void Decorate(int ID, Decoration deco, int op1 = -1) { ctx.AddInstrDecorate(ID, deco, op1); } void MemberDecorate(int ID, int index, Decoration deco, int op1 = -1) { ctx.AddInstrMemberDecorate(ID, index, deco, op1); } void SetDepthReplacing() { DepthReplacing = true; } void SetLocalSize(unsigned int x) { //layout(local_size_x = ) in; LocalSize = true; unsigned int workgroup[3] = { x, 1, 1 }; ctx.AddInstrDecorate( ctx.AddInstrConstantCompositeUInt(workgroup, 3), Decoration::BuiltIn, (int)BuiltIn::WorkgroupSize ); } void GenerateEntryPoint(int mainFunctionID) { ctx.AddInstrEntryPoint(currentExecutionModel, mainFunctionID, interfaceIDs); if (currentExecutionModel == ExecutionModel::Fragment) { ctx.AddInstrExecutionMode(mainFunctionID, ExecutionMode::OriginUpperLeft); } if (DepthReplacing) { ctx.AddInstrExecutionMode(mainFunctionID, ExecutionMode::DepthReplacing); } if (LocalSize) { ctx.AddInstrExecutionMode(mainFunctionID, ExecutionMode::LocalSize, 256, 1, 1); } } int GenerateGlGlobalInvocationID() //return the ID of the gl_GlobalInvocationID variable { int GlobalInvocationID = ctx.AddInstrVariableDeclaration(0, GetTypeFromString("uvec3"), StorageClass::Input, "gl_GlobalInvocationID", 0); ctx.AddInstrDecorate(GlobalInvocationID, Decoration::BuiltIn, (int)BuiltIn::GlobalInvocationId); ctx.InterfaceNameToID["gl_GlobalInvocationID"] = GlobalInvocationID; return GlobalInvocationID; } int GenerateInterfaceForSingleVariable( String varName, RefPtr varType, StorageClass storageClass, int location = -1, int bindIndex = -1) //return the ID of the variable { int entID = ctx.AddInstrVariableDeclaration(0, varType, storageClass, varName, 0); ctx.InterfaceNameToID[varName] = entID; if (location != -1) ctx.AddInstrDecorate(entID, Decoration::Location, location); if (bindIndex != -1) { ctx.AddInstrDecorate(entID, Decoration::DescriptorSet, 0); ctx.AddInstrDecorate(entID, Decoration::Binding, bindIndex); } if (storageClass == StorageClass::Input || storageClass == StorageClass::Output) interfaceIDs.Add(entID); return entID; } std::pair GenerateInterfaceForStructVariable( String structTypeName, String structVariableName, List> memberTypes, List memberNames, int UniformOrBuffer, StorageClass storageClass, int location = -1, int bindindex = -1) //first - the ID of the struct type //second - the ID of the variable { RefPtr structIL = new ILStructType(); structIL->TypeName = structTypeName; int n = memberTypes.Count(); for (int i = 0; i < n; i++) { ILStructType::ILStructField field; field.Type = memberTypes[i]; field.FieldName = memberNames[i]; structIL->Members.Add(field); } int structTypeID = ctx.DefineType(structIL, UniformOrBuffer); int structVariableID = ctx.AddInstrVariableDeclaration(0, structIL, storageClass, structVariableName, UniformOrBuffer); ctx.InterfaceNameToID[structVariableName] = structVariableID; if (UniformOrBuffer == 1) { ctx.AddInstrDecorate(structVariableID, Decoration::DescriptorSet, 0); ctx.AddInstrDecorate(structTypeID, Decoration::Block); } else if (UniformOrBuffer == 2) { ctx.AddInstrDecorate(structVariableID, Decoration::DescriptorSet, 0); ctx.AddInstrDecorate(structTypeID, Decoration::BufferBlock); } if (location != -1) { ctx.AddInstrDecorate(structVariableID, Decoration::Location, location); } if (bindindex != -1) { ctx.AddInstrDecorate(structVariableID, Decoration::Binding, bindindex); } if (storageClass == StorageClass::Input || storageClass == StorageClass::Output) { for (int i = 0; i < n; i++) if (memberTypes[i]->IsIntegral()) ctx.AddInstrMemberDecorate(structTypeID, i, Decoration::Flat); interfaceIDs.Add(structVariableID); } return std::make_pair(structTypeID, structVariableID); } CompiledShaderSource GenerateShaderWorld() { CompiledShaderSource rs; auto binaryForm = ctx.ProduceWordStream(); rs.BinaryCode.SetSize(binaryForm.Count() * sizeof(unsigned int)); memcpy(rs.BinaryCode.Buffer(), binaryForm.Buffer(), rs.BinaryCode.Count()); rs.MainCode = ctx.ProduceTextCode(); return rs; } }; class SpirVCodeGen : public CodeGenBackend { String vertexOutputName; EnumerableDictionary backendArguments; public: ExecutionModel currentExecutionModel = ExecutionModel::Invalid; void ProcessBufferImportOrExportInterfaces(SpirvModule & spvModule, String blkName, int index) { if (!spvModule.BufferPreProcessed) { spvModule.BufferPreProcessed = true; spvModule.SetLocalSize(256); spvModule.GenerateGlGlobalInvocationID(); List> memberTypes; List memberNames; memberTypes.Add(GetTypeFromString("uint")); memberNames.Add("sys_thread_count"); spvModule.GenerateInterfaceForStructVariable( "SystemBlock", "blkSystemBlock", memberTypes, memberNames, 1, StorageClass::Uniform, -1, 0 ); } List> memberTypes; List memberNames; memberTypes.Add(GetTypeFromString("float[0]")); memberNames.Add("a"); spvModule.GenerateInterfaceForStructVariable( blkName, "blk" + blkName, memberTypes, memberNames, 2, StorageClass::Uniform, -1, index ); } virtual CompiledShaderSource GenerateShaderWorld(CompileResult & result, SymbolTable * /*symbols*/, CompiledWorld * shaderWorld, Dictionary& opHandlers, Dictionary& exportHandlers) override { currentExecutionModel = ExecutionModel::Invalid; if (shaderWorld->ExportOperator.Content == "fragmentExport") currentExecutionModel = ExecutionModel::Fragment; else if (shaderWorld->BackendParameters.ContainsKey("vertex")) currentExecutionModel = ExecutionModel::Vertex; else if (shaderWorld->ExportOperator.Content == "bufferExport") currentExecutionModel = ExecutionModel::GLCompute; CompiledFunction mainFunction; mainFunction.Name = "main"; mainFunction.ReturnType = nullptr; mainFunction.Code = shaderWorld->Code; SpirvModule spvModule; spvModule.Initiate(currentExecutionModel, shaderWorld); spvModule.GenerateFunctionDeclaration(&mainFunction); for (auto funcName : shaderWorld->ReferencedFunctions) for(auto & func : result.Program->Functions) if (funcName == func->Name) spvModule.GenerateFunctionDeclaration(func.Ptr()); for (auto funcName : shaderWorld->ReferencedFunctions) for (auto & func : result.Program->Functions) if (funcName == func->Name) spvModule.GenerateFunctionDefinition(func.Ptr()); for (auto & inputBlock : shaderWorld->WorldInputs) { auto block = inputBlock.Value.Block; if (!block->UserWorlds.Contains(shaderWorld->WorldName)) continue; String impOpName = inputBlock.Value.ImportOperator.Name.Content; if (impOpName == "standardImport") { List> memberTypes; List memberNames; for (auto & ent : block->Entries) { memberTypes.Add(ent.Value.Type); memberNames.Add(ent.Value.Name); } spvModule.GenerateInterfaceForStructVariable( block->Name, "blk"+block->Name, memberTypes, memberNames, 0, StorageClass::Input, 0 ); } else if (impOpName == "vertexImport") { int location = 0; for (auto & ent : block->Entries) { spvModule.GenerateInterfaceForSingleVariable( ent.Key, ent.Value.Type, StorageClass::Input, location ); location++; } } else if (impOpName == "uniformImport") { int nonTextureCount = 0; for (auto & ent : block->Entries) if (!ent.Value.Type->IsTexture()) nonTextureCount++; int TypeOfStruct = 1; //1: uniform buffer; 2: shader storage buffer; 0: not a buffer if (block->Attributes.ContainsKey("ShaderStorageBlock")) TypeOfStruct = 2; if (nonTextureCount) { List> memberTypes; List memberNames; for (auto & ent : block->Entries) if (!ent.Value.Type->IsTexture()) { memberTypes.Add(ent.Value.Type); memberNames.Add(ent.Value.Name); } String strIndex; int index = -1; if (block->Attributes.TryGetValue("Index", strIndex)) index = StringToInt(strIndex); spvModule.GenerateInterfaceForStructVariable( block->Name, "blk" + block->Name, memberTypes, memberNames, TypeOfStruct, StorageClass::Uniform, -1, index ); } int bindPoint = 0; String bindingStart; if (backendArguments.TryGetValue("TextureBindingStart", bindingStart)) bindPoint = StringToInt(bindingStart); for(auto & ent : block->Entries) if (ent.Value.Type->IsTexture()) { spvModule.GenerateInterfaceForSingleVariable( ent.Key, ent.Value.Type, StorageClass::UniformConstant, -1, bindPoint ); bindPoint++; } } else if (impOpName == "textureImport") { int bindPoint = 0; String strIndex; if (block->Attributes.TryGetValue("Index", strIndex)) bindPoint = StringToInt(strIndex); for (auto & ent : block->Entries) { spvModule.GenerateInterfaceForSingleVariable( ent.Key, ent.Value.Type, StorageClass::UniformConstant, -1, bindPoint ); bindPoint++; } } else if (impOpName == "bufferImport") { String strIdx; int index = 0; if (block->Attributes.TryGetValue("Index", strIdx)) index = StringToInt(strIdx); ProcessBufferImportOrExportInterfaces(spvModule, block->Name, index); } else throw NotImplementedException("not implemented input interface: " + impOpName); } if (shaderWorld->ExportOperator.Content == "fragmentExport") { int location = 0; for (auto & ent : shaderWorld->WorldOutput->Entries) if (!ent.Value.LayoutAttribs.ContainsKey("DepthOutput")) { spvModule.GenerateInterfaceForSingleVariable( ent.Key, ent.Value.Type, StorageClass::Output, location ); location++; } else { int entID = spvModule.GenerateInterfaceForSingleVariable( "gl_FragDepth", ent.Value.Type, StorageClass::Output, location ); spvModule.SetDepthReplacing(); spvModule.Decorate(entID, Decoration::BuiltIn, (int)BuiltIn::FragDepth); } } else if (shaderWorld->ExportOperator.Content == "standardExport") { List> memberTypes; List memberNames; for (auto & ent : shaderWorld->WorldOutput->Entries) { memberTypes.Add(ent.Value.Type); memberNames.Add(ent.Value.Name); } spvModule.GenerateInterfaceForStructVariable( shaderWorld->WorldOutput->Name, "blk" + shaderWorld->WorldOutput->Name, memberTypes, memberNames, 0, StorageClass::Output, 0 ); } else if (shaderWorld->ExportOperator.Content == "bufferExport") { String strIdx; int index = 0; if (shaderWorld->WorldOutput->Attributes.TryGetValue("Index", strIdx)) index = StringToInt(strIdx); ProcessBufferImportOrExportInterfaces(spvModule, shaderWorld->WorldOutput->Name, index); } else throw NotImplementedException("not implemented output interface: " + shaderWorld->ExportOperator.Content); if (vertexOutputName.Length()) { CompiledComponent ccomp; if (shaderWorld->LocalComponents.TryGetValue(vertexOutputName, ccomp)) { List> memberTypes; List memberNames; memberTypes.Add(GetTypeFromString("vec4")); memberNames.Add("gl_Position"); memberTypes.Add(GetTypeFromString("float")); memberNames.Add("gl_PointSize"); memberTypes.Add(GetTypeFromString("float[1]")); memberNames.Add("gl_ClipDistance"); memberTypes.Add(GetTypeFromString("float[1]")); memberNames.Add("gl_CullDistance"); int typeID = spvModule.GenerateInterfaceForStructVariable( "gl_PerVertex", "variable_gl_PerVertex", memberTypes, memberNames, 0, StorageClass::Output, -1, -1 ).first; spvModule.Decorate(typeID, Decoration::Block); spvModule.MemberDecorate(typeID, 0, Decoration::BuiltIn, (int)BuiltIn::Position); spvModule.MemberDecorate(typeID, 1, Decoration::BuiltIn, (int)BuiltIn::PointSize); spvModule.MemberDecorate(typeID, 2, Decoration::BuiltIn, (int)BuiltIn::ClipDistance); spvModule.MemberDecorate(typeID, 3, Decoration::BuiltIn, (int)BuiltIn::CullDistance); } } ILOperand *vertexOutput = nullptr; if (vertexOutputName.Length()) { CompiledComponent ccomp; if (shaderWorld->LocalComponents.TryGetValue(vertexOutputName, ccomp)) { vertexOutput = ccomp.CodeOperand; } else throw InvalidOperationException("can not find vertexOutputName"); } int mainFunctionID = spvModule.GenerateFunctionDefinition(&mainFunction, vertexOutput); spvModule.GenerateEntryPoint(mainFunctionID); return spvModule.GenerateShaderWorld(); /* ctx.Clear(); currentWorld = shaderWorld; CompiledShaderSource rs; ctx.Result = &result; currentExecutionModel = ExecutionModel::Invalid; if (currentWorld->ExportOperator.Content == "fragmentExport") currentExecutionModel = ExecutionModel::Fragment; else if (currentWorld->BackendParameters.ContainsKey("vertex")) currentExecutionModel = ExecutionModel::Vertex; else if (currentWorld->ExportOperator.Content == "bufferExport") currentExecutionModel = ExecutionModel::GLCompute; if (ExecutionModel::Invalid == currentExecutionModel) throw InvalidOperationException("invalid execution model for shader world: " + currentWorld->WorldName); ctx.CurrentID = 1; ctx.CodeGen.Initiate(); ctx.CodeGen.ProgramHeader(); //add all functions type definition for (auto funcName : shaderWorld->ReferencedFunctions) { for (auto &func : result.Program->Functions) { if (func->Name == funcName) { List> args; for (auto & instr : *func->Code) if (auto arg = instr.As()) if (arg->ArgId != 0) { args.Add(arg->Type); } ctx.AddInstrTypeFunction(func.Ptr(), args, func->ReturnType); ctx.FunctionNameToFunctionID[func->Name] = ++ctx.CurrentID; //this is reserverd for the result ID of the OpFunction instruction! } } } //add all functions definition for (auto funcName : shaderWorld->ReferencedFunctions) { for (auto &func : result.Program->Functions) { if (func->Name == funcName) { ctx.ClearBuffer(); ctx.PushScope(); int funcID = ctx.FunctionNameToFunctionID[func->Name](); int funcTypeID = ctx.FunctionNameToFunctionTypeID[func->Name](); ctx.AddInstrFunction(funcID, ctx.DefineType(func->ReturnType), funcTypeID, GetFuncOriginalName(funcName)); for (auto & instr : *func->Code) if (auto arg = instr.As()) if (arg->ArgId != 0) { if (!ctx.ParameterNameToID.ContainsKey((ILOperand*)&instr)) { ctx.DefineType(arg->Type); int typeID = ctx.DefineTypePointer(arg->Type, StorageClass::Function); ctx.AddInstrFunctionParameter((ILOperand*)&instr, typeID, arg->Name); } } ++ctx.CurrentID; ctx.AddInstrLabel_AtFunctionHeader(ctx.CurrentID); func->Code->NameAllInstructions(); GenerateCode(func->Code.Ptr(), ctx.CurrentID); ctx.AddInstrReturn(); ctx.AddInstrFunctionEnd(); ctx.ProduceFunction(); ctx.PopScope(); } } } ctx.ClearBuffer(); ctx.PushScope(); List interfaceIDs; bool DepthReplacing = false; bool LocalSize = false; bool BufferImportOrExport = false; ProcessInterfaces(interfaceIDs, shaderWorld, opHandlers, DepthReplacing, LocalSize, BufferImportOrExport); //for gl_Position if (vertexOutputName.Length()) { CompiledComponent ccomp; if (currentWorld->LocalComponents.TryGetValue(vertexOutputName, ccomp)) { RefPtr structIL = new ILStructType(); structIL->TypeName = "gl_PerVertex"; ILStructType::ILStructField f1; f1.Type = GetTypeFromString("vec4"); f1.FieldName = "gl_Position"; structIL->Members.Add(f1); ILStructType::ILStructField f2; f2.Type = GetTypeFromString("float"); f2.FieldName = "gl_PointSize"; structIL->Members.Add(f2); ILStructType::ILStructField f3; f3.Type = GetTypeFromString("float[1]"); f3.FieldName = "gl_ClipDistance"; structIL->Members.Add(f3); ILStructType::ILStructField f4; f4.Type = GetTypeFromString("float[1]"); f4.FieldName = "gl_CullDistance"; structIL->Members.Add(f4); int structTypeID = ctx.DefineType(structIL); int structVariableID = ctx.AddInstrVariableDeclaration(0, structIL, StorageClass::Output, "gl_PerVertex"); ctx.InterfaceNameToID["gl_PerVertex"] = structVariableID; ctx.AddInstrDecorate(structTypeID, Decoration::Block); ctx.AddInstrMemberDecorate(structTypeID, 0, Decoration::BuiltIn, (int)BuiltIn::Position); ctx.AddInstrMemberDecorate(structTypeID, 1, Decoration::BuiltIn, (int)BuiltIn::PointSize); ctx.AddInstrMemberDecorate(structTypeID, 2, Decoration::BuiltIn, (int)BuiltIn::ClipDistance); ctx.AddInstrMemberDecorate(structTypeID, 3, Decoration::BuiltIn, (int)BuiltIn::CullDistance); interfaceIDs.Add(structVariableID); } else throw InvalidOperationException("can not find vertexOutputName"); } //add Main function type definition ctx.MainFunctionTypeID = ctx.AddInstrTypeFunction(nullptr, List>(), nullptr); ctx.MainFunctionID = ++ctx.CurrentID; //Entry Point ctx.AddInstrEntryPoint(currentExecutionModel, ctx.MainFunctionID, interfaceIDs); //execution mode if (currentExecutionModel == ExecutionModel::Fragment) { //CodeGen.OpExecutionMode(ctx.MainFunctionID, ExecutionMode::OriginUpperLeft); ctx.AddInstrExecutionMode(ctx.MainFunctionID, ExecutionMode::OriginUpperLeft); } if (DepthReplacing) { //CodeGen.OpExecutionMode(ctx.MainFunctionID, ExecutionMode::DepthReplacing); ctx.AddInstrExecutionMode(ctx.MainFunctionID, ExecutionMode::DepthReplacing); } if (LocalSize) { ctx.AddInstrExecutionMode(ctx.MainFunctionID, ExecutionMode::LocalSize, 256, 1, 1); } //MainFunction ctx.AddInstrFunction(ctx.MainFunctionID, ctx.TypeNameToID["void"](), ctx.MainFunctionTypeID, "main"); ++ctx.CurrentID; ctx.AddInstrLabel_AtFunctionHeader(ctx.CurrentID); if (BufferImportOrExport) { int GIIDx = ctx.AddInstrLoad( ctx.AddInstrAccessChain_VectorMember(0, ctx.InterfaceNameToID["gl_GlobalInvocationID"], -1, 0), MemoryAccess::None ); //GlobalInvocationID.x int SysThreadCountID = ctx.AddInstrLoad( ctx.AddInstrAccessChain_StructMember(0, ctx.InterfaceNameToID["SystemBlock"], "sys_thread_count"), MemoryAccess::None ); int conditionID = ctx.AddInstrBinaryInstr( 0, GetTypeFromString("bool"), "OpUGreaterThanEqua", GIIDx, SysThreadCountID ); int MergeLabel = ++ctx.CurrentID; int TrueLabel = ++ctx.CurrentID; ctx.AddInstrSelectionMerge(MergeLabel); ctx.AddInstrBranchConditional(conditionID, TrueLabel, MergeLabel); ctx.AddInstrLabel_AtFunctionBody(TrueLabel); ctx.AddInstrReturn(); ctx.AddInstrLabel_AtFunctionBody(MergeLabel); } shaderWorld->Code->NameAllInstructions(); GenerateCode(shaderWorld->Code.Ptr(), ctx.CurrentID); if (vertexOutputName.Length()) { CompiledComponent ccomp; if (currentWorld->LocalComponents.TryGetValue(vertexOutputName, ccomp)) { int valueID = ctx.AddInstrLoad(nullptr, ccomp.CodeOperand, MemoryAccess::None); int gl_PositionID = ctx.AddInstrAccessChain_StructMember( 0, ctx.InterfaceNameToID["gl_PerVertex"], "gl_Position" ); ctx.AddInstrStore(0, gl_PositionID, valueID); } else throw InvalidOperationException("can not find vertexOutputName"); } //MainFunction End ctx.AddInstrReturn(); ctx.AddInstrFunctionEnd(); ctx.ProduceFunction(); ctx.PopScope(); auto binaryForm = ctx.ProduceWordStream(); rs.BinaryCode.SetSize(binaryForm.Count() * sizeof(unsigned int)); memcpy(rs.BinaryCode.Buffer(), binaryForm.Buffer(), rs.BinaryCode.Count()); rs.MainCode = ctx.ProduceTextCode(); rs.OutputDeclarations = "spirv"; //print IL { auto compiledProgram = result.Program.Ptr(); StringBuilder sb; //function part sb << "function" << EndLine; sb << "{" << EndLine; for (auto &pfunc : compiledProgram->Functions) { sb << pfunc->ReturnType->ToString() << " " << pfunc->Name << "("; bool first = true; for (auto &name2param : pfunc->Parameters) { if (!first) sb << ", "; sb << name2param.Value->ToString() << " " << name2param.Key; first = false; } sb << ")" << EndLine; sb << "{" << EndLine; pfunc->Code->NameAllInstructions(); sb << pfunc->Code->ToString() << EndLine; sb << "}" << EndLine; } sb << "}" << EndLine; //shader part for (auto &pshader : compiledProgram->Shaders) { sb << "Shader " << pshader->MetaData.ShaderName << EndLine; sb << "{" << EndLine; for (auto &pworld : pshader->Worlds) { sb << "World " << pworld.Key << EndLine; sb << "{" << EndLine; pworld.Value.Ptr()->Code->NameAllInstructions(); sb << pworld.Value.Ptr()->Code->ToString() << EndLine; sb << "}" << EndLine; } sb << "}" << EndLine; } StringBuilder sb_indent; IndentString(sb_indent, sb.ToSt\`ring()); CoreLib::IO::StreamWriter sw("IL-" + currentWorld->ShaderName + "-" + currentWorld->WorldOutput->Name + String(".out")); sw.Write(sb_indent.ToString()); } currentWorld = nullptr; return rs; */ } virtual void SetParameters(const EnumerableDictionary& arguments) override { backendArguments = arguments; if (!arguments.TryGetValue("vertex", vertexOutputName)) vertexOutputName = ""; } }; CodeGenBackend * CreateSpirVCodeGen() { return new SpirVCodeGen(); } } } #endif ================================================ FILE: Source/SpireCore/SpireCore.vcxproj ================================================  DebugClang Win32 DebugClang x64 Debug_VS2013 Win32 Debug_VS2013 x64 Debug Win32 Debug x64 Release_VS2013 Win32 Release_VS2013 x64 Release Win32 Release x64 {DB00DA62-0533-4AFD-B59F-A67D5B3A0808} Win32Proj SpireCore SpireCore 8.1 StaticLibrary true v140 Unicode StaticLibrary true v120 Unicode StaticLibrary true v140_clang_3_7 Unicode StaticLibrary true v140 Unicode StaticLibrary true v120 Unicode StaticLibrary true v140_Clang_3_7 Unicode StaticLibrary false v140 true Unicode StaticLibrary false v120 true Unicode StaticLibrary false v140 true Unicode StaticLibrary false v120 true Unicode true true true true true true false false false false Level4 Disabled WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) ../ MultiThreadedDebug false Console true Level4 Disabled WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) ../ MultiThreadedDebug false Console true EnableAllWarnings Disabled WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) ../ MultiThreadedDebug false true Console true Level4 Disabled WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) ../ MultiThreadedDebug true false Console true true Level4 Disabled WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) ../ MultiThreadedDebug true false Console true true Level4 Disabled WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) ../ MultiThreadedDebug true false Console true true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) ../ MultiThreaded false Console true true true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) ../ MultiThreaded false Console true true true Level4 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) ../ MultiThreaded false Console true true true Level4 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) ../ MultiThreaded false Console true true true true true true true true true true true true true {f9be7957-8399-899e-0c49-e714fddd4b65} ================================================ FILE: Source/SpireCore/SpireCore.vcxproj.filters ================================================  {d1e94d15-4d7f-41dd-937e-ae0f31fe5cd8} {bf6a2182-b6c8-4870-ac96-7cd4822903bf} Front End Front End Front End Front End Front End Back End Back End Front End Front End Front End Front End Back End Back End Front End Front End Front End Front End Back End Front End Front End Front End Front End Front End Back End Front End Front End Front End Front End Back End Front End Front End Back End Front End Front End Back End Back End Back End Front End Back End Back End Front End Front End Front End Front End Back End Back End Front End Front End Front End Front End Back End ================================================ FILE: Source/SpireCore/StdInclude.cpp ================================================ #include "StdInclude.h" #include "Syntax.h" const char * LibIncludeString = R"( __intrinsic float dFdx(float v); __intrinsic float dFdy(float v); __intrinsic float fwidth(float v); __intrinsic vec2 dFdx(vec2 v); __intrinsic vec2 dFdy(vec2 v); __intrinsic vec2 fwidth(vec2 v); __intrinsic vec3 dFdx(vec3 v); __intrinsic vec3 dFdy(vec3 v); __intrinsic vec3 fwidth(vec3 v); __intrinsic vec4 dFdx(vec4 v); __intrinsic vec4 dFdy(vec4 v); __intrinsic vec4 fwidth(vec4 v); __intrinsic float ddx(float v); __intrinsic float ddy(float v); __intrinsic vec2 ddx(vec2 v); __intrinsic vec2 ddy(vec2 v); __intrinsic vec3 ddx(vec3 v); __intrinsic vec3 ddy(vec3 v); __intrinsic vec4 ddx(vec4 v); __intrinsic vec4 ddy(vec4 v); __intrinsic float intBitsToFloat(int x); __intrinsic int floatBitsToInt(float x); __intrinsic vec3 normalize(vec3 v); __intrinsic float dot(vec2 v0, vec2 v1); __intrinsic float dot(vec3 v0, vec3 v1); __intrinsic float dot(vec4 v0, vec4 v1); __intrinsic float sin(float v); __intrinsic float cos(float v); __intrinsic float tan(float v); __intrinsic float sqrt(float v); __intrinsic vec2 sin(vec2 v); __intrinsic vec2 cos(vec2 v); __intrinsic vec2 tan(vec2 v); __intrinsic vec2 sqrt(vec2 v); __intrinsic vec3 sin(vec3 v); __intrinsic vec3 cos(vec3 v); __intrinsic vec3 tan(vec3 v); __intrinsic vec3 sqrt(vec3 v); __intrinsic vec4 sin(vec4 v); __intrinsic vec4 cos(vec4 v); __intrinsic vec4 tan(vec4 v); __intrinsic vec4 sqrt(vec4 v); __intrinsic float abs(float v); __intrinsic vec2 abs(vec2 v); __intrinsic vec3 abs(vec3 v); __intrinsic vec4 abs(vec4 v); __intrinsic float exp(float v); __intrinsic vec2 exp(vec2 v); __intrinsic vec3 exp(vec3 v); __intrinsic vec4 exp(vec4 v); __intrinsic float log(float v); __intrinsic vec2 log(vec2 v); __intrinsic vec3 log(vec3 v); __intrinsic vec4 log(vec4 v); __intrinsic float exp2(float v); __intrinsic vec2 exp2(vec2 v); __intrinsic vec3 exp2(vec3 v); __intrinsic vec4 exp2(vec4 v); __intrinsic float log2(float v); __intrinsic vec2 log2(vec2 v); __intrinsic vec3 log2(vec3 v); __intrinsic vec4 log2(vec4 v); __intrinsic float asin(float v); __intrinsic vec2 asin(vec2 v); __intrinsic vec3 asin(vec3 v); __intrinsic vec4 asin(vec4 v); __intrinsic float acos(float v); __intrinsic vec2 acos(vec2 v); __intrinsic vec3 acos(vec3 v); __intrinsic vec4 acos(vec4 v); __intrinsic float atan(float v); __intrinsic vec2 atan(vec2 v); __intrinsic vec3 atan(vec3 v); __intrinsic vec4 atan(vec4 v); __intrinsic float sign(float x); __intrinsic vec2 sign(vec2 x); __intrinsic vec3 sign(vec3 x); __intrinsic vec4 sign(vec4 x); __intrinsic float pow(float base, float e); __intrinsic vec2 pow(vec2 base, vec2 e); __intrinsic vec3 pow(vec3 base, vec3 e); __intrinsic vec4 pow(vec4 base, vec4 e); __intrinsic float atan2(float x, float y); __intrinsic float floor(float v); __intrinsic vec2 floor(vec2 v); __intrinsic vec3 floor(vec3 v); __intrinsic vec4 floor(vec4 v); __intrinsic float fract(float v); __intrinsic vec2 fract(vec2 v); __intrinsic vec3 fract(vec3 v); __intrinsic vec4 fract(vec4 v); __intrinsic float ceil(float v); __intrinsic vec2 ceil(vec2 v); __intrinsic vec3 ceil(vec3 v); __intrinsic vec4 ceil(vec4 v); __intrinsic float step(float v, float y); __intrinsic vec2 step(vec2 v, vec2 v1); __intrinsic vec3 step(vec3 v, vec3 v1); __intrinsic vec4 step(vec4 v, vec4 v1); __intrinsic float smoothstep(float e0, float e1, float v); __intrinsic vec2 smoothstep(vec2 e0, vec2 e1, vec2 v); __intrinsic vec3 smoothstep(vec3 e0, vec3 e1, vec3 v); __intrinsic vec4 smoothstep(vec4 e0, vec4 e1, vec4 v); __intrinsic vec4 Sample(Texture2D tex, SamplerState sampler, vec2 uv); __intrinsic vec4 Sample(Texture2D tex, SamplerState sampler, vec2 uv, ivec2 offset); __intrinsic vec4 Sample(Texture2DArray tex, SamplerState sampler, vec3 uv, ivec3 offset); __intrinsic vec4 Sample(TextureCube tex, SamplerState sampler, vec3 uv); __intrinsic vec4 Sample(Texture3D tex, SamplerState sampler, vec3 uv); __intrinsic vec4 Sample(Texture2DArray tex, SamplerState sampler, vec3 uv); __intrinsic vec4 Sample(TextureCubeArray tex, SamplerState sampler, vec4 uv); __intrinsic vec4 SampleLevel(Texture2D tex, SamplerState sampler, vec2 uv, float lod); __intrinsic vec4 SampleLevel(TextureCube tex, SamplerState sampler, vec3 uv, float lod); __intrinsic vec4 SampleLevel(Texture3D tex, SamplerState sampler, vec3 uv, float lod); __intrinsic vec4 SampleLevel(TextureCubeArray tex, SamplerState sampler, vec4 uv, float lod); __intrinsic float SampleCmp(Texture2DShadow tex, SamplerComparisonState s, vec2 location, float compareValue, ivec2 offset); __intrinsic float SampleCmp(Texture2DShadow tex, SamplerComparisonState s, vec2 location, float compareValue); __intrinsic float SampleCmp(Texture2DArrayShadow tex, SamplerComparisonState s, vec3 location, float compareValue, ivec2 offset); __intrinsic float SampleCmp(Texture2DArrayShadow tex, SamplerComparisonState s, vec3 location, float compareValue); __intrinsic float SampleCmp(TextureCubeShadowArray tex, SamplerComparisonState s, vec4 location, float compareValue); __intrinsic vec4 SampleGrad(Texture2D tex, SamplerState sampler, vec2 uv, vec2 ddx, vec2 ddy); __intrinsic vec4 SampleGrad(Texture2D tex, SamplerState sampler, vec2 uv, vec2 ddx, vec2 ddy, ivec2 offset); __intrinsic vec4 SampleGrad(TextureCube tex, SamplerState sampler, vec3 uv, vec3 ddx, vec3 ddy); __intrinsic vec4 SampleGrad(TextureCubeArray tex, SamplerState sampler, vec4 uv, vec3 ddx, vec3 ddy); __intrinsic vec4 SampleBias(Texture2D tex, SamplerState sampler, vec2 uv, float bias); __intrinsic vec4 SampleBias(Texture2D tex, SamplerState sampler, vec2 uv, float bias, ivec2 offset); __intrinsic vec4 SampleBias(TextureCube tex, SamplerState sampler, vec3 uv, float bias); __intrinsic vec4 SampleBias(TextureCubeArray tex, SamplerState sampler, vec4 uv, float bias); __intrinsic vec4 SampleBias(Texture2DArray tex, SamplerState sampler, vec3 uv, float bias); __intrinsic vec4 Load(Texture2D tex, int3 location); __intrinsic vec4 Load(Texture2D tex, int3 location, ivec2 offset); __intrinsic vec4 Load(Texture3D tex, int4 location); __intrinsic float diff(float v); __intrinsic float mod(float x, float y); __intrinsic float max(float v); __intrinsic float min(float v); __intrinsic float max(float v, float v1); __intrinsic float min(float v, float v1); __intrinsic vec2 max(vec2 v, vec2 v1); __intrinsic vec2 min(vec2 v, vec2 v1); __intrinsic vec3 max(vec3 v, vec3 v1); __intrinsic vec3 min(vec3 v, vec3 v1); __intrinsic vec4 max(vec4 v, vec4 v1); __intrinsic vec4 min(vec4 v, vec4 v1); __intrinsic vec2 max(vec2 v, float v1); __intrinsic vec2 min(vec2 v, float v1); __intrinsic vec3 max(vec3 v, float v1); __intrinsic vec3 min(vec3 v, float v1); __intrinsic vec4 max(vec4 v, float v1); __intrinsic vec4 min(vec4 v, float v1); __intrinsic float clamp(float v, float v1, float v2); __intrinsic vec2 clamp(vec2 v, vec2 v1, vec2 v2); __intrinsic vec3 clamp(vec3 v, vec3 v1, vec3 v2); __intrinsic vec4 clamp(vec4 v, vec4 v1, vec4 v2); __intrinsic vec2 clamp(vec2 v, float v1, float v2); __intrinsic vec3 clamp(vec3 v, float v1, float v2); __intrinsic vec4 clamp(vec4 v, float v1, float v2); __intrinsic vec3 reflect(vec3 I, vec3 N); __intrinsic vec3 refract(vec3 I, vec3 N, float eta); __intrinsic float length(vec2 v); __intrinsic float length(vec3 v); __intrinsic float length(vec4 v); __intrinsic void alphaTest(float alpha, float threshold); __intrinsic vec3 mix(vec3 v0, vec3 v1, float t); __intrinsic vec4 mix(vec4 v0, vec4 v1, float t); __intrinsic vec2 mix(vec2 v0, vec2 v1, float t); __intrinsic float mix(float v0, float v1, float t); __intrinsic vec3 mix(vec3 v0, vec3 v1, vec3 t); __intrinsic vec4 mix(vec4 v0, vec4 v1, vec4 t); __intrinsic vec2 mix(vec2 v0, vec2 v1, vec2 t); __intrinsic vec3 lerp(vec3 v0, vec3 v1, float t); __intrinsic vec4 lerp(vec4 v0, vec4 v1, float t); __intrinsic vec2 lerp(vec2 v0, vec2 v1, float t); __intrinsic float lerp(float v0, float v1, float t); __intrinsic vec3 lerp(vec3 v0, vec3 v1, vec3 t); __intrinsic vec4 lerp(vec4 v0, vec4 v1, vec4 t); __intrinsic vec2 lerp(vec2 v0, vec2 v1, vec2 t); __intrinsic mat3 mat3(vec3 a, vec3 b, vec3 c); __intrinsic mat3 mat3(float a0, float a1, float a2, float a3, float a4, float a5, float a6, float a7, float a8); __intrinsic mat4 mat4(vec4 a, vec4 b, vec4 c, vec4 d); __intrinsic mat4 mat4(float a0, float a1, float a2, float a3, float a4, float a5, float a6, float a7, float a8, float a9, float a10, float a11, float a12, float a13, float a14, float a15); __intrinsic vec3 cross(vec3 v1, vec3 v2); __intrinsic float float(float v); __intrinsic int int(int v); __intrinsic uint uint(uint v); __intrinsic bool bool(bool v); __intrinsic vec2 vec2(float v); __intrinsic vec3 vec3(float v); __intrinsic vec4 vec4(float v); __intrinsic vec2 vec2(float x, float y); __intrinsic vec3 vec3(float x, float y, float z); __intrinsic vec3 vec3(vec2 v, float z); __intrinsic vec4 vec4(float x, float y, float z, float w); __intrinsic vec4 vec4(vec3 v, float w); __intrinsic vec4 vec4(vec2 v, float z, float w); __intrinsic vec4 vec4(vec2 v, vec2 w); __intrinsic ivec2 ivec2(int x, int y); __intrinsic ivec3 ivec3(int x, int y, int z); __intrinsic ivec3 ivec3(ivec2 v, int z); __intrinsic ivec4 ivec4(int x, int y, int z, int w); __intrinsic ivec4 ivec4(ivec3 v, int w); __intrinsic ivec4 ivec4(ivec2 v, int z, int w); __intrinsic ivec4 ivec4(ivec2 v, ivec2 w); __intrinsic uvec2 uvec2(uint x, uint y); __intrinsic uvec3 uvec3(uint x, uint y, uint z); __intrinsic uvec3 uvec3(uvec2 v, uint z); __intrinsic uvec4 uvec4(uint x, uint y, uint z, uint w); __intrinsic uvec4 uvec4(uvec3 v, uint w); __intrinsic uvec4 uvec4(uvec2 v, uint z, uint w); __intrinsic uvec4 uvec4(uvec2 v, uvec2 w); __intrinsic int int(uint val); __intrinsic int int(float val); __intrinsic ivec2 ivec2(uvec2 val); __intrinsic ivec2 ivec2(vec2 val); __intrinsic ivec3 ivec3(uvec3 val); __intrinsic ivec3 ivec3(vec3 val); __intrinsic ivec4 ivec4(uvec4 val); __intrinsic ivec4 ivec4(vec4 val); __intrinsic uint uint(int val); __intrinsic uint uint(float val); __intrinsic uvec2 uvec2(ivec2 val); __intrinsic uvec2 uvec2(vec2 val); __intrinsic uvec3 uvec3(ivec3 val); __intrinsic uvec3 uvec3(vec3 val); __intrinsic uvec4 uvec4(ivec4 val); __intrinsic uvec4 uvec4(vec4 val); __intrinsic float float(int val); __intrinsic float float(uint val); __intrinsic vec2 vec2(ivec2 val); __intrinsic vec2 vec2(uvec2 val); __intrinsic vec3 vec3(ivec3 val); __intrinsic vec3 vec3(uvec3 val); __intrinsic vec4 vec4(ivec4 val); __intrinsic vec4 vec4(uvec4 val); __intrinsic mat3 transpose(mat3 in); __intrinsic mat4 transpose(mat4 in); __intrinsic mat3 mat3(mat4 in); __intrinsic float saturate(float v); __intrinsic vec2 saturate(vec2 v); __intrinsic vec3 saturate(vec3 v); __intrinsic vec4 saturate(vec4 v); struct trait __intrinsic {}; __intrinsic trait IsTriviallyPassable(float); __intrinsic trait IsTriviallyPassable(vec2); __intrinsic trait IsTriviallyPassable(vec3); __intrinsic trait IsTriviallyPassable(vec4); __intrinsic trait IsTriviallyPassable(mat3); __intrinsic trait IsTriviallyPassable(mat4); __intrinsic trait IsTriviallyPassable(int); __intrinsic trait IsTriviallyPassable(ivec2); __intrinsic trait IsTriviallyPassable(ivec3); __intrinsic trait IsTriviallyPassable(ivec4); __intrinsic trait IsTriviallyPassable(uint); __intrinsic trait IsTriviallyPassable(uvec2); __intrinsic trait IsTriviallyPassable(uvec3); __intrinsic trait IsTriviallyPassable(uvec4); __intrinsic trait IsTriviallyPassable(bool); #line default )"; using namespace CoreLib::Basic; namespace Spire { namespace Compiler { String SpireStdLib::code; String SpireStdLib::GetCode() { if (code.Length() > 0) return code; StringBuilder sb; // generate operator overloads Operator floatUnaryOps[] = { Operator::Neg, Operator::Not, Operator::PreInc, Operator::PreDec }; Operator intUnaryOps[] = { Operator::Neg, Operator::Not, Operator::BitNot, Operator::PreInc, Operator::PreDec}; Operator floatOps[] = { Operator::Mul, Operator::Div, Operator::Add, Operator::Sub, Operator::And, Operator::Or, Operator::Eql, Operator::Neq, Operator::Greater, Operator::Less, Operator::Geq, Operator::Leq }; Operator intOps[] = { Operator::Mul, Operator::Div, Operator::Mod, Operator::Add, Operator::Sub, Operator::Lsh, Operator::Rsh, Operator::Eql, Operator::Neq, Operator::Greater, Operator::Less, Operator::Geq, Operator::Leq, Operator::BitAnd, Operator::BitXor, Operator::BitOr, Operator::And, Operator::Or }; String floatTypes[] = { "float", "vec2", "vec3", "vec4" }; String intTypes[] = { "int", "ivec2", "ivec3", "ivec4" }; String uintTypes[] = { "uint", "uvec2", "uvec3", "uvec4" }; sb << "__intrinsic vec3 operator * (vec3, mat3);\n"; sb << "__intrinsic vec3 operator * (mat3, vec3);\n"; sb << "__intrinsic vec4 operator * (vec4, mat4);\n"; sb << "__intrinsic vec4 operator * (mat4, vec4);\n"; sb << "__intrinsic mat3 operator * (mat3, mat3);\n"; sb << "__intrinsic mat4 operator * (mat4, mat4);\n"; sb << "__intrinsic bool operator && (bool, bool);\n"; sb << "__intrinsic bool operator || (bool, bool);\n"; for (auto type : intTypes) { sb << "__intrinsic bool operator && (bool, " << type << ");\n"; sb << "__intrinsic bool operator || (bool, " << type << ");\n"; sb << "__intrinsic bool operator && (" << type << ", bool);\n"; sb << "__intrinsic bool operator || (" << type << ", bool);\n"; } for (auto op : intUnaryOps) { String opName = GetOperatorFunctionName(op); for (int i = 0; i < 4; i++) { auto itype = intTypes[i]; auto utype = uintTypes[i]; for (int j = 0; j < 2; j++) { auto retType = (op == Operator::Not) ? "bool" : j == 0 ? itype : utype; sb << "__intrinsic " << retType << " operator " << opName << "(" << (j == 0 ? itype : utype) << ");\n"; } } } for (auto op : floatUnaryOps) { String opName = GetOperatorFunctionName(op); for (int i = 0; i < 4; i++) { auto type = floatTypes[i]; auto retType = (op == Operator::Not) ? "bool" : type; sb << "__intrinsic " << retType << " operator " << opName << "(" << type << ");\n"; } } for (auto op : floatOps) { String opName = GetOperatorFunctionName(op); for (int i = 0; i < 4; i++) { auto type = floatTypes[i]; auto itype = intTypes[i]; auto utype = uintTypes[i]; auto retType = ((op >= Operator::Eql && op <= Operator::Leq) || op == Operator::And || op == Operator::Or) ? "bool" : type; sb << "__intrinsic " << retType << " operator " << opName << "(" << type << ", " << type << ");\n"; sb << "__intrinsic " << retType << " operator " << opName << "(" << itype << ", " << type << ");\n"; sb << "__intrinsic " << retType << " operator " << opName << "(" << utype << ", " << type << ");\n"; sb << "__intrinsic " << retType << " operator " << opName << "(" << type << ", " << itype << ");\n"; sb << "__intrinsic " << retType << " operator " << opName << "(" << type << ", " << utype << ");\n"; if (i > 0) { sb << "__intrinsic " << retType << " operator " << opName << "(" << type << ", " << floatTypes[0] << ");\n"; sb << "__intrinsic " << retType << " operator " << opName << "(" << floatTypes[0] << ", " << type << ");\n"; sb << "__intrinsic " << retType << " operator " << opName << "(" << type << ", " << intTypes[0] << ");\n"; sb << "__intrinsic " << retType << " operator " << opName << "(" << intTypes[0] << ", " << type << ");\n"; sb << "__intrinsic " << retType << " operator " << opName << "(" << type << ", " << uintTypes[0] << ");\n"; sb << "__intrinsic " << retType << " operator " << opName << "(" << uintTypes[0] << ", " << type << ");\n"; } } } for (auto op : intOps) { String opName = GetOperatorFunctionName(op); for (int i = 0; i < 4; i++) { auto type = intTypes[i]; auto utype = uintTypes[i]; auto retType = ((op >= Operator::Eql && op <= Operator::Leq) || op == Operator::And || op == Operator::Or) ? "bool" : type; sb << "__intrinsic " << retType << " operator " << opName << "(" << type << ", " << type << ");\n"; sb << "__intrinsic " << retType << " operator " << opName << "(" << utype << ", " << type << ");\n"; sb << "__intrinsic " << retType << " operator " << opName << "(" << type << ", " << utype << ");\n"; sb << "__intrinsic " << retType << " operator " << opName << "(" << utype << ", " << utype << ");\n"; if (i > 0) { sb << "__intrinsic " << retType << " operator " << opName << "(" << type << ", " << intTypes[0] << ");\n"; sb << "__intrinsic " << retType << " operator " << opName << "(" << intTypes[0] << ", " << type << ");\n"; sb << "__intrinsic " << retType << " operator " << opName << "(" << type << ", " << uintTypes[0] << ");\n"; sb << "__intrinsic " << retType << " operator " << opName << "(" << uintTypes[0] << ", " << type << ");\n"; } } } sb << LibIncludeString; code = sb.ProduceString(); return code; } void SpireStdLib::Finalize() { code = nullptr; } } } ================================================ FILE: Source/SpireCore/StdInclude.h ================================================ #ifndef SHADER_COMPILER_STD_LIB_H #define SHADER_COMPILER_STD_LIB_H #include "../CoreLib/Basic.h" namespace Spire { namespace Compiler { class SpireStdLib { private: static CoreLib::String code; public: static CoreLib::String GetCode(); static void Finalize(); }; } } #endif ================================================ FILE: Source/SpireCore/StringObject.h ================================================ #ifndef SPIRE_STRING_OBJECT_H #define SPIRE_STRING_OBJECT_H #include "../CoreLib/Basic.h" namespace Spire { namespace Compiler { class StringObject : public CoreLib::Object { public: CoreLib::String Content; StringObject() {} StringObject(const CoreLib::String & str) : Content(str) {} }; } } #endif ================================================ FILE: Source/SpireCore/SymbolTable.cpp ================================================ #include "SymbolTable.h" #include "ShaderCompiler.h" namespace Spire { namespace Compiler { bool SymbolTable::SortShaders() { HashSet shaderSet; ShaderDependenceOrder.Clear(); List nextShaders, currentShaders; // sort shaders in dependency order for (auto & shader : Shaders) { if (shader.Value->DependentShaders.Count() == 0) { ShaderDependenceOrder.Add(shader.Value.Ptr()); shaderSet.Add(shader.Value.Ptr()); } else currentShaders.Add(shader.Value.Ptr()); } while (currentShaders.Count()) { nextShaders.Clear(); for (auto & shader : currentShaders) { bool pass = true; for (auto & dshader : shader->DependentShaders) if (!shaderSet.Contains(dshader)) { pass = false; break; } if (pass) { ShaderDependenceOrder.Add(shader); shaderSet.Add(shader); } else nextShaders.Add(shader); } currentShaders.SwapWith(nextShaders); } return (ShaderDependenceOrder.Count() == Shaders.Count()); } void SymbolTable::EvalFunctionReferenceClosure() { for (auto & func : Functions) { if (!func.Value->IsReferencedFunctionsTransitiveClosureEvaluated) { List funcList; EnumerableHashSet funcSet; for (auto & ref : func.Value->ReferencedFunctions) { funcList.Add(ref); funcSet.Add(ref); } for (int i = 0; i < funcList.Count(); i++) { RefPtr funcSym; if (Functions.TryGetValue(funcList[i], funcSym)) { if (funcSym->IsReferencedFunctionsTransitiveClosureEvaluated) { for (auto rfunc : funcSym->ReferencedFunctions) funcSet.Add(rfunc); } else { for (auto rfunc : funcSym->ReferencedFunctions) { if (funcSet.Add(rfunc)) funcList.Add(rfunc); } } } } func.Value->ReferencedFunctions = _Move(funcSet); func.Value->IsReferencedFunctionsTransitiveClosureEvaluated = true; } } } List& PipelineSymbol::GetPaths(String srcWorld, String destWorld) { if (auto first = pathCache.TryGetValue(srcWorld)) { if (auto second = first->TryGetValue(destWorld)) return *second; } else { pathCache[srcWorld] = EnumerableDictionary>(); } auto path = FindPaths(srcWorld, destWorld); auto & dict = pathCache[srcWorld](); dict[destWorld] = _Move(path); return dict[destWorld](); } List PipelineSymbol::FindPaths(String worldSrc, String worldDest) { List resultPaths; if (worldSrc == worldDest) return resultPaths; List paths, paths2; paths.Add(ImportPath()); paths[0].Nodes.Add(ImportPath::Node(worldSrc, nullptr)); while (paths.Count()) { paths2.Clear(); for (auto & p : paths) { String world0 = p.Nodes.Last().TargetWorld; for (auto op : SyntaxNode->GetImportOperators()) { if (op->SourceWorld.Content == world0) { ImportPath np = p; if (op->GetParameters().Count() != 0) np.IsImplicitPath = false; for (auto &req : op->Requirements) np.TypeRequirements.Add(req.Ptr()); np.Nodes.Add(ImportPath::Node(op->DestWorld.Content, op.Ptr())); if (op->DestWorld.Content == worldDest) resultPaths.Add(np); else paths2.Add(np); } } } paths.SwapWith(paths2); } return resultPaths; } bool PipelineSymbol::IsAbstractWorld(String world) { WorldSyntaxNode* worldDecl; if (Worlds.TryGetValue(world, worldDecl)) return worldDecl->IsAbstract(); return false; } bool PipelineSymbol::IsChildOf(PipelineSymbol * parentPipeline) { if (this == parentPipeline) return true; else if (ParentPipeline) return ParentPipeline->IsChildOf(parentPipeline); else return false; } List& PipelineSymbol::GetWorldTopologyOrder() { if (WorldTopologyOrder.Count() != 0) return WorldTopologyOrder; List rs; HashSet rsSet; bool changed = true; while (changed) { changed = false; for (auto & w : WorldDependency) { if (!rsSet.Contains(w.Key)) { bool canAdd = true; for (auto & dw : w.Value) if (!rsSet.Contains(dw)) { canAdd = false; break; } if (canAdd) { rsSet.Add(w.Key); rs.Add(w.Key); changed = true; } } } } WorldTopologyOrder = _Move(rs); return WorldTopologyOrder; } List PipelineSymbol::GetImportOperatorsFromSourceWorld(String worldSrc) { List rs; auto dict = ImportOperatorsByPath.TryGetValue(worldSrc); if (dict) { for (auto & op : *dict) { for (auto & x : op.Value) rs.Add(x.Ptr()); } } return rs; } void PipelineSymbol::AddImportOperator(RefPtr op) { auto list = ImportOperators.TryGetValue(op->Name.Content); if (!list) { ImportOperators[op->Name.Content] = List>(); list = ImportOperators.TryGetValue(op->Name.Content); } list->Add(op); auto first = ImportOperatorsByPath.TryGetValue(op->SourceWorld.Content); if (!first) { ImportOperatorsByPath[op->SourceWorld.Content] = EnumerableDictionary>>(); first = ImportOperatorsByPath.TryGetValue(op->SourceWorld.Content); } auto second = first->TryGetValue(op->DestWorld.Content); if (!second) { (*first)[op->DestWorld.Content] = List>(); second = first->TryGetValue(op->DestWorld.Content); } second->Add(op); } List ShaderSymbol::GetComponentDependencyOrder() { List components; for (auto & comp : Components) { components.Add(comp.Value.Ptr()); } SortComponents(components); return components; } void ShaderSymbol::SortComponents(List& comps) { comps.Sort([&](ShaderComponentSymbol*c0, ShaderComponentSymbol*c1) { return c0->Implementations.First()->SyntaxNode->Position < c1->Implementations.First()->SyntaxNode->Position; }); HashSet allSymbols, addedSymbols; for (auto & comp : comps) allSymbols.Add(comp); List sorted; bool changed = true; while (changed) { changed = false; for (auto & comp : comps) { if (!addedSymbols.Contains(comp)) { bool isFirst = true; for (auto & impl : comp->Implementations) for (auto & dep : impl->DependentComponents) if (allSymbols.Contains(dep.Key) && !addedSymbols.Contains(dep.Key)) { isFirst = false; goto loopEnd; } loopEnd:; if (isFirst) { addedSymbols.Add(comp); sorted.Add(comp); changed = true; } } } } comps = _Move(sorted); } ShaderSymbol::ComponentReference ShaderSymbol::ResolveComponentReference(String compName, bool topLevel) { ComponentReference result; result.IsAccessible = true; RefPtr refComp, privateRefComp; if (Components.TryGetValue(compName, refComp)) { result.Component = refComp.Ptr(); return result; } for (auto & shaderUsing : ShaderUsings) { if (shaderUsing.Shader->Components.TryGetValue(compName, refComp)) { if (refComp->Implementations.First()->SyntaxNode->IsPublic()) { result.Component = refComp.Ptr(); result.IsAccessible = true; return result; } else { result.Component = refComp.Ptr(); result.IsAccessible = false; } } else if (shaderUsing.IsPublic || topLevel) { auto rresult = shaderUsing.Shader->ResolveComponentReference(compName, false); if (rresult.IsAccessible) return rresult; else result = rresult; } } if (ParentPipeline && ParentPipeline->Components.TryGetValue(compName, refComp)) { if (!refComp->IsRequire()) { result.Component = refComp.Ptr(); // HACK(tfoley): I'm not sure if this is a valid bug fix or not. result.IsAccessible = true; return result; } } result.IsAccessible = false; return result; } bool SymbolTable::CheckComponentImplementationConsistency(DiagnosticSink * err, ShaderComponentSymbol * comp, ShaderComponentImplSymbol * impl) { bool rs = true; if (impl->SyntaxNode->Rate) { for (auto & cimpl : comp->Implementations) { for (auto & w : cimpl->Worlds) if (impl->Worlds.Contains(w)) { err->diagnose(impl->SyntaxNode->Position, Diagnostics::componentIsAlreadyDefinedInThatWorld, comp->Name, w); rs = false; } } } else { for (auto & cimpl : comp->Implementations) { if (cimpl->Worlds.Count() == 0 && impl->Worlds.Count() == 0) { err->diagnose(impl->SyntaxNode->Position, Diagnostics::componentIsAlreadyDefined, comp->Name); rs = false; } } } for (auto & cimpl : comp->Implementations) { if (impl->SyntaxNode->IsOutput() != cimpl->SyntaxNode->IsOutput()) { err->diagnose(impl->SyntaxNode->Position, Diagnostics::inconsistentSignatureForComponent, comp->Name); err->diagnose(cimpl->SyntaxNode->Position, Diagnostics::seePreviousDefinition); rs = false; break; } if (impl->SyntaxNode->IsRequire() != cimpl->SyntaxNode->IsRequire()) { err->diagnose(impl->SyntaxNode->Position, Diagnostics::inconsistentSignatureForComponent, comp->Name); err->diagnose(cimpl->SyntaxNode->Position, Diagnostics::seePreviousDefinition); rs = false; break; } if (impl->SyntaxNode->IsPublic() != cimpl->SyntaxNode->IsPublic()) { err->diagnose(impl->SyntaxNode->Position, Diagnostics::inconsistentSignatureForComponent, comp->Name); err->diagnose(cimpl->SyntaxNode->Position, Diagnostics::seePreviousDefinition); rs = false; break; } if (!impl->SyntaxNode->Type->Equals(cimpl->SyntaxNode->Type.Ptr())) { err->diagnose(impl->SyntaxNode->Position, Diagnostics::inconsistentSignatureForComponent, comp->Name); err->diagnose(cimpl->SyntaxNode->Position, Diagnostics::seePreviousDefinition); rs = false; break; } } if (impl->SyntaxNode->IsRequire() && comp->Implementations.Count() != 0) { err->diagnose(impl->SyntaxNode->Position, Diagnostics::parameterNameConflictsWithExistingDefinition, comp->Name); rs = false; } return rs; } String PrintType(RefPtr type, String recordReplaceStr) { if (auto basic = type->AsBasicType()) { if (basic->BaseType == BaseType::Generic) return recordReplaceStr; else return basic->ToString(); } else if (auto arr = type.As()) { if (arr->ArrayLength > 0) return PrintType(arr->BaseType, recordReplaceStr) + "[" + arr->ArrayLength + "]"; else return PrintType(arr->BaseType, recordReplaceStr) + "[]"; } else if (auto gen = type.As()) { return gen->GenericTypeName + "<" + PrintType(gen->BaseType, recordReplaceStr) + ">"; } return ""; } bool SymbolTable::CheckTypeRequirement(const ImportPath & p, RefPtr type) { for (auto & req : p.TypeRequirements) { auto typeStr = type->ToString(); auto retType = PrintType(req->ReturnType, typeStr); StringBuilder sbInternalName; sbInternalName << req->Name.Content; for (auto & op : req->GetParameters()) { sbInternalName << "@" << PrintType(op->Type, typeStr); } auto funcName = sbInternalName.ProduceString(); auto func = Functions.TryGetValue(funcName); if (!func) return false; if ((*func)->SyntaxNode->ReturnType->ToString() != retType) return false; } return true; } bool SymbolTable::IsWorldReachable(PipelineSymbol * pipe, String src, String targetWorld, RefPtr type) { if (src == targetWorld) return true; return From(pipe->GetPaths(src, targetWorld)).Any([&](const ImportPath & p) { return CheckTypeRequirement(p, type); }); } bool SymbolTable::IsWorldImplicitlyReachable(PipelineSymbol * pipe, String src, String targetWorld, RefPtr type) { if (src == targetWorld) return true; return From(pipe->GetPaths(src, targetWorld)).Any([&](const ImportPath & p) { return p.IsImplicitPath && CheckTypeRequirement(p, type); }); } bool SymbolTable::IsWorldImplicitlyReachable(PipelineSymbol * pipe, EnumerableHashSet& src, String targetWorld, RefPtr type) { for (auto srcW : src) { if (IsWorldImplicitlyReachable(pipe, srcW, targetWorld, type)) return true; } return false; } bool SymbolTable::IsWorldReachable(PipelineSymbol * pipe, EnumerableHashSet& src, String targetWorld, RefPtr type) { for (auto srcW : src) { if (IsWorldReachable(pipe, srcW, targetWorld, type)) return true; } return false; } List SymbolTable::FindImplicitImportOperatorChain(PipelineSymbol * pipe, String worldSrc, String worldDest, RefPtr type) { return From(pipe->GetPaths(worldSrc, worldDest)).Where([&](const ImportPath & p) { if (p.IsImplicitPath) return CheckTypeRequirement(p, type); return false; }).ToList(); } Decl* SymbolTable::LookUp(String const& name) { Decl* decl = nullptr; if (globalDecls.TryGetValue(name, decl)) return decl; return nullptr; } void SymbolTable::MergeWith(SymbolTable & symTable) { for (auto & f : symTable.FunctionOverloads) FunctionOverloads[f.Key] = f.Value; for (auto & f : symTable.Functions) Functions[f.Key] = f.Value; for (auto & f : symTable.Shaders) Shaders[f.Key] = f.Value; for (auto & f : symTable.Pipelines) Pipelines[f.Key] = f.Value; for (auto & f : symTable.globalDecls) globalDecls[f.Key] = f.Value; SortShaders(); } int UniqueIdGenerator::currentGUID = 0; void UniqueIdGenerator::Clear() { currentGUID = 0; } int UniqueIdGenerator::Next() { return currentGUID++; } RefPtr ShaderClosure::FindComponent(String name, bool findInPrivate, bool includeParams) { RefPtr rs; if (RefMap.TryGetValue(name, rs)) { if (includeParams || !rs->IsRequire()) return rs; else return nullptr; } if (Components.TryGetValue(name, rs)) { if (includeParams || !rs->IsRequire()) return rs; else return nullptr; } for (auto & subClosure : SubClosures) { if (subClosure.Value->IsInPlace) { rs = subClosure.Value->FindComponent(name, findInPrivate, includeParams); if (rs && (findInPrivate || rs->Implementations.First()->SyntaxNode->IsPublic())) return rs; else rs = nullptr; } } ShaderClosure * root = this; while (root->Parent != nullptr) root = root->Parent; if (root != this) { // find global components in root (pipeline-defined components) if (root->Components.TryGetValue(name, rs)) return rs; } return rs; } RefPtr ShaderClosure::FindClosure(String name) { RefPtr rs; if (SubClosures.TryGetValue(name, rs)) return rs; for (auto & subClosure : SubClosures) { if (subClosure.Value->IsInPlace) { rs = subClosure.Value->FindClosure(name); if (rs && rs->IsPublic) return rs; else rs = nullptr; } } return rs; } List ShaderClosure::GetDependencyOrder() { List comps; for (auto & comp : AllComponents) comps.Add(comp.Value.Symbol); comps.Sort([&](ShaderComponentSymbol*c0, ShaderComponentSymbol*c1) { return c0->Implementations.First()->SyntaxNode->Position < c1->Implementations.First()->SyntaxNode->Position; }); HashSet allSymbols, addedSymbols; for (auto & comp : comps) allSymbols.Add(comp); List sorted; bool changed = true; while (changed) { changed = false; for (auto & comp : comps) { if (!addedSymbols.Contains(comp)) { bool isFirst = true; for (auto & impl : comp->Implementations) for (auto & dep : impl->DependentComponents) if (allSymbols.Contains(dep.Key) && !addedSymbols.Contains(dep.Key)) { isFirst = false; goto loopEnd; } loopEnd:; if (isFirst) { addedSymbols.Add(comp); sorted.Add(comp); changed = true; } } } } return sorted; } } } ================================================ FILE: Source/SpireCore/SymbolTable.h ================================================ #ifndef RASTER_RENDERER_SYMBOL_TABLE_H #define RASTER_RENDERER_SYMBOL_TABLE_H #include "../CoreLib/Basic.h" #include "Syntax.h" #include "IL.h" #include "VariantIR.h" namespace Spire { namespace Compiler { class FunctionSymbol { public: bool IsReferencedFunctionsTransitiveClosureEvaluated = false; FunctionSyntaxNode * SyntaxNode; EnumerableHashSet ReferencedFunctions; }; class ShaderComponentSymbol; class ShaderComponentImplSymbol : public RefObject { public: EnumerableHashSet Worlds, ExportWorlds, SrcPinnedWorlds; RefPtr SyntaxNode; EnumerableDictionary>> DependentComponents; // key: dependent components, value: set of import expression nodes (null means implicit reference) EnumerableDictionary ComponentReferencePositions; ShaderComponentImplSymbol() = default; ShaderComponentImplSymbol(const ShaderComponentImplSymbol & other) { Worlds = other.Worlds; ExportWorlds = other.ExportWorlds; SrcPinnedWorlds = other.SrcPinnedWorlds; CloneContext ctx; SyntaxNode = other.SyntaxNode->Clone(ctx); } }; class ShaderComponentSymbol : public RefObject { public: bool IsDceEntryPoint = false; String Name, UniqueName, UniqueKey; List ChoiceNames; EnumerableDictionary>> DependentComponents; List> Implementations; RefPtr Type; bool IsRequire() { for (auto & impl : Implementations) if (impl->SyntaxNode->IsRequire()) return true; return false; } ShaderComponentSymbol() = default; ShaderComponentSymbol(const ShaderComponentSymbol & other) { Type = new Spire::Compiler::Type(*other.Type); for (auto &impl : other.Implementations) this->Implementations.Add(new ShaderComponentImplSymbol(*impl)); this->Name = other.Name; } }; class PipelineSymbol; class ShaderClosure; class ShaderSymbol; class ShaderUsing { public: ShaderSymbol * Shader; bool IsPublic; }; class ShaderSymbolBase { public: PipelineSymbol * ParentPipeline = nullptr; // components that are functions, they are also listed in Components, index by original names EnumerableDictionary>> FunctionComponents; // all components in this shader, function components are indexed by their unique names EnumerableDictionary> Components; }; class ShaderSymbol : public ShaderSymbolBase { public: bool IsAbstract = false; bool SemanticallyChecked = false; RefPtr SyntaxNode = nullptr; List GetComponentDependencyOrder(); EnumerableHashSet DependentShaders; List ShaderUsings; EnumerableDictionary ShaderObjects; void SortComponents(List & comps); struct ComponentReference { ShaderComponentSymbol * Component = nullptr; bool IsAccessible = false; }; ComponentReference ResolveComponentReference(String compName, bool topLevel = true); }; class ShaderClosure; class ComponentInstance { public: ShaderComponentSymbol * Symbol = nullptr; ShaderClosure * Closure = nullptr; ComponentInstance() = default; ComponentInstance(ShaderClosure * closure, ShaderComponentSymbol * comp) { Closure = closure; Symbol = comp; } }; class ShaderClosure : public Object { public: ShaderClosure * Parent = nullptr; ShaderSyntaxNode * ModuleSyntaxNode = nullptr; CodePosition Position; PipelineSymbol * Pipeline = nullptr; int BindingIndex = -1; bool IsInPlace = false; bool IsPublic = false; String Name; CodePosition UsingPosition; EnumerableDictionary> RefMap; EnumerableDictionary> Components; EnumerableDictionary AllComponents; EnumerableDictionary> SubClosures; RefPtr FindComponent(String name, bool findInPrivate = false, bool includeParams = true); RefPtr FindClosure(String name); List GetDependencyOrder(); RefPtr IR; }; class ImportPath { public: class Node { public: String TargetWorld; ImportOperatorDefSyntaxNode * ImportOperator; Node() = default; Node(String world, ImportOperatorDefSyntaxNode * imp) : TargetWorld(world), ImportOperator(imp) {} }; bool IsImplicitPath = true; EnumerableHashSet TypeRequirements; List Nodes; }; class PipelineSymbol : public ShaderSymbolBase { private: List WorldTopologyOrder; EnumerableDictionary>> pathCache; List FindPaths(String worldSrc, String worldDest); public: PipelineSyntaxNode * SyntaxNode; PipelineSymbol * ParentPipeline; EnumerableDictionary>> ImportOperators; // SourceWorld=>DestinationWorld=>ImportOperator EnumerableDictionary>>> ImportOperatorsByPath; EnumerableDictionary> WorldDependency; EnumerableDictionary Worlds; bool IsAbstractWorld(String world); bool IsChildOf(PipelineSymbol * parentPipeline); List & GetWorldTopologyOrder(); List & GetPaths(String srcWorld, String destWorld); List GetImportOperatorsFromSourceWorld(String worldSrc); void AddImportOperator(RefPtr op); }; class CompileResult; class SymbolTable { private: bool CheckTypeRequirement(const ImportPath & p, RefPtr type); public: EnumerableDictionary>> FunctionOverloads; // indexed by original name EnumerableDictionary> Functions; // indexed by internal name EnumerableDictionary> Shaders; EnumerableDictionary> Pipelines; EnumerableDictionary globalDecls; List ShaderDependenceOrder; bool SortShaders(); // return true if success, return false if dependency is cyclic void EvalFunctionReferenceClosure(); bool CheckComponentImplementationConsistency(DiagnosticSink * sink, ShaderComponentSymbol * comp, ShaderComponentImplSymbol * impl); bool IsWorldReachable(PipelineSymbol * pipe, EnumerableHashSet & src, String targetWorld, RefPtr type); bool IsWorldReachable(PipelineSymbol * pipe, String src, String targetWorld, RefPtr type); bool IsWorldImplicitlyReachable(PipelineSymbol * pipe, EnumerableHashSet & src, String targetWorld, RefPtr type); bool IsWorldImplicitlyReachable(PipelineSymbol * pipe, String src, String targetWorld, RefPtr type); List FindImplicitImportOperatorChain(PipelineSymbol * pipe, String worldSrc, String worldDest, RefPtr type); Decl* LookUp(String const& name); void MergeWith(SymbolTable & symTable); }; class UniqueIdGenerator { private: static int currentGUID; public: static void Clear(); static int Next(); }; template void DependencySort(List & list, const GetDependencyFunc & getDep) { HashSet allSymbols, addedSymbols; for (auto & comp : list) allSymbols.Add(comp); List sorted; bool changed = true; while (changed) { changed = false; for (auto & comp : list) { if (!addedSymbols.Contains(comp)) { bool isFirst = true; auto && dependency = getDep(comp); for (auto & dep : dependency) if (allSymbols.Contains(dep) && !addedSymbols.Contains(dep)) { isFirst = false; break; } if (isFirst) { addedSymbols.Add(comp); sorted.Add(comp); changed = true; } } } } list = _Move(sorted); } } } #endif ================================================ FILE: Source/SpireCore/Syntax.cpp ================================================ #include "Syntax.h" #include "SyntaxVisitors.h" #include "SymbolTable.h" #include namespace Spire { namespace Compiler { // Scope Decl* Scope::LookUp(String const& name) { Scope* scope = this; while (scope) { for (auto m : scope->containerDecl->Members) { if (m->Name.Content == name) return m.Ptr(); } scope = scope->Parent.Ptr(); } return nullptr; } // Decl bool Decl::FindSimpleAttribute(String const& key, Token& outValue) { for (auto attr : GetLayoutAttributes()) { if (attr->Key == key) { outValue = attr->Value; return true; } } return false; } bool Decl::FindSimpleAttribute(String const& key, String& outValue) { for (auto attr : GetLayoutAttributes()) { if (attr->Key == key) { outValue = attr->Value.Content; return true; } } return false; } bool Decl::HasSimpleAttribute(String const& key) { for (auto attr : GetLayoutAttributes()) { if (attr->Key == key) { return true; } } return false; } SpecializeModifier * Decl::FindSpecializeModifier() { auto list = GetModifiersOfType(); if (list.begin() != list.end()) return *list.begin(); return nullptr; } // bool BasicExpressionType::EqualsImpl(const ExpressionType * type) const { auto basicType = dynamic_cast(type); if (basicType == nullptr) return false; return (basicType->BaseType == BaseType && basicType->Func == Func && basicType->Shader == Shader && basicType->structDecl == structDecl && basicType->RecordTypeName == RecordTypeName); } ExpressionType* BasicExpressionType::CreateCanonicalType() { // A basic type is already canonical, in our setup return this; } BindableResourceType BasicExpressionType::GetBindableResourceType() const { switch (BaseType) { case Compiler::BaseType::Texture2DArray: case Compiler::BaseType::Texture2DArrayShadow: case Compiler::BaseType::Texture2D: case Compiler::BaseType::Texture2DShadow: case Compiler::BaseType::Texture3D: case Compiler::BaseType::TextureCube: case Compiler::BaseType::TextureCubeShadow: case Compiler::BaseType::TextureCubeArray: case Compiler::BaseType::TextureCubeShadowArray: return BindableResourceType::Texture; case Compiler::BaseType::SamplerState: case Compiler::BaseType::SamplerComparisonState: return BindableResourceType::Sampler; } return BindableResourceType::NonBindable; } bool BasicExpressionType::IsVectorTypeImpl() const { return IsVector(BaseType); } CoreLib::Basic::String BasicExpressionType::ToString() const { CoreLib::Basic::StringBuilder res; switch (BaseType) { case Compiler::BaseType::Int: res.Append("int"); break; case Compiler::BaseType::UInt: res.Append("uint"); break; case Compiler::BaseType::Bool: res.Append("bool"); break; case Compiler::BaseType::Float: res.Append("float"); break; case Compiler::BaseType::Int2: res.Append("ivec2"); break; case Compiler::BaseType::UInt2: res.Append("uvec2"); break; case Compiler::BaseType::Float2: res.Append("vec2"); break; case Compiler::BaseType::Int3: res.Append("ivec3"); break; case Compiler::BaseType::UInt3: res.Append("uvec3"); break; case Compiler::BaseType::Float3: res.Append("vec3"); break; case Compiler::BaseType::Int4: res.Append("ivec4"); break; case Compiler::BaseType::UInt4: res.Append("uvec4"); break; case Compiler::BaseType::Float4: res.Append("vec4"); break; case Compiler::BaseType::Float3x3: res.Append("mat3"); break; case Compiler::BaseType::Float4x4: res.Append("mat4"); break; case Compiler::BaseType::Texture2DArray: res.Append("sampler2DArray"); break; case Compiler::BaseType::Texture2DArrayShadow: res.Append("sampler2DArrayShadow"); break; case Compiler::BaseType::Texture2D: res.Append("sampler2D"); break; case Compiler::BaseType::Texture2DShadow: res.Append("sampler2DShadow"); break; case Compiler::BaseType::Texture3D: res.Append("sampler3D"); break; case Compiler::BaseType::TextureCube: res.Append("samplerCube"); break; case Compiler::BaseType::TextureCubeShadow: res.Append("samplerCubeShadow"); break; case Compiler::BaseType::Function: res.Append(Func->SyntaxNode->InternalName); break; case Compiler::BaseType::Shader: res.Append(Shader->SyntaxNode->Name.Content); break; case Compiler::BaseType::Void: res.Append("void"); break; case Compiler::BaseType::Struct: res.Append(structDecl->Name.Content); break; case Compiler::BaseType::Record: res.Append(RecordTypeName); break; case Compiler::BaseType::SamplerState: res.Append("SamplerState"); break; case Compiler::BaseType::Error: res.Append(""); break; default: break; } return res.ProduceString(); } ExpressionType * BasicExpressionType::Clone() { BasicExpressionType * rs = new BasicExpressionType(*this); return rs; } RefPtr ProgramSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitProgram(this); } ProgramSyntaxNode * ProgramSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new ProgramSyntaxNode(*this), ctx); rs->Members.Clear(); for (auto & m : Members) rs->Members.Add(m->Clone(ctx)); return rs; } RefPtr FunctionSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitFunction(this); } FunctionSyntaxNode * FunctionSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new FunctionSyntaxNode(*this), ctx); for (auto & member : rs->Members) { member = member->Clone(ctx); } rs->ReturnTypeNode = ReturnTypeNode->Clone(ctx); rs->Body = Body->Clone(ctx); return rs; } // RefPtr ScopeDecl::Accept(SyntaxVisitor * visitor) { return visitor->VisitScopeDecl(this); } ScopeDecl* ScopeDecl::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new ScopeDecl(*this), ctx); for (auto & member : rs->Members) { member = member->Clone(ctx); } return rs; } // RefPtr BlockStatementSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitBlockStatement(this); } BlockStatementSyntaxNode * BlockStatementSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new BlockStatementSyntaxNode(*this), ctx); rs->Statements.Clear(); for (auto & stmt : Statements) { rs->Statements.Add(stmt->Clone(ctx)); } return rs; } RefPtr BreakStatementSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitBreakStatement(this); } BreakStatementSyntaxNode * BreakStatementSyntaxNode::Clone(CloneContext & ctx) { return CloneSyntaxNodeFields(new BreakStatementSyntaxNode(*this), ctx); } RefPtr ContinueStatementSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitContinueStatement(this); } ContinueStatementSyntaxNode * ContinueStatementSyntaxNode::Clone(CloneContext & ctx) { return CloneSyntaxNodeFields(new ContinueStatementSyntaxNode(*this), ctx); } RefPtr DoWhileStatementSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitDoWhileStatement(this); } DoWhileStatementSyntaxNode * DoWhileStatementSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new DoWhileStatementSyntaxNode(*this), ctx); if (Predicate) rs->Predicate = Predicate->Clone(ctx); if (Statement) rs->Statement = Statement->Clone(ctx); return rs; } RefPtr EmptyStatementSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitEmptyStatement(this); } EmptyStatementSyntaxNode * EmptyStatementSyntaxNode::Clone(CloneContext & ctx) { return CloneSyntaxNodeFields(new EmptyStatementSyntaxNode(*this), ctx); } RefPtr ForStatementSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitForStatement(this); } ForStatementSyntaxNode * ForStatementSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new ForStatementSyntaxNode(*this), ctx); if (InitialStatement) rs->InitialStatement = InitialStatement->Clone(ctx); if (SideEffectExpression) rs->SideEffectExpression = SideEffectExpression->Clone(ctx); if (PredicateExpression) rs->PredicateExpression = PredicateExpression->Clone(ctx); if (Statement) rs->Statement = Statement->Clone(ctx); return rs; } RefPtr IfStatementSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitIfStatement(this); } IfStatementSyntaxNode * IfStatementSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new IfStatementSyntaxNode(*this), ctx); if (Predicate) rs->Predicate = Predicate->Clone(ctx); if (PositiveStatement) rs->PositiveStatement = PositiveStatement->Clone(ctx); if (NegativeStatement) rs->NegativeStatement = NegativeStatement->Clone(ctx); return rs; } RefPtr ReturnStatementSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitReturnStatement(this); } ReturnStatementSyntaxNode * ReturnStatementSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new ReturnStatementSyntaxNode(*this), ctx); if (Expression) rs->Expression = Expression->Clone(ctx); return rs; } RefPtr VarDeclrStatementSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitVarDeclrStatement(this); } VarDeclrStatementSyntaxNode * VarDeclrStatementSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new VarDeclrStatementSyntaxNode(*this), ctx); rs->decl = rs->decl->Clone(ctx); return rs; } RefPtr Variable::Accept(SyntaxVisitor * visitor) { return visitor->VisitDeclrVariable(this); } Variable * Variable::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new Variable(*this), ctx); if (Expr) rs->Expr = Expr->Clone(ctx); return rs; } RefPtr WhileStatementSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitWhileStatement(this); } WhileStatementSyntaxNode * WhileStatementSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new WhileStatementSyntaxNode(*this), ctx); if (Predicate) rs->Predicate = Predicate->Clone(ctx); if (Statement) rs->Statement = Statement->Clone(ctx); return rs; } RefPtr ExpressionStatementSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitExpressionStatement(this); } ExpressionStatementSyntaxNode * ExpressionStatementSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new ExpressionStatementSyntaxNode(*this), ctx); if (Expression) rs->Expression = Expression->Clone(ctx); return rs; } RefPtr BinaryExpressionSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitBinaryExpression(this); } BinaryExpressionSyntaxNode * BinaryExpressionSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new BinaryExpressionSyntaxNode(*this), ctx); rs->LeftExpression = LeftExpression->Clone(ctx); rs->RightExpression = RightExpression->Clone(ctx); return rs; } RefPtr ConstantExpressionSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitConstantExpression(this); } ConstantExpressionSyntaxNode * ConstantExpressionSyntaxNode::Clone(CloneContext & ctx) { return CloneSyntaxNodeFields(new ConstantExpressionSyntaxNode(*this), ctx); } IndexExpressionSyntaxNode * IndexExpressionSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new IndexExpressionSyntaxNode(*this), ctx); rs->BaseExpression = BaseExpression->Clone(ctx); rs->IndexExpression = IndexExpression->Clone(ctx); return rs; } RefPtr IndexExpressionSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitIndexExpression(this); } RefPtr MemberExpressionSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitMemberExpression(this); } MemberExpressionSyntaxNode * MemberExpressionSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new MemberExpressionSyntaxNode(*this), ctx); rs->BaseExpression = BaseExpression->Clone(ctx); return rs; } RefPtr InvokeExpressionSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitInvokeExpression(this); } InvokeExpressionSyntaxNode * InvokeExpressionSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new InvokeExpressionSyntaxNode(*this), ctx); rs->FunctionExpr = FunctionExpr->Clone(ctx); rs->Arguments.Clear(); for (auto & arg : Arguments) { rs->Arguments.Add(arg->Clone(ctx)); } return rs; } RefPtr TypeCastExpressionSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitTypeCastExpression(this); } TypeCastExpressionSyntaxNode * TypeCastExpressionSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new TypeCastExpressionSyntaxNode(*this), ctx); rs->TargetType = TargetType->Clone(ctx); rs->Expression = Expression->Clone(ctx); return rs; } RefPtr SelectExpressionSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitSelectExpression(this); } SelectExpressionSyntaxNode * SelectExpressionSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new SelectExpressionSyntaxNode(*this), ctx); rs->SelectorExpr = SelectorExpr->Clone(ctx); rs->Expr0 = Expr0->Clone(ctx); rs->Expr1 = Expr1->Clone(ctx); return rs; } RefPtr UnaryExpressionSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitUnaryExpression(this); } UnaryExpressionSyntaxNode * UnaryExpressionSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new UnaryExpressionSyntaxNode(*this), ctx); rs->Expression = Expression->Clone(ctx); return rs; } RefPtr VarExpressionSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitVarExpression(this); } VarExpressionSyntaxNode * VarExpressionSyntaxNode::Clone(CloneContext & ctx) { return CloneSyntaxNodeFields(new VarExpressionSyntaxNode(*this), ctx); } RefPtr ParameterSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitParameter(this); } ParameterSyntaxNode * ParameterSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new ParameterSyntaxNode(*this), ctx); rs->TypeNode = TypeNode->Clone(ctx); rs->Expr = Expr->Clone(ctx); return rs; } RefPtr BasicTypeSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitBasicType(this); } RefPtr ComponentSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitComponent(this); } ComponentSyntaxNode * ComponentSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new ComponentSyntaxNode(*this), ctx); rs->TypeNode = TypeNode->Clone(ctx); if (Rate) rs->Rate = Rate->Clone(ctx); if (BlockStatement) rs->BlockStatement = BlockStatement->Clone(ctx); if (Expression) rs->Expression = Expression->Clone(ctx); return rs; } RefPtr ShaderSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitShader(this); } ShaderSyntaxNode * ShaderSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new ShaderSyntaxNode(*this), ctx); rs->Members.Clear(); for (auto & comp : Members) rs->Members.Add(comp->Clone(ctx)); return rs; } // UsingFileDecl RefPtr UsingFileDecl::Accept(SyntaxVisitor * visitor) { return visitor->VisitUsingFileDecl(this); } UsingFileDecl* UsingFileDecl::Clone(CloneContext & ctx) { return CloneSyntaxNodeFields(new UsingFileDecl(*this), ctx); } // RateSyntaxNode * RateSyntaxNode::Clone(CloneContext & ctx) { return CloneSyntaxNodeFields(new RateSyntaxNode(*this), ctx); } WorldSyntaxNode * WorldSyntaxNode::Clone(CloneContext & ctx) { return CloneSyntaxNodeFields(new WorldSyntaxNode(*this), ctx); } RefPtr ImportOperatorDefSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitImportOperatorDef(this); } ImportOperatorDefSyntaxNode * ImportOperatorDefSyntaxNode::Clone(CloneContext & ctx) { return CloneSyntaxNodeFields(new ImportOperatorDefSyntaxNode(*this), ctx); } PipelineSyntaxNode * PipelineSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new PipelineSyntaxNode(*this), ctx); rs->Members.Clear(); for (auto & m : Members) rs->Members.Add(m->Clone(ctx)); return rs; } ChoiceValueSyntaxNode * ChoiceValueSyntaxNode::Clone(CloneContext & ctx) { return CloneSyntaxNodeFields(new ChoiceValueSyntaxNode(*this), ctx); } RefPtr ImportSyntaxNode::Accept(SyntaxVisitor * v) { return v->VisitImport(this); } ImportSyntaxNode * ImportSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new ImportSyntaxNode(*this), ctx); rs->Arguments.Clear(); for (auto & arg : Arguments) rs->Arguments.Add(arg->Clone(ctx)); return rs; } RefPtr ImportArgumentSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitImportArgument(this); } ImportArgumentSyntaxNode * ImportArgumentSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new ImportArgumentSyntaxNode(*this), ctx); rs->Expression = Expression->Clone(ctx); return rs; } RefPtr ImportStatementSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitImportStatement(this); } ImportStatementSyntaxNode * ImportStatementSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new ImportStatementSyntaxNode(*this), ctx); rs->Import = Import->Clone(ctx); return rs; } RefPtr StructField::Accept(SyntaxVisitor * visitor) { return visitor->VisitStructField(this); } RefPtr StructSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitStruct(this); } RefPtr TypeDefDecl::Accept(SyntaxVisitor * visitor) { return visitor->VisitTypeDefDecl(this); } TypeDefDecl* TypeDefDecl::Clone(CloneContext & ctx) { auto result = CloneSyntaxNodeFields(new TypeDefDecl(*this), ctx); return result; } RefPtr DiscardStatementSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitDiscardStatement(this); } DiscardStatementSyntaxNode * DiscardStatementSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new DiscardStatementSyntaxNode(*this), ctx); return rs; } bool BasicExpressionType::IsIntegralImpl() const { return (BaseType == Compiler::BaseType::Int || BaseType == Compiler::BaseType::UInt || BaseType == Compiler::BaseType::Bool); } bool ExpressionType::IsIntegral() const { return GetCanonicalType()->IsIntegralImpl(); } bool ExpressionType::Equals(const ExpressionType * type) const { return GetCanonicalType()->EqualsImpl(type->GetCanonicalType()); } bool ExpressionType::Equals(RefPtr type) const { return Equals(type.Ptr()); } bool ExpressionType::IsVectorType() const { return GetCanonicalType()->IsVectorTypeImpl(); } bool ExpressionType::IsArray() const { return GetCanonicalType()->IsArrayImpl(); } bool ExpressionType::IsGenericType(String typeName) const { return GetCanonicalType()->IsGenericTypeImpl(typeName); } BasicExpressionType * ExpressionType::AsBasicType() const { return GetCanonicalType()->AsBasicTypeImpl(); } ArrayExpressionType * ExpressionType::AsArrayType() const { return GetCanonicalType()->AsArrayTypeImpl(); } GenericExpressionType * ExpressionType::AsGenericType() const { return GetCanonicalType()->AsGenericTypeImpl(); } NamedExpressionType* ExpressionType::AsNamedType() const { return AsNamedTypeImpl(); } ExpressionType* ExpressionType::GetCanonicalType() const { ExpressionType* et = const_cast(this); if (!et->canonicalType) { // TODO(tfoley): worry about thread safety here? et->canonicalType = et->CreateCanonicalType(); } return et->canonicalType; } bool ExpressionType::IsTexture() const { auto basicType = AsBasicType(); if (basicType) return basicType->BaseType == BaseType::Texture2D || basicType->BaseType == BaseType::TextureCube || basicType->BaseType == BaseType::Texture2DArray || basicType->BaseType == BaseType::Texture2DShadow || basicType->BaseType == BaseType::TextureCubeShadow || basicType->BaseType == BaseType::Texture2DArrayShadow || basicType->BaseType == BaseType::TextureCubeArray || basicType->BaseType == BaseType::TextureCubeShadowArray || basicType->BaseType == BaseType::Texture3D; return false; } bool ExpressionType::IsTextureOrSampler() const { auto basicType = AsBasicType(); if (basicType) return basicType->BaseType == BaseType::Texture2D || basicType->BaseType == BaseType::TextureCube || basicType->BaseType == BaseType::Texture2DArray || basicType->BaseType == BaseType::Texture2DShadow || basicType->BaseType == BaseType::TextureCubeShadow || basicType->BaseType == BaseType::Texture2DArrayShadow || basicType->BaseType == BaseType::Texture3D || basicType->BaseType == BaseType::TextureCubeArray || basicType->BaseType == BaseType::TextureCubeShadowArray || basicType->BaseType == BaseType::SamplerState; return false; } bool ExpressionType::IsStruct() const { auto basicType = AsBasicType(); if (basicType) return basicType->structDecl != nullptr; return false; } bool ExpressionType::IsShader() const { auto basicType = AsBasicType(); if (basicType) return basicType->Shader != nullptr; return false; } RefPtr ExpressionType::Bool; RefPtr ExpressionType::UInt; RefPtr ExpressionType::UInt2; RefPtr ExpressionType::UInt3; RefPtr ExpressionType::UInt4; RefPtr ExpressionType::Int; RefPtr ExpressionType::Int2; RefPtr ExpressionType::Int3; RefPtr ExpressionType::Int4; RefPtr ExpressionType::Float; RefPtr ExpressionType::Float2; RefPtr ExpressionType::Float3; RefPtr ExpressionType::Float4; RefPtr ExpressionType::Void; RefPtr ExpressionType::Error; List> ExpressionType::sCanonicalTypes; void ExpressionType::Init() { Bool = new BasicExpressionType(BaseType::Bool); UInt = new BasicExpressionType(BaseType::UInt); UInt2 = new BasicExpressionType(BaseType::UInt2); UInt3 = new BasicExpressionType(BaseType::UInt3); UInt4 = new BasicExpressionType(BaseType::UInt4); Int = new BasicExpressionType(BaseType::Int); Int2 = new BasicExpressionType(BaseType::Int2); Int3 = new BasicExpressionType(BaseType::Int3); Int4 = new BasicExpressionType(BaseType::Int4); Float = new BasicExpressionType(BaseType::Float); Float2 = new BasicExpressionType(BaseType::Float2); Float3 = new BasicExpressionType(BaseType::Float3); Float4 = new BasicExpressionType(BaseType::Float4); Void = new BasicExpressionType(BaseType::Void); Error = new BasicExpressionType(BaseType::Error); } void ExpressionType::Finalize() { Bool = nullptr; UInt = nullptr; UInt2 = nullptr; UInt3 = nullptr; UInt4 = nullptr; Int = nullptr; Int2 = nullptr; Int3 = nullptr; Int4 = nullptr; Float = nullptr; Float2 = nullptr; Float3 = nullptr; Float4 = nullptr; Void = nullptr; Error = nullptr; // Note(tfoley): This seems to be just about the only way to clear out a List sCanonicalTypes = List>(); } bool ArrayExpressionType::IsArrayImpl() const { return true; } bool ArrayExpressionType::EqualsImpl(const ExpressionType * type) const { auto arrType = type->AsArrayType(); if (!arrType) return false; return (ArrayLength == arrType->ArrayLength && BaseType->Equals(arrType->BaseType.Ptr())); } ExpressionType* ArrayExpressionType::CreateCanonicalType() { auto canonicalBaseType = BaseType->GetCanonicalType(); auto canonicalArrayType = new ArrayExpressionType(); sCanonicalTypes.Add(canonicalArrayType); canonicalArrayType->BaseType = canonicalBaseType; canonicalArrayType->ArrayLength = ArrayLength; return canonicalArrayType; } CoreLib::Basic::String ArrayExpressionType::ToString() const { if (ArrayLength > 0) return BaseType->ToString() + "[" + String(ArrayLength) + "]"; else return BaseType->ToString() + "[]"; } ExpressionType * ArrayExpressionType::Clone() { auto rs = new ArrayExpressionType(*this); rs->BaseType = BaseType->Clone(); return rs; } RefPtr ArrayTypeSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitArrayType(this); } RefPtr GenericTypeSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitGenericType(this); } bool GenericExpressionType::EqualsImpl(const ExpressionType * type) const { if (auto gtype = type->AsGenericType()) return GenericTypeName == gtype->GenericTypeName && gtype->BaseType->Equals(BaseType.Ptr()); return false; } ExpressionType* GenericExpressionType::CreateCanonicalType() { auto canonicalBaseType = BaseType->GetCanonicalType(); auto canonicalGenericType = new GenericExpressionType(); sCanonicalTypes.Add(canonicalGenericType); canonicalGenericType->BaseType = canonicalBaseType; canonicalGenericType->GenericTypeName = GenericTypeName; return canonicalGenericType; } BindableResourceType GenericExpressionType::GetBindableResourceType() const { if (GenericTypeName == "StructuredBuffer" || GenericTypeName == "RWStructuredBuffer") return BindableResourceType::StorageBuffer; else if (GenericTypeName == "Uniform") return BindableResourceType::Buffer; return BindableResourceType::NonBindable; } CoreLib::Basic::String GenericExpressionType::ToString() const { return GenericTypeName + "<" + BaseType->ToString() + ">"; } ExpressionType * GenericExpressionType::Clone() { auto rs = new GenericExpressionType(*this); rs->BaseType = BaseType->Clone(); return rs; } // NamedExpressionType String NamedExpressionType::ToString() const { return decl->Name.Content; } ExpressionType * NamedExpressionType::Clone() { NamedExpressionType* result = new NamedExpressionType(); result->decl = decl; return result; } BindableResourceType NamedExpressionType::GetBindableResourceType() const { return GetCanonicalType()->GetBindableResourceType(); } bool NamedExpressionType::EqualsImpl(const ExpressionType * /*type*/) const { assert(!"unreachable"); return false; } NamedExpressionType * NamedExpressionType::AsNamedTypeImpl() const { return const_cast(this); } ExpressionType* NamedExpressionType::CreateCanonicalType() { return decl->Type->GetCanonicalType(); } RefPtr ImportExpressionSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitImportExpression(this); } ImportExpressionSyntaxNode * ImportExpressionSyntaxNode::Clone(CloneContext & ctx) { ImportExpressionSyntaxNode * result = new ImportExpressionSyntaxNode(*this); CloneSyntaxNodeFields(result, ctx); result->Component = Component->Clone(ctx); result->Arguments.Clear(); for (auto & arg : Arguments) result->Arguments.Add(arg->Clone(ctx)); return result; } StageSyntaxNode * StageSyntaxNode::Clone(CloneContext &) { return new StageSyntaxNode(*this); } RefPtr SyntaxVisitor::VisitComponent(ComponentSyntaxNode * comp) { if (comp->TypeNode) comp->TypeNode = comp->TypeNode->Accept(this).As(); if (comp->Expression) comp->Expression = comp->Expression->Accept(this).As(); if (comp->BlockStatement) comp->BlockStatement = comp->BlockStatement->Accept(this).As(); return comp; } String GetOperatorFunctionName(Operator op) { switch (op) { case Operator::Add: case Operator::AddAssign: return "+"; case Operator::Sub: case Operator::SubAssign: return "-"; case Operator::Neg: return "-"; case Operator::Not: return "!"; case Operator::BitNot: return "~"; case Operator::PreInc: case Operator::PostInc: return "++"; case Operator::PreDec: case Operator::PostDec: return "--"; case Operator::Mul: case Operator::MulAssign: return "*"; case Operator::Div: case Operator::DivAssign: return "/"; case Operator::Mod: case Operator::ModAssign: return "%"; case Operator::Lsh: case Operator::LshAssign: return "<<"; case Operator::Rsh: case Operator::RshAssign: return ">>"; case Operator::Eql: return "=="; case Operator::Neq: return "!="; case Operator::Greater: return ">"; case Operator::Less: return "<"; case Operator::Geq: return ">="; case Operator::Leq: return "<="; case Operator::BitAnd: case Operator::AndAssign: return "&"; case Operator::BitXor: case Operator::XorAssign: return "^"; case Operator::BitOr: case Operator::OrAssign: return "|"; case Operator::And: return "&&"; case Operator::Or: return "||"; default: return ""; } } RefPtr ProjectExpressionSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitProject(this); } ProjectExpressionSyntaxNode * ProjectExpressionSyntaxNode::Clone(CloneContext & ctx) { auto * result = new ProjectExpressionSyntaxNode(*this); result->BaseExpression = BaseExpression->Clone(ctx); return result; } RefPtr InterfaceSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitInterface(this); } InterfaceSyntaxNode * InterfaceSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new InterfaceSyntaxNode(*this), ctx); rs->Members.Clear(); for (auto & comp : Members) rs->Members.Add(comp->Clone(ctx)); return rs; } RefPtr TemplateShaderSyntaxNode::Accept(SyntaxVisitor * visitor) { return visitor->VisitTemplateShader(this); } TemplateShaderSyntaxNode * TemplateShaderSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new TemplateShaderSyntaxNode(*this), ctx); rs->Parameters.Clear(); for (auto & param : Parameters) rs->Parameters.Add(param->Clone(ctx)); for (auto & member : Members) rs->Members.Add(member->Clone(ctx)); return rs; } TemplateShaderParameterSyntaxNode * TemplateShaderParameterSyntaxNode::Clone(CloneContext & ctx) { auto rs = CloneSyntaxNodeFields(new TemplateShaderParameterSyntaxNode(*this), ctx); return rs; } } } ================================================ FILE: Source/SpireCore/Syntax.h ================================================ #ifndef RASTER_RENDERER_SYNTAX_H #define RASTER_RENDERER_SYNTAX_H #include "../CoreLib/Basic.h" #include "Lexer.h" #include "IL.h" namespace Spire { namespace Compiler { using namespace CoreLib::Basic; class SyntaxVisitor; class FunctionSyntaxNode; // We use a unified representation for modifiers on all declarations. // (Eventually this will also apply to statements that support attributes) // // The parser allows any set of modifiers on any declaration, and we leave // it to later phases of analysis to reject inappropriate uses. // TODO: implement rejection properly. // // Some common modifiers (thos represented by single keywords) are // specified via a simple set of flags. // TODO: consider using a real AST even for these, so we can give good // error messages on confliction modifiers. typedef unsigned int ModifierFlags; enum ModifierFlag : ModifierFlags { None = 0, Uniform = 1 << 0, Out = 1 << 1, In = 1 << 2, Const = 1 << 4, Instance = 1 << 5, Builtin = 1 << 6, Inline = 1 << 8, Public = 1 << 9, Require = 1 << 10, Param = (1 << 11) | ModifierFlag::Public, Extern = 1 << 12, Input = 1 << 13, Intrinsic = (1 << 14) | ModifierFlag::Extern, // TODO(tfoley): This should probably be its own flag InOut = ModifierFlag::In | ModifierFlag::Out, }; // // Other modifiers may have more elaborate data, and so // are represented as heap-allocated objects, in a linked // list. // class Modifier : public RefObject { public: RefPtr next; }; // A `layout` modifier class LayoutModifier : public Modifier { public: String LayoutString; }; // An attribute of the form `[Name]` or `[Name: Value]` class SimpleAttribute : public Modifier { public: String Key; Token Value; String const& GetValue() const { return Value.Content; } }; // A set of modifiers attached to a syntax node struct Modifiers { // The first modifier in the linked list of heap-allocated modifiers RefPtr first; // The bit-flags for the common modifiers ModifierFlags flags = ModifierFlag::None; }; // Helper class for iterating over a list of heap-allocated modifiers struct ModifierList { struct Iterator { Modifier* current; Modifier* operator*() { return current; } void operator++() { current = current->next.Ptr(); } bool operator!=(Iterator other) { return current != other.current; }; Iterator() : current(nullptr) {} Iterator(Modifier* modifier) : current(modifier) {} }; ModifierList() : modifiers(nullptr) {} ModifierList(Modifier* modifiers) : modifiers(modifiers) {} Iterator begin() { return Iterator(modifiers); } Iterator end() { return Iterator(nullptr); } Modifier* modifiers; }; // Helper class for iterating over heap-allocated modifiers // of a specific type. template struct FilteredModifierList { struct Iterator { Modifier* current; T* operator*() { return (T*)current; } void operator++() { current = Adjust(current->next.Ptr()); } bool operator!=(Iterator other) { return current != other.current; }; Iterator() : current(nullptr) {} Iterator(Modifier* modifier) : current(modifier) {} }; FilteredModifierList() : modifiers(nullptr) {} FilteredModifierList(Modifier* modifiers) : modifiers(Adjust(modifiers)) {} Iterator begin() { return Iterator(modifiers); } Iterator end() { return Iterator(nullptr); } static Modifier* Adjust(Modifier* modifier) { Modifier* m = modifier; for (;;) { if (!m) return m; if (dynamic_cast(m)) return m; m = m->next.Ptr(); } } Modifier* modifiers; }; enum class BaseType { Void = 0, Int = 16, Int2 = 17, Int3 = 18, Int4 = 19, Float = 32, Float2 = 33, Float3 = 34, Float4 = 35, UInt = 512, UInt2 = 513, UInt3 = 514, UInt4 = 515, Bool = 128, Bool2 = 129, Bool3 = 130, Bool4 = 131, Float3x3 = 40, Float4x4 = 47, Texture2D = 48, TextureCube = 49, Texture2DArray = 50, Texture2DShadow = 51, TextureCubeShadow = 52, Texture2DArrayShadow = 53, Texture3D = 54, TextureCubeArray = 55, TextureCubeShadowArray = 56, SamplerState = 4096, SamplerComparisonState = 4097, Function = 64, Shader = 256, Struct = 1024, Record = 2048, Generic = 8192, Error = 16384, }; inline bool IsVector(BaseType type) { return (((int)type) & 15) != 0; } inline int GetVectorSize(BaseType type) { return (((int)type) & 15) + 1; } inline BaseType GetVectorBaseType(BaseType type) { return (BaseType)(((int)type) & (~15)); } class Decl; class SymbolTable; class ShaderSymbol; class ShaderClosure; class StructSyntaxNode; class ShaderComponentSymbol; class FunctionSymbol; class BasicExpressionType; class ArrayExpressionType; class GenericExpressionType; class TypeDefDecl; class NamedExpressionType; class ExpressionType : public RefObject { public: static RefPtr Bool; static RefPtr UInt; static RefPtr UInt2; static RefPtr UInt3; static RefPtr UInt4; static RefPtr Int; static RefPtr Int2; static RefPtr Int3; static RefPtr Int4; static RefPtr Float; static RefPtr Float2; static RefPtr Float3; static RefPtr Float4; static RefPtr Void; static RefPtr Error; // Note: just exists to make sure we can clean up // canonical types we create along the way static List> sCanonicalTypes; public: virtual String ToString() const = 0; virtual ExpressionType * Clone() = 0; bool IsIntegral() const; bool Equals(const ExpressionType * type) const; bool Equals(RefPtr type) const; bool IsVectorType() const; bool IsArray() const; bool IsGenericType(String typeName) const; BasicExpressionType * AsBasicType() const; ArrayExpressionType * AsArrayType() const; GenericExpressionType * AsGenericType() const; NamedExpressionType* AsNamedType() const; bool IsTextureOrSampler() const; bool IsTexture() const; bool IsStruct() const; bool IsShader() const; static void Init(); static void Finalize(); ExpressionType* GetCanonicalType() const; virtual BindableResourceType GetBindableResourceType() const { return BindableResourceType::NonBindable; } protected: virtual bool IsIntegralImpl() const { return false; } virtual bool EqualsImpl(const ExpressionType * type) const = 0; virtual bool IsVectorTypeImpl() const { return false; } virtual bool IsArrayImpl() const { return false; } virtual bool IsGenericTypeImpl(String typeName) const { return nullptr; } virtual BasicExpressionType * AsBasicTypeImpl() const { return nullptr; } virtual ArrayExpressionType * AsArrayTypeImpl() const { return nullptr; } virtual GenericExpressionType * AsGenericTypeImpl() const { return nullptr; } virtual NamedExpressionType * AsNamedTypeImpl() const { return nullptr; } virtual ExpressionType* CreateCanonicalType() = 0; ExpressionType* canonicalType = nullptr; }; class BasicExpressionType : public ExpressionType { public: bool IsLeftValue; bool IsReference; bool IsMaskedVector = false; BaseType BaseType; ShaderSymbol * Shader = nullptr; ShaderClosure * ShaderClosure = nullptr; FunctionSymbol * Func = nullptr; ShaderComponentSymbol * Component = nullptr; StructSyntaxNode* structDecl = nullptr; String RecordTypeName, GenericTypeVar; BasicExpressionType() { BaseType = Compiler::BaseType::Int; Func = 0; IsLeftValue = false; IsReference = false; } BasicExpressionType(Compiler::BaseType baseType) { BaseType = baseType; Func = 0; IsLeftValue = false; IsReference = false; } BasicExpressionType(ShaderSymbol * shaderSym, Compiler::ShaderClosure * closure) { this->BaseType = BaseType::Shader; this->ShaderClosure = closure; this->Shader = shaderSym; } virtual CoreLib::Basic::String ToString() const override; virtual ExpressionType * Clone() override; protected: virtual bool IsIntegralImpl() const override; virtual bool EqualsImpl(const ExpressionType * type) const override; virtual bool IsVectorTypeImpl() const override; virtual BasicExpressionType * AsBasicTypeImpl() const override { return const_cast(this); } virtual ExpressionType* CreateCanonicalType() override; virtual BindableResourceType GetBindableResourceType() const override; }; class ArrayExpressionType : public ExpressionType { public: RefPtr BaseType; int ArrayLength = 0; virtual CoreLib::Basic::String ToString() const override; virtual ExpressionType * Clone() override; protected: virtual bool IsArrayImpl() const override; virtual bool EqualsImpl(const ExpressionType * type) const override; virtual ArrayExpressionType * AsArrayTypeImpl() const override { return const_cast(this); } virtual ExpressionType* CreateCanonicalType() override; }; class GenericExpressionType : public ExpressionType { public: RefPtr BaseType; String GenericTypeName; virtual CoreLib::Basic::String ToString() const override; virtual ExpressionType * Clone() override; protected: virtual bool EqualsImpl(const ExpressionType * type) const override; virtual bool IsGenericTypeImpl(String typeName) const override { return GenericTypeName == typeName; } virtual GenericExpressionType * AsGenericTypeImpl() const override { return const_cast(this); } virtual ExpressionType* CreateCanonicalType() override; virtual BindableResourceType GetBindableResourceType() const override; }; class NamedExpressionType : public ExpressionType { public: TypeDefDecl* decl; virtual String ToString() const override; virtual ExpressionType * Clone() override; virtual BindableResourceType GetBindableResourceType() const override; protected: virtual bool EqualsImpl(const ExpressionType * type) const override; virtual NamedExpressionType * AsNamedTypeImpl() const override; virtual ExpressionType* CreateCanonicalType() override; }; class Type { public: RefPtr DataType; // ContrainedWorlds: Implementation must be defined at at least one of of these worlds in order to satisfy global dependency // FeasibleWorlds: The component can be computed at any of these worlds EnumerableHashSet ConstrainedWorlds, FeasibleWorlds; EnumerableHashSet PinnedWorlds; }; class ContainerDecl; class Scope : public RefObject { public: RefPtr Parent; ContainerDecl* containerDecl; Dictionary decls; Decl* LookUp(String const& name); Scope(RefPtr parent, ContainerDecl* containerDecl) : Parent(parent) , containerDecl(containerDecl) {} }; class CloneContext { public: Dictionary> ScopeTranslateTable; }; class SyntaxNode : public RefObject { protected: template T* CloneSyntaxNodeFields(T * target, CloneContext & ctx) { if (this->Scope) { RefPtr newScope; if (ctx.ScopeTranslateTable.TryGetValue(this->Scope.Ptr(), newScope)) target->Scope = newScope; else { target->Scope = new Spire::Compiler::Scope(*this->Scope); ctx.ScopeTranslateTable[this->Scope.Ptr()] = target->Scope; RefPtr parentScope; if (ctx.ScopeTranslateTable.TryGetValue(target->Scope->Parent.Ptr(), parentScope)) target->Scope->Parent = parentScope.Ptr(); } } target->Position = this->Position; target->Tags = this->Tags; return target; } public: EnumerableDictionary> Tags; CodePosition Position; RefPtr Scope; virtual RefPtr Accept(SyntaxVisitor * visitor) = 0; virtual SyntaxNode * Clone(CloneContext & ctx) = 0; }; class TypeSyntaxNode : public SyntaxNode { public: virtual TypeSyntaxNode * Clone(CloneContext & ctx) = 0; }; class BasicTypeSyntaxNode : public TypeSyntaxNode { public: String TypeName; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual BasicTypeSyntaxNode * Clone(CloneContext & ctx) override { return CloneSyntaxNodeFields(new BasicTypeSyntaxNode(*this), ctx); } }; class ArrayTypeSyntaxNode : public TypeSyntaxNode { public: RefPtr BaseType; int ArrayLength; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual ArrayTypeSyntaxNode * Clone(CloneContext & ctx) override { auto rs = CloneSyntaxNodeFields(new ArrayTypeSyntaxNode(*this), ctx); rs->BaseType = BaseType->Clone(ctx); return rs; } }; class GenericTypeSyntaxNode : public TypeSyntaxNode { public: RefPtr BaseType; String GenericTypeName; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual GenericTypeSyntaxNode * Clone(CloneContext & ctx) override { auto rs = CloneSyntaxNodeFields(new GenericTypeSyntaxNode(*this), ctx); rs->BaseType = BaseType->Clone(ctx); return rs; } }; class ContainerDecl; class SpecializeModifier; class Decl : public SyntaxNode { public: ContainerDecl* ParentDecl; Token Name; Modifiers modifiers; bool HasModifier(ModifierFlags flags) { return (modifiers.flags & flags) == flags; } template FilteredModifierList GetModifiersOfType() { return FilteredModifierList(modifiers.first.Ptr()); } FilteredModifierList GetLayoutAttributes() { return GetModifiersOfType(); } bool FindSimpleAttribute(String const& key, Token& outValue); bool FindSimpleAttribute(String const& key, String& outValue); bool HasSimpleAttribute(String const& key); SpecializeModifier * FindSpecializeModifier(); virtual Decl * Clone(CloneContext & ctx) = 0; }; template struct FilteredMemberList { typedef RefPtr Element; FilteredMemberList() : mBegin(NULL) , mEnd(NULL) {} explicit FilteredMemberList( List const& list) : mBegin(Adjust(list.begin(), list.end())) , mEnd(list.end()) {} struct Iterator { Element* mCursor; Element* mEnd; bool operator!=(Iterator const& other) { return mCursor != other.mCursor; } void operator++() { mCursor = Adjust(mCursor + 1, mEnd); } RefPtr& operator*() { return *(RefPtr*)mCursor; } }; Iterator begin() { Iterator iter = { mBegin, mEnd }; return iter; } Iterator end() { Iterator iter = { mEnd, mEnd }; return iter; } static Element* Adjust(Element* cursor, Element* end) { while (cursor != end) { if ((*cursor).As()) return cursor; cursor++; } return cursor; } // TODO(tfoley): It is ugly to have these. // We should probably fix the call sites instead. RefPtr& First() { return *begin(); } int Count() { int count = 0; for (auto iter : (*this)) { (void)iter; count++; } return count; } List> ToArray() { List> result; for (auto element : (*this)) { result.Add(element); } return result; } Element* mBegin; Element* mEnd; }; // A "container" decl is a parent to other declarations class ContainerDecl : public Decl { public: List> Members; template FilteredMemberList GetMembersOfType() { return FilteredMemberList(Members); } }; enum class ExpressionAccess { Read, Write }; class ExpressionSyntaxNode : public SyntaxNode { public: RefPtr Type; ExpressionAccess Access; ExpressionSyntaxNode() { Access = ExpressionAccess::Read; } ExpressionSyntaxNode(const ExpressionSyntaxNode & expr) = default; virtual ExpressionSyntaxNode* Clone(CloneContext & ctx) = 0; }; // A 'specialize' modifier indicating the shader parameter should be specialized class SpecializeModifier : public Modifier { public: List> Values; }; // // Declarations // // Base class for all variable-like declarations class VarDeclBase : public Decl { public: // Syntax for type specifier RefPtr TypeNode; // Resolved type of the variable RefPtr Type; // Initializer expression (optional) RefPtr Expr; }; // A field of a `struct` type class StructField : public VarDeclBase { public: StructField() {} virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual StructField * Clone(CloneContext & ctx) override { auto rs = CloneSyntaxNodeFields(new StructField(*this), ctx); rs->TypeNode = TypeNode->Clone(ctx); return rs; } }; class StructSyntaxNode : public ContainerDecl { public: FilteredMemberList GetFields() { return GetMembersOfType(); } bool SemanticallyChecked = false; bool IsIntrinsic = false; virtual RefPtr Accept(SyntaxVisitor * visitor) override; StructField* FindField(String name) { for (auto field : GetFields()) { if (field->Name.Content == name) return field.Ptr(); } return nullptr; } int FindFieldIndex(String name) { int index = 0; for (auto field : GetFields()) { if (field->Name.Content == name) return index; index++; } return -1; } virtual StructSyntaxNode * Clone(CloneContext & ctx) override { auto rs = CloneSyntaxNodeFields(new StructSyntaxNode(*this), ctx); rs->Members.Clear(); for (auto & m : Members) rs->Members.Add(m->Clone(ctx)); return rs; } }; // A `typedef` declaration class TypeDefDecl : public Decl { public: RefPtr TypeNode; RefPtr Type; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual TypeDefDecl * Clone(CloneContext & ctx) override; }; class StatementSyntaxNode : public SyntaxNode { public: virtual StatementSyntaxNode* Clone(CloneContext & ctx) = 0; }; // A scope for local declarations (e.g., as part of a statement) class ScopeDecl : public ContainerDecl { public: virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual ScopeDecl * Clone(CloneContext & ctx) override; }; class ScopeStmt : public StatementSyntaxNode { public: RefPtr scopeDecl; }; class BlockStatementSyntaxNode : public ScopeStmt { public: List> Statements; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual BlockStatementSyntaxNode * Clone(CloneContext & ctx) override; }; // TODO(tfoley): Only used by IL at this point enum class ParameterQualifier { In, Out, InOut, Uniform }; class ParameterSyntaxNode : public VarDeclBase { public: virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual ParameterSyntaxNode * Clone(CloneContext & ctx) override; }; class FunctionDeclBase : public ContainerDecl { public: FilteredMemberList GetParameters() { return GetMembersOfType(); } RefPtr Body; }; class FunctionSyntaxNode : public FunctionDeclBase { public: String InternalName; RefPtr ReturnType; RefPtr ReturnTypeNode; bool SemanticallyChecked = false; bool IsInline() { return HasModifier(ModifierFlag::Inline); } bool IsExtern() { return HasModifier(ModifierFlag::Extern); } bool HasSideEffect() { return !HasModifier(ModifierFlag::Intrinsic); } virtual RefPtr Accept(SyntaxVisitor * visitor) override; FunctionSyntaxNode() { } virtual FunctionSyntaxNode * Clone(CloneContext & ctx) override; }; class ImportOperatorDefSyntaxNode : public FunctionDeclBase { public: Token SourceWorld, DestWorld; Token TypeName; List> Requirements; List Usings; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual ImportOperatorDefSyntaxNode * Clone(CloneContext & ctx) override; }; class ChoiceValueSyntaxNode : public ExpressionSyntaxNode { public: String WorldName; virtual RefPtr Accept(SyntaxVisitor *) { return this; } virtual ChoiceValueSyntaxNode * Clone(CloneContext & ctx); }; class VarExpressionSyntaxNode : public ExpressionSyntaxNode { public: String Variable; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual VarExpressionSyntaxNode * Clone(CloneContext & ctx) override; }; class ConstantExpressionSyntaxNode : public ExpressionSyntaxNode { public: enum class ConstantType { Int, UInt, Bool, Float }; ConstantType ConstType; union { int IntValue; float FloatValue; }; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual ConstantExpressionSyntaxNode * Clone(CloneContext & ctx) override; }; enum class Operator { Neg, Not, BitNot, PreInc, PreDec, PostInc, PostDec, Mul, Div, Mod, Add, Sub, Lsh, Rsh, Eql, Neq, Greater, Less, Geq, Leq, BitAnd, BitXor, BitOr, And, Or, Assign = 200, AddAssign, SubAssign, MulAssign, DivAssign, ModAssign, LshAssign, RshAssign, OrAssign, AndAssign, XorAssign }; String GetOperatorFunctionName(Operator op); class ImportExpressionSyntaxNode : public ExpressionSyntaxNode { public: RefPtr Component; String ComponentUniqueName; // filled by ResolveDependence RefPtr ImportOperatorDef; // filled by semantics List> Arguments; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual ImportExpressionSyntaxNode * Clone(CloneContext & ctx) override; }; class ProjectExpressionSyntaxNode : public ExpressionSyntaxNode { public: RefPtr BaseExpression; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual ProjectExpressionSyntaxNode * Clone(CloneContext & ctx) override; }; class UnaryExpressionSyntaxNode : public ExpressionSyntaxNode { public: Operator Operator; RefPtr Expression; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual UnaryExpressionSyntaxNode * Clone(CloneContext & ctx) override; }; class BinaryExpressionSyntaxNode : public ExpressionSyntaxNode { public: Operator Operator; RefPtr LeftExpression; RefPtr RightExpression; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual BinaryExpressionSyntaxNode * Clone(CloneContext & ctx) override; }; class IndexExpressionSyntaxNode : public ExpressionSyntaxNode { public: RefPtr BaseExpression; RefPtr IndexExpression; virtual IndexExpressionSyntaxNode * Clone(CloneContext & ctx) override; virtual RefPtr Accept(SyntaxVisitor * visitor) override; }; class MemberExpressionSyntaxNode : public ExpressionSyntaxNode { public: RefPtr BaseExpression; String MemberName; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual MemberExpressionSyntaxNode * Clone(CloneContext & ctx) override; }; class InvokeExpressionSyntaxNode : public ExpressionSyntaxNode { public: RefPtr FunctionExpr; List> Arguments; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual InvokeExpressionSyntaxNode * Clone(CloneContext & ctx) override; }; class TypeCastExpressionSyntaxNode : public ExpressionSyntaxNode { public: RefPtr TargetType; RefPtr Expression; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual TypeCastExpressionSyntaxNode * Clone(CloneContext & ctx) override; }; class SelectExpressionSyntaxNode : public ExpressionSyntaxNode { public: RefPtr SelectorExpr, Expr0, Expr1; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual SelectExpressionSyntaxNode * Clone(CloneContext & ctx) override; }; class EmptyStatementSyntaxNode : public StatementSyntaxNode { public: virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual EmptyStatementSyntaxNode * Clone(CloneContext & ctx) override; }; class DiscardStatementSyntaxNode : public StatementSyntaxNode { public: virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual DiscardStatementSyntaxNode * Clone(CloneContext & ctx) override; }; struct Variable : public VarDeclBase { virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual Variable * Clone(CloneContext & ctx) override; }; class VarDeclrStatementSyntaxNode : public StatementSyntaxNode { public: RefPtr decl; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual VarDeclrStatementSyntaxNode * Clone(CloneContext & ctx) override; }; class RateWorld { public: Token World; bool Pinned = false; RateWorld() {} RateWorld(String world) { World.Content = world; World.Type = TokenType::Identifier; } }; class RateSyntaxNode : public SyntaxNode { public: List Worlds; virtual RefPtr Accept(SyntaxVisitor *) override { return this; } virtual RateSyntaxNode * Clone(CloneContext & ctx) override; }; class ComponentSyntaxNode : public ContainerDecl { public: bool IsOutput() { return HasModifier(ModifierFlag::Out); } bool IsPublic() { return HasModifier(ModifierFlag::Public); } bool IsInline() { return HasModifier(ModifierFlag::Inline) || IsComponentFunction(); } bool IsRequire() { return HasModifier(ModifierFlag::Require); } bool IsInput() { return HasModifier(ModifierFlag::Extern); } bool IsParam() { return HasModifier(ModifierFlag::Param); } RefPtr TypeNode; RefPtr Type; RefPtr Rate; RefPtr BlockStatement; RefPtr Expression; FilteredMemberList GetParameters() { return GetMembersOfType(); } bool IsComponentFunction() { return GetParameters().Count() != 0; } virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual ComponentSyntaxNode * Clone(CloneContext & ctx) override; }; class WorldSyntaxNode : public Decl { public: bool IsAbstract() { return HasModifier(ModifierFlag::Input); } virtual RefPtr Accept(SyntaxVisitor *) override { return this; } virtual WorldSyntaxNode * Clone(CloneContext & ctx) override; }; class StageSyntaxNode : public Decl { public: Token StageType; EnumerableDictionary Attributes; virtual RefPtr Accept(SyntaxVisitor *) override { return this; } virtual StageSyntaxNode * Clone(CloneContext & ctx) override; }; // Shared functionality for "shader class"-like declarations class ShaderDeclBase : public ContainerDecl { public: Token ParentPipelineName; List InterfaceNames; }; class PipelineSyntaxNode : public ShaderDeclBase { public: // Access members of specific types FilteredMemberList GetWorlds() { return GetMembersOfType(); } FilteredMemberList GetImportOperators() { return GetMembersOfType(); } FilteredMemberList GetStages() { return GetMembersOfType(); } FilteredMemberList GetAbstractComponents() { return GetMembersOfType(); } virtual RefPtr Accept(SyntaxVisitor *) override { return this; } virtual PipelineSyntaxNode * Clone(CloneContext & ctx) override; }; class ImportArgumentSyntaxNode : public SyntaxNode { public: RefPtr Expression; Token ArgumentName; virtual RefPtr Accept(SyntaxVisitor *) override; virtual ImportArgumentSyntaxNode * Clone(CloneContext & ctx) override; }; class ImportSyntaxNode : public Decl { public: bool IsInplace = false; bool IsPublic() { return HasModifier(ModifierFlag::Public); } Token ShaderName; Token ObjectName; List> Arguments; virtual RefPtr Accept(SyntaxVisitor *) override; virtual ImportSyntaxNode * Clone(CloneContext & ctx) override; }; class TemplateShaderParameterSyntaxNode : public SyntaxNode { public: Token ModuleName, InterfaceName; virtual RefPtr Accept(SyntaxVisitor * /*visitor*/) { return this; } virtual TemplateShaderParameterSyntaxNode * Clone(CloneContext & ctx) override; }; class TemplateShaderSyntaxNode : public ShaderDeclBase { public: List> Parameters; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual TemplateShaderSyntaxNode * Clone(CloneContext & ctx) override; }; class ShaderSyntaxNode : public ShaderDeclBase { public: bool IsModule = false; bool SemanticallyChecked = false; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual ShaderSyntaxNode * Clone(CloneContext & ctx) override; }; class InterfaceSyntaxNode : public ShaderDeclBase { public: bool SemanticallyChecked = false; FilteredMemberList GetComponents() { return GetMembersOfType(); } virtual RefPtr Accept(SyntaxVisitor *) override; virtual InterfaceSyntaxNode * Clone(CloneContext & ctx) override; }; class UsingFileDecl : public Decl { public: Token fileName; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual UsingFileDecl * Clone(CloneContext & ctx) override; }; class ProgramSyntaxNode : public ContainerDecl { public: // Access members of specific types FilteredMemberList GetUsings() { return GetMembersOfType(); } FilteredMemberList GetFunctions() { return GetMembersOfType(); } FilteredMemberList GetPipelines() { return GetMembersOfType(); } FilteredMemberList GetInterfaces() { return GetMembersOfType(); } FilteredMemberList GetShaders() { return GetMembersOfType(); } FilteredMemberList GetStructs() { return GetMembersOfType(); } FilteredMemberList GetTypeDefs() { return GetMembersOfType(); } void Include(ProgramSyntaxNode * other) { Members.AddRange(other->Members); } virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual ProgramSyntaxNode * Clone(CloneContext & ctx) override; }; class ImportStatementSyntaxNode : public StatementSyntaxNode { public: RefPtr Import; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual ImportStatementSyntaxNode * Clone(CloneContext & ctx) override; }; class IfStatementSyntaxNode : public StatementSyntaxNode { public: RefPtr Predicate; RefPtr PositiveStatement; RefPtr NegativeStatement; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual IfStatementSyntaxNode * Clone(CloneContext & ctx) override; }; class ForStatementSyntaxNode : public ScopeStmt { public: RefPtr InitialStatement; RefPtr SideEffectExpression, PredicateExpression; RefPtr Statement; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual ForStatementSyntaxNode * Clone(CloneContext & ctx) override; }; class WhileStatementSyntaxNode : public StatementSyntaxNode { public: RefPtr Predicate; RefPtr Statement; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual WhileStatementSyntaxNode * Clone(CloneContext & ctx) override; }; class DoWhileStatementSyntaxNode : public StatementSyntaxNode { public: RefPtr Statement; RefPtr Predicate; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual DoWhileStatementSyntaxNode * Clone(CloneContext & ctx) override; }; class BreakStatementSyntaxNode : public StatementSyntaxNode { public: virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual BreakStatementSyntaxNode * Clone(CloneContext & ctx) override; }; class ContinueStatementSyntaxNode : public StatementSyntaxNode { public: virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual ContinueStatementSyntaxNode * Clone(CloneContext & ctx) override; }; class ReturnStatementSyntaxNode : public StatementSyntaxNode { public: RefPtr Expression; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual ReturnStatementSyntaxNode * Clone(CloneContext & ctx) override; }; class ExpressionStatementSyntaxNode : public StatementSyntaxNode { public: RefPtr Expression; virtual RefPtr Accept(SyntaxVisitor * visitor) override; virtual ExpressionStatementSyntaxNode * Clone(CloneContext & ctx) override; }; class SyntaxVisitor : public Object { protected: DiagnosticSink * sink = nullptr; DiagnosticSink* getSink() { return sink; } public: SyntaxVisitor(DiagnosticSink * sink) : sink(sink) {} virtual RefPtr VisitProgram(ProgramSyntaxNode* program) { for (auto & m : program->Members) m = m->Accept(this).As(); return program; } virtual RefPtr VisitShader(ShaderSyntaxNode * shader) { for (auto & comp : shader->Members) comp = comp->Accept(this).As(); return shader; } virtual RefPtr VisitUsingFileDecl(UsingFileDecl * decl) { return decl; } virtual RefPtr VisitComponent(ComponentSyntaxNode * comp); virtual RefPtr VisitFunction(FunctionSyntaxNode* func) { func->ReturnTypeNode = func->ReturnTypeNode->Accept(this).As(); for (auto & member : func->Members) member = member->Accept(this).As(); if (func->Body) func->Body = func->Body->Accept(this).As(); return func; } virtual RefPtr VisitScopeDecl(ScopeDecl* decl) { // By default don't visit children, because they will always // be encountered in the ordinary flow of the corresponding statement. return decl; } virtual RefPtr VisitStruct(StructSyntaxNode * s) { for (auto & f : s->Members) f = f->Accept(this).As(); return s; } virtual RefPtr VisitTypeDefDecl(TypeDefDecl* decl) { decl->TypeNode = decl->TypeNode->Accept(this).As(); return decl; } virtual RefPtr VisitDiscardStatement(DiscardStatementSyntaxNode * stmt) { return stmt; } virtual RefPtr VisitStructField(StructField * f) { f->TypeNode = f->TypeNode->Accept(this).As(); return f; } virtual RefPtr VisitBlockStatement(BlockStatementSyntaxNode* stmt) { for (auto & s : stmt->Statements) s = s->Accept(this).As(); return stmt; } virtual RefPtr VisitBreakStatement(BreakStatementSyntaxNode* stmt) { return stmt; } virtual RefPtr VisitContinueStatement(ContinueStatementSyntaxNode* stmt) { return stmt; } virtual RefPtr VisitDoWhileStatement(DoWhileStatementSyntaxNode* stmt) { if (stmt->Predicate) stmt->Predicate = stmt->Predicate->Accept(this).As(); if (stmt->Statement) stmt->Statement = stmt->Statement->Accept(this).As(); return stmt; } virtual RefPtr VisitEmptyStatement(EmptyStatementSyntaxNode* stmt) { return stmt; } virtual RefPtr VisitForStatement(ForStatementSyntaxNode* stmt) { if (stmt->InitialStatement) stmt->InitialStatement = stmt->InitialStatement->Accept(this).As(); if (stmt->PredicateExpression) stmt->PredicateExpression = stmt->PredicateExpression->Accept(this).As(); if (stmt->SideEffectExpression) stmt->SideEffectExpression = stmt->SideEffectExpression->Accept(this).As(); if (stmt->Statement) stmt->Statement = stmt->Statement->Accept(this).As(); return stmt; } virtual RefPtr VisitIfStatement(IfStatementSyntaxNode* stmt) { if (stmt->Predicate) stmt->Predicate = stmt->Predicate->Accept(this).As(); if (stmt->PositiveStatement) stmt->PositiveStatement = stmt->PositiveStatement->Accept(this).As(); if (stmt->NegativeStatement) stmt->NegativeStatement = stmt->NegativeStatement->Accept(this).As(); return stmt; } virtual RefPtr VisitReturnStatement(ReturnStatementSyntaxNode* stmt) { if (stmt->Expression) stmt->Expression = stmt->Expression->Accept(this).As(); return stmt; } virtual RefPtr VisitVarDeclrStatement(VarDeclrStatementSyntaxNode* stmt) { stmt->decl = stmt->decl->Accept(this).As(); return stmt; } virtual RefPtr VisitWhileStatement(WhileStatementSyntaxNode* stmt) { if (stmt->Predicate) stmt->Predicate = stmt->Predicate->Accept(this).As(); if (stmt->Statement) stmt->Statement = stmt->Statement->Accept(this).As(); return stmt; } virtual RefPtr VisitExpressionStatement(ExpressionStatementSyntaxNode* stmt) { if (stmt->Expression) stmt->Expression = stmt->Expression->Accept(this).As(); return stmt; } virtual RefPtr VisitBinaryExpression(BinaryExpressionSyntaxNode* expr) { if (expr->LeftExpression) expr->LeftExpression = expr->LeftExpression->Accept(this).As(); if (expr->RightExpression) expr->RightExpression = expr->RightExpression->Accept(this).As(); return expr; } virtual RefPtr VisitConstantExpression(ConstantExpressionSyntaxNode* expr) { return expr; } virtual RefPtr VisitIndexExpression(IndexExpressionSyntaxNode* expr) { if (expr->BaseExpression) expr->BaseExpression = expr->BaseExpression->Accept(this).As(); if (expr->IndexExpression) expr->IndexExpression = expr->IndexExpression->Accept(this).As(); return expr; } virtual RefPtr VisitMemberExpression(MemberExpressionSyntaxNode * stmt) { if (stmt->BaseExpression) stmt->BaseExpression = stmt->BaseExpression->Accept(this).As(); return stmt; } virtual RefPtr VisitInvokeExpression(InvokeExpressionSyntaxNode* stmt) { stmt->FunctionExpr->Accept(this); for (auto & arg : stmt->Arguments) arg = arg->Accept(this).As(); return stmt; } virtual RefPtr VisitImportExpression(ImportExpressionSyntaxNode * expr) { for (auto & arg : expr->Arguments) arg = arg->Accept(this).As(); if (expr->ImportOperatorDef) expr->ImportOperatorDef->Accept(this); return expr; } virtual RefPtr VisitTypeCastExpression(TypeCastExpressionSyntaxNode * stmt) { if (stmt->Expression) stmt->Expression = stmt->Expression->Accept(this).As(); return stmt->Expression; } virtual RefPtr VisitSelectExpression(SelectExpressionSyntaxNode * expr) { if (expr->SelectorExpr) expr->SelectorExpr = expr->SelectorExpr->Accept(this).As(); if (expr->Expr0) expr->Expr0 = expr->Expr0->Accept(this).As(); if (expr->Expr1) expr->Expr1 = expr->Expr1->Accept(this).As(); return expr; } virtual RefPtr VisitUnaryExpression(UnaryExpressionSyntaxNode* expr) { if (expr->Expression) expr->Expression = expr->Expression->Accept(this).As(); return expr; } virtual RefPtr VisitVarExpression(VarExpressionSyntaxNode* expr) { return expr; } virtual RefPtr VisitPipeline(PipelineSyntaxNode * pipe) { for (auto & comp : pipe->Members) comp = comp->Accept(this).As(); return pipe; } virtual RefPtr VisitImportOperatorDef(ImportOperatorDefSyntaxNode * imp) { imp->Body = imp->Body->Accept(this).As(); return imp; } virtual RefPtr VisitParameter(ParameterSyntaxNode* param) { return param; } virtual RefPtr VisitBasicType(BasicTypeSyntaxNode* type) { return type; } virtual RefPtr VisitArrayType(ArrayTypeSyntaxNode* type) { return type; } virtual RefPtr VisitGenericType(GenericTypeSyntaxNode* type) { return type; } virtual RefPtr VisitDeclrVariable(Variable* dclr) { if (dclr->Expr) dclr->Expr = dclr->Expr->Accept(this).As(); return dclr; } virtual RefPtr VisitImport(ImportSyntaxNode* imp) { for (auto & arg : imp->Arguments) if (arg->Expression) arg->Expression = arg->Expression->Accept(this).As(); return imp; } virtual RefPtr VisitImportStatement(ImportStatementSyntaxNode* stmt) { if (stmt->Import) stmt->Import = stmt->Import->Accept(this).As(); return stmt; } virtual RefPtr VisitImportArgument(ImportArgumentSyntaxNode * arg) { if (arg->Expression) arg->Expression = arg->Expression->Accept(this).As(); return arg; } virtual RefPtr VisitProject(ProjectExpressionSyntaxNode * project) { if (project->BaseExpression) project->BaseExpression = project->BaseExpression->Accept(this).As(); return project; } virtual RefPtr VisitInterface(InterfaceSyntaxNode * node) { for (auto & member : node->GetComponents()) { member->Accept(this); } return node; } virtual RefPtr VisitTemplateShader(TemplateShaderSyntaxNode * shader) { for (auto & param : shader->Parameters) param->Accept(this); for (auto & member : shader->Members) member->Accept(this); return shader; } }; } } #endif ================================================ FILE: Source/SpireCore/SyntaxVisitors.h ================================================ #ifndef RASTER_RENDERER_SYNTAX_PRINTER_H #define RASTER_RENDERER_SYNTAX_PRINTER_H #include "Diagnostics.h" #include "Syntax.h" #include "IL.h" #include "SymbolTable.h" namespace Spire { namespace Compiler { class CodeGenBackend; class ShaderCompiler; class ShaderLinkInfo; class ShaderSymbol; class ICodeGenerator : public SyntaxVisitor { public: ICodeGenerator(DiagnosticSink * perr) : SyntaxVisitor(perr) {} virtual void ProcessFunction(FunctionSyntaxNode * func) = 0; virtual void ProcessShader(ShaderIR * shader) = 0; virtual void ProcessStruct(StructSyntaxNode * st) = 0; }; SyntaxVisitor * CreateSemanticsVisitor(SymbolTable * symbols, DiagnosticSink * err); ICodeGenerator * CreateCodeGenerator(SymbolTable * symbols, CompileResult & result, CodeGenBackend* backend); } } #endif ================================================ FILE: Source/SpireCore/TypeLayout.cpp ================================================ // TypeLayout.cpp #include "TypeLayout.h" #include "Syntax.h" #include "SymbolTable.h" #include namespace Spire { namespace Compiler { size_t RoundToAlignment(size_t offset, size_t alignment) { size_t remainder = offset % alignment; if (remainder == 0) return offset; else return offset + (alignment - remainder); } static size_t RoundUpToPowerOfTwo( size_t value ) { // TODO(tfoley): I know this isn't a fast approach size_t result = 1; while (result < value) result *= 2; return result; } struct DefaultLayoutRulesImpl : LayoutRulesImpl { // Get size and alignment for a single value of base type. LayoutInfo GetScalarLayout(BaseType baseType) override { switch (baseType) { case BaseType::Int: case BaseType::UInt: case BaseType::Float: case BaseType::Bool: return{ 4, 4 }; case BaseType::Int2: return GetVectorLayout(GetScalarLayout(BaseType::Int), 2); case BaseType::Bool2: return GetVectorLayout(GetScalarLayout(BaseType::Bool), 2); case BaseType::UInt2: return GetVectorLayout(GetScalarLayout(BaseType::UInt), 2); case BaseType::Float2: return GetVectorLayout(GetScalarLayout(BaseType::Float), 2); case BaseType::Int3: return GetVectorLayout(GetScalarLayout(BaseType::Int), 3); case BaseType::UInt3: return GetVectorLayout(GetScalarLayout(BaseType::UInt), 3); case BaseType::Float3: return GetVectorLayout(GetScalarLayout(BaseType::Float), 3); case BaseType::Bool3: return GetVectorLayout(GetScalarLayout(BaseType::Bool), 3); case BaseType::Int4: return GetVectorLayout(GetScalarLayout(BaseType::Int), 4); case BaseType::UInt4: return GetVectorLayout(GetScalarLayout(BaseType::UInt), 4); case BaseType::Float4: return GetVectorLayout(GetScalarLayout(BaseType::Float), 4); case BaseType::Bool4: return GetVectorLayout(GetScalarLayout(BaseType::Bool), 4); case BaseType::Float3x3: return GetMatrixLayout(GetScalarLayout(BaseType::Float), 3, 3); case BaseType::Float4x4: return GetMatrixLayout(GetScalarLayout(BaseType::Float), 4, 4); case BaseType::Texture2D: case BaseType::TextureCube: case BaseType::Texture2DArray: case BaseType::Texture2DArrayShadow: case BaseType::Texture2DShadow: case BaseType::Texture3D: case BaseType::TextureCubeShadow: case BaseType::TextureCubeArray: case BaseType::TextureCubeShadowArray: return{ 8, 8 }; default: assert(!"unimplemented"); return{ 0, 1 }; } } LayoutInfo GetScalarLayout(ILBaseType baseType) override { switch (baseType) { case ILBaseType::Int: case ILBaseType::UInt: case ILBaseType::Float: case ILBaseType::Bool: return{ 4, 4 }; case ILBaseType::Int2: return GetVectorLayout(GetScalarLayout(ILBaseType::Int), 2); case ILBaseType::UInt2: return GetVectorLayout(GetScalarLayout(ILBaseType::UInt), 2); case ILBaseType::Float2: return GetVectorLayout(GetScalarLayout(ILBaseType::Float), 2); case ILBaseType::Int3: return GetVectorLayout(GetScalarLayout(ILBaseType::Int), 3); case ILBaseType::UInt3: return GetVectorLayout(GetScalarLayout(ILBaseType::UInt), 3); case ILBaseType::Float3: return GetVectorLayout(GetScalarLayout(ILBaseType::Float), 3); case ILBaseType::Int4: return GetVectorLayout(GetScalarLayout(ILBaseType::Int), 4); case ILBaseType::UInt4: return GetVectorLayout(GetScalarLayout(ILBaseType::UInt), 4); case ILBaseType::Float4: return GetVectorLayout(GetScalarLayout(ILBaseType::Float), 4); case ILBaseType::Float3x3: return GetMatrixLayout(GetScalarLayout(ILBaseType::Float), 3, 3); case ILBaseType::Float4x4: return GetMatrixLayout(GetScalarLayout(ILBaseType::Float), 4, 4); case ILBaseType::Texture2D: case ILBaseType::TextureCube: case ILBaseType::Texture2DArray: case ILBaseType::Texture2DArrayShadow: case ILBaseType::Texture2DShadow: case ILBaseType::Texture3D: case ILBaseType::TextureCubeShadow: case ILBaseType::TextureCubeArray: case ILBaseType::TextureCubeShadowArray: return{ 8, 8 }; default: assert(!"unimplemented"); return{ 0, 1 }; } } LayoutInfo GetArrayLayout(LayoutInfo elementInfo, size_t elementCount) override { LayoutInfo arrayInfo; arrayInfo.size = elementInfo.size * elementCount; arrayInfo.alignment = elementInfo.alignment; return arrayInfo; } LayoutInfo GetVectorLayout(LayoutInfo elementInfo, size_t elementCount) override { LayoutInfo vectorInfo; vectorInfo.size = elementInfo.size * elementCount; vectorInfo.alignment = RoundUpToPowerOfTwo(elementInfo.size * elementInfo.alignment); return vectorInfo; } LayoutInfo GetMatrixLayout(LayoutInfo elementInfo, size_t rowCount, size_t columnCount) override { return GetArrayLayout( GetVectorLayout(elementInfo, columnCount), rowCount); } LayoutInfo BeginStructLayout() override { LayoutInfo structInfo; structInfo.size = 0; structInfo.alignment = 1; return structInfo; } size_t AddStructField(LayoutInfo* ioStructInfo, LayoutInfo fieldInfo) override { ioStructInfo->alignment = std::max(ioStructInfo->alignment, fieldInfo.alignment); ioStructInfo->size = RoundToAlignment(ioStructInfo->size, fieldInfo.alignment); size_t fieldOffset = ioStructInfo->size; ioStructInfo->size += fieldInfo.size; return fieldOffset; } void EndStructLayout(LayoutInfo* ioStructInfo) override { ioStructInfo->size = RoundToAlignment(ioStructInfo->size, ioStructInfo->alignment); } }; struct Std140LayoutRulesImpl : DefaultLayoutRulesImpl { // The `std140` rules require that all array elements // be a multiple of 16 bytes. LayoutInfo GetArrayLayout(LayoutInfo elementInfo, size_t elementCount) override { if (elementInfo.alignment < 16) elementInfo.alignment = 16; elementInfo.size = RoundToAlignment(elementInfo.size, elementInfo.alignment); return DefaultLayoutRulesImpl::GetArrayLayout(elementInfo, elementCount); } // The `std140` rules require that a `struct` type be // alinged to at least 16. LayoutInfo BeginStructLayout() override { LayoutInfo structInfo; structInfo.size = 0; structInfo.alignment = 16; return structInfo; } }; struct Std430LayoutRulesImpl : DefaultLayoutRulesImpl { }; struct PackedLayoutRulesImpl : DefaultLayoutRulesImpl { }; struct HLSLLayoutRulesImpl : Std140LayoutRulesImpl { LayoutInfo GetVectorLayout(LayoutInfo elementInfo, size_t elementCount) override { LayoutInfo vectorInfo; vectorInfo.size = elementInfo.size * elementCount; vectorInfo.alignment = elementInfo.alignment; vectorInfo.avoid16ByteBoundary = true; return vectorInfo; } LayoutInfo BeginStructLayout() override { LayoutInfo structInfo; structInfo.size = 0; structInfo.alignment = 16; structInfo.avoid16ByteBoundary = true; return structInfo; } size_t AddStructField(LayoutInfo* ioStructInfo, LayoutInfo fieldInfo) override { ioStructInfo->size = RoundToAlignment(ioStructInfo->size, fieldInfo.alignment); // If this field would straddle a 16-byte boundary, then round up to the start // of a 16-byte boundary. Note that the computation is for the *last* byte // offset for the field, and *not* the byte offset of the end of the field. // It is okay for a field to end right on a 16-byte boundary without triggering // this rule. size_t firstOffset = ioStructInfo->size; size_t lastOffset = firstOffset + fieldInfo.size - 1; if( (firstOffset / 16) != (lastOffset / 16) ) { ioStructInfo->size = RoundToAlignment(firstOffset, 16u); } size_t fieldOffset = ioStructInfo->size; ioStructInfo->size += fieldInfo.size; return fieldOffset; } void EndStructLayout(LayoutInfo* ioStructInfo) override { ioStructInfo->size = RoundToAlignment(ioStructInfo->size, ioStructInfo->alignment); } }; Std140LayoutRulesImpl kStd140LayoutRulesImpl; Std430LayoutRulesImpl kStd430LayoutRulesImpl; PackedLayoutRulesImpl kPackedLayoutRulesImpl; HLSLLayoutRulesImpl kHlslLayoutRulesImpl; LayoutRulesImpl* GetLayoutRulesImpl(LayoutRule rule) { switch (rule) { case LayoutRule::Std140: return &kStd140LayoutRulesImpl; case LayoutRule::Std430: return &kStd430LayoutRulesImpl; case LayoutRule::HLSL: return &kHlslLayoutRulesImpl; case LayoutRule::Packed: return &kPackedLayoutRulesImpl; default: return nullptr; } } LayoutInfo GetLayout(ExpressionType* type, LayoutRulesImpl* rules) { if (auto basicType = dynamic_cast(type)) { if (auto structDecl = basicType->structDecl) { LayoutInfo info = rules->BeginStructLayout(); for (auto field : structDecl->GetFields()) { rules->AddStructField(&info, GetLayout(field->Type.Ptr(), rules)); } rules->EndStructLayout(&info); return info; } else { return rules->GetScalarLayout(basicType->BaseType); } } else if (auto arrayType = dynamic_cast(type)) { return rules->GetArrayLayout( GetLayout(arrayType->BaseType.Ptr(), rules), arrayType->ArrayLength); } else if (auto genericType = dynamic_cast(type)) { return GetLayout(genericType->BaseType.Ptr(), rules); } else { assert(!"unimplemented"); return{ 0, 1 }; } } LayoutInfo GetLayout(ILType* type, LayoutRulesImpl* rules) { if (auto basicType = dynamic_cast(type)) { return rules->GetScalarLayout(basicType->Type); } else if (auto arrayType = dynamic_cast(type)) { return rules->GetArrayLayout( GetLayout(arrayType->BaseType.Ptr(), rules), arrayType->ArrayLength); } else if (auto structType = dynamic_cast(type)) { LayoutInfo info = rules->BeginStructLayout(); for (auto field : structType->Members) { rules->AddStructField(&info, GetLayout(field.Type.Ptr(), rules)); } rules->EndStructLayout(&info); return info; } else if (auto recordType = dynamic_cast(type)) { // TODO: this need to be implemented LayoutInfo info = { 0, 1 }; return info; } else if (auto genericType = dynamic_cast(type)) { return GetLayout(genericType->BaseType.Ptr(), rules); } else { assert(!"unimplemented"); return{ 0, 1 }; } } LayoutInfo GetLayout(ExpressionType* type, LayoutRule rule) { LayoutRulesImpl* rulesImpl = GetLayoutRulesImpl(rule); return GetLayout(type, rulesImpl); } LayoutInfo GetLayout(ILType* type, LayoutRule rule) { LayoutRulesImpl* rulesImpl = GetLayoutRulesImpl(rule); return GetLayout(type, rulesImpl); } }} ================================================ FILE: Source/SpireCore/TypeLayout.h ================================================ #ifndef SPIRE_TYPE_LAYOUT_H #define SPIRE_TYPE_LAYOUT_H #include "../CoreLib/Basic.h" #include "IL.h" namespace Spire { namespace Compiler { // Forward declarations enum class BaseType; class ExpressionType; // enum class LayoutRule { Std140, Std430, HLSL, Packed, }; struct LayoutInfo { size_t size = 0; size_t alignment = 1; bool avoid16ByteBoundary = false; LayoutInfo() = default; LayoutInfo(size_t s, size_t a) { size = s; alignment = a; } }; struct LayoutRulesImpl { // Get size and alignment for a single value of base type. virtual LayoutInfo GetScalarLayout(BaseType baseType) = 0; virtual LayoutInfo GetScalarLayout(ILBaseType baseType) = 0; // Get size and alignment for an array of elements virtual LayoutInfo GetArrayLayout(LayoutInfo elementInfo, size_t elementCount) = 0; // Get layout for a vector or matrix type virtual LayoutInfo GetVectorLayout(LayoutInfo elementInfo, size_t elementCount) = 0; virtual LayoutInfo GetMatrixLayout(LayoutInfo elementInfo, size_t rowCount, size_t columnCount) = 0; // Begin doing layout on a `struct` type virtual LayoutInfo BeginStructLayout() = 0; // Add a field to a `struct` type, and return the offset for the field virtual size_t AddStructField(LayoutInfo* ioStructInfo, LayoutInfo fieldInfo) = 0; // End layout for a struct, and finalize its size/alignment. virtual void EndStructLayout(LayoutInfo* ioStructInfo) = 0; }; LayoutRulesImpl* GetLayoutRulesImpl(LayoutRule rule); LayoutInfo GetLayout(ExpressionType* type, LayoutRulesImpl* rules); LayoutInfo GetLayout(ILType* type, LayoutRulesImpl* rules); LayoutInfo GetLayout(ExpressionType* type, LayoutRule rule = LayoutRule::Std430); LayoutInfo GetLayout(ILType* type, LayoutRule rule = LayoutRule::Std430); inline size_t GetTypeSize(ExpressionType* type, LayoutRule rule = LayoutRule::Std430) { return GetLayout(type, rule).size; } inline size_t GetTypeSize(ILType* type, LayoutRule rule = LayoutRule::Std430) { return GetLayout(type, rule).size; } inline size_t GetTypeAlignment(ExpressionType* type, LayoutRule rule = LayoutRule::Std430) { return GetLayout(type, rule).alignment; } inline size_t GetTypeAlignment(ILType* type, LayoutRule rule = LayoutRule::Std430) { return GetLayout(type, rule).alignment; } }} #endif ================================================ FILE: Source/SpireCore/TypeTranslation.cpp ================================================ #include "TypeTranslation.h" #include "SymbolTable.h" namespace Spire { namespace Compiler { RefPtr TranslateExpressionType(ExpressionType * type, Dictionary> * genericTypeMappings) { RefPtr resultType = 0; if (auto basicType = type->AsBasicType()) { if (basicType->BaseType == BaseType::Struct) { resultType = basicType->Struct->Type; } else if (basicType->BaseType == BaseType::Record) { if (genericTypeMappings) return (*genericTypeMappings)[basicType->RecordTypeName](); else throw InvalidProgramException("unexpected record type."); } else if (basicType->BaseType == BaseType::Generic) { if (genericTypeMappings) return (*genericTypeMappings)[basicType->GenericTypeVar](); else throw InvalidProgramException("unexpected generic type."); } else { auto base = new ILBasicType(); base->Type = (ILBaseType)basicType->BaseType; resultType = base; } } else if (auto arrType = type->AsArrayType()) { auto nArrType = new ILArrayType(); nArrType->BaseType = TranslateExpressionType(arrType->BaseType.Ptr(), genericTypeMappings); nArrType->ArrayLength = arrType->ArrayLength; resultType = nArrType; } else if (auto genType = type->AsGenericType()) { auto gType = new ILGenericType(); gType->GenericTypeName = genType->GenericTypeName; gType->BaseType = TranslateExpressionType(genType->BaseType.Ptr(), genericTypeMappings); resultType = gType; } return resultType; } RefPtr TranslateExpressionType(const RefPtr & type, Dictionary> * genericTypeMappings) { return TranslateExpressionType(type.Ptr(), genericTypeMappings); } } } ================================================ FILE: Source/SpireCore/TypeTranslation.h ================================================ #ifndef SPIRE_TYPE_TRANSLATION_H #define SPIRE_TYPE_TRANSLATION_H #include "Syntax.h" #include "IL.h" namespace Spire { namespace Compiler { RefPtr TranslateExpressionType(ExpressionType * type, Dictionary> * genericTypeMappings = nullptr); RefPtr TranslateExpressionType(const RefPtr & type, Dictionary> * genericTypeMappings = nullptr); } } #endif ================================================ FILE: Source/SpireCore/VariantIR.cpp ================================================ #include "VariantIR.h" #include "Closure.h" #include "StringObject.h" #include "GetDependencyVisitor.h" namespace Spire { namespace Compiler { void ShaderIR::EliminateDeadCode() { // mark entry points auto MarkUsing = [&](String compName, String userWorld) { if (auto defs = DefinitionsByComponent.TryGetValue(compName)) { if (auto def = defs->TryGetValue(userWorld)) (*def)->IsEntryPoint = true; else { for (auto & world : Shader->Pipeline->WorldDependency[userWorld]()) { if (auto def2 = defs->TryGetValue(world)) { (*def2)->IsEntryPoint = true; break; } } } } }; for (auto & impOp : Shader->Pipeline->SyntaxNode->GetImportOperators()) for (auto & ref : impOp->Usings) MarkUsing(ref, impOp->DestWorld.Content); for (auto & req : Shader->Pipeline->Components) if (req.Value->IsRequire()) { for (auto & impl : req.Value->Implementations) { if (impl->Worlds.Count()) { for (auto & world : impl->Worlds) MarkUsing(req.Key, world); } else { for (auto & world : Shader->Pipeline->Worlds) MarkUsing(req.Key, world.Key); } } } for (auto & def : Definitions) if (def->SyntaxNode->HasSimpleAttribute("FragDepth")) def->IsEntryPoint = true; List workList; HashSet referencedDefs; for (auto & def : Definitions) { if (def->IsEntryPoint) { if (referencedDefs.Add(def.Ptr())) workList.Add(def.Ptr()); } } for (int i = 0; i < workList.Count(); i++) { auto def = workList[i]; for (auto & dep : def->Dependency) { if (referencedDefs.Add(dep)) workList.Add(dep); } } List> newDefinitions; for (auto & def : Definitions) { if (referencedDefs.Contains(def.Ptr())) { newDefinitions.Add(def); EnumerableHashSet newSet; for (auto & comp : def->Users) if (referencedDefs.Contains(comp)) { newSet.Add(comp); } def->Users = newSet; newSet.Clear(); for (auto & comp : def->Dependency) if (referencedDefs.Contains(comp)) { newSet.Add(comp); } def->Dependency = newSet; } } Definitions = _Move(newDefinitions); for (auto & kv : DefinitionsByComponent) { for (auto & def : kv.Value) if (!referencedDefs.Contains(def.Value)) kv.Value.Remove(def.Key); } } class ReferenceWorkItem { public: ComponentDependency Dependency; String SourceWorld; int GetHashCode() { return Dependency.GetHashCode(); } bool operator == (const ReferenceWorkItem & other) { return Dependency == other.Dependency && SourceWorld == other.SourceWorld; } }; void ShaderIR::ResolveComponentReference() { // build bidirectional dependency map of component definitions for (auto & comp : Definitions) { comp->Dependency.Clear(); comp->Users.Clear(); } for (auto & comp : Definitions) { List workList; for (auto & dep : GetDependentComponents(comp->SyntaxNode.Ptr())) { ReferenceWorkItem item; item.Dependency = dep; item.SourceWorld = dep.ImportOperator ? dep.ImportOperator->SourceWorld.Content : comp->World; workList.Add(item); } HashSet proceseedDefCompss; for (int i = 0; i < workList.Count(); i++) { auto dep = workList[i]; if (!proceseedDefCompss.Add(dep)) continue; auto & depDefs = DefinitionsByComponent[dep.Dependency.ReferencedComponent](); // select the best overload according to import operator ordering, // prefer user-pinned definitions (as provided in the choice file) List depWorlds; depWorlds.Add(dep.SourceWorld); for (auto & w : Shader->Pipeline->WorldDependency[dep.SourceWorld]()) depWorlds.Add(w); depWorlds.Add(""); for (int pass = 0; pass < 2; pass++) { // in the first pass, examine the pinned definitions only // in the second pass, examine all the rest definitions for (auto & depWorld : depWorlds) { auto refComp = Shader->AllComponents[dep.Dependency.ReferencedComponent](); bool isPinned = refComp.Symbol->Type->PinnedWorlds.Contains(depWorld); if ((pass == 0 && !isPinned) || (pass == 1 && isPinned)) continue; ComponentDefinitionIR * depDef; if (depDefs.TryGetValue(depWorld, depDef)) { comp->Dependency.Add(depDef); depDef->Users.Add(comp.Ptr()); // add additional dependencies due to import operators auto processImportOperatorUsings = [&](ImportOperatorDefSyntaxNode * importOp) { for (auto & importUsing : importOp->Usings) { ComponentInstance refComp; if (!Shader->AllComponents.TryGetValue(importUsing, refComp)) throw InvalidProgramException("import operator dependency not exists."); ReferenceWorkItem workItem; workItem.Dependency = ComponentDependency(refComp.Symbol->UniqueName, nullptr); workItem.SourceWorld = importOp->SourceWorld.Content; workList.Add(workItem); } }; if (dep.Dependency.ImportOperator) { processImportOperatorUsings(dep.Dependency.ImportOperator); } if (depWorld != dep.SourceWorld && depWorld != "") { auto importPath = SymbolTable->FindImplicitImportOperatorChain(Shader->Pipeline, depWorld, dep.SourceWorld, refComp.Symbol->Type->DataType); if (importPath.Count() == 0) continue; processImportOperatorUsings(importPath.First().Nodes.Last().ImportOperator); } goto selectionEnd; // first preferred overload is found, terminate searching } } } selectionEnd:; } } } List ShaderIR::GetComponentDependencyOrder() { List result, workList; HashSet set; for (auto & comp : DefinitionsByComponent) { bool emptyDependency = true; for (auto & def : comp.Value) if (def.Value->Dependency.Count()) { emptyDependency = false; break; } if (emptyDependency) { workList.Add(comp.Key); } } for (int i = 0; i < workList.Count(); i++) { auto comp = workList[i]; if (!set.Contains(comp)) { bool insertable = true; for (auto & def : DefinitionsByComponent[comp]()) { for (auto & dep : def.Value->Dependency) if (!set.Contains(dep->UniqueName)) { insertable = false; goto breakLoc; } } breakLoc:; if (insertable) { if (set.Add(comp)) { result.Add(comp); for (auto & def : DefinitionsByComponent[comp]()) for (auto & user : def.Value->Users) workList.Add(user->UniqueName); } } } } return result; } EnumerableHashSet& ComponentDefinitionIR::GetComponentFunctionDependencyClosure() { if (dependencyClosure.Count() || Dependency.Count() == 0) return dependencyClosure; for (auto & dep : Dependency) { dependencyClosure.Add(dep); if (dep->SyntaxNode->IsComponentFunction()) { for (auto & x : dep->GetComponentFunctionDependencyClosure()) dependencyClosure.Add(x); } } return dependencyClosure; } } } ================================================ FILE: Source/SpireCore/VariantIR.h ================================================ #ifndef VARIANT_IR_H #define VARIANT_IR_H #include "Syntax.h" namespace Spire { namespace Compiler { class ShaderClosure; class ModuleInstanceIR : public RefObject { public: ShaderSyntaxNode * SyntaxNode; String BindingName; CodePosition UsingPosition; int BindingIndex; bool IsTopLevel = false; List SubModuleInstances; }; class ComponentDefinitionIR : public RefObject { private: EnumerableHashSet dependencyClosure; public: String OriginalName, UniqueName, UniqueKey; RefPtr SyntaxNode; RefPtr Type; ModuleInstanceIR * ModuleInstance = nullptr; String World; bool IsEntryPoint = false; EnumerableHashSet Users, Dependency; // Bidirectional dependency; EnumerableHashSet & GetComponentFunctionDependencyClosure(); void ClearDependency() { Dependency.Clear(); dependencyClosure.Clear(); } }; class ShaderIR : public RefObject { public: ShaderClosure * Shader; SymbolTable * SymbolTable; List> ModuleInstances; List> Definitions; EnumerableDictionary> DefinitionsByComponent; void EliminateDeadCode(); // returns remaining definitions in reverse dependency order void ResolveComponentReference(); // resolve reference and build dependency map List GetComponentDependencyOrder(); // returns a list of all components' unique names in dependency order template void RemoveDefinitions(const ShouldRemoveFunc &shouldRemove) { List> newDefinitions; for (auto & def : Definitions) { if (!shouldRemove(def.Ptr())) { newDefinitions.Add(def); } } Definitions = _Move(newDefinitions); for (auto & kv : DefinitionsByComponent) { for (auto & def : kv.Value) if (shouldRemove(def.Value)) kv.Value.Remove(def.Key); } } }; } } #endif ================================================ FILE: Source/SpireLib/SpireLib.cpp ================================================ #include "SpireLib.h" #include "../CoreLib/LibIO.h" #include "../CoreLib/Tokenizer.h" #include "../SpireCore/StdInclude.h" #include "../../Spire.h" #include "../SpireCore/TypeLayout.h" #include "../SpireCore/Preprocessor.h" using namespace CoreLib::Basic; using namespace CoreLib::IO; using namespace CoreLib::Text; using namespace Spire::Compiler; struct SpireDiagnosticSink { int errorCount; CoreLib::List diagnostics; }; struct SpireUniformFieldImpl { int offset; int size; String name; String type; }; struct SpireParameterSet { ILModuleParameterSet * paramSet = nullptr; int bindingSlotCount = 0; int uniformBufferLegacyBindingPoint = -1; List bindings; List subsets; List uniforms; }; class ComponentMetaData { public: RefPtr Type; String TypeName; String Name; bool IsSpecialize = false; List Values; int Offset = 0; int Alignment = 0; int Size = 0; BindableResourceType bindableType; int typeSpecificBindingIndex = -1; int typeAgnosticBindingIndex = -1; int GetHashCode() { return Name.GetHashCode(); } bool operator == (const ComponentMetaData & other) { return Name == other.Name; } }; struct CompilerState; struct SpireModule { String Name; int Id = 0; int UniformBufferSize = 0; int UniformBufferOffset = 0; SpireBindingIndex BindingIndex; List Parameters; EnumerableDictionary ParameterMap; List Requirements; Dictionary Attribs; List> SubModules; CompilerState * State = nullptr; static int IdAllocator; }; int SpireModule::IdAllocator = 0; namespace SpireLib { void ReadSource(EnumerableDictionary & sources, CoreLib::Text::TokenReader & parser, String src) { auto getShaderSource = [&]() { auto token = parser.ReadToken(); int endPos = token.Position.Pos + 1; int brace = 0; while (endPos < src.Length() && !(src[endPos] == '}' && brace == 0)) { if (src[endPos] == '{') brace++; else if (src[endPos] == '}') brace--; endPos++; } while (!parser.IsEnd() && parser.NextToken().Position.Pos != endPos) parser.ReadToken(); parser.ReadToken(); return src.SubString(token.Position.Pos + 1, endPos - token.Position.Pos - 1); }; while (!parser.IsEnd() && !parser.LookAhead("}")) { auto worldName = parser.ReadWord(); StageSource compiledSrc; if (parser.LookAhead("binary")) { parser.ReadToken(); parser.Read("{"); while (!parser.LookAhead("}") && !parser.IsEnd()) { auto val = parser.ReadUInt(); compiledSrc.BinaryCode.AddRange((unsigned char*)&val, sizeof(unsigned int)); if (parser.LookAhead(",")) parser.ReadToken(); } parser.Read("}"); } if (parser.LookAhead("text")) { parser.ReadToken(); compiledSrc.MainCode = getShaderSource(); } sources[worldName] = compiledSrc; } } StageSource ShaderLib::GetStageSource(String stage) { StageSource rs; Sources.TryGetValue(stage, rs); return rs; } ShaderLib::ShaderLib(CoreLib::Basic::String fileName) { Reload(fileName); } void ShaderLib::Reload(CoreLib::Basic::String fileName) { Load(fileName); } bool ShaderLib::CompileFrom(String symbolName, String sourceFileName, String schedule) { Spire::Compiler::CompileResult result; CompileOptions options; options.ScheduleSource = schedule; options.SymbolToCompile = symbolName; options.Mode = CompilerMode::ProduceShader; auto shaderLibs = CompileShaderSourceFromFile(result, sourceFileName, options); if (result.GetErrorCount() == 0) { for (auto & lib : shaderLibs) { if (lib.MetaData.ShaderName == symbolName) { FromString(shaderLibs[0].ToString()); return true; } } } result.PrintDiagnostics(); return false; } List CompileUnits(Spire::Compiler::CompileResult & compileResult, ShaderCompiler * compiler, List & units, Spire::Compiler::CompileOptions & options) { List resultFiles; compiler->Compile(compileResult, units, options); if (compileResult.GetErrorCount() == 0) { if (options.Mode == CompilerMode::ProduceShader) { EnumerableDictionary shaderLibs; for (auto file : compileResult.CompiledSource) { ShaderLibFile libFile; libFile.MetaData = file.Value.MetaData; libFile.Sources = file.Value.Stages; resultFiles.Add(libFile); } } } return resultFiles; } List CompileShaderSource(Spire::Compiler::CompileResult & compileResult, const CoreLib::String & src, const CoreLib::String & fileName, Spire::Compiler::CompileOptions & options) { struct IncludeHandlerImpl : IncludeHandler { List searchDirs; virtual bool TryToFindIncludeFile( CoreLib::String const& pathToInclude, CoreLib::String const& pathIncludedFrom, CoreLib::String* outFoundPath, CoreLib::String* outFoundSource) override { String path = Path::Combine(Path::GetDirectoryName(pathIncludedFrom), pathToInclude); if (File::Exists(path)) { *outFoundPath = path; *outFoundSource = File::ReadAllText(path); return true; } for (auto & dir : searchDirs) { path = Path::Combine(dir, pathToInclude); if (File::Exists(path)) { *outFoundPath = path; *outFoundSource = File::ReadAllText(path); return true; } } return false; } }; IncludeHandlerImpl includeHandler; includeHandler.searchDirs = options.SearchDirectories; Spire::Compiler::NamingCounter = 0; RefPtr compiler = CreateShaderCompiler(); List units; HashSet processedUnits; List unitsToInclude; unitsToInclude.Add(fileName); processedUnits.Add(fileName); auto searchDirs = options.SearchDirectories; searchDirs.Add(Path::GetDirectoryName(fileName)); searchDirs.Reverse(); auto predefUnit = compiler->Parse(compileResult, SpireStdLib::GetCode(), "stdlib", &includeHandler, options.PreprocessorDefinitions); for (int i = 0; i < unitsToInclude.Count(); i++) { auto inputFileName = unitsToInclude[i]; try { String source = src; if (i > 0) source = File::ReadAllText(inputFileName); auto unit = compiler->Parse(compileResult, source, inputFileName, &includeHandler, options.PreprocessorDefinitions); units.Add(unit); if (unit.SyntaxNode) { for (auto inc : unit.SyntaxNode->GetUsings()) { bool found = false; for (auto & dir : searchDirs) { String includeFile = Path::Combine(dir, inc->fileName.Content); if (File::Exists(includeFile)) { if (processedUnits.Add(includeFile)) { unitsToInclude.Add(includeFile); } found = true; break; } } if (!found) { compileResult.GetErrorWriter()->diagnose(inc->fileName.Position, Diagnostics::cannotFindFile, inc->fileName); } } } } catch (IOException) { compileResult.GetErrorWriter()->diagnose(CodePosition(0, 0, 0, ""), Diagnostics::cannotOpenFile, inputFileName); } } units.Add(predefUnit); if (compileResult.GetErrorCount() == 0) return CompileUnits(compileResult, compiler.Ptr(), units, options); else return List(); } List CompileShaderSourceFromFile(Spire::Compiler::CompileResult & compileResult, const CoreLib::Basic::String & sourceFileName, Spire::Compiler::CompileOptions & options) { try { return CompileShaderSource(compileResult, File::ReadAllText(sourceFileName), sourceFileName, options); } catch (IOException) { compileResult.GetErrorWriter()->diagnose(CodePosition(0, 0, 0, ""), Diagnostics::cannotOpenFile, Path::GetFileName(sourceFileName)); } return List(); } void ShaderLibFile::AddSource(CoreLib::Basic::String source, CoreLib::Text::TokenReader & parser) { ReadSource(Sources, parser, source); } CoreLib::String ShaderLibFile::ToString() { StringBuilder writer; writer << "name " << MetaData.ShaderName << EndLine; for (auto & ublock : MetaData.ParameterSets) { writer << "paramset \"" << ublock.Key << "\" size " << ublock.Value->BufferSize << " binding " << ublock.Value->DescriptorSetId << "\n{\n"; for (auto & entry : ublock.Value->Parameters) { writer << entry.Value->Name << "(\"" << entry.Key << "\") : "; entry.Value->Type->Serialize(writer); writer << " at "; if (entry.Value->BufferOffset == -1) { writer << "binding("; for (auto binding : entry.Value->BindingPoints) writer << binding << " "; writer << ")"; } else { // TODO(tfoley): Should use the correct layout rule here, and not assume `Std140` writer << "buffer(" << entry.Value->BufferOffset << ", " << (int)GetTypeSize(entry.Value->Type.Ptr(), LayoutRule::Std140) << ")"; } writer << ";\n"; } writer << "}\n"; } writer << "source" << EndLine << "{" << EndLine; for (auto & src : Sources) { writer << src.Key << EndLine; if (src.Value.BinaryCode.Count()) { writer << "binary" << EndLine << "{" << EndLine; auto binaryBuffer = (unsigned int*)src.Value.BinaryCode.Buffer(); for (int i = 0; i < src.Value.BinaryCode.Count() / 4; i++) { writer << String((long long)binaryBuffer[i]) << ","; if ((i + 1) % 10) writer << EndLine; } writer << EndLine << "}" << EndLine; } writer << "text" << EndLine << "{" << EndLine; writer << src.Value.MainCode << EndLine; writer << "}" << EndLine; } writer << "}" << EndLine; StringBuilder formatSB; IndentString(formatSB, writer.ProduceString()); return formatSB.ProduceString(); } void ShaderLibFile::Clear() { Sources.Clear(); MetaData.ParameterSets.Clear(); Sources.Clear(); } void ShaderLibFile::SaveToFile(CoreLib::Basic::String fileName) { StreamWriter fwriter(fileName); fwriter.Write(ToString()); } void ShaderLibFile::FromString(const String & src) { Clear(); CoreLib::Text::TokenReader parser(src); while (!parser.IsEnd()) { auto fieldName = parser.ReadWord(); if (fieldName == "name") { MetaData.ShaderName = parser.ReadWord(); } else if (fieldName == "source") { parser.Read("{"); ReadSource(Sources, parser, src); parser.Read("}"); } else if (fieldName == "paramset") { RefPtr paramSet = new ILModuleParameterSet(); paramSet->BindingName = parser.ReadStringLiteral(); if (parser.LookAhead("size")) { parser.ReadToken(); paramSet->BufferSize = parser.ReadInt(); } if (parser.LookAhead("binding")) { parser.ReadToken(); paramSet->DescriptorSetId = parser.ReadInt(); } parser.Read("{"); while (!parser.LookAhead("}")) { RefPtr inst = new ILModuleParameterInstance(); inst->Name = parser.ReadWord(); parser.Read("("); auto key = parser.ReadStringLiteral(); parser.Read(")"); inst->Type = ILType::Deserialize(parser); parser.Read("at"); if (parser.LookAhead("binding")) { parser.ReadToken(); parser.Read("("); while (!parser.LookAhead(")")) inst->BindingPoints.Add(parser.ReadInt()); parser.Read(")"); } else { parser.Read("buffer"); parser.Read("("); inst->BufferOffset = parser.ReadInt(); parser.Read(")"); } paramSet->Parameters.Add(key, inst); } parser.Read("}"); MetaData.ParameterSets.Add(paramSet->BindingName, paramSet); } } } void ShaderLibFile::Load(String fileName) { String src = File::ReadAllText(fileName); FromString(src); } } using namespace SpireLib; struct ShaderParameter { String TypeName; String Name; int BindingId; }; class Shader { friend class ::CompilationContext; private: String shaderName; String src; public: int Id; List Parameters; RefPtr Syntax; Shader(String name, String source) { static int idAllocator = 0; Id = idAllocator++; shaderName = name; src = source; } String GetName() const { return shaderName; } String GetSource() const { return src; } }; class CompileResult { public: CoreLib::EnumerableDictionary Sources; CoreLib::EnumerableDictionary> ParamSets; }; struct CompilerState : public RefObject { List moduleUnits; HashSet processedModuleUnits; EnumerableDictionary> modules; EnumerableDictionary> shaders; RefPtr context; RefPtr Parent; // the version of this state int Version = 0; // the version of parent states cached in this state int CachedParentVersion = 0; void Update() // update this state to include latest version of parent { if (Parent) { Parent->Update(); if (Parent->Version != CachedParentVersion) { context->MergeWith(Parent->context.Ptr()); CachedParentVersion = Parent->Version; Version++; } } } int errorCount = 0; CompilerState() { context = new Spire::Compiler::CompilationContext(); } CompilerState(RefPtr parent) { this->Parent = parent; // Do the ugly thing to copy parent's symbol table to child if (parent) context = new Spire::Compiler::CompilationContext(*parent->context); } ~CompilerState() { printf("break"); } }; class CompilationContext; struct SpireCompilationEnvironment { ::CompilationContext * context; RefPtr<::CompilerState> state; }; class CompilationContext { public: bool useCache = false; CoreLib::String cacheDir; List> states; RefPtr compiler; struct IncludeHandlerImpl : IncludeHandler { List searchDirs; virtual bool TryToFindIncludeFile( CoreLib::String const& pathToInclude, CoreLib::String const& pathIncludedFrom, CoreLib::String* outFoundPath, CoreLib::String* outFoundSource) override { String path = Path::Combine(Path::GetDirectoryName(pathIncludedFrom), pathToInclude); if (File::Exists(path)) { *outFoundPath = path; *outFoundSource = File::ReadAllText(path); return true; } for (auto & dir : searchDirs) { path = Path::Combine(dir, pathToInclude); if (File::Exists(path)) { *outFoundPath = path; *outFoundSource = File::ReadAllText(path); return true; } } return false; } }; IncludeHandlerImpl includeHandler; public: CompileOptions Options; CompilationContext(bool /*pUseCache*/, CoreLib::String /*pCacheDir*/) { compiler = CreateShaderCompiler(); states.Add(new ::CompilerState()); LoadModuleSource(states.First().Ptr(), SpireStdLib::GetCode(), "stdlib", NULL); } ~CompilationContext() { SpireStdLib::Finalize(); } SpireModule * FindModule(CoreLib::String moduleName) { auto ptr = states.Last()->modules.TryGetValue(moduleName); if (ptr) return ptr->Ptr(); else return nullptr; } StringBuilder moduleKeyBuilder; SpireModule * SpecializeModule(SpireModule * module, int * params, int numParams, SpireDiagnosticSink * sink) { moduleKeyBuilder.Clear(); moduleKeyBuilder.Append(module->Name); for (auto & param : module->Parameters) { if (param.IsSpecialize) { int id = -1; for (int i = 0; i < numParams; i++) { moduleKeyBuilder.Append(params[id]); moduleKeyBuilder.Append('_'); } } } if (auto smodule = module->State->modules.TryGetValue(moduleKeyBuilder.Buffer())) return smodule->Ptr(); RefPtr originalModule; module->State->context->Symbols.Shaders.TryGetValue(module->Name, originalModule); CompileUnit unit; unit.SyntaxNode = new ProgramSyntaxNode(); CloneContext cloneCtx; auto newModule = originalModule->SyntaxNode->Clone(cloneCtx); newModule->Name.Content = moduleKeyBuilder.ToString(); int id = 0; for (auto & member : newModule->Members) { if (auto param = member.As()) { if (auto specialize = param->FindSpecializeModifier()) { if (id >= numParams) { return nullptr; } auto newParam = param->Clone(cloneCtx); newParam->modifiers.first = nullptr; newParam->modifiers.flags = ModifierFlag::Public; param->BlockStatement = nullptr; auto expr = new ConstantExpressionSyntaxNode(); if (param->Type->Equals(ExpressionType::Bool)) expr->ConstType = ConstantExpressionSyntaxNode::ConstantType::Bool; else expr->ConstType = ConstantExpressionSyntaxNode::ConstantType::Int; expr->IntValue = params[id]; newParam->Expression = expr; newModule->Members.Add(newParam); param->Name.Content = param->Name.Content + "placeholder"; id++; } } } unit.SyntaxNode->Members.Add(newModule); List units; units.Add(unit); UpdateModuleLibrary(module->State, units, sink); return FindModule(newModule->Name.Content); } LayoutRule GetUniformBufferLayoutRule() { if (this->Options.Target == CodeGenTarget::HLSL) return LayoutRule::HLSL; else return LayoutRule::Std140; } RefPtr CreateModule(CompilerState * state, Spire::Compiler::ShaderSymbol * shader, LayoutInfo & parentParamStruct, SpireBindingIndex & bindingIndex) { RefPtr newModule = new SpireModule(); newModule->State = state; auto & meta = *newModule; meta.Id = SpireModule::IdAllocator++; meta.Name = shader->SyntaxNode->Name.Content; meta.BindingIndex = bindingIndex; int offsets[2] = { 0, 0 }; for (auto attrib : shader->SyntaxNode->GetModifiersOfType()) meta.Attribs[attrib->Key] = attrib->Value.Content; auto layout = GetLayoutRulesImpl(GetUniformBufferLayoutRule()); LayoutInfo requireStruct = layout->BeginStructLayout(); bool firstCompEncountered = false; for (auto & comp : shader->Components) { if (comp.Value->Implementations.Count() != 1) continue; auto impl = comp.Value->Implementations.First(); if (!impl->SyntaxNode->IsRequire() && !impl->SyntaxNode->IsParam()) continue; if (comp.Value->Implementations.First()->SyntaxNode->IsComponentFunction()) continue; auto & structInfo = impl->SyntaxNode->IsParam() ? parentParamStruct : requireStruct; ComponentMetaData compMeta; compMeta.Name = comp.Key; compMeta.Type = comp.Value->Type->DataType; compMeta.TypeName = compMeta.Type->ToString(); if (auto specialize = impl->SyntaxNode->FindSpecializeModifier()) { for (auto val : specialize->Values) { compMeta.Values.Add(dynamic_cast(val.Ptr())->IntValue); } compMeta.IsSpecialize = true; } auto bindableType = compMeta.Type->GetBindableResourceType(); if (compMeta.Type->GetBindableResourceType() == BindableResourceType::NonBindable) { if (!firstCompEncountered) { // TODO(tfoley): This logic seems very wrong. // We should be handling things by adding a field to the `parentParamStruct` // (which should handle whatever layout rules it wants to use) // rather than doing this ad hoc logic to special-case HLSL... firstCompEncountered = true; if (GetUniformBufferLayoutRule() == LayoutRule::HLSL) parentParamStruct.size = (size_t)RoundToAlignment((int)parentParamStruct.size, 16); meta.UniformBufferOffset = (int)parentParamStruct.size; } compMeta.Alignment = (int)GetTypeAlignment(compMeta.Type.Ptr(), GetUniformBufferLayoutRule()); compMeta.Size = (int)GetTypeSize(compMeta.Type.Ptr(), GetUniformBufferLayoutRule()); auto fieldInfo = GetLayout(compMeta.Type.Ptr(), layout); compMeta.Offset = (int)layout->AddStructField(&structInfo, fieldInfo); } else { switch (bindableType) { case BindableResourceType::Texture: compMeta.typeSpecificBindingIndex = bindingIndex.texture; bindingIndex.texture++; break; case BindableResourceType::Sampler: compMeta.typeSpecificBindingIndex = bindingIndex.sampler; bindingIndex.sampler++; break; case BindableResourceType::Buffer: compMeta.typeSpecificBindingIndex = bindingIndex.uniformBuffer; bindingIndex.uniformBuffer++; break; case BindableResourceType::StorageBuffer: compMeta.typeSpecificBindingIndex = bindingIndex.storageBuffer; bindingIndex.storageBuffer++; break; } compMeta.typeAgnosticBindingIndex = bindingIndex.general; bindingIndex.general++; } if (impl->SyntaxNode->IsRequire()) meta.Requirements.Add(compMeta); else { meta.Parameters.Add(compMeta); meta.ParameterMap[compMeta.Name] = compMeta; } } layout->EndStructLayout(&requireStruct); for (auto sub : shader->SyntaxNode->GetMembersOfType()) { newModule->SubModules.Add(CreateModule(state, state->context->Symbols.Shaders[sub->ShaderName.Content]().Ptr(), parentParamStruct, bindingIndex)); } meta.UniformBufferSize = (int)(parentParamStruct.size - meta.UniformBufferOffset); return newModule; } void UpdateModuleLibrary(CompilerState * state, List & units, SpireDiagnosticSink * sink) { Spire::Compiler::CompileResult result; compiler->Compile(result, *state->context, units, Options); state->Version++; for (auto & shader : state->context->Symbols.Shaders) { if (!state->modules.ContainsKey(shader.Key)) { SpireBindingIndex bindingIndex; auto layout = GetLayoutRulesImpl(GetUniformBufferLayoutRule()); LayoutInfo paramStruct = layout->BeginStructLayout(); RefPtr newModule = CreateModule(state, shader.Value.Ptr(), paramStruct, bindingIndex); newModule->BindingIndex; layout->EndStructLayout(¶mStruct); newModule->UniformBufferSize = (int)paramStruct.size; state->modules.Add(shader.Key, newModule); } } for (auto & unit : units) { for (auto & shader : unit.SyntaxNode->GetMembersOfType()) { RefPtr rs = new Shader(shader->Name.Content, ""); int i = 0; rs->Syntax = shader; for (auto & param : shader->Parameters) { ShaderParameter p; p.BindingId = i; p.Name = param->ModuleName.Content; p.TypeName = param->InterfaceName.Content; rs->Parameters.Add(p); i++; } state->shaders[shader->Name.Content] = rs; } for (auto & shader : unit.SyntaxNode->GetMembersOfType()) { if (shader->IsModule) continue; RefPtr rs = new Shader(shader->Name.Content, ""); rs->Syntax = shader; HashSet usedIds; for (auto & imp : unit.SyntaxNode->GetMembersOfType()) { ShaderParameter param; param.TypeName = imp->ShaderName.Content; param.Name = imp->ObjectName.Content; param.BindingId = -1; String binding; if (imp->FindSimpleAttribute("Binding", binding)) { param.BindingId = StringToInt(binding); usedIds.Add(param.BindingId); } rs->Parameters.Add(param); } int idAlloc = 0; for (auto & param : rs->Parameters) { if (param.BindingId == -1) { while (usedIds.Contains(idAlloc)) idAlloc++; param.BindingId = idAlloc; idAlloc++; } } state->shaders[shader->Name.Content] = rs; } } if (sink) { sink->diagnostics.AddRange(result.sink.diagnostics); sink->errorCount += result.GetErrorCount(); } } int LoadModuleSource(CompilerState * state, CoreLib::String src, CoreLib::String fileName, SpireDiagnosticSink* sink) { List units; int errCount = LoadModuleUnits(state, units, src, fileName, sink); state->moduleUnits.AddRange(units); UpdateModuleLibrary(state, units, sink); return errCount; } int LoadModuleUnits(CompilerState * state, List & units, CoreLib::String src, CoreLib::String fileName, SpireDiagnosticSink* sink) { auto & processedUnits = state->processedModuleUnits; Spire::Compiler::CompileResult result; List unitsToInclude; unitsToInclude.Add(fileName); processedUnits.Add(fileName); auto searchDirs = Options.SearchDirectories; searchDirs.Add(Path::GetDirectoryName(fileName)); searchDirs.Reverse(); for (int i = 0; i < unitsToInclude.Count(); i++) { auto inputFileName = unitsToInclude[i]; try { String source = src; if (i > 0) source = File::ReadAllText(inputFileName); auto unit = compiler->Parse(result, source, inputFileName, &includeHandler, Options.PreprocessorDefinitions); units.Add(unit); if (unit.SyntaxNode) { for (auto inc : unit.SyntaxNode->GetUsings()) { bool found = false; for (auto & dir : searchDirs) { String includeFile = Path::Combine(dir, inc->fileName.Content); if (File::Exists(includeFile)) { if (processedUnits.Add(includeFile)) { unitsToInclude.Add(includeFile); } found = true; break; } } if (!found) { result.GetErrorWriter()->diagnose(inc->fileName.Position, Diagnostics::cannotFindFile, inc->fileName); } } } } catch (IOException) { result.GetErrorWriter()->diagnose(CodePosition(0, 0, 0, ""), Diagnostics::cannotOpenFile, inputFileName); } } if (sink) { sink->diagnostics.AddRange(result.sink.diagnostics); sink->errorCount += result.GetErrorCount(); } return result.GetErrorCount(); } Shader * FindShader(const char * name) { RefPtr rs; if (states.Last()->shaders.TryGetValue(name, rs)) return rs.Ptr(); return nullptr; } Shader * GetShader(int index) { int i = 0; for (auto & shader : states.Last()->shaders) { if (i == index) return shader.Value.Ptr(); i++; } return nullptr; } int GetShaderCount() { if (states.Count()) return states.Last()->shaders.Count(); return 0; } Shader * NewShaderFromSource(CompilerState * state, const char * source, const char * fileName, SpireDiagnosticSink * sink) { int shaderCount = GetShaderCount(); LoadModuleSource(state, source, fileName, sink); int newShaderCount = GetShaderCount(); if (newShaderCount > shaderCount) return GetShader(shaderCount); return nullptr; } Shader * NewShaderFromFile(CompilerState * state, const char * fileName, SpireDiagnosticSink * sink) { try { return NewShaderFromSource(state, File::ReadAllText(fileName).Buffer(), fileName, sink); } catch (Exception) { return nullptr; } } void PushContext() { states.Add(new CompilerState(states.Last())); } void PopContext() { states.Last() = nullptr; states.SetSize(states.Count() - 1); } bool Compile(::CompileResult & result, RefPtr currentState, const Shader & shader, ArrayView modulesArgs, const char * additionalSource, SpireDiagnosticSink* sink) { Options.SymbolToCompile = shader.GetName(); Options.TemplateShaderArguments.Clear(); for (auto module : modulesArgs) Options.TemplateShaderArguments.Add(module->Name); return Compile(result, currentState, shader.Syntax, additionalSource, shader.GetName(), sink); } SpireParameterSet GetParameterSet(ILModuleParameterSet * module) { SpireParameterSet set; set.paramSet = module; set.uniformBufferLegacyBindingPoint = module->UniformBufferLegacyBindingPoint; for (auto & item : module->Parameters) { auto resType = item.Value->Type->GetBindableResourceType(); if (resType != BindableResourceType::NonBindable) { SpireResourceBindingInfo info; info.Type = (SpireBindableResourceType)resType; info.NumLegacyBindingPoints = item.Value->BindingPoints.Count(); info.LegacyBindingPoints = item.Value->BindingPoints.Buffer(); info.Name = item.Value->Name.Buffer(); set.bindings.Add(info); } else { SpireUniformFieldImpl ufield; ufield.name = item.Key.Buffer(); ufield.offset = item.Value->BufferOffset; ufield.size = item.Value->Size; StringBuilder sb; item.Value->Type->Serialize(sb); ufield.type = sb.ProduceString(); set.uniforms.Add(ufield); } } for (auto & submodule : module->SubModules) set.subsets.Add(GetParameterSet(submodule.Ptr())); return set; } bool Compile(::CompileResult & result, RefPtr currentState, RefPtr entryPoint, CoreLib::String source, CoreLib::String fileName, SpireDiagnosticSink* sink) { if (currentState->errorCount != 0) return false; currentState->Update(); List units; currentState->errorCount += LoadModuleUnits(currentState.Ptr(), units, source, fileName, sink); if (currentState->errorCount != 0) { return false; } if (entryPoint) { CompileUnit newUnit; newUnit.SyntaxNode = new ProgramSyntaxNode(); newUnit.SyntaxNode->Members.Add(entryPoint); units.Add(newUnit); } Spire::Compiler::CompileResult cresult; compiler->Compile(cresult, *(currentState->context), units, Options); result.Sources = cresult.CompiledSource; currentState->errorCount += cresult.GetErrorCount(); if (sink) { sink->diagnostics.AddRange(cresult.sink.diagnostics); sink->errorCount += cresult.GetErrorCount(); } if (currentState->errorCount == 0) { for (auto shader : result.Sources) { List paramSets; for (auto & pset : shader.Value.MetaData.ParameterSets) { if (!pset.Value->IsTopLevel) continue; paramSets.Add(GetParameterSet(pset.Value.Ptr())); } result.ParamSets[shader.Key] = _Move(paramSets); } } bool succ = currentState->errorCount == 0; return succ; } }; // implementation of C interface #define CTX(x) reinterpret_cast<::CompilationContext *>(x) #define SHADER(x) reinterpret_cast<::Shader*>(x) #define RS(x) reinterpret_cast<::CompileResult*>(x) SpireCompilationContext * spCreateCompilationContext(const char * cacheDir) { return reinterpret_cast(new ::CompilationContext((cacheDir ? true : false), cacheDir)); } void spSetCodeGenTarget(SpireCompilationContext * ctx, int target) { CTX(ctx)->Options.Target = (CodeGenTarget)target; } void spAddSearchPath(SpireCompilationContext * ctx, const char * searchDir) { CTX(ctx)->Options.SearchDirectories.Add(searchDir); } void spAddPreprocessorDefine(SpireCompilationContext * ctx, const char * key, const char * value) { CTX(ctx)->Options.PreprocessorDefinitions[key] = value; } void spSetBackendParameter(SpireCompilationContext * ctx, const char * paramName, const char * value) { CTX(ctx)->Options.BackendArguments[paramName] = value; } void spSetShaderToCompile(SpireCompilationContext * ctx, const char * shaderName) { CTX(ctx)->Options.SymbolToCompile = shaderName; } void spDestroyCompilationContext(SpireCompilationContext * ctx) { delete CTX(ctx); } // `SpireDiagnosticSink` implementation SpireDiagnosticSink* spCreateDiagnosticSink(SpireCompilationContext * /*ctx*/) { SpireDiagnosticSink* sink = new SpireDiagnosticSink(); sink->errorCount = 0; return sink; } void spClearDiagnosticSink(SpireDiagnosticSink* sink) { if (!sink) return; sink->errorCount = 0; sink->diagnostics.Clear(); } void spDestroyDiagnosticSink(SpireDiagnosticSink* sink) { delete sink; } // void spLoadModuleLibrary(SpireCompilationContext * ctx, const char * fileName, SpireDiagnosticSink* sink) { CTX(ctx)->LoadModuleSource(CTX(ctx)->states.Last().Ptr(), File::ReadAllText(fileName), fileName, sink); } void spEnvLoadModuleLibrary(SpireCompilationEnvironment * env, const char * fileName, SpireDiagnosticSink * sink) { env->context->LoadModuleSource(env->state.Ptr(), File::ReadAllText(fileName), fileName, sink); } void spLoadModuleLibraryFromSource(SpireCompilationContext * ctx, const char * source, const char * fileName, SpireDiagnosticSink* sink) { CTX(ctx)->LoadModuleSource(CTX(ctx)->states.Last().Ptr(), source, fileName, sink); } void spEnvLoadModuleLibraryFromSource(SpireCompilationEnvironment * env, const char * source, const char * fileName, SpireDiagnosticSink * sink) { env->context->LoadModuleSource(env->state.Ptr(), source, fileName, sink); } void spPushContext(SpireCompilationContext * ctx) { CTX(ctx)->PushContext(); } void spPopContext(SpireCompilationContext * ctx) { CTX(ctx)->PopContext(); } SpireCompilationEnvironment * spGetCurrentEnvironment(SpireCompilationContext * ctx) { auto rs = new SpireCompilationEnvironment(); rs->context = CTX(ctx); rs->state = CTX(ctx)->states.Last(); return rs; } SpireCompilationEnvironment * spCreateEnvironment(SpireCompilationContext * ctx, SpireCompilationEnvironment * forkOrigin) { auto rs = new SpireCompilationEnvironment(); rs->context = CTX(ctx); if (forkOrigin) rs->state = new CompilerState(*forkOrigin->state); else rs->state = new CompilerState(); return rs; } void spReleaseEnvironment(SpireCompilationEnvironment * env) { delete env; } SpireShader* spCreateShaderFromSource(SpireCompilationContext * ctx, const char * source, SpireDiagnosticSink * sink) { return reinterpret_cast(CTX(ctx)->NewShaderFromSource(CTX(ctx)->states.Last().Ptr(), source, "", sink)); } SpireShader * spEnvCreateShaderFromSource(SpireCompilationEnvironment * env, const char * source, SpireDiagnosticSink * sink) { return reinterpret_cast(env->context->NewShaderFromSource(env->state.Ptr(), source, "", sink)); } SpireShader * spFindShader(SpireCompilationContext * ctx, const char * name) { return reinterpret_cast(CTX(ctx)->FindShader(name)); } SpireShader * spEnvFindShader(SpireCompilationEnvironment * env, const char * name) { RefPtr rs; if (env->state->shaders.TryGetValue(name, rs)) return reinterpret_cast(rs.Ptr()); return nullptr; } int spGetShaderCount(SpireCompilationContext * ctx) { return CTX(ctx)->GetShaderCount(); } int spEnvGetShaderCount(SpireCompilationEnvironment * env) { return env->state->shaders.Count(); } SpireShader * spGetShader(SpireCompilationContext * ctx, int index) { return reinterpret_cast(CTX(ctx)->GetShader(index)); } SpireShader * spEnvGetShader(SpireCompilationEnvironment * env, int index) { int i = 0; for (auto & shader : env->state->shaders) { if (i == index) return reinterpret_cast(shader.Value.Ptr()); i++; } return nullptr; } SpireShader* spCreateShaderFromFile(SpireCompilationContext * ctx, const char * fileName, SpireDiagnosticSink * sink) { return reinterpret_cast(CTX(ctx)->NewShaderFromFile(CTX(ctx)->states.Last().Ptr(), fileName, sink)); } SpireShader * spEnvCreateShaderFromFile(SpireCompilationEnvironment * env, const char * fileName, SpireDiagnosticSink * sink) { return reinterpret_cast(env->context->NewShaderFromFile(env->state.Ptr(), fileName, sink)); } unsigned int spShaderGetId(SpireShader * shader) { return SHADER(shader)->Id; } const char* spShaderGetName(SpireShader * shader) { return SHADER(shader)->GetName().Buffer(); } const char * spShaderGetParameterType(SpireShader * shader, int i) { if (shader && i >= 0 && i < SHADER(shader)->Parameters.Count()) return SHADER(shader)->Parameters[i].TypeName.Buffer(); return nullptr; } const char * spShaderGetParameterName(SpireShader * shader, int i) { if (shader && i >= 0 && i < SHADER(shader)->Parameters.Count()) return SHADER(shader)->Parameters[i].Name.Buffer(); return nullptr; } int spShaderGetParameterBinding(SpireShader * shader, int i) { if (shader && i >= 0 && i < SHADER(shader)->Parameters.Count()) return SHADER(shader)->Parameters[i].BindingId; return -1; } int spShaderGetParameterCount(SpireShader * shader) { return SHADER(shader)->Parameters.Count(); } SpireModule * spFindModule(SpireCompilationContext * ctx, const char * moduleName) { return CTX(ctx)->FindModule(moduleName); } SpireModule * spEnvFindModule(SpireCompilationEnvironment * env, const char * moduleName) { auto ptr = env->state->modules.TryGetValue(moduleName); if (ptr) return ptr->Ptr(); else return nullptr; } unsigned int spGetModuleUID(SpireModule * module) { return module->Id; } const char * spGetModuleName(SpireModule * module) { if (!module) return nullptr; auto moduleNode = module; return moduleNode->Name.Buffer(); } SpireModule * spSpecializeModule(SpireCompilationContext * ctx, SpireModule * module, int * paramValues, int numParams, SpireDiagnosticSink * sink) { return CTX(ctx)->SpecializeModule(module, paramValues, numParams, sink); } int spModuleGetParameterCount(SpireModule * module) { auto moduleNode = module; return moduleNode->Parameters.Count(); } int spModuleGetParameterBufferSize(SpireModule * module) { return module->UniformBufferSize; } int spModuleHasAttrib(SpireModule * module, const char * name) { return module->Attribs.ContainsKey(name); } int spModuleGetParameter(SpireModule * module, int index, SpireComponentInfo * result) { auto moduleNode = module; auto & param = moduleNode->Parameters[index]; result->TypeName = param.TypeName.Buffer(); result->Size = param.Size; result->Offset = param.Offset; result->Alignment = param.Alignment; result->Name = param.Name.Buffer(); result->BindableResourceType = (int)param.Type->GetBindableResourceType(); result->Specialize = param.IsSpecialize; return 1; } int spModuleGetParameterByName(SpireModule * module, const char * name, SpireComponentInfo * result) { ComponentMetaData param; if (module->ParameterMap.TryGetValue(name, param)) { result->TypeName = param.TypeName.Buffer(); result->Size = param.Size; result->Offset = param.Offset; result->Alignment = param.Alignment; result->Name = param.Name.Buffer(); result->BindableResourceType = (int)param.Type->GetBindableResourceType(); result->Specialize = param.IsSpecialize; return 1; } return 0; } int spModuleGetSubModuleCount(SpireModule * module) { return module->SubModules.Count(); } SpireModule * spModuleGetSubModule(SpireModule * module, int index) { return module->SubModules[index].Ptr(); } int spModuleGetBufferOffset(SpireModule * module) { return module->UniformBufferOffset; } int spModuleGetBindingOffset(SpireModule * module, SpireBindingIndex * pIndexOut) { *pIndexOut = module->BindingIndex; return 0; } int spModuleGetRequiredComponents(SpireModule * module, SpireComponentInfo * buffer, int bufferSize) { auto moduleNode = module; auto & components = moduleNode->Requirements; if (!buffer) return components.Count(); if (bufferSize < components.Count()) return SPIRE_ERROR_INSUFFICIENT_BUFFER; int ptr = 0; for (auto & comp : components) { buffer[ptr].Name = comp.Name.Buffer(); buffer[ptr].TypeName = comp.TypeName.Buffer(); buffer[ptr].Alignment = (int)GetTypeAlignment(comp.Type.Ptr()); buffer[ptr].Size = (int)GetTypeSize(comp.Type.Ptr()); buffer[ptr].Offset = comp.Offset; ptr++; } return ptr; } SpireCompilationResult * spCompileShader(SpireCompilationContext * ctx, SpireShader * shader, SpireModule** args, int argCount, const char * additionalSource, SpireDiagnosticSink* sink) { ::CompileResult * rs = new ::CompileResult(); CTX(ctx)->PushContext(); CTX(ctx)->Compile(*rs, CTX(ctx)->states.Last(), *SHADER(shader), ArrayView(args, argCount), additionalSource, sink); CTX(ctx)->PopContext(); return reinterpret_cast(rs); } SPIRE_API SpireCompilationResult * spEnvCompileShader(SpireCompilationEnvironment * env, SpireShader * shader, SpireModule ** args, int argCount, const char * additionalSource, SpireDiagnosticSink * sink) { ::CompileResult * rs = new ::CompileResult(); env->context->Compile(*rs, env->state, *SHADER(shader), ArrayView(args, argCount), additionalSource, sink); return reinterpret_cast(rs); } SpireCompilationResult * spCompileShaderFromSource(SpireCompilationContext * ctx, const char * source, const char * fileName, SpireDiagnosticSink* sink) { ::CompileResult * rs = new ::CompileResult(); CTX(ctx)->PushContext(); CTX(ctx)->Compile(*rs, CTX(ctx)->states.Last(), nullptr, source, fileName, sink); CTX(ctx)->PopContext(); return reinterpret_cast(rs); } int spDiagnosticSinkHasAnyErrors(SpireDiagnosticSink* sink) { if (!sink) return false; return sink->errorCount != 0; } int spGetDiagnosticCount(SpireDiagnosticSink* sink) { return sink->diagnostics.Count(); } int spGetDiagnosticByIndex(SpireDiagnosticSink* sink, int index, SpireDiagnostic * outDiagnostic) { if (!sink) return SPIRE_ERROR_INVALID_PARAMETER; if (!outDiagnostic) return SPIRE_ERROR_INVALID_PARAMETER; if (index < 0) return SPIRE_ERROR_INVALID_PARAMETER; auto & diagnostics = sink->diagnostics; if (index >= diagnostics.Count()) return SPIRE_ERROR_INVALID_PARAMETER; auto & msg = diagnostics[index]; outDiagnostic->Message = msg.Message.Buffer(); outDiagnostic->ErrorId = msg.ErrorID; outDiagnostic->FileName = msg.Position.FileName.Buffer(); outDiagnostic->Line = msg.Position.Line; outDiagnostic->Col = msg.Position.Col; // Note: we rely here on the `SpireSeverity` and `Spire::Compiler::Severity` // enums having the same members. Realistically, we should probably just // use the external enum internally too. outDiagnostic->severity = (SpireSeverity)msg.severity; return 1; } int ReturnStr(const char * content, char * buffer, int bufferSize) { int len = (int)strlen(content); if (buffer) { if (bufferSize >= len + 1) { memcpy(buffer, content, len + 1); return len + 1; } else return SPIRE_ERROR_INSUFFICIENT_BUFFER; } else return len + 1; } int spGetDiagnosticOutput(SpireDiagnosticSink* sink, char * buffer, int bufferSize) { StringBuilder sb; for (auto & x : sink->diagnostics) { sb << x.Position.ToString() << ": " << Spire::Compiler::getSeverityName(x.severity); if (x.ErrorID >= 0) { sb << " " << x.ErrorID; } sb << ": " << x.Message << "\n"; } auto str = sb.ProduceString(); return ReturnStr(str.Buffer(), buffer, bufferSize); } int spGetCompiledShaderNames(SpireCompilationResult * result, char * buffer, int bufferSize) { StringBuilder sb; auto rs = RS(result); bool first = true; for (auto x : rs->Sources) { if (!first) sb << "\n"; sb << x.Key; first = false; } auto str = sb.ProduceString(); return ReturnStr(str.Buffer(), buffer, bufferSize); } int spGetCompiledShaderStageNames(SpireCompilationResult * result, const char * shaderName, char * buffer, int bufferSize) { auto rs = RS(result); if (auto src = rs->Sources.TryGetValue(shaderName)) { StringBuilder sb; bool first = true; for (auto x : src->Stages) { if (!first) sb << "\n"; sb << x.Key; first = false; } auto str = sb.ProduceString(); return ReturnStr(str.Buffer(), buffer, bufferSize); } else { return SPIRE_ERROR_INVALID_PARAMETER; } } const char * spGetShaderStageSource(SpireCompilationResult * result, const char * shaderName, const char * stage, int * length) { auto rs = RS(result); CompiledShaderSource * src = nullptr; if (shaderName == nullptr) { if (rs->Sources.Count()) src = &rs->Sources.First().Value; } else { src = rs->Sources.TryGetValue(shaderName); } if (src) { if (auto state = src->Stages.TryGetValue(stage)) { if (state->MainCode.Length()) { if (length) *length = state->MainCode.Length() + 1; return state->MainCode.Buffer(); } else { if (length) *length = state->BinaryCode.Count(); return (const char*)state->BinaryCode.Buffer(); } } } return nullptr; } int spGetShaderParameterSetCount(SpireCompilationResult * result, const char * shaderName) { auto rs = RS(result); List * list = nullptr; if (shaderName == nullptr) { if (rs->ParamSets.Count()) list = &rs->ParamSets.First().Value; } else { list = rs->ParamSets.TryGetValue(shaderName); } if (list) { return list->Count(); } return 0; } SpireParameterSet * spGetShaderParameterSet(SpireCompilationResult * result, const char * shaderName, int index) { auto rs = RS(result); List * sets = nullptr; if (shaderName == nullptr) { if (rs->ParamSets.Count()) sets = &rs->ParamSets.First().Value; } else { sets = rs->ParamSets.TryGetValue(shaderName); } if (sets) { return &(*sets)[index]; } return nullptr; } int spParameterSetGetBufferSize(SpireParameterSet * set) { return set->paramSet->BufferSize; } int spParameterSetGetBufferOffset(SpireParameterSet * set) { return set->paramSet->UniformBufferOffset; } int spParameterSetGetStartBindingIndex(SpireParameterSet * set, SpireBindingIndex * pIndexOut) { pIndexOut->texture = set->paramSet->TextureBindingStartIndex; pIndexOut->sampler = set->paramSet->SamplerBindingStartIndex; pIndexOut->storageBuffer = set->paramSet->StorageBufferBindingStartIndex; pIndexOut->uniformBuffer = set->paramSet->UniformBindingStartIndex; return 0; } int spParameterSetGetUniformField(SpireParameterSet * set, int index, SpireUniformField * pUniformLayout) { pUniformLayout->name = set->uniforms[index].name.Buffer(); pUniformLayout->type = set->uniforms[index].type.Buffer(); pUniformLayout->offset = set->uniforms[index].offset; pUniformLayout->size = set->uniforms[index].size; return 0; } int spParameterSetGetUniformFieldCount(SpireParameterSet * set) { return set->uniforms.Count(); } int spParameterSetGetSubSetCount(SpireParameterSet * set) { return set->subsets.Count(); } SpireParameterSet * spParameterSetGetSubSet(SpireParameterSet * set, int index) { return &set->subsets[index]; } const char * spParameterSetGetBindingName(SpireParameterSet * set) { return set->paramSet->BindingName.Buffer(); } int spParameterSetGetBindingIndex(SpireParameterSet * set) { return set->paramSet->DescriptorSetId; } int spParameterSetGetUniformBufferLegacyBindingPoint(SpireParameterSet * set) { return set->paramSet->UniformBufferLegacyBindingPoint; } int spParameterSetGetBindingSlotCount(SpireParameterSet * set) { return set->bindings.Count(); } SpireResourceBindingInfo * spParameterSetGetBindingSlot(SpireParameterSet * set, int index) { return &set->bindings[index]; } void spDestroyCompilationResult(SpireCompilationResult * result) { delete RS(result); } ================================================ FILE: Source/SpireLib/SpireLib.h ================================================ #ifndef LIB_BAKER_SL_H #define LIB_BAKER_SL_H #include "../CoreLib/Basic.h" #include "../CoreLib/Tokenizer.h" #include "../SpireCore/ShaderCompiler.h" namespace SpireLib { class ShaderLibFile : public CoreLib::Basic::Object { public: CoreLib::Basic::EnumerableDictionary Sources; // indexed by world Spire::Compiler::ShaderMetaData MetaData; void AddSource(CoreLib::Basic::String source, CoreLib::Text::TokenReader & parser); void FromString(const CoreLib::String & str); CoreLib::String ToString(); void SaveToFile(CoreLib::Basic::String fileName); ShaderLibFile() = default; void Clear(); void Load(CoreLib::Basic::String fileName); }; CoreLib::Basic::List CompileShaderSourceFromFile(Spire::Compiler::CompileResult & result, const CoreLib::Basic::String & sourceFileName, Spire::Compiler::CompileOptions &options); CoreLib::Basic::List CompileShaderSource(Spire::Compiler::CompileResult & result, const CoreLib::Basic::String &source, const CoreLib::Basic::String & sourceFileName, Spire::Compiler::CompileOptions &options); class ShaderLib : public ShaderLibFile { public: Spire::Compiler::StageSource GetStageSource(CoreLib::Basic::String world); ShaderLib() = default; ShaderLib(CoreLib::Basic::String fileName); void Reload(CoreLib::Basic::String fileName); bool CompileFrom(CoreLib::Basic::String symbolName, CoreLib::Basic::String sourceFileName, CoreLib::Basic::String schedule); }; } #endif ================================================ FILE: Source/SpireLib/SpireLib.vcxproj ================================================ DebugClang Win32 DebugClang x64 Debug_VS2013 Win32 Debug_VS2013 x64 Debug Win32 Release_VS2013 Win32 Release_VS2013 x64 Release Win32 Debug x64 Release x64 {1168C449-66A5-4D23-80E2-2C1A07E58F83} Win32Proj SpireLib 8.1 SpireLib StaticLibrary true v140 Unicode StaticLibrary true v120 Unicode StaticLibrary true v140_clang_3_7 Unicode StaticLibrary false v140 true Unicode StaticLibrary false v120 true Unicode StaticLibrary true v140 Unicode StaticLibrary true v120 Unicode StaticLibrary true v140_Clang_3_7 Unicode StaticLibrary false v140 true Unicode StaticLibrary false v120 true Unicode Level4 Disabled WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) true ../; MultiThreadedDebug Windows Level4 Disabled WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) true ../; MultiThreadedDebug Windows EnableAllWarnings Disabled WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) true ../; MultiThreadedDebug true Windows Level4 Disabled _DEBUG;_LIB;%(PreprocessorDefinitions) true MultiThreadedDebug ../; true Windows Level4 Disabled _DEBUG;_LIB;%(PreprocessorDefinitions) true MultiThreadedDebug ../; Windows Level4 Disabled _DEBUG;_LIB;%(PreprocessorDefinitions) true MultiThreadedDebug ../; Windows Level4 MaxSpeed true true WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true MultiThreaded ../; Windows true true Level4 MaxSpeed true true WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) true MultiThreaded ../; Windows true true Level4 MaxSpeed true true NDEBUG;_LIB;%(PreprocessorDefinitions) true MultiThreaded ../; true Windows true true Level4 MaxSpeed true true NDEBUG;_LIB;%(PreprocessorDefinitions) true MultiThreaded ../; Windows true true {f9be7957-8399-899e-0c49-e714fddd4b65} {db00da62-0533-4afd-b59f-a67d5b3a0808} ================================================ FILE: Source/SpireLib/SpireLib.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Header Files Source Files ================================================ FILE: Spire.h ================================================ #ifndef SPIRE_H #define SPIRE_H #ifdef _MSC_VER #ifdef SPIRE_COMPILING_DLL #define SPIRE_API __declspec(dllexport) #else #ifdef SPIRE_DYNAMIC #define SPIRE_API __declspec(dllimport) #else #define SPIRE_API #endif #endif #else #define SPIRE_API #endif #ifdef __cplusplus extern "C" { #endif /*! @mainpage Introduction 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. Paper: http://graphics.cs.cmu.edu/projects/spire/ API Reference: Spire.h @file Spire.h */ /*! @brief Severity of a diagnostic generated by the compiler. Values come from the enum below, with higher values representing more severe conditions, and all values >= SPIRE_SEVERITY_ERROR indicating compilation failure. */ typedef int SpireSeverity; enum { SPIRE_SEVERITY_NOTE = 0, /**< An informative message. */ SPIRE_SEVERITY_WARNING, /**< A warning, which indicates a possible proble. */ SPIRE_SEVERITY_ERROR, /**< An error, indicating that compilation failed. */ SPIRE_SEVERITY_FATAL, /**< An unrecoverable error, which forced compilation to abort. */ SPIRE_SEVERITY_INTERNAL, /**< An internal error, indicating a logic error in the compiler. */ }; typedef int SpireBindableResourceType; enum { SPIRE_NON_BINDABLE = 0, SPIRE_TEXTURE, SPIRE_SAMPLER, SPIRE_UNIFORM_BUFFER, SPIRE_STORAGE_BUFFER, }; enum { SPIRE_GLSL = 0, SPIRE_GLSL_VULKAN, SPIRE_GLSL_VULKAN_ONE_DESC, SPIRE_HLSL, SPIRE_SPIRV }; //#define SPIRE_LAYOUT_UNIFORM 0 //#define SPIRE_LAYOUT_PACKED 1 //#define SPIRE_LAYOUT_STORAGE 2 #define SPIRE_ERROR_INSUFFICIENT_BUFFER -1 #define SPIRE_ERROR_INVALID_PARAMETER -2 /*! @brief Represents a compilation context. Created by spCreateCompilationContext(). Related Functions - spCreateCompilationContext() - spDestroyCompilationContext() - spCreateShaderFromSource() - spCreateShaderFromFile() - spCompileShader() - spSetCodeGenTarget() - spAddSearchPath() - spSetBackendParameter() */ struct SpireCompilationContext {}; struct SpireCompilationEnvironment; /*! @brief Represents a shader. A SpireShader can be created by calling spCreateShaderFromSource(), or by loading a module library via spLoadModuleLibrary() Related Functions - spShaderGetName() - spCompileShader() - spShaderGetParameterType() - spShaderGetParameterName() - spShaderGetParameterBinding() */ struct SpireShader {}; /*! @brief SpireModule objects provide reflection data about a module. Module objects can be obtained by calling spFindModule() once a module library is loaded via spLoadModuleLibrary(). Related Functions - spLoadModuleLibrary() - spLoadModuleLibraryFromSource() - spFindModule() - spSpecializeModule() - spModuleGetParameterCount() - spModuleGetParameter() - spModuleGetParameterBufferSize() - spModuleGetRequiredComponents() */ struct SpireModule; /*! @brief Represents the compilation result, including error messages and compiled source code for each stage. Related Functions - spCompileShader() - spCompileShaderFromSource() - spIsCompilationSucessful() - spGetCompilerOutput() - spGetDiagnosticCount() - spGetDiagnosticByIndex() - spGetCompiledShaderNames() - spGetCompiledShaderStageNames() - spGetShaderStageSource() - spDestroyCompilationResult() */ struct SpireCompilationResult {}; struct SpireBindingIndex { int texture = 0; int sampler = 0; int storageBuffer = 0; int uniformBuffer = 0; int general = 0; }; struct SpireUniformField { int offset; int size; const char * name; const char * type; }; /*! @brief A collection of diagnostic messages output by the compiler. */ typedef struct SpireDiagnosticSink SpireDiagnosticSink; /*! @brief Represents a diagnostic message from the compiler. */ struct SpireDiagnostic { const char * Message; /**< Content of the message. Storage is owned by SpireCompilationContext.*/ SpireSeverity severity; /**< Severity of the diagnostic.*/ int ErrorId; /**< A unique identifier for this type of error.*/ const char * FileName; /**< The source file name of this error. Storage is owned by SpireCompilationContext*/ int Line; /**< The line number of this error.*/ int Col; /**< The column position of this error.*/ }; /*! @brief Stores description of a component. */ struct SpireComponentInfo { const char * Name; /**< The name of the component. Storage is owned by SpireCompilationContext.*/ const char * TypeName; /**< The type name of the component. Storage is owned by SpireCompilationContext.*/ SpireBindableResourceType BindableResourceType; /**< The type of bindable resource this component represents.*/ int Size; /**< The size (in bytes) of the component. For opaque types (e.g. sampler and texture), this value is 0.*/ int Alignment; /**< The alignment (in bytes) of the component. For opaque types (e.g. sampler and texture), this value is 0.*/ int Offset; /**< The offset (in bytes) of the component. For opaque types (e.g. sampler and texture), this value is 0.*/ bool Specialize; /**< Indicating whether this is a specialization parameter.*/ }; /*! @brief Represents a parameter set. */ typedef struct SpireParameterSet SpireParameterSet; /*! @brief Represents information on a binding slot of a parameter set. */ struct SpireResourceBindingInfo { SpireBindableResourceType Type; /**< The type of this binding slot. */ int NumLegacyBindingPoints; /**< The number of legacy binding points. */ int * LegacyBindingPoints; /**< The legacy binding points. Storage is owned by SpireCompilationResult.*/ const char * Name; /**< The shader code name of this resource. Storage is owned by SpireCompilationResult.*/ }; /*! @brief Create a compilation context. @param cacheDir The directory used to store cached compilation results. Pass NULL to disable caching. @return A new compilation context. */ SPIRE_API SpireCompilationContext * spCreateCompilationContext(const char * cacheDir); /*! @brief Sets the target for code generation. @param ctx The compilation context. @param target The code generation target. Possible values are: - SPIRE_GLSL. Generates GLSL code. - SPIRE_HLSL. Generates HLSL code. - SPIRE_SPIRV. Generates SPIR-V code. */ SPIRE_API void spSetCodeGenTarget(SpireCompilationContext * ctx, int target); /*! @brief Add a path in which source files are being search. When the programmer specifies @code using @endcode in code, the compiler searches the file in all search pathes in order. @param ctx The compilation context. @param searchDir The additional search directory. */ SPIRE_API void spAddSearchPath(SpireCompilationContext * ctx, const char * searchDir); /*! @brief Add a macro definition to be used during preprocessing. @param key The name of the macro to define. @param value The value of the macro to define. */ SPIRE_API void spAddPreprocessorDefine(SpireCompilationContext * ctx, const char * key, const char * value); /*! @brief Sets a parameter used by the compiler back-end. @param ctx The compilation context. @param paramName The name of the parameter. @param value The value of the parameter. */ SPIRE_API void spSetBackendParameter(SpireCompilationContext * ctx, const char * paramName, const char * value); /*! @brief Sets a shader to compile. By default, the compiler will generate code for all shaders in current context. After setting this option, the compiler will only generate code for the specified shader. @param ctx The compilation context. @param shaderName The name of the shader to compile. */ SPIRE_API void spSetShaderToCompile(SpireCompilationContext * ctx, const char * shaderName); /*! @brief Destorys the compilation context. Destorying a compilation context will free the memory for all strings owned by the SpireComilationContext and all SpireModule objects. These objects will not be available after a call to spDestroyCompilationContext. However, all SpireCompilationResult objects will continue to be available until they are destroyed. @param ctx The compilation context to destroy. */ SPIRE_API void spDestroyCompilationContext(SpireCompilationContext * ctx); /*! @brief Create a sink for diagnostic messages. This sink can be used to capture diagnostic output from compilation operations, and can then be used to iterate over the diagnostics produced. @param ctx The compilation context. */ SPIRE_API SpireDiagnosticSink* spCreateDiagnosticSink(SpireCompilationContext * ctx); /*! @brief Reset a diagnostic sink to its original state. This clears out any diagnostic messages that have been written to the sink. Re-using a single sink across multiple operations may be more efficeint than creating and destroying a sink every time. @param sink The diagnostic sink to reset. */ SPIRE_API void spClearDiagnosticSink(SpireDiagnosticSink* sink); /*! @brief Destroy a diagnostic sink. @param sink The diagnostic sink to destroy. */ SPIRE_API void spDestroyDiagnosticSink(SpireDiagnosticSink* sink); /*! @brief Load and precompile spire modules from spire source file. Compilation status and error messages can be obtained via spIsCompilationSucessful(), spGetDiagnosticCount() and spGetDiagnosticByIndex() functions. @param ctx The compilation context. @param fileName The filename of the spire source code. @param sink The sink where diagnostic output should be sent, or NULL to ignore messages. */ SPIRE_API void spLoadModuleLibrary(SpireCompilationContext * ctx, const char * fileName, SpireDiagnosticSink* sink); SPIRE_API void spEnvLoadModuleLibrary(SpireCompilationEnvironment * env, const char * fileName, SpireDiagnosticSink* sink); /*! @brief Load and precompile spire modules from spire source code in memory. Compilation status and error messages can be obtained via spIsCompilationSucessful(), spGetDiagnosticCount() and spGetDiagnosticByIndex() functions. @param ctx The compilation context. @param source The spire source code to precompile. All strings should be in UTF-8 encoding. @param fileName The filename used to report error messages regarding to code in @p source. @param sink The sink where diagnostic output should be sent, or NULL to ignore messages. */ SPIRE_API void spLoadModuleLibraryFromSource(SpireCompilationContext * ctx, const char * source, const char * fileName, SpireDiagnosticSink* sink); SPIRE_API void spEnvLoadModuleLibraryFromSource(SpireCompilationEnvironment * env, const char * source, const char * fileName, SpireDiagnosticSink* sink); /*! @brief Store current compilation context to a stack. spLoadModuleLibrary() and spLoadModuleLibraryFromSource() load new symbols to a compilation context. spPushContext() and spPopContext() can be used to save and restore context state. @param ctx The compilation context whose state to store. */ void spPushContext(SpireCompilationContext * ctx); /*! @brief Restore current compilation context to a previously saved state. spLoadModuleLibrary() and spLoadModuleLibraryFromSource() load new symbols to a compilation context. spPushContext() and spPopContext() can be used to save and restore context state. @param ctx The compilation context whose state to restore. */ void spPopContext(SpireCompilationContext * ctx); SPIRE_API SpireCompilationEnvironment * spGetCurrentEnvironment(SpireCompilationContext * ctx); SPIRE_API SpireCompilationEnvironment * spCreateEnvironment(SpireCompilationContext * ctx, SpireCompilationEnvironment * forkOrigin); SPIRE_API void spReleaseEnvironment(SpireCompilationEnvironment* env); /*! @brief Create a template shader object from Spire source code. This is equivalent to calling spLoadModuleLibrary() and spGetShader(). @param ctx The compilation context. @param name The source code of the shader. */ SPIRE_API SpireShader* spCreateShaderFromSource(SpireCompilationContext * ctx, const char * source, SpireDiagnosticSink * sink); SPIRE_API SpireShader* spEnvCreateShaderFromSource(SpireCompilationEnvironment * env, const char * source, SpireDiagnosticSink * sink); /*! @brief Create a template shader object from a Spire source file. @param ctx The compilation context. @param name The source code of the shader. */ SPIRE_API SpireShader* spCreateShaderFromFile(SpireCompilationContext * ctx, const char * fileName, SpireDiagnosticSink * sink); SPIRE_API SpireShader* spEnvCreateShaderFromFile(SpireCompilationEnvironment * env, const char * fileName, SpireDiagnosticSink * sink); /*! @brief Find a template shader from current context. @param ctx The compilation context in which to find shaders. @param name The name of the shader object to find. @return A handle to the template shader object that can be used for code generation and reflection. NULL if the shader with specified name does not exist. */ SPIRE_API SpireShader* spFindShader(SpireCompilationContext * ctx, const char * name); SPIRE_API SpireShader* spEnvFindShader(SpireCompilationEnvironment * env, const char * name); /*! @brief Returns the total number of entry point shaders in current compilation context. @param ctx The compilation context. */ SPIRE_API int spGetShaderCount(SpireCompilationContext * ctx); SPIRE_API int spEnvGetShaderCount(SpireCompilationEnvironment * env); /*! @brief Retrieves the handle to the specified shader object. @param ctx The compilation context in which to retrieve shaders. @param name The index of the shader object to retrieve. @return A handle to the template shader object that can be used for code generation and reflection. NULL if the shader at specified index does not exist. */ SPIRE_API SpireShader* spGetShader(SpireCompilationContext * ctx, int index); SPIRE_API SpireShader* spEnvGetShader(SpireCompilationEnvironment * ctx, int index); /*! @brief Retrieves the runtime unique Id of a shader. @param shader The shader object whose Id to retrieve. @return Id of the shader object. */ SPIRE_API unsigned int spShaderGetId(SpireShader * shader); /*! @brief Retrieves the name of a shader. @param shader The shader object whose name to retrieve. @return Name of the shader object. */ SPIRE_API const char * spShaderGetName(SpireShader * shader); SPIRE_API const char * spShaderGetParameterType(SpireShader * shader, int i); SPIRE_API const char * spShaderGetParameterName(SpireShader * shader, int i); SPIRE_API int spShaderGetParameterBinding(SpireShader * shader, int i); SPIRE_API int spShaderGetParameterCount(SpireShader * shader); /*! @brief Find a precompiled module in a SpireCompilationContext. @param ctx The compilation context. @param moduleName The name of the module to find. @return If a module with the specified name exists in the current context, a handle to the module is returned. Otherwise, the return value is NULL. @note All SpireModule objects are destroyed when its containing SpireCompilationContext is destroyed. */ SPIRE_API SpireModule * spFindModule(SpireCompilationContext * ctx, const char * moduleName); SPIRE_API SpireModule * spEnvFindModule(SpireCompilationEnvironment * env, const char * moduleName); /*! @brief Retrieve the run-time unique Id of a SpireModule. @param module The module to get the unique Id of. @return The unique Id of the module. */ SPIRE_API unsigned int spGetModuleUID(SpireModule * module); /*! @brief Retrieve the name of a SpireModule. @param module The module to get the name of. @return The name of the module as a null-terminated string, or NULL if ther are any errors. @note The memory for the return value will be freed when the containing SpireCopmilationContext is destroyed. */ SPIRE_API const char * spGetModuleName(SpireModule * module); /*! @brief Create a specialized module from an existing module. @param ctx A spire compilation context used to hold the specialized module. @param module The module to create specialization from. @param paramValues The values of specialization parameters. @param numParams Number of entries in @p paramValues array. @param sink [Optional] A SpireDiagnosticSink object used to receive error messages. @return If succesfull, this function returns the specialized module; otherwise the return value is NULL. @note The memory for the returning SpireModule will be freed when the @p ctx is destroyed, or when the current context is poped via spPopContext(). */ SPIRE_API SpireModule * spSpecializeModule(SpireCompilationContext * ctx, SpireModule * module, int * paramValues, int numParams, SpireDiagnosticSink * sink); /*! @brief Retrieves number of parameters defined by a module. @param module The module from which to retrieve parameters. @return Number of parameters defined in @p module. */ SPIRE_API int spModuleGetParameterCount(SpireModule * module); /*! @brief Retrieves buffer size required to hold all parameters defined by a module. @param module The module from which to retrieve parameter buffer size information. @return Number of bytes required to store all parameters defined by @p module. */ SPIRE_API int spModuleGetParameterBufferSize(SpireModule * module); /*! @brief Returns whether a module has defined an attribute with specified name. @param module The module from which to query attribute definition. @param componentName The name of the attribute to test existence of. @return 1 if the component is defined, 0 otherwise. */ SPIRE_API int spModuleHasAttrib(SpireModule * module, const char * attribName); /*! @brief Retrieves parameter info from a SpireModule. @param module The module from which to retrieve parameters. @param index Index of the requesting parameter. @param result A pointer to a SpireComponentInfo structure used to receive info on the specified parameter. @return If successful, this function returns 0. Otherwise, the return value is one of the following error codes: - SPIRE_ERROR_INVALID_PARAMETER if any of the parameters are invalid. */ SPIRE_API int spModuleGetParameter(SpireModule * module, int index, SpireComponentInfo * result); SPIRE_API int spModuleGetParameterByName(SpireModule * module, const char * name, SpireComponentInfo * result); SPIRE_API int spModuleGetSubModuleCount(SpireModule * module); SPIRE_API SpireModule * spModuleGetSubModule(SpireModule * module, int index); SPIRE_API int spModuleGetBufferOffset(SpireModule * module); SPIRE_API int spModuleGetBindingOffset(SpireModule * module, SpireBindingIndex * pIndexOut); /*! @brief Retrieve a list of components that are required by the specified module. @param module The module from where to retrieve components. @param buffer A user allocated buffer of SpireComponentInfo for receiving outputs. @param bufferSize The size (in number of SpireComponentInfo structs) of the specified buffer. @return If @p buffer is NULL, the return value is the required size, in number of SpireComponentInfo. Otherwise, if the function suceeds, the return value is the number of SpireComponentInfo instances written to @p buffer. The function returns a negative value if it does not suceed. Possible error codes are: - SPIRE_ERROR_INSUFFICIENT_BUFFER. The supplied buffer size was not large enough. - SPIRE_ERROR_INVALID_PARAMETER. Any of the parameter values was invalid. */ SPIRE_API int spModuleGetRequiredComponents(SpireModule * module, SpireComponentInfo * buffer, int bufferSize); /*! @brief Compiles a shader object. @param ctx A shader compilation context. @param shader The shader object to compile. @param args The modules used as template shader arguments. @param argCount The number of elements in @p args array. @param additionalSource Additional source code to append before passing to compiler. @param sink The sink where diagnostic output should be sent, or NULL to ignore messages. @return The return value is a handle to a SpireCompilationResult object that contains error messages and compiled source code. @note You are responsible for destorying a SpireCompilationResult object when it is no longer used. Destroying a SpireCompilationContext does not automatically destroy SpireCompilationResult objects. */ SPIRE_API SpireCompilationResult* spCompileShader(SpireCompilationContext * ctx, SpireShader * shader, SpireModule** args, int argCount, const char * additionalSource, SpireDiagnosticSink* sink); SPIRE_API SpireCompilationResult* spEnvCompileShader(SpireCompilationEnvironment * env, SpireShader * shader, SpireModule** args, int argCount, const char * additionalSource, SpireDiagnosticSink* sink); /*! @brief Compiles a shader object. @param ctx A shader compilation context. @param source A string that represents the Spire source code that defines a shader. @param fileName The filename to use to report error messages regarding to @p source. @param sink The sink where diagnostic output should be sent, or NULL to ignore messages. @return The return value is a handle to a SpireCompilationResult object that contains error messages and compiled source code. @note You are responsible for destorying a SpireCompilationResult object when it is no longer used. Destroying a SpireCompilationContext does not automatically destroy SpireCompilationResult objects. @see spDestroyCompilationResult() */ SPIRE_API SpireCompilationResult* spCompileShaderFromSource(SpireCompilationContext * ctx, const char * source, const char * fileName, SpireDiagnosticSink* sink); /*deprecated*/ /*! @brief Checks if any errors have been output to the diagnostic sink. @param sink The SpireDiagnosticSink to be checked. @return 1 if any errors have been output, 0 otherwise. */ SPIRE_API int spDiagnosticSinkHasAnyErrors(SpireDiagnosticSink* sink); /*! @brief Retrieve the number of compiler diagnostics in a SpireCompilationResult object. @param result A SpireCompilationResult object. @return The number of diagnostics available. */ SPIRE_API int spGetDiagnosticCount(SpireDiagnosticSink* sink); /*! @brief Retrieve the content of compiler diagnostics in a SpireCompilationResult object. @param result A SpireCompilationResult object. @param index The index of the compiler diagnostic to retrieve. @param pMsg A pointer to a SpireDiagnostic structure to receive the diagnostic. @return 1 if successful. SPIRE_ERROR_INVALID_PARAMETER if any of the parameters is invalid. */ SPIRE_API int spGetDiagnosticByIndex(SpireDiagnosticSink* sink, int index, SpireDiagnostic * pMsg); /*! @brief Get compiler output messages as a single string. @param result A SpireCompilationResult object. @param buffer The buffer used to receive compiler messages. If this parameter is NULL, the function returns the number of bytes required for the buffer. @param bufferSize The size of @p buffer (in bytes). @return If successful, the return value is the number of bytes written to @p buffer. If @p buffer is NULL, the return value is the number of bytes required for @p buffer to store the entire output message. Otherwise, the function returns one of the following error codes: - SPIRE_ERROR_INSUFFICIENT_BUFFER. if @p bufferSize is smaller than required buffer size. - SPIRE_ERROR_INVALID_PARAMETER. if any of the parameters is invalid. */ SPIRE_API int spGetDiagnosticOutput(SpireDiagnosticSink* sink, char * buffer, int bufferSize); /*! @brief Retrieve a list of shader names that has been compiled. @param result A SpireCompilationResult object. @param buffer A buffer used to receive shader names. Shader names are separated by '\\n'. If this parameter is NULL, the function returns the required buffer size. @param bufferSize The size (in bytes) of @p buffer. @return If sucessful, the return value is greater or equal to 0 representing the number of charaters required or written to buffer, including the trailing 0. Otherwise, it returns one of the following error codes: - SPIRE_ERROR_INSUFFICIENT_BUFFER. The supplied buffer size was not large enough. - SPIRE_ERROR_INVALID_PARAMETER. Any of the parameter values was invalid. */ SPIRE_API int spGetCompiledShaderNames(SpireCompilationResult * result, char * buffer, int bufferSize); /*! @brief Retrieve a list of stage names in a compiled shader. @param result A SpireCompilationResult object. @param shaderName The name of a shader. @param buffer A buffer used to receive stage names. Stage names are separated by '\\n'. If this parameter is NULL, the function returns the required buffer size. @param bufferSize The size (in bytes) of @p buffer. @return If sucessful, the return value is greater or equal to 0 representing the number of charaters required or written to buffer, including the trailing 0. Otherwise, it returns one of the following error codes: - SPIRE_ERROR_INSUFFICIENT_BUFFER. The supplied buffer size was not large enough. - SPIRE_ERROR_INVALID_PARAMETER. Any of the parameter values was invalid. */ SPIRE_API int spGetCompiledShaderStageNames(SpireCompilationResult * result, const char * shaderName, char * buffer, int bufferSize); /*! @brief Retrieve the compiled code (binary or textual, depending on the target language) of a stage in a compiled shader. @param result A SpireCompilationResult object. @param shaderName The name of a shader. If @p shaderName is NULL, the function returns the source code of the first shader in @p result. @param stage The name of a stage. @param[out] length A pointer used to receive the length of the compiled code, can be set to NULL. @return If sucessful, the return value is a pointer to the buffer storing the compiled code. Otherwise, the return value is NULL. @note The backing memory of the returned code buffer is owned by the SpireCompilationResult object. Destroying the SpireCompilationResult object will render this code buffer unusable. */ SPIRE_API const char * spGetShaderStageSource(SpireCompilationResult * result, const char * shaderName, const char * stage, int * length); /*! @brief Retrieve the number of parameter sets defined by a compiled shader. @param result A SpireCompilationResult object, as a result of shader compilation. @param shaderName The name of a shader. If @p shaderName is NULL, the function returns the source code of the first shader in @p result. @return The number of parameter sets in specified shader. */ SPIRE_API int spGetShaderParameterSetCount(SpireCompilationResult * result, const char * shaderName); /*! @brief Retrieve a SpireParameterSet object representing a parameter set defined by a compiled shader. @param result A SpireCompilationResult object, as a result of shader compilation. @param shaderName The name of a shader. If @p shaderName is NULL, the function returns the source code of the first shader in @p result. @param index The index of the parameter set to return @return A SpireParameterSet object representing the requested parameter set. The life-time of the returned object is owned by @p result. */ SPIRE_API SpireParameterSet * spGetShaderParameterSet(SpireCompilationResult * result, const char * shaderName, int index); /*! @brief Get the required uniform buffer size of a SpireParameterSet object. @param set A SpireParameterSet object whose uniform buffer size to get. @return Required uniform buffer size. */ SPIRE_API int spParameterSetGetBufferSize(SpireParameterSet * set); /*! @brief Get the uniform buffer offset of a SpireParameterSet object. @param set A SpireParameterSet object whose uniform buffer offset to get. @return uniform buffer offset. */ SPIRE_API int spParameterSetGetBufferOffset(SpireParameterSet * set); /*! @brief Get the uniform buffer offset of a SpireParameterSet object. @param set A SpireParameterSet object whose uniform buffer offset to get. @return uniform buffer offset. */ SPIRE_API int spParameterSetGetStartBindingIndex(SpireParameterSet * set, SpireBindingIndex * pIndexOut); SPIRE_API int spParameterSetGetUniformField(SpireParameterSet * set, int index, SpireUniformField * pUniformLayout); SPIRE_API int spParameterSetGetUniformFieldCount(SpireParameterSet * set); SPIRE_API int spParameterSetGetSubSetCount(SpireParameterSet * set); SPIRE_API SpireParameterSet* spParameterSetGetSubSet(SpireParameterSet * set, int index); /*! @brief Get the binding name of a SpireParameterSet object. @param set A SpireParameterSet object whose binding name to get. @return The binding name. The life-time of the returned string is owned by @p set. */ SPIRE_API const char * spParameterSetGetBindingName(SpireParameterSet * set); /*! @brief Get the binding index of a SpireParameterSet object. @param set A SpireParameterSet object whose binding index to get. @return The binding index. */ SPIRE_API int spParameterSetGetBindingIndex(SpireParameterSet * set); /*! @brief Get the legacy binding index for the uniform buffer of a SpireParameterSet object. @param set A SpireParameterSet object whose uniform buffer legacy binding index to get. @return The legacy binding index. */ SPIRE_API int spParameterSetGetUniformBufferLegacyBindingPoint(SpireParameterSet * set); /*! @brief Get the number of binding slots of a SpireParameterSet object. @param set A SpireParameterSet object whose number of binding slots to get. @return The number of binding slots. */ SPIRE_API int spParameterSetGetBindingSlotCount(SpireParameterSet * set); /*! @brief Get information on a binding slot in a SpireParameterSet object. @param set A SpireParameterSet object whose binding slot information to get. @return The pointer to a SpireResourceBindingInfo structure that holds information about the requested binding slot. The life-time of the returned structure is owned by @p set and should be freed by the user. */ SPIRE_API SpireResourceBindingInfo * spParameterSetGetBindingSlot(SpireParameterSet * set, int index); /*! @brief Destroys the SpireCompilationResult object. @param result A SpireCompilationResult object to destroy. @note Destroying a SpireCompilationContext object does not automatically destroy SpireCompilationResult objects. You are required to destroy a SpireCompilationResult object once it is no longer in use. */ SPIRE_API void spDestroyCompilationResult(SpireCompilationResult * result); #ifdef __cplusplus } #endif #endif ================================================ FILE: SpireAllSource.h ================================================ #ifndef SPIRE_NO_CORE_LIB #include "Source/CoreLib/CommandLineParser.cpp" #include "Source/CoreLib/LibIO.cpp" #include "Source/CoreLib/LibMath.cpp" #include "Source/CoreLib/LibString.cpp" #include "Source/CoreLib/Stream.cpp" #include "Source/CoreLib/TextIO.cpp" #include "Source/CoreLib/Tokenizer.cpp" #include "Source/CoreLib/VectorMath.cpp" #endif #include "Source/SpireCore/CLikeCodeGen.cpp" #include "Source/SpireCore/Closure.cpp" #include "Source/SpireCore/CodeGenerator.cpp" #include "Source/SpireCore/CompiledProgram.cpp" #include "Source/SpireCore/ConstantPool.cpp" #include "Source/SpireCore/Diagnostics.cpp" #include "Source/SpireCore/GetDependencyVisitor.cpp" #include "Source/SpireCore/GLSLCodeGen.cpp" #include "Source/SpireCore/HLSLCodeGen.cpp" #include "Source/SpireCore/IL.cpp" #include "Source/SpireCore/InsertImplicitImportOperator.cpp" #include "Source/SpireCore/KeyHoleMatching.cpp" #include "Source/SpireCore/Lexer.cpp" #include "Source/SpireCore/Naming.cpp" #include "Source/SpireCore/NewSpirVCodeGen.cpp" #include "Source/SpireCore/Parser.cpp" #include "Source/SpireCore/Preprocessor.cpp" #include "Source/SpireCore/Schedule.cpp" #include "Source/SpireCore/SemanticsVisitor.cpp" #include "Source/SpireCore/ShaderCompiler.cpp" #include "Source/SpireCore/SpirVCodeGen.cpp" #include "Source/SpireCore/StdInclude.cpp" #include "Source/SpireCore/SymbolTable.cpp" #include "Source/SpireCore/Syntax.cpp" #include "Source/SpireCore/TypeLayout.cpp" #include "Source/SpireCore/SamplerUsageAnalysis.cpp" #include "Source/SpireCore/VariantIR.cpp" #include "Source/SpireLib/SpireLib.cpp" ================================================ FILE: Tests/Diagnostics/break-outside-loop.spire ================================================ // `break` where it isn't allowed void foo() { break; } ================================================ FILE: Tests/Diagnostics/break-outside-loop.spire.expected ================================================ result code = -1 standard error = { Tests/Diagnostics/break-outside-loop.spire(3): error 30003: 'break' must appear inside loop constructs. } standard output = { } ================================================ FILE: Tests/Diagnostics/call-argument-type.spire ================================================ // call function with wrong argument type struct A {}; struct B {}; void f(A a) {} void g(B b) { f(b); } ================================================ FILE: Tests/Diagnostics/call-argument-type.spire.expected ================================================ result code = -1 standard error = { Tests/Diagnostics/call-argument-type.spire(9): error 30021: f: no overload takes arguments () } standard output = { } ================================================ FILE: Tests/Diagnostics/continue-outside-loop.spire ================================================ // `continue` where it isn't allowed void foo() { continue; } ================================================ FILE: Tests/Diagnostics/continue-outside-loop.spire.expected ================================================ result code = -1 standard error = { Tests/Diagnostics/continue-outside-loop.spire(3): error 30004: 'continue' must appear inside loop constructs. } standard output = { } ================================================ FILE: Tests/Diagnostics/expected-token-eof.spire ================================================ // expected one token, but got EOF int foo() { int a = 3 ================================================ FILE: Tests/Diagnostics/expected-token-eof.spire.expected ================================================ result code = -1 standard error = { Tests/Diagnostics/expected-token-eof.spire(0): error 20001: unexpected end of file, expected ';' } standard output = { } ================================================ FILE: Tests/Diagnostics/expected-token.spire ================================================ // expected one token, but got another int foo() { int a = 3 ] } ================================================ FILE: Tests/Diagnostics/expected-token.spire.expected ================================================ result code = -1 standard error = { Tests/Diagnostics/expected-token.spire(5): error 20001: unexpected ']', expected ';' } standard output = { } ================================================ FILE: Tests/Diagnostics/function-redefinition.spire ================================================ // redefining a function int foo(int a) { return 0; } int foo(int b) { return 1; } ================================================ FILE: Tests/Diagnostics/function-redefinition.spire.expected ================================================ result code = -1 standard error = { Tests/Diagnostics/function-redefinition.spire(4): error 30001: 'foo(int)': function redefinition. } standard output = { } ================================================ FILE: Tests/Diagnostics/hull-shader-invalid-domain.spire ================================================ // `HullShader` without `Domain` attribute pipeline P { world CoarseVertex; world ControlPoint; world CornerPoint; world TessPatch; world FineVertex; require @FineVertex vec4 RS_Position; require @ControlPoint vec2 tessLevelInner; require @ControlPoint vec4 tessLevelOuter; // implicit import operator CoarseVertex->CornerPoint extern @CornerPoint CoarseVertex[] CoarseVertex_ControlPoint; [PerCornerIterator] extern @CornerPoint int HS_CornerID; extern @ControlPoint CoarseVertex[] CoarseVertex_ControlPoint; extern @TessPatch CoarseVertex[] CoarseVertex_ControlPoint; [InvocationId] extern @ControlPoint int invocationId; extern @FineVertex ControlPoint[] ControlPoint_tes; extern @FineVertex Patch perPatch_tes; extern @FineVertex Patch perCorner_tes; [TessCoord] extern @FineVertex vec3 tessCoord; stage hs : HullShader { PatchWorld: TessPatch; ControlPointWorld: ControlPoint; CornerPointWorld: CornerPoint; InputControlPointCount: 3; ControlPointCount: 1; Domain: pentagons; TessLevelOuter: tessLevelOuter; TessLevelInner: tessLevelInner; Partitioning: integer; OutputTopology: triangle_ccw; } } shader S targets P { @FineVertex float4 RS_Position = float4(0.0); @ControlPoint float2 tessLevelInner = float2(2.0); @ControlPoint float4 tessLevelOuter = float4(2.0); } ================================================ FILE: Tests/Diagnostics/hull-shader-invalid-domain.spire.expected ================================================ result code = -1 standard error = { Tests/Diagnostics/hull-shader-invalid-domain.spire(38): error 50053: 'Domain' should be either 'triangles' or 'quads'. } standard output = { } ================================================ FILE: Tests/Diagnostics/hull-shader-no-domain.spire ================================================ // `HullShader` without `Domain` attribute pipeline P { world CoarseVertex; world ControlPoint; world CornerPoint; world TessPatch; world FineVertex; require @FineVertex vec4 RS_Position; require @ControlPoint vec2 tessLevelInner; require @ControlPoint vec4 tessLevelOuter; // implicit import operator CoarseVertex->CornerPoint extern @CornerPoint CoarseVertex[] CoarseVertex_ControlPoint; [PerCornerIterator] extern @CornerPoint int HS_CornerID; extern @ControlPoint CoarseVertex[] CoarseVertex_ControlPoint; extern @TessPatch CoarseVertex[] CoarseVertex_ControlPoint; [InvocationId] extern @ControlPoint int invocationId; extern @FineVertex ControlPoint[] ControlPoint_tes; extern @FineVertex Patch perPatch_tes; extern @FineVertex Patch perCorner_tes; [TessCoord] extern @FineVertex vec3 tessCoord; stage hs : HullShader { PatchWorld: TessPatch; ControlPointWorld: ControlPoint; CornerPointWorld: CornerPoint; InputControlPointCount: 3; ControlPointCount: 1; // Domain: triangles; TessLevelOuter: tessLevelOuter; TessLevelInner: tessLevelInner; Partitioning: integer; OutputTopology: triangle_ccw; } } shader S targets P { @FineVertex float4 RS_Position = float4(0.0); @ControlPoint float2 tessLevelInner = float2(2.0); @ControlPoint float4 tessLevelOuter = float4(2.0); } ================================================ FILE: Tests/Diagnostics/hull-shader-no-domain.spire.expected ================================================ result code = -1 standard error = { Tests/Diagnostics/hull-shader-no-domain.spire(31): error 50052: 'HullShader' requires attribute 'Domain'. } standard output = { } ================================================ FILE: Tests/Diagnostics/illegal-character.spire ================================================ // illegal character $ ================================================ FILE: Tests/Diagnostics/illegal-character.spire.expected ================================================ result code = -1 standard error = { Tests/Diagnostics/illegal-character.spire(3): error 10000: Illegal character '\x24' } standard output = { } ================================================ FILE: Tests/Diagnostics/missing-file.spire ================================================ // trying to import a non-existant file using "does-not-exist.spire" ================================================ FILE: Tests/Diagnostics/missing-file.spire.expected ================================================ result code = -1 standard error = { Tests/Diagnostics/missing-file.spire(0): error 20001: unexpected end of file, expected ';' Tests/Diagnostics/missing-file.spire(3): error 2: cannot find file 'does-not-exist.spire'. } standard output = { } ================================================ FILE: Tests/Diagnostics/parameter-already-defined.spire ================================================ // re-use parameter name int foo( int a, float a ) { return 0; } ================================================ FILE: Tests/Diagnostics/parameter-already-defined.spire.expected ================================================ result code = -1 standard error = { Tests/Diagnostics/parameter-already-defined.spire(3): error 30002: parameter 'a' already defined. } standard output = { } ================================================ FILE: Tests/Diagnostics/undefined-identifier.spire ================================================ // use of undefined identifier void foo() { int a = b; } ================================================ FILE: Tests/Diagnostics/undefined-identifier.spire.expected ================================================ result code = -1 standard error = { Tests/Diagnostics/undefined-identifier.spire(5): error 30015: undefined identifier 'b'. } standard output = { } ================================================ FILE: Tests/Diagnostics/variable-void-type.spire ================================================ // variable with `void` type void foo() { void a; } ================================================ FILE: Tests/Diagnostics/variable-void-type.spire.expected ================================================ result code = -1 standard error = { Tests/Diagnostics/variable-void-type.spire(5): error 30009: invalid type 'void'. } standard output = { } ================================================ FILE: Tests/Diagnostics/while-predicate-type.spire ================================================ // bad type for `while` predicate struct S {}; void foo() { S s; while(s) {break;} } ================================================ FILE: Tests/Diagnostics/while-predicate-type.spire.expected ================================================ result code = -1 standard error = { Tests/Diagnostics/while-predicate-type.spire(8): error 30010: 'while': expression must evaluate to int. } standard output = { } ================================================ FILE: Tests/FrontEnd/lexer-comments.spire ================================================ // confirming that the lexer handles comments correctly // line comment /* block comment */ /* block comments don't nest /* */ float f(float f) { return f; } ================================================ FILE: Tests/FrontEnd/parser-decls.spire ================================================ // test that we can parse all the expected kinds of declarations // global-scope `using` is another test // pipeline pipeline P { } // empty declaration ; // struct type struct Pair { int head; float tail; } // function at global scope float tail(Pair p) { return p.tail; } // module module M { // component declarations // using declarations } // a module can "inherit" from a pipeline module M2 targets P { } // shader shader S { // component declarations // using declarations } ================================================ FILE: Tests/FrontEnd/parser-empty.spire ================================================ ================================================ FILE: Tests/FrontEnd/parser-error-unclosed-curly.spire ================================================ shader Test { // Note: no closing curly brace ================================================ FILE: Tests/FrontEnd/parser-error-unclosed-curly.spire.expected ================================================ result code = -1 standard error = { Tests/FrontEnd/parser-error-unclosed-curly.spire(0): error 20001: unexpected end of file, expected '}' } standard output = { } ================================================ FILE: Tests/FrontEnd/parser-using-file-a.spireh ================================================ // this file exists to be included by "parser-using-file.spire" float a(float x) { return x * x; } ================================================ FILE: Tests/FrontEnd/parser-using-file.spire ================================================ // test that we can include a file via `using` using "parser-using-file-a.spireh"; float base( float x ) { return a(x); } ================================================ FILE: Tests/FrontEnd/pipeline-simple.spireh ================================================ // pipeline-simple.spireh // TODO(tfoley): strip this down to a minimal pipeline pipeline StandardPipeline { [Pinned] input world MeshVertex; world CoarseVertex;// : "glsl(vertex:projCoord)" using projCoord export standardExport; world Fragment;// : "glsl" export fragmentExport; require @CoarseVertex vec4 projCoord; [VertexInput] extern @CoarseVertex MeshVertex vertAttribIn; import(MeshVertex->CoarseVertex) vertexImport() { return project(vertAttribIn); } extern @Fragment CoarseVertex CoarseVertexIn; import(CoarseVertex->Fragment) standardImport() // TODO(tfoley): this trait doesn't seem to be implemented on `vec3` // require trait IsTriviallyPassable(CoarseVertex) { return project(CoarseVertexIn); } stage vs : VertexShader { World: CoarseVertex; Position: projCoord; } stage fs : FragmentShader { World: Fragment; } } ================================================ FILE: Tests/FrontEnd/struct.spire ================================================ // test that `struct` decls work #include "pipeline-simple.spireh" // struct declaration struct Foo { vec3 a; vec3 b; }; // function on a struct Foo makeFoo(float x, float y) { // local of struct type Foo foo; foo.a = vec3(x); foo.b = vec3(y); return foo; } shader Test targets StandardPipeline { // Uniform of struct type param Foo foo1; @MeshVertex vec3 position; @MeshVertex vec3 color; param mat4 modelViewProjection; public vec4 projCoord = modelViewProjection * vec4(position, 1.0); // Component of struct type // Note(tfoley): use of `public` here required to work around parser limitations public Foo foo2 = makeFoo(color.x, color.y); // vec3 result = foo1.a + foo2.b; out @Fragment vec4 colorTarget = vec4(result,1); } ================================================ FILE: Tests/FrontEnd/typedef.spire ================================================ // test that we can `typedef` a type typedef float F32; F32 foo() { float x = 123.0; return x; } float bar() { return foo(); } ================================================ FILE: Tests/HLSLCodeGen/DeferredLighting.fs.hlsl ================================================ #pragma warning(disable: 3576) #pragma pack_matrix( row_major ) struct ArrInStruct { float3 ttt; float3 points[4]; }; struct SkinningResult { float3 pos; float3 tangent; float3 binormal; }; Texture2D DeferredLightingParams_albedoTex: register(t0); Texture2D DeferredLightingParams_pbrTex: register(t1); Texture2D DeferredLightingParams_normalTex: register(t2); Texture2D DeferredLightingParams_depthTex: register(t3); SamplerState DeferredLightingParams_nearestSampler: register(s0); cbuffer bufForwardBasePassParams : register(b1) { struct { float4x4 viewTransform; float4x4 viewProjectionTransform; float4x4 invViewTransform; float4x4 invViewProjTransform; float3 cameraPos; float time; } ForwardBasePassParams; }; SamplerState ForwardBasePassParams_textureSampler: register(s1); cbuffer buflighting : register(b2) { struct { float3 lightDir; float3 lightColor; float ambient; int shadowMapId; int numCascades; float4x4 lightMatrix[8]; float4 zPlanes[2]; } lighting; }; Texture2DArray lighting_shadowMapArray: register(t4); SamplerComparisonState lighting_shadowMapSampler: register(s2); TextureCube lighting_envMap: register(t5); Texture2D layers_l0_albedoTex: register(t6); SamplerState layers_l0_samplerState: register(s3); Texture2D layers_l1_albedoTex: register(t7); SamplerState layers_l1_samplerState: register(s4); Texture2D layers_l2_albedoTex: register(t8); SamplerState layers_l2_samplerState: register(s5); int Test(float3 p_b, inout ArrInStruct p_testOut); float PhongApprox(float p_Roughness, float p_RoL); float3 EnvBRDFApprox(float3 p_SpecularColor, float p_Roughness, float p_NoV); float DeferredLighting_selfShadow_vec3(float3 p0_x); float4 SubModuleWithParam_Eval_vec2(Texture2D p0_albedoTex, SamplerState p1_samplerState, float2 p2_uv); float4 SubModuleWithParam1_Eval_vec2(Texture2D p0_albedoTex, SamplerState p1_samplerState, float2 p2_uv); int Test(float3 p_b, inout ArrInStruct p_testOut) { int i = 0; p_testOut.ttt = p_b; i = 0; for (; (i < 4); (i = (i + 1))) { p_testOut.points[i] = 0; } return 0; } float PhongApprox(float p_Roughness, float p_RoL) { float a; float a2; float rcp_a2; float c; float p; a = (p_Roughness * p_Roughness); a = max(a, 8.000000379980e-03); a2 = (a * a); rcp_a2 = (1.000000000000e+00 / a2); c = ((7.213475108147e-01 * rcp_a2) + 3.967411220074e-01); p = (rcp_a2 * exp2(((c * p_RoL) - c))); return min(p, rcp_a2); } float3 EnvBRDFApprox(float3 p_SpecularColor, float p_Roughness, float p_NoV) { float4 c0; float4 c1; float4 r; float a004; float2 AB; c0 = float4((-1), (-2.749999985099e-02), (-5.720000267029e-01), 2.199999988079e-02); c1 = float4(1, 4.250000044703e-02, 1.039999961853e+00, (-3.999999910593e-02)); r = ((p_Roughness * c0) + c1); a004 = ((min((r.x * r.x), exp2(((-9.279999732971e+00) * p_NoV))) * r.x) + r.y); AB = ((float2((-1.039999961853e+00), 1.039999961853e+00) * a004) + r.zw); AB.y = (AB.y * min((5.000000000000e+01 * p_SpecularColor.y), 1.000000000000e+00)); return ((p_SpecularColor * AB.x) + AB.y); } float DeferredLighting_selfShadow_vec3(float3 p0_x) { return 1.000000000000e+00; } float4 SubModuleWithParam_Eval_vec2(Texture2D p0_albedoTex, SamplerState p1_samplerState, float2 p2_uv) { return p0_albedoTex.Sample(p1_samplerState, p2_uv); } float4 SubModuleWithParam1_Eval_vec2(Texture2D p0_albedoTex, SamplerState p1_samplerState, float2 p2_uv) { return p0_albedoTex.Sample(p1_samplerState, p2_uv); } struct TCoarseVertex { float2 vertUV_CoarseVertex : A0A; }; struct TFragment { float4 outputColor : A0A; }; struct TFragmentExt { TFragment user : SV_Target; }; TFragmentExt main( TCoarseVertex stage_input) { TFragmentExt stage_output; float2 vertUV; float4 normalSample; float3 normal; float4 pbr; float3 albedo; float3 lightParam; float4 t45F; float4 position; float3 pos; float3 view; float shadow; float3 viewPos; int i = 0; int t474 = 0; int t476 = 0; float4 lightSpacePosT; float3 lightSpacePos; float val; float3 lNormal; float dielectricSpecluar; float3 diffuseColor; float3 specularColor; float NoV; float3 R; float RoL; float3 result; float3 specularIBL; float3 diffuseIBL; float4 outputColor; ArrInStruct s; int t4CD = 0; vertUV = stage_input/*standard*/.vertUV_CoarseVertex; normalSample = DeferredLightingParams_normalTex.Sample(DeferredLightingParams_nearestSampler, vertUV); normal = ((normalSample.xyz * 2.000000000000e+00) - 1.000000000000e+00); pbr = DeferredLightingParams_pbrTex.Sample(DeferredLightingParams_nearestSampler, vertUV); albedo = DeferredLightingParams_albedoTex.Sample(DeferredLightingParams_nearestSampler, vertUV).xyz; lightParam = float3(pbr.x, pbr.y, pbr.z); t45F = DeferredLightingParams_depthTex.Sample(DeferredLightingParams_nearestSampler, vertUV); position = mul(float4(((vertUV.x * 2) - 1), ((vertUV.y * 2) - 1), t45F.x, 1.000000000000e+00), ForwardBasePassParams.invViewProjTransform); pos = (position.xyz / position.w); view = normalize((ForwardBasePassParams.cameraPos - pos)); shadow = DeferredLighting_selfShadow_vec3(lighting.lightDir); if (bool(lighting.numCascades)) { viewPos = mul(float4(pos, 1.000000000000e+00), ForwardBasePassParams.viewTransform).xyz; i = 0; for (; (i < lighting.numCascades); (i = (i + 1))) { t474 = (i >> 2); t476 = (i & 3); if (bool(((-viewPos.z) < lighting.zPlanes[t474][t476]))) { lightSpacePosT = mul(float4(pos, 1.000000000000e+00), lighting.lightMatrix[i]); lightSpacePos = (lightSpacePosT.xyz / lightSpacePosT.w); val = lighting_shadowMapArray.SampleCmp(lighting_shadowMapSampler, float3(lightSpacePos.xy, (i + lighting.shadowMapId)), lightSpacePos.z); shadow = (shadow * val); break; } } } if (bool((normalSample.w != 0.000000000000e+00))) { lNormal = ((dot(normal, view) < 0)?(-normal):normal); } else { lNormal = normal; } dielectricSpecluar = (1.999999955297e-02 * lightParam.z); diffuseColor = (albedo - (albedo * lightParam.y)); specularColor = (((float3) (dielectricSpecluar - (dielectricSpecluar * lightParam.y))) + (albedo * lightParam.y)); NoV = max(dot(lNormal, view), 0.000000000000e+00); specularColor = EnvBRDFApprox(specularColor, lightParam.x, NoV); R = reflect((-view), lNormal); RoL = max(0, dot(R, lighting.lightDir)); result = (((lighting.lightColor * clamp(dot(lNormal, lighting.lightDir), 9.999999776483e-03, 9.900000095367e-01)) * (diffuseColor + (specularColor * PhongApprox(lightParam.x, RoL)))) * shadow); specularIBL = (specularColor * lighting_envMap.SampleLevel(ForwardBasePassParams_textureSampler, R, (clamp(lightParam.x, 0.000000000000e+00, 1.000000000000e+00) * 8.000000000000e+00)).xyz); diffuseIBL = ((diffuseColor * lighting_envMap.SampleLevel(ForwardBasePassParams_textureSampler, lNormal, 8.000000000000e+00).xyz) * lighting.ambient); result = (result + (specularIBL + diffuseIBL)); result = (result * pbr.w); outputColor = (float4(result, 1.000000000000e+00) + ((SubModuleWithParam_Eval_vec2(layers_l0_albedoTex, layers_l0_samplerState, ((float2) 0.000000000000e+00)) + SubModuleWithParam_Eval_vec2(layers_l1_albedoTex, layers_l1_samplerState, ((float2) 0.000000000000e+00))) + SubModuleWithParam1_Eval_vec2(layers_l2_albedoTex, layers_l2_samplerState, ((float2) 0.000000000000e+00)))); t4CD = Test(((float3) 1.000000000000e+00), s); stage_output.user.outputColor = outputColor; stage_output.user.outputColor = outputColor; return stage_output; } ================================================ FILE: Tests/HLSLCodeGen/DeferredLighting.vs.hlsl ================================================ #pragma warning(disable: 3576) #pragma pack_matrix( row_major ) struct SkinningResult { float3 pos; float3 tangent; float3 binormal; }; Texture2D DeferredLightingParams_albedoTex: register(t0, space-1); Texture2D DeferredLightingParams_pbrTex: register(t1, space-1); Texture2D DeferredLightingParams_normalTex: register(t2, space-1); Texture2D DeferredLightingParams_depthTex: register(t3, space-1); SamplerState DeferredLightingParams_nearestSampler: register(s0, space-1); SamplerState ForwardBasePassParams_textureSampler: register(s1, space-1); Texture2DArray lighting_shadowMapArray: register(t4, space-1); SamplerComparisonState lighting_shadowMapSampler: register(s2, space-1); TextureCube lighting_envMap: register(t5, space-1); Texture2D layers_l0_albedoTex: register(t6, space-1); SamplerState layers_l0_samplerState: register(s3, space-1); Texture2D layers_l1_albedoTex: register(t7, space-1); SamplerState layers_l1_samplerState: register(s4, space-1); Texture2D layers_l2_albedoTex: register(t8, space-1); SamplerState layers_l2_samplerState: register(s5, space-1); Texture2D DeferredLightingParams_albedoTex: register(t0, space0); Texture2D DeferredLightingParams_pbrTex: register(t1, space0); Texture2D DeferredLightingParams_normalTex: register(t2, space0); Texture2D DeferredLightingParams_depthTex: register(t3, space0); SamplerState DeferredLightingParams_nearestSampler: register(s0, space0); cbuffer bufForwardBasePassParams : register(b1) { struct { float4x4 viewTransform; float4x4 viewProjectionTransform; float4x4 invViewTransform; float4x4 invViewProjTransform; float3 cameraPos; float time; } ForwardBasePassParams; }; SamplerState ForwardBasePassParams_textureSampler: register(s0, space1); cbuffer buflighting : register(b2) { struct { float3 lightDir; float3 lightColor; float ambient; int shadowMapId; int numCascades; float4x4 lightMatrix[8]; float4 zPlanes[2]; } lighting; }; Texture2DArray lighting_shadowMapArray: register(t0, space2); SamplerComparisonState lighting_shadowMapSampler: register(s0, space2); TextureCube lighting_envMap: register(t1, space2); Texture2D layers_l0_albedoTex: register(t0, space3); SamplerState layers_l0_samplerState: register(s0, space3); Texture2D layers_l1_albedoTex: register(t1, space3); SamplerState layers_l1_samplerState: register(s1, space3); Texture2D layers_l2_albedoTex: register(t2, space3); SamplerState layers_l2_samplerState: register(s2, space3); struct TMeshVertex { float2 vertPos : A0A; float2 vertUV : A1A; }; struct TCoarseVertex { float2 vertUV_CoarseVertex : A0A; }; struct TCoarseVertexExt { TCoarseVertex user; float4 sv_position : SV_Position; }; TCoarseVertexExt main( TMeshVertex stage_input : A) { TCoarseVertexExt stage_output; float2 vertPos; float2 vertUV; vertPos = stage_input/*standard*/.vertPos; vertUV = stage_input/*standard*/.vertUV; stage_output.user.vertUV_CoarseVertex = vertUV; stage_output.sv_position = float4(vertPos.xy, 0.000000000000e+00, 1.000000000000e+00); return stage_output; } ================================================ FILE: Tests/HLSLCodeGen/StandardPipeline.spire ================================================ pipeline StandardPipeline { [Pinned] input world MeshVertex; world CoarseVertex; world Fragment; require @CoarseVertex vec4 projCoord; [VertexInput] extern @CoarseVertex MeshVertex vertAttribIn; import(MeshVertex->CoarseVertex) vertexImport() { return project(vertAttribIn); } extern @Fragment CoarseVertex CoarseVertexIn; import(CoarseVertex->Fragment) standardImport() require trait IsTriviallyPassable(T) { return project(CoarseVertexIn); } stage vs : VertexShader { World: CoarseVertex; Position: projCoord; } stage fs : FragmentShader { World: Fragment; } } pipeline TessellationPipeline : StandardPipeline { [Pinned] input world MeshVertex; world CoarseVertex; world ControlPoint; world CornerPoint; world TessPatch; world FineVertex; world Fragment; require @FineVertex vec4 projCoord; require @ControlPoint vec2 tessLevelInner; require @ControlPoint vec4 tessLevelOuter; [VertexInput] extern @CoarseVertex MeshVertex vertAttribs; import(MeshVertex->CoarseVertex) vertexImport() { return project(vertAttribs); } // implicit import operator CoarseVertex->CornerPoint extern @CornerPoint CoarseVertex[] CoarseVertex_ControlPoint; [PerCornerIterator] extern @CornerPoint int sysLocalIterator; import (CoarseVertex->CornerPoint) standardImport() require trait IsTriviallyPassable(T) { return project(CoarseVertex_ControlPoint[sysLocalIterator]); } // implicit import operator FineVertex->Fragment extern @Fragment FineVertex tes_Fragment; import(FineVertex->Fragment) standardImport() require trait IsTriviallyPassable(T) { return project(tes_Fragment); } extern @ControlPoint CoarseVertex[] CoarseVertex_ControlPoint; extern @TessPatch CoarseVertex[] CoarseVertex_ControlPoint; [InvocationId] extern @ControlPoint int invocationId; import(CoarseVertex->ControlPoint) indexImport(int id) require trait IsTriviallyPassable(T) { return project(CoarseVertex_ControlPoint[id]); } import(CoarseVertex->TessPatch) indexImport(int id) require trait IsTriviallyPassable(T) { return project(CoarseVertex_ControlPoint[id]); } extern @FineVertex ControlPoint[] ControlPoint_tes; import(ControlPoint->FineVertex) indexImport(int id) require trait IsTriviallyPassable(T) { return project(ControlPoint_tes[id]); } extern @FineVertex Patch perPatch_tes; import (TessPatch->FineVertex) standardImport() require trait IsTriviallyPassable(T) { return project(perPatch_tes); } extern @FineVertex Patch perCorner_tes; [TessCoord] extern @FineVertex vec3 tessCoord; import(CornerPoint->FineVertex) standardImport() require T operator + (T, T) require T operator * (T, float) { return project(perCorner_tes[0]) * tessCoord.x + project(perCorner_tes[1]) * tessCoord.y + project(perCorner_tes[2]) * tessCoord.z; } stage vs : VertexShader { VertexInput: vertAttribs; World: CoarseVertex; } stage tcs : HullShader { PatchWorld: TessPatch; ControlPointWorld: ControlPoint; CornerPointWorld: CornerPoint; ControlPointCount: 1; Domain: triangles; TessLevelOuter: tessLevelOuter; TessLevelInner: tessLevelInner; } stage tes : DomainShader { World : FineVertex; Position : projCoord; Domain: triangles; } stage fs : FragmentShader { World: Fragment; CoarseVertexInput: vertexOutputBlock; } } ================================================ FILE: Tests/HLSLCodeGen/Utils.spire ================================================ vec4 QuaternionMul(vec4 q1, vec4 q2) { vec4 rs; rs.x = q1.w*q2.x + q1.x*q2.w + q1.y*q2.z - q1.z*q2.y; rs.y = q1.w*q2.y + q1.y*q2.w + q1.z*q2.x - q1.x*q2.z; rs.z = q1.w*q2.z + q1.z*q2.w + q1.x*q2.y - q1.y*q2.x; rs.w = q1.w*q2.w - q1.x*q2.x - q1.y*q2.y - q1.z*q2.z; return rs; } vec4 QuaternionConjugate(vec4 q) { return vec4(-q.x, -q.y, -q.z, q.w); } vec3 QuaternionRotate(vec4 q, vec3 pos) { return QuaternionMul(QuaternionMul(q, vec4(pos, 0.0)), QuaternionConjugate(q)).xyz; } float ComputeLuminance(vec3 color) { return color.x * 0.3 + color.y * 0.59 + color.z * 0.11; } vec3 desaturate(vec3 color, float factor) { float lum = ComputeLuminance(color); return mix(color, vec3(lum, lum, lum), factor); } module TangentSpaceTransform { require vec3 coarseVertTangent; require vec3 coarseVertBinormal; require vec3 worldTransformTangent(vec3 pos); public vec3 vTangent = worldTransformTangent(coarseVertTangent); public vec3 vBiTangent = worldTransformTangent(coarseVertBinormal); public vec3 vNormal = cross(vBiTangent, vTangent); public vec3 WorldSpaceToTangentSpace(vec3 v) { return vec3(dot(v, vTangent), dot(v, vBiTangent), dot(v, vNormal)); } public vec3 TangentSpaceToWorldSpace(vec3 v) { return v.x * vTangent + v.y * vBiTangent + v.z * vNormal; } } module VertexTransform { require vec3 fineVertPos; require vec3 displacement; require mat4 viewProjectionTransform; require vec3 worldTransformPos(vec3 pos); public vec3 pos = worldTransformPos(fineVertPos+displacement); public vec4 projCoord { vec4 rs = viewProjectionTransform * vec4(pos, 1); return rs; } } module NoAnimation { param mat4 modelMatrix; require vec3 vertPos; require vec3 vertNormal; require vec3 vertTangent; require vec3 vertBinormal; public @CoarseVertex vec3 coarseVertPos = vertPos; public @CoarseVertex vec3 coarseVertNormal = vertNormal; public @CoarseVertex vec3 coarseVertTangent = vertTangent; public @CoarseVertex vec3 coarseVertBinormal = vertBinormal; public vec3 worldTransformPos(vec3 pos) { return (modelMatrix * vec4(pos, 1)).xyz; } public vec3 worldTransformTangent(vec3 tangent) { return normalize(mat3(modelMatrix) * tangent); } } struct SkinningResult { vec3 pos; vec3 tangent; vec3 binormal; } module SkeletalAnimation { require vec3 vertPos; require vec3 vertBinormal; require vec3 vertTangent; require uint boneIds; require uint boneWeights; require mat4 viewProjectionTransform; param mat4[128] boneTransforms; public SkinningResult skinning { SkinningResult result; result.pos = vec3(0.0); result.binormal = vec3(0.0); result.tangent = vec3(0.0); for (int i = 0; i < 4; i++) { uint boneId = (boneIds >> (i*8)) & 255; if (boneId == 255) continue; float boneWeight = float((boneWeights >> (i*8)) & 255) * (1.0/255.0); vec3 tp = (boneTransforms[boneId] * vec4(vertPos, 1.0)).xyz; result.pos += tp * boneWeight; tp = mat3(boneTransforms[boneId]) * vertBinormal; result.binormal += tp * boneWeight; tp = mat3(boneTransforms[boneId]) * vertTangent; result.tangent += tp * boneWeight; } result.binormal = normalize(result.binormal); result.tangent = normalize(result.tangent); return result; } public @CoarseVertex vec3 coarseVertPos = skinning.pos; public @CoarseVertex vec3 coarseVertBinormal = skinning.binormal; public @CoarseVertex vec3 coarseVertTangent = skinning.tangent; public @CoarseVertex vec3 coarseVertNormal = normalize(cross(coarseVertBinormal, coarseVertTangent)); public vec3 worldTransformPos(vec3 pos) { return pos; } public vec3 worldTransformTangent(vec3 tangent) { return tangent; } } module NoTessellation { require vec3 coarseVertPos; public vec3 fineVertPos = coarseVertPos; } module PN_Tessellation targets TessellationPipeline { require vec3 coarseVertPos; require vec3 coarseVertNormal; public @ControlPoint vec4 tessLevelOuter = vec4(3, 3, 3, 0); public @ControlPoint vec2 tessLevelInner = vec2(3.0); vec3 ProjectToPlane(vec3 Point, vec3 PlanePoint, vec3 PlaneNormal) {     vec3 v = Point - PlanePoint;     float Len = dot(v, PlaneNormal);     vec3 d = Len * PlaneNormal;     return (Point - d); } @ControlPoint vec3 WorldPos_B030 = indexImport(coarseVertPos, 0); @ControlPoint vec3 WorldPos_B003 = indexImport(coarseVertPos, 1); @ControlPoint vec3 WorldPos_B300 = indexImport(coarseVertPos, 2);     // Edges are names according to the opposing vertex     vec3 EdgeB300 = WorldPos_B003 - WorldPos_B030;     vec3 EdgeB030 = WorldPos_B300 - WorldPos_B003;     vec3 EdgeB003 = WorldPos_B030 - WorldPos_B300;     // Generate two midpoints on each edge     vec3 WorldPos_B021t = WorldPos_B030 + EdgeB300 / 3.0;     vec3 WorldPos_B012t = WorldPos_B030 + EdgeB300 * 2.0 / 3.0;     vec3 WorldPos_B102t = WorldPos_B003 + EdgeB030 / 3.0;     vec3 WorldPos_B201t = WorldPos_B003 + EdgeB030 * 2.0 / 3.0;     vec3 WorldPos_B210t = WorldPos_B300 + EdgeB003 / 3.0;     vec3 WorldPos_B120t = WorldPos_B300 + EdgeB003 * 2.0 / 3.0;     // Project each midpoint on the plane defined by the nearest vertex and its normal     @ControlPoint vec3 WorldPos_B021 = ProjectToPlane(WorldPos_B021t, WorldPos_B030,                                          indexImport(coarseVertNormal, 0));     @ControlPoint vec3 WorldPos_B012 = ProjectToPlane(WorldPos_B012t, WorldPos_B003,                                          indexImport(coarseVertNormal, 1));     @ControlPoint vec3 WorldPos_B102 = ProjectToPlane(WorldPos_B102t, WorldPos_B003,                                          indexImport(coarseVertNormal, 1));     @ControlPoint vec3 WorldPos_B201 = ProjectToPlane(WorldPos_B201t, WorldPos_B300,                                          indexImport(coarseVertNormal, 2));     @ControlPoint vec3 WorldPos_B210 = ProjectToPlane(WorldPos_B210t, WorldPos_B300,                                          indexImport(coarseVertNormal, 2));     @ControlPoint vec3 WorldPos_B120 = ProjectToPlane(WorldPos_B120t, WorldPos_B030,                                          indexImport(coarseVertNormal, 0));     // Handle the center     vec3 Center = (WorldPos_B003 + WorldPos_B030 + WorldPos_B300) / 3.0;     vec3 WorldPos_B111t = (WorldPos_B021 + WorldPos_B012 + WorldPos_B102 +                           WorldPos_B201 + WorldPos_B210 + WorldPos_B120) / 6.0;     vec3 WorldPos_B111 = WorldPos_B111t + (WorldPos_B111t - Center) / 2.0; float u = tessCoord.x;     float v = tessCoord.y;     float w = tessCoord.z;     float vPow2 = v*v;     float uPow2 = u*u;     float wPow2 = w*w;     float uPow3 = uPow2 * u;     float vPow3 = vPow2 * v;     float wPow3 = wPow2 * w;     public @FineVertex float3 fineVertPos = indexImport(WorldPos_B300, 0) * wPow3 +                     indexImport(WorldPos_B030, 0) * uPow3 +                     indexImport(WorldPos_B003, 0) * vPow3 +                     indexImport(WorldPos_B210, 0) * 3.0 * wPow2 * u +                     indexImport(WorldPos_B120, 0) * 3.0 * w * uPow2 +                     indexImport(WorldPos_B201, 0) * 3.0 * wPow2 * v +                     indexImport(WorldPos_B021, 0) * 3.0 * uPow2 * v +                     indexImport(WorldPos_B102, 0) * 3.0 * w * vPow2 +                     indexImport(WorldPos_B012, 0) * 3.0 * u * vPow2 +                     indexImport(WorldPos_B111, 0) * 6.0 * w * u * v; } module ParallaxOcclusionMapping { require float GetHeight(vec2 uvCoord); require vec3 viewDirTangentSpace; require vec2 uv; require float parallaxScale; require SamplerState textureSampler; vec3 parallaxMapping { vec3 V = viewDirTangentSpace; vec2 T = uv; float parallaxHeight; // determine optimal number of layers float minLayers = 10; float maxLayers = 15; float numLayers = mix(maxLayers, minLayers, abs(V.z)); // height of each layer float layerHeight = 1.0 / numLayers; // current depth of the layer float curLayerHeight = 0.01; // shift of texture coordinates for each layer vec2 dtex = parallaxScale * V.xy / max(V.z, 1e-5) / numLayers; dtex.y = -dtex.y; // current texture coordinates vec2 currentTextureCoords = T; // depth from heightmap float heightFromTexture = 1.0 - GetHeight(currentTextureCoords);//heightTexture.Sample(textureSampler, currentTextureCoords).r; // while point is above the surface while (heightFromTexture > curLayerHeight) { // to the next layer curLayerHeight += layerHeight; // shift of texture coordinates currentTextureCoords -= dtex; // new depth from heightmap heightFromTexture = 1.0 - GetHeight(currentTextureCoords);//-heightTexture.Sample(textureSampler, currentTextureCoords).r; } /////////////////////////////////////////////////////////// // Start of Relief Parallax Mapping // decrease shift and height of layer by half vec2 deltaTexCoord = dtex / 2; float deltaHeight = layerHeight / 2; // return to the mid point of previous layer currentTextureCoords += deltaTexCoord; curLayerHeight -= deltaHeight; // binary search to increase precision of Steep Paralax Mapping int numSearches = 5; for (int i = 0; i <= numSearches; i++) { // decrease shift and height of layer by half deltaTexCoord /= 2; deltaHeight /= 2; // new depth from heightmap heightFromTexture = 1.0 - GetHeight(currentTextureCoords);//heightTexture.Sample(textureSampler, currentTextureCoords).r; // shift along or agains vector V if(heightFromTexture > curLayerHeight) // below the surface { currentTextureCoords -= deltaTexCoord; curLayerHeight += deltaHeight; } else // above the surface { currentTextureCoords += deltaTexCoord; curLayerHeight -= deltaHeight; } } parallaxHeight = curLayerHeight; return vec3(currentTextureCoords, parallaxHeight); } public vec2 uvOut = parallaxMapping.xy; public float heightOut = parallaxMapping.z; public float selfShadow(vec3 L_tangentSpace) { float initialHeight = heightOut - 0.05; vec3 L = L_tangentSpace; vec2 initialTexCoord = uvOut; float shadowMultiplier = 1; float minLayers = 15; float maxLayers = 30; // calculate lighting only for surface oriented to the light source if (L.z > 0) { // calculate initial parameters float numSamplesUnderSurface = 0; shadowMultiplier = 0; float numLayers = mix(maxLayers, minLayers, abs(L.z)); float layerHeight = max(0.03, abs(initialHeight / numLayers)); vec2 texStep = parallaxScale * L.xy / L.z / numLayers; texStep.y = -texStep.y; // current parameters float currentLayerHeight = initialHeight - layerHeight; vec2 currentTextureCoords = initialTexCoord + texStep; float heightFromTexture = 1.0 - GetHeight(currentTextureCoords); //heightTexture.Sample(textureSampler, currentTextureCoords).r; // while point is below depth 0.0 ) while(currentLayerHeight > 0) { // if point is under the surface if(heightFromTexture < currentLayerHeight) { numSamplesUnderSurface += 1; break; } // ofFragmentet to the next layer currentLayerHeight -= layerHeight; currentTextureCoords += texStep; heightFromTexture = 1.0 - GetHeight(currentTextureCoords); //-heightTexture.Sample(textureSampler, currentTextureCoords).r; } // Shadowing factor should be 1 if there were no points under the surface if(numSamplesUnderSurface < 1) { shadowMultiplier = 1; } else { shadowMultiplier = 0.0; } } return shadowMultiplier; } } float PhongApprox(float Roughness, float RoL) { float a = Roughness * Roughness; a = max(a, 0.008); float a2 = a * a; float rcp_a2 = 1.0/(a2); float c = 0.72134752 * rcp_a2 + 0.39674113; float p = rcp_a2 * exp2(c * RoL - c); // Total 7 instr return min(p, rcp_a2); } vec3 EnvBRDFApprox( vec3 SpecularColor, float Roughness, float NoV ) { vec4 c0 = vec4(-1, -0.0275, -0.572, 0.022); vec4 c1 = vec4(1, 0.0425, 1.04, -0.04); vec4 r = Roughness * c0 + c1; float a004 = min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y; vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw; AB.y *= min(50.0 * SpecularColor.g, 1.0); return SpecularColor * AB.x + AB.y; } module Lighting { public param vec3 lightDir; public param vec3 lightColor; public param float ambient; public param int shadowMapId; public param int numCascades; public param mat4[8] lightMatrix; public param vec4[2] zPlanes; public param Texture2DArrayShadow shadowMapArray; public param SamplerComparisonState shadowMapSampler; public param TextureCube envMap; require vec3 normal; require vec3 albedo; require vec3 lightParam; require float ao; require vec3 pos; require vec3 cameraPos; require float selfShadow(vec3 lightDir); require mat4 viewTransform; require bool isDoubleSided; require SamplerState textureSampler; vec3 lNormal { vec3 result; if (isDoubleSided) { result = dot(normal, view) < 0 ? -normal : normal; } else { result = normal; } return result; } vec3 view = normalize(cameraPos - pos); inline float roughness_in = lightParam.x; inline float metallic_in = lightParam.y; inline float specular_in = lightParam.z; vec3 L = lightDir; vec3 H = normalize(view+L); float dotNL = clamp(dot(lNormal,L), 0.01, 0.99); float dotLH = clamp(dot(L,H), 0.01, 0.99); float dotNH = clamp(dot(lNormal,H), 0.01, 0.99); float Pow4(float x) { return (x*x)*(x*x); } vec2 LightingFuncGGX_FV(float dotLH, float roughness) { float alpha = roughness*roughness;/*sf*/ // F float F_a; float F_b; float dotLH5 = Pow4(1.0-dotLH) * (1.0 - dotLH); F_a = 1.0; F_b = dotLH5; // V float vis; float k = alpha/2.0; float k2 = k*k; float invK2 = 1.0-k2; vis = 1.0/(dotLH*dotLH*invK2 + k2); return vec2(F_a*vis, F_b*vis); } float LightingFuncGGX_D(float dotNH, float roughness) { float alpha = roughness*roughness; float alphaSqr = alpha*alpha; float pi = 3.14159; float denom = dotNH * dotNH *(alphaSqr-1.0) + 1.0; float D = alphaSqr/(pi * denom * denom); return D; } float shadow { float result = selfShadow(lightDir); if (numCascades) { vec3 viewPos = (viewTransform * vec4(pos, 1.0)).xyz; for (int i = 0; i < numCascades; i++) { if (-viewPos.z < zPlanes[i>>2][i&3]) { vec4 lightSpacePosT = lightMatrix[i] * vec4(pos, 1.0); vec3 lightSpacePos = lightSpacePosT.xyz / lightSpacePosT.w; float val = shadowMapArray.SampleCmp(shadowMapSampler, vec3(lightSpacePos.xy, i+shadowMapId), lightSpacePos.z); result *= val; break; } } } return result; } float brightness = clamp(dot(lightDir, lNormal), 0.0, 1.0) * shadow; public vec3 result { float dielectricSpecluar = 0.02 * specular_in; vec3 diffuseColor = albedo - albedo * metallic_in; vec3 specularColor = vec3(dielectricSpecluar - dielectricSpecluar * metallic_in) + albedo * metallic_in; float NoV = max(dot(lNormal, view), 0.0); specularColor = EnvBRDFApprox(specularColor, roughness_in, NoV); vec3 R = reflect(-view, lNormal); float RoL = max(0, dot(R, lightDir)); vec3 color = lightColor * dotNL * (diffuseColor + specularColor * PhongApprox(roughness_in, RoL)) * shadow; vec3 specularIBL = specularColor * envMap.SampleLevel(textureSampler, R, clamp(roughness_in, 0.0, 1.0) * 8.0).xyz; vec3 diffuseIBL = diffuseColor * envMap.SampleLevel(textureSampler, lNormal, 8.0).xyz * ambient; color += specularIBL + diffuseIBL; color *= ao; return color; } } module ForwardBasePassParams { public param mat4 viewTransform; public param mat4 viewProjectionTransform; public param mat4 invViewTransform; public param mat4 invViewProjTransform; public param vec3 cameraPos; public param float time; public param SamplerState textureSampler; } interface IMaterialPattern { vec3 albedo = vec3(1.0); vec3 normal = vec3(0.0, 0.0, 1.0); float roughness = 0.5; float metallic = 0.3; float specular = 0.8; float opacity = 1.0; float ao = 1.0; bool isDoubleSided = false; float selfShadow(vec3 lightDir) { return 1.0; } } interface IMaterialGeometry { public vec3 fineVertPos; public vec3 displacement; } ================================================ FILE: Tests/HLSLCodeGen/shader1.spire ================================================ using "StandardPipeline.spire"; using "Utils.spire"; struct ArrInStruct { vec3 ttt; vec3[4] points; } int Test(in vec3 b, inout ArrInStruct testOut) { testOut.ttt = b; for (int i = 0; i < 4; i++) testOut.points[i] = 0; return 0; } module DeferredLightingParams { param Texture2D albedoTex; param Texture2D pbrTex; param Texture2D normalTex; param Texture2D depthTex; param SamplerState nearestSampler; } module SubModuleWithParam { param Texture2D albedoTex; param SamplerState samplerState; vec4 Eval(vec2 uv) { return albedoTex.Sample(samplerState, uv); } } module SubModuleWithParam1 { param Texture2D albedoTex; param SamplerState samplerState; vec4 Eval(vec2 uv) { return albedoTex.Sample(samplerState, uv); } } module TopModule { using l0 = SubModuleWithParam(); using l1 = SubModuleWithParam(); using l2 = SubModuleWithParam1(); vec4 shadingResult = l0.Eval(vec2(0.0)) + l1.Eval(vec2(0.0)) + l2.Eval(vec2(0.0)); } shader DeferredLighting targets StandardPipeline { [Binding: "0"] public using DeferredLightingParams; [Binding: "1"] public using ForwardBasePassParams; public @MeshVertex vec2 vertPos; public @MeshVertex vec2 vertUV; public vec4 projCoord = vec4(vertPos.xy, 0.0, 1.0); public vec4 normalSample = normalTex.Sample(nearestSampler, vertUV); public vec3 normal = normalSample.xyz * 2.0 - 1.0; public vec4 pbr = pbrTex.Sample(nearestSampler, vertUV); public float roughness = pbr.x; public float metallic = pbr.y; public float specular = pbr.z; public float ao = pbr.w; public vec3 albedo = albedoTex.Sample(nearestSampler, vertUV).xyz; public float selfShadow(vec3 x) { return 1.0; } public bool isDoubleSided = normalSample.w != 0.0; vec3 lightParam = vec3(roughness, metallic, specular); float z = depthTex.Sample(nearestSampler, vertUV).r; float x = vertUV.x*2-1; float y = vertUV.y*2-1; vec4 position = invViewProjTransform * vec4(x, y, z, 1.0f); vec3 pos = position.xyz / position.w; [Binding: "2"] using lighting = Lighting(); [Binding: "3"] using layers = TopModule(); public out @Fragment vec4 outputColor { vec4 rs = vec4(lighting.result, 1.0) + layers.shadingResult; ArrInStruct s; Test(vec3(1.0f), s); return rs; } } /* template shader ForwardBase(passParams : ForwardBasePassParams, lightingModule, geometryModule : IMaterialGeometry, materialModule : IMaterialPattern, animationModule) targets StandardPipeline { public using VertexAttributes; public using passParams; public using animationModule; public using TangentSpaceTransform; public using geometryModule; public using VertexTransform; public using materialModule; vec3 lightParam = vec3(roughness, metallic, specular); using lighting = lightingModule(TangentSpaceToWorldSpace(vec3(normal.x, -normal.y, normal.z))); public out @Fragment vec4 outputColor { ArrInStruct t; int r = Test(vec3(1.0f), t); if (opacity < 0.01f) discard; return vec4(lighting.result, opacity + (float)r); } } */ ================================================ FILE: Tests/Preprocessor/define-function-like.spire ================================================ // support for function-like macros #define FOO(x) 1.0 + x float foo(float y) { return FOO(y) * 2.0; } // simple token pasting #define PASTE(a,b) a##b PASTE(flo,at) bar() { return 0.0; } // no space before parens? not a function-like macro #define M (x) - (x) // Error: undefined identifier `x` float bar(float a) { return M(a); } ================================================ FILE: Tests/Preprocessor/define-function-like.spire.expected ================================================ result code = -1 standard error = { Tests/Preprocessor/define-function-like.spire(15): error 30015: undefined identifier 'x'. Tests/Preprocessor/define-function-like.spire(15): error 30015: undefined identifier 'x'. } standard output = { } ================================================ FILE: Tests/Preprocessor/define-simple.spire ================================================ // #define support #define FOO 1.0f float foo() { return FOO + 2.0; } #define BAR 99 #if BAR > 10 int bar() { return 0; } #else BadThing shouldntCompile; #endif ================================================ FILE: Tests/Preprocessor/if.spire ================================================ // #ifdef support #if (1 - 1*2) < 0 int foo() { return 0; } #else BadThing thatWontCompile; #endif #if (1 >> 1) && ~999 AnotherError onThisLine; #else int bar() { return foo(); } #endif ================================================ FILE: Tests/Preprocessor/ifdef.spire ================================================ // #ifdef support #define A #ifdef A int foo() { return 0; } #else BadThing thatWontCompile; #endif #ifdef BadThing AnotherError onThisLine; #else int bar() { return foo(); } #endif ================================================ FILE: Tests/Preprocessor/include-a.spireh ================================================ // #include support int bar() { return foo(); } ================================================ FILE: Tests/Preprocessor/include.spire ================================================ // #include support int foo() { return 0; } #include "include-a.spireh" int baz() { return bar(); } ================================================ FILE: Tests/SpireTestTool/SpireTestTool.vcxproj ================================================  Debug_VS2013 Win32 Debug_VS2013 x64 Debug Win32 Release_VS2013 Win32 Release_VS2013 x64 Release Win32 Debug x64 Release x64 {0C768A18-1D25-4000-9F37-DA5FE99E3B64} Win32Proj SpireTestTool 8.1 Application true v140 Unicode Application false v140 true Unicode Application false v140 true Unicode Application true v140 Unicode Application false v140 true Unicode Application false v120 true Unicode v140 v120 true true false false false false Level3 Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreadedDebug Console true Level3 Disabled _DEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreadedDebug Console true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded Console true true true Level3 MaxSpeed true true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded Console true true true Level3 MaxSpeed true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded Console true true true Level3 MaxSpeed true true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) MultiThreaded Console true true true MultiThreadedDebug Disabled {f9be7957-8399-899e-0c49-e714fddd4b65} ================================================ FILE: Tests/SpireTestTool/SpireTestTool.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Source Files Source Files Header Files ================================================ FILE: Tests/SpireTestTool/main.cpp ================================================ // main.cpp #include "../../Source/CoreLib/LibIO.h" using namespace CoreLib::Basic; using namespace CoreLib::IO; #include "os.h" #include #include #include #include // Called for an error in the test-runner (not for an error involving // a test itself). void error(char const* message, ...) { fprintf(stderr, "error: "); va_list args; va_start(args, message); vfprintf(stderr, message, args); va_end(args); fprintf(stderr, "\n"); } enum TestResult { kTestResult_Fail, kTestResult_Pass, }; TestResult runTestImpl( String filePath) { // need to execute the stand-alone Spire compiler on the file, and compare its output to what we expect OSProcessSpawner spawner; spawner.pushExecutableName("Source/Debug/SpireCompiler.exe"); spawner.pushArgument(filePath); if (spawner.spawnAndWaitForCompletion() != kOSError_None) { error("failed to run test '%S'", filePath.ToWString()); return kTestResult_Fail; } // We ignore output to stdout, and only worry about what the compiler // wrote to stderr. OSProcessSpawner::ResultCode resultCode = spawner.getResultCode(); String standardOuptut = spawner.getStandardOutput(); String standardError = spawner.getStandardError(); // We construct a single output string that captures the results StringBuilder actualOutputBuilder; actualOutputBuilder.Append("result code = "); actualOutputBuilder.Append(resultCode); actualOutputBuilder.Append("\nstandard error = {\n"); actualOutputBuilder.Append(standardError); actualOutputBuilder.Append("}\nstandard output = {\n"); actualOutputBuilder.Append(standardOuptut); actualOutputBuilder.Append("}\n"); String actualOutput = actualOutputBuilder.ProduceString(); String expectedOutputPath = filePath + ".expected"; String expectedOutput; try { expectedOutput = CoreLib::IO::File::ReadAllText(expectedOutputPath); } catch (CoreLib::IO::IOException) { } TestResult result = kTestResult_Pass; // If no expected output file was found, then we // expect everything to be empty if (expectedOutput.Length() == 0) { if (resultCode != 0) result = kTestResult_Fail; if (standardError.Length() != 0) result = kTestResult_Fail; if (standardOuptut.Length() != 0) result = kTestResult_Fail; } // Otherwise we compare to the expected output else if (actualOutput != expectedOutput) { result = kTestResult_Fail; } // If the test failed, then we write the actual output to a file // so that we can easily diff it from the command line and // diagnose the problem. if (result == kTestResult_Fail) { String actualOutputPath = filePath + ".actual"; CoreLib::IO::File::WriteAllText(actualOutputPath, actualOutput); } return result; } struct TestContext { int totalTestCount; int passedTestCount; int failedTestCount; }; void runTest( TestContext* context, String filePath) { context->totalTestCount++; TestResult result = runTestImpl(filePath); if (result == kTestResult_Pass) { printf("passed"); context->passedTestCount++; } else { printf("FAILED"); context->failedTestCount++; } printf(" test: '%S'\n", filePath.ToWString()); } void runTestsInDirectory( TestContext* context, String directoryPath) { for (auto file : osFindFilesInDirectoryMatchingPattern(directoryPath, "*.spire")) { runTest(context, file); } } // int main( int argc, char** argv) { TestContext context = { 0 }; // Enumerate test files according to policy // TODO: add more directories to this list // TODO: allow for a command-line argument to select a particular directory runTestsInDirectory(&context, "Tests/FrontEnd/"); runTestsInDirectory(&context, "Tests/Diagnostics/"); runTestsInDirectory(&context, "Tests/Preprocessor/"); if (!context.totalTestCount) { printf("no tests run"); return 0; } printf("\n===\n%d%% of tests passed (%d/%d)\n===\n\n", (context.passedTestCount*100) / context.totalTestCount, context.passedTestCount, context.totalTestCount); return context.passedTestCount == context.totalTestCount ? 0 : 1; } ================================================ FILE: Tests/SpireTestTool/os.cpp ================================================ // os.cpp #include "os.h" #include #include #include using namespace CoreLib::Basic; // Platform-specific code follows #ifdef _WIN32 #include bool OSFindFilesResult::findNextFile() { BOOL result = FindNextFileW( findHandle_, &fileData_); if (!result) return false; filePath_ = directoryPath_ + String::FromWString(fileData_.cFileName); return true; } OSFindFilesResult osFindFilesInDirectoryMatchingPattern( CoreLib::Basic::String directoryPath, CoreLib::Basic::String pattern) { // TODO: add separator to end of directory path if needed String searchPath = directoryPath + pattern; OSFindFilesResult result; HANDLE findHandle = FindFirstFileW( searchPath.ToWString(), &result.fileData_); result.directoryPath_ = directoryPath; result.findHandle_ = findHandle; if (findHandle == INVALID_HANDLE_VALUE) { result.error_ = kOSError_FileNotFound; } else { result.filePath_ = directoryPath + String::FromWString(result.fileData_.cFileName); result.error_ = kOSError_None; } return result; } // OSProcessSpawner struct OSProcessSpawner_ReaderThreadInfo { HANDLE file; String output; }; static DWORD WINAPI osReaderThreadProc(LPVOID threadParam) { OSProcessSpawner_ReaderThreadInfo* info = (OSProcessSpawner_ReaderThreadInfo*)threadParam; HANDLE file = info->file; static const int kChunkSize = 1024; char buffer[kChunkSize]; StringBuilder outputBuilder; // We need to re-write the output to deal with line // endings, so we check for paired '\r' and '\n' // characters, which may span chunks. int prevChar = -1; for (;;) { DWORD bytesRead = 0; BOOL readResult = ReadFile(file, buffer, kChunkSize, &bytesRead, nullptr); if (!readResult || GetLastError() == ERROR_BROKEN_PIPE) { break; } // walk the buffer and rewrite to eliminate '\r' '\n' pairs char* readCursor = buffer; char const* end = buffer + bytesRead; char* writeCursor = buffer; while (readCursor != end) { int p = prevChar; int c = *readCursor++; prevChar = c; switch (c) { case '\r': case '\n': // swallow input if '\r' and '\n' appear in sequence if ((p ^ c) == ('\r' ^ '\n')) { // but don't swallow the next byte prevChar = -1; continue; } // always replace '\r' with '\n' c = '\n'; break; default: break; } *writeCursor++ = c; } bytesRead = (DWORD)(writeCursor - buffer); // Note: Current Spire CoreLib gives no way to know // the length of the buffer, so we ultimately have // to just assume null termination... outputBuilder.Append(buffer, bytesRead); } info->output = outputBuilder.ProduceString(); return 0; } void OSProcessSpawner::pushExecutableName( CoreLib::Basic::String executableName) { executableName_ = executableName; commandLine_.Append(executableName); } void OSProcessSpawner::pushArgument( CoreLib::Basic::String argument) { // TODO(tfoley): handle cases where arguments need some escaping commandLine_.Append(" "); commandLine_.Append(argument); } OSError OSProcessSpawner::spawnAndWaitForCompletion() { SECURITY_ATTRIBUTES securityAttributes; securityAttributes.nLength = sizeof(securityAttributes); securityAttributes.lpSecurityDescriptor = nullptr; securityAttributes.bInheritHandle = true; // create stdout pipe for child process HANDLE childStdOutReadTmp = nullptr; HANDLE childStdOutWrite = nullptr; if (!CreatePipe(&childStdOutReadTmp, &childStdOutWrite, &securityAttributes, 0)) { return kOSError_OperationFailed; } // create stderr pipe for child process HANDLE childStdErrReadTmp = nullptr; HANDLE childStdErrWrite = nullptr; if (!CreatePipe(&childStdErrReadTmp, &childStdErrWrite, &securityAttributes, 0)) { return kOSError_OperationFailed; } // create stdin pipe for child process HANDLE childStdInRead = nullptr; HANDLE childStdInWriteTmp = nullptr; if (!CreatePipe(&childStdInRead, &childStdInWriteTmp, &securityAttributes, 0)) { return kOSError_OperationFailed; } HANDLE currentProcess = GetCurrentProcess(); // create a non-inheritable duplicate of the stdout reader HANDLE childStdOutRead = nullptr; if (!DuplicateHandle( currentProcess, childStdOutReadTmp, currentProcess, &childStdOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS)) { return kOSError_OperationFailed; } if (!CloseHandle(childStdOutReadTmp)) { return kOSError_OperationFailed; } // create a non-inheritable duplicate of the stderr reader HANDLE childStdErrRead = nullptr; if (!DuplicateHandle( currentProcess, childStdErrReadTmp, currentProcess, &childStdErrRead, 0, FALSE, DUPLICATE_SAME_ACCESS)) { return kOSError_OperationFailed; } if (!CloseHandle(childStdErrReadTmp)) { return kOSError_OperationFailed; } // create a non-inheritable duplicate of the stdin writer HANDLE childStdInWrite = nullptr; if (!DuplicateHandle( currentProcess, childStdInWriteTmp, currentProcess, &childStdInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS)) { return kOSError_OperationFailed; } if (!CloseHandle(childStdInWriteTmp)) { return kOSError_OperationFailed; } // Now we can actually get around to starting a process PROCESS_INFORMATION processInfo; ZeroMemory(&processInfo, sizeof(processInfo)); // TODO: switch to proper wide-character versions of these... STARTUPINFOW startupInfo; ZeroMemory(&startupInfo, sizeof(startupInfo)); startupInfo.cb = sizeof(startupInfo); startupInfo.hStdError = childStdErrWrite; startupInfo.hStdOutput = childStdOutWrite; startupInfo.hStdInput = childStdInRead; startupInfo.dwFlags = STARTF_USESTDHANDLES; // `CreateProcess` requires write access to this, for some reason... BOOL success = CreateProcessW( executableName_.ToWString(), (LPWSTR)commandLine_.ToString().ToWString(), nullptr, nullptr, true, CREATE_NO_WINDOW, nullptr, // TODO: allow specifying environment variables? nullptr, &startupInfo, &processInfo); if (!success) { return kOSError_OperationFailed; } // close handles we are now done with CloseHandle(processInfo.hThread); CloseHandle(childStdOutWrite); CloseHandle(childStdErrWrite); CloseHandle(childStdInRead); // Create a thread to read from the child's stdout. OSProcessSpawner_ReaderThreadInfo stdOutThreadInfo; stdOutThreadInfo.file = childStdOutRead; HANDLE stdOutThread = CreateThread(nullptr, 0, &osReaderThreadProc, (LPVOID)&stdOutThreadInfo, 0, nullptr); // Create a thread to read from the child's stderr. OSProcessSpawner_ReaderThreadInfo stdErrThreadInfo; stdErrThreadInfo.file = childStdErrRead; HANDLE stdErrThread = CreateThread(nullptr, 0, &osReaderThreadProc, (LPVOID)&stdErrThreadInfo, 0, nullptr); // wait for the process to exit // TODO: set a timeout as a safety measure... WaitForSingleObject(processInfo.hProcess, INFINITE); // get exit code for process DWORD childExitCode = 0; if (!GetExitCodeProcess(processInfo.hProcess, &childExitCode)) { return kOSError_OperationFailed; } // wait for the reader threads WaitForSingleObject(stdOutThread, INFINITE); WaitForSingleObject(stdErrThread, INFINITE); CloseHandle(processInfo.hProcess); CloseHandle(childStdOutRead); CloseHandle(childStdErrRead); CloseHandle(childStdInWrite); standardOutput_ = stdOutThreadInfo.output; standardError_ = stdErrThreadInfo.output; resultCode_ = childExitCode; return kOSError_None; } #else // TODO(tfoley): write a default POSIX implementation #endif ================================================ FILE: Tests/SpireTestTool/os.h ================================================ // os.h #include "../../Source/CoreLib/LibIO.h" // This file encapsulates the platform-specific operations needed by the test // runner that are not already provided by the core Spire libs #ifdef _WIN32 // Include Windows header in a way that minimized namespace pollution. // TODO: We could try to avoid including this at all, but it would // mean trying to hide certain struct layouts, which would add // more dynamic allocation. #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include #undef WIN32_LEAN_AND_MEAN #undef NOMINMAX #else #endif // A simple set of error codes for possible runtime failures enum OSError { kOSError_None = 0, kOSError_InvalidArgument, kOSError_OperationFailed, kOSError_FileNotFound, }; // A helper type used during enumeration of files in a directory. struct OSFindFilesResult { CoreLib::Basic::String directoryPath_; CoreLib::Basic::String filePath_; #ifdef WIN32 HANDLE findHandle_; WIN32_FIND_DATAW fileData_; OSError error_; #else #endif bool findNextFile(); struct Iterator { OSFindFilesResult* context_; bool operator!=(Iterator other) const { return context_ != other.context_; } void operator++() { if (!context_->findNextFile()) { context_ = NULL; } } CoreLib::Basic::String const& operator*() const { return context_->filePath_; } }; Iterator begin() { Iterator result = { this }; return result; } Iterator end() { Iterator result = { NULL }; return result; } }; // Enumerate files in the given `directoryPath` that match the provided // `pattern` as a simplified regex for files to return (e.g., "*.txt") // and return a logical collection of the results // that can be iterated with a range-based `for` loop: // // for( auto file : osFindFilesInDirectoryMatchingPattern(dir, "*.txt")) // { ... } // // Each element in the range is a `CoreLib::Basic::String` representing the // path to a file in the directory. OSFindFilesResult osFindFilesInDirectoryMatchingPattern( CoreLib::Basic::String directoryPath, CoreLib::Basic::String pattern); // An `OSProcessSpawner` can be used to launch a process, and handles // putting together the arguments in the form required by the target // platform, as well as capturing any output from the process (both // standard output and standard error) as strings. struct OSProcessSpawner { // Set the executable name for the process to be spawned. // Note: this call must be made before any arguments are pushed. void pushExecutableName( CoreLib::Basic::String executableName); // Append an argument for the process to be spawned. void pushArgument( CoreLib::Basic::String argument); // Attempt to spawn the process, and wait for it to complete. // Returns an error if the attempt to spawn and/or wait fails, // but returns `kOSError_None` if the process is run to completion, // whether or not the process returns "successfully" (with a zero // result code); OSError spawnAndWaitForCompletion(); // If the process is successfully spawned and completes, then // the user can query the result code that the process produce // on exit, along with the output it wrote to stdout and stderr. typedef int ResultCode; ResultCode getResultCode() { return resultCode_; } CoreLib::Basic::String const& getStandardOutput() { return standardOutput_; } CoreLib::Basic::String const& getStandardError() { return standardError_; } // "private" data follows CoreLib::Basic::String standardOutput_; CoreLib::Basic::String standardError_; ResultCode resultCode_; #ifdef WIN32 CoreLib::Basic::String executableName_; CoreLib::Basic::StringBuilder commandLine_; #else #endif }; ================================================ FILE: test.bat ================================================ @echo off setlocal pushd %~dp0 :: TODO: ensure that everything is built? .\Source\Debug\SpireTestTool.exe