https://github.com/shader-slang/slang
Tip revision: 45ae0eb9ada14b8ead3c9785e16cc234a4d31ef0 authored by Yong He on 01 November 2021, 17:53:42 UTC
Disable aarch64 build for releases. (#2000)
Disable aarch64 build for releases. (#2000)
Tip revision: 45ae0eb
slang-compiler.h
#ifndef SLANG_COMPILER_H_INCLUDED
#define SLANG_COMPILER_H_INCLUDED
#include "../core/slang-basic.h"
#include "../core/slang-shared-library.h"
#include "../core/slang-archive-file-system.h"
#include "../core/slang-file-system.h"
#include "../compiler-core/slang-downstream-compiler.h"
#include "../compiler-core/slang-name.h"
#include "../compiler-core/slang-include-system.h"
#include "../compiler-core/slang-command-line-args.h"
#include "../core/slang-std-writers.h"
#include "../../slang-com-ptr.h"
#include "slang-capability.h"
#include "slang-diagnostics.h"
#include "slang-preprocessor.h"
#include "slang-profile.h"
#include "slang-syntax.h"
#include "slang-serialize-ir-types.h"
#include "../../slang.h"
namespace Slang
{
struct PathInfo;
struct IncludeHandler;
class ProgramLayout;
class PtrType;
class TargetProgram;
class TargetRequest;
class TypeLayout;
enum class CompilerMode
{
ProduceLibrary,
ProduceShader,
GenerateChoice
};
enum class StageTarget
{
Unknown,
VertexShader,
HullShader,
DomainShader,
GeometryShader,
FragmentShader,
ComputeShader,
};
enum class CodeGenTarget
{
Unknown = SLANG_TARGET_UNKNOWN,
None = SLANG_TARGET_NONE,
GLSL = SLANG_GLSL,
GLSL_Vulkan = SLANG_GLSL_VULKAN,
GLSL_Vulkan_OneDesc = SLANG_GLSL_VULKAN_ONE_DESC,
HLSL = SLANG_HLSL,
SPIRV = SLANG_SPIRV,
SPIRVAssembly = SLANG_SPIRV_ASM,
DXBytecode = SLANG_DXBC,
DXBytecodeAssembly = SLANG_DXBC_ASM,
DXIL = SLANG_DXIL,
DXILAssembly = SLANG_DXIL_ASM,
CSource = SLANG_C_SOURCE,
CPPSource = SLANG_CPP_SOURCE,
Executable = SLANG_EXECUTABLE,
SharedLibrary = SLANG_SHARED_LIBRARY,
HostCallable = SLANG_HOST_CALLABLE,
CUDASource = SLANG_CUDA_SOURCE,
PTX = SLANG_PTX,
ObjectCode = SLANG_OBJECT_CODE,
CountOf = SLANG_TARGET_COUNT_OF,
};
void printDiagnosticArg(StringBuilder& sb, CodeGenTarget val);
enum class ContainerFormat : SlangContainerFormat
{
None = SLANG_CONTAINER_FORMAT_NONE,
SlangModule = SLANG_CONTAINER_FORMAT_SLANG_MODULE,
};
enum class LineDirectiveMode : SlangLineDirectiveMode
{
Default = SLANG_LINE_DIRECTIVE_MODE_DEFAULT,
None = SLANG_LINE_DIRECTIVE_MODE_NONE,
Standard = SLANG_LINE_DIRECTIVE_MODE_STANDARD,
GLSL = SLANG_LINE_DIRECTIVE_MODE_GLSL,
};
enum class ResultFormat
{
None,
Text,
Binary,
};
// When storing the layout for a matrix-type
// value, we need to know whether it has been
// laid out with row-major or column-major
// storage.
//
enum MatrixLayoutMode
{
kMatrixLayoutMode_RowMajor = SLANG_MATRIX_LAYOUT_ROW_MAJOR,
kMatrixLayoutMode_ColumnMajor = SLANG_MATRIX_LAYOUT_COLUMN_MAJOR,
};
enum class DebugInfoLevel : SlangDebugInfoLevel
{
None = SLANG_DEBUG_INFO_LEVEL_NONE,
Minimal = SLANG_DEBUG_INFO_LEVEL_MINIMAL,
Standard = SLANG_DEBUG_INFO_LEVEL_STANDARD,
Maximal = SLANG_DEBUG_INFO_LEVEL_MAXIMAL,
};
enum class OptimizationLevel : SlangOptimizationLevel
{
None = SLANG_OPTIMIZATION_LEVEL_NONE,
Default = SLANG_OPTIMIZATION_LEVEL_DEFAULT,
High = SLANG_OPTIMIZATION_LEVEL_HIGH,
Maximal = SLANG_OPTIMIZATION_LEVEL_MAXIMAL,
};
class Linkage;
class Module;
class FrontEndCompileRequest;
class BackEndCompileRequest;
class EndToEndCompileRequest;
class TranslationUnitRequest;
// Result of compiling an entry point.
// Should only ever be string, binary or shared library
class CompileResult
{
public:
CompileResult() = default;
explicit CompileResult(String const& str) : format(ResultFormat::Text), outputString(str) {}
explicit CompileResult(ISlangBlob* inBlob) : format(ResultFormat::Binary), blob(inBlob) {}
explicit CompileResult(DownstreamCompileResult* inDownstreamResult): format(ResultFormat::Binary), downstreamResult(inDownstreamResult) {}
explicit CompileResult(const UnownedStringSlice& slice ) : format(ResultFormat::Text), outputString(slice) {}
SlangResult getBlob(ComPtr<ISlangBlob>& outBlob) const;
SlangResult getSharedLibrary(ComPtr<ISlangSharedLibrary>& outSharedLibrary);
ResultFormat format = ResultFormat::None;
String outputString; ///< Only set if result type is ResultFormat::Text
mutable ComPtr<ISlangBlob> blob;
RefPtr<DownstreamCompileResult> downstreamResult;
};
/// Information collected about global or entry-point shader parameters
struct ShaderParamInfo
{
DeclRef<VarDeclBase> paramDeclRef;
Int firstSpecializationParamIndex = 0;
Int specializationParamCount = 0;
};
/// A request for the front-end to find and validate an entry-point function
struct FrontEndEntryPointRequest : RefObject
{
public:
/// Create a request for an entry point.
FrontEndEntryPointRequest(
FrontEndCompileRequest* compileRequest,
int translationUnitIndex,
Name* name,
Profile profile);
/// Get the parent front-end compile request.
FrontEndCompileRequest* getCompileRequest() { return m_compileRequest; }
/// Get the translation unit that contains the entry point.
TranslationUnitRequest* getTranslationUnit();
/// Get the name of the entry point to find.
Name* getName() { return m_name; }
/// Get the stage that the entry point is to be compiled for
Stage getStage() { return m_profile.getStage(); }
/// Get the profile that the entry point is to be compiled for
Profile getProfile() { return m_profile; }
/// Get the index to the translation unit
int getTranslationUnitIndex() const { return m_translationUnitIndex; }
private:
// The parent compile request
FrontEndCompileRequest* m_compileRequest;
// The index of the translation unit that will hold the entry point
int m_translationUnitIndex;
// The name of the entry point function to look for
Name* m_name;
// The profile to compile for (including stage)
Profile m_profile;
};
/// Tracks an ordered list of modules that something depends on.
struct ModuleDependencyList
{
public:
/// Get the list of modules that are depended on.
List<Module*> const& getModuleList() { return m_moduleList; }
/// Add a module and everything it depends on to the list.
void addDependency(Module* module);
/// Add a module to the list, but not the modules it depends on.
void addLeafDependency(Module* module);
private:
void _addDependency(Module* module);
List<Module*> m_moduleList;
HashSet<Module*> m_moduleSet;
};
/// Tracks an unordered list of filesystem paths that something depends on
struct FilePathDependencyList
{
public:
/// Get the list of paths that are depended on.
List<String> const& getFilePathList() { return m_filePathList; }
/// Add a path to the list, if it is not already present
void addDependency(String const& path);
/// Add all of the paths that `module` depends on to the list
void addDependency(Module* module);
private:
// TODO: We are using a `HashSet` here to deduplicate
// the paths so that we don't return the same path
// multiple times from `getFilePathList`, but because
// order isn't important, we could potentially do better
// in terms of memory (at some cost in performance) by
// just sorting the `m_filePathList` every once in
// a while and then deduplicating.
List<String> m_filePathList;
HashSet<String> m_filePathSet;
};
class EntryPoint;
class ComponentType;
class ComponentTypeVisitor;
/// Base class for "component types" that represent the pieces a final
/// shader program gets linked together from.
///
class ComponentType : public RefObject, public slang::IComponentType
{
public:
//
// ISlangUnknown interface
//
SLANG_REF_OBJECT_IUNKNOWN_ALL;
ISlangUnknown* getInterface(Guid const& guid);
//
// slang::IComponentType interface
//
SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() SLANG_OVERRIDE;
SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL getLayout(
SlangInt targetIndex,
slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
SlangInt entryPointIndex,
SlangInt targetIndex,
slang::IBlob** outCode,
slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
slang::SpecializationArg const* specializationArgs,
SlangInt specializationArgCount,
slang::IComponentType** outSpecializedComponentType,
ISlangBlob** outDiagnostics) SLANG_OVERRIDE;
SLANG_NO_THROW SlangResult SLANG_MCALL link(
slang::IComponentType** outLinkedComponentType,
ISlangBlob** outDiagnostics) SLANG_OVERRIDE;
SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
int entryPointIndex,
int targetIndex,
ISlangSharedLibrary** outSharedLibrary,
slang::IBlob** outDiagnostics) SLANG_OVERRIDE;
/// Get the linkage (aka "session" in the public API) for this component type.
Linkage* getLinkage() { return m_linkage; }
/// Get the target-specific version of this program for the given `target`.
///
/// The `target` must be a target on the `Linkage` that was used to create this program.
TargetProgram* getTargetProgram(TargetRequest* target);
/// Get the number of entry points linked into this component type.
virtual Index getEntryPointCount() = 0;
/// Get one of the entry points linked into this component type.
virtual RefPtr<EntryPoint> getEntryPoint(Index index) = 0;
/// Get the mangled name of one of the entry points linked into this component type.
virtual String getEntryPointMangledName(Index index) = 0;
/// Get the number of global shader parameters linked into this component type.
virtual Index getShaderParamCount() = 0;
/// Get one of the global shader parametesr linked into this component type.
virtual ShaderParamInfo getShaderParam(Index index) = 0;
/// Get the specialization parameter at `index`.
virtual SpecializationParam const& getSpecializationParam(Index index) = 0;
/// Get the number of "requirements" that this component type has.
///
/// A requirement represents another component type that this component
/// needs in order to function correctly. For example, the dependency
/// of one module on another module that it `import`s is represented
/// as a requirement, as is the dependency of an entry point on the
/// module that defines it.
///
virtual Index getRequirementCount() = 0;
/// Get the requirement at `index`.
virtual RefPtr<ComponentType> getRequirement(Index index) = 0;
/// Parse a type from a string, in the context of this component type.
///
/// Any names in the string will be resolved using the modules
/// referenced by the program.
///
/// On an error, returns null and reports diagnostic messages
/// to the provided `sink`.
///
/// TODO: This function shouldn't be on the base class, since
/// it only really makes sense on `Module`.
///
Type* getTypeFromString(
String const& typeStr,
DiagnosticSink* sink);
/// Get a list of modules that this component type depends on.
///
virtual List<Module*> const& getModuleDependencies() = 0;
/// Get the full list of filesystem paths this component type depends on.
///
virtual List<String> const& getFilePathDependencies() = 0;
/// Callback for use with `enumerateIRModules`
typedef void (*EnumerateIRModulesCallback)(IRModule* irModule, void* userData);
/// Invoke `callback` on all the IR modules that are (transitively) linked into this component type.
void enumerateIRModules(EnumerateIRModulesCallback callback, void* userData);
/// Invoke `callback` on all the IR modules that are (transitively) linked into this component type.
template<typename F>
void enumerateIRModules(F const& callback)
{
struct Helper
{
static void helper(IRModule* irModule, void* userData)
{
(*(F*)userData)(irModule);
}
};
enumerateIRModules(&Helper::helper, (void*)&callback);
}
/// Callback for use with `enumerateModules`
typedef void (*EnumerateModulesCallback)(Module* module, void* userData);
/// Invoke `callback` on all the modules that are (transitively) linked into this component type.
void enumerateModules(EnumerateModulesCallback callback, void* userData);
/// Invoke `callback` on all the modules that are (transitively) linked into this component type.
template<typename F>
void enumerateModules(F const& callback)
{
struct Helper
{
static void helper(Module* module, void* userData)
{
(*(F*)userData)(module);
}
};
enumerateModules(&Helper::helper, (void*)&callback);
}
/// Side-band information generated when specializing this component type.
///
/// Difference subclasses of `ComponentType` are expected to create their
/// own subclass of `SpecializationInfo` as the output of `_validateSpecializationArgs`.
/// Later, whenever we want to use a specialized component type we will
/// also have the `SpecializationInfo` available and will expect it to
/// have the correct (subclass-specific) type.
///
class SpecializationInfo : public RefObject
{
};
/// Validate the given specialization `args` and compute any side-band specialization info.
///
/// Any errors will be reported to `sink`, which can thus be used to test
/// if the operation was successful.
///
/// A null return value is allowed, since not all subclasses require
/// custom side-band specialization information.
///
/// This function is an implementation detail of `specialize()`.
///
virtual RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
SpecializationArg const* args,
Index argCount,
DiagnosticSink* sink) = 0;
/// Validate the given specialization `args` and compute any side-band specialization info.
///
/// Any errors will be reported to `sink`, which can thus be used to test
/// if the operation was successful.
///
/// A null return value is allowed, since not all subclasses require
/// custom side-band specialization information.
///
/// This function is an implementation detail of `specialize()`.
///
RefPtr<SpecializationInfo> _validateSpecializationArgs(
SpecializationArg const* args,
Index argCount,
DiagnosticSink* sink)
{
if(argCount == 0) return nullptr;
return _validateSpecializationArgsImpl(args, argCount, sink);
}
/// Specialize this component type given `specializationArgs`
///
/// Any diagnostics will be reported to `sink`, which can be used
/// to determine if the operation was successful. It is allowed
/// for this operation to have a non-null return even when an
/// error is ecnountered.
///
RefPtr<ComponentType> specialize(
SpecializationArg const* specializationArgs,
SlangInt specializationArgCount,
DiagnosticSink* sink);
/// Invoke `visitor` on this component type, using the appropriate dynamic type.
///
/// This function implements the "visitor pattern" for `ComponentType`.
///
/// If the `specializationInfo` argument is non-null, it must be specialization
/// information generated for this specific component type by `_validateSpecializationArgs`.
/// In that case, appropriately-typed specialization information will be passed
/// when invoking the `visitor`.
///
virtual void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo) = 0;
/// Create a scope suitable for looking up names or parsing specialization arguments.
///
/// This facility is only needed to support legacy APIs for string-based lookup
/// and parsing via Slang reflection, and is not recommended for future APIs to use.
///
Scope* _createScopeForLegacyLookup(ASTBuilder* astBuilder);
protected:
ComponentType(Linkage* linkage);
private:
Linkage* m_linkage;
// Cache of target-specific programs for each target.
Dictionary<TargetRequest*, RefPtr<TargetProgram>> m_targetPrograms;
// Any types looked up dynamically using `getTypeFromString`
//
// TODO: Remove this. Type lookup should only be supported on `Module`s.
//
Dictionary<String, Type*> m_types;
};
/// A component type built up from other component types.
class CompositeComponentType : public ComponentType
{
public:
static RefPtr<ComponentType> create(
Linkage* linkage,
List<RefPtr<ComponentType>> const& childComponents);
List<RefPtr<ComponentType>> const& getChildComponents() { return m_childComponents; };
Index getChildComponentCount() { return m_childComponents.getCount(); }
RefPtr<ComponentType> getChildComponent(Index index) { return m_childComponents[index]; }
Index getEntryPointCount() SLANG_OVERRIDE;
RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE;
String getEntryPointMangledName(Index index) SLANG_OVERRIDE;
Index getShaderParamCount() SLANG_OVERRIDE;
ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE;
SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE;
SpecializationParam const& getSpecializationParam(Index index) SLANG_OVERRIDE;
Index getRequirementCount() SLANG_OVERRIDE;
RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE;
List<Module*> const& getModuleDependencies() SLANG_OVERRIDE;
List<String> const& getFilePathDependencies() SLANG_OVERRIDE;
class CompositeSpecializationInfo : public SpecializationInfo
{
public:
List<RefPtr<SpecializationInfo>> childInfos;
};
protected:
void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo) SLANG_OVERRIDE;
RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
SpecializationArg const* args,
Index argCount,
DiagnosticSink* sink) SLANG_OVERRIDE;
private:
CompositeComponentType(
Linkage* linkage,
List<RefPtr<ComponentType>> const& childComponents);
List<RefPtr<ComponentType>> m_childComponents;
// The following arrays hold the concatenated entry points, parameters,
// etc. from the child components. This approach allows for reasonably
// fast (constant time) access through operations like `getShaderParam`,
// but means that the memory usage of a composite is proportional to
// the sum of the memory usage of the children, rather than being fixed
// by the number of children (as it would be if we just stored
// `m_childComponents`).
//
// TODO: We could conceivably build some O(numChildren) arrays that
// support binary-search to provide logarithmic-time access to entry
// points, parameters, etc. while giving a better overall memory usage.
//
List<EntryPoint*> m_entryPoints;
List<String> m_entryPointMangledNames;
List<ShaderParamInfo> m_shaderParams;
List<SpecializationParam> m_specializationParams;
List<ComponentType*> m_requirements;
ModuleDependencyList m_moduleDependencyList;
FilePathDependencyList m_filePathDependencyList;
};
/// A component type created by specializing another component type.
class SpecializedComponentType : public ComponentType
{
public:
SpecializedComponentType(
ComponentType* base,
SpecializationInfo* specializationInfo,
List<SpecializationArg> const& specializationArgs,
DiagnosticSink* sink);
/// Get the base (unspecialized) component type that is being specialized.
RefPtr<ComponentType> getBaseComponentType() { return m_base; }
RefPtr<SpecializationInfo> getSpecializationInfo() { return m_specializationInfo; }
/// Get the number of arguments supplied for existential type parameters.
///
/// Note that the number of arguments may not match the number of parameters.
/// In particular, an unspecialized entry point may have many parameters, but zero arguments.
Index getSpecializationArgCount() { return m_specializationArgs.getCount(); }
/// Get the existential type argument (type and witness table) at `index`.
SpecializationArg const& getSpecializationArg(Index index) { return m_specializationArgs[index]; }
/// Get an array of all existential type arguments.
SpecializationArg const* getSpecializationArgs() { return m_specializationArgs.getBuffer(); }
Index getEntryPointCount() SLANG_OVERRIDE { return m_base->getEntryPointCount(); }
RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE { return m_base->getEntryPoint(index); }
String getEntryPointMangledName(Index index) SLANG_OVERRIDE;
Index getShaderParamCount() SLANG_OVERRIDE { return m_base->getShaderParamCount(); }
ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE { return m_base->getShaderParam(index); }
SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE { return 0; }
SpecializationParam const& getSpecializationParam(Index index) SLANG_OVERRIDE { SLANG_UNUSED(index); static SpecializationParam dummy; return dummy; }
Index getRequirementCount() SLANG_OVERRIDE;
RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE;
List<Module*> const& getModuleDependencies() SLANG_OVERRIDE { return m_moduleDependencies; }
List<String> const& getFilePathDependencies() SLANG_OVERRIDE { return m_filePathDependencies; }
/// Get a list of tagged-union types referenced by the specialization parameters.
List<TaggedUnionType*> const& getTaggedUnionTypes() { return m_taggedUnionTypes; }
RefPtr<IRModule> getIRModule() { return m_irModule; }
void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo) SLANG_OVERRIDE;
protected:
RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
SpecializationArg const* args,
Index argCount,
DiagnosticSink* sink) SLANG_OVERRIDE
{
SLANG_UNUSED(args);
SLANG_UNUSED(argCount);
SLANG_UNUSED(sink);
return nullptr;
}
private:
RefPtr<ComponentType> m_base;
RefPtr<SpecializationInfo> m_specializationInfo;
SpecializationArgs m_specializationArgs;
RefPtr<IRModule> m_irModule;
List<String> m_entryPointMangledNames;
// Any tagged union types that were referenced by the specialization arguments.
List<TaggedUnionType*> m_taggedUnionTypes;
List<Module*> m_moduleDependencies;
List<String> m_filePathDependencies;
List<RefPtr<ComponentType>> m_requirements;
};
/// Describes an entry point for the purposes of layout and code generation.
///
/// This class also tracks any generic arguments to the entry point,
/// in the case that it is a specialization of a generic entry point.
///
/// There is also a provision for creating a "dummy" entry point for
/// the purposes of pass-through compilation modes. Only the
/// `getName()` and `getProfile()` methods should be expected to
/// return useful data on pass-through entry points.
///
class EntryPoint : public ComponentType, public slang::IEntryPoint
{
typedef ComponentType Super;
public:
SLANG_REF_OBJECT_IUNKNOWN_ALL
ISlangUnknown* getInterface(const Guid& guid);
// Forward `IComponentType` methods
SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() SLANG_OVERRIDE
{
return Super::getSession();
}
SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL getLayout(
SlangInt targetIndex,
slang::IBlob** outDiagnostics) SLANG_OVERRIDE
{
return Super::getLayout(targetIndex, outDiagnostics);
}
SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
SlangInt entryPointIndex,
SlangInt targetIndex,
slang::IBlob** outCode,
slang::IBlob** outDiagnostics) SLANG_OVERRIDE
{
return Super::getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
}
SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
slang::SpecializationArg const* specializationArgs,
SlangInt specializationArgCount,
slang::IComponentType** outSpecializedComponentType,
ISlangBlob** outDiagnostics) SLANG_OVERRIDE
{
return Super::specialize(
specializationArgs,
specializationArgCount,
outSpecializedComponentType,
outDiagnostics);
}
SLANG_NO_THROW SlangResult SLANG_MCALL link(
slang::IComponentType** outLinkedComponentType,
ISlangBlob** outDiagnostics) SLANG_OVERRIDE
{
return Super::link(
outLinkedComponentType,
outDiagnostics);
}
SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
int entryPointIndex,
int targetIndex,
ISlangSharedLibrary** outSharedLibrary,
slang::IBlob** outDiagnostics) SLANG_OVERRIDE
{
return Super::getEntryPointHostCallable(entryPointIndex, targetIndex, outSharedLibrary, outDiagnostics);
}
/// Create an entry point that refers to the given function.
static RefPtr<EntryPoint> create(
Linkage* linkage,
DeclRef<FuncDecl> funcDeclRef,
Profile profile);
/// Get the function decl-ref, including any generic arguments.
DeclRef<FuncDecl> getFuncDeclRef() { return m_funcDeclRef; }
/// Get the function declaration (without generic arguments).
FuncDecl* getFuncDecl() { return m_funcDeclRef.getDecl(); }
/// Get the name of the entry point
Name* getName() { return m_name; }
/// Get the profile associated with the entry point
///
/// Note: only the stage part of the profile is expected
/// to contain useful data, but certain legacy code paths
/// allow for "shader model" information to come via this path.
///
Profile getProfile() { return m_profile; }
/// Get the stage that the entry point is for.
Stage getStage() { return m_profile.getStage(); }
/// Get the module that contains the entry point.
Module* getModule();
/// Get a list of modules that this entry point depends on.
///
/// This will include the module that defines the entry point (see `getModule()`),
/// but may also include modules that are required by its generic type arguments.
///
List<Module*> const& getModuleDependencies() SLANG_OVERRIDE; // { return getModule()->getModuleDependencies(); }
List<String> const& getFilePathDependencies() SLANG_OVERRIDE; // { return getModule()->getFilePathDependencies(); }
/// Create a dummy `EntryPoint` that is only usable for pass-through compilation.
static RefPtr<EntryPoint> createDummyForPassThrough(
Linkage* linkage,
Name* name,
Profile profile);
/// Create a dummy `EntryPoint` that stands in for a serialized entry point
static RefPtr<EntryPoint> createDummyForDeserialize(
Linkage* linkage,
Name* name,
Profile profile,
String mangledName);
/// Get the number of existential type parameters for the entry point.
SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE;
/// Get the existential type parameter at `index`.
SpecializationParam const& getSpecializationParam(Index index) SLANG_OVERRIDE;
Index getRequirementCount() SLANG_OVERRIDE;
RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE;
SpecializationParams const& getExistentialSpecializationParams() { return m_existentialSpecializationParams; }
Index getGenericSpecializationParamCount() { return m_genericSpecializationParams.getCount(); }
Index getExistentialSpecializationParamCount() { return m_existentialSpecializationParams.getCount(); }
/// Get an array of all entry-point shader parameters.
List<ShaderParamInfo> const& getShaderParams() { return m_shaderParams; }
Index getEntryPointCount() SLANG_OVERRIDE { return 1; };
RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE { SLANG_UNUSED(index); return this; }
String getEntryPointMangledName(Index index) SLANG_OVERRIDE;
Index getShaderParamCount() SLANG_OVERRIDE { return 0; }
ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE { SLANG_UNUSED(index); return ShaderParamInfo(); }
class EntryPointSpecializationInfo : public SpecializationInfo
{
public:
DeclRef<FuncDecl> specializedFuncDeclRef;
List<ExpandedSpecializationArg> existentialSpecializationArgs;
};
protected:
void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo) SLANG_OVERRIDE;
RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
SpecializationArg const* args,
Index argCount,
DiagnosticSink* sink) SLANG_OVERRIDE;
private:
EntryPoint(
Linkage* linkage,
Name* name,
Profile profile,
DeclRef<FuncDecl> funcDeclRef);
void _collectGenericSpecializationParamsRec(Decl* decl);
void _collectShaderParams();
// The name of the entry point function (e.g., `main`)
//
Name* m_name = nullptr;
// The declaration of the entry-point function itself.
//
DeclRef<FuncDecl> m_funcDeclRef;
/// The mangled name of the entry point function
String m_mangledName;
SpecializationParams m_genericSpecializationParams;
SpecializationParams m_existentialSpecializationParams;
/// Information about entry-point parameters
List<ShaderParamInfo> m_shaderParams;
// The profile that the entry point will be compiled for
// (this is a combination of the target stage, and also
// a feature level that sets capabilities)
//
// Note: the profile-version part of this should probably
// be moving towards deprecation, in favor of the version
// information (e.g., "Shader Model 5.1") always coming
// from the target, while the stage part is all that is
// intrinsic to the entry point.
//
Profile m_profile;
};
class TypeConformance
: public ComponentType
, public slang::ITypeConformance
{
typedef ComponentType Super;
public:
SLANG_REF_OBJECT_IUNKNOWN_ALL
ISlangUnknown* getInterface(const Guid& guid);
TypeConformance(
Linkage* linkage,
SubtypeWitness* witness,
Int confomrmanceIdOverride,
DiagnosticSink* sink);
// Forward `IComponentType` methods
SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() SLANG_OVERRIDE
{
return Super::getSession();
}
SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL
getLayout(SlangInt targetIndex, slang::IBlob** outDiagnostics) SLANG_OVERRIDE
{
return Super::getLayout(targetIndex, outDiagnostics);
}
SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
SlangInt entryPointIndex,
SlangInt targetIndex,
slang::IBlob** outCode,
slang::IBlob** outDiagnostics) SLANG_OVERRIDE
{
return Super::getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
}
SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
slang::SpecializationArg const* specializationArgs,
SlangInt specializationArgCount,
slang::IComponentType** outSpecializedComponentType,
ISlangBlob** outDiagnostics) SLANG_OVERRIDE
{
return Super::specialize(
specializationArgs,
specializationArgCount,
outSpecializedComponentType,
outDiagnostics);
}
SLANG_NO_THROW SlangResult SLANG_MCALL link(
slang::IComponentType** outLinkedComponentType,
ISlangBlob** outDiagnostics) SLANG_OVERRIDE
{
return Super::link(outLinkedComponentType, outDiagnostics);
}
SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
int entryPointIndex,
int targetIndex,
ISlangSharedLibrary** outSharedLibrary,
slang::IBlob** outDiagnostics) SLANG_OVERRIDE
{
return Super::getEntryPointHostCallable(
entryPointIndex, targetIndex, outSharedLibrary, outDiagnostics);
}
List<Module*> const& getModuleDependencies() SLANG_OVERRIDE;
List<String> const& getFilePathDependencies() SLANG_OVERRIDE;
SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE { return 0; }
/// Get the existential type parameter at `index`.
SpecializationParam const& getSpecializationParam(Index /*index*/) SLANG_OVERRIDE
{
static SpecializationParam emptyParam;
return emptyParam;
}
Index getRequirementCount() SLANG_OVERRIDE;
RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE;
Index getEntryPointCount() SLANG_OVERRIDE { return 0; };
RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE
{
SLANG_UNUSED(index);
return nullptr;
}
String getEntryPointMangledName(Index /*index*/) SLANG_OVERRIDE { return ""; }
Index getShaderParamCount() SLANG_OVERRIDE { return 0; }
ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE
{
SLANG_UNUSED(index);
return ShaderParamInfo();
}
SubtypeWitness* getSubtypeWitness() { return m_subtypeWitness; }
IRModule* getIRModule() { return m_irModule.Ptr(); }
protected:
void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo)
SLANG_OVERRIDE;
RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
SpecializationArg const* args,
Index argCount,
DiagnosticSink* sink) SLANG_OVERRIDE;
private:
SubtypeWitness* m_subtypeWitness;
ModuleDependencyList m_moduleDependency;
FilePathDependencyList m_pathDependency;
List<RefPtr<Module>> m_requirements;
HashSet<Module*> m_requirementSet;
RefPtr<IRModule> m_irModule;
Int m_conformanceIdOverride;
void addDepedencyFromWitness(SubtypeWitness* witness);
};
enum class PassThroughMode : SlangPassThroughIntegral
{
None = SLANG_PASS_THROUGH_NONE, ///< don't pass through: use Slang compiler
Fxc = SLANG_PASS_THROUGH_FXC, ///< pass through HLSL to `D3DCompile` API
Dxc = SLANG_PASS_THROUGH_DXC, ///< pass through HLSL to `IDxcCompiler` API
Glslang = SLANG_PASS_THROUGH_GLSLANG, ///< pass through GLSL to `glslang` library
Clang = SLANG_PASS_THROUGH_CLANG, ///< Pass through clang compiler
VisualStudio = SLANG_PASS_THROUGH_VISUAL_STUDIO, ///< Visual studio compiler
Gcc = SLANG_PASS_THROUGH_GCC, ///< Gcc compiler
GenericCCpp = SLANG_PASS_THROUGH_GENERIC_C_CPP, ///< Generic C/C++ compiler
NVRTC = SLANG_PASS_THROUGH_NVRTC, ///< NVRTC CUDA compiler
LLVM = SLANG_PASS_THROUGH_LLVM, ///< LLVM 'compiler'
CountOf = SLANG_PASS_THROUGH_COUNT_OF,
};
void printDiagnosticArg(StringBuilder& sb, PassThroughMode val);
class SourceFile;
/// A module of code that has been compiled through the front-end
///
/// A module comprises all the code from one translation unit (which
/// may span multiple Slang source files), and provides access
/// to both the AST and IR representations of that code.
///
class Module : public ComponentType, public slang::IModule
{
typedef ComponentType Super;
public:
SLANG_REF_OBJECT_IUNKNOWN_ALL
ISlangUnknown* getInterface(const Guid& guid);
// Forward `IComponentType` methods
SLANG_NO_THROW slang::ISession* SLANG_MCALL getSession() SLANG_OVERRIDE
{
return Super::getSession();
}
SLANG_NO_THROW slang::ProgramLayout* SLANG_MCALL getLayout(
SlangInt targetIndex,
slang::IBlob** outDiagnostics) SLANG_OVERRIDE
{
return Super::getLayout(targetIndex, outDiagnostics);
}
SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCode(
SlangInt entryPointIndex,
SlangInt targetIndex,
slang::IBlob** outCode,
slang::IBlob** outDiagnostics) SLANG_OVERRIDE
{
return Super::getEntryPointCode(entryPointIndex, targetIndex, outCode, outDiagnostics);
}
SLANG_NO_THROW SlangResult SLANG_MCALL specialize(
slang::SpecializationArg const* specializationArgs,
SlangInt specializationArgCount,
slang::IComponentType** outSpecializedComponentType,
ISlangBlob** outDiagnostics) SLANG_OVERRIDE
{
return Super::specialize(
specializationArgs,
specializationArgCount,
outSpecializedComponentType,
outDiagnostics);
}
SLANG_NO_THROW SlangResult SLANG_MCALL link(
slang::IComponentType** outLinkedComponentType,
ISlangBlob** outDiagnostics) SLANG_OVERRIDE
{
return Super::link(
outLinkedComponentType,
outDiagnostics);
}
SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(
int entryPointIndex,
int targetIndex,
ISlangSharedLibrary** outSharedLibrary,
slang::IBlob** outDiagnostics) SLANG_OVERRIDE
{
return Super::getEntryPointHostCallable(entryPointIndex, targetIndex, outSharedLibrary, outDiagnostics);
}
SLANG_NO_THROW SlangResult SLANG_MCALL findEntryPointByName(
char const* name,
slang::IEntryPoint** outEntryPoint) SLANG_OVERRIDE
{
ComPtr<slang::IEntryPoint> entryPoint(findEntryPointByName(UnownedStringSlice(name)));
if((!entryPoint))
return SLANG_FAIL;
*outEntryPoint = entryPoint.detach();
return SLANG_OK;
}
//
/// Create a module (initially empty).
Module(Linkage* linkage, ASTBuilder* astBuilder = nullptr);
/// Get the AST for the module (if it has been parsed)
ModuleDecl* getModuleDecl() { return m_moduleDecl; }
/// The the IR for the module (if it has been generated)
IRModule* getIRModule() { return m_irModule; }
/// Get the list of other modules this module depends on
List<Module*> const& getModuleDependencyList() { return m_moduleDependencyList.getModuleList(); }
/// Get the list of filesystem paths this module depends on
List<String> const& getFilePathDependencyList() { return m_filePathDependencyList.getFilePathList(); }
/// Register a module that this module depends on
void addModuleDependency(Module* module);
/// Register a filesystem path that this module depends on
void addFilePathDependency(String const& path);
/// Set the AST for this module.
///
/// This should only be called once, during creation of the module.
///
void setModuleDecl(ModuleDecl* moduleDecl);// { m_moduleDecl = moduleDecl; }
/// Set the IR for this module.
///
/// This should only be called once, during creation of the module.
///
void setIRModule(IRModule* irModule) { m_irModule = irModule; }
Index getEntryPointCount() SLANG_OVERRIDE { return 0; }
RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE { SLANG_UNUSED(index); return nullptr; }
String getEntryPointMangledName(Index index) SLANG_OVERRIDE { SLANG_UNUSED(index); return String(); }
Index getShaderParamCount() SLANG_OVERRIDE { return m_shaderParams.getCount(); }
ShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE { return m_shaderParams[index]; }
SLANG_NO_THROW Index SLANG_MCALL getSpecializationParamCount() SLANG_OVERRIDE { return m_specializationParams.getCount(); }
SpecializationParam const& getSpecializationParam(Index index) SLANG_OVERRIDE { return m_specializationParams[index]; }
Index getRequirementCount() SLANG_OVERRIDE;
RefPtr<ComponentType> getRequirement(Index index) SLANG_OVERRIDE;
List<Module*> const& getModuleDependencies() SLANG_OVERRIDE { return m_moduleDependencyList.getModuleList(); }
List<String> const& getFilePathDependencies() SLANG_OVERRIDE { return m_filePathDependencyList.getFilePathList(); }
/// Given a mangled name finds the exported NodeBase associated with this module.
/// If not found returns nullptr.
NodeBase* findExportFromMangledName(const UnownedStringSlice& slice);
/// Get the ASTBuilder
ASTBuilder* getASTBuilder() { return m_astBuilder; }
/// Collect information on the shader parameters of the module.
///
/// This method should only be called once, after the core
/// structured of the module (its AST and IR) have been created,
/// and before any of the `ComponentType` APIs are used.
///
/// TODO: We might eventually consider a non-stateful approach
/// to constructing a `Module`.
///
void _collectShaderParams();
class ModuleSpecializationInfo : public SpecializationInfo
{
public:
struct GenericArgInfo
{
Decl* paramDecl = nullptr;
Val* argVal = nullptr;
};
List<GenericArgInfo> genericArgs;
List<ExpandedSpecializationArg> existentialArgs;
};
RefPtr<EntryPoint> findEntryPointByName(UnownedStringSlice const& name);
List<RefPtr<EntryPoint>> const& getEntryPoints() { return m_entryPoints; }
void _addEntryPoint(EntryPoint* entryPoint);
void _processFindDeclsExportSymbolsRec(Decl* decl);
protected:
void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo) SLANG_OVERRIDE;
RefPtr<SpecializationInfo> _validateSpecializationArgsImpl(
SpecializationArg const* args,
Index argCount,
DiagnosticSink* sink) SLANG_OVERRIDE;
private:
// The AST for the module
ModuleDecl* m_moduleDecl = nullptr;
// The IR for the module
RefPtr<IRModule> m_irModule = nullptr;
List<ShaderParamInfo> m_shaderParams;
SpecializationParams m_specializationParams;
List<Module*> m_requirements;
// List of modules this module depends on
ModuleDependencyList m_moduleDependencyList;
// List of filesystem paths this module depends on
FilePathDependencyList m_filePathDependencyList;
// Entry points that were defined in thsi module
//
// Note: the entry point defined in the module are *not*
// part of the memory image/layout of the module when
// it is considered as an IComponentType. This can be
// a bit confusing, but if all the entry points in the
// module were automatically linked into the component
// type, we'd need a way to access just the global
// scope of the module without the entry points, in
// case we wanted to link a single entry point against
// the global scope. The `Module` type provides exactly
// that "module without its entry points" unit of
// granularity for linking.
//
// This list only exists for lookup purposes, so that
// the user can find an existing entry-point function
// that was defined as part of the module.
//
List<RefPtr<EntryPoint>> m_entryPoints;
// The builder that owns all of the AST nodes from parsing the source of
// this module.
RefPtr<ASTBuilder> m_astBuilder;
// Holds map of exported mangled names to symbols. m_mangledExportPool maps names to indices,
// and m_mangledExportSymbols holds the NodeBase* values for each index.
StringSlicePool m_mangledExportPool;
List<NodeBase*> m_mangledExportSymbols;
};
typedef Module LoadedModule;
/// A request for the front-end to compile a translation unit.
class TranslationUnitRequest : public RefObject
{
public:
TranslationUnitRequest(
FrontEndCompileRequest* compileRequest);
// The parent compile request
FrontEndCompileRequest* compileRequest = nullptr;
// The language in which the source file(s)
// are assumed to be written
SourceLanguage sourceLanguage = SourceLanguage::Unknown;
// The source file(s) that will be compiled to form this translation unit
//
// Usually, for HLSL or GLSL there will be only one file.
List<SourceFile*> m_sourceFiles;
List<SourceFile*> const& getSourceFiles() { return m_sourceFiles; }
void addSourceFile(SourceFile* sourceFile);
// The entry points associated with this translation unit
List<RefPtr<EntryPoint>> const& getEntryPoints() { return module->getEntryPoints(); }
void _addEntryPoint(EntryPoint* entryPoint) { module->_addEntryPoint(entryPoint); }
// Preprocessor definitions to use for this translation unit only
// (whereas the ones on `compileRequest` will be shared)
Dictionary<String, String> preprocessorDefinitions;
/// The name that will be used for the module this translation unit produces.
Name* moduleName = nullptr;
/// Result of compiling this translation unit (a module)
RefPtr<Module> module;
Module* getModule() { return module; }
ModuleDecl* getModuleDecl() { return module->getModuleDecl(); }
Session* getSession();
NamePool* getNamePool();
SourceManager* getSourceManager();
};
enum class FloatingPointMode : SlangFloatingPointMode
{
Default = SLANG_FLOATING_POINT_MODE_DEFAULT,
Fast = SLANG_FLOATING_POINT_MODE_FAST,
Precise = SLANG_FLOATING_POINT_MODE_PRECISE,
};
enum class WriterChannel : SlangWriterChannel
{
Diagnostic = SLANG_WRITER_CHANNEL_DIAGNOSTIC,
StdOutput = SLANG_WRITER_CHANNEL_STD_OUTPUT,
StdError = SLANG_WRITER_CHANNEL_STD_ERROR,
CountOf = SLANG_WRITER_CHANNEL_COUNT_OF,
};
enum class WriterMode : SlangWriterMode
{
Text = SLANG_WRITER_MODE_TEXT,
Binary = SLANG_WRITER_MODE_BINARY,
};
/// A request to generate output in some target format.
class TargetRequest : public RefObject
{
public:
TargetRequest(Linkage* linkage, CodeGenTarget format);
void addTargetFlags(SlangTargetFlags flags)
{
targetFlags |= flags;
}
void setTargetProfile(Slang::Profile profile)
{
targetProfile = profile;
}
void setFloatingPointMode(FloatingPointMode mode)
{
floatingPointMode = mode;
}
void setLineDirectiveMode(LineDirectiveMode mode)
{
lineDirectiveMode = mode;
}
void setDumpIntermediates(bool value)
{
dumpIntermediates = value;
}
void addCapability(CapabilityAtom capability);
bool shouldEmitSPIRVDirectly()
{
return (targetFlags & SLANG_TARGET_FLAG_GENERATE_SPIRV_DIRECTLY) != 0;
}
bool isWholeProgramRequest()
{
return (targetFlags & SLANG_TARGET_FLAG_GENERATE_WHOLE_PROGRAM) != 0;
}
bool shouldDumpIntermediates() { return dumpIntermediates; }
Linkage* getLinkage() { return linkage; }
CodeGenTarget getTarget() { return format; }
Profile getTargetProfile() { return targetProfile; }
FloatingPointMode getFloatingPointMode() { return floatingPointMode; }
LineDirectiveMode getLineDirectiveMode() { return lineDirectiveMode; }
SlangTargetFlags getTargetFlags() { return targetFlags; }
CapabilitySet getTargetCaps();
Session* getSession();
MatrixLayoutMode getDefaultMatrixLayoutMode();
// TypeLayouts created on the fly by reflection API
Dictionary<Type*, RefPtr<TypeLayout>> typeLayouts;
Dictionary<Type*, RefPtr<TypeLayout>>& getTypeLayouts() { return typeLayouts; }
TypeLayout* getTypeLayout(Type* type);
private:
Linkage* linkage = nullptr;
CodeGenTarget format = CodeGenTarget::Unknown;
SlangTargetFlags targetFlags = 0;
Slang::Profile targetProfile = Slang::Profile();
FloatingPointMode floatingPointMode = FloatingPointMode::Default;
List<CapabilityAtom> rawCapabilities;
CapabilitySet cookedCapabilities;
LineDirectiveMode lineDirectiveMode = LineDirectiveMode::Default;
bool dumpIntermediates = false;
};
/// Are we generating code for a D3D API?
bool isD3DTarget(TargetRequest* targetReq);
/// Are we generating code for a Khronos API (OpenGL or Vulkan)?
bool isKhronosTarget(TargetRequest* targetReq);
/// Are we generating code for a CUDA API (CUDA / OptiX)?
bool isCUDATarget(TargetRequest* targetReq);
/// Are resource types "bindless" (implemented as ordinary data) on the given `target`?
bool areResourceTypesBindlessOnTarget(TargetRequest* target);
// Compute the "effective" profile to use when outputting the given entry point
// for the chosen code-generation target.
//
// The stage of the effective profile will always come from the entry point, while
// the profile version (aka "shader model") will be computed as follows:
//
// - If the entry point and target belong to the same profile family, then take
// the latest version between the two (e.g., if the entry point specified `ps_5_1`
// and the target specifies `sm_5_0` then use `sm_5_1` as the version).
//
// - If the entry point and target disagree on the profile family, always use the
// profile family and version from the target.
//
Profile getEffectiveProfile(EntryPoint* entryPoint, TargetRequest* target);
/// Given a target returns the required downstream compiler
PassThroughMode getDownstreamCompilerRequiredForTarget(CodeGenTarget target);
/// Given a target returns a downstream compiler the prelude should be taken from.
SourceLanguage getDefaultSourceLanguageForDownstreamCompiler(PassThroughMode compiler);
/// Get the build tag string
const char* getBuildTagString();
struct TypeCheckingCache;
struct ContainerTypeKey
{
slang::TypeReflection* elementType;
slang::ContainerType containerType;
bool operator==(ContainerTypeKey other)
{
return elementType == other.elementType && containerType == other.containerType;
}
Slang::HashCode getHashCode()
{
return Slang::combineHash(
Slang::getHashCode(elementType), Slang::getHashCode(containerType));
}
};
/// A context for loading and re-using code modules.
class Linkage : public RefObject, public slang::ISession
{
public:
SLANG_REF_OBJECT_IUNKNOWN_ALL
ISlangUnknown* getInterface(const Guid& guid);
SLANG_NO_THROW slang::IGlobalSession* SLANG_MCALL getGlobalSession() override;
SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModule(
const char* moduleName,
slang::IBlob** outDiagnostics = nullptr) override;
SLANG_NO_THROW slang::IModule* SLANG_MCALL loadModuleFromSource(
const char* moduleName,
slang::IBlob* source,
slang::IBlob** outDiagnostics = nullptr) override;
SLANG_NO_THROW SlangResult SLANG_MCALL createCompositeComponentType(
slang::IComponentType* const* componentTypes,
SlangInt componentTypeCount,
slang::IComponentType** outCompositeComponentType,
ISlangBlob** outDiagnostics = nullptr) override;
SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL specializeType(
slang::TypeReflection* type,
slang::SpecializationArg const* specializationArgs,
SlangInt specializationArgCount,
ISlangBlob** outDiagnostics = nullptr) override;
SLANG_NO_THROW slang::TypeLayoutReflection* SLANG_MCALL getTypeLayout(
slang::TypeReflection* type,
SlangInt targetIndex = 0,
slang::LayoutRules rules = slang::LayoutRules::Default,
ISlangBlob** outDiagnostics = nullptr) override;
SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL getContainerType(
slang::TypeReflection* elementType,
slang::ContainerType containerType,
ISlangBlob** outDiagnostics = nullptr) override;
SLANG_NO_THROW slang::TypeReflection* SLANG_MCALL getDynamicType() override;
SLANG_NO_THROW SlangResult SLANG_MCALL getTypeRTTIMangledName(
slang::TypeReflection* type,
ISlangBlob** outNameBlob) override;
SLANG_NO_THROW SlangResult SLANG_MCALL getTypeConformanceWitnessMangledName(
slang::TypeReflection* type,
slang::TypeReflection* interfaceType,
ISlangBlob** outNameBlob) override;
SLANG_NO_THROW SlangResult SLANG_MCALL getTypeConformanceWitnessSequentialID(
slang::TypeReflection* type,
slang::TypeReflection* interfaceType,
uint32_t* outId) override;
SLANG_NO_THROW SlangResult SLANG_MCALL createTypeConformanceComponentType(
slang::TypeReflection* type,
slang::TypeReflection* interfaceType,
slang::ITypeConformance** outConformance,
SlangInt conformanceIdOverride,
ISlangBlob** outDiagnostics) override;
SLANG_NO_THROW SlangResult SLANG_MCALL createCompileRequest(
SlangCompileRequest** outCompileRequest) override;
void addTarget(
slang::TargetDesc const& desc);
SlangResult addSearchPath(
char const* path);
SlangResult addPreprocessorDefine(
char const* name,
char const* value);
SlangResult setMatrixLayoutMode(
SlangMatrixLayoutMode mode);
/// Create an initially-empty linkage
Linkage(Session* session, ASTBuilder* astBuilder, Linkage* builtinLinkage);
/// Dtor
~Linkage();
/// Get the parent session for this linkage
Session* getSessionImpl() { return m_session; }
// Information on the targets we are being asked to
// generate code for.
List<RefPtr<TargetRequest>> targets;
// Directories to search for `#include` files or `import`ed modules
SearchDirectoryList searchDirectories;
SearchDirectoryList const& getSearchDirectories() { return searchDirectories; }
// Definitions to provide during preprocessing
Dictionary<String, String> preprocessorDefinitions;
// Source manager to help track files loaded
SourceManager m_defaultSourceManager;
SourceManager* m_sourceManager = nullptr;
bool m_obfuscateCode = false;
// Determine whether to output heterogeneity-related code
bool m_heterogeneous = false;
/// Holds any args that are destined for downstream compilers/tools etc
DownstreamArgs m_downstreamArgs;
// Name pool for looking up names
NamePool namePool;
NamePool* getNamePool() { return &namePool; }
ASTBuilder* getASTBuilder() { return m_astBuilder; }
RefPtr<ASTBuilder> m_astBuilder;
// Cache for container types.
Dictionary<ContainerTypeKey, Type*> m_containerTypes;
// cache used by type checking, implemented in check.cpp
TypeCheckingCache* getTypeCheckingCache();
void destroyTypeCheckingCache();
TypeCheckingCache* m_typeCheckingCache = nullptr;
// Modules that have been dynamically loaded via `import`
//
// This is a list of unique modules loaded, in the order they were encountered.
List<RefPtr<LoadedModule> > loadedModulesList;
// Map from the path (or uniqueIdentity if available) of a module file to its definition
Dictionary<String, RefPtr<LoadedModule>> mapPathToLoadedModule;
// Map from the logical name of a module to its definition
Dictionary<Name*, RefPtr<LoadedModule>> mapNameToLoadedModules;
// Map from the mangled name of RTTI objects to sequential IDs
// used by `switch`-based dynamic dispatch.
Dictionary<String, uint32_t> mapMangledNameToRTTIObjectIndex;
// Counters for allocating sequential IDs to witness tables conforming to each interface type.
Dictionary<String, uint32_t> mapInterfaceMangledNameToSequentialIDCounters;
// The resulting specialized IR module for each entry point request
List<RefPtr<IRModule>> compiledModules;
/// File system implementation to use when loading files from disk.
///
/// If this member is `null`, a default implementation that tries
/// to use the native OS filesystem will be used instead.
///
ComPtr<ISlangFileSystem> m_fileSystem;
/// The extended file system implementation. Will be set to a default implementation
/// if fileSystem is nullptr. Otherwise it will either be fileSystem's interface,
/// or a wrapped impl that makes fileSystem operate as fileSystemExt
ComPtr<ISlangFileSystemExt> m_fileSystemExt;
/// Set if fileSystemExt is a cache file system
RefPtr<CacheFileSystem> m_cacheFileSystem;
ISlangFileSystemExt* getFileSystemExt() { return m_fileSystemExt; }
CacheFileSystem* getCacheFileSystem() const { return m_cacheFileSystem; }
/// Load a file into memory using the configured file system.
///
/// @param path The path to attempt to load from
/// @param outBlob A destination pointer to receive the loaded blob
/// @returns A `SlangResult` to indicate success or failure.
///
SlangResult loadFile(String const& path, PathInfo& outPathInfo, ISlangBlob** outBlob);
Expr* parseTermString(String str, Scope* scope);
Type* specializeType(
Type* unspecializedType,
Int argCount,
Type* const* args,
DiagnosticSink* sink);
/// Add a mew target and return its index.
UInt addTarget(
CodeGenTarget target);
RefPtr<Module> loadModule(
Name* name,
const PathInfo& filePathInfo,
ISlangBlob* fileContentsBlob,
SourceLoc const& loc,
DiagnosticSink* sink);
void loadParsedModule(
RefPtr<FrontEndCompileRequest> compileRequest,
RefPtr<TranslationUnitRequest> translationUnit,
Name* name,
PathInfo const& pathInfo);
/// Load a module of the given name.
Module* loadModule(String const& name);
RefPtr<Module> findOrImportModule(
Name* name,
SourceLoc const& loc,
DiagnosticSink* sink);
SourceManager* getSourceManager()
{
return m_sourceManager;
}
/// Override the source manager for the linkage.
///
/// This is only used to install a temporary override when
/// parsing stuff from strings (where we don't want to retain
/// full source files for the parsed result).
///
/// TODO: We should remove the need for this hack.
///
void setSourceManager(SourceManager* sourceManager)
{
m_sourceManager = sourceManager;
}
void setRequireCacheFileSystem(bool requireCacheFileSystem);
void setFileSystem(ISlangFileSystem* fileSystem);
/// The layout to use for matrices by default (row/column major)
MatrixLayoutMode defaultMatrixLayoutMode = kMatrixLayoutMode_ColumnMajor;
MatrixLayoutMode getDefaultMatrixLayoutMode() { return defaultMatrixLayoutMode; }
DebugInfoLevel debugInfoLevel = DebugInfoLevel::None;
OptimizationLevel optimizationLevel = OptimizationLevel::Default;
SerialCompressionType serialCompressionType = SerialCompressionType::VariableByteLite;
bool m_requireCacheFileSystem = false;
bool m_useFalcorCustomSharedKeywordSemantics = false;
// Modules that have been read in with the -r option
List<RefPtr<IRModule>> m_libModules;
void _stopRetainingParentSession()
{
m_retainedSession = nullptr;
}
private:
/// The global Slang library session that this linkage is a child of
Session* m_session = nullptr;
RefPtr<Session> m_retainedSession;
/// Tracks state of modules currently being loaded.
///
/// This information is used to diagnose cases where
/// a user tries to recursively import the same module
/// (possibly along a transitive chain of `import`s).
///
struct ModuleBeingImportedRAII
{
public:
ModuleBeingImportedRAII(
Linkage* linkage,
Module* module,
Name* name,
SourceLoc const& importLoc)
: linkage(linkage)
, module(module)
, name(name)
, importLoc(importLoc)
{
next = linkage->m_modulesBeingImported;
linkage->m_modulesBeingImported = this;
}
~ModuleBeingImportedRAII()
{
linkage->m_modulesBeingImported = next;
}
Linkage* linkage;
Module* module;
Name* name;
SourceLoc importLoc;
ModuleBeingImportedRAII* next;
};
// Any modules currently being imported will be listed here
ModuleBeingImportedRAII*m_modulesBeingImported = nullptr;
/// Is the given module in the middle of being imported?
bool isBeingImported(Module* module);
/// Diagnose that an error occured in the process of importing a module
void _diagnoseErrorInImportedModule(
DiagnosticSink* sink);
List<Type*> m_specializedTypes;
};
/// Shared functionality between front- and back-end compile requests.
///
/// This is the base class for both `FrontEndCompileRequest` and
/// `BackEndCompileRequest`, and allows a small number of parts of
/// the compiler to be easily invocable from either front-end or
/// back-end work.
///
class CompileRequestBase : public RefObject
{
// TODO: We really shouldn't need this type in the long run.
// The few places that rely on it should be refactored to just
// depend on the underlying information (a linkage and a diagnostic
// sink) directly.
//
// The flags to control dumping and validation of IR should be
// moved to some kind of shared settings/options `struct` that
// both front-end and back-end requests can store.
public:
Session* getSession();
Linkage* getLinkage() { return m_linkage; }
DiagnosticSink* getSink() { return m_sink; }
SourceManager* getSourceManager() { return getLinkage()->getSourceManager(); }
NamePool* getNamePool() { return getLinkage()->getNamePool(); }
ISlangFileSystemExt* getFileSystemExt() { return getLinkage()->getFileSystemExt(); }
SlangResult loadFile(String const& path, PathInfo& outPathInfo, ISlangBlob** outBlob) { return getLinkage()->loadFile(path, outPathInfo, outBlob); }
bool shouldDumpIR = false;
bool shouldValidateIR = false;
bool shouldDumpAST = false;
bool shouldDocument = false;
bool outputPreprocessor = false;
/// If true will after lexical analysis output the hierarchy of includes to stdout
bool outputIncludes = false;
protected:
CompileRequestBase(
Linkage* linkage,
DiagnosticSink* sink);
private:
Linkage* m_linkage = nullptr;
DiagnosticSink* m_sink = nullptr;
};
/// A request to compile source code to an AST + IR.
class FrontEndCompileRequest : public CompileRequestBase
{
public:
/// Note that writers can be parsed as nullptr to disable output,
/// and individual channels set to null to disable them
FrontEndCompileRequest(
Linkage* linkage,
StdWriters* writers,
DiagnosticSink* sink);
int addEntryPoint(
int translationUnitIndex,
String const& name,
Profile entryPointProfile);
// Translation units we are being asked to compile
List<RefPtr<TranslationUnitRequest> > translationUnits;
RefPtr<TranslationUnitRequest> getTranslationUnit(UInt index) { return translationUnits[index]; }
// Compile flags to be shared by all translation units
SlangCompileFlags compileFlags = 0;
// If true then generateIR will serialize out IR, and serialize back in again. Making
// serialization a bottleneck or firewall between the front end and the backend
bool useSerialIRBottleneck = false;
// If true will serialize and de-serialize with debug information
bool verifyDebugSerialization = false;
List<RefPtr<FrontEndEntryPointRequest>> m_entryPointReqs;
List<RefPtr<FrontEndEntryPointRequest>> const& getEntryPointReqs() { return m_entryPointReqs; }
UInt getEntryPointReqCount() { return m_entryPointReqs.getCount(); }
FrontEndEntryPointRequest* getEntryPointReq(UInt index) { return m_entryPointReqs[index]; }
// Directories to search for `#include` files or `import`ed modules
// NOTE! That for now these search directories are not settable via the API
// so the search directories on Linkage is used for #include as well as for modules.
SearchDirectoryList searchDirectories;
SearchDirectoryList const& getSearchDirectories() { return searchDirectories; }
// Definitions to provide during preprocessing
Dictionary<String, String> preprocessorDefinitions;
void parseTranslationUnit(
TranslationUnitRequest* translationUnit);
// Perform primary semantic checking on all
// of the translation units in the program
void checkAllTranslationUnits();
void checkEntryPoints();
void generateIR();
SlangResult executeActionsInner();
/// Add a translation unit to be compiled.
///
/// @param language The source language that the translation unit will use (e.g., `SourceLanguage::Slang`
/// @param moduleName The name that will be used for the module compile from the translation unit.
///
/// If moduleName is passed as nullptr a module name is generated.
/// If all translation units in a compile request use automatically generated
/// module names, then they are guaranteed not to conflict with one another.
///
/// @return The zero-based index of the translation unit in this compile request.
int addTranslationUnit(SourceLanguage language, Name* moduleName);
int addTranslationUnit(TranslationUnitRequest* translationUnit);
void addTranslationUnitSourceFile(
int translationUnitIndex,
SourceFile* sourceFile);
void addTranslationUnitSourceBlob(
int translationUnitIndex,
String const& path,
ISlangBlob* sourceBlob);
void addTranslationUnitSourceString(
int translationUnitIndex,
String const& path,
String const& source);
void addTranslationUnitSourceFile(
int translationUnitIndex,
String const& path);
/// Get a component type that represents the global scope of the compile request.
ComponentType* getGlobalComponentType() { return m_globalComponentType; }
/// Get a component type that represents the global scope of the compile request, plus the requested entry points.
ComponentType* getGlobalAndEntryPointsComponentType() { return m_globalAndEntryPointsComponentType; }
List<RefPtr<ComponentType>> const& getUnspecializedEntryPoints() { return m_unspecializedEntryPoints; }
/// Does the code we are compiling represent part of the Slang standard library?
bool m_isStandardLibraryCode = false;
Name* m_defaultModuleName = nullptr;
/// An "extra" entry point that was added via a library reference
struct ExtraEntryPointInfo
{
Name* name;
Profile profile;
String mangledName;
};
/// A list of "extra" entry points added via a library reference
List<ExtraEntryPointInfo> m_extraEntryPoints;
private:
/// A component type that includes only the global scopes of the translation unit(s) that were compiled.
RefPtr<ComponentType> m_globalComponentType;
/// A component type that extends the global scopes with all of the entry points that were specified.
RefPtr<ComponentType> m_globalAndEntryPointsComponentType;
List<RefPtr<ComponentType>> m_unspecializedEntryPoints;
RefPtr<StdWriters> m_writers;
};
/// A visitor for use with `ComponentType`s, allowing dispatch over the concrete subclasses.
class ComponentTypeVisitor
{
public:
// The following methods should be overriden in a concrete subclass
// to customize how it acts on each of the concrete types of component.
//
// In cases where the application wants to simply "recurse" on a
// composite, specialized, or legacy component type it can use
// the `visitChildren` methods below.
//
virtual void visitEntryPoint(EntryPoint* entryPoint, EntryPoint::EntryPointSpecializationInfo* specializationInfo) = 0;
virtual void visitModule(Module* module, Module::ModuleSpecializationInfo* specializationInfo) = 0;
virtual void visitComposite(CompositeComponentType* composite, CompositeComponentType::CompositeSpecializationInfo* specializationInfo) = 0;
virtual void visitSpecialized(SpecializedComponentType* specialized) = 0;
virtual void visitTypeConformance(TypeConformance* conformance) = 0;
protected:
// These helpers can be used to recurse into the logical children of a
// component type, and are useful for the common case where a visitor
// only cares about a few leaf cases.
//
void visitChildren(CompositeComponentType* composite, CompositeComponentType::CompositeSpecializationInfo* specializationInfo);
void visitChildren(SpecializedComponentType* specialized);
};
/// A `TargetProgram` represents a `ComponentType` specialized for a particular `TargetRequest`
///
/// TODO: This should probably be renamed to `TargetComponentType`.
///
/// By binding a component type to a specific target, a `TargetProgram` allows
/// for things like layout to be computed, that fundamentally depend on
/// the choice of target.
///
/// A `TargetProgram` handles request for compiled kernel code for
/// entry point functions. In practice, kernel code can only be
/// correctly generated when the underlying `ComponentType` is "fully linked"
/// (has no remaining unsatisfied requirements).
///
class TargetProgram : public RefObject
{
public:
TargetProgram(
ComponentType* componentType,
TargetRequest* targetReq);
/// Get the underlying program
ComponentType* getProgram() { return m_program; }
/// Get the underlying target
TargetRequest* getTargetReq() { return m_targetReq; }
/// Get the layout for the program on the target.
///
/// If this is the first time the layout has been
/// requested, report any errors that arise during
/// layout to the given `sink`.
///
ProgramLayout* getOrCreateLayout(DiagnosticSink* sink);
/// Get the layout for the program on the taarget.
///
/// This routine assumes that `getOrCreateLayout`
/// has already been called previously.
///
ProgramLayout* getExistingLayout()
{
SLANG_ASSERT(m_layout);
return m_layout;
}
/// Get the compiled code for an entry point on the target.
///
/// If this is the first time that code generation has
/// been requested, report any errors that arise during
/// code generation to the given `sink`.
///
CompileResult& getOrCreateEntryPointResult(Int entryPointIndex, DiagnosticSink* sink);
CompileResult& getOrCreateWholeProgramResult(DiagnosticSink* sink);
CompileResult& getExistingWholeProgramResult()
{
return m_wholeProgramResult;
}
/// Get the compiled code for an entry point on the target.
///
/// This routine assumes that `getOrCreateEntryPointResult`
/// has already been called previously.
///
CompileResult& getExistingEntryPointResult(Int entryPointIndex)
{
return m_entryPointResults[entryPointIndex];
}
CompileResult& _createWholeProgramResult(
BackEndCompileRequest* backEndRequest,
EndToEndCompileRequest* endToEndRequest);
/// Internal helper for `getOrCreateEntryPointResult`.
///
/// This is used so that command-line and API-based
/// requests for code can bottleneck through the same place.
///
/// Shouldn't be called directly by most code.
///
CompileResult& _createEntryPointResult(
Int entryPointIndex,
BackEndCompileRequest* backEndRequest,
EndToEndCompileRequest* endToEndRequest);
RefPtr<IRModule> getOrCreateIRModuleForLayout(DiagnosticSink* sink);
RefPtr<IRModule> getExistingIRModuleForLayout()
{
return m_irModuleForLayout;
}
private:
RefPtr<IRModule> createIRModuleForLayout(DiagnosticSink* sink);
// The program being compiled or laid out
ComponentType* m_program;
// The target that code/layout will be generated for
TargetRequest* m_targetReq;
// The computed layout, if it has been generated yet
RefPtr<ProgramLayout> m_layout;
// Generated compile results for each entry point
// in the parent `Program` (indexing matches
// the order they are given in the `Program`)
CompileResult m_wholeProgramResult;
List<CompileResult> m_entryPointResults;
RefPtr<IRModule> m_irModuleForLayout;
};
/// A request to generate code for a program
class BackEndCompileRequest : public CompileRequestBase
{
public:
BackEndCompileRequest(
Linkage* linkage,
DiagnosticSink* sink,
ComponentType* program = nullptr);
// Should we dump intermediate results along the way, for debugging?
bool shouldDumpIntermediates = false;
ComponentType* getProgram() { return m_program; }
void setProgram(ComponentType* program) { m_program = program; }
// Should R/W images without explicit formats be assumed to have "unknown" format?
//
// The default behavior is to make a best-effort guess as to what format is intended.
//
bool useUnknownImageFormatAsDefault = false;
// If true will disable generics/existential value specialization pass.
bool disableSpecialization = false;
// If true will disable generating dynamic dispatch code.
bool disableDynamicDispatch = false;
String m_dumpIntermediatePrefix;
private:
RefPtr<ComponentType> m_program;
};
/// A compile request that spans the front and back ends of the compiler
///
/// This is what the command-line `slangc` uses, as well as the legacy
/// C API. It ties together the functionality of `Linkage`,
/// `FrontEndCompileRequest`, and `BackEndCompileRequest`, plus a small
/// number of additional features that primarily make sense for
/// command-line usage.
///
class EndToEndCompileRequest : public RefObject, public slang::ICompileRequest
{
public:
SLANG_CLASS_GUID(0xce6d2383, 0xee1b, 0x4fd7, { 0xa0, 0xf, 0xb8, 0xb6, 0x33, 0x12, 0x95, 0xc8 })
// ISlangUnknown
SLANG_NO_THROW SlangResult SLANG_MCALL queryInterface(SlangUUID const& uuid, void** outObject) SLANG_OVERRIDE;
SLANG_REF_OBJECT_IUNKNOWN_ADD_REF
SLANG_REF_OBJECT_IUNKNOWN_RELEASE
// slang::ICompileRequest
virtual SLANG_NO_THROW void SLANG_MCALL setFileSystem(ISlangFileSystem* fileSystem) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setCompileFlags(SlangCompileFlags flags) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setDumpIntermediates(int enable) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setDumpIntermediatePrefix(const char* prefix) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setLineDirectiveMode(SlangLineDirectiveMode mode) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setCodeGenTarget(SlangCompileTarget target) SLANG_OVERRIDE;
virtual SLANG_NO_THROW int SLANG_MCALL addCodeGenTarget(SlangCompileTarget target) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setTargetProfile(int targetIndex, SlangProfileID profile) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setTargetFlags(int targetIndex, SlangTargetFlags flags) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setTargetFloatingPointMode(int targetIndex, SlangFloatingPointMode mode) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setTargetMatrixLayoutMode(int targetIndex, SlangMatrixLayoutMode mode) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setMatrixLayoutMode(SlangMatrixLayoutMode mode) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setDebugInfoLevel(SlangDebugInfoLevel level) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setOptimizationLevel(SlangOptimizationLevel level) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setOutputContainerFormat(SlangContainerFormat format) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setPassThrough(SlangPassThrough passThrough) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setDiagnosticCallback(SlangDiagnosticCallback callback, void const* userData) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setWriter(SlangWriterChannel channel, ISlangWriter* writer) SLANG_OVERRIDE;
virtual SLANG_NO_THROW ISlangWriter* SLANG_MCALL getWriter(SlangWriterChannel channel) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL addSearchPath(const char* searchDir) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL addPreprocessorDefine(const char* key, const char* value) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL processCommandLineArguments(char const* const* args, int argCount) SLANG_OVERRIDE;
virtual SLANG_NO_THROW int SLANG_MCALL addTranslationUnit(SlangSourceLanguage language, char const* name) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setDefaultModuleName(const char* defaultModuleName) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL addTranslationUnitPreprocessorDefine(int translationUnitIndex, const char* key, const char* value) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL addTranslationUnitSourceFile(int translationUnitIndex, char const* path) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL addTranslationUnitSourceString(int translationUnitIndex, char const* path, char const* source) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL addLibraryReference(const void* libData, size_t libDataSize) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL addTranslationUnitSourceStringSpan(int translationUnitIndex, char const* path, char const* sourceBegin, char const* sourceEnd) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL addTranslationUnitSourceBlob(int translationUnitIndex, char const* path, ISlangBlob* sourceBlob) SLANG_OVERRIDE;
virtual SLANG_NO_THROW int SLANG_MCALL addEntryPoint(int translationUnitIndex, char const* name, SlangStage stage) SLANG_OVERRIDE;
virtual SLANG_NO_THROW int SLANG_MCALL addEntryPointEx(int translationUnitIndex, char const* name, SlangStage stage, int genericArgCount, char const** genericArgs) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL setGlobalGenericArgs(int genericArgCount, char const** genericArgs) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL setTypeNameForGlobalExistentialTypeParam(int slotIndex, char const* typeName) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL setTypeNameForEntryPointExistentialTypeParam(int entryPointIndex, int slotIndex, char const* typeName) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL compile() SLANG_OVERRIDE;
virtual SLANG_NO_THROW char const* SLANG_MCALL getDiagnosticOutput() SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL getDiagnosticOutputBlob(ISlangBlob** outBlob) SLANG_OVERRIDE;
virtual SLANG_NO_THROW int SLANG_MCALL getDependencyFileCount() SLANG_OVERRIDE;
virtual SLANG_NO_THROW char const* SLANG_MCALL getDependencyFilePath(int index) SLANG_OVERRIDE;
virtual SLANG_NO_THROW int SLANG_MCALL getTranslationUnitCount() SLANG_OVERRIDE;
virtual SLANG_NO_THROW char const* SLANG_MCALL getEntryPointSource(int entryPointIndex) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void const* SLANG_MCALL getEntryPointCode(int entryPointIndex, size_t* outSize) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointCodeBlob(int entryPointIndex, int targetIndex, ISlangBlob** outBlob) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPointHostCallable(int entryPointIndex, int targetIndex, ISlangSharedLibrary** outSharedLibrary) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL getTargetCodeBlob(int targetIndex, ISlangBlob** outBlob) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL getTargetHostCallable(int targetIndex, ISlangSharedLibrary** outSharedLibrary) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void const* SLANG_MCALL getCompileRequestCode(size_t* outSize) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL getContainerCode(ISlangBlob** outBlob) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL loadRepro(ISlangFileSystem* fileSystem, const void* data, size_t size) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL saveRepro(ISlangBlob** outBlob) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL enableReproCapture() SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL getProgram(slang::IComponentType** outProgram) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL getEntryPoint(SlangInt entryPointIndex, slang::IComponentType** outEntryPoint) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL getModule(SlangInt translationUnitIndex, slang::IModule** outModule) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL getSession(slang::ISession** outSession) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangReflection* SLANG_MCALL getReflection() SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setCommandLineCompilerMode() SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL addTargetCapability(SlangInt targetIndex, SlangCapabilityID capability) SLANG_OVERRIDE;
virtual SLANG_NO_THROW SlangResult SLANG_MCALL getProgramWithEntryPoints(slang::IComponentType** outProgram) SLANG_OVERRIDE;
virtual SLANG_NO_THROW void SLANG_MCALL setTargetLineDirectiveMode(
SlangInt targetIndex,
SlangLineDirectiveMode mode) SLANG_OVERRIDE;
EndToEndCompileRequest(
Session* session);
EndToEndCompileRequest(
Linkage* linkage);
// What container format are we being asked to generate?
// If it's set to a format, the container blob will be calculated during compile
ContainerFormat m_containerFormat = ContainerFormat::None;
/// Where the container blob is stored. This is calculated as part of compile if m_containerFormat is set to
/// a supported format.
ComPtr<ISlangBlob> m_containerBlob;
// Path to output container to
String m_containerOutputPath;
// Should we just pass the input to another compiler?
PassThroughMode m_passThrough = PassThroughMode::None;
/// Source code for the specialization arguments to use for the global specialization parameters of the program.
List<String> m_globalSpecializationArgStrings;
bool m_shouldSkipCodegen = false;
// Are we being driven by the command-line `slangc`, and should act accordingly?
bool m_isCommandLineCompile = false;
String m_diagnosticOutput;
// If set, will dump the compilation state
String m_dumpRepro;
/// If set, if a compilation failure occurs will attempt to save off a dump repro with a unique name
bool m_dumpReproOnError = false;
/// A blob holding the diagnostic output
ComPtr<ISlangBlob> m_diagnosticOutputBlob;
/// Line directive mode for new targets to be added to this request.
/// This is needed to support the legacy `setLineDirectiveMode` API.
/// We can remove this field if we move to `setTargetLineDirectiveMode`.
LineDirectiveMode m_lineDirectiveMode = LineDirectiveMode::Default;
/// Per-entry-point information not tracked by other compile requests
class EntryPointInfo : public RefObject
{
public:
/// Source code for the specialization arguments to use for the specialization parameters of the entry point.
List<String> specializationArgStrings;
};
List<EntryPointInfo> m_entryPoints;
/// Per-target information only needed for command-line compiles
class TargetInfo : public RefObject
{
public:
// Requested output paths for each entry point.
// An empty string indices no output desired for
// the given entry point.
Dictionary<Int, String> entryPointOutputPaths;
String wholeTargetOutputPath;
};
Dictionary<TargetRequest*, RefPtr<TargetInfo>> m_targetInfos;
/// Writes the modules in a container to the stream
SlangResult writeContainerToStream(Stream* stream);
/// If a container format has been specified produce a container (stored in m_containerBlob)
SlangResult maybeCreateContainer();
/// If a container has been constructed and the filename/path has contents will try to write
/// the container contents to the file
SlangResult maybeWriteContainer(const String& fileName);
Linkage* getLinkage() { return m_linkage; }
int addEntryPoint(
int translationUnitIndex,
String const& name,
Profile profile,
List<String> const & genericTypeNames);
void setWriter(WriterChannel chan, ISlangWriter* writer);
ISlangWriter* getWriter(WriterChannel chan) const { return m_writers->getWriter(SlangWriterChannel(chan)); }
/// The end to end request can be passed as nullptr, if not driven by one
SlangResult executeActionsInner();
SlangResult executeActions();
Session* getSession() { return m_session; }
DiagnosticSink* getSink() { return &m_sink; }
NamePool* getNamePool() { return getLinkage()->getNamePool(); }
FrontEndCompileRequest* getFrontEndReq() { return m_frontEndReq; }
BackEndCompileRequest* getBackEndReq() { return m_backEndReq; }
ComponentType* getUnspecializedGlobalComponentType() { return getFrontEndReq()->getGlobalComponentType(); }
ComponentType* getUnspecializedGlobalAndEntryPointsComponentType()
{
return getFrontEndReq()->getGlobalAndEntryPointsComponentType();
}
ComponentType* getSpecializedGlobalComponentType() { return m_specializedGlobalComponentType; }
ComponentType* getSpecializedGlobalAndEntryPointsComponentType() { return m_specializedGlobalAndEntryPointsComponentType; }
ComponentType* getSpecializedEntryPointComponentType(Index index)
{
return m_specializedEntryPoints[index];
}
~EndToEndCompileRequest()
{
m_linkage = nullptr;
m_frontEndReq = nullptr;
}
private:
ISlangUnknown* getInterface(const Guid& guid);
void init();
Session* m_session = nullptr;
RefPtr<Linkage> m_linkage;
DiagnosticSink m_sink;
RefPtr<FrontEndCompileRequest> m_frontEndReq;
RefPtr<ComponentType> m_specializedGlobalComponentType;
RefPtr<ComponentType> m_specializedGlobalAndEntryPointsComponentType;
List<RefPtr<ComponentType>> m_specializedEntryPoints;
RefPtr<BackEndCompileRequest> m_backEndReq;
// For output
RefPtr<StdWriters> m_writers;
};
void generateOutput(
BackEndCompileRequest* compileRequest);
void generateOutput(
EndToEndCompileRequest* compileRequest);
// Helper to dump intermediate output when debugging
void maybeDumpIntermediate(
BackEndCompileRequest* compileRequest,
void const* data,
size_t size,
CodeGenTarget target);
void maybeDumpIntermediate(
BackEndCompileRequest* compileRequest,
char const* text,
CodeGenTarget target);
void maybeDumpIntermediate(
BackEndCompileRequest* compileRequest,
DownstreamCompileResult* compileResult,
CodeGenTarget target);
/* Returns SLANG_OK if pass through support is available */
SlangResult checkExternalCompilerSupport(Session* session, PassThroughMode passThrough);
/* Report an error appearing from external compiler to the diagnostic sink error to the diagnostic sink.
@param compilerName The name of the compiler the error came for (or nullptr if not known)
@param res Result associated with the error. The error code will be reported. (Can take HRESULT - and will expand to string if known)
@param diagnostic The diagnostic string associated with the compile failure
@param sink The diagnostic sink to report to */
void reportExternalCompileError(const char* compilerName, SlangResult res, const UnownedStringSlice& diagnostic, DiagnosticSink* sink);
/* Determines a suitable filename to identify the input for a given entry point being compiled.
If the end-to-end compile is a pass-through case, will attempt to find the (unique) source file
pathname for the translation unit containing the entry point at `entryPointIndex.
If the compilation is not in a pass-through case, then always returns `"slang-generated"`.
@param endToEndReq The end-to-end compile request which might be using pass-through compilation
@param entryPointIndex The index of the entry point to compute a filename for.
@return the appropriate source filename */
String calcSourcePathForEntryPoint(EndToEndCompileRequest* endToEndReq, Int entryPointIndex);
String calcSourcePathForEntryPoints(EndToEndCompileRequest* endToEndReq, const List<Int>& entryPointIndices);
class ExtensionTracker : public RefObject
{
public:
};
/* Emits entry point source taking into account if a pass-through or not. Uses 'target' to determine
the target (not targetReq) */
SlangResult emitEntryPointsSource(
BackEndCompileRequest* compileRequest,
const List<Int>& entryPointIndices,
TargetRequest* targetReq,
CodeGenTarget target,
EndToEndCompileRequest* endToEndReq,
ExtensionTracker* extensionTracker,
String& outSource);
SlangResult emitEntryPointSource(
BackEndCompileRequest* compileRequest,
Int entryPointIndex,
TargetRequest* targetReq,
CodeGenTarget target,
EndToEndCompileRequest* endToEndReq,
ExtensionTracker* extensionTracker,
String& outSource);
//
// Information about BaseType that's useful for checking literals
struct BaseTypeInfo
{
typedef uint8_t Flags;
struct Flag
{
enum Enum : Flags
{
Signed = 0x1,
FloatingPoint = 0x2,
Integer = 0x4,
};
};
SLANG_FORCE_INLINE static const BaseTypeInfo& getInfo(BaseType baseType) { return s_info[Index(baseType)]; }
static UnownedStringSlice asText(BaseType baseType);
uint8_t sizeInBytes; ///< Size of type in bytes
Flags flags;
uint8_t baseType;
static bool check();
private:
static const BaseTypeInfo s_info[Index(BaseType::CountOf)];
};
class CodeGenTransitionMap
{
public:
struct Pair
{
typedef Pair ThisType;
SLANG_FORCE_INLINE bool operator==(const ThisType& rhs) const { return source == rhs.source && target == rhs.target; }
SLANG_FORCE_INLINE bool operator!=(const ThisType& rhs) const { return !(*this == rhs); }
SLANG_FORCE_INLINE HashCode getHashCode() const { return combineHash(HashCode(source), HashCode(target)); }
CodeGenTarget source;
CodeGenTarget target;
};
void removeTransition(CodeGenTarget source, CodeGenTarget target)
{
m_map.Remove(Pair{ source, target });
}
void addTransition(CodeGenTarget source, CodeGenTarget target, PassThroughMode compiler)
{
SLANG_ASSERT(source != target);
m_map.Add(Pair{ source, target }, compiler);
}
bool hasTransition(CodeGenTarget source, CodeGenTarget target) const
{
return m_map.ContainsKey(Pair{ source, target });
}
PassThroughMode getTransition(CodeGenTarget source, CodeGenTarget target) const
{
const Pair pair{ source, target };
auto value = m_map.TryGetValue(pair);
return value ? *value : PassThroughMode::None;
}
protected:
Dictionary<Pair, PassThroughMode> m_map;
};
class Session : public RefObject, public slang::IGlobalSession
{
public:
SLANG_REF_OBJECT_IUNKNOWN_ALL
ISlangUnknown* getInterface(const Guid& guid);
// slang::IGlobalSession
SLANG_NO_THROW SlangResult SLANG_MCALL createSession(slang::SessionDesc const& desc, slang::ISession** outSession) override;
SLANG_NO_THROW SlangProfileID SLANG_MCALL findProfile(char const* name) override;
SLANG_NO_THROW void SLANG_MCALL setDownstreamCompilerPath(SlangPassThrough passThrough, char const* path) override;
SLANG_NO_THROW void SLANG_MCALL setDownstreamCompilerPrelude(SlangPassThrough inPassThrough, char const* prelude) override;
SLANG_NO_THROW void SLANG_MCALL getDownstreamCompilerPrelude(SlangPassThrough inPassThrough, ISlangBlob** outPrelude) override;
SLANG_NO_THROW const char* SLANG_MCALL getBuildTagString() override;
SLANG_NO_THROW SlangResult SLANG_MCALL setDefaultDownstreamCompiler(SlangSourceLanguage sourceLanguage, SlangPassThrough defaultCompiler) override;
SLANG_NO_THROW SlangPassThrough SLANG_MCALL getDefaultDownstreamCompiler(SlangSourceLanguage sourceLanguage) override;
SLANG_NO_THROW void SLANG_MCALL setLanguagePrelude(SlangSourceLanguage inSourceLanguage, char const* prelude) override;
SLANG_NO_THROW void SLANG_MCALL getLanguagePrelude(SlangSourceLanguage inSourceLanguage, ISlangBlob** outPrelude) override;
SLANG_NO_THROW SlangResult SLANG_MCALL createCompileRequest(slang::ICompileRequest** outCompileRequest) override;
SLANG_NO_THROW void SLANG_MCALL addBuiltins(char const* sourcePath, char const* sourceString) override;
SLANG_NO_THROW void SLANG_MCALL setSharedLibraryLoader(ISlangSharedLibraryLoader* loader) override;
SLANG_NO_THROW ISlangSharedLibraryLoader* SLANG_MCALL getSharedLibraryLoader() override;
SLANG_NO_THROW SlangResult SLANG_MCALL checkCompileTargetSupport(SlangCompileTarget target) override;
SLANG_NO_THROW SlangResult SLANG_MCALL checkPassThroughSupport(SlangPassThrough passThrough) override;
SLANG_NO_THROW SlangResult SLANG_MCALL compileStdLib(slang::CompileStdLibFlags flags) override;
SLANG_NO_THROW SlangResult SLANG_MCALL loadStdLib(const void* stdLib, size_t stdLibSizeInBytes) override;
SLANG_NO_THROW SlangResult SLANG_MCALL saveStdLib(SlangArchiveType archiveType, ISlangBlob** outBlob) override;
SLANG_NO_THROW SlangCapabilityID SLANG_MCALL findCapability(char const* name) override;
SLANG_NO_THROW void SLANG_MCALL setDownstreamCompilerForTransition(SlangCompileTarget source, SlangCompileTarget target, SlangPassThrough compiler) override;
SLANG_NO_THROW SlangPassThrough SLANG_MCALL getDownstreamCompilerForTransition(SlangCompileTarget source, SlangCompileTarget target) override;
/// Get the downstream compiler for a transition
DownstreamCompiler* getDownstreamCompiler(CodeGenTarget source, CodeGenTarget target);
Scope* baseLanguageScope = nullptr;
Scope* coreLanguageScope = nullptr;
Scope* hlslLanguageScope = nullptr;
Scope* slangLanguageScope = nullptr;
ModuleDecl* baseModuleDecl = nullptr;
List<RefPtr<Module>> stdlibModules;
SourceManager builtinSourceManager;
SourceManager* getBuiltinSourceManager() { return &builtinSourceManager; }
// Name pool stuff for unique-ing identifiers
RootNamePool rootNamePool;
NamePool namePool;
RootNamePool* getRootNamePool() { return &rootNamePool; }
NamePool* getNamePool() { return &namePool; }
Name* getNameObj(String name) { return namePool.getName(name); }
Name* tryGetNameObj(String name) { return namePool.tryGetName(name); }
//
/// This AST Builder should only be used for creating AST nodes that are global across requests
/// not doing so could lead to memory being consumed but not used.
ASTBuilder* getGlobalASTBuilder() { return globalAstBuilder; }
RefPtr<ASTBuilder> globalAstBuilder;
// Generated code for stdlib, etc.
String stdlibPath;
String coreLibraryCode;
String slangLibraryCode;
String hlslLibraryCode;
String glslLibraryCode;
String getStdlibPath();
String getCoreLibraryCode();
String getHLSLLibraryCode();
RefPtr<SharedASTBuilder> m_sharedASTBuilder;
//
void _setSharedLibraryLoader(ISlangSharedLibraryLoader* loader);
/// Will try to load the library by specified name (using the set loader), if not one already available.
DownstreamCompiler* getOrLoadDownstreamCompiler(PassThroughMode type, DiagnosticSink* sink);
/// Will unload the specified shared library if it's currently loaded
void resetDownstreamCompiler(PassThroughMode type);
/// Get the prelude associated with the language
const String& getPreludeForLanguage(SourceLanguage language) { return m_languagePreludes[int(language)]; }
/// Get the built in linkage -> handy to get the stdlibs from
Linkage* getBuiltinLinkage() const { return m_builtinLinkage; }
void init();
void addBuiltinSource(
Scope* scope,
String const& path,
String const& source);
~Session();
ComPtr<ISlangSharedLibraryLoader> m_sharedLibraryLoader; ///< The shared library loader (never null)
int m_downstreamCompilerInitialized = 0;
RefPtr<DownstreamCompilerSet> m_downstreamCompilerSet; ///< Information about all available downstream compilers.
RefPtr<DownstreamCompiler> m_downstreamCompilers[int(PassThroughMode::CountOf)]; ///< A downstream compiler for a pass through
DownstreamCompilerLocatorFunc m_downstreamCompilerLocators[int(PassThroughMode::CountOf)];
private:
void _initCodeGenTransitionMap();
SlangResult _readBuiltinModule(ISlangFileSystem* fileSystem, Scope* scope, String moduleName);
SlangResult _loadRequest(EndToEndCompileRequest* request, const void* data, size_t size);
/// Linkage used for all built-in (stdlib) code.
RefPtr<Linkage> m_builtinLinkage;
String m_downstreamCompilerPaths[int(PassThroughMode::CountOf)]; ///< Paths for each pass through
String m_languagePreludes[int(SourceLanguage::CountOf)]; ///< Prelude for each source language
PassThroughMode m_defaultDownstreamCompilers[int(SourceLanguage::CountOf)];
// Describes a conversion from one code gen target (source) to another (target)
CodeGenTransitionMap m_codeGenTransitionMap;
};
//
// The following functions are utilties to convert between
// matching "external" (public API) and "internal" (implementation)
// types. They are favored over explicit casts because they
// help avoid making incorrect conversions (e.g., when using
// `reinterpret_cast` or C-style casts), and because they
// abstract over the conversion required for each pair of types.
//
SLANG_FORCE_INLINE slang::IGlobalSession* asExternal(Session* session)
{
return static_cast<slang::IGlobalSession*>(session);
}
SLANG_FORCE_INLINE Session* asInternal(slang::IGlobalSession* session)
{
return static_cast<Session*>(session);
}
SLANG_FORCE_INLINE slang::ISession* asExternal(Linkage* linkage)
{
return static_cast<slang::ISession*>(linkage);
}
SLANG_FORCE_INLINE Module* asInternal(slang::IModule* module)
{
return static_cast<Module*>(module);
}
SLANG_FORCE_INLINE slang::IModule* asExternal(Module* module)
{
return static_cast<slang::IModule*>(module);
}
ComponentType* asInternal(slang::IComponentType* inComponentType);
SLANG_FORCE_INLINE slang::IComponentType* asExternal(ComponentType* componentType)
{
return static_cast<slang::IComponentType*>(componentType);
}
SLANG_FORCE_INLINE slang::ProgramLayout* asExternal(ProgramLayout* programLayout)
{
return (slang::ProgramLayout*) programLayout;
}
SLANG_FORCE_INLINE Type* asInternal(slang::TypeReflection* type)
{
return reinterpret_cast<Type*>(type);
}
SLANG_FORCE_INLINE slang::TypeReflection* asExternal(Type* type)
{
return reinterpret_cast<slang::TypeReflection*>(type);
}
SLANG_FORCE_INLINE TypeLayout* asInternal(slang::TypeLayoutReflection* type)
{
return reinterpret_cast<TypeLayout*>(type);
}
SLANG_FORCE_INLINE slang::TypeLayoutReflection* asExternal(TypeLayout* type)
{
return reinterpret_cast<slang::TypeLayoutReflection*>(type);
}
SLANG_FORCE_INLINE SlangCompileRequest* asExternal(EndToEndCompileRequest* request)
{
return static_cast<SlangCompileRequest*>(request);
}
SLANG_FORCE_INLINE EndToEndCompileRequest* asInternal(SlangCompileRequest* request)
{
// Converts to the internal type -- does a runtime type check through queryInterfae
SLANG_ASSERT(request);
EndToEndCompileRequest* endToEndRequest = nullptr;
// NOTE! We aren't using to access an interface, so *doesn't* return with a refcount
request->queryInterface(EndToEndCompileRequest::getTypeGuid(), (void**)&endToEndRequest);
SLANG_ASSERT(endToEndRequest);
return endToEndRequest;
}
}
#endif