diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2019-10-17 17:34:31 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-10-17 17:34:31 -0700 |
| commit | a854bf2fde6e466aa698f4132971faadc827913a (patch) | |
| tree | ca7eceb1462379f2869ea366c727be25c894e73f /source | |
| parent | bb4a2ac62f59fd0cd2f597207bbfa93e07f7525b (diff) | |
Initial work on representing layout at IR level (#1079)
* Initial work on representing layout at IR level
This change starts the process of making the back-end of the compiler independent of the AST-level layout information (`TypeLayout`, `VarLayout`, etc.) so that it instead only relies on layout information that is embedded into IR modules. This brings us incrementally closer to a world in which the back-end could be run without the AST-level structures even existing (e.g., for an application that just wants to ship IR without any AST information for IP protection, while still supporting some amount of linking and specialization).
The main parts of the change are:
* There is a bunch of incidental churn related to specifying entry points by index instead of the `EntryPoint` object for certain operations. This ends up being a better choice because we can use the index to look up side-band information about the entry point that might not be stored on the `EntryPoint` object itself. In particular...
* We expand the `ComponentType` interface to support looking up the mangled name of an entry point by index. In common cases (no generic/interface specialization) this would be the same as asking the `EntryPoint` for its mangled name, but in cases where we have specialized a generic entry point, the mangled name would include speicalization arguments that are only available on the `SpecializedComponentType` that wraps the entry point. This part of the change isn't ideal and there might be a better solution waiting to be invented. Note that we store mangled entry point names as strings rather than using `DeclRef`s because that ensures that the information could be serialized and deserialized without a dependence on the AST.
* The `TargetProgram` type (which represents binding a specific `ComponentType` for a shader program to a specific `TargetRequest` that represents the target platform) is expanded to include an `IRModule` that represents layout information, in addition to the AST-level `ProgramLayout` it already contained. We create both of these objects at the same time (on-demand) to simplify the overall flow (so that any code that triggers creation of the AST-level layout will also ensure that the IR-level layout exists).
* A bunch of code in the emit passes that was passing down layout-related objects has been eliminated. It appears that most of those objects weren't actually being used, so this is just a cleanup, but it helps ensure that the back-end steps are "clean" and don't depend on the AST-level information. The one big exception here is that the emit logic needs to know the stage for the entry point being emitted (to deal with one wrinkle in translating DXR to VKRT).
* A big change (actually introduced by @jsmall-nvidia in a branch that this change copied and then built from) is to introduce some more explicit IR instructions to represent layout information, notably an `IRTypeLayout` and an `IRVarLayout`. For now these objects still reference their AST equivalents, but the separation gives us an incremental path to move information from the AST-level objects over to the IR ones. This work includes logic in `IRBuilder` to construct the IR-level layout objects from the AST-level ones on-demand, so that the existing code paths that try to attach AST-level layout will continue to work for now.
* Because layout information is now embedded in the IR, the `slang-ir-link.cpp` logic loses a lot of cases that used to deal with attaching AST-level layout objects to IR-level instructions during the linking process. Instead, the linker now assumes that one (or more) of the input IR modules will have layout information associated with it, and the linker makes sure to copy layout decorations (and the instructions they reference) from the input IR module(s) to the output using its more ordinary mechanisms.
* Inside `slang-lower-to-ir.cpp`, we add logic to construct an IR module in a `TargetProgram` that simply references the global shader parameters, entry points, etc. and attaches IR layout decorations to them. This is akin to the existing pass in the same file that constructs IR to represent specialization information, and both of these passes share infrastructure with the main AST->IR lowering pass. Eventually, it is expected that this pass will encompass more of the logic for copying AST-level layout information over to IR-level equivalents.
* One small wrinkle with this change was that the output for an HLSL generation test case changed some of its `#line` directives. The old code was actually more inaccurate than the new, so this change just updated the baseline. It also added some logic in the linker to make sure that when an IR instruction has multiple definitions, we try to pick up a source location from any of them, in case the "main" one somehow didn't get a location.
* Another small fix was that the key/value map in `StructTypeLayout` for mapping fields/members to their layouts was keyed on `Decl*` when it really should have been `VarDeclBase*`.
This change should in principle be a pure refactoring with no functionality changes, so no new tests were added. It is unfortunately also a change that has a high probability of breaking at least *some* client code, so we may want to be defensive and mark this with a new major version number (well, a new *minor* version number since we are pre-`1.0`) to give us some room for releasing hotfixes to the old version if needed.
* fixup: infinite recursion bug detected by clang
* fixup: remove commented-out code
Diffstat (limited to 'source')
24 files changed, 614 insertions, 255 deletions
diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp index ecd34ccd3..ec959536a 100644 --- a/source/slang/slang-compiler.cpp +++ b/source/slang/slang-compiler.cpp @@ -9,6 +9,7 @@ #include "slang-compiler.h" #include "slang-lexer.h" #include "slang-lower-to-ir.h" +#include "slang-mangle.h" #include "slang-parameter-binding.h" #include "slang-parser.h" #include "slang-preprocessor.h" @@ -328,6 +329,23 @@ namespace Slang return getModule(); } + String EntryPoint::getEntryPointMangledName(Index index) + { + SLANG_UNUSED(index); + SLANG_ASSERT(index == 0); + + // Note: this routine might get called on the "dummy" + // `EntryPoint` objects we create when doing pass-through + // compilation, in which case there won't be any + // function decl to be referenced and thus have + // its mangled name computed. + // + if(auto funcDeclRef = getFuncDeclRef()) + return getMangledName(funcDeclRef); + else + return String(); + } + void EntryPoint::acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo) { visitor->visitEntryPoint(this, as<EntryPointSpecializationInfo>(specializationInfo)); @@ -597,7 +615,6 @@ namespace Slang String emitHLSLForEntryPoint( BackEndCompileRequest* compileRequest, - EntryPoint* entryPoint, Int entryPointIndex, TargetRequest* targetReq, EndToEndCompileRequest* endToEndReq) @@ -622,7 +639,7 @@ namespace Slang { return emitEntryPoint( compileRequest, - entryPoint, + entryPointIndex, CodeGenTarget::HLSL, targetReq); } @@ -630,7 +647,6 @@ namespace Slang String emitCPPForEntryPoint( BackEndCompileRequest* compileRequest, - EntryPoint* entryPoint, Int entryPointIndex, TargetRequest* targetReq, EndToEndCompileRequest* endToEndReq) @@ -653,13 +669,12 @@ namespace Slang } else { - return emitEntryPoint(compileRequest, entryPoint, CodeGenTarget::CPPSource, targetReq); + return emitEntryPoint(compileRequest, entryPointIndex, CodeGenTarget::CPPSource, targetReq); } } String emitGLSLForEntryPoint( BackEndCompileRequest* compileRequest, - EntryPoint* entryPoint, Int entryPointIndex, TargetRequest* targetReq, EndToEndCompileRequest* endToEndReq) @@ -694,11 +709,9 @@ namespace Slang } else { - // TODO(tfoley): need to pass along the entry point - // so that we properly emit it as the `main` function. return emitEntryPoint( compileRequest, - entryPoint, + entryPointIndex, CodeGenTarget::GLSL, targetReq); } @@ -896,7 +909,7 @@ namespace Slang return SLANG_FAIL; } - auto hlslCode = emitHLSLForEntryPoint(compileRequest, entryPoint, entryPointIndex, targetReq, endToEndReq); + auto hlslCode = emitHLSLForEntryPoint(compileRequest, entryPointIndex, targetReq, endToEndReq); maybeDumpIntermediate(compileRequest, hlslCode.getBuffer(), CodeGenTarget::HLSL); auto profile = getEffectiveProfile(entryPoint, targetReq); @@ -1163,7 +1176,6 @@ SlangResult dissassembleDXILUsingDXC( SlangResult emitCPUBinaryForEntryPoint( BackEndCompileRequest* slangRequest, - EntryPoint* entryPoint, Int entryPointIndex, TargetRequest* targetReq, EndToEndCompileRequest* endToEndReq, @@ -1330,7 +1342,6 @@ SlangResult dissassembleDXILUsingDXC( { rawSource = emitCPPForEntryPoint( slangRequest, - entryPoint, entryPointIndex, targetReq, endToEndReq); @@ -1602,7 +1613,6 @@ SlangResult dissassembleDXILUsingDXC( String rawGLSL = emitGLSLForEntryPoint( slangRequest, - entryPoint, entryPointIndex, targetReq, endToEndReq); @@ -1677,7 +1687,6 @@ SlangResult dissassembleDXILUsingDXC( List<uint8_t> code; if (SLANG_SUCCEEDED(emitCPUBinaryForEntryPoint( compileRequest, - entryPoint, entryPointIndex, targetReq, endToEndReq, @@ -1700,7 +1709,6 @@ SlangResult dissassembleDXILUsingDXC( { String code = emitHLSLForEntryPoint( compileRequest, - entryPoint, entryPointIndex, targetReq, endToEndReq); @@ -1713,7 +1721,6 @@ SlangResult dissassembleDXILUsingDXC( { String code = emitGLSLForEntryPoint( compileRequest, - entryPoint, entryPointIndex, targetReq, endToEndReq); @@ -1727,7 +1734,7 @@ SlangResult dissassembleDXILUsingDXC( { return emitEntryPoint( compileRequest, - entryPoint, + entryPointIndex, target, targetReq); } diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h index ebde8b6d3..143d3bdaf 100644 --- a/source/slang/slang-compiler.h +++ b/source/slang/slang-compiler.h @@ -307,6 +307,9 @@ namespace Slang /// Get one of the entry points linked into this component type. virtual RefPtr<EntryPoint> getEntryPoint(Index index) = 0; + /// Get the mangled name of one of the entry points linked into this component type. + virtual String getEntryPointMangledName(Index index) = 0; + /// Get the number of global shader parameters linked into this component type. virtual Index getShaderParamCount() = 0; @@ -495,6 +498,7 @@ namespace Slang Index getEntryPointCount() SLANG_OVERRIDE; RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE; + String getEntryPointMangledName(Index index) SLANG_OVERRIDE; Index getShaderParamCount() SLANG_OVERRIDE; GlobalShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE; @@ -543,6 +547,7 @@ namespace Slang // points, parameters, etc. while giving a better overall memory usage. // List<EntryPoint*> m_entryPoints; + List<String> m_entryPointMangledNames; List<GlobalShaderParamInfo> m_shaderParams; List<SpecializationParam> m_specializationParams; List<ComponentType*> m_requirements; @@ -580,6 +585,7 @@ namespace Slang Index getEntryPointCount() SLANG_OVERRIDE { return m_base->getEntryPointCount(); } RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE { return m_base->getEntryPoint(index); } + String getEntryPointMangledName(Index index) SLANG_OVERRIDE; Index getShaderParamCount() SLANG_OVERRIDE { return m_base->getShaderParamCount(); } GlobalShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE { return m_base->getShaderParam(index); } @@ -621,6 +627,8 @@ namespace Slang SpecializationArgs m_specializationArgs; RefPtr<IRModule> m_irModule; + List<String> m_entryPointMangledNames; + // Any tagged union types that were referenced by the specialization arguments. List<RefPtr<TaggedUnionType>> m_taggedUnionTypes; @@ -701,6 +709,7 @@ namespace Slang Index getEntryPointCount() SLANG_OVERRIDE { return 1; }; RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE { SLANG_UNUSED(index); return this; } + String getEntryPointMangledName(Index index) SLANG_OVERRIDE; Index getShaderParamCount() SLANG_OVERRIDE { return 0; } GlobalShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE { SLANG_UNUSED(index); return GlobalShaderParamInfo(); } @@ -861,6 +870,7 @@ namespace Slang Index getEntryPointCount() SLANG_OVERRIDE { return 0; } RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE { SLANG_UNUSED(index); return nullptr; } + String getEntryPointMangledName(Index index) SLANG_OVERRIDE { SLANG_UNUSED(index); return String(); } Index getShaderParamCount() SLANG_OVERRIDE { return m_shaderParams.getCount(); } GlobalShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE { return m_shaderParams[index]; } @@ -1450,6 +1460,7 @@ namespace Slang Index getEntryPointCount() SLANG_OVERRIDE { return 0; } RefPtr<EntryPoint> getEntryPoint(Index index) SLANG_OVERRIDE { SLANG_UNUSED(index); return nullptr; } + String getEntryPointMangledName(Index index) SLANG_OVERRIDE { SLANG_UNUSED(index); return String(); } Index getShaderParamCount() SLANG_OVERRIDE { return m_shaderParams.getCount(); } GlobalShaderParamInfo getShaderParam(Index index) SLANG_OVERRIDE { return m_shaderParams[index]; } @@ -1594,7 +1605,16 @@ namespace Slang BackEndCompileRequest* backEndRequest, EndToEndCompileRequest* endToEndRequest); + RefPtr<IRModule> getOrCreateIRModuleForLayout(DiagnosticSink* sink); + + RefPtr<IRModule> getExistingIRModuleForLayout() + { + return m_irModuleForLayout; + } + private: + RefPtr<IRModule> createIRModuleForLayout(DiagnosticSink* sink); + // The program being compiled or laid out ComponentType* m_program; @@ -1608,6 +1628,8 @@ namespace Slang // in the parent `Program` (indexing matches // the order they are given in the `Program`) List<CompileResult> m_entryPointResults; + + RefPtr<IRModule> m_irModuleForLayout; }; /// A request to generate code for a program diff --git a/source/slang/slang-dxc-support.cpp b/source/slang/slang-dxc-support.cpp index b4bc77fe5..816de5960 100644 --- a/source/slang/slang-dxc-support.cpp +++ b/source/slang/slang-dxc-support.cpp @@ -31,7 +31,6 @@ namespace Slang String GetHLSLProfileName(Profile profile); String emitHLSLForEntryPoint( BackEndCompileRequest* compileRequest, - EntryPoint* entryPoint, Int entryPointIndex, TargetRequest* targetReq, EndToEndCompileRequest* endToEndReq); @@ -95,7 +94,6 @@ namespace Slang // point, since we'll need that to feed into dxc. auto hlslCode = emitHLSLForEntryPoint( compileRequest, - entryPoint, entryPointIndex, targetReq, endToEndReq); diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index 991540782..c89eadfa3 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -127,13 +127,8 @@ CLikeSourceEmitter::CLikeSourceEmitter(const Desc& desc) m_target = desc.target; m_compileRequest = desc.compileRequest; - m_entryPoint = desc.entryPoint; + m_entryPointStage = desc.entryPointStage; m_effectiveProfile = desc.effectiveProfile; - - m_entryPointLayout = desc.entryPointLayout; - - m_programLayout = desc.programLayout; - m_globalStructLayout = desc.globalStructLayout; } // @@ -784,6 +779,12 @@ bool CLikeSourceEmitter::shouldFoldInstIntoUseSites(IRInst* inst) case kIROp_BoolLit: return true; + // Treat these as folded, because they don't make sense to emit on their own. + case kIROp_TypeLayout: + case kIROp_VarLayout: + case kIROp_EntryPointLayout: + return true; + // Always fold these in, because their results // cannot be represented in the type system of // our current targets. @@ -1477,7 +1478,7 @@ void CLikeSourceEmitter::emitTargetIntrinsicCallExpr( // The `$XT` case handles selecting between // the `gl_HitTNV` and `gl_RayTmaxNV` builtins, // based on what stage we are using: - switch( m_entryPoint->getStage() ) + switch( m_entryPointStage ) { default: m_writer->emit("gl_RayTmaxNV"); @@ -2202,7 +2203,7 @@ VarLayout* CLikeSourceEmitter::getVarLayout(IRInst* var) if (!decoration) return nullptr; - return (VarLayout*) decoration->getLayout(); + return (VarLayout*) decoration->getIRLayout()->getASTLayout(); } void CLikeSourceEmitter::emitLayoutSemantics(IRInst* inst, char const* uniformSemanticSpelling) @@ -2686,7 +2687,7 @@ EntryPointLayout* CLikeSourceEmitter::getEntryPointLayout(IRFunc* func) { if( auto layoutDecoration = func->findDecoration<IRLayoutDecoration>() ) { - return as<EntryPointLayout>(layoutDecoration->getLayout()); + return as<EntryPointLayout>(layoutDecoration->getIRLayout()->getASTLayout()); } return nullptr; } @@ -2695,7 +2696,7 @@ EntryPointLayout* CLikeSourceEmitter::asEntryPoint(IRFunc* func) { if (auto layoutDecoration = func->findDecoration<IRLayoutDecoration>()) { - if (auto entryPointLayout = as<EntryPointLayout>(layoutDecoration->getLayout())) + if (auto entryPointLayout = as<EntryPointLayout>(layoutDecoration->getIRLayout()->getASTLayout())) { return entryPointLayout; } diff --git a/source/slang/slang-emit-c-like.h b/source/slang/slang-emit-c-like.h index 1f0a6c818..5aa5cf202 100644 --- a/source/slang/slang-emit-c-like.h +++ b/source/slang/slang-emit-c-like.h @@ -25,23 +25,13 @@ public: BackEndCompileRequest* compileRequest = nullptr; // The target language we want to generate code for CodeGenTarget target = CodeGenTarget::Unknown; - // The entry point we are being asked to compile - EntryPoint* entryPoint = nullptr; + // The stage for the entry point we are being asked to compile + Stage entryPointStage = Stage::Unknown; // The "effective" profile that is being used to emit code, // combining information from the target and entry point. Profile effectiveProfile = Profile::RawEnum::Unknown; SourceWriter* sourceWriter = nullptr; - // The layout for the entry point - EntryPointLayout* entryPointLayout = nullptr; - - ProgramLayout* programLayout = nullptr; - // We track the original global-scope layout so that we can - // find layout information for `import`ed parameters. - // - // TODO: This will probably change if we represent imports - // explicitly in the layout data. - StructTypeLayout* globalStructLayout = nullptr; }; enum @@ -361,11 +351,14 @@ public: BackEndCompileRequest* m_compileRequest = nullptr; - // The entry point we are being asked to compile - EntryPoint* m_entryPoint; - - // The layout for the entry point - EntryPointLayout* m_entryPointLayout; + // The stage for which we are emitting code. + // + // TODO: We should support emitting code that includes multiple + // entry points for different stages, but this value is used + // in some very specific cases to determine how a construct + // should map to GLSL. + // + Stage m_entryPointStage; // The target language we want to generate code for CodeGenTarget m_target; @@ -379,15 +372,6 @@ public: // we maintain a set of already-emitted modules. HashSet<ModuleDecl*> m_modulesAlreadyEmitted; - // We track the original global-scope layout so that we can - // find layout information for `import`ed parameters. - // - // TODO: This will probably change if we represent imports - // explicitly in the layout data. - StructTypeLayout* m_globalStructLayout; - - ProgramLayout* m_programLayout; - ModuleDecl* m_program; GLSLExtensionTracker m_glslExtensionTracker; diff --git a/source/slang/slang-emit-hlsl.cpp b/source/slang/slang-emit-hlsl.cpp index 565f5ae94..0ee51e117 100644 --- a/source/slang/slang-emit-hlsl.cpp +++ b/source/slang/slang-emit-hlsl.cpp @@ -645,7 +645,7 @@ void HLSLSourceEmitter::emitSemanticsImpl(IRInst* inst) if (auto layoutDecoration = inst->findDecoration<IRLayoutDecoration>()) { - auto layout = layoutDecoration->getLayout(); + auto layout = layoutDecoration->getIRLayout()->getASTLayout(); if (auto varLayout = as<VarLayout>(layout)) { emitSemantics(varLayout); diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index 5a4e8300a..f60febe0b 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -158,16 +158,15 @@ static void dumpIRIfEnabled( String emitEntryPoint( BackEndCompileRequest* compileRequest, - EntryPoint* entryPoint, + Int entryPointIndex, 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 entryPoint = program->getEntryPoint(entryPointIndex); auto lineDirectiveMode = compileRequest->getLineDirectiveMode(); // To try to make the default behavior reasonable, we will @@ -187,24 +186,10 @@ String emitEntryPoint( desc.compileRequest = compileRequest; desc.target = target; - desc.entryPoint = entryPoint; + desc.entryPointStage = entryPoint->getStage(); desc.effectiveProfile = getEffectiveProfile(entryPoint, targetRequest); desc.sourceWriter = &sourceWriter; - if (entryPoint && programLayout) - { - desc.entryPointLayout = findEntryPointLayout(programLayout, entryPoint); - } - - desc.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; - desc.globalStructLayout = globalStructLayout; - RefPtr<CLikeSourceEmitter> sourceEmitter; typedef CLikeSourceEmitter::SourceStyle SourceStyle; @@ -251,10 +236,9 @@ String emitEntryPoint( // linkedIR = linkIR( compileRequest, - entryPoint, - programLayout, + entryPointIndex, target, - targetRequest); + targetProgram); auto irModule = linkedIR.module; auto irEntryPoint = linkedIR.entryPoint; diff --git a/source/slang/slang-emit.h b/source/slang/slang-emit.h index c0981a5e4..46131d726 100644 --- a/source/slang/slang-emit.h +++ b/source/slang/slang-emit.h @@ -12,16 +12,32 @@ namespace Slang class ProgramLayout; class TranslationUnitRequest; - // Emit code for a single entry point, based on - // the input translation unit. + /// Emit source code for a single entry point. + /// + /// This function generates source code for the + /// entry point with index `entryPointIndex` + /// inside the program being compiled in `compileRequest`. + /// + /// The code is generated for the language specified + /// as `target` (e.g., HLSL or GLSL). + /// + /// The `targetRequest` gives further information about + /// the compilation target and its options. + /// + /// Note: it is possible that `target` and `targetRequest` + /// do not store the same target format. For example + /// `target` might be HLSL, while `targetRequest` is + /// a DXIL target. The split information here tells us + /// both the immediate target language (HLSL) as well + /// as the eventual destination format (DXIL) in case + /// we need to customize the output (e.g., we might + /// generate different HLSL output if we know it + /// will be used to generate SPIR-V). + /// String emitEntryPoint( BackEndCompileRequest* compileRequest, - EntryPoint* entryPoint, - - // The target language to generate code in (e.g., HLSL/GLSL) + Int entryPointIndex, CodeGenTarget target, - - // The full target request TargetRequest* targetRequest); } #endif diff --git a/source/slang/slang-ir-bind-existentials.cpp b/source/slang/slang-ir-bind-existentials.cpp index a99da3410..ed400ba0a 100644 --- a/source/slang/slang-ir-bind-existentials.cpp +++ b/source/slang/slang-ir-bind-existentials.cpp @@ -196,7 +196,7 @@ struct BindExistentialSlots auto layoutDecoration = param->findDecoration<IRLayoutDecoration>(); if(!layoutDecoration) return; - auto varLayout = as<VarLayout>(layoutDecoration->getLayout()); + auto varLayout = as<VarLayout>(layoutDecoration->getASTLayout()); if(!varLayout) return; diff --git a/source/slang/slang-ir-dce.cpp b/source/slang/slang-ir-dce.cpp index 6dc315c76..4d58947d4 100644 --- a/source/slang/slang-ir-dce.cpp +++ b/source/slang/slang-ir-dce.cpp @@ -222,6 +222,11 @@ struct DeadCodeEliminationContext // if(inst->mightHaveSideEffects()) return true; + + // If it's a layout instruction we don't want to remove it + if (as<IRLayout>(inst)) + return true; + // // The `mightHaveSideEffects` query is conservative, and will // return `true` as its default mode, so once we are past that diff --git a/source/slang/slang-ir-entry-point-uniforms.cpp b/source/slang/slang-ir-entry-point-uniforms.cpp index 9e36c0853..3a6e9e82c 100644 --- a/source/slang/slang-ir-entry-point-uniforms.cpp +++ b/source/slang/slang-ir-entry-point-uniforms.cpp @@ -155,7 +155,7 @@ struct MoveEntryPointUniformParametersToGlobalScope if(!funcLayoutDecoration) return; - auto entryPointLayout = as<EntryPointLayout>(funcLayoutDecoration->getLayout()); + auto entryPointLayout = as<EntryPointLayout>(funcLayoutDecoration->getASTLayout()); SLANG_ASSERT(entryPointLayout); if(!entryPointLayout) return; @@ -208,7 +208,7 @@ struct MoveEntryPointUniformParametersToGlobalScope SLANG_ASSERT(layoutDecoration); if(!layoutDecoration) continue; - auto paramLayout = as<VarLayout>(layoutDecoration->getLayout()); + auto paramLayout = as<VarLayout>(layoutDecoration->getIRLayout()->getASTLayout()); SLANG_ASSERT(paramLayout); if(!paramLayout) continue; diff --git a/source/slang/slang-ir-glsl-legalize.cpp b/source/slang/slang-ir-glsl-legalize.cpp index 6ccff806f..373acb802 100644 --- a/source/slang/slang-ir-glsl-legalize.cpp +++ b/source/slang/slang-ir-glsl-legalize.cpp @@ -1556,7 +1556,7 @@ void legalizeEntryPointForGLSL( auto layoutDecoration = func->findDecoration<IRLayoutDecoration>(); SLANG_ASSERT(layoutDecoration); - auto entryPointLayout = as<EntryPointLayout>(layoutDecoration->getLayout()); + auto entryPointLayout = as<EntryPointLayout>(layoutDecoration->getIRLayout()->getASTLayout()); SLANG_ASSERT(entryPointLayout); @@ -1684,7 +1684,7 @@ void legalizeEntryPointForGLSL( // auto paramLayoutDecoration = pp->findDecoration<IRLayoutDecoration>(); SLANG_ASSERT(paramLayoutDecoration); - auto paramLayout = as<VarLayout>(paramLayoutDecoration->getLayout()); + auto paramLayout = as<VarLayout>(paramLayoutDecoration->getIRLayout()->getASTLayout()); SLANG_ASSERT(paramLayout); legalizeEntryPointParameterForGLSL( diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index 3912a3915..2e896cc93 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -394,7 +394,7 @@ INST(HighLevelDeclDecoration, highLevelDecl, 1, 0) INST(TargetIntrinsicDecoration, targetIntrinsic, 2, 0) INST_RANGE(TargetSpecificDecoration, TargetDecoration, TargetIntrinsicDecoration) INST(GLSLOuterArrayDecoration, glslOuterArray, 1, 0) - INST(SemanticDecoration, semantic, 1, 0) + INST(InterpolationModeDecoration, interpolationMode, 1, 0) INST(NameHintDecoration, nameHint, 1, 0) @@ -456,7 +456,18 @@ INST(HighLevelDeclDecoration, highLevelDecl, 1, 0) INST(ExportDecoration, export, 1, 0) INST_RANGE(LinkageDecoration, ImportDecoration, ExportDecoration) -INST_RANGE(Decoration, HighLevelDeclDecoration, ExportDecoration) + /* Layout decorations that do not use AST */ + INST(StageLayoutDecoration, stageLayoutDecoration, 1, 0) + INST(ResourceInfoLayoutDecoration, resourceInfoLayoutDecoration, 3, 0) + + /* SemanticLayoutDecoration */ + INST(SemanticDecoration, semantic, 2, 0) + INST(SystemSemanticDecoration, systemSemantic, 2, 0) + INST_RANGE(SemanticDecorationBase, SemanticDecoration, SystemSemanticDecoration) + + INST(PendingVarLayoutDecoration, pendingVarLayoutDecoration, 1, 0) + +INST_RANGE(Decoration, HighLevelDeclDecoration, PendingVarLayoutDecoration) // @@ -483,6 +494,12 @@ INST(ExtractTaggedUnionPayload, extractTaggedUnionPayload, 1, 0) INST(BitCast, bitCast, 1, 0) +/* Layout */ + INST(VarLayout, varLayout, 4, 0) + INST(TypeLayout, typeLayout, 1, 0) + INST(EntryPointLayout, EntryPointLayout, 1, 0) +INST_RANGE(Layout, VarLayout, EntryPointLayout) + PSEUDO_INST(Pos) PSEUDO_INST(PreInc) diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index ec327b1a9..96ef40103 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -39,16 +39,6 @@ struct IRHighLevelDeclDecoration : IRDecoration Decl* getDecl() { return (Decl*) getDeclOperand()->getValue(); } }; -// Associates an IR-level decoration with a source layout -struct IRLayoutDecoration : IRDecoration -{ - enum { kOp = kIROp_LayoutDecoration }; - IR_LEAF_ISA(LayoutDecoration) - - IRPtrLit* getLayoutOperand() { return cast<IRPtrLit>(getOperand(0)); } - Layout* getLayout() { return (Layout*) getLayoutOperand()->getValue(); } -}; - enum IRLoopControl { kIRLoopControl_Unroll, @@ -112,28 +102,6 @@ struct IRGLSLOuterArrayDecoration : IRDecoration } }; -// A decoration that marks a field key as having been associated -// with a particular simple semantic (e.g., `COLOR` or `SV_Position`, -// but not a `register` semantic). -// -// This is currently needed so that we can round-trip HLSL `struct` -// types that get used for varying input/output. This is an unfortunate -// case where some amount of "layout" information can't just come -// in via the `TypeLayout` part of things. -// -struct IRSemanticDecoration : IRDecoration -{ - enum { kOp = kIROp_SemanticDecoration }; - IR_LEAF_ISA(SemanticDecoration) - - IRStringLit* getSemanticNameOperand() { return cast<IRStringLit>(getOperand(0)); } - - UnownedStringSlice getSemanticName() - { - return getSemanticNameOperand()->getStringSlice(); - } -}; - enum class IRInterpolationMode { Linear, @@ -400,6 +368,133 @@ struct IRLookupWitnessTable : IRInst IRUse interfaceType; }; +// Layout decorations + +struct IRStageLayoutDecoration : public IRDecoration +{ + enum { kOp = kIROp_StageLayoutDecoration }; + IR_LEAF_ISA(StageLayoutDecoration) + + IRIntLit* getStageInst() { return cast<IRIntLit>(getOperand(0)); } + Stage getStage() { return Stage(GetIntVal(getStageInst())); } +}; + +struct IRSemanticDecorationBase : public IRDecoration +{ + IR_PARENT_ISA(SemanticDecorationBase) + + IRStringLit* getSemanticNameOperand() { return cast<IRStringLit>(getOperand(0)); } + UnownedStringSlice getSemanticName() { return getSemanticNameOperand()->getStringSlice(); } + IRIntLit* getSemanticIndexOperand() { return cast<IRIntLit>(getOperand(1)); } + int getSemanticIndex() { return int(GetIntVal(getSemanticIndexOperand())); } +}; + +// A decoration that marks a field key as having been associated +// with a particular simple semantic (e.g., `COLOR` or `SV_Position`, +// but not a `register` semantic). +// +// This is currently needed so that we can round-trip HLSL `struct` +// types that get used for varying input/output. This is an unfortunate +// case where some amount of "layout" information can't just come +// in via the `TypeLayout` part of things. +// +struct IRSemanticDecoration : IRSemanticDecorationBase +{ + enum { kOp = kIROp_SemanticDecoration }; + IR_LEAF_ISA(SemanticDecoration) +}; + +struct IRSystemSemanticDecoration : IRSemanticDecorationBase +{ + enum { kOp = kIROp_SystemSemanticDecoration }; + IR_LEAF_ISA(SystemSemanticDecoration) +}; + +/// Holds the resource usage. Typically bound to an IRVarLayout. Note that there can be multiple decorations, +/// for each resource kind. That there should at most be one decoration connected to an instruction for a *kind*. +struct IRResourceInfoLayoutDecoration : public IRDecoration +{ + enum { kOp = kIROp_ResourceInfoLayoutDecoration }; + IR_LEAF_ISA(ResourceInfoLayoutDecoration) + + // What kind of register was it? + IRIntLit* getResourceKindInst() { return cast<IRIntLit>(getOperand(0)); } + LayoutResourceKind getResourceKind() { return (LayoutResourceKind)GetIntVal(getResourceKindInst()); } + + // What binding space (HLSL) or set (Vulkan) are we placed in? + IRIntLit* getSpaceInst() { return cast<IRIntLit>(getOperand(1)); } + UInt getSpace() { return UInt(GetIntVal(getSpaceInst())); } + + // What is our starting register in that space? + // + // (In the case of uniform data, this is a byte offset) + IRIntLit* getIndexInst() { return cast<IRIntLit>(getOperand(2)); } + UInt getIndex() { return UInt(GetIntVal(getIndexInst())); } +}; + +// Layout + +struct IRLayout : IRInst +{ + IR_PARENT_ISA(Layout) + + /// TODO(JS): Hold the pointer to the AST for now, whilst process of transitioning + /// Over to using IR layout. + IRPtrLit* getASTLayoutOperand() { return cast<IRPtrLit>(getOperand(0)); } + Layout* getASTLayout() { return (VarLayout*)getASTLayoutOperand()->getValue(); } +}; + +struct IRTypeLayout : IRLayout +{ + IR_LEAF_ISA(TypeLayout); + TypeLayout* getLayout() { return static_cast<TypeLayout*>(getASTLayout()); } +}; + +struct IREntryPointLayout : IRLayout +{ + IR_LEAF_ISA(EntryPointLayout) + EntryPointLayout* getLayout() { return static_cast<EntryPointLayout*>(getASTLayout()); } +}; + +// Associated data can be attached via the following decorations +// * SemanticDecoration/SystemSemanticDecoration for semantics +// * (potentially multiple) ResourceInfoLayoutDecoration +// * StageLayoutDecoration to indicate a specific associated stage +// * PendingVarLayoutDecoration to indicate pending var layout +// The VarLayoutFlag::HasSemantic flag is equivalent to having the SemanticDecoration +struct IRVarLayout : IRLayout +{ + IR_LEAF_ISA(VarLayout) + + /// The name of this variable + IRStringLit* getName() { return cast<IRStringLit>(getOperand(1)); } + /// For now this uses a link back into the AST representation. Will be replaced by IR based type representation + IRTypeLayout* getTypeLayout() { return cast<IRTypeLayout>(getOperand(2)); } + + /// Get/set absolute layout + IRVarLayout* getAbsoluteLayout() { return cast<IRVarLayout>(getOperand(3)); } + void setAbsoluteLayout(IRVarLayout* layout) { getOperands()[3].set(layout); } +}; + +// Associates an IR-level decoration with a source layout +struct IRLayoutDecoration : IRDecoration +{ + enum { kOp = kIROp_LayoutDecoration }; + IR_LEAF_ISA(LayoutDecoration) + + IRLayout* getIRLayout() { return cast<IRLayout>(getOperand(0)); } + + Layout* getASTLayout() + { + IRLayout* irLayout = getIRLayout(); + if (!irLayout) + { + return nullptr; + } + return irLayout->getASTLayout(); + } +}; + // struct IRCall : IRInst @@ -803,6 +898,9 @@ struct SharedIRBuilder Dictionary<IRInstKey, IRInst*> globalValueNumberingMap; Dictionary<IRConstantKey, IRConstant*> constantMap; + + // TODO: We probably shouldn't use this in the long run. + Dictionary<void*, IRLayout*> layoutMap; }; struct IRBuilderSourceLocRAII; @@ -1335,8 +1433,11 @@ struct IRBuilder } void addHighLevelDeclDecoration(IRInst* value, Decl* decl); + void addLayoutDecoration(IRInst* value, Layout* layout); + IRLayout* getLayout(Layout* astLayout); + void addNameHintDecoration(IRInst* value, IRStringLit* name) { addDecoration(value, kIROp_NameHintDecoration, name); @@ -1362,9 +1463,9 @@ struct IRBuilder addDecoration(value, kIROp_LoopControlDecoration, getIntValue(getIntType(), IRIntegerValue(mode))); } - void addSemanticDecoration(IRInst* value, UnownedStringSlice const& text) + void addSemanticDecoration(IRInst* value, UnownedStringSlice const& text, int index = 0) { - addDecoration(value, kIROp_SemanticDecoration, getStringValue(text)); + addDecoration(value, kIROp_SemanticDecoration, getStringValue(text), getIntValue(getIntType(), index)); } void addTargetIntrinsicDecoration(IRInst* value, UnownedStringSlice const& target, UnownedStringSlice const& definition) diff --git a/source/slang/slang-ir-legalize-types.cpp b/source/slang/slang-ir-legalize-types.cpp index 8fab0fd09..f2a3e3d9a 100644 --- a/source/slang/slang-ir-legalize-types.cpp +++ b/source/slang/slang-ir-legalize-types.cpp @@ -1249,7 +1249,7 @@ static LegalVal legalizeInst( RefPtr<VarLayout> findVarLayout(IRInst* value) { if (auto layoutDecoration = value->findDecoration<IRLayoutDecoration>()) - return as<VarLayout>(layoutDecoration->getLayout()); + return as<VarLayout>(layoutDecoration->getIRLayout()->getASTLayout()); return nullptr; } diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp index 56b06a499..f46a0db09 100644 --- a/source/slang/slang-ir-link.cpp +++ b/source/slang/slang-ir-link.cpp @@ -54,10 +54,6 @@ struct IRSharedSpecContext struct IRSpecContextBase { - // A map from the mangled name of a global variable - // to the layout to use for it. - Dictionary<String, VarLayout*> globalVarLayouts; - IRSharedSpecContext* shared; IRSharedSpecContext* getShared() { return shared; } @@ -231,6 +227,7 @@ IRInst* IRSpecContext::maybeCloneValue(IRInst* originalValue) case kIROp_StructKey: case kIROp_GlobalGenericParam: case kIROp_WitnessTable: + case kIROp_TaggedUnionType: return cloneGlobalValue(this, originalValue); case kIROp_BoolLit: @@ -414,6 +411,17 @@ static void cloneExtraDecorations( IRBuilder* builder = &builderStorage; builder->setInsertInto(clonedInst); + // If the `clonedInst` already has any non-decoration + // children, then we want to insert before those, + // to maintain the invariant that decorations always + // precede non-decoration instructions in the list of + // decorations and children. + // + if(auto firstChild = clonedInst->getFirstChild()) + { + builder->setInsertBefore(firstChild); + } + for(auto sym = originalValues.sym; sym; sym = sym->nextWithSameName) { for(auto decoration : sym->irGlobalValue->getDecorations()) @@ -424,6 +432,7 @@ static void cloneExtraDecorations( break; case kIROp_BindExistentialSlotsDecoration: + case kIROp_LayoutDecoration: if(!clonedInst->findDecorationImpl(decoration->op)) { cloneInst(context, builder, decoration); @@ -431,6 +440,14 @@ static void cloneExtraDecorations( break; } } + + // We will also copy over source location information from the alternative + // values, in case any of them has it available. + // + if(sym->irGlobalValue->sourceLoc.isValid() && !clonedInst->sourceLoc.isValid()) + { + clonedInst->sourceLoc = sym->irGlobalValue->sourceLoc; + } } } @@ -472,16 +489,6 @@ IRGlobalParam* cloneGlobalParamImpl( cloneType(context, originalVal->getFullType())); cloneSimpleGlobalValueImpl(context, originalVal, originalValues, clonedVal); - if(auto linkage = originalVal->findDecoration<IRLinkageDecoration>()) - { - auto mangledName = String(linkage->getMangledName()); - VarLayout* layout = nullptr; - if (context->globalVarLayouts.TryGetValue(mangledName, layout)) - { - builder->addLayoutDecoration(clonedVal, layout); - } - } - return clonedVal; } @@ -723,9 +730,63 @@ void cloneFunctionCommon( IRInst* specializeGeneric( IRSpecialize* specializeInst); + /// Copy layout information for an entry-point function to its parameters. + /// + /// When layout information is initially attached to an IR entry point, + /// it may be attached to a declaration that would have no `IRParam`s + /// to represent the entry-point parameters. + /// + /// After linking, we expect an entry point to have a full definition, + /// so it becomes possible to copy per-parameter layout information + /// from the entry-point layout down to the individual parameters, + /// which simplifies subsequent IR steps taht want to look for + /// per-parameter layout information. + /// + /// TODO: This step should probably be hoisted out to be an independent + /// IR pass that runs after linking, rather than being folded into + /// the linking step. + /// +static void maybeCopyLayoutInformationToParameters( + IRFunc* func, + IRBuilder* builder) +{ + auto layoutDecor = func->findDecoration<IRLayoutDecoration>(); + if(!layoutDecor) + return; + + auto entryPointLayout = as<EntryPointLayout>(layoutDecor->getASTLayout()); + if(!entryPointLayout) + return; + + if( auto firstBlock = func->getFirstBlock() ) + { + auto paramsStructLayout = getScopeStructLayout(entryPointLayout); + Index paramLayoutCount = paramsStructLayout->fields.getCount(); + Index paramCounter = 0; + for( auto pp = firstBlock->getFirstParam(); pp; pp = pp->getNextParam() ) + { + Index paramIndex = paramCounter++; + if( paramIndex < paramLayoutCount ) + { + auto paramLayout = paramsStructLayout->fields[paramIndex]; + + auto offsetParamLayout = applyOffsetToVarLayout(paramLayout, entryPointLayout->parametersLayout); + + builder->addLayoutDecoration( + pp, + offsetParamLayout); + } + else + { + SLANG_UNEXPECTED("too many parameters"); + } + } + } +} + IRFunc* specializeIRForEntryPoint( IRSpecContext* context, - EntryPointLayout* entryPointLayout) + String const& mangledName) { // We start by looking up the IR symbol that // matches the mangled name given to the @@ -736,7 +797,6 @@ IRFunc* specializeIRForEntryPoint( // so that the mangled name of the decl-ref is // not the same as the mangled name of the decl. // - auto mangledName = getMangledName(entryPointLayout->getFuncDeclRef()); RefPtr<IRSpecSymbol> sym; if (!context->getSymbols().TryGetValue(mangledName, sym)) { @@ -744,8 +804,14 @@ IRFunc* specializeIRForEntryPoint( return nullptr; } - // TODO: deal with the case where we might - // have multiple (profile-overloaded) versions... + // Note: it is possible that `sym` shows multiple + // definitions, coming from different IR modules that + // were input to the linking process. We don't have + // to do anything special about that here, because + // we can use *any* of the IR values as the starting + // point for cloning, and `cloneGlobalValue` will + // follow the linkage decoration and discover the + // other values on its own. // auto originalVal = sym->irGlobalValue; @@ -788,26 +854,21 @@ IRFunc* specializeIRForEntryPoint( // we don't want to share the definition between // an entry point and an ordinary function anyway. // - clonedVal = specializeGeneric(clonedSpec); - } + auto specializedClone = specializeGeneric(clonedSpec); - // TODO: If there is an existential-related decoration - // on the entry point, we need to transfer it over - // to the specialized function. - if( auto bindExistentialSlots = originalVal->findDecorationImpl(kIROp_BindExistentialSlotsDecoration) ) - { - if( !clonedVal->findDecorationImpl(kIROp_BindExistentialSlotsDecoration) ) - { - IRBuilder builderStorage = *context->builder; - IRBuilder* builder = &builderStorage; - builder->setInsertInto(clonedVal); + // One special case we need to be aware of is that there + // might be decorations attached to the `specialize` + // instruction, which we then want to copy over to the + // result of specialization. + // + cloneExtraDecorations(context, specializedClone, IROriginalValuesForClone(sym)); - auto clonedBind = cloneInst(context, builder, bindExistentialSlots); - clonedBind->moveToStart(); - } + // Now we will move to considering the specialized instruction + // instead of the unspecialized one for all further steps. + // + clonedVal = specializedClone; } - auto clonedFunc = as<IRFunc>(clonedVal); if(!clonedFunc) { @@ -820,42 +881,11 @@ IRFunc* specializeIRForEntryPoint( context->builder->addKeepAliveDecoration(clonedFunc); } - // We need to attach the layout information for - // the entry point to this declaration, so that - // we can use it to inform downstream code emit. - // - context->builder->addLayoutDecoration( - clonedFunc, - entryPointLayout); - // We will also go on and attach layout information // to the function parameters, so that we have it // available directly on the parameters, rather // than having to look it up on the original entry-point layout. - if( auto firstBlock = clonedFunc->getFirstBlock() ) - { - auto paramsStructLayout = getScopeStructLayout(entryPointLayout); - Index paramLayoutCount = paramsStructLayout->fields.getCount(); - Index paramCounter = 0; - for( auto pp = firstBlock->getFirstParam(); pp; pp = pp->getNextParam() ) - { - Index paramIndex = paramCounter++; - if( paramIndex < paramLayoutCount ) - { - auto paramLayout = paramsStructLayout->fields[paramIndex]; - - auto offsetParamLayout = applyOffsetToVarLayout(paramLayout, entryPointLayout->parametersLayout); - - context->builder->addLayoutDecoration( - pp, - offsetParamLayout); - } - else - { - SLANG_UNEXPECTED("too many parameters"); - } - } - } + maybeCopyLayoutInformationToParameters(clonedFunc, context->builder); return clonedFunc; } @@ -1073,6 +1103,7 @@ IRInst* cloneInst( builder->addInst(clonedInst); context->builder = oldBuilder; cloneDecorations(context, clonedInst, originalInst); + cloneExtraDecorations(context, clonedInst, originalValues); return clonedInst; } @@ -1251,7 +1282,6 @@ void initializeSharedSpecContext( struct IRSpecializationState { - ProgramLayout* programLayout; CodeGenTarget target; TargetRequest* targetReq; @@ -1279,11 +1309,12 @@ struct IRSpecializationState LinkedIR linkIR( BackEndCompileRequest* compileRequest, - EntryPoint* entryPoint, - ProgramLayout* programLayout, + Int entryPointIndex, CodeGenTarget target, - TargetRequest* targetReq) + TargetProgram* targetProgram) { + auto targetReq = targetProgram->getTargetReq(); + // TODO: We need to make sure that the program we are being asked // to compile has been "resolved" so that it has no outstanding // unsatisfied requirements. @@ -1291,7 +1322,6 @@ LinkedIR linkIR( IRSpecializationState stateStorage; auto state = &stateStorage; - state->programLayout = programLayout; state->target = target; state->targetReq = targetReq; @@ -1316,34 +1346,18 @@ LinkedIR linkIR( insertGlobalValueSymbols(sharedContext, irModule); }); + // We will also insert the IR global symbols from the IR module + // attached to the `TargetProgram`, since this module is + // responsible for associating layout information to those + // global symbols via decorations. + // + insertGlobalValueSymbols(sharedContext, targetProgram->getExistingIRModuleForLayout()); + auto context = state->getContext(); context->shared = sharedContext; context->builder = &sharedContext->builderStorage; - // Next, we want to optimize lookup for layout information - // associated with global declarations, so that we can - // look things up based on the IR values (using mangled names) - // - // Note: We are scanning over all the key-value pairs for - // entries in the global scope, to account for the fact - // that the "same" shader parameter could be declared in - // multiple translation units, and thus end up with - // multiple mangled names (when the unique translation - // unit name gets involved). - // - auto globalStructLayout = getScopeStructLayout(programLayout); - for(auto entry : globalStructLayout->mapVarToLayout) - { - auto mangledName = getMangledName(entry.Key); - auto globalVarLayout = entry.Value; - context->globalVarLayouts.AddIfNotExists(mangledName, globalVarLayout); - } - - auto entryPointLayout = findEntryPointLayout(programLayout, entryPoint); - - auto offsetEntryPointLayout = entryPointLayout; - context->builder->setInsertInto(context->getModule()->getModuleInst()); // for now, clone all unreferenced witness tables @@ -1362,7 +1376,15 @@ LinkedIR linkIR( // the entry point function itself, and rely on // this step to recursively copy over anything else // it might reference. - auto irEntryPoint = specializeIRForEntryPoint(context, offsetEntryPointLayout); + // + // Note: We query the mangled name of the entry point from + // the `program` instead of the `entryPoint` directly, + // because the `program` will include any specialization + // arguments which might end up affecting the mangled + // entry point name. + // + auto entryPointMangledName = program->getEntryPointMangledName(entryPointIndex); + auto irEntryPoint = specializeIRForEntryPoint(context, entryPointMangledName); // Bindings for global generic parameters are currently represented // as stand-alone global-scope instructions in the IR module for @@ -1388,29 +1410,6 @@ LinkedIR linkIR( } }); - // HACK: we need to ensure that any tagged union types - // in the IR module have layout information copied over to them. - // - // Note that we do this *after* cloning the `bindGlobalGenericParam` - // instructions, since we expected the tagged union type(s) to - // be referenced by them. - // - for( auto taggedUnionTypeLayout : programLayout->taggedUnionTypeLayouts ) - { - auto taggedUnionType = taggedUnionTypeLayout->getType(); - auto mangledName = getMangledTypeName(taggedUnionType); - - RefPtr<IRSpecSymbol> sym; - if(!context->getSymbols().TryGetValue(mangledName, sym)) - continue; - - IRInst* clonedType = findClonedValue(context, sym->irGlobalValue); - if(!clonedType) - continue; - - context->builder->addLayoutDecoration(clonedType, taggedUnionTypeLayout); - } - // TODO: *technically* we should consider the case where // we have global variables with initializers, since // these should get run whether or not the entry point diff --git a/source/slang/slang-ir-link.h b/source/slang/slang-ir-link.h index 4cea3941e..9fc9cb975 100644 --- a/source/slang/slang-ir-link.h +++ b/source/slang/slang-ir-link.h @@ -15,15 +15,14 @@ namespace Slang // Clone the IR values reachable from the given entry point // into the IR module associated with the specialization state. // When multiple definitions of a symbol are found, the one - // that is best specialized for the given `targetReq` will be - // used. + // that is best specialized for the appropriate compilation + // target will be used. // LinkedIR linkIR( BackEndCompileRequest* compileRequest, - EntryPoint* entryPoint, - ProgramLayout* programLayout, + Int entryPointIndex, CodeGenTarget target, - TargetRequest* targetReq); + TargetProgram* targetProgram); // Replace any global constants in the IR module with their // definitions, if possible. diff --git a/source/slang/slang-ir-union.cpp b/source/slang/slang-ir-union.cpp index e39fae262..d456cebee 100644 --- a/source/slang/slang-ir-union.cpp +++ b/source/slang/slang-ir-union.cpp @@ -670,7 +670,7 @@ struct DesugarUnionTypesContext // auto layoutDecoration = type->findDecoration<IRLayoutDecoration>(); SLANG_ASSERT(layoutDecoration); - auto layout = layoutDecoration->getLayout(); + auto layout = layoutDecoration->getIRLayout()->getASTLayout(); SLANG_ASSERT(layout); auto taggedUnionTypeLayout = as<TaggedUnionTypeLayout>(layout); SLANG_ASSERT(taggedUnionTypeLayout); diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index ea6a4afda..78758d944 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -10,6 +10,9 @@ namespace Slang { struct IRSpecContext; + + SLANG_COMPILE_TIME_ASSERT(kIROpCount < kIRPseudoOp_First); + IRInst* cloneGlobalValueWithLinkage( IRSpecContext* context, IRInst* originalVal, @@ -3083,14 +3086,82 @@ namespace Slang addDecoration(inst, kIROp_HighLevelDeclDecoration, ptrConst); } - void IRBuilder::addLayoutDecoration(IRInst* inst, Layout* layout) + void IRBuilder::addLayoutDecoration(IRInst* value, Layout* layout) { - auto ptrConst = getPtrValue(addRefObjectToFree(layout)); - addDecoration(inst, kIROp_LayoutDecoration, ptrConst); + IRLayout* irLayout = getLayout(layout); + addDecoration(value, kIROp_LayoutDecoration, irLayout); + + } - // + IRLayout* IRBuilder::getLayout(Layout* astLayout) + { + if (astLayout == nullptr) + { + return nullptr; + } + + IRLayout* irLayout = nullptr; + if(sharedBuilder->layoutMap.TryGetValue(astLayout, irLayout)) + { + SLANG_ASSERT(irLayout->getASTLayout() == astLayout); + return irLayout; + } + + if (EntryPointLayout* entryPointLayout = as<EntryPointLayout>(astLayout)) + { + irLayout = createInst<IREntryPointLayout>(this, kIROp_EntryPointLayout, nullptr, getPtrValue(astLayout)); + } + else if (VarLayout* varLayout = as<VarLayout>(astLayout)) + { + UnownedStringSlice nameSlice; + if (varLayout->getVariable()) + { + Name* name = varLayout->getName(); + if (name) + { + nameSlice = name->text.getUnownedSlice(); + } + } + + // Get the name as a literal. + // We use an empty length string, as we can't use a null inst ptr. + // If there was a 'null' instruction then it might make more sense to use that + IRStringLit* nameLit = getStringValue(nameSlice); + + // Layout, name, type layout, absolute layout + IRInst* args[4] = { + getPtrValue(astLayout), + nameLit, + getLayout(varLayout->getTypeLayout()), + getLayout(varLayout->m_absoluteLayout) + }; + + irLayout = createInst<IRVarLayout>(this, kIROp_VarLayout, nullptr, 4, args); + } + else if (TypeLayout* typeLayout = as<TypeLayout>(astLayout)) + { + irLayout = createInst<IRTypeLayout>(this, kIROp_TypeLayout, nullptr, getPtrValue(astLayout)); + } + else + { + SLANG_UNEXPECTED("Unknown layout type"); + } + + SLANG_ASSERT(irLayout); + SLANG_ASSERT(irLayout->getASTLayout() == astLayout); + + sharedBuilder->layoutMap[astLayout] = irLayout; + + addGlobalValue(this, irLayout); + // need to keep in scope + addRefObjectToFree(astLayout); + + return irLayout; + } + + // struct IRDumpContext { diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index 50ac6948b..cd398f4e7 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -6707,4 +6707,98 @@ RefPtr<IRModule> generateIRForSpecializedComponentType( return context.process(componentType, sink); } + +RefPtr<IRModule> TargetProgram::getOrCreateIRModuleForLayout(DiagnosticSink* sink) +{ + getOrCreateLayout(sink); + return m_irModuleForLayout; +} + +RefPtr<IRModule> TargetProgram::createIRModuleForLayout(DiagnosticSink* sink) +{ + if(m_irModuleForLayout) + return m_irModuleForLayout; + + + // Okay, now we need to fill it in. + + auto programLayout = getOrCreateLayout(sink); + if(!programLayout) + return nullptr; + + auto program = getProgram(); + auto linkage = program->getLinkage(); + auto session = linkage->getSessionImpl(); + + SharedIRGenContext sharedContextStorage( + session, + sink); + auto sharedContext = &sharedContextStorage; + + IRGenContext contextStorage(sharedContext); + auto context = &contextStorage; + + SharedIRBuilder sharedBuilderStorage; + auto sharedBuilder = &sharedBuilderStorage; + sharedBuilder->module = nullptr; + sharedBuilder->session = session; + + IRBuilder builderStorage; + auto builder = &builderStorage; + builder->sharedBuilder = sharedBuilder; + + RefPtr<IRModule> irModule = builder->createModule(); + sharedBuilder->module = irModule; + + builder->setInsertInto(irModule->getModuleInst()); + + context->irBuilder = builder; + + + // Okay, now we need to walk through and decorate everything. + auto globalStructLayout = getScopeStructLayout(programLayout); + for(auto globalVarPair : globalStructLayout->mapVarToLayout) + { + auto varDecl = globalVarPair.Key; + auto varLayout = globalVarPair.Value; + + // Ensure that an `[import(...)]` declaration for the variable + // has been emitted to this module, so that we will have something + // to decorate. + // + auto irVar = getSimpleVal(context, ensureDecl(context, varDecl)); + + // Now attach the decoration to the variable. + // + builder->addLayoutDecoration(irVar, varLayout); + } + + for( auto entryPointLayout : programLayout->entryPoints ) + { + auto funcDeclRef = entryPointLayout->entryPoint; + + auto irFuncType = lowerType(context, getFuncType(session, funcDeclRef)); + auto irFunc = getSimpleVal(context, emitDeclRef(context, funcDeclRef, irFuncType)); + + if( !irFunc->findDecoration<IRLinkageDecoration>() ) + { + builder->addImportDecoration(irFunc, getMangledName(funcDeclRef).getUnownedSlice()); + } + + builder->addLayoutDecoration(irFunc, entryPointLayout); + } + + for( auto taggedUnionTypeLayout : programLayout->taggedUnionTypeLayouts ) + { + auto taggedUnionType = taggedUnionTypeLayout->getType(); + auto irType = lowerType(context, taggedUnionType); + builder->addLayoutDecoration(irType, taggedUnionTypeLayout); + } + + m_irModuleForLayout = irModule; + return irModule; +} + + + } // namespace Slang diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp index 6e5838da5..a2353824c 100644 --- a/source/slang/slang-parameter-binding.cpp +++ b/source/slang/slang-parameter-binding.cpp @@ -3227,6 +3227,10 @@ ProgramLayout* TargetProgram::getOrCreateLayout(DiagnosticSink* sink) if( !m_layout ) { m_layout = generateParameterBindings(this, sink); + if( m_layout ) + { + m_irModuleForLayout = createIRModuleForLayout(sink); + } } return m_layout; } diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp index ff1f2c147..807d0cab4 100644 --- a/source/slang/slang-type-layout.cpp +++ b/source/slang/slang-type-layout.cpp @@ -2384,7 +2384,7 @@ static RefPtr<TypeLayout> maybeAdjustLayoutForArrayElementType( for( auto p : originalStructTypeLayout->mapVarToLayout ) { - Decl* key = p.Key; + VarDeclBase* key = p.Key; RefPtr<VarLayout> originalVal = p.Value; RefPtr<VarLayout> adjustedVal; if( mapOriginalFieldToAdjusted.TryGetValue(originalVal, adjustedVal) ) diff --git a/source/slang/slang-type-layout.h b/source/slang/slang-type-layout.h index 5e86be113..f63dd01c0 100644 --- a/source/slang/slang-type-layout.h +++ b/source/slang/slang-type-layout.h @@ -15,6 +15,8 @@ namespace Slang { enum class BaseType; class Type; +struct IRLayout; + // #if 0 @@ -601,7 +603,7 @@ public: // TODO: This should map from a declaration to the *index* // in the array above, rather than to the actual pointer, // so that we - Dictionary<Decl*, RefPtr<VarLayout>> mapVarToLayout; + Dictionary<VarDeclBase*, RefPtr<VarLayout>> mapVarToLayout; // As an accellerator for type layouts created at the // IR layer, we include a second map that use IR "key" diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp index 5f22f8a23..4ae3f4654 100644 --- a/source/slang/slang.cpp +++ b/source/slang/slang.cpp @@ -7,6 +7,7 @@ #include "slang-parameter-binding.h" #include "slang-lower-to-ir.h" +#include "slang-mangle.h" #include "slang-parser.h" #include "slang-preprocessor.h" #include "slang-reflection.h" @@ -1136,6 +1137,7 @@ SlangResult FrontEndCompileRequest::executeActionsInner() { auto targetProgram = m_globalAndEntryPointsComponentType->getTargetProgram(targetReq); targetProgram->getOrCreateLayout(getSink()); + targetProgram->getOrCreateIRModuleForLayout(getSink()); } if (getSink()->GetErrorCount() != 0) return SLANG_FAIL; @@ -2033,6 +2035,7 @@ CompositeComponentType::CompositeComponentType( for(Index cc = 0; cc < childEntryPointCount; ++cc) { m_entryPoints.add(child->getEntryPoint(cc)); + m_entryPointMangledNames.add(child->getEntryPointMangledName(cc)); } auto childShaderParamCount = child->getShaderParamCount(); @@ -2079,6 +2082,11 @@ RefPtr<EntryPoint> CompositeComponentType::getEntryPoint(Index index) return m_entryPoints[index]; } +String CompositeComponentType::getEntryPointMangledName(Index index) +{ + return m_entryPointMangledNames[index]; +} + Index CompositeComponentType::getShaderParamCount() { return m_shaderParams.getCount(); @@ -2198,6 +2206,48 @@ SpecializedComponentType::SpecializedComponentType( m_taggedUnionTypes.add(taggedUnionType); } + + // Because we are specializing shader code, the mangled entry + // point names for this component type may be different than + // for the base component type (e.g., the mangled name for `f<int>` + // is different than that that of the generic `f` function + // itself). + // + // We will compute the mangled names of all the entry points and + // store them here, so that we don't have to do it on the fly. + // Because the `ComponentType` structure is hierarchical, we + // need to use a recursive visitor to compute the names, + // and we will define that visitor locally: + // + struct EntryPointMangledNameCollector : ComponentTypeVisitor + { + List<String>* mangledEntryPointNames; + + void visitEntryPoint(EntryPoint* entryPoint, EntryPoint::EntryPointSpecializationInfo* specializationInfo) SLANG_OVERRIDE + { + auto funcDeclRef = entryPoint->getFuncDeclRef(); + if(specializationInfo) + funcDeclRef = specializationInfo->specializedFuncDeclRef; + + (*mangledEntryPointNames).add(getMangledName(funcDeclRef)); + } + + void visitModule(Module*, Module::ModuleSpecializationInfo*) SLANG_OVERRIDE + {} + void visitComposite(CompositeComponentType* composite, CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE + { visitChildren(composite, specializationInfo); } + void visitSpecialized(SpecializedComponentType* specialized) SLANG_OVERRIDE + { visitChildren(specialized); } + void visitLegacy(LegacyProgram* legacy, CompositeComponentType::CompositeSpecializationInfo* specializationInfo) SLANG_OVERRIDE + { visitChildren(legacy, specializationInfo); } + }; + + // With the visitor defined, we apply it to ourself to compute + // and collect the mangled entry point names. + // + EntryPointMangledNameCollector collector; + collector.mangledEntryPointNames = &m_entryPointMangledNames; + collector.visitSpecialized(this); } void SpecializedComponentType::acceptVisitor(ComponentTypeVisitor* visitor, SpecializationInfo* specializationInfo) @@ -2221,6 +2271,11 @@ RefPtr<ComponentType> SpecializedComponentType::getRequirement(Index index) return m_base->getRequirement(index); } +String SpecializedComponentType::getEntryPointMangledName(Index index) +{ + return m_entryPointMangledNames[index]; +} + // // LegacyProgram // |
