diff options
Diffstat (limited to 'source/slang/slang-emit.cpp')
| -rw-r--r-- | source/slang/slang-emit.cpp | 606 |
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 |
