From f12c2552b3f494cbc8245edb90b32b93ca8a1539 Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Mon, 16 Oct 2017 13:12:11 -0700 Subject: Implement notion of a "container format" (#213) The big addition here is that the Slang "bytecode" is no longer treated as just a "code generation target" (`CodeGenTarget`) akin to DX bytecode (DXBC) or SPIR-V, but instead is a `ContainerFormat` that can be used to emit all the results of a compile request (well, currently just the IR-as-BC, but the intention is there). Getting to this goal involved some prior checkins that eliminated bogus "targets" that weren't really akin to SPIR-V or DXBC: `-target slang-ir-asm` and `-target reflection-json`. Those targets were really in place to support testing, and so they've been made more explicit testing/debug options. This change eliminates `-target slang-ir` and instead tries to allow the user to specify `-o foo.slang-module` as an output file name, that indicates the intention to output a "container" file that will wrap up all the generated code. I've also gone ahead and generalized the existing `-target` option so that we are actually building up a *list* of code generation targets. This is largely just a cleanup, since it forces code to be more aware of when it is doing something target-specific vs. target independent. For example, reflection layout information lives on a requested target, and not on the compile request as a whole, and similarly output code is per-target, per-entry-point. As a cleanup, I eliminated support for per-translation-unit output. This was vestigial code from back when I used to try and do HLSL generation for a whole translation unit instead of per-entry-point (which turned out to be a lot of complexity for little gain), and it was only being used in the `hello` example and the `render-test` test fixture - in both cases fixing it up was easy enough. I've stubbed out the old `spGetTranslationUnitSource` API, but haven't removed it yet. --- source/slang/slang.cpp | 192 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 135 insertions(+), 57 deletions(-) (limited to 'source/slang/slang.cpp') diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 6e57c104c..dff322c29 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -2,6 +2,7 @@ #include "../core/slang-io.h" #include "parameter-binding.h" +#include "lower-to-ir.h" #include "../slang/parser.h" #include "../slang/preprocessor.h" #include "../slang/reflection.h" @@ -175,11 +176,46 @@ void CompileRequest::parseTranslationUnit( void CompileRequest::checkAllTranslationUnits() { + // Iterate over all translation units and + // apply the semantic checking logic. for( auto& translationUnit : translationUnits ) { checkTranslationUnit(translationUnit.Ptr()); } } + +void CompileRequest::generateIR() +{ + // Our task in this function is to generate IR code + // for all of the declarations in the translation + // units that were loaded. + + // At the moment, use of the IR is not enabled by + // default, so we will skip this step unless + // the flag was set to op in. + if (!(compileFlags & SLANG_COMPILE_FLAG_USE_IR)) + return; + + // Each translation unit is its own little world + // for code generation (we are not trying to + // replicate the GLSL linkage model), and so + // we will generate IR for each (if needed) + // in isolation. + for( auto& translationUnit : translationUnits ) + { + // If the user opted out of semantic checking for + // the translation unit, then IR code generation + // is not in general even possible; there might + // be semantics errors (diagnosed or not) in the + // code, and we don't want to deal with those. + if (translationUnit->compileFlags & SLANG_COMPILE_FLAG_NO_CHECKING) + continue; + + // Okay, we seem to be in the clear now. + translationUnit->irModule = generateIRForTranslationUnit(translationUnit); + } +} + // Try to infer a single common source language for a request static SourceLanguage inferSourceLanguage(CompileRequest* request) { @@ -223,18 +259,18 @@ int CompileRequest::executeActionsInner() } // If no code-generation target was specified, then try to infer one from the source language, - // just to make sure we can do something reasonable when `reflection-json` is specified - if (Target == CodeGenTarget::Unknown) + // just to make sure we can do something reasonable when invoked from the command line. + if (targets.Count() == 0) { auto language = inferSourceLanguage(this); switch (language) { case SourceLanguage::HLSL: - Target = CodeGenTarget::DXBytecodeAssembly; + addTarget(CodeGenTarget::DXBytecode); break; case SourceLanguage::GLSL: - Target = CodeGenTarget::SPIRVAssembly; + addTarget(CodeGenTarget::SPIRV); break; default: @@ -242,40 +278,6 @@ int CompileRequest::executeActionsInner() } } -#if 0 - // If we are being asked to do pass-through, then we need to do that here... - if (passThrough != PassThroughMode::None) - { - for (auto& translationUnitOptions : Options.translationUnits) - { - switch (translationUnitOptions.sourceLanguage) - { - // We can pass-through code written in a native shading language - case SourceLanguage::GLSL: - case SourceLanguage::HLSL: - break; - - // All other translation units need to be skipped - default: - continue; - } - - auto sourceFile = translationUnitOptions.sourceFiles[0]; - auto sourceFilePath = sourceFile->path; - String source = sourceFile->content; - - auto translationUnitResult = passThrough( - source, - sourceFilePath, - Options, - translationUnitOptions); - - mResult.translationUnits.Add(translationUnitResult); - } - return 0; - } -#endif - // We only do parsing and semantic checking if we *aren't* doing // a pass-through compilation. // @@ -295,17 +297,29 @@ int CompileRequest::executeActionsInner() if (mSink.GetErrorCount() != 0) return 1; - // Now do shader parameter binding generation, which - // needs to be performed globally. - generateParameterBindings(this); + // Generate initial IR for all the translation + // units, if we are in a mode where IR is called for. + generateIR(); if (mSink.GetErrorCount() != 0) return 1; + + // For each code generation target generate + // parameter binding information. + // This step is done globaly, because all translation + // units and entry points need to agree on where + // parameters are allocated. + for (auto targetReq : targets) + { + generateParameterBindings(targetReq); + if (mSink.GetErrorCount() != 0) + return 1; + } } // If command line specifies to skip codegen, we exit here. // Note: this is a debugging option. -// if (shouldSkipCodegen) -// return 0; + if (shouldSkipCodegen) + return 0; // Generate output code, in whatever format was requested generateOutput(this); @@ -401,6 +415,19 @@ int CompileRequest::addEntryPoint( return (int) result; } +UInt CompileRequest::addTarget( + CodeGenTarget target) +{ + RefPtr targetReq = new TargetRequest(); + targetReq->compileRequest = this; + targetReq->target = target; + + UInt result = targets.Count(); + targets.Add(targetReq); + return (int) result; +} + + RefPtr CompileRequest::loadModule( Name* name, String const& path, @@ -713,9 +740,28 @@ SLANG_API void spSetCodeGenTarget( SlangCompileRequest* request, int target) { - REQ(request)->Target = (Slang::CodeGenTarget)target; + auto req = REQ(request); + req->targets.Clear(); + req->addTarget(Slang::CodeGenTarget(target)); } +SLANG_API void spAddCodeGenTarget( + SlangCompileRequest* request, + SlangCompileTarget target) +{ + auto req = REQ(request); + req->addTarget(Slang::CodeGenTarget(target)); +} + +SLANG_API void spSetOutputContainerFormat( + SlangCompileRequest* request, + SlangContainerFormat format) +{ + auto req = REQ(request); + req->containerFormat = Slang::ContainerFormat(format); +} + + SLANG_API void spSetPassThrough( SlangCompileRequest* request, SlangPassThrough passThrough) @@ -918,16 +964,8 @@ SLANG_API char const* spGetTranslationUnitSource( SlangCompileRequest* request, int translationUnitIndex) { - auto req = REQ(request); - return req->translationUnits[translationUnitIndex]->result.outputString.Buffer(); -} - -SLANG_API char const* spGetEntryPointSource( - SlangCompileRequest* request, - int entryPointIndex) -{ - auto req = REQ(request); - return req->entryPoints[entryPointIndex]->result.outputString.Buffer(); + fprintf(stderr, "DEPRECATED: spGetTranslationUnitSource()\n"); + return nullptr; } SLANG_API void const* spGetEntryPointCode( @@ -936,7 +974,14 @@ SLANG_API void const* spGetEntryPointCode( size_t* outSize) { auto req = REQ(request); - Slang::CompileResult& result = req->entryPoints[entryPointIndex]->result; + + // TODO: We should really accept a target index in this API + auto targetCount = req->targets.Count(); + if (targetCount == 0) + return nullptr; + auto targetReq = req->targets[0]; + + Slang::CompileResult& result = targetReq->entryPointResults[entryPointIndex]; void const* data = nullptr; size_t size = 0; @@ -962,15 +1007,48 @@ SLANG_API void const* spGetEntryPointCode( return data; } +SLANG_API char const* spGetEntryPointSource( + SlangCompileRequest* request, + int entryPointIndex) +{ + return (char const*) spGetEntryPointCode(request, entryPointIndex, nullptr); +} + +SLANG_API void const* spGetCompileRequestCode( + SlangCompileRequest* request, + size_t* outSize) +{ + auto req = REQ(request); + + void const* data = req->generatedBytecode.Buffer(); + size_t size = req->generatedBytecode.Count(); + + if(outSize) *outSize = size; + return data; +} + // Reflection API SLANG_API SlangReflection* spGetReflection( SlangCompileRequest* request) { if( !request ) return 0; - auto req = REQ(request); - return (SlangReflection*) req->layout.Ptr(); + + // Note(tfoley): The API signature doesn't let the client + // specify which target they want to access reflection + // information for, so for now we default to the first one. + // + // TODO: Add a new `spGetReflectionForTarget(req, targetIndex)` + // so that we can do this better, and make it clear that + // `spGetReflection()` is shorthand for `targetIndex == 0`. + // + auto targetCount = req->targets.Count(); + if (targetCount == 0) + return 0; + auto targetReq = req->targets[0]; + + return (SlangReflection*) targetReq->layout.Ptr(); } // ... rest of reflection API implementation is in `Reflection.cpp` -- cgit v1.2.3