summaryrefslogtreecommitdiff
path: root/source/slang/slang-compiler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-compiler.cpp')
-rw-r--r--source/slang/slang-compiler.cpp3097
1 files changed, 1 insertions, 3096 deletions
diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp
index b13624aac..4b689455b 100644
--- a/source/slang/slang-compiler.cpp
+++ b/source/slang/slang-compiler.cpp
@@ -1,1223 +1,9 @@
-// Compiler.cpp : Defines the entry point for the console application.
-//
+// slang-compiler.cpp
#include "slang-compiler.h"
-#include "../compiler-core/slang-lexer.h"
-#include "../core/slang-basic.h"
-#include "../core/slang-castable.h"
-#include "../core/slang-hex-dump-util.h"
-#include "../core/slang-io.h"
-#include "../core/slang-performance-profiler.h"
-#include "../core/slang-platform.h"
-#include "../core/slang-riff.h"
-#include "../core/slang-string-util.h"
-#include "../core/slang-type-convert-util.h"
-#include "../core/slang-type-text-util.h"
-#include "slang-check-impl.h"
-#include "slang-check.h"
-
-#include <chrono>
-
-// Artifact
-#include "../compiler-core/slang-artifact-associated.h"
-#include "../compiler-core/slang-artifact-container-util.h"
-#include "../compiler-core/slang-artifact-desc-util.h"
-#include "../compiler-core/slang-artifact-diagnostic-util.h"
-#include "../compiler-core/slang-artifact-impl.h"
-#include "../compiler-core/slang-artifact-representation-impl.h"
-#include "../compiler-core/slang-artifact-util.h"
-
-// Artifact output
-#include "slang-artifact-output-util.h"
-#include "slang-emit-cuda.h"
-#include "slang-extension-tracker.h"
-#include "slang-lower-to-ir.h"
-#include "slang-mangle.h"
-#include "slang-parameter-binding.h"
-#include "slang-parser.h"
-#include "slang-preprocessor.h"
-#include "slang-serialize-ast.h"
-#include "slang-serialize-container.h"
-#include "slang-type-layout.h"
-
namespace Slang
{
-// !!!!!!!!!!!!!!!!!!!!!! free functions for DiagnosicSink !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-bool isHeterogeneousTarget(CodeGenTarget target)
-{
- return ArtifactDescUtil::makeDescForCompileTarget(asExternal(target)).style ==
- ArtifactStyle::Host;
-}
-
-void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val)
-{
- UnownedStringSlice name = TypeTextUtil::getCompileTargetName(asExternal(val));
- name = name.getLength() ? name : toSlice("<unknown>");
- sb << name;
-}
-
-void printDiagnosticArg(StringBuilder& sb, PassThroughMode val)
-{
- sb << TypeTextUtil::getPassThroughName(SlangPassThrough(val));
-}
-
-//
-// 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
-//
-
-ISlangUnknown* EntryPoint::getInterface(const Guid& guid)
-{
- if (guid == slang::IEntryPoint::getTypeGuid())
- return static_cast<slang::IEntryPoint*>(this);
-
- return Super::getInterface(guid);
-}
-
-RefPtr<EntryPoint> EntryPoint::create(
- Linkage* linkage,
- DeclRef<FuncDecl> funcDeclRef,
- Profile profile)
-{
- RefPtr<EntryPoint> entryPoint =
- new EntryPoint(linkage, funcDeclRef.getName(), profile, funcDeclRef);
- entryPoint->m_mangledName = getMangledName(linkage->getASTBuilder(), funcDeclRef);
- return entryPoint;
-}
-
-RefPtr<EntryPoint> EntryPoint::createDummyForPassThrough(
- Linkage* linkage,
- Name* name,
- Profile profile)
-{
- RefPtr<EntryPoint> entryPoint = new EntryPoint(linkage, name, profile, DeclRef<FuncDecl>());
- return entryPoint;
-}
-
-RefPtr<EntryPoint> EntryPoint::createDummyForDeserialize(
- Linkage* linkage,
- Name* name,
- Profile profile,
- String mangledName)
-{
- RefPtr<EntryPoint> entryPoint = new EntryPoint(linkage, name, profile, DeclRef<FuncDecl>());
- entryPoint->m_mangledName = mangledName;
- return entryPoint;
-}
-
-EntryPoint::EntryPoint(Linkage* linkage, Name* name, Profile profile, DeclRef<FuncDecl> funcDeclRef)
- : ComponentType(linkage), m_name(name), m_profile(profile), m_funcDeclRef(funcDeclRef)
-{
- // Collect any specialization parameters used by the entry point
- //
- _collectShaderParams();
-}
-
-Module* EntryPoint::getModule()
-{
- return Slang::getModule(getFuncDecl());
-}
-
-Index EntryPoint::getSpecializationParamCount()
-{
- return m_genericSpecializationParams.getCount() + m_existentialSpecializationParams.getCount();
-}
-
-SpecializationParam const& EntryPoint::getSpecializationParam(Index index)
-{
- auto genericParamCount = m_genericSpecializationParams.getCount();
- if (index < genericParamCount)
- {
- return m_genericSpecializationParams[index];
- }
- else
- {
- return m_existentialSpecializationParams[index - genericParamCount];
- }
-}
-
-Index EntryPoint::getRequirementCount()
-{
- // The only requirement of an entry point is the module that contains it.
- //
- // TODO: We will eventually want to support the case of an entry
- // point nested in a `struct` type, in which case there should be
- // a single requirement representing that outer type (so that multiple
- // entry points nested under the same type can share the storage
- // for parameters at that scope).
-
- // Note: the defensive coding is here because the
- // "dummy" entry points we create for pass-through
- // compilation will not have an associated module.
- //
- if (const auto module = getModule())
- {
- return 1;
- }
- return 0;
-}
-
-RefPtr<ComponentType> EntryPoint::getRequirement(Index index)
-{
- SLANG_UNUSED(index);
- SLANG_ASSERT(index == 0);
- SLANG_ASSERT(getModule());
- return getModule();
-}
-
-String EntryPoint::getEntryPointMangledName(Index index)
-{
- SLANG_UNUSED(index);
- SLANG_ASSERT(index == 0);
-
- return m_mangledName;
-}
-
-String EntryPoint::getEntryPointNameOverride(Index index)
-{
- SLANG_UNUSED(index);
- SLANG_ASSERT(index == 0);
-
- return m_name ? m_name->text : "";
-}
-
-void EntryPoint::acceptVisitor(
- ComponentTypeVisitor* visitor,
- SpecializationInfo* specializationInfo)
-{
- visitor->visitEntryPoint(this, as<EntryPointSpecializationInfo>(specializationInfo));
-}
-
-void EntryPoint::buildHash(DigestBuilder<SHA1>& builder)
-{
- SLANG_UNUSED(builder);
-}
-
-List<Module*> const& EntryPoint::getModuleDependencies()
-{
- if (auto module = getModule())
- return module->getModuleDependencies();
-
- static List<Module*> empty;
- return empty;
-}
-
-List<SourceFile*> const& EntryPoint::getFileDependencies()
-{
- if (const auto module = getModule())
- return getModule()->getFileDependencies();
-
- static List<SourceFile*> empty;
- return empty;
-}
-
-TypeConformance::TypeConformance(
- Linkage* linkage,
- SubtypeWitness* witness,
- Int confomrmanceIdOverride,
- DiagnosticSink* sink)
- : ComponentType(linkage)
- , m_subtypeWitness(witness)
- , m_conformanceIdOverride(confomrmanceIdOverride)
-{
- addDepedencyFromWitness(witness);
- m_irModule = generateIRForTypeConformance(this, m_conformanceIdOverride, sink);
-}
-
-void TypeConformance::addDepedencyFromWitness(SubtypeWitness* witness)
-{
- if (auto declaredWitness = as<DeclaredSubtypeWitness>(witness))
- {
- auto declModule = getModule(declaredWitness->getDeclRef().getDecl());
- m_moduleDependencyList.addDependency(declModule);
- m_fileDependencyList.addDependency(declModule);
- if (m_requirementSet.add(declModule))
- {
- m_requirements.add(declModule);
- }
- // TODO: handle the specialization arguments in declaredWitness->declRef.substitutions.
- }
- else if (auto transitiveWitness = as<TransitiveSubtypeWitness>(witness))
- {
- addDepedencyFromWitness(transitiveWitness->getMidToSup());
- addDepedencyFromWitness(transitiveWitness->getSubToMid());
- }
- else if (auto conjunctionWitness = as<ConjunctionSubtypeWitness>(witness))
- {
- auto componentCount = conjunctionWitness->getComponentCount();
- for (Index i = 0; i < componentCount; ++i)
- {
- auto w = as<SubtypeWitness>(conjunctionWitness->getComponentWitness(i));
- if (w)
- addDepedencyFromWitness(w);
- }
- }
-}
-
-ISlangUnknown* TypeConformance::getInterface(const Guid& guid)
-{
- if (guid == slang::ITypeConformance::getTypeGuid())
- return static_cast<slang::ITypeConformance*>(this);
-
- return Super::getInterface(guid);
-}
-
-void TypeConformance::buildHash(DigestBuilder<SHA1>& builder)
-{
- // TODO: Implement some kind of hashInto for Val then replace this
- auto subtypeWitness = m_subtypeWitness->toString();
-
- builder.append(subtypeWitness);
- builder.append(m_conformanceIdOverride);
-}
-
-List<Module*> const& TypeConformance::getModuleDependencies()
-{
- return m_moduleDependencyList.getModuleList();
-}
-
-List<SourceFile*> const& TypeConformance::getFileDependencies()
-{
- return m_fileDependencyList.getFileList();
-}
-
-Index TypeConformance::getRequirementCount()
-{
- return m_requirements.getCount();
-}
-
-RefPtr<ComponentType> TypeConformance::getRequirement(Index index)
-{
- return m_requirements[index];
-}
-
-void TypeConformance::acceptVisitor(
- ComponentTypeVisitor* visitor,
- ComponentType::SpecializationInfo* specializationInfo)
-{
- SLANG_UNUSED(specializationInfo);
- visitor->visitTypeConformance(this);
-}
-
-RefPtr<ComponentType::SpecializationInfo> TypeConformance::_validateSpecializationArgsImpl(
- SpecializationArg const* args,
- Index argCount,
- DiagnosticSink* sink)
-{
- SLANG_UNUSED(args);
- SLANG_UNUSED(argCount);
- SLANG_UNUSED(sink);
- return nullptr;
-}
-
-//
-
-Profile Profile::lookUp(UnownedStringSlice const& name)
-{
-#define PROFILE(TAG, NAME, STAGE, VERSION) \
- if (name == UnownedTerminatedStringSlice(#NAME)) \
- return Profile::TAG;
-#define PROFILE_ALIAS(TAG, DEF, NAME) \
- if (name == UnownedTerminatedStringSlice(#NAME)) \
- return Profile::TAG;
-#include "slang-profile-defs.h"
-
- return Profile::Unknown;
-}
-
-Profile Profile::lookUp(char const* name)
-{
- return lookUp(UnownedTerminatedStringSlice(name));
-}
-
-CapabilitySet Profile::getCapabilityName()
-{
- List<CapabilityName> result;
- switch (getVersion())
- {
-#define PROFILE_VERSION(TAG, NAME) \
- case ProfileVersion::TAG: \
- result.add(CapabilityName::TAG); \
- break;
-#include "slang-profile-defs.h"
- default:
- break;
- }
- switch (getStage())
- {
-#define PROFILE_STAGE(TAG, NAME, VAL) \
- case Stage::TAG: \
- result.add(CapabilityName::NAME); \
- break;
-#include "slang-profile-defs.h"
- default:
- break;
- }
-
- CapabilitySet resultSet = CapabilitySet(result);
- for (auto i : this->additionalCapabilities)
- resultSet.join(i);
- return resultSet;
-}
-
-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 "slang-profile-defs.h"
- }
-}
-
-static const StageInfo kStages[] = {
-#define PROFILE_STAGE(ID, NAME, ENUM) {#NAME, Stage::ID},
-
-#define PROFILE_STAGE_ALIAS(ID, NAME, VAL) {#NAME, Stage::ID},
-
-#include "slang-profile-defs.h"
-};
-
-ConstArrayView<StageInfo> getStageInfos()
-{
- return makeConstArrayView(kStages);
-}
-
-Stage findStageByName(String const& name)
-{
- for (auto entry : kStages)
- {
- if (name == entry.name)
- {
- return entry.stage;
- }
- }
-
- return Stage::Unknown;
-}
-
-UnownedStringSlice getStageText(Stage stage)
-{
- for (auto entry : kStages)
- {
- if (stage == entry.stage)
- {
- return UnownedStringSlice(entry.name);
- }
- }
- return UnownedStringSlice();
-}
-
-Stage getStageFromAtom(CapabilityAtom atom)
-{
- switch (atom)
- {
- case CapabilityAtom::vertex:
- return Stage::Vertex;
- case CapabilityAtom::hull:
- return Stage::Hull;
- case CapabilityAtom::domain:
- return Stage::Domain;
- case CapabilityAtom::geometry:
- return Stage::Geometry;
- case CapabilityAtom::fragment:
- return Stage::Fragment;
- case CapabilityAtom::compute:
- return Stage::Compute;
- case CapabilityAtom::_mesh:
- return Stage::Mesh;
- case CapabilityAtom::_amplification:
- return Stage::Amplification;
- case CapabilityAtom::_anyhit:
- return Stage::AnyHit;
- case CapabilityAtom::_closesthit:
- return Stage::ClosestHit;
- case CapabilityAtom::_intersection:
- return Stage::Intersection;
- case CapabilityAtom::_raygen:
- return Stage::RayGeneration;
- case CapabilityAtom::_miss:
- return Stage::Miss;
- case CapabilityAtom::_callable:
- return Stage::Callable;
- case CapabilityAtom::dispatch:
- return Stage::Dispatch;
- default:
- SLANG_UNEXPECTED("unknown stage atom");
- UNREACHABLE_RETURN(Stage::Unknown);
- }
-}
-
-CapabilityAtom getAtomFromStage(Stage stage)
-{
- // Convert Slang::Stage to CapabilityAtom.
- // Note that capabilities do not share the same values as Slang::Stage
- // and must be explicitly converted.
- switch (stage)
- {
- case Stage::Compute:
- return CapabilityAtom::compute;
- case Stage::Vertex:
- return CapabilityAtom::vertex;
- case Stage::Fragment:
- return CapabilityAtom::fragment;
- case Stage::Geometry:
- return CapabilityAtom::geometry;
- case Stage::Hull:
- return CapabilityAtom::hull;
- case Stage::Domain:
- return CapabilityAtom::domain;
- case Stage::Mesh:
- return CapabilityAtom::_mesh;
- case Stage::Amplification:
- return CapabilityAtom::_amplification;
- case Stage::RayGeneration:
- return CapabilityAtom::_raygen;
- case Stage::AnyHit:
- return CapabilityAtom::_anyhit;
- case Stage::ClosestHit:
- return CapabilityAtom::_closesthit;
- case Stage::Miss:
- return CapabilityAtom::_miss;
- case Stage::Intersection:
- return CapabilityAtom::_intersection;
- case Stage::Callable:
- return CapabilityAtom::_callable;
- case Stage::Dispatch:
- return CapabilityAtom::dispatch;
- default:
- SLANG_UNEXPECTED("unknown stage");
- UNREACHABLE_RETURN(CapabilityAtom::Invalid);
- }
-}
-
-SlangResult checkExternalCompilerSupport(Session* session, PassThroughMode passThrough)
-{
- // Check if the type is supported on this compile
- if (passThrough == PassThroughMode::None)
- {
- // If no pass through -> that will always work!
- return SLANG_OK;
- }
-
- return session->getOrLoadDownstreamCompiler(passThrough, nullptr) ? SLANG_OK
- : SLANG_E_NOT_FOUND;
-}
-
-SourceLanguage getDefaultSourceLanguageForDownstreamCompiler(PassThroughMode compiler)
-{
- switch (compiler)
- {
- case PassThroughMode::None:
- {
- return SourceLanguage::Unknown;
- }
- case PassThroughMode::Fxc:
- case PassThroughMode::Dxc:
- {
- return SourceLanguage::HLSL;
- }
- case PassThroughMode::Glslang:
- {
- return SourceLanguage::GLSL;
- }
- case PassThroughMode::LLVM:
- case PassThroughMode::Clang:
- case PassThroughMode::VisualStudio:
- case PassThroughMode::Gcc:
- case PassThroughMode::GenericCCpp:
- {
- // These could ingest C, but we only have this function to work out a
- // 'default' language to ingest.
- return SourceLanguage::CPP;
- }
- case PassThroughMode::NVRTC:
- {
- return SourceLanguage::CUDA;
- }
- case PassThroughMode::Tint:
- {
- return SourceLanguage::WGSL;
- }
- case PassThroughMode::SpirvDis:
- {
- return SourceLanguage::SPIRV;
- }
- case PassThroughMode::MetalC:
- {
- return SourceLanguage::Metal;
- }
- default:
- break;
- }
- SLANG_ASSERT(!"Unknown compiler");
- return SourceLanguage::Unknown;
-}
-
-PassThroughMode getDownstreamCompilerRequiredForTarget(CodeGenTarget target)
-{
- switch (target)
- {
- // Don't *require* a downstream compiler for source output
- case CodeGenTarget::GLSL:
- case CodeGenTarget::HLSL:
- case CodeGenTarget::CUDASource:
- case CodeGenTarget::CPPSource:
- case CodeGenTarget::HostCPPSource:
- case CodeGenTarget::PyTorchCppBinding:
- case CodeGenTarget::CSource:
- case CodeGenTarget::Metal:
- case CodeGenTarget::WGSL:
- {
- return PassThroughMode::None;
- }
- case CodeGenTarget::None:
- {
- return PassThroughMode::None;
- }
- case CodeGenTarget::WGSLSPIRVAssembly:
- case CodeGenTarget::SPIRVAssembly:
- case CodeGenTarget::SPIRV:
- {
- return PassThroughMode::SpirvDis;
- }
- case CodeGenTarget::DXBytecode:
- case CodeGenTarget::DXBytecodeAssembly:
- {
- return PassThroughMode::Fxc;
- }
- case CodeGenTarget::DXIL:
- case CodeGenTarget::DXILAssembly:
- {
- return PassThroughMode::Dxc;
- }
- case CodeGenTarget::MetalLib:
- case CodeGenTarget::MetalLibAssembly:
- {
- return PassThroughMode::MetalC;
- }
- case CodeGenTarget::ShaderHostCallable:
- case CodeGenTarget::ShaderSharedLibrary:
- case CodeGenTarget::HostExecutable:
- case CodeGenTarget::HostHostCallable:
- case CodeGenTarget::HostSharedLibrary:
- {
- // We need some C/C++ compiler
- return PassThroughMode::GenericCCpp;
- }
- case CodeGenTarget::PTX:
- {
- return PassThroughMode::NVRTC;
- }
- case CodeGenTarget::WGSLSPIRV:
- {
- return PassThroughMode::Tint;
- }
- default:
- break;
- }
-
- SLANG_ASSERT(!"Unhandled target");
- return PassThroughMode::None;
-}
-
-EndToEndCompileRequest* CodeGenContext::isPassThroughEnabled()
-{
- auto endToEndReq = isEndToEndCompile();
-
- // 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 on that end-to-end compile,
- // then we clearly areb't doing a pass-through compile.
- //
- if (endToEndReq->m_passThrough == PassThroughMode::None)
- return nullptr;
-
- // If we have confirmed that pass-through compilation is going on,
- // we return the end-to-end request, because it has all the
- // relevant state that we need to implement pass-through mode.
- //
- return endToEndReq;
-}
-
-/// If there is a pass-through compile going on, find the translation unit for the given entry
-/// point. Assumes isPassThroughEnabled has already been called
-TranslationUnitRequest* getPassThroughTranslationUnit(
- EndToEndCompileRequest* endToEndReq,
- Int entryPointIndex)
-{
- SLANG_ASSERT(endToEndReq);
- SLANG_ASSERT(endToEndReq->m_passThrough != PassThroughMode::None);
- auto frontEndReq = endToEndReq->getFrontEndReq();
- auto entryPointReq = frontEndReq->getEntryPointReq(entryPointIndex);
- auto translationUnit = entryPointReq->getTranslationUnit();
- return translationUnit;
-}
-
-TranslationUnitRequest* CodeGenContext::findPassThroughTranslationUnit(Int entryPointIndex)
-{
- if (auto endToEndReq = isPassThroughEnabled())
- return getPassThroughTranslationUnit(endToEndReq, entryPointIndex);
- return nullptr;
-}
-
-static void _appendCodeWithPath(
- const UnownedStringSlice& filePath,
- const UnownedStringSlice& fileContent,
- StringBuilder& outCodeBuilder)
-{
- outCodeBuilder << "#line 1 \"";
- auto handler = StringEscapeUtil::getHandler(StringEscapeUtil::Style::Cpp);
- handler->appendEscaped(filePath, outCodeBuilder);
- outCodeBuilder << "\"\n";
- outCodeBuilder << fileContent << "\n";
-}
-
-void trackGLSLTargetCaps(ShaderExtensionTracker* extensionTracker, CapabilitySet const& caps)
-{
- for (auto& conjunctions : caps.getAtomSets())
- {
- for (auto atom : conjunctions)
- {
- switch (asAtom(atom))
- {
- default:
- break;
-
- case CapabilityAtom::glsl_spirv_1_0:
- extensionTracker->requireSPIRVVersion(SemanticVersion(1, 0));
- break;
- case CapabilityAtom::glsl_spirv_1_1:
- extensionTracker->requireSPIRVVersion(SemanticVersion(1, 1));
- break;
- case CapabilityAtom::glsl_spirv_1_2:
- extensionTracker->requireSPIRVVersion(SemanticVersion(1, 2));
- break;
- case CapabilityAtom::glsl_spirv_1_3:
- extensionTracker->requireSPIRVVersion(SemanticVersion(1, 3));
- break;
- case CapabilityAtom::glsl_spirv_1_4:
- extensionTracker->requireSPIRVVersion(SemanticVersion(1, 4));
- break;
- case CapabilityAtom::glsl_spirv_1_5:
- extensionTracker->requireSPIRVVersion(SemanticVersion(1, 5));
- break;
- case CapabilityAtom::glsl_spirv_1_6:
- extensionTracker->requireSPIRVVersion(SemanticVersion(1, 6));
- break;
- }
- }
- }
-}
-
-SlangResult CodeGenContext::requireTranslationUnitSourceFiles()
-{
- if (auto endToEndReq = isPassThroughEnabled())
- {
- for (auto entryPointIndex : getEntryPointIndices())
- {
- auto translationUnit = getPassThroughTranslationUnit(endToEndReq, entryPointIndex);
- SLANG_ASSERT(translationUnit);
- /// Make sure we have the source files
- SLANG_RETURN_ON_FAIL(translationUnit->requireSourceFiles());
- }
- }
-
- return SLANG_OK;
-}
-
-#if SLANG_VC
-// TODO(JS): This is a workaround
-// In debug VS builds there is a warning on line about it being unreachable.
-// for (auto entryPointIndex : getEntryPointIndices())
-// It's not clear how that could possibly be unreachable
-#pragma warning(push)
-#pragma warning(disable : 4702)
-#endif
-SlangResult CodeGenContext::emitEntryPointsSource(ComPtr<IArtifact>& outArtifact)
-{
- outArtifact.setNull();
-
- SLANG_RETURN_ON_FAIL(requireTranslationUnitSourceFiles());
-
- auto endToEndReq = isPassThroughEnabled();
- if (endToEndReq)
- {
- for (auto entryPointIndex : getEntryPointIndices())
- {
- auto translationUnit = getPassThroughTranslationUnit(endToEndReq, entryPointIndex);
- SLANG_ASSERT(translationUnit);
-
- /// Make sure we have the source files
- SLANG_RETURN_ON_FAIL(translationUnit->requireSourceFiles());
-
- // 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;
- if (getTargetFormat() == CodeGenTarget::GLSL)
- {
- // Special case GLSL
- 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";
- }
- }
- else
- {
- for (auto sourceFile : translationUnit->getSourceFiles())
- {
- _appendCodeWithPath(
- sourceFile->getPathInfo().foundPath.getUnownedSlice(),
- sourceFile->getContent(),
- codeBuilder);
- }
- }
-
- auto artifact =
- ArtifactUtil::createArtifactForCompileTarget(asExternal(getTargetFormat()));
- artifact->addRepresentationUnknown(StringBlob::moveCreate(codeBuilder));
-
- outArtifact.swap(artifact);
- return SLANG_OK;
- }
- return SLANG_OK;
- }
- else
- {
- return emitEntryPointsSourceFromIR(outArtifact);
- }
-}
-#if SLANG_VC
-#pragma warning(pop)
-#endif
-
-SlangResult CodeGenContext::emitPrecompiledDownstreamIR(ComPtr<IArtifact>& outArtifact)
-{
- return _emitEntryPoints(outArtifact);
-}
-
-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_1);
- 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);
- CASE(Amplification, as);
- CASE(Mesh, ms);
-#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_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);
- CASE(DX_6_4, _6_4);
- CASE(DX_6_5, _6_5);
- CASE(DX_6_6, _6_6);
- CASE(DX_6_7, _6_7);
- CASE(DX_6_8, _6_8);
- CASE(DX_6_9, _6_9);
-#undef CASE
-
- default:
- return "unknown";
- }
-
- String result;
- result.append(stagePrefix);
- result.append(versionSuffix);
- return result;
-}
-
-void reportExternalCompileError(
- const char* compilerName,
- Severity severity,
- SlangResult res,
- const UnownedStringSlice& diagnostic,
- DiagnosticSink* sink)
-{
- StringBuilder builder;
- if (compilerName)
- {
- builder << compilerName << ": ";
- }
-
- 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);
- }
-
- if (diagnostic.getLength() > 0)
- {
- builder.append(diagnostic);
- if (!diagnostic.endsWith("\n"))
- {
- builder.append("\n");
- }
- }
-
- sink->diagnoseRaw(severity, builder.getUnownedSlice());
-}
-
-void reportExternalCompileError(
- const char* compilerName,
- SlangResult res,
- const UnownedStringSlice& diagnostic,
- DiagnosticSink* sink)
-{
- // 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...)
- reportExternalCompileError(
- compilerName,
- SLANG_FAILED(res) ? Severity::Error : Severity::Warning,
- res,
- diagnostic,
- sink);
-}
-
-static String _getDisplayPath(DiagnosticSink* sink, SourceFile* sourceFile)
-{
- if (sink->isFlagSet(DiagnosticSink::Flag::VerbosePath))
- {
- return sourceFile->calcVerbosePath();
- }
- else
- {
- return sourceFile->getPathInfo().foundPath;
- }
-}
-
-String CodeGenContext::calcSourcePathForEntryPoints()
-{
- String failureMode = "slang-generated";
- if (getEntryPointCount() != 1)
- return failureMode;
- auto entryPointIndex = getSingleEntryPointIndex();
- auto translationUnitRequest = findPassThroughTranslationUnit(entryPointIndex);
- if (!translationUnitRequest)
- return failureMode;
-
- const auto& sourceFiles = translationUnitRequest->getSourceFiles();
-
- auto sink = getSink();
-
- 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;
- }
- }
-}
-
-// Helper function for cases where we can assume a single entry point
-Int assertSingleEntryPoint(List<Int> const& entryPointIndices)
-{
- SLANG_ASSERT(entryPointIndices.getCount() == 1);
- return *entryPointIndices.begin();
-}
-
-// True if it's best to use 'emitted' source for complication. For a downstream compiler
-// that is not file based, this is always ok.
-///
-/// If the downstream compiler is file system based, we may want to just use the file that was
-/// passed to be compiled. That the downstream compiler can determine if it will then save the file
-/// or not based on if it's a match - and generally there will not be a match with emitted source.
-///
-/// This test is only used for pass through mode.
-static bool _useEmittedSource(
- IDownstreamCompiler* compiler,
- TranslationUnitRequest* translationUnit)
-{
- // We only bother if it's a file based compiler.
- if (compiler->isFileBased())
- {
- // It can only have *one* source file as otherwise we have to combine to make a new source
- // file anyway
- return translationUnit->getSourceArtifacts().getCount() != 1;
- }
- return true;
-}
-
-static Severity _getDiagnosticSeverity(ArtifactDiagnostic::Severity severity)
-{
- switch (severity)
- {
- case ArtifactDiagnostic::Severity::Warning:
- return Severity::Warning;
- case ArtifactDiagnostic::Severity::Info:
- return Severity::Note;
- default:
- return Severity::Error;
- }
-}
-
-static RefPtr<ExtensionTracker> _newExtensionTracker(CodeGenTarget target)
-{
- switch (target)
- {
- case CodeGenTarget::PTX:
- case CodeGenTarget::CUDASource:
- {
- return new CUDAExtensionTracker;
- }
- case CodeGenTarget::SPIRV:
- case CodeGenTarget::GLSL:
- case CodeGenTarget::WGSL:
- case CodeGenTarget::WGSLSPIRV:
- case CodeGenTarget::WGSLSPIRVAssembly:
- {
- return new ShaderExtensionTracker;
- }
- default:
- return nullptr;
- }
-}
-
-static CodeGenTarget _getDefaultSourceForTarget(CodeGenTarget target)
-{
- switch (target)
- {
- case CodeGenTarget::ShaderHostCallable:
- case CodeGenTarget::ShaderSharedLibrary:
- {
- return CodeGenTarget::CPPSource;
- }
- case CodeGenTarget::HostHostCallable:
- case CodeGenTarget::HostExecutable:
- case CodeGenTarget::HostSharedLibrary:
- {
- return CodeGenTarget::HostCPPSource;
- }
- case CodeGenTarget::PTX:
- return CodeGenTarget::CUDASource;
- case CodeGenTarget::DXBytecode:
- return CodeGenTarget::HLSL;
- case CodeGenTarget::DXIL:
- return CodeGenTarget::HLSL;
- case CodeGenTarget::SPIRV:
- return CodeGenTarget::GLSL;
- case CodeGenTarget::MetalLib:
- return CodeGenTarget::Metal;
- case CodeGenTarget::WGSLSPIRV:
- return CodeGenTarget::WGSL;
- default:
- break;
- }
- return CodeGenTarget::Unknown;
-}
-
-static bool _isCPUHostTarget(CodeGenTarget target)
-{
- auto desc = ArtifactDescUtil::makeDescForCompileTarget(asExternal(target));
- return desc.style == ArtifactStyle::Host;
-}
-
-static bool _shouldSetEntryPointName(TargetProgram* targetProgram)
-{
- if (!isKhronosTarget(targetProgram->getTargetReq()))
- return true;
- if (targetProgram->getOptionSet().getBoolOption(CompilerOptionName::VulkanUseEntryPointName))
- return true;
- return false;
-}
-
-SlangResult passthroughDownstreamDiagnostics(
- DiagnosticSink* sink,
- IDownstreamCompiler* compiler,
- IArtifact* artifact)
-{
- auto diagnostics = findAssociatedRepresentation<IArtifactDiagnostics>(artifact);
-
- if (!diagnostics)
- return SLANG_OK;
-
- if (diagnostics->getCount())
- {
- StringBuilder compilerText;
- DownstreamCompilerUtil::appendAsText(compiler->getDesc(), compilerText);
-
- StringBuilder builder;
-
- auto const diagnosticCount = diagnostics->getCount();
- for (Index i = 0; i < diagnosticCount; ++i)
- {
- const auto& diagnostic = *diagnostics->getAt(i);
-
- builder.clear();
-
- const Severity severity = _getDiagnosticSeverity(diagnostic.severity);
-
- if (diagnostic.filePath.count == 0 && diagnostic.location.line == 0 &&
- severity == Severity::Note)
- {
- // If theres no filePath line number and it's info, output severity and text alone
- builder << getSeverityName(severity) << " : ";
- }
- else
- {
- if (diagnostic.filePath.count)
- {
- builder << asStringSlice(diagnostic.filePath);
- }
-
- if (diagnostic.location.line)
- {
- builder << "(" << diagnostic.location.line << ")";
- }
-
- builder << ": ";
-
- if (diagnostic.stage == ArtifactDiagnostic::Stage::Link)
- {
- builder << "link ";
- }
-
- builder << getSeverityName(severity);
- builder << " " << asStringSlice(diagnostic.code) << ": ";
- }
-
- builder << asStringSlice(diagnostic.text);
- reportExternalCompileError(
- compilerText.getBuffer(),
- severity,
- SLANG_OK,
- builder.getUnownedSlice(),
- sink);
- }
- }
-
- // If any errors are emitted, then we are done
- if (diagnostics->hasOfAtLeastSeverity(ArtifactDiagnostic::Severity::Error))
- {
- return SLANG_FAIL;
- }
-
- return SLANG_OK;
-}
-
bool isValidSlangLanguageVersion(SlangLanguageVersion version)
{
switch (version)
@@ -1258,1885 +44,4 @@ bool isValidGLSLVersion(int version)
}
}
-SlangResult CodeGenContext::emitWithDownstreamForEntryPoints(ComPtr<IArtifact>& outArtifact)
-{
- outArtifact.setNull();
-
- auto sink = getSink();
- auto session = getSession();
-
- CodeGenTarget sourceTarget = CodeGenTarget::None;
- SourceLanguage sourceLanguage = SourceLanguage::Unknown;
-
- auto target = getTargetFormat();
- RefPtr<ExtensionTracker> extensionTracker = _newExtensionTracker(target);
- PassThroughMode compilerType;
-
- SliceAllocator allocator;
-
- if (auto endToEndReq = isPassThroughEnabled())
- {
- compilerType = endToEndReq->m_passThrough;
- }
- else
- {
- // If we are not in pass through, lookup the default compiler for the emitted source type
-
- // Get the default source codegen type for a given target
- sourceTarget = _getDefaultSourceForTarget(target);
- compilerType = (PassThroughMode)session->getDownstreamCompilerForTransition(
- (SlangCompileTarget)sourceTarget,
- (SlangCompileTarget)target);
- // We should have a downstream compiler set at this point
- if (compilerType == PassThroughMode::None)
- {
- auto sourceName = TypeTextUtil::getCompileTargetName(SlangCompileTarget(sourceTarget));
- auto targetName = TypeTextUtil::getCompileTargetName(SlangCompileTarget(target));
-
- sink->diagnose(
- SourceLoc(),
- Diagnostics::compilerNotDefinedForTransition,
- sourceName,
- targetName);
- return SLANG_FAIL;
- }
- }
-
- SLANG_ASSERT(compilerType != PassThroughMode::None);
-
- // Get the required downstream compiler
- IDownstreamCompiler* compiler = session->getOrLoadDownstreamCompiler(compilerType, sink);
- if (!compiler)
- {
- auto compilerName = TypeTextUtil::getPassThroughAsHumanText((SlangPassThrough)compilerType);
- sink->diagnose(SourceLoc(), Diagnostics::passThroughCompilerNotFound, compilerName);
- return SLANG_FAIL;
- }
-
- Dictionary<String, String> preprocessorDefinitions;
- List<String> includePaths;
-
- typedef DownstreamCompileOptions CompileOptions;
- CompileOptions options;
-
- List<DownstreamCompileOptions::CapabilityVersion> requiredCapabilityVersions;
- List<String> compilerSpecificArguments;
- List<ComPtr<IArtifact>> libraries;
- List<String> libraryPaths;
-
- // Set compiler specific args
- {
- auto name = TypeTextUtil::getPassThroughName((SlangPassThrough)compilerType);
- List<String> downstreamArgs = getTargetProgram()->getOptionSet().getDownstreamArgs(name);
- for (const auto& arg : downstreamArgs)
- {
- // We special case some kinds of args, that can be handled directly
- if (arg.startsWith("-I"))
- {
- // We handle the -I option, by just adding to the include paths
- includePaths.add(arg.getUnownedSlice().tail(2));
- }
- else
- {
- compilerSpecificArguments.add(arg);
- }
- }
- }
-
- ComPtr<IArtifact> sourceArtifact;
-
- /* This is more convoluted than the other scenarios, because when we invoke C/C++ compiler we
- would ideally like to use the original file. We want to do this because we want includes
- relative to the source file to work, and for that to work most easily we want to use the
- original file, if there is one */
- if (auto endToEndReq = isPassThroughEnabled())
- {
- // If we are pass through, we may need to set extension tracker state.
- if (ShaderExtensionTracker* glslTracker = as<ShaderExtensionTracker>(extensionTracker))
- {
- trackGLSLTargetCaps(glslTracker, getTargetCaps());
- }
-
- auto translationUnit =
- getPassThroughTranslationUnit(endToEndReq, getSingleEntryPointIndex());
-
- // We are just passing thru, so it's whatever it originally was
- sourceLanguage = translationUnit->sourceLanguage;
-
- // TODO(JS): This seems like a bit of a hack
- // That if a pass-through is being performed and the source language is Slang
- // no downstream compiler knows how to deal with that, so probably means 'HLSL'
- sourceLanguage =
- (sourceLanguage == SourceLanguage::Slang) ? SourceLanguage::HLSL : sourceLanguage;
- sourceTarget = CodeGenTarget(TypeConvertUtil::getCompileTargetFromSourceLanguage(
- (SlangSourceLanguage)sourceLanguage));
-
- // If it's pass through we accumulate the preprocessor definitions.
- for (const auto& define :
- endToEndReq->getOptionSet().getArray(CompilerOptionName::MacroDefine))
- preprocessorDefinitions.add(define.stringValue, define.stringValue2);
- for (const auto& define : translationUnit->preprocessorDefinitions)
- preprocessorDefinitions.add(define);
-
- {
- /* TODO(JS): Not totally clear what options should be set here. If we are using the pass
- through - then using say the defines/includes all makes total sense. If we are
- generating C++ code from slang, then should we really be using these values -> aren't
- they what is being set for the *slang* source, not for the C++ generated code. That
- being the case it implies that there needs to be a mechanism (if there isn't already) to
- specify such information on a particular pass/pass through etc.
-
- On invoking DXC for example include paths do not appear to be set at all (even with
- pass-through).
- */
-
- auto linkage = getLinkage();
-
- // Add all the search paths
-
- const auto searchDirectories = linkage->getSearchDirectories();
- const SearchDirectoryList* searchList = &searchDirectories;
- while (searchList)
- {
- for (const auto& searchDirectory : searchList->searchDirectories)
- {
- includePaths.add(searchDirectory.path);
- }
- searchList = searchList->parent;
- }
- }
-
- // If emitted source is required, emit and set the path
- if (_useEmittedSource(compiler, translationUnit))
- {
- CodeGenContext sourceCodeGenContext(this, sourceTarget, extensionTracker);
-
- SLANG_RETURN_ON_FAIL(sourceCodeGenContext.emitEntryPointsSource(sourceArtifact));
-
- // If it's not file based we can set an appropriate path name, and it doesn't matter if
- // it doesn't exist on the file system. We set the name to the path as this will be used
- // for downstream reporting.
- auto sourcePath = calcSourcePathForEntryPoints();
- sourceArtifact->setName(sourcePath.getBuffer());
-
- sourceCodeGenContext.maybeDumpIntermediate(sourceArtifact);
- }
- else
- {
- // Special case if we have a single file, so that we pass the path, and the contents as
- // is.
- const auto& sourceArtifacts = translationUnit->getSourceArtifacts();
- SLANG_ASSERT(sourceArtifacts.getCount() == 1);
-
- sourceArtifact = sourceArtifacts[0];
- SLANG_ASSERT(sourceArtifact);
- }
- }
- else
- {
- CodeGenContext sourceCodeGenContext(this, sourceTarget, extensionTracker);
-
- sourceCodeGenContext.removeAvailableInDownstreamIR = true;
-
- SLANG_RETURN_ON_FAIL(sourceCodeGenContext.emitEntryPointsSource(sourceArtifact));
- sourceCodeGenContext.maybeDumpIntermediate(sourceArtifact);
-
- sourceLanguage = (SourceLanguage)TypeConvertUtil::getSourceLanguageFromTarget(
- (SlangCompileTarget)sourceTarget);
- }
-
- if (sourceArtifact)
- {
- // Set the source artifacts
- options.sourceArtifacts = makeSlice(sourceArtifact.readRef(), 1);
- }
-
- // Add any preprocessor definitions associated with the linkage
- {
- // TODO(JS): This is somewhat arguable - should defines passed to Slang really be
- // passed to downstream compilers? It does appear consistent with the behavior if
- // there is an endToEndReq.
- //
- // That said it's very convenient and provides way to control aspects
- // of downstream compilation.
-
- for (const auto& define :
- getTargetProgram()->getOptionSet().getArray(CompilerOptionName::MacroDefine))
- {
- preprocessorDefinitions.addIfNotExists(define.stringValue, define.stringValue2);
- }
- }
-
-
- // If we have an extension tracker, we may need to set options such as SPIR-V version
- // and CUDA Shader Model.
- if (extensionTracker)
- {
- // Look for the version
- if (auto cudaTracker = as<CUDAExtensionTracker>(extensionTracker))
- {
- cudaTracker->finalize();
-
- if (cudaTracker->m_smVersion.isSet())
- {
- DownstreamCompileOptions::CapabilityVersion version;
- version.kind = DownstreamCompileOptions::CapabilityVersion::Kind::CUDASM;
- version.version = cudaTracker->m_smVersion;
-
- requiredCapabilityVersions.add(version);
- }
-
- if (cudaTracker->isBaseTypeRequired(BaseType::Half))
- {
- options.flags |= CompileOptions::Flag::EnableFloat16;
- }
- }
- else if (ShaderExtensionTracker* glslTracker = as<ShaderExtensionTracker>(extensionTracker))
- {
- DownstreamCompileOptions::CapabilityVersion version;
- version.kind = DownstreamCompileOptions::CapabilityVersion::Kind::SPIRV;
- version.version = glslTracker->getSPIRVVersion();
-
- requiredCapabilityVersions.add(version);
- }
- }
-
- CapabilitySet targetCaps = getTargetCaps();
- for (auto atomSets : targetCaps.getAtomSets())
- {
- for (auto atomVal : atomSets)
- {
- auto atom = CapabilityAtom(atomVal);
- switch (atom)
- {
- default:
- break;
-
-#define CASE(KIND, NAME, VERSION) \
- case CapabilityAtom::NAME: \
- requiredCapabilityVersions.add(DownstreamCompileOptions::CapabilityVersion{ \
- DownstreamCompileOptions::CapabilityVersion::Kind::KIND, \
- VERSION}); \
- break
-
- CASE(CUDASM, _cuda_sm_1_0, SemanticVersion(1, 0));
- CASE(CUDASM, _cuda_sm_2_0, SemanticVersion(2, 0));
- CASE(CUDASM, _cuda_sm_3_0, SemanticVersion(3, 0));
- CASE(CUDASM, _cuda_sm_4_0, SemanticVersion(4, 0));
- CASE(CUDASM, _cuda_sm_5_0, SemanticVersion(5, 0));
- CASE(CUDASM, _cuda_sm_6_0, SemanticVersion(6, 0));
- CASE(CUDASM, _cuda_sm_7_0, SemanticVersion(7, 0));
- CASE(CUDASM, _cuda_sm_8_0, SemanticVersion(8, 0));
- CASE(CUDASM, _cuda_sm_9_0, SemanticVersion(9, 0));
-
-#undef CASE
- }
- }
- }
-
- // Set the file sytem and source manager, as *may* be used by downstream compiler
- options.fileSystemExt = getFileSystemExt();
- options.sourceManager = getSourceManager();
-
- // Set the source type
- options.sourceLanguage = SlangSourceLanguage(sourceLanguage);
-
- switch (target)
- {
- case CodeGenTarget::ShaderHostCallable:
- case CodeGenTarget::ShaderSharedLibrary:
- // Disable exceptions and security checks
- options.flags &=
- ~(CompileOptions::Flag::EnableExceptionHandling |
- CompileOptions::Flag::EnableSecurityChecks);
- break;
- }
-
- Profile profile;
-
- if (compilerType == PassThroughMode::Fxc || compilerType == PassThroughMode::Dxc ||
- compilerType == PassThroughMode::Glslang)
- {
- const auto entryPointIndices = getEntryPointIndices();
- auto targetReq = getTargetReq();
-
- const auto entryPointIndicesCount = entryPointIndices.getCount();
-
- // Whole program means
- // * can have 0-N entry points
- // * 'doesn't build into an executable/kernel'
- //
- // So in some sense it is a library
- if (getTargetProgram()->getOptionSet().getBoolOption(
- CompilerOptionName::GenerateWholeProgram))
- {
- if (compilerType == PassThroughMode::Dxc)
- {
- // Can support no entry points on DXC because we can build libraries
- profile =
- Profile(getTargetProgram()->getOptionSet().getEnumOption<Profile::RawEnum>(
- CompilerOptionName::Profile));
- }
- else
- {
- auto downstreamCompilerName =
- TypeTextUtil::getPassThroughName((SlangPassThrough)compilerType);
-
- sink->diagnose(
- SourceLoc(),
- Diagnostics::downstreamCompilerDoesntSupportWholeProgramCompilation,
- downstreamCompilerName);
- return SLANG_FAIL;
- }
- }
- else if (entryPointIndicesCount == 1)
- {
- // All support a single entry point
- const Index entryPointIndex = entryPointIndices[0];
-
- auto entryPoint = getEntryPoint(entryPointIndex);
- profile = getEffectiveProfile(entryPoint, targetReq);
-
- if (_shouldSetEntryPointName(getTargetProgram()))
- {
- options.entryPointName = allocator.allocate(getText(entryPoint->getName()));
- auto entryPointNameOverride =
- getProgram()->getEntryPointNameOverride(entryPointIndex);
- if (entryPointNameOverride.getLength() != 0)
- {
- options.entryPointName = allocator.allocate(entryPointNameOverride);
- }
- }
- }
- else
- {
- // We only support a single entry point on this target
- SLANG_ASSERT(!"Can only compile with a single entry point on this target");
- return SLANG_FAIL;
- }
-
- options.stage = SlangStage(profile.getStage());
-
- if (compilerType == PassThroughMode::Dxc)
- {
- // We will enable the flag to generate proper code for 16 - bit types
- // by default, as long as the user is requesting a sufficiently
- // high shader model.
- //
- // TODO: Need to check that this is safe to enable in all cases,
- // or if it will make a shader demand hardware features that
- // aren't always present.
- //
- // TODO: Ideally the dxc back-end should be passed some information
- // on the "capabilities" that were used and/or requested in the code.
- //
- if (profile.getVersion() >= ProfileVersion::DX_6_2)
- {
- options.flags |= CompileOptions::Flag::EnableFloat16;
- }
-
- // Set the matrix layout
- options.matrixLayout =
- (SlangMatrixLayoutMode)getTargetProgram()->getOptionSet().getMatrixLayoutMode();
- }
-
- // Set the profile
- options.profileName = allocator.allocate(GetHLSLProfileName(profile));
- }
-
- // If we aren't using LLVM 'host callable', we want downstream compile to produce a shared
- // library
- if (compilerType != PassThroughMode::LLVM &&
- ArtifactDescUtil::makeDescForCompileTarget(asExternal(target)).kind ==
- ArtifactKind::HostCallable)
- {
- target = CodeGenTarget::ShaderSharedLibrary;
- }
-
- if (!isPassThroughEnabled())
- {
- if (_isCPUHostTarget(target))
- {
- libraryPaths.add(Path::getParentDirectory(Path::getExecutablePath()));
- libraryPaths.add(
- Path::combine(Path::getParentDirectory(Path::getExecutablePath()), "../lib"));
-
- // Set up the library artifact
- auto artifact = Artifact::create(
- ArtifactDesc::make(ArtifactKind::Library, Artifact::Payload::HostCPU),
- toSlice("slang-rt"));
-
- ComPtr<IOSFileArtifactRepresentation> fileRep(new OSFileArtifactRepresentation(
- IOSFileArtifactRepresentation::Kind::NameOnly,
- toSlice("slang-rt"),
- nullptr));
- artifact->addRepresentation(fileRep);
-
- libraries.add(artifact);
- }
- }
-
- options.targetType = (SlangCompileTarget)target;
-
- // Need to configure for the compilation
-
- {
- auto linkage = getLinkage();
-
- switch (getTargetProgram()->getOptionSet().getEnumOption<OptimizationLevel>(
- CompilerOptionName::Optimization))
- {
- case OptimizationLevel::None:
- options.optimizationLevel = DownstreamCompileOptions::OptimizationLevel::None;
- break;
- case OptimizationLevel::Default:
- options.optimizationLevel = DownstreamCompileOptions::OptimizationLevel::Default;
- break;
- case OptimizationLevel::High:
- options.optimizationLevel = DownstreamCompileOptions::OptimizationLevel::High;
- break;
- case OptimizationLevel::Maximal:
- options.optimizationLevel = DownstreamCompileOptions::OptimizationLevel::Maximal;
- break;
- default:
- SLANG_ASSERT(!"Unhandled optimization level");
- break;
- }
-
- switch (getTargetProgram()->getOptionSet().getEnumOption<DebugInfoLevel>(
- CompilerOptionName::DebugInformation))
- {
- case DebugInfoLevel::None:
- options.debugInfoType = DownstreamCompileOptions::DebugInfoType::None;
- break;
- case DebugInfoLevel::Minimal:
- options.debugInfoType = DownstreamCompileOptions::DebugInfoType::Minimal;
- break;
-
- case DebugInfoLevel::Standard:
- options.debugInfoType = DownstreamCompileOptions::DebugInfoType::Standard;
- break;
- case DebugInfoLevel::Maximal:
- options.debugInfoType = DownstreamCompileOptions::DebugInfoType::Maximal;
- break;
- default:
- SLANG_ASSERT(!"Unhandled debug level");
- break;
- }
-
- switch (getTargetProgram()->getOptionSet().getEnumOption<FloatingPointMode>(
- CompilerOptionName::FloatingPointMode))
- {
- case FloatingPointMode::Default:
- options.floatingPointMode = DownstreamCompileOptions::FloatingPointMode::Default;
- break;
- case FloatingPointMode::Precise:
- options.floatingPointMode = DownstreamCompileOptions::FloatingPointMode::Precise;
- break;
- case FloatingPointMode::Fast:
- options.floatingPointMode = DownstreamCompileOptions::FloatingPointMode::Fast;
- break;
- default:
- SLANG_ASSERT(!"Unhandled floating point mode");
- }
-
- if (getTargetProgram()->getOptionSet().hasOption(CompilerOptionName::DenormalModeFp16))
- {
- switch (getTargetProgram()->getOptionSet().getEnumOption<FloatingPointDenormalMode>(
- CompilerOptionName::DenormalModeFp16))
- {
- case FloatingPointDenormalMode::Any:
- options.denormalModeFp16 = DownstreamCompileOptions::FloatingPointDenormalMode::Any;
- break;
- case FloatingPointDenormalMode::Preserve:
- options.denormalModeFp16 =
- DownstreamCompileOptions::FloatingPointDenormalMode::Preserve;
- break;
- case FloatingPointDenormalMode::FlushToZero:
- options.denormalModeFp16 =
- DownstreamCompileOptions::FloatingPointDenormalMode::FlushToZero;
- break;
- default:
- SLANG_ASSERT(!"Unhandled fp16 denormal handling mode");
- }
- }
-
- if (getTargetProgram()->getOptionSet().hasOption(CompilerOptionName::DenormalModeFp32))
- {
- switch (getTargetProgram()->getOptionSet().getEnumOption<FloatingPointDenormalMode>(
- CompilerOptionName::DenormalModeFp32))
- {
- case FloatingPointDenormalMode::Any:
- options.denormalModeFp32 = DownstreamCompileOptions::FloatingPointDenormalMode::Any;
- break;
- case FloatingPointDenormalMode::Preserve:
- options.denormalModeFp32 =
- DownstreamCompileOptions::FloatingPointDenormalMode::Preserve;
- break;
- case FloatingPointDenormalMode::FlushToZero:
- options.denormalModeFp32 =
- DownstreamCompileOptions::FloatingPointDenormalMode::FlushToZero;
- break;
- default:
- SLANG_ASSERT(!"Unhandled fp32 denormal handling mode");
- }
- }
-
- if (getTargetProgram()->getOptionSet().hasOption(CompilerOptionName::DenormalModeFp64))
- {
- switch (getTargetProgram()->getOptionSet().getEnumOption<FloatingPointDenormalMode>(
- CompilerOptionName::DenormalModeFp64))
- {
- case FloatingPointDenormalMode::Any:
- options.denormalModeFp64 = DownstreamCompileOptions::FloatingPointDenormalMode::Any;
- break;
- case FloatingPointDenormalMode::Preserve:
- options.denormalModeFp64 =
- DownstreamCompileOptions::FloatingPointDenormalMode::Preserve;
- break;
- case FloatingPointDenormalMode::FlushToZero:
- options.denormalModeFp64 =
- DownstreamCompileOptions::FloatingPointDenormalMode::FlushToZero;
- break;
- default:
- SLANG_ASSERT(!"Unhandled fp64 denormal handling mode");
- }
- }
-
- {
- // We need to look at the stage of the entry point(s) we are
- // being asked to compile, since this will determine the
- // "pipeline" that the result should be compiled for (e.g.,
- // compute vs. ray tracing).
- //
- // TODO: This logic is kind of messy in that it assumes
- // a program to be compiled will only contain kernels for
- // a single pipeline type, but that invariant isn't expressed
- // at all in the front-end today. It also has no error
- // checking for the case where there are conflicts.
- //
- // HACK: Right now none of the above concerns matter
- // because we always perform code generation on a single
- // entry point at a time.
- //
- Index entryPointCount = getEntryPointCount();
- for (Index ee = 0; ee < entryPointCount; ++ee)
- {
- auto stage = getEntryPoint(ee)->getStage();
- switch (stage)
- {
- default:
- break;
-
- case Stage::Compute:
- options.pipelineType = DownstreamCompileOptions::PipelineType::Compute;
- break;
-
- case Stage::Vertex:
- case Stage::Hull:
- case Stage::Domain:
- case Stage::Geometry:
- case Stage::Fragment:
- options.pipelineType = DownstreamCompileOptions::PipelineType::Rasterization;
- break;
-
- case Stage::RayGeneration:
- case Stage::Intersection:
- case Stage::AnyHit:
- case Stage::ClosestHit:
- case Stage::Miss:
- case Stage::Callable:
- options.pipelineType = DownstreamCompileOptions::PipelineType::RayTracing;
- break;
- }
- }
- }
-
- // Add all the search paths (as calculated earlier - they will only be set if this is a pass
- // through else will be empty)
- options.includePaths = allocator.allocate(includePaths);
-
- // Add the specified defines (as calculated earlier - they will only be set if this is a
- // pass through else will be empty)
- {
- const auto count = preprocessorDefinitions.getCount();
- auto dst = allocator.getArena().allocateArray<DownstreamCompileOptions::Define>(count);
-
- Index i = 0;
-
- for (const auto& [defKey, defValue] : preprocessorDefinitions)
- {
- auto& define = dst[i];
-
- define.nameWithSig = allocator.allocate(defKey);
- define.value = allocator.allocate(defValue);
-
- ++i;
- }
- options.defines = makeSlice(dst, count);
- }
-
- // Add all of the module libraries
- libraries.addRange(linkage->m_libModules.getBuffer(), linkage->m_libModules.getCount());
- }
-
- auto program = getProgram();
-
- // Load embedded precompiled libraries from IR into library artifacts
- program->enumerateIRModules(
- [&](IRModule* irModule)
- {
- for (auto globalInst : irModule->getModuleInst()->getChildren())
- {
- if (target == CodeGenTarget::DXILAssembly || target == CodeGenTarget::DXIL)
- {
- if (auto inst = as<IREmbeddedDownstreamIR>(globalInst))
- {
- if (inst->getTarget() == CodeGenTarget::DXIL)
- {
- auto slice = inst->getBlob()->getStringSlice();
- ArtifactDesc desc =
- ArtifactDescUtil::makeDescForCompileTarget(SLANG_DXIL);
- desc.kind = ArtifactKind::Library;
-
- auto library = ArtifactUtil::createArtifact(desc);
-
- library->addRepresentationUnknown(StringBlob::create(slice));
- libraries.add(library);
- }
- }
- }
- }
- });
-
- options.compilerSpecificArguments = allocator.allocate(compilerSpecificArguments);
- options.requiredCapabilityVersions = SliceUtil::asSlice(requiredCapabilityVersions);
- options.libraries = SliceUtil::asSlice(libraries);
- options.libraryPaths = allocator.allocate(libraryPaths);
-
- if (m_targetProfile.getFamily() == ProfileFamily::DX)
- {
- options.enablePAQ = m_targetProfile.getVersion() >= ProfileVersion::DX_6_7;
- }
-
- // Compile
- ComPtr<IArtifact> artifact;
- auto downstreamStartTime = std::chrono::high_resolution_clock::now();
- SLANG_RETURN_ON_FAIL(compiler->compile(options, artifact.writeRef()));
- auto downstreamElapsedTime =
- (std::chrono::high_resolution_clock::now() - downstreamStartTime).count() * 0.000000001;
- getSession()->addDownstreamCompileTime(downstreamElapsedTime);
-
- SLANG_RETURN_ON_FAIL(passthroughDownstreamDiagnostics(getSink(), compiler, artifact));
-
- // Copy over all of the information associated with the source into the output
- if (sourceArtifact)
- {
- for (auto associatedArtifact : sourceArtifact->getAssociated())
- {
- artifact->addAssociated(associatedArtifact);
- }
- }
-
- // Set the artifact
- outArtifact.swap(artifact);
- return SLANG_OK;
-}
-
-SlangResult emitSPIRVForEntryPointsDirectly(
- CodeGenContext* codeGenContext,
- ComPtr<IArtifact>& outArtifact);
-
-SlangResult emitHostVMCode(CodeGenContext* codeGenContext, ComPtr<IArtifact>& outArtifact);
-
-static CodeGenTarget _getIntermediateTarget(CodeGenTarget target)
-{
- switch (target)
- {
- case CodeGenTarget::DXBytecodeAssembly:
- return CodeGenTarget::DXBytecode;
- case CodeGenTarget::DXILAssembly:
- return CodeGenTarget::DXIL;
- case CodeGenTarget::SPIRVAssembly:
- return CodeGenTarget::SPIRV;
- case CodeGenTarget::WGSLSPIRVAssembly:
- return CodeGenTarget::WGSLSPIRV;
- default:
- return CodeGenTarget::None;
- }
-}
-
-static IArtifact* _getSeparateDbgArtifact(IArtifact* artifact)
-{
- if (!artifact)
- return nullptr;
-
- // The first associated artifact of kind ObjectCode and SPIRV payload should be the debug
- // artifact.
- for (auto* associated : artifact->getAssociated())
- {
- auto desc = associated->getDesc();
- if (desc.kind == ArtifactKind::ObjectCode && desc.payload == ArtifactPayload::SPIRV)
- return associated;
- }
-
- return nullptr;
-}
-
-/// Function to simplify the logic around emitting, and dissassembling
-SlangResult CodeGenContext::_emitEntryPoints(ComPtr<IArtifact>& outArtifact)
-{
- auto target = getTargetFormat();
- switch (target)
- {
- case CodeGenTarget::SPIRVAssembly:
- case CodeGenTarget::DXBytecodeAssembly:
- case CodeGenTarget::DXILAssembly:
- case CodeGenTarget::MetalLibAssembly:
- case CodeGenTarget::WGSLSPIRVAssembly:
- {
- // First compile to an intermediate target for the corresponding binary format.
- const CodeGenTarget intermediateTarget = _getIntermediateTarget(target);
- CodeGenContext intermediateContext(this, intermediateTarget);
-
- ComPtr<IArtifact> intermediateArtifact;
-
- SLANG_RETURN_ON_FAIL(intermediateContext._emitEntryPoints(intermediateArtifact));
- intermediateContext.maybeDumpIntermediate(intermediateArtifact);
-
- // Then disassemble the intermediate binary result to get the desired output
- // Output the disassemble
- ComPtr<IArtifact> disassemblyArtifact;
- SLANG_RETURN_ON_FAIL(ArtifactOutputUtil::dissassembleWithDownstream(
- getSession(),
- intermediateArtifact,
- getSink(),
- disassemblyArtifact.writeRef()));
-
- // Also disassemble the debug artifact if one exists.
- auto debugArtifact = _getSeparateDbgArtifact(intermediateArtifact);
- ComPtr<IArtifact> disassemblyDebugArtifact;
- if (debugArtifact)
- {
- SLANG_RETURN_ON_FAIL(ArtifactOutputUtil::dissassembleWithDownstream(
- getSession(),
- debugArtifact,
- getSink(),
- disassemblyDebugArtifact.writeRef()));
- disassemblyDebugArtifact->setName(debugArtifact->getName());
-
- // The disassembly needs both the metadata for the debug build identifier
- // and the debug spirv to be associated with is.
- for (auto associated : intermediateArtifact->getAssociated())
- {
- if (associated->getDesc().payload == ArtifactPayload::Metadata ||
- associated->getDesc().payload == ArtifactPayload::PostEmitMetadata)
- {
- disassemblyArtifact->addAssociated(associated);
- break;
- }
- }
- disassemblyArtifact->addAssociated(disassemblyDebugArtifact);
- }
-
- outArtifact.swap(disassemblyArtifact);
- return SLANG_OK;
- }
- case CodeGenTarget::SPIRV:
- if (getTargetProgram()->getOptionSet().shouldEmitSPIRVDirectly())
- {
- SLANG_RETURN_ON_FAIL(emitSPIRVForEntryPointsDirectly(this, outArtifact));
- return SLANG_OK;
- }
- [[fallthrough]];
- case CodeGenTarget::DXIL:
- case CodeGenTarget::DXBytecode:
- case CodeGenTarget::MetalLib:
- case CodeGenTarget::PTX:
- case CodeGenTarget::ShaderHostCallable:
- case CodeGenTarget::ShaderSharedLibrary:
- case CodeGenTarget::HostExecutable:
- case CodeGenTarget::HostHostCallable:
- case CodeGenTarget::HostSharedLibrary:
- case CodeGenTarget::WGSLSPIRV:
- SLANG_RETURN_ON_FAIL(emitWithDownstreamForEntryPoints(outArtifact));
- return SLANG_OK;
- case CodeGenTarget::HostVM:
- SLANG_RETURN_ON_FAIL(emitHostVMCode(this, outArtifact));
- return SLANG_OK;
- default:
- break;
- }
-
- return SLANG_FAIL;
-}
-
-// Helper class for recording compile time.
-struct CompileTimerRAII
-{
- std::chrono::high_resolution_clock::time_point startTime;
- Session* session;
- CompileTimerRAII(Session* inSession)
- {
- startTime = std::chrono::high_resolution_clock::now();
- session = inSession;
- }
- ~CompileTimerRAII()
- {
- double elapsedTime = std::chrono::duration_cast<std::chrono::microseconds>(
- std::chrono::high_resolution_clock::now() - startTime)
- .count() /
- 1e6;
- session->addTotalCompileTime(elapsedTime);
- }
-};
-
-// Do emit logic for a zero or more entry points
-SlangResult CodeGenContext::emitEntryPoints(ComPtr<IArtifact>& outArtifact)
-{
- CompileTimerRAII recordCompileTime(getSession());
-
- auto target = getTargetFormat();
-
- switch (target)
- {
- case CodeGenTarget::SPIRVAssembly:
- case CodeGenTarget::DXBytecodeAssembly:
- case CodeGenTarget::DXILAssembly:
- case CodeGenTarget::SPIRV:
- case CodeGenTarget::DXIL:
- case CodeGenTarget::DXBytecode:
- case CodeGenTarget::MetalLib:
- case CodeGenTarget::MetalLibAssembly:
- case CodeGenTarget::PTX:
- case CodeGenTarget::HostHostCallable:
- case CodeGenTarget::ShaderHostCallable:
- case CodeGenTarget::ShaderSharedLibrary:
- case CodeGenTarget::HostExecutable:
- case CodeGenTarget::HostSharedLibrary:
- case CodeGenTarget::WGSLSPIRVAssembly:
- case CodeGenTarget::HostVM:
- {
- SLANG_RETURN_ON_FAIL(_emitEntryPoints(outArtifact));
-
- maybeDumpIntermediate(outArtifact);
- return SLANG_OK;
- }
- break;
- case CodeGenTarget::GLSL:
- case CodeGenTarget::HLSL:
- case CodeGenTarget::CUDASource:
- case CodeGenTarget::CPPSource:
- case CodeGenTarget::HostCPPSource:
- case CodeGenTarget::PyTorchCppBinding:
- case CodeGenTarget::CSource:
- case CodeGenTarget::Metal:
- case CodeGenTarget::WGSL:
- {
- RefPtr<ExtensionTracker> extensionTracker = _newExtensionTracker(target);
-
- CodeGenContext subContext(this, target, extensionTracker);
-
- ComPtr<IArtifact> sourceArtifact;
-
- SLANG_RETURN_ON_FAIL(subContext.emitEntryPointsSource(sourceArtifact));
-
- subContext.maybeDumpIntermediate(sourceArtifact);
- outArtifact = sourceArtifact;
- return SLANG_OK;
- }
- break;
-
- case CodeGenTarget::None:
- // The user requested no output
- return SLANG_OK;
-
- // Note(tfoley): We currently hit this case when compiling the core module
- case CodeGenTarget::Unknown:
- return SLANG_OK;
-
- default:
- SLANG_UNEXPECTED("unhandled code generation target");
- break;
- }
- return SLANG_FAIL;
-}
-
-void EndToEndCompileRequest::writeArtifactToStandardOutput(
- IArtifact* artifact,
- DiagnosticSink* sink)
-{
- // If it's host callable it's not available to write to output
- if (isDerivedFrom(artifact->getDesc().kind, ArtifactKind::HostCallable))
- {
- return;
- }
-
- auto session = getSession();
- ArtifactOutputUtil::maybeConvertAndWrite(
- session,
- artifact,
- sink,
- toSlice("stdout"),
- getWriter(WriterChannel::StdOutput));
-}
-
-String EndToEndCompileRequest::_getWholeProgramPath(TargetRequest* targetReq)
-{
- RefPtr<EndToEndCompileRequest::TargetInfo> targetInfo;
- if (m_targetInfos.tryGetValue(targetReq, targetInfo))
- {
- return targetInfo->wholeTargetOutputPath;
- }
- return String();
-}
-
-String EndToEndCompileRequest::_getEntryPointPath(TargetRequest* targetReq, Index entryPointIndex)
-{
- // 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 (m_targetInfos.tryGetValue(targetReq, targetInfo))
- {
- String outputPath;
- if (targetInfo->entryPointOutputPaths.tryGetValue(entryPointIndex, outputPath))
- {
- return outputPath;
- }
- }
-
- return String();
-}
-
-SlangResult EndToEndCompileRequest::_writeArtifact(const String& path, IArtifact* artifact)
-{
- if (path.getLength() > 0)
- {
- SLANG_RETURN_ON_FAIL(ArtifactOutputUtil::writeToFile(artifact, getSink(), path));
- }
- else if (m_containerFormat == ContainerFormat::None)
- {
- // If we aren't writing to a container and we didn't write to a file, we can output to
- // standard output
- writeArtifactToStandardOutput(artifact, getSink());
- }
- return SLANG_OK;
-}
-
-SlangResult EndToEndCompileRequest::_maybeWriteArtifact(const String& path, IArtifact* artifact)
-{
- // We don't have to do anything if there is no artifact
- if (!artifact)
- {
- return SLANG_OK;
- }
-
- // If embedding is enabled...
- if (m_sourceEmbedStyle != SourceEmbedUtil::Style::None)
- {
- SourceEmbedUtil::Options options;
-
- options.style = m_sourceEmbedStyle;
- options.variableName = m_sourceEmbedName;
- options.language = (SlangSourceLanguage)m_sourceEmbedLanguage;
-
- ComPtr<IArtifact> embeddedArtifact;
- SLANG_RETURN_ON_FAIL(SourceEmbedUtil::createEmbedded(artifact, options, embeddedArtifact));
-
- if (!embeddedArtifact)
- {
- return SLANG_FAIL;
- }
- SLANG_RETURN_ON_FAIL(
- _writeArtifact(SourceEmbedUtil::getPath(path, options), embeddedArtifact));
- return SLANG_OK;
- }
- else
- {
- SLANG_RETURN_ON_FAIL(_writeArtifact(path, artifact));
- }
-
- return SLANG_OK;
-}
-
-// These helper functions are used by the -separate-debug-info command line
-// arg to extract the associated artifact containing the debug SPIRV data
-// and save it to a file with a .dbg.spv extension.
-static String _getDebugSpvPath(const String& basePath)
-{
- // Find the last occurrence of ".spv" at the end of the string.
- static const char ext[] = ".spv";
- static const char dbgExt[] = ".dbg.spv";
- Index extLen = 4;
- if (basePath.getLength() >= extLen && basePath.endsWith(ext))
- {
- // Replace the ".spv" extension with ".dbg.spv"
- String prefix = String(basePath.subString(0, basePath.getLength() - extLen));
- return prefix + dbgExt;
- }
- // If it doesn't end with .spv, just append .dbg.spv
- return basePath + dbgExt;
-}
-
-SlangResult EndToEndCompileRequest::_maybeWriteDebugArtifact(
- TargetProgram* targetProgram,
- const String& path,
- IArtifact* artifact)
-{
- if (targetProgram->getOptionSet().getBoolOption(CompilerOptionName::EmitSeparateDebug))
- {
- const auto dbgArtifact = _getSeparateDbgArtifact(artifact);
- // Check if a debug artifact was actually created (only for SPIR-V targets)
- if (dbgArtifact)
- {
- // The artifact's name may have been set to the debug build id hash, use
- // it as the filename if it exists.
- String dbgPath = dbgArtifact->getName();
- if (dbgPath.getLength() == 0)
- dbgPath = _getDebugSpvPath(path);
- else
- dbgPath.append(".dbg.spv");
- return _maybeWriteArtifact(dbgPath, dbgArtifact);
- }
- // If no debug artifact exists (e.g., for non-SPIR-V targets), just silently succeed
- // The warning about unsupported targets is already issued during option parsing
- }
-
- return SLANG_OK;
-}
-
-IArtifact* TargetProgram::_createWholeProgramResult(
- DiagnosticSink* sink,
- EndToEndCompileRequest* endToEndReq)
-{
- // We want to call `emitEntryPoints` function to generate code that contains
- // all the entrypoints defined in `m_program`.
- // The current logic of `emitEntryPoints` takes a list of entry-point indices to
- // emit code for, so we construct such a list first.
- List<Int> entryPointIndices;
-
- m_entryPointResults.setCount(m_program->getEntryPointCount());
- entryPointIndices.setCount(m_program->getEntryPointCount());
- for (Index i = 0; i < entryPointIndices.getCount(); i++)
- entryPointIndices[i] = i;
-
- CodeGenContext::Shared sharedCodeGenContext(this, entryPointIndices, sink, endToEndReq);
- CodeGenContext codeGenContext(&sharedCodeGenContext);
-
- if (SLANG_FAILED(codeGenContext.emitEntryPoints(m_wholeProgramResult)))
- {
- return nullptr;
- }
-
- return m_wholeProgramResult;
-}
-
-IArtifact* TargetProgram::_createEntryPointResult(
- Int entryPointIndex,
- DiagnosticSink* sink,
- EndToEndCompileRequest* endToEndReq)
-{
- // It is possible that entry points got added to the `Program`
- // *after* we created this `TargetProgram`, so there might be
- // a request for an entry point that we didn't allocate space for.
- //
- // TODO: Change the construction logic so that a `Program` is
- // constructed all at once rather than incrementally, to avoid
- // this problem.
- //
- if (entryPointIndex >= m_entryPointResults.getCount())
- m_entryPointResults.setCount(entryPointIndex + 1);
-
-
- CodeGenContext::EntryPointIndices entryPointIndices;
- entryPointIndices.add(entryPointIndex);
-
- CodeGenContext::Shared sharedCodeGenContext(this, entryPointIndices, sink, endToEndReq);
- CodeGenContext codeGenContext(&sharedCodeGenContext);
-
- codeGenContext.emitEntryPoints(m_entryPointResults[entryPointIndex]);
-
- return m_entryPointResults[entryPointIndex];
-}
-
-IArtifact* TargetProgram::getOrCreateWholeProgramResult(DiagnosticSink* sink)
-{
- if (m_wholeProgramResult)
- return m_wholeProgramResult;
-
- // If we haven't yet computed a layout for this target
- // program, we need to make sure that is done before
- // code generation.
- //
- if (!getOrCreateIRModuleForLayout(sink))
- {
- return nullptr;
- }
-
- return _createWholeProgramResult(sink);
-}
-
-IArtifact* TargetProgram::getOrCreateEntryPointResult(Int entryPointIndex, DiagnosticSink* sink)
-{
- if (entryPointIndex >= m_entryPointResults.getCount())
- m_entryPointResults.setCount(entryPointIndex + 1);
-
- if (IArtifact* artifact = m_entryPointResults[entryPointIndex])
- return artifact;
-
- // If we haven't yet computed a layout for this target
- // program, we need to make sure that is done before
- // code generation.
- //
- if (!getOrCreateIRModuleForLayout(sink))
- {
- return nullptr;
- }
-
- return _createEntryPointResult(entryPointIndex, sink);
-}
-
-void EndToEndCompileRequest::generateOutput(TargetProgram* targetProgram)
-{
- auto program = targetProgram->getProgram();
-
- // Generate target code any entry points that
- // have been requested for compilation.
- auto entryPointCount = program->getEntryPointCount();
- if (targetProgram->getOptionSet().getBoolOption(CompilerOptionName::GenerateWholeProgram))
- {
- targetProgram->_createWholeProgramResult(getSink(), this);
- }
- else
- {
- for (Index ii = 0; ii < entryPointCount; ++ii)
- {
- targetProgram->_createEntryPointResult(ii, getSink(), this);
- }
- }
-}
-
-
-bool _shouldWriteSourceLocs(Linkage* linkage)
-{
- // If debug information or source manager are not avaiable we can't/shouldn't write out locs
- if (linkage->m_optionSet.getEnumOption<DebugInfoLevel>(CompilerOptionName::DebugInformation) ==
- DebugInfoLevel::None ||
- linkage->getSourceManager() == nullptr)
- {
- return false;
- }
-
- // Otherwise we do want to write out the locs
- return true;
-}
-
-SlangResult EndToEndCompileRequest::writeContainerToStream(Stream* stream)
-{
- auto linkage = getLinkage();
-
- // Set up options
- SerialContainerUtil::WriteOptions options;
-
- // If debug information is enabled, enable writing out source locs
- if (_shouldWriteSourceLocs(linkage))
- {
- options.sourceManagerToUseWhenSerializingSourceLocs = linkage->getSourceManager();
- }
-
- SLANG_RETURN_ON_FAIL(SerialContainerUtil::write(this, options, stream));
-
- return SLANG_OK;
-}
-
-static IBoxValue<SourceMap>* _getObfuscatedSourceMap(TranslationUnitRequest* translationUnit)
-{
- if (auto module = translationUnit->getModule())
- {
- if (auto irModule = module->getIRModule())
- {
- return irModule->getObfuscatedSourceMap();
- }
- }
- return nullptr;
-}
-
-SlangResult EndToEndCompileRequest::maybeCreateContainer()
-{
- m_containerArtifact.setNull();
-
- List<ComPtr<IArtifact>> artifacts;
-
- auto linkage = getLinkage();
-
- auto program = getSpecializedGlobalAndEntryPointsComponentType();
-
- for (auto targetReq : linkage->targets)
- {
- auto targetProgram = program->getTargetProgram(targetReq);
-
- if (targetProgram->getOptionSet().getBoolOption(CompilerOptionName::GenerateWholeProgram))
- {
- if (auto artifact = targetProgram->getExistingWholeProgramResult())
- {
- if (!targetProgram->getOptionSet().getBoolOption(
- CompilerOptionName::EmbedDownstreamIR))
- {
- artifacts.add(ComPtr<IArtifact>(artifact));
- }
- }
- }
- else
- {
- Index entryPointCount = program->getEntryPointCount();
- for (Index ee = 0; ee < entryPointCount; ++ee)
- {
- if (auto artifact = targetProgram->getExistingEntryPointResult(ee))
- {
- artifacts.add(ComPtr<IArtifact>(artifact));
- }
- }
- }
- }
-
- // If IR emitting is enabled, add IR to the artifacts
- if (m_emitIr && (m_containerFormat == ContainerFormat::SlangModule))
- {
- OwnedMemoryStream stream(FileAccess::Write);
- SlangResult res = writeContainerToStream(&stream);
- if (SLANG_FAILED(res))
- {
- getSink()->diagnose(SourceLoc(), Diagnostics::unableToCreateModuleContainer);
- return res;
- }
-
- // Need to turn into a blob
- List<uint8_t> blobData;
- stream.swapContents(blobData);
-
- auto containerBlob = ListBlob::moveCreate(blobData);
-
- auto irArtifact = Artifact::create(ArtifactDesc::make(
- Artifact::Kind::CompileBinary,
- ArtifactPayload::SlangIR,
- ArtifactStyle::Unknown));
- irArtifact->addRepresentationUnknown(containerBlob);
-
- // Add the IR artifact
- artifacts.add(irArtifact);
- }
-
- // If there is only one artifact we can use that as the container
- if (artifacts.getCount() == 1)
- {
- m_containerArtifact = artifacts[0];
- }
- else
- {
- m_containerArtifact = ArtifactUtil::createArtifact(
- ArtifactDesc::make(ArtifactKind::Container, ArtifactPayload::CompileResults));
-
- for (IArtifact* childArtifact : artifacts)
- {
- m_containerArtifact->addChild(childArtifact);
- }
- }
-
- // Get all of the source obfuscated source maps and add those
- if (m_containerArtifact)
- {
- auto frontEndReq = getFrontEndReq();
-
- for (auto translationUnit : frontEndReq->translationUnits)
- {
- // Hmmm do I have to therefore add a map for all translation units(!)
- // I guess this is okay in so far as an association can always be looked up by name
- if (auto sourceMap = _getObfuscatedSourceMap(translationUnit))
- {
- auto artifactDesc = ArtifactDesc::make(
- ArtifactKind::Json,
- ArtifactPayload::SourceMap,
- ArtifactStyle::Obfuscated);
-
- // Create the source map artifact
- auto sourceMapArtifact =
- Artifact::create(artifactDesc, sourceMap->get().m_file.getUnownedSlice());
-
- // Add the repesentation
- sourceMapArtifact->addRepresentation(sourceMap);
-
- // Associate with the container
- m_containerArtifact->addAssociated(sourceMapArtifact);
- }
- }
- }
-
- return SLANG_OK;
-}
-
-CompilerOptionSet& EndToEndCompileRequest::getTargetOptionSet(TargetRequest* req)
-{
- return req->getOptionSet();
-}
-
-CompilerOptionSet& EndToEndCompileRequest::getTargetOptionSet(Index targetIndex)
-{
- return m_linkage->targets[targetIndex]->getOptionSet();
-}
-
-SlangResult EndToEndCompileRequest::maybeWriteContainer(const String& fileName)
-{
- // If there is no container, or filename, don't write anything
- if (fileName.getLength() == 0 || !m_containerArtifact)
- {
- return SLANG_OK;
- }
-
- // Filter the containerArtifact into things that can be written
- ComPtr<IArtifact> writeArtifact;
- SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::filter(m_containerArtifact, writeArtifact));
-
- // Only write if there is something to write
- if (writeArtifact)
- {
- SLANG_RETURN_ON_FAIL(ArtifactContainerUtil::writeContainer(writeArtifact, fileName));
- }
-
- return SLANG_OK;
-}
-
-static void _writeString(Stream& stream, const char* string)
-{
- stream.write(string, strlen(string));
-}
-
-static void _escapeDependencyString(const char* string, StringBuilder& outBuilder)
-{
- // make has unusual escaping rules, but we only care about characters that are acceptable in a
- // path
- for (const char* p = string; *p; ++p)
- {
- char c = *p;
- switch (c)
- {
- case ' ':
- case ':':
- case '#':
- case '[':
- case ']':
- case '\\':
- outBuilder.appendChar('\\');
- break;
-
- case '$':
- outBuilder.appendChar('$');
- break;
- }
-
- outBuilder.appendChar(c);
- }
-}
-
-// Writes a line to the file stream, formatted like this:
-// <output-file>: <dependency-file> <dependency-file...>
-static void _writeDependencyStatement(
- Stream& stream,
- EndToEndCompileRequest* compileRequest,
- const String& outputPath)
-{
- if (outputPath.getLength() == 0)
- return;
-
- StringBuilder builder;
- _escapeDependencyString(outputPath.begin(), builder);
- _writeString(stream, builder.begin());
- _writeString(stream, ": ");
-
- int dependencyCount = compileRequest->getDependencyFileCount();
- for (int dependencyIndex = 0; dependencyIndex < dependencyCount; ++dependencyIndex)
- {
- builder.clear();
- _escapeDependencyString(compileRequest->getDependencyFilePath(dependencyIndex), builder);
- _writeString(stream, builder.begin());
- _writeString(stream, (dependencyIndex + 1 < dependencyCount) ? " " : "\n");
- }
-}
-
-// Writes a file with dependency info, with one line in the output file per compile product.
-static SlangResult _writeDependencyFile(EndToEndCompileRequest* compileRequest)
-{
- if (compileRequest->m_dependencyOutputPath.getLength() == 0)
- return SLANG_OK;
-
- FileStream stream;
- SLANG_RETURN_ON_FAIL(stream.init(
- compileRequest->m_dependencyOutputPath,
- FileMode::Create,
- FileAccess::Write,
- FileShare::ReadWrite));
-
- auto linkage = compileRequest->getLinkage();
- auto program = compileRequest->getSpecializedGlobalAndEntryPointsComponentType();
-
- // Iterate over all the targets and their outputs
- for (const auto& targetReq : linkage->targets)
- {
- if (compileRequest->getTargetOptionSet(targetReq).getBoolOption(
- CompilerOptionName::GenerateWholeProgram))
- {
- RefPtr<EndToEndCompileRequest::TargetInfo> targetInfo;
- if (compileRequest->m_targetInfos.tryGetValue(targetReq, targetInfo))
- {
- _writeDependencyStatement(
- stream,
- compileRequest,
- targetInfo->wholeTargetOutputPath);
- }
- }
- else
- {
- Index entryPointCount = program->getEntryPointCount();
- for (Index entryPointIndex = 0; entryPointIndex < entryPointCount; ++entryPointIndex)
- {
- RefPtr<EndToEndCompileRequest::TargetInfo> targetInfo;
- if (compileRequest->m_targetInfos.tryGetValue(targetReq, targetInfo))
- {
- String outputPath;
- if (targetInfo->entryPointOutputPaths.tryGetValue(entryPointIndex, outputPath))
- {
- _writeDependencyStatement(stream, compileRequest, outputPath);
- }
- }
- }
- }
- }
-
- // When the output is a binary module, linkage->targets can be empty. So
- // we need to do their dependencies separately.
- if (compileRequest->m_containerFormat == ContainerFormat::SlangModule)
- {
- _writeDependencyStatement(stream, compileRequest, compileRequest->m_containerOutputPath);
- }
-
- return SLANG_OK;
-}
-
-
-void EndToEndCompileRequest::generateOutput(ComponentType* program)
-{
- // When dynamic dispatch is disabled, the program must
- // be fully specialized by now. So we check if we still
- // have unspecialized generic/existential parameters,
- // and report them as an error.
- //
- auto specializationParamCount = program->getSpecializationParamCount();
- if (getOptionSet().getBoolOption(CompilerOptionName::DisableDynamicDispatch) &&
- specializationParamCount != 0)
- {
- auto sink = getSink();
-
- for (Index ii = 0; ii < specializationParamCount; ++ii)
- {
- auto specializationParam = program->getSpecializationParam(ii);
- if (auto decl = as<Decl>(specializationParam.object))
- {
- sink->diagnose(
- specializationParam.loc,
- Diagnostics::specializationParameterOfNameNotSpecialized,
- decl);
- }
- else if (auto type = as<Type>(specializationParam.object))
- {
- sink->diagnose(
- specializationParam.loc,
- Diagnostics::specializationParameterOfNameNotSpecialized,
- type);
- }
- else
- {
- sink->diagnose(
- specializationParam.loc,
- Diagnostics::specializationParameterNotSpecialized);
- }
- }
-
- return;
- }
-
-
- // Go through the code-generation targets that the user
- // has specified, and generate code for each of them.
- //
- auto linkage = getLinkage();
- for (auto targetReq : linkage->targets)
- {
- if (targetReq->getOptionSet().getBoolOption(CompilerOptionName::EmbedDownstreamIR))
- continue;
-
- auto targetProgram = program->getTargetProgram(targetReq);
- generateOutput(targetProgram);
- }
-}
-
-void EndToEndCompileRequest::generateOutput()
-{
- SLANG_PROFILE;
- generateOutput(getSpecializedGlobalAndEntryPointsComponentType());
-
- // If we are in command-line mode, we might be expected to actually
- // write output to one or more files here.
-
- if (m_isCommandLineCompile && m_containerFormat == ContainerFormat::None)
- {
- auto linkage = getLinkage();
- auto program = getSpecializedGlobalAndEntryPointsComponentType();
-
- for (auto targetReq : linkage->targets)
- {
- auto targetProgram = program->getTargetProgram(targetReq);
-
- if (targetProgram->getOptionSet().getBoolOption(
- CompilerOptionName::GenerateWholeProgram))
- {
- if (const auto artifact = targetProgram->getExistingWholeProgramResult())
- {
- const auto path = _getWholeProgramPath(targetReq);
-
- _maybeWriteArtifact(path, artifact);
-
- // If we are compiling separate debug info, check for the additional
- // SPIRV artifact and write that if needed.
- _maybeWriteDebugArtifact(targetProgram, path, artifact);
- }
- }
- else
- {
- Index entryPointCount = program->getEntryPointCount();
- for (Index ee = 0; ee < entryPointCount; ++ee)
- {
- if (const auto artifact = targetProgram->getExistingEntryPointResult(ee))
- {
- const auto path = _getEntryPointPath(targetReq, ee);
-
- _maybeWriteArtifact(path, artifact);
-
- // If we are compiling separate debug info, check for the additional
- // SPIRV artifact and write that if needed.
- _maybeWriteDebugArtifact(targetProgram, path, artifact);
- }
- }
- }
- }
- }
-
- // Maybe create the container
- maybeCreateContainer();
-
- // If it's a command line compile we may need to write the container to a file
- if (m_isCommandLineCompile)
- {
- // TODO(JS):
- // We could write the container into a source embedded format potentially
-
- maybeWriteContainer(m_containerOutputPath);
-
- _writeDependencyFile(this);
- }
-}
-
-// Debug logic for dumping intermediate outputs
-
-
-void CodeGenContext::_dumpIntermediateMaybeWithAssembly(IArtifact* artifact)
-{
- _dumpIntermediate(artifact);
-
- ComPtr<IArtifact> assembly;
- ArtifactOutputUtil::maybeDisassemble(getSession(), artifact, nullptr, assembly);
-
- if (assembly)
- {
- _dumpIntermediate(assembly);
- }
-}
-
-void CodeGenContext::_dumpIntermediate(IArtifact* artifact)
-{
- ComPtr<ISlangBlob> blob;
- if (SLANG_FAILED(artifact->loadBlob(ArtifactKeep::No, blob.writeRef())))
- {
- return;
- }
- _dumpIntermediate(artifact->getDesc(), blob->getBufferPointer(), blob->getBufferSize());
-}
-
-void CodeGenContext::_dumpIntermediate(const ArtifactDesc& desc, void const* data, size_t size)
-{
- // 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 std::atomic<uint32_t> counter(0);
-
- const uint32_t id = ++counter;
-
- // Just use the counter for the 'base name'
- StringBuilder basename;
-
- // Add the prefix
- basename << getIntermediateDumpPrefix();
-
- // Add the id
- basename << int(id);
-
- // Work out the filename based on the desc and the basename
- StringBuilder filename;
- ArtifactDescUtil::calcNameForDesc(desc, basename.getUnownedSlice(), filename);
-
- // If didn't produce a filename, use basename with .unknown extension
- if (filename.getLength() == 0)
- {
- filename = basename;
- filename << ".unknown";
- }
-
- // Write to a file
- ArtifactOutputUtil::writeToFile(desc, data, size, filename);
-}
-
-void CodeGenContext::maybeDumpIntermediate(IArtifact* artifact)
-{
- if (!shouldDumpIntermediates())
- return;
-
-
- _dumpIntermediateMaybeWithAssembly(artifact);
-}
-
-IRDumpOptions CodeGenContext::getIRDumpOptions()
-{
- if (auto endToEndReq = isEndToEndCompile())
- {
- return endToEndReq->getFrontEndReq()->m_irDumpOptions;
- }
- return IRDumpOptions();
-}
-
-bool CodeGenContext::shouldValidateIR()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(CompilerOptionName::ValidateIr);
-}
-
-bool CodeGenContext::shouldSkipSPIRVValidation()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(
- CompilerOptionName::SkipSPIRVValidation);
-}
-
-bool CodeGenContext::shouldDumpIR()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(CompilerOptionName::DumpIr);
-}
-
-bool CodeGenContext::shouldSkipDownstreamLinking()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(
- CompilerOptionName::SkipDownstreamLinking);
-}
-
-bool CodeGenContext::shouldReportCheckpointIntermediates()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(
- CompilerOptionName::ReportCheckpointIntermediates);
-}
-
-bool CodeGenContext::shouldDumpIntermediates()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(CompilerOptionName::DumpIntermediates);
-}
-
-bool CodeGenContext::shouldTrackLiveness()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(CompilerOptionName::TrackLiveness);
-}
-
-String CodeGenContext::getIntermediateDumpPrefix()
-{
- return getTargetProgram()->getOptionSet().getStringOption(
- CompilerOptionName::DumpIntermediatePrefix);
-}
-
-bool CodeGenContext::getUseUnknownImageFormatAsDefault()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(
- CompilerOptionName::DefaultImageFormatUnknown);
-}
-
-bool CodeGenContext::isSpecializationDisabled()
-{
- return getTargetProgram()->getOptionSet().getBoolOption(
- CompilerOptionName::DisableSpecialization);
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL Module::serialize(ISlangBlob** outSerializedBlob)
-{
- SerialContainerUtil::WriteOptions writeOptions;
- OwnedMemoryStream memoryStream(FileAccess::Write);
- SLANG_RETURN_ON_FAIL(SerialContainerUtil::write(this, writeOptions, &memoryStream));
- *outSerializedBlob = RawBlob::create(
- memoryStream.getContents().getBuffer(),
- (size_t)memoryStream.getContents().getCount())
- .detach();
- return SLANG_OK;
-}
-
-SLANG_NO_THROW SlangResult SLANG_MCALL Module::writeToFile(char const* fileName)
-{
- SerialContainerUtil::WriteOptions writeOptions;
- FileStream fileStream;
- SLANG_RETURN_ON_FAIL(fileStream.init(fileName, FileMode::Create));
- return SerialContainerUtil::write(this, writeOptions, &fileStream);
-}
-
-SLANG_NO_THROW const char* SLANG_MCALL Module::getName()
-{
- if (m_name)
- return m_name->text.getBuffer();
- return nullptr;
-}
-
-SLANG_NO_THROW const char* SLANG_MCALL Module::getFilePath()
-{
- if (m_pathInfo.hasFoundPath())
- return m_pathInfo.foundPath.getBuffer();
- return nullptr;
-}
-
-SLANG_NO_THROW const char* SLANG_MCALL Module::getUniqueIdentity()
-{
- if (m_pathInfo.hasUniqueIdentity())
- return m_pathInfo.getMostUniqueIdentity().getBuffer();
- return nullptr;
-}
-
-SLANG_NO_THROW SlangInt32 SLANG_MCALL Module::getDependencyFileCount()
-{
- return (SlangInt32)getFileDependencies().getCount();
-}
-
-SLANG_NO_THROW char const* SLANG_MCALL Module::getDependencyFilePath(SlangInt32 index)
-{
- SourceFile* sourceFile = getFileDependencies()[index];
- return sourceFile->getPathInfo().hasFoundPath()
- ? sourceFile->getPathInfo().getMostUniqueIdentity().getBuffer()
- : nullptr;
-}
-
-void validateEntryPoint(EntryPoint* entryPoint, DiagnosticSink* sink);
-
-void Module::_discoverEntryPoints(DiagnosticSink* sink, const List<RefPtr<TargetRequest>>& targets)
-{
- if (m_entryPoints.getCount() > 0)
- return;
- _discoverEntryPointsImpl(m_moduleDecl, sink, targets);
-}
-void Module::_discoverEntryPointsImpl(
- ContainerDecl* containerDecl,
- DiagnosticSink* sink,
- const List<RefPtr<TargetRequest>>& targets)
-{
- for (auto globalDecl : containerDecl->getDirectMemberDecls())
- {
- auto maybeFuncDecl = globalDecl;
- if (auto genericDecl = as<GenericDecl>(maybeFuncDecl))
- {
- maybeFuncDecl = genericDecl->inner;
- }
-
- if (as<NamespaceDeclBase>(globalDecl) || as<FileDecl>(globalDecl) ||
- as<StructDecl>(globalDecl))
- {
- _discoverEntryPointsImpl(as<ContainerDecl>(globalDecl), sink, targets);
- continue;
- }
-
- auto funcDecl = as<FuncDecl>(maybeFuncDecl);
- if (!funcDecl)
- continue;
-
- Profile profile;
- bool resolvedStageOfProfileWithEntryPoint = resolveStageOfProfileWithEntryPoint(
- profile,
- getLinkage()->m_optionSet,
- targets,
- funcDecl,
- sink);
- if (!resolvedStageOfProfileWithEntryPoint)
- {
- // If there isn't a [shader] attribute, look for a [numthreads] attribute
- // since that implicitly means a compute shader. We'll not do this when compiling for
- // CUDA/Torch since [numthreads] attributes are utilized differently for those targets.
- //
-
- bool allTargetsCUDARelated = true;
- for (auto target : targets)
- {
- if (!isCUDATarget(target) &&
- target->getTarget() != CodeGenTarget::PyTorchCppBinding)
- {
- allTargetsCUDARelated = false;
- break;
- }
- }
-
- if (allTargetsCUDARelated && targets.getCount() > 0)
- continue;
-
- bool canDetermineStage = false;
- for (auto modifier : funcDecl->modifiers)
- {
- if (as<NumThreadsAttribute>(modifier))
- {
- if (funcDecl->findModifier<OutputTopologyAttribute>())
- profile.setStage(Stage::Mesh);
- else
- profile.setStage(Stage::Compute);
- canDetermineStage = true;
- break;
- }
- else if (as<PatchConstantFuncAttribute>(modifier))
- {
- profile.setStage(Stage::Hull);
- canDetermineStage = true;
- break;
- }
- }
- if (!canDetermineStage)
- continue;
- }
-
- RefPtr<EntryPoint> entryPoint =
- EntryPoint::create(getLinkage(), makeDeclRef(funcDecl), profile);
-
- validateEntryPoint(entryPoint, sink);
-
- // Note: in the case that the user didn't explicitly
- // specify entry points and we are instead compiling
- // a shader "library," then we do not want to automatically
- // combine the entry points into groups in the generated
- // `Program`, since that would be slightly too magical.
- //
- // Instead, each entry point will end up in a singleton
- // group, so that its entry-point parameters lay out
- // independent of the others.
- //
- _addEntryPoint(entryPoint);
- }
-}
} // namespace Slang