summaryrefslogtreecommitdiffstats
path: root/source/slang/compiler.cpp
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2019-05-31 17:20:37 -0400
committerGitHub <noreply@github.com>2019-05-31 17:20:37 -0400
commit6cbc3929a54d37bd23cb5efa8e3320ba02f78b2f (patch)
tree5a23cb47782e9e2a77762c90dd35da1005eba8d0 /source/slang/compiler.cpp
parentb81ff3ef968d1cc4e954b31a1812b3c391d17b02 (diff)
Use slang- prefix on slang compiler and core source (#973)
* Prefixing source files in source/slang with slang- * Prefix source in source/slang with slang- prefix. * Rename core source files with slang- prefix. * Update project files. * Fix problems from automatic merge.
Diffstat (limited to 'source/slang/compiler.cpp')
-rw-r--r--source/slang/compiler.cpp1645
1 files changed, 0 insertions, 1645 deletions
diff --git a/source/slang/compiler.cpp b/source/slang/compiler.cpp
deleted file mode 100644
index 22c1d4cd8..000000000
--- a/source/slang/compiler.cpp
+++ /dev/null
@@ -1,1645 +0,0 @@
-// Compiler.cpp : Defines the entry point for the console application.
-//
-#include "../core/basic.h"
-#include "../core/platform.h"
-#include "../core/slang-io.h"
-#include "../core/slang-string-util.h"
-
-#include "compiler.h"
-#include "lexer.h"
-#include "lower-to-ir.h"
-#include "parameter-binding.h"
-#include "parser.h"
-#include "preprocessor.h"
-#include "syntax-visitors.h"
-#include "type-layout.h"
-#include "reflection.h"
-#include "emit.h"
-
-// Enable calling through to `fxc` or `dxc` to
-// generate code on Windows.
-#ifdef _WIN32
- #define WIN32_LEAN_AND_MEAN
- #define NOMINMAX
- #include <Windows.h>
- #undef WIN32_LEAN_AND_MEAN
- #undef NOMINMAX
- #include <d3dcompiler.h>
- #ifndef SLANG_ENABLE_DXBC_SUPPORT
- #define SLANG_ENABLE_DXBC_SUPPORT 1
- #endif
- #ifndef SLANG_ENABLE_DXIL_SUPPORT
- #define SLANG_ENABLE_DXIL_SUPPORT 1
- #endif
-#endif
-//
-// Otherwise, don't enable DXBC/DXIL by default:
-#ifndef SLANG_ENABLE_DXBC_SUPPORT
- #define SLANG_ENABLE_DXBC_SUPPORT 0
-#endif
-#ifndef SLANG_ENABLE_DXIL_SUPPORT
- #define SLANG_ENABLE_DXIL_SUPPORT 0
-#endif
-
-// Enable calling through to `glslang` on
-// all platforms.
-#ifndef SLANG_ENABLE_GLSLANG_SUPPORT
- #define SLANG_ENABLE_GLSLANG_SUPPORT 1
-#endif
-
-#if SLANG_ENABLE_GLSLANG_SUPPORT
-#include "../slang-glslang/slang-glslang.h"
-#endif
-
-// Includes to allow us to control console
-// output when writing assembly dumps.
-#include <fcntl.h>
-#ifdef _WIN32
-#include <io.h>
-#else
-#include <unistd.h>
-#endif
-
-#ifdef _MSC_VER
-#pragma warning(disable: 4996)
-#endif
-
-#ifdef CreateDirectory
-#undef CreateDirectory
-#endif
-
-namespace Slang
-{
-
- // CompileResult
-
- void CompileResult::append(CompileResult const& result)
- {
- // Find which to append to
- ResultFormat appendTo = ResultFormat::None;
-
- if (format == ResultFormat::None)
- {
- format = result.format;
- appendTo = result.format;
- }
- else if (format == result.format)
- {
- appendTo = format;
- }
-
- if (appendTo == ResultFormat::Text)
- {
- outputString.append(result.outputString.getBuffer());
- }
- else if (appendTo == ResultFormat::Binary)
- {
- outputBinary.addRange(result.outputBinary.getBuffer(), result.outputBinary.getCount());
- }
- }
-
- ComPtr<ISlangBlob> CompileResult::getBlob()
- {
- if(!blob)
- {
- switch(format)
- {
- case ResultFormat::None:
- default:
- break;
-
- case ResultFormat::Text:
- blob = StringUtil::createStringBlob(outputString);
- break;
-
- case ResultFormat::Binary:
- blob = createRawBlob(outputBinary.getBuffer(), outputBinary.getCount());
- break;
- }
- }
- return blob;
- }
-
- //
- // FrontEndEntryPointRequest
- //
-
- FrontEndEntryPointRequest::FrontEndEntryPointRequest(
- FrontEndCompileRequest* compileRequest,
- int translationUnitIndex,
- Name* name,
- Profile profile)
- : m_compileRequest(compileRequest)
- , m_translationUnitIndex(translationUnitIndex)
- , m_name(name)
- , m_profile(profile)
- {}
-
-
- TranslationUnitRequest* FrontEndEntryPointRequest::getTranslationUnit()
- {
- return getCompileRequest()->translationUnits[m_translationUnitIndex];
- }
-
- //
- // EntryPoint
- //
-
- RefPtr<EntryPoint> EntryPoint::create(
- DeclRef<FuncDecl> funcDeclRef,
- Profile profile)
- {
- RefPtr<EntryPoint> entryPoint = new EntryPoint(
- funcDeclRef.GetName(),
- profile,
- funcDeclRef);
- return entryPoint;
- }
-
- RefPtr<EntryPoint> EntryPoint::createDummyForPassThrough(
- Name* name,
- Profile profile)
- {
- RefPtr<EntryPoint> entryPoint = new EntryPoint(
- name,
- profile,
- DeclRef<FuncDecl>());
- return entryPoint;
- }
-
- EntryPoint::EntryPoint(
- Name* name,
- Profile profile,
- DeclRef<FuncDecl> funcDeclRef)
- : m_name(name)
- , m_profile(profile)
- , m_funcDeclRef(funcDeclRef)
- {
- // In order for later code generation to work, we need to track what
- // modules each entry point depends on. We will build up the dependency
- // list here when an `EntryPoint` gets created.
- //
- // We know an entry point depends on the module that declared the
- // entry-point function itself.
- //
- // Note: we are carefully handling the case where `module` could
- // be null, becase of "dummy" entry points created for pass-through
- // compilation.
- //
- if(auto module = getModule())
- {
- m_dependencyList.addDependency(module);
- }
- //
- // TODO: We also need to include the modules needed by any generic
- // arguments in the dependency list, since in the general case they
- // might come from modules other than the one defining the entry point.
-
- // The following is a bit of a hack.
- //
- // Back-end code generation relies on us having computed layouts for all tagged
- // unions that end up being used in the code, which means we need a way to find
- // all such types that get used in a program (and the stuff it imports).
- //
- // For now we are assuming a tagged union type only comes into existence
- // as a (top-level) argument for a generic type parameter, so that we
- // can check for them here and cache them on the entry point.
- //
- // A longer-term strategy might need to consider any (tagged or untagged)
- // union types that get used inside of a module, and also take
- // those lists into account.
- //
- // An even longer-term strategy would be to allow type layout to
- // be performed on IR types, so taht we don't need to have front-end
- // code worrying about this stuff.
- //
- for( auto subst = funcDeclRef.substitutions.substitutions; subst; subst = subst->outer )
- {
- if( auto genericSubst = as<GenericSubstitution>(subst) )
- {
- for( auto arg : genericSubst->args )
- {
- if( auto taggedUnionType = as<TaggedUnionType>(arg) )
- {
- m_taggedUnionTypes.add(taggedUnionType);
- }
- }
- }
- }
-
- // Collect any existential-type parameters used by the entry point
- //
- _collectShaderParams();
- }
-
- Module* EntryPoint::getModule()
- {
- return Slang::getModule(getFuncDecl());
- }
-
- Linkage* EntryPoint::getLinkage()
- {
- return getModule()->getLinkage();
- }
-
- //
-
- Profile Profile::LookUp(char const* name)
- {
- #define PROFILE(TAG, NAME, STAGE, VERSION) if(strcmp(name, #NAME) == 0) return Profile::TAG;
- #define PROFILE_ALIAS(TAG, DEF, NAME) if(strcmp(name, #NAME) == 0) return Profile::TAG;
- #include "profile-defs.h"
-
- return Profile::Unknown;
- }
-
- char const* Profile::getName()
- {
- switch( raw )
- {
- default:
- return "unknown";
-
- #define PROFILE(TAG, NAME, STAGE, VERSION) case Profile::TAG: return #NAME;
- #define PROFILE_ALIAS(TAG, DEF, NAME) /* empty */
- #include "profile-defs.h"
- }
- }
-
- Stage findStageByName(String const& name)
- {
- static const struct
- {
- char const* name;
- Stage stage;
- } kStages[] =
- {
- #define PROFILE_STAGE(ID, NAME, ENUM) \
- { #NAME, Stage::ID },
-
- #define PROFILE_STAGE_ALIAS(ID, NAME, VAL) \
- { #NAME, Stage::ID },
-
- #include "profile-defs.h"
- };
-
- for(auto entry : kStages)
- {
- if(name == entry.name)
- {
- return entry.stage;
- }
- }
-
- return Stage::Unknown;
- }
-
- SlangResult checkExternalCompilerSupport(Session* session, PassThroughMode passThrough)
- {
- switch (passThrough)
- {
- case PassThroughMode::None:
- {
- // If no pass through -> that will always work!
- return SLANG_OK;
- }
- case PassThroughMode::dxc:
- {
-#if SLANG_ENABLE_DXIL_SUPPORT
- // Must have dxc
- return session->getOrLoadSharedLibrary(SharedLibraryType::Dxc, nullptr) ? SLANG_OK : SLANG_E_NOT_FOUND;
-#endif
- break;
- }
- case PassThroughMode::fxc:
- {
-#if SLANG_ENABLE_DXBC_SUPPORT
- // Must have fxc
- return session->getOrLoadSharedLibrary(SharedLibraryType::Fxc, nullptr) ? SLANG_OK : SLANG_E_NOT_FOUND;
-#endif
- break;
- }
- case PassThroughMode::glslang:
- {
-#if SLANG_ENABLE_GLSLANG_SUPPORT
- return session->getOrLoadSharedLibrary(Slang::SharedLibraryType::Glslang, nullptr) ? SLANG_OK : SLANG_E_NOT_FOUND;
-#endif
- break;
- }
- }
- return SLANG_E_NOT_IMPLEMENTED;
- }
-
- static PassThroughMode _getExternalCompilerRequiredForTarget(CodeGenTarget target)
- {
- switch (target)
- {
- case CodeGenTarget::None:
- {
- return PassThroughMode::None;
- }
- case CodeGenTarget::GLSL:
- case CodeGenTarget::GLSL_Vulkan:
- case CodeGenTarget::GLSL_Vulkan_OneDesc:
- {
- // Can always output GLSL
- return PassThroughMode::None;
- }
- case CodeGenTarget::HLSL:
- {
- // Can always output HLSL
- return PassThroughMode::None;
- }
- case CodeGenTarget::SPIRVAssembly:
- case CodeGenTarget::SPIRV:
- {
- return PassThroughMode::glslang;
- }
- case CodeGenTarget::DXBytecode:
- case CodeGenTarget::DXBytecodeAssembly:
- {
- return PassThroughMode::fxc;
- }
- case CodeGenTarget::DXIL:
- case CodeGenTarget::DXILAssembly:
- {
- return PassThroughMode::dxc;
- }
- case CodeGenTarget::CPPSource:
- case CodeGenTarget::CSource:
- {
- // Don't need an external compiler to output C and C++ code
- return PassThroughMode::None;
- }
-
- default: break;
- }
-
- SLANG_ASSERT(!"Unhandled target");
- return PassThroughMode::None;
- }
-
- SlangResult checkCompileTargetSupport(Session* session, CodeGenTarget target)
- {
- const PassThroughMode mode = _getExternalCompilerRequiredForTarget(target);
- return (mode != PassThroughMode::None) ?
- checkExternalCompilerSupport(session, mode) :
- SLANG_OK;
- }
-
- //
-
- /// If there is a pass-through compile going on, find the translation unit for the given entry point.
- TranslationUnitRequest* findPassThroughTranslationUnit(
- EndToEndCompileRequest* endToEndReq,
- Int entryPointIndex)
- {
- // If there isn't an end-to-end compile going on,
- // there can be no pass-through.
- //
- if(!endToEndReq) return nullptr;
-
- // And if pass-through isn't set, we don't need
- // access to the translation unit.
- //
- if(endToEndReq->passThrough == PassThroughMode::None) return nullptr;
-
- auto frontEndReq = endToEndReq->getFrontEndReq();
- auto entryPointReq = frontEndReq->getEntryPointReq(entryPointIndex);
- auto translationUnit = entryPointReq->getTranslationUnit();
- return translationUnit;
- }
-
- String emitHLSLForEntryPoint(
- BackEndCompileRequest* compileRequest,
- EntryPoint* entryPoint,
- Int entryPointIndex,
- TargetRequest* targetReq,
- EndToEndCompileRequest* endToEndReq)
- {
- if(auto translationUnit = findPassThroughTranslationUnit(endToEndReq, entryPointIndex))
- {
- // Generate a string that includes the content of
- // the source file(s), along with a line directive
- // to ensure that we get reasonable messages
- // from the downstream compiler when in pass-through
- // mode.
-
- StringBuilder codeBuilder;
- for(auto sourceFile : translationUnit->getSourceFiles())
- {
- codeBuilder << "#line 1 \"";
-
- const String& path = sourceFile->getPathInfo().foundPath;
-
- for(auto c : path)
- {
- char buffer[] = { c, 0 };
- switch(c)
- {
- default:
- codeBuilder << buffer;
- break;
-
- case '\\':
- codeBuilder << "\\\\";
- }
- }
- codeBuilder << "\"\n";
-
- codeBuilder << sourceFile->getContent() << "\n";
- }
-
- return codeBuilder.ProduceString();
- }
- else
- {
- return emitEntryPoint(
- compileRequest,
- entryPoint,
- CodeGenTarget::HLSL,
- targetReq);
- }
- }
-
- String emitGLSLForEntryPoint(
- BackEndCompileRequest* compileRequest,
- EntryPoint* entryPoint,
- Int entryPointIndex,
- TargetRequest* targetReq,
- EndToEndCompileRequest* endToEndReq)
- {
- if(auto translationUnit = findPassThroughTranslationUnit(endToEndReq, entryPointIndex))
- {
- // Generate a string that includes the content of
- // the source file(s), along with a line directive
- // to ensure that we get reasonable messages
- // from the downstream compiler when in pass-through
- // mode.
-
- StringBuilder codeBuilder;
- int translationUnitCounter = 0;
- for(auto sourceFile : translationUnit->getSourceFiles())
- {
- int translationUnitIndex = translationUnitCounter++;
-
- // We want to output `#line` directives, but we need
- // to skip this for the first file, since otherwise
- // some GLSL implementations will get tripped up by
- // not having the `#version` directive be the first
- // thing in the file.
- if(translationUnitIndex != 0)
- {
- codeBuilder << "#line 1 " << translationUnitIndex << "\n";
- }
- codeBuilder << sourceFile->getContent() << "\n";
- }
-
- return codeBuilder.ProduceString();
- }
- else
- {
- // TODO(tfoley): need to pass along the entry point
- // so that we properly emit it as the `main` function.
- return emitEntryPoint(
- compileRequest,
- entryPoint,
- CodeGenTarget::GLSL,
- targetReq);
- }
- }
-
- String GetHLSLProfileName(Profile profile)
- {
- switch( profile.getFamily() )
- {
- case ProfileFamily::DX:
- // Profile version is a DX one, so stick with it.
- break;
-
- default:
- // Profile is a non-DX profile family, so we need to try
- // to clobber it with something to get a default.
- //
- // TODO: This is a huge hack...
- profile.setVersion(ProfileVersion::DX_5_0);
- break;
- }
-
- char const* stagePrefix = nullptr;
- switch( profile.GetStage() )
- {
- // Note: All of the raytracing-related stages require
- // compiling for a `lib_*` profile, even when only a
- // single entry point is present.
- //
- // We also go ahead and use this target in any case
- // where we don't know the actual stage to compiel for,
- // as a fallback option.
- //
- // TODO: We also want to use this option when compiling
- // multiple entry points to a DXIL library.
- //
- default:
- stagePrefix = "lib";
- break;
-
- // The traditional rasterization pipeline and compute
- // shaders all have custom profile names that identify
- // both the stage and shader model, which need to be
- // used when compiling a single entry point.
- //
- #define CASE(NAME, PREFIX) case Stage::NAME: stagePrefix = #PREFIX; break
- CASE(Vertex, vs);
- CASE(Hull, hs);
- CASE(Domain, ds);
- CASE(Geometry, gs);
- CASE(Fragment, ps);
- CASE(Compute, cs);
- #undef CASE
- }
-
- char const* versionSuffix = nullptr;
- switch(profile.GetVersion())
- {
- #define CASE(TAG, SUFFIX) case ProfileVersion::TAG: versionSuffix = #SUFFIX; break
- CASE(DX_4_0, _4_0);
- CASE(DX_4_0_Level_9_0, _4_0_level_9_0);
- CASE(DX_4_0_Level_9_1, _4_0_level_9_1);
- CASE(DX_4_0_Level_9_3, _4_0_level_9_3);
- CASE(DX_4_1, _4_1);
- CASE(DX_5_0, _5_0);
- CASE(DX_5_1, _5_1);
- CASE(DX_6_0, _6_0);
- CASE(DX_6_1, _6_1);
- CASE(DX_6_2, _6_2);
- CASE(DX_6_3, _6_3);
- #undef CASE
-
- default:
- return "unknown";
- }
-
- String result;
- result.append(stagePrefix);
- result.append(versionSuffix);
- return result;
- }
-
- void reportExternalCompileError(const char* compilerName, SlangResult res, const UnownedStringSlice& diagnostic, DiagnosticSink* sink)
- {
- StringBuilder builder;
- if (compilerName)
- {
- builder << compilerName << ": ";
- }
-
- if (diagnostic.size() > 0)
- {
- builder.Append(diagnostic);
- }
-
- if (SLANG_FAILED(res) && res != SLANG_FAIL)
- {
- {
- char tmp[17];
- sprintf_s(tmp, SLANG_COUNT_OF(tmp), "0x%08x", uint32_t(res));
- builder << "Result(" << tmp << ") ";
- }
-
- PlatformUtil::appendResult(res, builder);
- }
-
- // TODO(tfoley): need a better policy for how we translate diagnostics
- // back into the Slang world (although we should always try to generate
- // HLSL that doesn't produce any diagnostics...)
- sink->diagnoseRaw(SLANG_FAILED(res) ? Severity::Error : Severity::Warning, builder.getUnownedSlice());
- }
-
- static String _getDisplayPath(DiagnosticSink* sink, SourceFile* sourceFile)
- {
- if (sink->flags & DiagnosticSink::Flag::VerbosePath)
- {
- return sourceFile->calcVerbosePath();
- }
- else
- {
- return sourceFile->getPathInfo().foundPath;
- }
- }
-
- String calcSourcePathForEntryPoint(
- EndToEndCompileRequest* endToEndReq,
- UInt entryPointIndex)
- {
- auto translationUnitRequest = findPassThroughTranslationUnit(endToEndReq, entryPointIndex);
- if(!translationUnitRequest)
- return "slang-generated";
-
- auto sink = endToEndReq->getSink();
-
- const auto& sourceFiles = translationUnitRequest->getSourceFiles();
-
- const Index numSourceFiles = sourceFiles.getCount();
-
- switch (numSourceFiles)
- {
- case 0: return "unknown";
- case 1: return _getDisplayPath(sink, sourceFiles[0]);
- default:
- {
- StringBuilder builder;
- builder << _getDisplayPath(sink, sourceFiles[0]);
- for (int i = 1; i < numSourceFiles; ++i)
- {
- builder << ";" << _getDisplayPath(sink, sourceFiles[i]);
- }
- return builder;
- }
- }
- }
-
-#if SLANG_ENABLE_DXBC_SUPPORT
-
- static UnownedStringSlice _getSlice(ID3DBlob* blob)
- {
- if (blob)
- {
- const char* chars = (const char*)blob->GetBufferPointer();
- size_t len = blob->GetBufferSize();
- len -= size_t(len > 0 && chars[len - 1] == 0);
- return UnownedStringSlice(chars, len);
- }
- return UnownedStringSlice();
- }
-
- SlangResult emitDXBytecodeForEntryPoint(
- BackEndCompileRequest* compileRequest,
- EntryPoint* entryPoint,
- Int entryPointIndex,
- TargetRequest* targetReq,
- EndToEndCompileRequest* endToEndReq,
- List<uint8_t>& byteCodeOut)
- {
- byteCodeOut.clear();
-
- auto session = compileRequest->getSession();
- auto sink = compileRequest->getSink();
-
- auto compileFunc = (pD3DCompile)session->getSharedLibraryFunc(Session::SharedLibraryFuncType::Fxc_D3DCompile, sink);
- if (!compileFunc)
- {
- return SLANG_FAIL;
- }
-
- auto hlslCode = emitHLSLForEntryPoint(compileRequest, entryPoint, entryPointIndex, targetReq, endToEndReq);
- maybeDumpIntermediate(compileRequest, hlslCode.getBuffer(), CodeGenTarget::HLSL);
-
- auto profile = getEffectiveProfile(entryPoint, targetReq);
-
- // If we have been invoked in a pass-through mode, then we need to make sure
- // that the downstream compiler sees whatever options were passed to Slang
- // via the command line or API.
- //
- // TODO: more pieces of information should be added here as needed.
- //
- List<D3D_SHADER_MACRO> dxMacrosStorage;
- D3D_SHADER_MACRO const* dxMacros = nullptr;
- if(auto translationUnit = findPassThroughTranslationUnit(endToEndReq, entryPointIndex))
- {
- for( auto& define : translationUnit->compileRequest->preprocessorDefinitions )
- {
- D3D_SHADER_MACRO dxMacro;
- dxMacro.Name = define.Key.getBuffer();
- dxMacro.Definition = define.Value.getBuffer();
- dxMacrosStorage.add(dxMacro);
- }
- for( auto& define : translationUnit->preprocessorDefinitions )
- {
- D3D_SHADER_MACRO dxMacro;
- dxMacro.Name = define.Key.getBuffer();
- dxMacro.Definition = define.Value.getBuffer();
- dxMacrosStorage.add(dxMacro);
- }
- D3D_SHADER_MACRO nullTerminator = { 0, 0 };
- dxMacrosStorage.add(nullTerminator);
-
- dxMacros = dxMacrosStorage.getBuffer();
- }
-
- DWORD flags = 0;
-
- switch( targetReq->floatingPointMode )
- {
- default:
- break;
-
- case FloatingPointMode::Precise:
- flags |= D3DCOMPILE_IEEE_STRICTNESS;
- break;
- }
-
- // Some of the `D3DCOMPILE_*` constants aren't available in all
- // versions of `d3dcompiler.h`, so we define them here just in case
- #ifndef D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES
- #define D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES (1 << 20)
- #endif
-
- #ifndef D3DCOMPILE_ALL_RESOURCES_BOUND
- #define D3DCOMPILE_ALL_RESOURCES_BOUND (1 << 21)
- #endif
-
- flags |= D3DCOMPILE_ENABLE_STRICTNESS;
- flags |= D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES;
-
- auto linkage = compileRequest->getLinkage();
- switch( linkage->optimizationLevel )
- {
- default:
- break;
-
- case OptimizationLevel::None: flags |= D3DCOMPILE_OPTIMIZATION_LEVEL0; break;
- case OptimizationLevel::Default: flags |= D3DCOMPILE_OPTIMIZATION_LEVEL1; break;
- case OptimizationLevel::High: flags |= D3DCOMPILE_OPTIMIZATION_LEVEL2; break;
- case OptimizationLevel::Maximal: flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3; break;
- }
-
- switch( linkage->debugInfoLevel )
- {
- case DebugInfoLevel::None:
- break;
-
- default:
- flags |= D3DCOMPILE_DEBUG;
- break;
- }
-
- const String sourcePath = calcSourcePathForEntryPoint(endToEndReq, entryPointIndex);
-
- ComPtr<ID3DBlob> codeBlob;
- ComPtr<ID3DBlob> diagnosticsBlob;
- HRESULT hr = compileFunc(
- hlslCode.begin(),
- hlslCode.getLength(),
- sourcePath.getBuffer(),
- dxMacros,
- nullptr,
- getText(entryPoint->getName()).begin(),
- GetHLSLProfileName(profile).getBuffer(),
- flags,
- 0, // unused: effect flags
- codeBlob.writeRef(),
- diagnosticsBlob.writeRef());
-
- if (codeBlob && SLANG_SUCCEEDED(hr))
- {
- byteCodeOut.addRange((uint8_t const*)codeBlob->GetBufferPointer(), (int)codeBlob->GetBufferSize());
- }
-
- if (FAILED(hr))
- {
- reportExternalCompileError("fxc", hr, _getSlice(diagnosticsBlob), sink);
- }
-
- return hr;
- }
-
- SlangResult dissassembleDXBC(
- BackEndCompileRequest* compileRequest,
- void const* data,
- size_t size,
- String& assemOut)
- {
- assemOut = String();
-
- auto session = compileRequest->getSession();
- auto sink = compileRequest->getSink();
-
- auto disassembleFunc = (pD3DDisassemble)session->getSharedLibraryFunc(Session::SharedLibraryFuncType::Fxc_D3DDisassemble, sink);
- if (!disassembleFunc)
- {
- return SLANG_E_NOT_FOUND;
- }
-
- if (!data || !size)
- {
- return SLANG_FAIL;
- }
-
- ComPtr<ID3DBlob> codeBlob;
- SlangResult res = disassembleFunc(data, size, 0, nullptr, codeBlob.writeRef());
-
- if (codeBlob)
- {
- assemOut = _getSlice(codeBlob);
- }
- if (FAILED(res))
- {
- // TODO(tfoley): need to figure out what to diagnose here...
- reportExternalCompileError("fxc", res, UnownedStringSlice(), sink);
- }
-
- return res;
- }
-
- SlangResult emitDXBytecodeAssemblyForEntryPoint(
- BackEndCompileRequest* compileRequest,
- EntryPoint* entryPoint,
- Int entryPointIndex,
- TargetRequest* targetReq,
- EndToEndCompileRequest* endToEndReq,
- String& assemOut)
- {
-
- List<uint8_t> dxbc;
- SLANG_RETURN_ON_FAIL(emitDXBytecodeForEntryPoint(
- compileRequest,
- entryPoint,
- entryPointIndex,
- targetReq,
- endToEndReq,
- dxbc));
- if (!dxbc.getCount())
- {
- return SLANG_FAIL;
- }
- return dissassembleDXBC(compileRequest, dxbc.getBuffer(), dxbc.getCount(), assemOut);
- }
-#endif
-
-#if SLANG_ENABLE_DXIL_SUPPORT
-
-// Implementations in `dxc-support.cpp`
-
-SlangResult emitDXILForEntryPointUsingDXC(
- BackEndCompileRequest* compileRequest,
- EntryPoint* entryPoint,
- Int entryPointIndex,
- TargetRequest* targetReq,
- EndToEndCompileRequest* endToEndReq,
- List<uint8_t>& outCode);
-
-SlangResult dissassembleDXILUsingDXC(
- BackEndCompileRequest* compileRequest,
- void const* data,
- size_t size,
- String& stringOut);
-
-#endif
-
-#if SLANG_ENABLE_GLSLANG_SUPPORT
- SlangResult invokeGLSLCompiler(
- BackEndCompileRequest* slangCompileRequest,
- glslang_CompileRequest& request)
- {
- Session* session = slangCompileRequest->getSession();
- auto sink = slangCompileRequest->getSink();
-
- auto glslang_compile = (glslang_CompileFunc)session->getSharedLibraryFunc(Session::SharedLibraryFuncType::Glslang_Compile, sink);
- if (!glslang_compile)
- {
- return SLANG_FAIL;
- }
-
- StringBuilder diagnosticOutput;
-
- auto diagnosticOutputFunc = [](void const* data, size_t size, void* userData)
- {
- (*(StringBuilder*)userData).append((char const*)data, (char const*)data + size);
- };
-
- request.diagnosticFunc = diagnosticOutputFunc;
- request.diagnosticUserData = &diagnosticOutput;
-
- int err = glslang_compile(&request);
-
- if (err)
- {
- reportExternalCompileError("glslang", SLANG_FAIL, diagnosticOutput.getUnownedSlice(), sink);
- return SLANG_FAIL;
- }
-
- return SLANG_OK;
- }
-
- SlangResult dissassembleSPIRV(
- BackEndCompileRequest* slangRequest,
- void const* data,
- size_t size,
- String& stringOut)
- {
- stringOut = String();
-
- String output;
- auto outputFunc = [](void const* data, size_t size, void* userData)
- {
- (*(String*)userData).append((char const*)data, (char const*)data + size);
- };
-
- glslang_CompileRequest request;
- request.action = GLSLANG_ACTION_DISSASSEMBLE_SPIRV;
-
- request.sourcePath = nullptr;
-
- request.inputBegin = data;
- request.inputEnd = (char*)data + size;
-
- request.outputFunc = outputFunc;
- request.outputUserData = &output;
-
- SLANG_RETURN_ON_FAIL(invokeGLSLCompiler(slangRequest, request));
-
- stringOut = output;
- return SLANG_OK;
- }
-
- SlangResult emitSPIRVForEntryPoint(
- BackEndCompileRequest* slangRequest,
- EntryPoint* entryPoint,
- Int entryPointIndex,
- TargetRequest* targetReq,
- EndToEndCompileRequest* endToEndReq,
- List<uint8_t>& spirvOut)
- {
- spirvOut.clear();
-
- String rawGLSL = emitGLSLForEntryPoint(
- slangRequest,
- entryPoint,
- entryPointIndex,
- targetReq,
- endToEndReq);
- maybeDumpIntermediate(slangRequest, rawGLSL.getBuffer(), CodeGenTarget::GLSL);
-
- auto outputFunc = [](void const* data, size_t size, void* userData)
- {
- ((List<uint8_t>*)userData)->addRange((uint8_t*)data, size);
- };
-
- const String sourcePath = calcSourcePathForEntryPoint(endToEndReq, entryPointIndex);
-
- glslang_CompileRequest request;
- request.action = GLSLANG_ACTION_COMPILE_GLSL_TO_SPIRV;
- request.sourcePath = sourcePath.getBuffer();
- request.slangStage = (SlangStage)entryPoint->getStage();
-
- request.inputBegin = rawGLSL.begin();
- request.inputEnd = rawGLSL.end();
-
- request.outputFunc = outputFunc;
- request.outputUserData = &spirvOut;
-
- SLANG_RETURN_ON_FAIL(invokeGLSLCompiler(slangRequest, request));
- return SLANG_OK;
- }
-
- SlangResult emitSPIRVAssemblyForEntryPoint(
- BackEndCompileRequest* slangRequest,
- EntryPoint* entryPoint,
- Int entryPointIndex,
- TargetRequest* targetReq,
- EndToEndCompileRequest* endToEndReq,
- String& assemblyOut)
- {
- List<uint8_t> spirv;
- SLANG_RETURN_ON_FAIL(emitSPIRVForEntryPoint(
- slangRequest,
- entryPoint,
- entryPointIndex,
- targetReq,
- endToEndReq,
- spirv));
-
- if (spirv.getCount() == 0)
- return SLANG_FAIL;
-
- return dissassembleSPIRV(slangRequest, spirv.begin(), spirv.getCount(), assemblyOut);
- }
-#endif
-
- // Do emit logic for a single entry point
- CompileResult emitEntryPoint(
- BackEndCompileRequest* compileRequest,
- EntryPoint* entryPoint,
- Int entryPointIndex,
- TargetRequest* targetReq,
- EndToEndCompileRequest* endToEndReq)
- {
- CompileResult result;
-
- auto target = targetReq->target;
-
- switch (target)
- {
- case CodeGenTarget::HLSL:
- {
- String code = emitHLSLForEntryPoint(
- compileRequest,
- entryPoint,
- entryPointIndex,
- targetReq,
- endToEndReq);
- maybeDumpIntermediate(compileRequest, code.getBuffer(), target);
- result = CompileResult(code);
- }
- break;
-
- case CodeGenTarget::GLSL:
- {
- String code = emitGLSLForEntryPoint(
- compileRequest,
- entryPoint,
- entryPointIndex,
- targetReq,
- endToEndReq);
- maybeDumpIntermediate(compileRequest, code.getBuffer(), target);
- result = CompileResult(code);
- }
- break;
-
- case CodeGenTarget::CPPSource:
- case CodeGenTarget::CSource:
- {
- return emitEntryPoint(
- compileRequest,
- entryPoint,
- target,
- targetReq);
- }
- break;
-
-#if SLANG_ENABLE_DXBC_SUPPORT
- case CodeGenTarget::DXBytecode:
- {
- List<uint8_t> code;
- if (SLANG_SUCCEEDED(emitDXBytecodeForEntryPoint(
- compileRequest,
- entryPoint,
- entryPointIndex,
- targetReq,
- endToEndReq,
- code)))
- {
- maybeDumpIntermediate(compileRequest, code.getBuffer(), code.getCount(), target);
- result = CompileResult(code);
- }
- }
- break;
-
- case CodeGenTarget::DXBytecodeAssembly:
- {
- String code;
- if (SLANG_SUCCEEDED(emitDXBytecodeAssemblyForEntryPoint(
- compileRequest,
- entryPoint,
- entryPointIndex,
- targetReq,
- endToEndReq,
- code)))
- {
- maybeDumpIntermediate(compileRequest, code.getBuffer(), target);
- result = CompileResult(code);
- }
- }
- break;
-#endif
-
-#if SLANG_ENABLE_DXIL_SUPPORT
- case CodeGenTarget::DXIL:
- {
- List<uint8_t> code;
- if (SLANG_SUCCEEDED(emitDXILForEntryPointUsingDXC(
- compileRequest,
- entryPoint,
- entryPointIndex,
- targetReq,
- endToEndReq,
- code)))
- {
- maybeDumpIntermediate(compileRequest, code.getBuffer(), code.getCount(), target);
- result = CompileResult(code);
- }
- }
- break;
-
- case CodeGenTarget::DXILAssembly:
- {
- List<uint8_t> code;
- if (SLANG_SUCCEEDED(emitDXILForEntryPointUsingDXC(
- compileRequest,
- entryPoint,
- entryPointIndex,
- targetReq,
- endToEndReq,
- code)))
- {
- String assembly;
- dissassembleDXILUsingDXC(
- compileRequest,
- code.getBuffer(),
- code.getCount(),
- assembly);
-
- maybeDumpIntermediate(compileRequest, assembly.getBuffer(), target);
-
- result = CompileResult(assembly);
- }
- }
- break;
-#endif
-
- case CodeGenTarget::SPIRV:
- {
- List<uint8_t> code;
- if (SLANG_SUCCEEDED(emitSPIRVForEntryPoint(
- compileRequest,
- entryPoint,
- entryPointIndex,
- targetReq,
- endToEndReq,
- code)))
- {
- maybeDumpIntermediate(compileRequest, code.getBuffer(), code.getCount(), target);
- result = CompileResult(code);
- }
- }
- break;
-
- case CodeGenTarget::SPIRVAssembly:
- {
- String code;
- if (SLANG_SUCCEEDED(emitSPIRVAssemblyForEntryPoint(
- compileRequest,
- entryPoint,
- entryPointIndex,
- targetReq,
- endToEndReq,
- code)))
- {
- maybeDumpIntermediate(compileRequest, code.getBuffer(), target);
- result = CompileResult(code);
- }
- }
- break;
-
- case CodeGenTarget::None:
- // The user requested no output
- break;
-
- // Note(tfoley): We currently hit this case when compiling the stdlib
- case CodeGenTarget::Unknown:
- break;
-
- default:
- SLANG_UNEXPECTED("unhandled code generation target");
- break;
- }
-
- return result;
- }
-
- enum class OutputFileKind
- {
- Text,
- Binary,
- };
-
- static void writeOutputFile(
- BackEndCompileRequest* compileRequest,
- FILE* file,
- String const& path,
- void const* data,
- size_t size)
- {
- size_t count = fwrite(data, size, 1, file);
- if (count != 1)
- {
- compileRequest->getSink()->diagnose(
- SourceLoc(),
- Diagnostics::cannotWriteOutputFile,
- path);
- }
- }
-
- static void writeOutputFile(
- BackEndCompileRequest* compileRequest,
- ISlangWriter* writer,
- String const& path,
- void const* data,
- size_t size)
- {
-
- if (SLANG_FAILED(writer->write((const char*)data, size)))
- {
- compileRequest->getSink()->diagnose(
- SourceLoc(),
- Diagnostics::cannotWriteOutputFile,
- path);
- }
- }
-
- static void writeOutputFile(
- BackEndCompileRequest* compileRequest,
- String const& path,
- void const* data,
- size_t size,
- OutputFileKind kind)
- {
- FILE* file = fopen(
- path.getBuffer(),
- kind == OutputFileKind::Binary ? "wb" : "w");
- if (!file)
- {
- compileRequest->getSink()->diagnose(
- SourceLoc(),
- Diagnostics::cannotWriteOutputFile,
- path);
- return;
- }
-
- writeOutputFile(compileRequest, file, path, data, size);
- fclose(file);
- }
-
- static void writeEntryPointResultToFile(
- BackEndCompileRequest* compileRequest,
- EntryPoint* entryPoint,
- String const& outputPath,
- CompileResult const& result)
- {
- SLANG_UNUSED(entryPoint);
-
- switch (result.format)
- {
- case ResultFormat::Text:
- {
- auto text = result.outputString;
- writeOutputFile(compileRequest,
- outputPath,
- text.begin(),
- text.end() - text.begin(),
- OutputFileKind::Text);
- }
- break;
-
- case ResultFormat::Binary:
- {
- auto& data = result.outputBinary;
- writeOutputFile(compileRequest,
- outputPath,
- data.begin(),
- data.end() - data.begin(),
- OutputFileKind::Binary);
- }
- break;
-
- default:
- SLANG_UNEXPECTED("unhandled output format");
- break;
- }
-
- }
-
- static void writeOutputToConsole(
- ISlangWriter* writer,
- String const& text)
- {
- writer->write(text.getBuffer(), text.getLength());
- }
-
- static void writeEntryPointResultToStandardOutput(
- EndToEndCompileRequest* compileRequest,
- EntryPoint* entryPoint,
- TargetRequest* targetReq,
- CompileResult const& result)
- {
- SLANG_UNUSED(entryPoint);
-
- ISlangWriter* writer = compileRequest->getWriter(WriterChannel::StdOutput);
- auto backEndReq = compileRequest->getBackEndReq();
-
- switch (result.format)
- {
- case ResultFormat::Text:
- writeOutputToConsole(writer, result.outputString);
- break;
-
- case ResultFormat::Binary:
- {
- auto& data = result.outputBinary;
-
- if (writer->isConsole())
- {
- // Writing to console, so we need to generate text output.
-
- switch (targetReq->target)
- {
- #if SLANG_ENABLE_DXBC_SUPPORT
- case CodeGenTarget::DXBytecode:
- {
- String assembly;
- dissassembleDXBC(backEndReq,
- data.begin(),
- data.end() - data.begin(), assembly);
- writeOutputToConsole(writer, assembly);
- }
- break;
- #endif
-
- #if SLANG_ENABLE_DXIL_SUPPORT
- case CodeGenTarget::DXIL:
- {
- String assembly;
- dissassembleDXILUsingDXC(backEndReq,
- data.begin(),
- data.end() - data.begin(),
- assembly);
- writeOutputToConsole(writer, assembly);
- }
- break;
- #endif
-
- case CodeGenTarget::SPIRV:
- {
- String assembly;
- dissassembleSPIRV(backEndReq,
- data.begin(),
- data.end() - data.begin(), assembly);
- writeOutputToConsole(writer, assembly);
- }
- break;
-
- default:
- SLANG_UNEXPECTED("unhandled output format");
- return;
- }
- }
- else
- {
- // Redirecting stdout to a file, so do the usual thing
- writer->setMode(SLANG_WRITER_MODE_BINARY);
-
- writeOutputFile(
- backEndReq,
- writer,
- "stdout",
- data.begin(),
- data.end() - data.begin());
- }
- }
- break;
-
- default:
- SLANG_UNEXPECTED("unhandled output format");
- break;
- }
-
- }
-
- static void writeEntryPointResult(
- EndToEndCompileRequest* compileRequest,
- EntryPoint* entryPoint,
- TargetRequest* targetReq,
- Int entryPointIndex)
- {
- auto program = compileRequest->getSpecializedProgram();
- auto targetProgram = program->getTargetProgram(targetReq);
- auto backEndReq = compileRequest->getBackEndReq();
-
- auto& result = targetProgram->getExistingEntryPointResult(entryPointIndex);
-
- // Skip the case with no output
- if (result.format == ResultFormat::None)
- return;
-
- // It is possible that we are dynamically discovering entry
- // points (using `[shader(...)]` attributes), so that there
- // might be entry points added to the program that did not
- // get paths specified via command-line options.
- //
- RefPtr<EndToEndCompileRequest::TargetInfo> targetInfo;
- if(compileRequest->targetInfos.TryGetValue(targetReq, targetInfo))
- {
- String outputPath;
- if(targetInfo->entryPointOutputPaths.TryGetValue(entryPointIndex, outputPath))
- {
- writeEntryPointResultToFile(backEndReq, entryPoint, outputPath, result);
- return;
- }
- }
-
- writeEntryPointResultToStandardOutput(compileRequest, entryPoint, targetReq, result);
- }
-
- void generateOutputForTarget(
- BackEndCompileRequest* compileReq,
- TargetRequest* targetReq,
- EndToEndCompileRequest* endToEndReq)
- {
- auto program = compileReq->getProgram();
- auto targetProgram = program->getTargetProgram(targetReq);
-
- // Generate target code any entry points that
- // have been requested for compilation.
- auto entryPointCount = program->getEntryPointCount();
- for(Index ii = 0; ii < entryPointCount; ++ii)
- {
- auto entryPoint = program->getEntryPoint(ii);
- CompileResult entryPointResult = emitEntryPoint(
- compileReq,
- entryPoint,
- ii,
- targetReq,
- endToEndReq);
- targetProgram->setEntryPointResult(ii, entryPointResult);
- }
- }
-
- static void _generateOutput(
- BackEndCompileRequest* compileRequest,
- EndToEndCompileRequest* endToEndReq)
- {
- // Go through the code-generation targets that the user
- // has specified, and generate code for each of them.
- //
- auto linkage = compileRequest->getLinkage();
- for (auto targetReq : linkage->targets)
- {
- generateOutputForTarget(compileRequest, targetReq, endToEndReq);
- }
- }
-
- void generateOutput(
- BackEndCompileRequest* compileRequest)
- {
- _generateOutput(compileRequest, nullptr);
- }
-
- void generateOutput(
- EndToEndCompileRequest* compileRequest)
- {
- _generateOutput(compileRequest->getBackEndReq(), compileRequest);
-
- // If we are in command-line mode, we might be expected to actually
- // write output to one or more files here.
-
- if (compileRequest->isCommandLineCompile)
- {
- auto linkage = compileRequest->getLinkage();
- auto program = compileRequest->getSpecializedProgram();
- for (auto targetReq : linkage->targets)
- {
- Index entryPointCount = program->getEntryPointCount();
- for (Index ee = 0; ee < entryPointCount; ++ee)
- {
- writeEntryPointResult(
- compileRequest,
- program->getEntryPoint(ee),
- targetReq,
- ee);
- }
- }
- }
- }
-
- // Debug logic for dumping intermediate outputs
-
- //
-
- void dumpIntermediate(
- BackEndCompileRequest*,
- void const* data,
- size_t size,
- char const* ext,
- bool isBinary)
- {
- // Try to generate a unique ID for the file to dump,
- // even in cases where there might be multiple threads
- // doing compilation.
- //
- // This is primarily a debugging aid, so we don't
- // really need/want to do anything too elaborate
-
- static uint32_t counter = 0;
-#ifdef WIN32
- uint32_t id = InterlockedIncrement(&counter);
-#else
- // TODO: actually implement the case for other platforms
- uint32_t id = counter++;
-#endif
-
- String path;
- path.append("slang-dump-");
- path.append(id);
- path.append(ext);
-
- FILE* file = fopen(path.getBuffer(), isBinary ? "wb" : "w");
- if (!file) return;
-
- fwrite(data, size, 1, file);
- fclose(file);
- }
-
- void dumpIntermediateText(
- BackEndCompileRequest* compileRequest,
- void const* data,
- size_t size,
- char const* ext)
- {
- dumpIntermediate(compileRequest, data, size, ext, false);
- }
-
- void dumpIntermediateBinary(
- BackEndCompileRequest* compileRequest,
- void const* data,
- size_t size,
- char const* ext)
- {
- dumpIntermediate(compileRequest, data, size, ext, true);
- }
-
- void maybeDumpIntermediate(
- BackEndCompileRequest* compileRequest,
- void const* data,
- size_t size,
- CodeGenTarget target)
- {
- if (!compileRequest->shouldDumpIntermediates)
- return;
-
- switch (target)
- {
- default:
- break;
-
- case CodeGenTarget::HLSL:
- dumpIntermediateText(compileRequest, data, size, ".hlsl");
- break;
-
- case CodeGenTarget::GLSL:
- dumpIntermediateText(compileRequest, data, size, ".glsl");
- break;
-
- case CodeGenTarget::SPIRVAssembly:
- dumpIntermediateText(compileRequest, data, size, ".spv.asm");
- break;
-
-#if 0
- case CodeGenTarget::SlangIRAssembly:
- dumpIntermediateText(compileRequest, data, size, ".slang-ir.asm");
- break;
-#endif
-
- case CodeGenTarget::SPIRV:
- dumpIntermediateBinary(compileRequest, data, size, ".spv");
- {
- String spirvAssembly;
- dissassembleSPIRV(compileRequest, data, size, spirvAssembly);
- dumpIntermediateText(compileRequest, spirvAssembly.begin(), spirvAssembly.getLength(), ".spv.asm");
- }
- break;
-
- #if SLANG_ENABLE_DXBC_SUPPORT
- case CodeGenTarget::DXBytecodeAssembly:
- dumpIntermediateText(compileRequest, data, size, ".dxbc.asm");
- break;
-
- case CodeGenTarget::DXBytecode:
- dumpIntermediateBinary(compileRequest, data, size, ".dxbc");
- {
- String dxbcAssembly;
- dissassembleDXBC(compileRequest, data, size, dxbcAssembly);
- dumpIntermediateText(compileRequest, dxbcAssembly.begin(), dxbcAssembly.getLength(), ".dxbc.asm");
- }
- break;
- #endif
-
- #if SLANG_ENABLE_DXIL_SUPPORT
- case CodeGenTarget::DXILAssembly:
- dumpIntermediateText(compileRequest, data, size, ".dxil.asm");
- break;
-
- case CodeGenTarget::DXIL:
- dumpIntermediateBinary(compileRequest, data, size, ".dxil");
- {
- String dxilAssembly;
- dissassembleDXILUsingDXC(compileRequest, data, size, dxilAssembly);
- dumpIntermediateText(compileRequest, dxilAssembly.begin(), dxilAssembly.getLength(), ".dxil.asm");
- }
- break;
- #endif
- }
- }
-
- void maybeDumpIntermediate(
- BackEndCompileRequest* compileRequest,
- char const* text,
- CodeGenTarget target)
- {
- if (!compileRequest->shouldDumpIntermediates)
- return;
-
- maybeDumpIntermediate(compileRequest, text, strlen(text), target);
- }
-
-}