summaryrefslogtreecommitdiffstats
path: root/source/slang/compiler.h
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2019-02-19 11:46:05 -0800
committerGitHub <noreply@github.com>2019-02-19 11:46:05 -0800
commit32135c5bdfb4d387f8227742a2d2fd555898aca8 (patch)
treefe78cbff0927a41ec759ba859cc16dd1280f661b /source/slang/compiler.h
parenta3fd4e2bc40cfc77db953b14744c30e7a18e7c1d (diff)
First steps toward supporting interface-type parameters on shaders (#852)
* First steps toward supporting interface-type parameters on shaders What's New ---------- From the perspective of a user, the main thing this change adds is the ability to declare top-level shader parameters (either at global scope, or in an entry-point parameter list) with interface types. For example, the following becomes possible: ```hlsl // Define an interface to modify values interface IModifier { float4 modify(float4 val); } // Define some concrete implementations struct Doubler : IModifier { float4 modify(float4 val) { return val + val; } } struct Squarer : IModifier { ... } // Define a global shader parameter of interface type IModifier gGlobalModifier; // Define an entry point with an interface-type `uniform` parameter void myShader( unifrom IModifier entryPointModifier, float4 inColor : COLOR, out float4 outColor : SV_Target) { // Use the interface-type parameters to compute things float4 color = inColor; color = gGlobalModifier.modify(color); color = entryPointModifier.modify(color); outColor = color; } ``` The user can specialize that shader by specifying the concrete types to use for global and entry-point parameters of interface types (e.g., plugging in `Doubler` for `gGlobalModifier` and `Squarer` for `entryPointModifier`). The "plugging in" process is done in terms of a concept of both global and local "existential slots" which are a new `LayoutResourceKind` that represents the holes where concrete types need to be plugged in for existential/interface types. In simple cases like the above, each interface-type parameter will yield a single existential slot in either the global or entry-point parameter layout. Users can query the start slot and number of slots for each shader parameter, just like they would for any other resource that a parameter can consume. Before generating specialized code, the user plugs in the name of the concrete type they would like to use for each slot using `spSetTypeNameForGlobalExistentialSlot` and/or `spSetTypeNameForEntryPointExistentialSlot`. There are some major limitations to the implementation in this first change: * Parameters must be of interface type (e.g., `IFoo`) and not an array (`IFoo[3]`), or buffer (`ConstantBuffer<IFoo>`) over an interface type. Similarly, `struct` types with interface-type fields still don't work. * The work on interface-type function parameters still doesn't include support for `out` or `inout` parameters, nor for functions that return interface types (that isn't technically related to this change, but affects its usefullness). * No work is being done to correctly lay out shader parameters once the concrete types for existential slots are known, so that this change really only works when the concrete type that gets plugged in is empty. These limitations are severe enough that this feature isn't really usable as implemented in this change, and this merely represents a stepping stone toward a more complete implementation. Implementation -------------- The API side of thing largely mirrors what was already done to support passing strings for the type names to use for global/entry-point generic arguments, so there should be no major surprises there. The logic in `check.cpp` computes the list of existential slots when creating unspecialized `Program`s and `EntryPoint`s (this is logically the "front end" of the compiler), and then checks the supplied argument types against what is expected in each slot when creating specialized `Program`s and `EntryPoint`s. This again mirrors how generic arguments are handled. Type layout was extended to compute the number of existential slots that a type consumes, and will thus automatically assign ranges of slots to top-level and entry-point shader parameters in the same way it already allocates `register`s and `binding`s. The big missing feature is the ability to specialize a layout to account for the concrete types plugged into the existential-type slots. IR generation for specialized programs and entry points was slightly extended so that it attaches information about the concrete types plugged into the existential slots, and the witness tables that show how they conform to the interface for that slot. The linking step needed some small tweaks to make sure that information gets copied over to the target-specific program when we start code generation. The meat of the IR-level work is in `ir-bind-existentials.cpp`, which takes the information that was placed in the IR module by the generation/linking steps and uses it to rewrite shader parameters. For example, if there is a shader parameter `p` of type `IModifier`, and the corresponding existential slot has the type `Doubler` in it, we will rewrite the parameter to have type `Doubler`, and rewrite any uses of `p` to instead use `makeExistential(p, /*witness that Doubler conforms to IModifier*/)`. Once the replacement is done on the parameters, the existing work for specializing existential-based code when the input type(s) are known kicks in and does the rest. Testing ------- A single compute test is added to validate that this feature works. It is narrowly tailored to not require any of the features not supported by the initial implementation (e.g., all of the concrete types used have no members). The test case *does* include use of an associated type through one of these existential-type parameters, which has exposed a subtle bug in how "opening" of existential values is implemented in the front-end. Rather than fix the underlying problem, I cleaned up the code in the front-end to special case when the existential value being opened is a variable bound with `let`, to directly use a reference to that variable rather than introduce a temporary. Similarly, in the IR generation step, I added an optimization to make variables declared with `let` skip introducing an IR-level variable and just use the SSA value of their initializer directly instead. * fixup: missing files * fixup: incorrect type for unreachable return * fixup: actually comment ir-bind-existentials.cpp
Diffstat (limited to 'source/slang/compiler.h')
-rw-r--r--source/slang/compiler.h54
1 files changed, 51 insertions, 3 deletions
diff --git a/source/slang/compiler.h b/source/slang/compiler.h
index c975c1c2b..246b34ea7 100644
--- a/source/slang/compiler.h
+++ b/source/slang/compiler.h
@@ -17,7 +17,6 @@ namespace Slang
{
struct PathInfo;
struct IncludeHandler;
- class CompileRequest;
class ProgramLayout;
class PtrType;
class TargetProgram;
@@ -118,6 +117,26 @@ namespace Slang
ComPtr<ISlangBlob> blob;
};
+ /// Collects information about placeholder "slots" for interface/existential types.
+ struct ExistentialSlots
+ {
+ /// The existential/interface type associated with each slot.
+ List<RefPtr<Type>> types;
+
+ /// Source code for concrete type to plug in for each slot.
+// List<String> argStrings;
+
+ /// A concrete type argument plus a witness table for its conformance to the desired interface
+ struct Arg
+ {
+ RefPtr<Type> type;
+ RefPtr<Val> witness;
+ };
+
+ /// Concrete type arguments to plug into each slot
+ List<Arg> args;
+ };
+
/// A request for the front-end to find and validate an entry-point function
struct FrontEndEntryPointRequest : RefObject
{
@@ -264,12 +283,22 @@ namespace Slang
Name* name,
Profile profile);
+ UInt getExistentialSlotCount() { return m_existentialSlots.types.Count(); }
+ Type* getExistentialSlotType(UInt index) { return m_existentialSlots.types[index]; }
+ ExistentialSlots::Arg getExistentialSlotArg(UInt index) { return m_existentialSlots.args[index]; }
+
+ void _specializeExistentialSlots(
+ List<RefPtr<Expr>> const& args,
+ DiagnosticSink* sink);
+
private:
EntryPoint(
Name* name,
Profile profile,
DeclRef<FuncDecl> funcDeclRef);
+ void _collectExistentialParams();
+
// The name of the entry point function (e.g., `main`)
//
Name* m_name = nullptr;
@@ -278,6 +307,9 @@ namespace Slang
//
DeclRef<FuncDecl> m_funcDeclRef;
+ /// The existential/interface slots associated with the entry point parameter scope.
+ ExistentialSlots m_existentialSlots;
+
// 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)
@@ -528,8 +560,6 @@ namespace Slang
// Definitions to provide during preprocessing
Dictionary<String, String> preprocessorDefinitions;
-
-
// Source manager to help track files loaded
SourceManager m_defaultSourceManager;
SourceManager* m_sourceManager = nullptr;
@@ -886,7 +916,17 @@ namespace Slang
///
RefPtr<IRModule> getOrCreateIRModule(DiagnosticSink* sink);
+ UInt getExistentialSlotCount() { return m_globalExistentialSlots.types.Count(); }
+ Type* getExistentialSlotType(UInt index) { return m_globalExistentialSlots.types[index]; }
+ ExistentialSlots::Arg getExistentialSlotArg(UInt index) { return m_globalExistentialSlots.args[index]; }
+
+ void _collectExistentialParams();
+ void _specializeExistentialSlots(
+ List<RefPtr<Expr>> const& args,
+ DiagnosticSink* sink);
+
private:
+
// The linakge this program is associated with.
//
// Note that a `Program` keeps its associated linkage alive,
@@ -906,6 +946,9 @@ namespace Slang
// Specializations for global generic parameters (if any)
RefPtr<Substitutions> m_globalGenericSubst;
+ // The existential/interface slots associated with the global scope.
+ ExistentialSlots m_globalExistentialSlots;
+
// Generated IR for this program.
RefPtr<IRModule> m_irModule;
@@ -1044,6 +1087,8 @@ namespace Slang
/// Source code for the generic arguments to use for the global generic parameters of the program.
List<String> globalGenericArgStrings;
+ /// Types to use to fill global existential "slots"
+ List<String> globalExistentialSlotArgStrings;
bool shouldSkipCodegen = false;
@@ -1061,6 +1106,9 @@ namespace Slang
public:
/// Source code for the generic arguments to use for the generic parameters of the entry point.
List<String> genericArgStrings;
+
+ /// Source code for the type arguments to plug into the existential type "slots" of the entry point
+ List<String> existentialArgStrings;
};
List<EntryPointInfo> entryPoints;