summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2020-01-31 16:01:03 -0800
committerGitHub <noreply@github.com>2020-01-31 16:01:03 -0800
commit2c1fbf8330efc34b85e09ee9b101c6a55327778a (patch)
tree79707def5dec6a753bafaffcb474c8a92fb5081b /source
parent2ee06a4f2f3c995717bf18ba287a20e81d6141bc (diff)
Some Slang API additions (#1195)
* Some Slang API additions These are additions to the public Slang API that came up while I was trying to write an example to demonstrate GPU printing. The additions aren't strictly necessary for the example, but I found these to be missing services when writing the code the way I wanted to. The main public changes are: * There is a new distinct `IEntryPoint` interface which inherit from `IComponentType` (much like `IModule` before). For now this doesn't expose any new functions on top of `IComponentType`, but I expect it to do so eventually. * It is now possible to get the `IModule` for a specific translation unit in a compile request with `spCompileRequest_getModule`. Even for a compile request that had only one translation unit this is *not* the same object as gets returned by `spCompileRequest_getProgram`. The latter should probably be called `*_getLinkedProgram` because it returns a composite component type that links the module with everything it `import`s. * An `IModule` can look up entry points declared in the module. Eventually a module should support looking up most of the declarations in the module (e.g., types) by name, but entry points are an obvious case. * A new `link()` operation is added to `IComponentType`. It is possible to have component types that have unsatisfied dependencies, such that trying to generate kernel code from them will fail. The `link()` operation tries to produce a new composite component type that combines a component with its dependencies, to enable code generation. The implementation of end-to-end compilation was using a function like this internally, but it hadn't been exposed to the API. Notes on the implementation: * The list of entry points declared in a given translation unit has moved from `TranslationUnitRequest` to the `Module` inside of it. * `EntryPoint` now has to do a song and dance much like `Module` to both inherit from `ComponentType` and support the `IEntryPoint` interface. * The `Session* m_session` member in `Linkage` (in terms of public API, this is the `slang::ISession` holding a pointer to the `slang::IGlobalSession`) has been changed to a `RefPtr`. Without this change an application can't just hold onto a `ComPtr<slang::ISession>`; they also need to retain the `IGlobalSession` or things will crash. The new behavior seems more correct, but I worry that it might introduce a leak. * The `asInternal` operation for `IComponentType` had to be updated to not just perform a cast. A type like `Module` has two `IComponentType` sub-objects, and only one of these is at the same address as the `ComponentType` base. * Similarly, the `Module::getInterface` logic was changed to fall back to `Super::getInterface` for all the cases other than `IID_IModule`, so that it would be guaranteed to return the `IComponentType` at the same address as `ComponentType` in response to a `queryInterface`. * Fixes for memory retain cycles As part of the earlier change, I made the `Linkage` type hold a `RefPtr` to the `Session`. The motivation there is it lets a user hang onto just a `slang::ISession` without having to also retain the `slang::IGlobalSession` for no immediately apparent reason. There are two problems that this surfaced, one pre-existing and one new. The new problem was that `Session` already held a `RefPtr<Linkage> m_builtinLinkage` for the linkage that holds the stdlib code. I solved that problem by splitting the parent pointer in `Linkage` into two pointers: a raw pointer that is used to actually locate the parent session, and a ref-counted one that can be used to *optionally* retain the parent session. The builtin linkage is then set up to explicitly not retain its parent, thus breaking the cycle. The second problem was a pre-existing one, where every `ComponentType` was holding a retained pointer to its parent `Linkage`, but in turn the `Linkage` was holding retained pointers to many `ComponentTypes` (and subclasses thereof). For this case I used the more expedient fix of making the parent pointer into a raw pointer, and figuring that it is a reasonable rule to expect user to retain the `Linkage` (aka `slang::ISession`) that owns a component type if they want to be able to use the component type. I might need/want to investigate a better fix for the latter issue, but for now this seems to clean up the issues I was seeing in the tests. Fingers crossed.
Diffstat (limited to 'source')
-rw-r--r--source/slang/slang-check-shader.cpp6
-rw-r--r--source/slang/slang-compiler.cpp19
-rw-r--r--source/slang/slang-compiler.h126
-rw-r--r--source/slang/slang-lower-to-ir.cpp2
-rw-r--r--source/slang/slang.cpp108
5 files changed, 239 insertions, 22 deletions
diff --git a/source/slang/slang-check-shader.cpp b/source/slang/slang-check-shader.cpp
index 611f6be58..da0313049 100644
--- a/source/slang/slang-check-shader.cpp
+++ b/source/slang/slang-check-shader.cpp
@@ -828,7 +828,7 @@ namespace Slang
// but didn't specify any groups (since the current
// compilation API doesn't allow for grouping).
//
- entryPointReq->getTranslationUnit()->entryPoints.add(entryPoint);
+ entryPointReq->getTranslationUnit()->module->_addEntryPoint(entryPoint);
outUnspecializedEntryPoints.add(entryPoint);
allComponentTypes.add(entryPoint);
@@ -907,7 +907,7 @@ namespace Slang
// group, so that its entry-point parameters lay out
// independent of the others.
//
- translationUnit->entryPoints.add(entryPoint);
+ translationUnit->module->_addEntryPoint(entryPoint);
outUnspecializedEntryPoints.add(entryPoint);
allComponentTypes.add(entryPoint);
@@ -1264,7 +1264,7 @@ namespace Slang
if(sink->GetErrorCount())
return nullptr;
- return unspecializedEntryPoint->specialize(
+ return ((ComponentType*) unspecializedEntryPoint)->specialize(
args.getBuffer(),
args.getCount(),
sink);
diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp
index d0fca1ab5..65c48d2f2 100644
--- a/source/slang/slang-compiler.cpp
+++ b/source/slang/slang-compiler.cpp
@@ -196,6 +196,16 @@ namespace Slang
// EntryPoint
//
+ static const Guid IID_IEntryPoint = SLANG_UUID_IEntryPoint;
+
+ ISlangUnknown* EntryPoint::getInterface(const Guid& guid)
+ {
+ if(guid == IID_IEntryPoint)
+ return static_cast<slang::IEntryPoint*>(this);
+
+ return Super::getInterface(guid);
+ }
+
RefPtr<EntryPoint> EntryPoint::create(
Linkage* linkage,
DeclRef<FuncDecl> funcDeclRef,
@@ -2059,6 +2069,15 @@ SlangResult dissassembleDXILUsingDXC(
if( result.format != ResultFormat::None )
return result;
+ // 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 result;
+ }
+
RefPtr<BackEndCompileRequest> backEndRequest = new BackEndCompileRequest(
m_program->getLinkage(),
sink,
diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h
index 2783197af..2c58cb901 100644
--- a/source/slang/slang-compiler.h
+++ b/source/slang/slang-compiler.h
@@ -286,6 +286,9 @@ namespace Slang
SlangInt specializationArgCount,
slang::IComponentType** outSpecializedComponentType,
ISlangBlob** outDiagnostics) SLANG_OVERRIDE;
+ SLANG_NO_THROW SlangResult SLANG_MCALL link(
+ slang::IComponentType** outLinkedComponentType,
+ ISlangBlob** outDiagnostics) SLANG_OVERRIDE;
/// Get the linkage (aka "session" in the public API) for this component type.
Linkage* getLinkage() { return m_linkage; }
@@ -465,7 +468,7 @@ namespace Slang
ComponentType(Linkage* linkage);
private:
- RefPtr<Linkage> m_linkage;
+ Linkage* m_linkage;
// Cache of target-specific programs for each target.
Dictionary<TargetRequest*, RefPtr<TargetProgram>> m_targetPrograms;
@@ -637,9 +640,61 @@ namespace Slang
/// `getName()` and `getProfile()` methods should be expected to
/// return useful data on pass-through entry points.
///
- class EntryPoint : public ComponentType
+ 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);
+ }
+
/// Create an entry point that refers to the given function.
static RefPtr<EntryPoint> create(
Linkage* linkage,
@@ -837,6 +892,27 @@ namespace Slang
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 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).
@@ -912,6 +988,11 @@ namespace Slang
List<ExpandedSpecializationArg> existentialArgs;
};
+ RefPtr<EntryPoint> findEntryPointByName(UnownedStringSlice const& name);
+
+ List<RefPtr<EntryPoint>> const& getEntryPoints() { return m_entryPoints; }
+ void _addEntryPoint(EntryPoint* entryPoint);
+
protected:
void acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo) SLANG_OVERRIDE;
@@ -937,6 +1018,26 @@ namespace Slang
// 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;
};
typedef Module LoadedModule;
@@ -963,7 +1064,9 @@ namespace Slang
void addSourceFile(SourceFile* sourceFile);
// The entry points associated with this translation unit
- List<RefPtr<EntryPoint>> entryPoints;
+ 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)
@@ -1258,9 +1361,19 @@ namespace Slang
// 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
@@ -1975,7 +2088,7 @@ namespace Slang
/// Get the downstream compiler prelude
const String& getDownstreamCompilerPrelude(PassThroughMode mode) { return m_downstreamCompilerPreludes[int(mode)]; }
- Session();
+ void init();
void addBuiltinSource(
RefPtr<Scope> const& scope,
@@ -2058,10 +2171,7 @@ SLANG_FORCE_INLINE slang::IModule* asExternal(Module* module)
return static_cast<slang::IModule*>(module);
}
-SLANG_FORCE_INLINE ComponentType* asInternal(slang::IComponentType* componentType)
-{
- return static_cast<ComponentType*>(componentType);
-}
+ComponentType* asInternal(slang::IComponentType* inComponentType);
SLANG_FORCE_INLINE slang::IComponentType* asExternal(ComponentType* componentType)
{
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index 7cf5215c9..14cc68b16 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -6800,7 +6800,7 @@ IRModule* generateIRForTranslationUnit(
// First, ensure that all entry points have been emitted,
// in case they require special handling.
- for (auto entryPoint : translationUnit->entryPoints)
+ for (auto entryPoint : translationUnit->getEntryPoints())
{
lowerFrontEndEntryPointToIR(context, entryPoint);
}
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index fce90d612..bfc77b2e3 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -38,13 +38,15 @@
namespace Slang {
// Allocate static const storage for the various interface IDs that the Slang API needs to expose
-static const Guid IID_ISlangUnknown = SLANG_UUID_ISlangUnknown;
-static const Guid IID_ISlangBlob = SLANG_UUID_ISlangBlob;
-static const Guid IID_ISession = SLANG_UUID_ISession;
-static const Guid IID_IGlobalSession = SLANG_UUID_IGlobalSession;
-static const Guid IID_IModule = SLANG_UUID_IModule;
+static const Guid IID_IComponentType = SLANG_UUID_IComponentType;
+static const Guid IID_IEntryPoint = SLANG_UUID_IEntryPoint;
+static const Guid IID_IGlobalSession = SLANG_UUID_IGlobalSession;
+static const Guid IID_IModule = SLANG_UUID_IModule;
+static const Guid IID_ISession = SLANG_UUID_ISession;
+static const Guid IID_ISlangBlob = SLANG_UUID_ISlangBlob;
+static const Guid IID_ISlangUnknown = SLANG_UUID_ISlangUnknown;
-Session::Session()
+void Session::init()
{
::memset(m_downstreamCompilerLocators, 0, sizeof(m_downstreamCompilerLocators));
DownstreamCompilerUtil::setDefaultLocators(m_downstreamCompilerLocators);
@@ -77,6 +79,16 @@ Session::Session()
m_builtinLinkage = new Linkage(this);
+ // Because the `Session` retains the builtin `Linkage`,
+ // we need to make sure that the parent pointer inside
+ // `Linkage` doesn't create a retain cycle.
+ //
+ // This operation ensures that the parent pointer will
+ // just be a raw pointer, so that the builtin linkage
+ // doesn't keep the parent session alive.
+ //
+ m_builtinLinkage->_stopRetainingParentSession();
+
// Initialize representations of some very basic types:
initializeTypes();
@@ -437,6 +449,7 @@ Profile getEffectiveProfile(EntryPoint* entryPoint, TargetRequest* target)
Linkage::Linkage(Session* session)
: m_session(session)
+ , m_retainedSession(session)
, m_sourceManager(&m_defaultSourceManager)
{
getNamePool()->setRootNamePool(session->getRootNamePool());
@@ -1704,9 +1717,9 @@ Module::Module(Linkage* linkage)
ISlangUnknown* Module::getInterface(const Guid& guid)
{
- if(guid == IID_ISlangUnknown || guid == IID_IModule)
+ if(guid == IID_IModule)
return asExternal(this);
- return nullptr;
+ return Super::getInterface(guid);
}
void Module::addModuleDependency(Module* module)
@@ -1725,14 +1738,51 @@ void Module::setModuleDecl(ModuleDecl* moduleDecl)
m_moduleDecl = moduleDecl;
}
-// ComponentType
+RefPtr<EntryPoint> Module::findEntryPointByName(UnownedStringSlice const& name)
+{
+ // TODO: We should consider having this function be expanded to be able
+ // to look up and validate possible entry-point functions in teh module
+ // even if they were not marked with `[shader(...)]` in the source code.
+ //
+ // With such a change the function would probably need to accept a stage
+ // to use and a sink to write validation errors to.
+
+ for(auto entryPoint : m_entryPoints)
+ {
+ if(entryPoint->getName()->text.getUnownedSlice() == name)
+ return entryPoint;
+ }
+
+ return nullptr;
+}
+
+void Module::_addEntryPoint(EntryPoint* entryPoint)
+{
+ m_entryPoints.add(entryPoint);
+}
-static const Guid IID_IComponentType = SLANG_UUID_IComponentType;
+
+// ComponentType
ComponentType::ComponentType(Linkage* linkage)
: m_linkage(linkage)
{}
+ComponentType* asInternal(slang::IComponentType* inComponentType)
+{
+ // Note: we use a `queryInterface` here instead of just a `static_cast`
+ // to ensure that the `IComponentType` we get is the preferred/canonical
+ // one, which shares its address with the `ComponentType`.
+ //
+ // TODO: An alternative choice here would be to have a "magic" IID that
+ // we pass into `queryInterface` that returns the `ComponentType` directly
+ // (without even `addRef`-ing it).
+ //
+ ComPtr<slang::IComponentType> componentType;
+ inComponentType->queryInterface(IID_IComponentType, (void**) componentType.writeRef());
+ return static_cast<ComponentType*>(componentType.get());
+}
+
ISlangUnknown* ComponentType::getInterface(Guid const& guid)
{
if(guid == IID_ISlangUnknown
@@ -1873,6 +1923,28 @@ SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::specialize(
return SLANG_OK;
}
+RefPtr<ComponentType> fillRequirements(
+ ComponentType* inComponentType);
+
+SLANG_NO_THROW SlangResult SLANG_MCALL ComponentType::link(
+ slang::IComponentType** outLinkedComponentType,
+ ISlangBlob** outDiagnostics)
+{
+ // TODO: It should be possible for `fillRequirements` to fail,
+ // in cases where we have a dependency that can't be automatically
+ // resolved.
+ //
+ SLANG_UNUSED(outDiagnostics);
+
+ auto linked = fillRequirements(this);
+ if(!linked)
+ return SLANG_FAIL;
+
+ *outLinkedComponentType = ComPtr<slang::IComponentType>(linked).detach();
+ return SLANG_OK;
+}
+
+
/// Visitor used by `ComponentType::enumerateModules`
struct EnumerateModulesVisitor : ComponentTypeVisitor
{
@@ -2488,6 +2560,7 @@ Session::~Session()
SLANG_API SlangSession* spCreateSession(const char*)
{
Slang::RefPtr<Slang::Session> session(new Slang::Session());
+ session->init();
// Will be returned with a refcount of 1
return asExternal(session.detach());
}
@@ -2500,6 +2573,7 @@ SLANG_API SlangResult slang_createGlobalSession(
return SLANG_E_NOT_IMPLEMENTED;
Slang::Session* globalSession = new Slang::Session();
+ globalSession->init();
Slang::ComPtr<slang::IGlobalSession> result(Slang::asExternal(globalSession));
*outGlobalSession = result.detach();
return SLANG_OK;
@@ -3487,6 +3561,20 @@ SLANG_API SlangResult spCompileRequest_getProgram(
return SLANG_OK;
}
+SLANG_API SlangResult spCompileRequest_getModule(
+ SlangCompileRequest* request,
+ SlangInt translationUnitIndex,
+ slang::IModule** outModule)
+{
+ if( !request ) return SLANG_ERROR_INVALID_PARAMETER;
+ auto req = Slang::asInternal(request);
+
+ auto module = req->getFrontEndReq()->getTranslationUnit(translationUnitIndex)->getModule();
+
+ *outModule = Slang::ComPtr<slang::IModule>(module).detach();
+ return SLANG_OK;
+}
+
SLANG_API SlangResult spCompileRequest_getEntryPoint(
SlangCompileRequest* request,
SlangInt entryPointIndex,