diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2019-05-31 17:20:37 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-05-31 17:20:37 -0400 |
| commit | 6cbc3929a54d37bd23cb5efa8e3320ba02f78b2f (patch) | |
| tree | 5a23cb47782e9e2a77762c90dd35da1005eba8d0 /source/slang/slang-emit.cpp | |
| parent | b81ff3ef968d1cc4e954b31a1812b3c391d17b02 (diff) | |
Use slang- prefix on slang compiler and core source (#973)
* Prefixing source files in source/slang with slang-
* Prefix source in source/slang with slang- prefix.
* Rename core source files with slang- prefix.
* Update project files.
* Fix problems from automatic merge.
Diffstat (limited to 'source/slang/slang-emit.cpp')
| -rw-r--r-- | source/slang/slang-emit.cpp | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp new file mode 100644 index 000000000..67b57660a --- /dev/null +++ b/source/slang/slang-emit.cpp @@ -0,0 +1,510 @@ +// slang-emit.cpp +#include "slang-emit.h" + +#include "../core/slang-writer.h" +#include "slang-ir-bind-existentials.h" +#include "slang-ir-dce.h" +#include "slang-ir-entry-point-uniforms.h" +#include "slang-ir-glsl-legalize.h" +#include "slang-ir-insts.h" +#include "slang-ir-link.h" +#include "slang-ir-restructure.h" +#include "slang-ir-restructure-scoping.h" +#include "slang-ir-specialize.h" +#include "slang-ir-specialize-resources.h" +#include "slang-ir-ssa.h" +#include "slang-ir-union.h" +#include "slang-ir-validate.h" +#include "slang-legalize-types.h" +#include "slang-lower-to-ir.h" +#include "slang-mangle.h" +#include "slang-name.h" +#include "slang-syntax.h" +#include "slang-type-layout.h" +#include "slang-visitor.h" + +#include "slang-source-stream.h" +#include "slang-emit-context.h" + +#include "slang-c-like-source-emitter.h" + +#include <assert.h> + +namespace Slang { + +enum class BuiltInCOp +{ + Splat, //< Splat a single value to all values of a vector or matrix type + Init, //< Initialize with parameters (must match the type) +}; + + +// + + +// + +EntryPointLayout* findEntryPointLayout( + ProgramLayout* programLayout, + EntryPoint* entryPoint) +{ + for( auto entryPointLayout : programLayout->entryPoints ) + { + if(entryPointLayout->entryPoint->getName() != entryPoint->getName()) + continue; + + // TODO: We need to be careful about this check, since it relies on + // the profile information in the layout matching that in the request. + // + // What we really seem to want here is some dictionary mapping the + // `EntryPoint` directly to the `EntryPointLayout`, and maybe + // that is precisely what we should build... + // + if(entryPointLayout->profile != entryPoint->getProfile()) + continue; + + // TODO: can't easily filter on translation unit here... + // Ideally the `EntryPoint` should get filled in with a pointer + // the specific function declaration that represents the entry point. + + return entryPointLayout.Ptr(); + } + + return nullptr; +} + + /// Given a layout computed for a scope, get the layout to use when lookup up variables. + /// + /// A scope (such as the global scope of a program) groups its + /// parameters into a pseudo-`struct` type for layout purposes, + /// and in some cases that type will in turn be wrapped in a + /// `ConstantBuffer` type to indicate that the parameters needed + /// an implicit constant buffer to be allocated. + /// + /// This function "unwraps" the type layout to find the structure + /// type layout that must be stored inside. + /// +StructTypeLayout* getScopeStructLayout( + ScopeLayout* scopeLayout) +{ + auto scopeTypeLayout = scopeLayout->parametersLayout->typeLayout; + + if( auto constantBufferTypeLayout = as<ParameterGroupTypeLayout>(scopeTypeLayout) ) + { + scopeTypeLayout = constantBufferTypeLayout->offsetElementTypeLayout; + } + + if( auto structTypeLayout = as<StructTypeLayout>(scopeTypeLayout) ) + { + return structTypeLayout; + } + + SLANG_UNEXPECTED("uhandled global-scope binding layout"); + return nullptr; +} + + /// Given a layout computed for a program, get the layout to use when lookup up variables. + /// + /// This is just an alias of `getScopeStructLayout`. + /// +StructTypeLayout* getGlobalStructLayout( + ProgramLayout* programLayout) +{ + return getScopeStructLayout(programLayout); +} + +static void dumpIR( + BackEndCompileRequest* compileRequest, + IRModule* irModule, + char const* label) +{ + DiagnosticSinkWriter writerImpl(compileRequest->getSink()); + WriterHelper writer(&writerImpl); + + if(label) + { + writer.put("### "); + writer.put(label); + writer.put(":\n"); + } + + dumpIR(irModule, writer.getWriter()); + + if( label ) + { + writer.put("###\n"); + } +} + +static void dumpIRIfEnabled( + BackEndCompileRequest* compileRequest, + IRModule* irModule, + char const* label = nullptr) +{ + if(compileRequest->shouldDumpIR) + { + dumpIR(compileRequest, irModule, label); + } +} + +String emitEntryPoint( + BackEndCompileRequest* compileRequest, + EntryPoint* entryPoint, + CodeGenTarget target, + TargetRequest* targetRequest) +{ + auto sink = compileRequest->getSink(); + auto program = compileRequest->getProgram(); + auto targetProgram = program->getTargetProgram(targetRequest); + auto programLayout = targetProgram->getOrCreateLayout(sink); + +// auto translationUnit = entryPoint->getTranslationUnit(); + + auto lineDirectiveMode = compileRequest->getLineDirectiveMode(); + // To try to make the default behavior reasonable, we will + // always use C-style line directives (to give the user + // good source locations on error messages from downstream + // compilers) *unless* they requested raw GLSL as the + // output (in which case we want to maximize compatibility + // with downstream tools). + if (lineDirectiveMode == LineDirectiveMode::Default && targetRequest->getTarget() == CodeGenTarget::GLSL) + { + lineDirectiveMode = LineDirectiveMode::GLSL; + } + + SourceStream sourceStream(compileRequest->getSourceManager(), lineDirectiveMode ); + + EmitContext emitContext; + emitContext.compileRequest = compileRequest; + emitContext.target = target; + emitContext.entryPoint = entryPoint; + emitContext.effectiveProfile = getEffectiveProfile(entryPoint, targetRequest); + emitContext.stream = &sourceStream; + + if (entryPoint && programLayout) + { + emitContext.entryPointLayout = findEntryPointLayout( + programLayout, + entryPoint); + } + + emitContext.programLayout = programLayout; + + // Layout information for the global scope is either an ordinary + // `struct` in the common case, or a constant buffer in the case + // where there were global-scope uniforms. + + StructTypeLayout* globalStructLayout = programLayout ? getGlobalStructLayout(programLayout) : nullptr; + emitContext.globalStructLayout = globalStructLayout; + + CLikeSourceEmitter sourceEmitter(&emitContext); + + { + 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. + // + auto linkedIR = linkIR( + compileRequest, + entryPoint, + programLayout, + target, + targetRequest); + auto irModule = linkedIR.module; + auto irEntryPoint = linkedIR.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); + + // 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); +#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(compileRequest, irModule); +#if 0 + dumpIRIfEnabled(compileRequest, irModule, "AFTER DCE"); +#endif + validateIRModuleIfEnabled(compileRequest, irModule); + + // 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(compileRequest, 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(compileRequest, 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(), + &emitContext.extensionUsageTracker); + +#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(compileRequest, irModule); +#if 0 + dumpIRIfEnabled(compileRequest, irModule, "AFTER DCE"); +#endif + validateIRModuleIfEnabled(compileRequest, irModule); + + // After all of the required optimization and legalization + // passes have been performed, we can emit target code from + // the IR module. + // + // TODO: do we want to emit directly from IR, or translate the + // IR back into AST for emission? + sourceEmitter.emitIRModule(irModule); + } + + // Deal with cases where a particular stage requires certain GLSL versions + // and/or extensions. + switch( entryPoint->getStage() ) + { + default: + break; + + case Stage::AnyHit: + case Stage::Callable: + case Stage::ClosestHit: + case Stage::Intersection: + case Stage::Miss: + case Stage::RayGeneration: + if( target == CodeGenTarget::GLSL ) + { + emitContext.extensionUsageTracker.requireGLSLExtension("GL_NV_ray_tracing"); + emitContext.extensionUsageTracker.requireGLSLVersion(ProfileVersion::GLSL_460); + } + break; + } + + String code = sourceStream.getContent(); + sourceStream.clearContent(); + + // Now that we've emitted the code for all the declarations in the file, + // it is time to stitch together the final output. + + // There may be global-scope modifiers that we should emit now + sourceEmitter.emitGLSLPreprocessorDirectives(); + + sourceEmitter.emitLayoutDirectives(targetRequest); + + String prefix = sourceStream.getContent(); + + StringBuilder finalResultBuilder; + finalResultBuilder << prefix; + + finalResultBuilder << emitContext.extensionUsageTracker.getGLSLExtensionRequireLines(); + + finalResultBuilder << code; + + String finalResult = finalResultBuilder.ProduceString(); + + return finalResult; +} + +} // namespace Slang |
