summaryrefslogtreecommitdiff
path: root/source/slang/slang-emit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-emit.cpp')
-rw-r--r--source/slang/slang-emit.cpp606
1 files changed, 348 insertions, 258 deletions
diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp
index 345dfe9b2..f4db3c5c1 100644
--- a/source/slang/slang-emit.cpp
+++ b/source/slang/slang-emit.cpp
@@ -158,7 +158,293 @@ static void dumpIRIfEnabled(
}
}
-String emitEntryPoint(
+struct LinkingAndOptimizationOptions
+{
+ bool shouldLegalizeExistentialAndResourceTypes = true;
+ CLikeSourceEmitter* sourceEmitter = nullptr;
+};
+
+Result linkAndOptimizeIR(
+ BackEndCompileRequest* compileRequest,
+ Int entryPointIndex,
+ CodeGenTarget target,
+ TargetRequest* targetRequest,
+ LinkingAndOptimizationOptions const& options,
+ LinkedIR& outLinkedIR)
+{
+ auto sink = compileRequest->getSink();
+ auto program = compileRequest->getProgram();
+ auto targetProgram = program->getTargetProgram(targetRequest);
+
+ auto session = targetRequest->getSession();
+
+ // We start out by performing "linking" at the level of the IR.
+ // This step will create a fresh IR module to be used for
+ // code generation, and will copy in any IR definitions that
+ // the desired entry point requires. Along the way it will
+ // resolve references to imported/exported symbols across
+ // modules, and also select between the definitions of
+ // any "profile-overloaded" symbols.
+ //
+ outLinkedIR = linkIR(
+ compileRequest,
+ entryPointIndex,
+ target,
+ targetProgram);
+ auto irModule = outLinkedIR.module;
+ auto irEntryPoint = outLinkedIR.entryPoint;
+
+#if 0
+ dumpIRIfEnabled(compileRequest, irModule, "LINKED");
+#endif
+
+ validateIRModuleIfEnabled(compileRequest, irModule);
+
+ // If the user specified the flag that they want us to dump
+ // IR, then do it here, for the target-specific, but
+ // un-specialized IR.
+ dumpIRIfEnabled(compileRequest, irModule);
+
+ // Replace any global constants with their values.
+ //
+ replaceGlobalConstants(irModule);
+#if 0
+ dumpIRIfEnabled(compileRequest, irModule, "GLOBAL CONSTANTS REPLACED");
+#endif
+ validateIRModuleIfEnabled(compileRequest, irModule);
+
+
+ // When there are top-level existential-type parameters
+ // to the shader, we need to take the side-band information
+ // on how the existential "slots" were bound to concrete
+ // types, and use it to introduce additional explicit
+ // shader parameters for those slots, to be wired up to
+ // use sites.
+ //
+ bindExistentialSlots(irModule, sink);
+#if 0
+ dumpIRIfEnabled(compileRequest, irModule, "EXISTENTIALS BOUND");
+#endif
+ validateIRModuleIfEnabled(compileRequest, irModule);
+
+
+
+
+
+ // Now that we've linked the IR code, any layout/binding
+ // information has been attached to shader parameters
+ // and entry points. Now we are safe to make transformations
+ // that might move code without worrying about losing
+ // the connection between a parameter and its layout.
+ //
+ // An easy transformation of this kind is to take uniform
+ // parameters of a shader entry point and move them into
+ // the global scope instead.
+ //
+ moveEntryPointUniformParamsToGlobalScope(irModule, target);
+#if 0
+ dumpIRIfEnabled(compileRequest, irModule, "ENTRY POINT UNIFORMS MOVED");
+#endif
+ validateIRModuleIfEnabled(compileRequest, irModule);
+
+ // Desguar any union types, since these will be illegal on
+ // various targets.
+ //
+ desugarUnionTypes(irModule);
+#if 0
+ dumpIRIfEnabled(compileRequest, irModule, "UNIONS DESUGARED");
+#endif
+ validateIRModuleIfEnabled(compileRequest, irModule);
+
+ // Next, we need to ensure that the code we emit for
+ // the target doesn't contain any operations that would
+ // be illegal on the target platform. For example,
+ // none of our target supports generics, or interfaces,
+ // so we need to specialize those away.
+ //
+ // Simplification of existential-based and generics-based
+ // code may each open up opportunities for the other, so
+ // the relevant specialization transformations are handled in a
+ // single pass that looks for all simplification opportunities.
+ //
+ // TODO: We also need to extend this pass so that it will "expose"
+ // existential values that are nested inside of other types,
+ // so that the simplifications can be applied.
+ //
+ // TODO: This pass is *also* likely to be the place where we
+ // perform specialization of functions based on parameter
+ // values that need to be compile-time constants.
+ //
+ specializeModule(irModule);
+
+ // Debugging code for IR transformations...
+#if 0
+ dumpIRIfEnabled(compileRequest, irModule, "SPECIALIZED");
+#endif
+ validateIRModuleIfEnabled(compileRequest, irModule);
+
+
+ // Specialization can introduce dead code that could trip
+ // up downstream passes like type legalization, so we
+ // will run a DCE pass to clean up after the specialization.
+ //
+ // TODO: Are there other cleanup optimizations we should
+ // apply at this point?
+ //
+ eliminateDeadCode(irModule);
+#if 0
+ dumpIRIfEnabled(compileRequest, irModule, "AFTER DCE");
+#endif
+ validateIRModuleIfEnabled(compileRequest, irModule);
+
+ // We don't need the legalize pass for C/C++ based types
+ if(options.shouldLegalizeExistentialAndResourceTypes )
+// if (!(sourceStyle == SourceStyle::CPP || sourceStyle == SourceStyle::C))
+ {
+ // The Slang language allows interfaces to be used like
+ // ordinary types (including placing them in constant
+ // buffers and entry-point parameter lists), but then
+ // getting them to lay out in a reasonable way requires
+ // us to treat fields/variables with interface type
+ // *as if* they were pointers to heap-allocated "objects."
+ //
+ // Specialization will have replaced fields/variables
+ // with interface types like `IFoo` with fields/variables
+ // with pointer-like types like `ExistentialBox<SomeType>`.
+ //
+ // We need to legalize these pointer-like types away,
+ // which involves two main changes:
+ //
+ // 1. Any `ExistentialBox<...>` fields need to be moved
+ // out of their enclosing `struct` type, so that the layout
+ // of the enclosing type is computed as if the field had
+ // zero size.
+ //
+ // 2. Once an `ExistentialBox<X>` has been floated out
+ // of its parent and landed somwhere permanent (e.g., either
+ // a dedicated variable, or a field of constant buffer),
+ // we need to replace it with just an `X`, after which we
+ // will have (more) legal shader code.
+ //
+ legalizeExistentialTypeLayout(
+ irModule,
+ sink);
+ eliminateDeadCode(irModule);
+
+#if 0
+ dumpIRIfEnabled(compileRequest, irModule, "EXISTENTIALS LEGALIZED");
+#endif
+ validateIRModuleIfEnabled(compileRequest, irModule);
+
+ // Many of our target languages and/or downstream compilers
+ // don't support `struct` types that have resource-type fields.
+ // In order to work around this limitation, we will rewrite the
+ // IR so that any structure types with resource-type fields get
+ // split into a "tuple" that comprises the ordinary fields (still
+ // bundles up as a `struct`) and one element for each resource-type
+ // field (recursively).
+ //
+ // What used to be individual variables/parameters/arguments/etc.
+ // then become multiple variables/parameters/arguments/etc.
+ //
+ legalizeResourceTypes(
+ irModule,
+ sink);
+ eliminateDeadCode(irModule);
+
+ // Debugging output of legalization
+ #if 0
+ dumpIRIfEnabled(compileRequest, irModule, "LEGALIZED");
+ #endif
+ validateIRModuleIfEnabled(compileRequest, irModule);
+ }
+
+ // Once specialization and type legalization have been performed,
+ // we should perform some of our basic optimization steps again,
+ // to see if we can clean up any temporaries created by legalization.
+ // (e.g., things that used to be aggregated might now be split up,
+ // so that we can work with the individual fields).
+ constructSSA(irModule);
+
+#if 0
+ dumpIRIfEnabled(compileRequest, irModule, "AFTER SSA");
+#endif
+ validateIRModuleIfEnabled(compileRequest, irModule);
+
+ // After type legalization and subsequent SSA cleanup we expect
+ // that any resource types passed to functions are exposed
+ // as their own top-level parameters (which might have
+ // resource or array-of-...-resource types).
+ //
+ // Many of our targets place restrictions on how certain
+ // resource types can be used, so that having them as
+ // function parameters is invalid. To clean this up,
+ // we will try to specialize called functions based
+ // on the actual resources that are being passed to them
+ // at specific call sites.
+ //
+ // Because the legalization may depend on what target
+ // we are compiling for (certain things might be okay
+ // for D3D targets that are not okay for Vulkan), we
+ // pass down the target request along with the IR.
+ //
+ specializeResourceParameters(compileRequest, targetRequest, irModule);
+
+#if 0
+ dumpIRIfEnabled(compileRequest, irModule, "AFTER RESOURCE SPECIALIZATION");
+#endif
+ validateIRModuleIfEnabled(compileRequest, irModule);
+
+
+ // For GLSL only, we will need to perform "legalization" of
+ // the entry point and any entry-point parameters.
+ //
+ // TODO: We should consider moving this legalization work
+ // as late as possible, so that it doesn't affect how other
+ // optimization passes need to work.
+ //
+ switch (target)
+ {
+ case CodeGenTarget::GLSL:
+ {
+ legalizeEntryPointForGLSL(
+ session,
+ irModule,
+ irEntryPoint,
+ compileRequest->getSink(),
+ options.sourceEmitter->getGLSLExtensionTracker());
+
+#if 0
+ dumpIRIfEnabled(compileRequest, irModule, "GLSL LEGALIZED");
+#endif
+ validateIRModuleIfEnabled(compileRequest, irModule);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // The resource-based specialization pass above
+ // may create specialized versions of functions, but
+ // it does not try to completely eliminate the original
+ // functions, so there might still be invalid code in
+ // our IR module.
+ //
+ // To clean up the code, we will apply a fairly general
+ // dead-code-elimination (DCE) pass that only retains
+ // whatever code is "live."
+ //
+ eliminateDeadCode(irModule);
+#if 0
+ dumpIRIfEnabled(compileRequest, irModule, "AFTER DCE");
+#endif
+ validateIRModuleIfEnabled(compileRequest, irModule);
+
+ return SLANG_OK;
+}
+
+String emitEntryPointSource(
BackEndCompileRequest* compileRequest,
Int entryPointIndex,
CodeGenTarget target,
@@ -166,7 +452,6 @@ String emitEntryPoint(
{
auto sink = compileRequest->getSink();
auto program = compileRequest->getProgram();
- auto targetProgram = program->getTargetProgram(targetRequest);
auto entryPoint = program->getEntryPoint(entryPointIndex);
@@ -226,269 +511,30 @@ String emitEntryPoint(
// Outside because we want to keep IR in scope whilst we are processing emits
LinkedIR linkedIR;
{
- auto session = targetRequest->getSession();
-
- // We start out by performing "linking" at the level of the IR.
- // This step will create a fresh IR module to be used for
- // code generation, and will copy in any IR definitions that
- // the desired entry point requires. Along the way it will
- // resolve references to imported/exported symbols across
- // modules, and also select between the definitions of
- // any "profile-overloaded" symbols.
- //
- linkedIR = linkIR(
- compileRequest,
- entryPointIndex,
- target,
- targetProgram);
- auto irModule = linkedIR.module;
- auto irEntryPoint = linkedIR.entryPoint;
-
-#if 0
- dumpIRIfEnabled(compileRequest, irModule, "LINKED");
-#endif
-
- validateIRModuleIfEnabled(compileRequest, irModule);
+ LinkingAndOptimizationOptions linkingAndOptimizationOptions;
- // If the user specified the flag that they want us to dump
- // IR, then do it here, for the target-specific, but
- // un-specialized IR.
- dumpIRIfEnabled(compileRequest, irModule);
-
- // Replace any global constants with their values.
- //
- replaceGlobalConstants(irModule);
-#if 0
- dumpIRIfEnabled(compileRequest, irModule, "GLOBAL CONSTANTS REPLACED");
-#endif
- validateIRModuleIfEnabled(compileRequest, irModule);
-
-
- // When there are top-level existential-type parameters
- // to the shader, we need to take the side-band information
- // on how the existential "slots" were bound to concrete
- // types, and use it to introduce additional explicit
- // shader parameters for those slots, to be wired up to
- // use sites.
- //
- bindExistentialSlots(irModule, sink);
-#if 0
- dumpIRIfEnabled(compileRequest, irModule, "EXISTENTIALS BOUND");
-#endif
- validateIRModuleIfEnabled(compileRequest, irModule);
+ linkingAndOptimizationOptions.sourceEmitter = sourceEmitter;
-
-
-
-
- // Now that we've linked the IR code, any layout/binding
- // information has been attached to shader parameters
- // and entry points. Now we are safe to make transformations
- // that might move code without worrying about losing
- // the connection between a parameter and its layout.
- //
- // An easy transformation of this kind is to take uniform
- // parameters of a shader entry point and move them into
- // the global scope instead.
- //
- moveEntryPointUniformParamsToGlobalScope(irModule, target);
-#if 0
- dumpIRIfEnabled(compileRequest, irModule, "ENTRY POINT UNIFORMS MOVED");
-#endif
- validateIRModuleIfEnabled(compileRequest, irModule);
-
- // Desguar any union types, since these will be illegal on
- // various targets.
- //
- desugarUnionTypes(irModule);
-#if 0
- dumpIRIfEnabled(compileRequest, irModule, "UNIONS DESUGARED");
-#endif
- validateIRModuleIfEnabled(compileRequest, irModule);
-
- // Next, we need to ensure that the code we emit for
- // the target doesn't contain any operations that would
- // be illegal on the target platform. For example,
- // none of our target supports generics, or interfaces,
- // so we need to specialize those away.
- //
- // Simplification of existential-based and generics-based
- // code may each open up opportunities for the other, so
- // the relevant specialization transformations are handled in a
- // single pass that looks for all simplification opportunities.
- //
- // TODO: We also need to extend this pass so that it will "expose"
- // existential values that are nested inside of other types,
- // so that the simplifications can be applied.
- //
- // TODO: This pass is *also* likely to be the place where we
- // perform specialization of functions based on parameter
- // values that need to be compile-time constants.
- //
- specializeModule(irModule);
-
- // Debugging code for IR transformations...
-#if 0
- dumpIRIfEnabled(compileRequest, irModule, "SPECIALIZED");
-#endif
- validateIRModuleIfEnabled(compileRequest, irModule);
-
-
- // Specialization can introduce dead code that could trip
- // up downstream passes like type legalization, so we
- // will run a DCE pass to clean up after the specialization.
- //
- // TODO: Are there other cleanup optimizations we should
- // apply at this point?
- //
- eliminateDeadCode(irModule);
-#if 0
- dumpIRIfEnabled(compileRequest, irModule, "AFTER DCE");
-#endif
- validateIRModuleIfEnabled(compileRequest, irModule);
-
- // We don't need the legalize pass for C/C++ based types
- if (!(sourceStyle == SourceStyle::CPP || sourceStyle == SourceStyle::C))
+ switch( sourceStyle )
{
- // The Slang language allows interfaces to be used like
- // ordinary types (including placing them in constant
- // buffers and entry-point parameter lists), but then
- // getting them to lay out in a reasonable way requires
- // us to treat fields/variables with interface type
- // *as if* they were pointers to heap-allocated "objects."
- //
- // Specialization will have replaced fields/variables
- // with interface types like `IFoo` with fields/variables
- // with pointer-like types like `ExistentialBox<SomeType>`.
- //
- // We need to legalize these pointer-like types away,
- // which involves two main changes:
- //
- // 1. Any `ExistentialBox<...>` fields need to be moved
- // out of their enclosing `struct` type, so that the layout
- // of the enclosing type is computed as if the field had
- // zero size.
- //
- // 2. Once an `ExistentialBox<X>` has been floated out
- // of its parent and landed somwhere permanent (e.g., either
- // a dedicated variable, or a field of constant buffer),
- // we need to replace it with just an `X`, after which we
- // will have (more) legal shader code.
- //
- legalizeExistentialTypeLayout(
- irModule,
- sink);
- eliminateDeadCode(irModule);
-
-#if 0
- dumpIRIfEnabled(compileRequest, irModule, "EXISTENTIALS LEGALIZED");
-#endif
- validateIRModuleIfEnabled(compileRequest, irModule);
-
- // Many of our target languages and/or downstream compilers
- // don't support `struct` types that have resource-type fields.
- // In order to work around this limitation, we will rewrite the
- // IR so that any structure types with resource-type fields get
- // split into a "tuple" that comprises the ordinary fields (still
- // bundles up as a `struct`) and one element for each resource-type
- // field (recursively).
- //
- // What used to be individual variables/parameters/arguments/etc.
- // then become multiple variables/parameters/arguments/etc.
- //
- legalizeResourceTypes(
- irModule,
- sink);
- eliminateDeadCode(irModule);
- }
-
- // Debugging output of legalization
-#if 0
- dumpIRIfEnabled(compileRequest, irModule, "LEGALIZED");
-#endif
- validateIRModuleIfEnabled(compileRequest, irModule);
-
- // Once specialization and type legalization have been performed,
- // we should perform some of our basic optimization steps again,
- // to see if we can clean up any temporaries created by legalization.
- // (e.g., things that used to be aggregated might now be split up,
- // so that we can work with the individual fields).
- constructSSA(irModule);
-
-#if 0
- dumpIRIfEnabled(compileRequest, irModule, "AFTER SSA");
-#endif
- validateIRModuleIfEnabled(compileRequest, irModule);
-
- // After type legalization and subsequent SSA cleanup we expect
- // that any resource types passed to functions are exposed
- // as their own top-level parameters (which might have
- // resource or array-of-...-resource types).
- //
- // Many of our targets place restrictions on how certain
- // resource types can be used, so that having them as
- // function parameters is invalid. To clean this up,
- // we will try to specialize called functions based
- // on the actual resources that are being passed to them
- // at specific call sites.
- //
- // Because the legalization may depend on what target
- // we are compiling for (certain things might be okay
- // for D3D targets that are not okay for Vulkan), we
- // pass down the target request along with the IR.
- //
- specializeResourceParameters(compileRequest, targetRequest, irModule);
-
-#if 0
- dumpIRIfEnabled(compileRequest, irModule, "AFTER RESOURCE SPECIALIZATION");
-#endif
- validateIRModuleIfEnabled(compileRequest, irModule);
-
-
- // For GLSL only, we will need to perform "legalization" of
- // the entry point and any entry-point parameters.
- //
- // TODO: We should consider moving this legalization work
- // as late as possible, so that it doesn't affect how other
- // optimization passes need to work.
- //
- switch (target)
- {
- case CodeGenTarget::GLSL:
- {
- legalizeEntryPointForGLSL(
- session,
- irModule,
- irEntryPoint,
- compileRequest->getSink(),
- sourceEmitter->getGLSLExtensionTracker());
-
-#if 0
- dumpIRIfEnabled(compileRequest, irModule, "GLSL LEGALIZED");
-#endif
- validateIRModuleIfEnabled(compileRequest, irModule);
- }
- break;
-
default:
break;
+
+ case SourceStyle::CPP:
+ case SourceStyle::C:
+ linkingAndOptimizationOptions.shouldLegalizeExistentialAndResourceTypes = false;
+ break;
}
- // The resource-based specialization pass above
- // may create specialized versions of functions, but
- // it does not try to completely eliminate the original
- // functions, so there might still be invalid code in
- // our IR module.
- //
- // To clean up the code, we will apply a fairly general
- // dead-code-elimination (DCE) pass that only retains
- // whatever code is "live."
- //
- eliminateDeadCode(irModule);
-#if 0
- dumpIRIfEnabled(compileRequest, irModule, "AFTER DCE");
-#endif
- validateIRModuleIfEnabled(compileRequest, irModule);
+ linkAndOptimizeIR(
+ compileRequest,
+ entryPointIndex,
+ target,
+ targetRequest,
+ linkingAndOptimizationOptions,
+ linkedIR);
+
+ auto irModule = linkedIR.module;
// After all of the required optimization and legalization
// passes have been performed, we can emit target code from
@@ -570,4 +616,48 @@ String emitEntryPoint(
return finalResult;
}
+SlangResult emitSPIRVFromIR(
+ BackEndCompileRequest* compileRequest,
+ IRModule* irModule,
+ IRFunc* irEntryPoint,
+ List<uint8_t>& spirvOut);
+
+SlangResult emitSPIRVForEntryPointDirectly(
+ BackEndCompileRequest* compileRequest,
+ Int entryPointIndex,
+ TargetRequest* targetRequest,
+ List<uint8_t>& spirvOut)
+{
+ auto sink = compileRequest->getSink();
+ auto program = compileRequest->getProgram();
+ auto targetProgram = program->getTargetProgram(targetRequest);
+ auto programLayout = targetProgram->getOrCreateLayout(sink);
+
+ RefPtr<EntryPointLayout> entryPointLayout = programLayout->entryPoints[entryPointIndex];
+
+ // Outside because we want to keep IR in scope whilst we are processing emits
+ LinkedIR linkedIR;
+ LinkingAndOptimizationOptions linkingAndOptimizationOptions;
+ linkAndOptimizeIR(
+ compileRequest,
+ entryPointIndex,
+ targetRequest->getTarget(),
+ targetRequest,
+ linkingAndOptimizationOptions,
+ linkedIR);
+
+ auto irModule = linkedIR.module;
+ auto irEntryPoint = linkedIR.entryPoint;
+
+ emitSPIRVFromIR(
+ compileRequest,
+ irModule,
+ irEntryPoint,
+ spirvOut);
+
+ return SLANG_OK;
+}
+
+
+
} // namespace Slang