diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2019-10-22 14:53:21 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-10-22 14:53:21 -0700 |
| commit | 6a7f4c9cef766e538a808a8f03411af2f10106e1 (patch) | |
| tree | 98fc24c1c8c6ff77b86b35daab6505bbbaf69b0b | |
| parent | 365cd4d206f9a5f6e47843e005a18cf4de5bfdd5 (diff) | |
User IR-based layout for all IR steps (#1084)
This change builds on previous work that moves toward a more IR-based representation of layout.
Those steps added some instructions for representing layout in the IR (initially just proxies for the AST layout objects), and an explicit lowering pass that could build a target-specific IR module that binds parameters and entry points to layout information.
This change aims to complete that work, in the sense that the IR representation of layout is now self-contained and does not rely on having pointers back into the AST-level representation.
Achieving this requires two main kinds of work:
1. Update any code that used layout information derived from the IR (most notably all the `slang-emit-*` code) to use the new IR representation and its accessors.
2. Update any code that *constructs* layouts using information derived from the IR to construct IR layouts instead.
The biggest new infrastructure feature in this change is support for "attributes" in the IR (I'd welcome feedback on the naming).
An attribute can either be thought of like key/value arguments that can be added to certain instructions to encode optional data, or alternatively like a decoration that is referenced as an operand instead of a child.
The value of attributes over decorations is that they can affect the hash/identity of an instruction (which decorations can't), while the advantage of decorations is that they can easily be added/removed over the lifetime of an instruction (which attributes can't).
We mostly use them here to represent operands that are logically optional.
Once attributes are available, the encoding of layout information into the IR is mostly straightforward:
* An `IRVarLayout` has a fixed operand for its type layout, and can accept a few different attributes
* Zero or more `IRVarOffsetAttr`s that specify the offset of the variable for a given resource kind. These are equivalent to the `VarLayout::ResourceInfo`s at the AST level.
* An optional `IRUserSemanticAttr` and `IRSystemValueSemanticAttr` to represent the (possibly derived) semantic of a varying input/output parameter.
* An option `IRStageAttr` to represent the known stage for a parameter.
* An `IREntryPointLayout` has a var layout for the entry point parameters (logically grouped in to a struct) and another var layout for the result parameter.
* There is a small type hierarchy rooted at `IRTypeLayout` where each subtype can add fixed operands and attributes that are expected to appear. It also supports `IRTypeSizeAttr`s that serve a similar role to the `IRVarOffsetAttr`s.
* Structure types maintain the mapping of fields to their var layouts using `IRStructFieldLayoutAttr`s.
With the encoding in place, most of the changes in category (1) (code that just *uses* rather than *creates* layouts) was straightforward. The biggest different beyond name changes was that everything needs to be fetched using accessors instead of bare fields. It would have been possible to stage this commit and make the diffs smaller by first introducing mandatory acessors to the AST layout types.
The changes in category (2) were more involved. There were a lot of places in the existing code where a `TypeLayout` or `VarLayout` would be created, and then initialized piecemeal over several lines of code (and sometimes even across functions). Because of the way that layouts need to support many optional properties, it did not seem practical to just have monolithic factory functions that took all the options as arguments, so I instead opted for a builder approach.
The builders for `IRVarLayout` and `IREntryPointLayout` are both straightforward, and honestly there is no realy need for a builder for entry point layouts right now, but I was trying to future-proof in case we decidd to add some optional attributes to them.
The builders for type layouts are more involved because of the inheritance hierarchy. Each concrete sub-type of type layout needs to define its own builder type that customizes the opcode, operands, and attributes of the final instruction.
The refactoring that had to go into this change was a nice excuse to clean up a few ugly warts in the AST layout code that were largely there to support IR use cases. While this change adds a lot of new infrastructure code to the IR, most of the client code has stayed the same or gotten simpler.
One annoying wart that remains with this change is the notion of an "offset element type layout" for parameter group types. That idea was added to deal with a legacy feature in the reflection API that we realized was a mistake, but unfortunately having that "offset" layout handy made writing a few other pieces of code simpler so that there are use cases of the feature even in the IR. Removing those uses is do-able, but requires careful refactoring so it is best left to a follow-on change.
Another thing that could be considered for a follow-on change is how much information should be specified when constructing a `Builder` for an IR type layout, and how much should be allowed to be specified statefully/piecemeal. It would be nice to force all the required operands to be specified up front, but `IRParameterGroupTypeLayout::Builder` doesn't currently work that way because so much of the client code that needs it involved a lot of stateful setting and would need to be refactored heavily to provide the necessary information up front.
22 files changed, 2163 insertions, 606 deletions
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp index c89eadfa3..a983a7a73 100644 --- a/source/slang/slang-emit-c-like.cpp +++ b/source/slang/slang-emit-c-like.cpp @@ -401,9 +401,9 @@ UInt CLikeSourceEmitter::getBindingOffset(EmitVarChain* chain, LayoutResourceKin UInt offset = 0; for(auto cc = chain; cc; cc = cc->next) { - if(auto resInfo = cc->varLayout->FindResourceInfo(kind)) + if(auto resInfo = cc->varLayout->findOffsetAttr(kind)) { - offset += resInfo->index; + offset += resInfo->getOffset(); } } return offset; @@ -415,13 +415,13 @@ UInt CLikeSourceEmitter::getBindingSpace(EmitVarChain* chain, LayoutResourceKind for(auto cc = chain; cc; cc = cc->next) { auto varLayout = cc->varLayout; - if(auto resInfo = varLayout->FindResourceInfo(kind)) + if(auto resInfo = varLayout->findOffsetAttr(kind)) { - space += resInfo->space; + space += resInfo->getSpace(); } - if(auto resInfo = varLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace)) + if(auto resInfo = varLayout->findOffsetAttr(LayoutResourceKind::RegisterSpace)) { - space += resInfo->index; + space += resInfo->getOffset(); } } return space; @@ -779,12 +779,6 @@ 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. @@ -799,6 +793,15 @@ bool CLikeSourceEmitter::shouldFoldInstIntoUseSites(IRInst* inst) return true; } + // Layouts and attributes are only present to annotate other + // instructions, and should not be emitted as anything in + // source code. + // + if(as<IRLayout>(inst)) + return true; + if(as<IRAttr>(inst)) + return true; + switch( inst->op ) { default: @@ -2179,15 +2182,33 @@ void CLikeSourceEmitter::_emitInst(IRInst* inst) } } -void CLikeSourceEmitter::emitSemantics(VarLayout* varLayout) +void CLikeSourceEmitter::emitSemantics(IRVarLayout* varLayout) { - if(varLayout->flags & VarLayoutFlag::HasSemantic) + if(auto semanticAttr = varLayout->findAttr<IRSemanticAttr>()) { + // Note: We force the semantic name stored in the IR to + // upper-case here because that is what existing Slang + // tests had assumed and continue to rely upon. + // + // The original rationale for switching to uppercase was + // canonicalization for reflection (users can't accidentally + // write code that works for `COLOR` but not for `COLOR`), + // but it would probably be more ideal for our output code + // to give the semantic name as close to how it was originally spelled + // spelled as possible. + // + // TODO: Try removing this step and fixing up the test cases + // to see if we are happier with an approach that doesn't + // force uppercase. + // + String name = semanticAttr->getName(); + name = name.toUpper(); + m_writer->emit(" : "); - m_writer->emit(varLayout->semanticName); - if(varLayout->semanticIndex) + m_writer->emit(name); + if(auto index = semanticAttr->getIndex()) { - m_writer->emit(varLayout->semanticIndex); + m_writer->emit(index); } } } @@ -2197,13 +2218,13 @@ void CLikeSourceEmitter::emitSemantics(IRInst* inst) emitSemanticsImpl(inst); } -VarLayout* CLikeSourceEmitter::getVarLayout(IRInst* var) +IRVarLayout* CLikeSourceEmitter::getVarLayout(IRInst* var) { auto decoration = var->findDecoration<IRLayoutDecoration>(); if (!decoration) return nullptr; - return (VarLayout*) decoration->getIRLayout()->getASTLayout(); + return as<IRVarLayout>(decoration->getLayout()); } void CLikeSourceEmitter::emitLayoutSemantics(IRInst* inst, char const* uniformSemanticSpelling) @@ -2683,20 +2704,20 @@ void CLikeSourceEmitter::emitFuncDecl(IRFunc* func) m_writer->emit(");\n\n"); } -EntryPointLayout* CLikeSourceEmitter::getEntryPointLayout(IRFunc* func) +IREntryPointLayout* CLikeSourceEmitter::getEntryPointLayout(IRFunc* func) { if( auto layoutDecoration = func->findDecoration<IRLayoutDecoration>() ) { - return as<EntryPointLayout>(layoutDecoration->getIRLayout()->getASTLayout()); + return as<IREntryPointLayout>(layoutDecoration->getLayout()); } return nullptr; } -EntryPointLayout* CLikeSourceEmitter::asEntryPoint(IRFunc* func) +IREntryPointLayout* CLikeSourceEmitter::asEntryPoint(IRFunc* func) { if (auto layoutDecoration = func->findDecoration<IRLayoutDecoration>()) { - if (auto entryPointLayout = as<EntryPointLayout>(layoutDecoration->getIRLayout()->getASTLayout())) + if (auto entryPointLayout = as<IREntryPointLayout>(layoutDecoration->getLayout())) { return entryPointLayout; } @@ -2797,7 +2818,7 @@ void CLikeSourceEmitter::emitStruct(IRStructType* structType) m_writer->emit("};\n\n"); } -void CLikeSourceEmitter::emitInterpolationModifiers(IRInst* varInst, IRType* valueType, VarLayout* layout) +void CLikeSourceEmitter::emitInterpolationModifiers(IRInst* varInst, IRType* valueType, IRVarLayout* layout) { emitInterpolationModifiersImpl(varInst, valueType, layout); } @@ -2835,7 +2856,7 @@ void CLikeSourceEmitter::emitTempModifiers(IRInst* temp) } } -void CLikeSourceEmitter::emitVarModifiers(VarLayout* layout, IRInst* varDecl, IRType* varType) +void CLikeSourceEmitter::emitVarModifiers(IRVarLayout* layout, IRInst* varDecl, IRType* varType) { // TODO(JS): We could push all of this onto the target impls, and then not need so many virtual hooks. emitVarDecorationsImpl(varDecl); @@ -2850,8 +2871,8 @@ void CLikeSourceEmitter::emitVarModifiers(VarLayout* layout, IRInst* varDecl, IR // Target specific modifier output emitImageFormatModifierImpl(varDecl, varType); - if(layout->FindResourceInfo(LayoutResourceKind::VaryingInput) - || layout->FindResourceInfo(LayoutResourceKind::VaryingOutput)) + if(layout->usesResourceKind(LayoutResourceKind::VaryingInput) + || layout->usesResourceKind(LayoutResourceKind::VaryingOutput)) { emitInterpolationModifiers(varDecl, varType, layout); } @@ -2990,7 +3011,7 @@ void CLikeSourceEmitter::emitGlobalVar(IRGlobalVar* varDecl) // parameter. // SLANG_ASSERT(!getVarLayout(varDecl)); - VarLayout* layout = nullptr; + IRVarLayout* layout = nullptr; // An ordinary global variable (which is not a // shader parameter) may need special diff --git a/source/slang/slang-emit-c-like.h b/source/slang/slang-emit-c-like.h index 5aa5cf202..7b7ab5da6 100644 --- a/source/slang/slang-emit-c-like.h +++ b/source/slang/slang-emit-c-like.h @@ -77,7 +77,7 @@ public: // A chain of variables to use for emitting semantic/layout info struct EmitVarChain { - VarLayout* varLayout; + IRVarLayout* varLayout; EmitVarChain* next; EmitVarChain() @@ -85,12 +85,12 @@ public: , next(nullptr) {} - EmitVarChain(VarLayout* varLayout) + EmitVarChain(IRVarLayout* varLayout) : varLayout(varLayout) , next(nullptr) {} - EmitVarChain(VarLayout* varLayout, EmitVarChain* next) + EmitVarChain(IRVarLayout* varLayout, EmitVarChain* next) : varLayout(varLayout) , next(next) {} @@ -206,10 +206,17 @@ public: void emitInst(IRInst* inst); - void emitSemantics(VarLayout* varLayout); + // TODO: When this signature switched from `VarLayout` to `IRVarLayout` + // it became possible to get confused beetween this overload and the + // one that takes a base `IRInst`. We should probably be careful and + // rename one of the other of the `emitSemantics()` functions to avoid + // confusion. + // + void emitSemantics(IRVarLayout* varLayout); + void emitSemantics(IRInst* inst); - static VarLayout* getVarLayout(IRInst* var); + static IRVarLayout* getVarLayout(IRInst* var); void emitLayoutSemantics(IRInst* inst, char const* uniformSemanticSpelling = "register"); @@ -244,9 +251,9 @@ public: void emitFuncDecl(IRFunc* func); - EntryPointLayout* getEntryPointLayout(IRFunc* func); + IREntryPointLayout* getEntryPointLayout(IRFunc* func); - EntryPointLayout* asEntryPoint(IRFunc* func); + IREntryPointLayout* asEntryPoint(IRFunc* func); // Detect if the given IR function represents a // declaration of an intrinsic/builtin for the @@ -262,7 +269,7 @@ public: void emitStruct(IRStructType* structType); - void emitInterpolationModifiers(IRInst* varInst, IRType* valueType, VarLayout* layout); + void emitInterpolationModifiers(IRInst* varInst, IRType* valueType, IRVarLayout* layout); UInt getRayPayloadLocation(IRInst* inst); @@ -271,7 +278,7 @@ public: /// Emit modifiers that should apply even for a declaration of an SSA temporary. void emitTempModifiers(IRInst* temp); - void emitVarModifiers(VarLayout* layout, IRInst* varDecl, IRType* varType); + void emitVarModifiers(IRVarLayout* layout, IRInst* varDecl, IRType* varType); /// Emit the array brackets that go on the end of a declaration of the given type. void emitArrayBrackets(IRType* inType); @@ -317,16 +324,16 @@ public: virtual void emitEntryPointAttributesImpl(IRFunc* irFunc, IREntryPointDecoration* entryPointDecor) = 0; virtual void emitImageFormatModifierImpl(IRInst* varDecl, IRType* varType) { SLANG_UNUSED(varDecl); SLANG_UNUSED(varType); } - virtual void emitLayoutQualifiersImpl(VarLayout* layout) { SLANG_UNUSED(layout); } + virtual void emitLayoutQualifiersImpl(IRVarLayout* layout) { SLANG_UNUSED(layout); } virtual void emitPreprocessorDirectivesImpl() {} virtual void emitLayoutDirectivesImpl(TargetRequest* targetReq) { SLANG_UNUSED(targetReq); } virtual void emitRateQualifiersImpl(IRRate* rate) { SLANG_UNUSED(rate); } virtual void emitSemanticsImpl(IRInst* inst) { SLANG_UNUSED(inst); } virtual void emitSimpleFuncParamImpl(IRParam* param); - virtual void emitInterpolationModifiersImpl(IRInst* varInst, IRType* valueType, VarLayout* layout) { SLANG_UNUSED(varInst); SLANG_UNUSED(valueType); SLANG_UNUSED(layout); } + virtual void emitInterpolationModifiersImpl(IRInst* varInst, IRType* valueType, IRVarLayout* layout) { SLANG_UNUSED(varInst); SLANG_UNUSED(valueType); SLANG_UNUSED(layout); } virtual void emitSimpleTypeImpl(IRType* type) = 0; virtual void emitVarDecorationsImpl(IRInst* varDecl) { SLANG_UNUSED(varDecl); } - virtual void emitMatrixLayoutModifiersImpl(VarLayout* layout) { SLANG_UNUSED(layout); } + virtual void emitMatrixLayoutModifiersImpl(IRVarLayout* layout) { SLANG_UNUSED(layout); } virtual void emitTypeImpl(IRType* type, const StringSliceLoc* nameLoc); virtual void emitSimpleValueImpl(IRInst* inst); virtual void emitModuleImpl(IRModule* module); diff --git a/source/slang/slang-emit-cpp.cpp b/source/slang/slang-emit-cpp.cpp index 479359d6b..5ee0bc873 100644 --- a/source/slang/slang-emit-cpp.cpp +++ b/source/slang/slang-emit-cpp.cpp @@ -2389,9 +2389,9 @@ void CPPSourceEmitter::emitOperandImpl(IRInst* inst, EmitOpInfo const& outerPre if (varLayout) { - auto semanticNameSpelling = varLayout->systemValueSemantic; - if (semanticNameSpelling.getLength()) + if(auto systemValueSemantic = varLayout->findSystemValueSemanticAttr()) { + String semanticNameSpelling = systemValueSemantic->getName(); semanticNameSpelling = semanticNameSpelling.toLower(); if (semanticNameSpelling == "sv_dispatchthreadid") @@ -2714,17 +2714,18 @@ void CPPSourceEmitter::emitModuleImpl(IRModule* module) continue; } - VarLayout* varLayout = CLikeSourceEmitter::getVarLayout(action.inst); + IRVarLayout* varLayout = CLikeSourceEmitter::getVarLayout(action.inst); SLANG_ASSERT(varLayout); - const VarLayout::ResourceInfo* varInfo = varLayout->FindResourceInfo(LayoutResourceKind::Uniform); - TypeLayout* typeLayout = varLayout->getTypeLayout(); - TypeLayout::ResourceInfo* typeInfo = typeLayout->FindResourceInfo(LayoutResourceKind::Uniform); + + IRVarOffsetAttr* offsetAttr = varLayout->findOffsetAttr(LayoutResourceKind::Uniform); + IRTypeLayout* typeLayout = varLayout->getTypeLayout(); + IRTypeSizeAttr* sizeAttr = typeLayout->findSizeAttr(LayoutResourceKind::Uniform); GlobalParamInfo paramInfo; paramInfo.inst = action.inst; // Index is the byte offset for uniform - paramInfo.offset = varInfo ? varInfo->index : 0; - paramInfo.size = typeInfo ? typeInfo->count.raw : 0; + paramInfo.offset = offsetAttr ? offsetAttr->getOffset() : 0; + paramInfo.size = sizeAttr ? sizeAttr->getFiniteSize() : 0; params.add(paramInfo); } diff --git a/source/slang/slang-emit-glsl.cpp b/source/slang/slang-emit-glsl.cpp index abaa89e08..4aae8376c 100644 --- a/source/slang/slang-emit-glsl.cpp +++ b/source/slang/slang-emit-glsl.cpp @@ -195,13 +195,13 @@ void GLSLSourceEmitter::_emitGLSLParameterGroup(IRGlobalParam* varDecl, IRUnifor EmitVarChain containerChain = blockChain; EmitVarChain elementChain = blockChain; - auto typeLayout = varLayout->typeLayout->unwrapArray(); - if (auto parameterGroupTypeLayout = as<ParameterGroupTypeLayout>(typeLayout)) + auto typeLayout = varLayout->getTypeLayout()->unwrapArray(); + if (auto parameterGroupTypeLayout = as<IRParameterGroupTypeLayout>(typeLayout)) { - containerChain = EmitVarChain(parameterGroupTypeLayout->containerVarLayout, &blockChain); - elementChain = EmitVarChain(parameterGroupTypeLayout->elementVarLayout, &blockChain); + containerChain = EmitVarChain(parameterGroupTypeLayout->getContainerVarLayout(), &blockChain); + elementChain = EmitVarChain(parameterGroupTypeLayout->getElementVarLayout(), &blockChain); - typeLayout = parameterGroupTypeLayout->elementVarLayout->typeLayout; + typeLayout = parameterGroupTypeLayout->getElementVarLayout()->getTypeLayout(); } /* @@ -432,7 +432,7 @@ bool GLSLSourceEmitter::_emitGLSLLayoutQualifier(LayoutResourceKind kind, EmitVa { if (!chain) return false; - if (!chain->varLayout->FindResourceInfo(kind)) + if (!chain->varLayout->findOffsetAttr(kind)) return false; UInt index = getBindingOffset(chain, kind); @@ -509,7 +509,7 @@ bool GLSLSourceEmitter::_emitGLSLLayoutQualifier(LayoutResourceKind kind, EmitVa return true; } -void GLSLSourceEmitter::_emitGLSLLayoutQualifiers(RefPtr<VarLayout> layout, EmitVarChain* inChain, LayoutResourceKind filter) +void GLSLSourceEmitter::_emitGLSLLayoutQualifiers(IRVarLayout* layout, EmitVarChain* inChain, LayoutResourceKind filter) { if (!layout) return; @@ -524,16 +524,16 @@ void GLSLSourceEmitter::_emitGLSLLayoutQualifiers(RefPtr<VarLayout> layout, Emit EmitVarChain chain(layout, inChain); - for (auto info : layout->resourceInfos) + for (auto info : layout->getOffsetAttrs()) { // Skip info that doesn't match our filter if (filter != LayoutResourceKind::None - && filter != info.kind) + && filter != info->getResourceKind()) { continue; } - _emitGLSLLayoutQualifier(info.kind, &chain); + _emitGLSLLayoutQualifier(info->getResourceKind(), &chain); } } @@ -849,16 +849,16 @@ void GLSLSourceEmitter::emitImageFormatModifierImpl(IRInst* varDecl, IRType* var } } -void GLSLSourceEmitter::emitLayoutQualifiersImpl(VarLayout* layout) +void GLSLSourceEmitter::emitLayoutQualifiersImpl(IRVarLayout* layout) { // Layout-related modifiers need to come before the declaration, // so deal with them here. _emitGLSLLayoutQualifiers(layout, nullptr); // try to emit an appropriate leading qualifier - for (auto rr : layout->resourceInfos) + for (auto rr : layout->getOffsetAttrs()) { - switch (rr.kind) + switch (rr->getResourceKind()) { case LayoutResourceKind::Uniform: case LayoutResourceKind::ShaderResource: @@ -1471,7 +1471,7 @@ static UnownedStringSlice _getInterpolationModifierText(IRInterpolationMode mode } } -void GLSLSourceEmitter::emitInterpolationModifiersImpl(IRInst* varInst, IRType* valueType, VarLayout* layout) +void GLSLSourceEmitter::emitInterpolationModifiersImpl(IRInst* varInst, IRType* valueType, IRVarLayout* layout) { bool anyModifiers = false; @@ -1507,8 +1507,8 @@ void GLSLSourceEmitter::emitInterpolationModifiersImpl(IRInst* varInst, IRType* // output everything with `flat` except for // fragment *outputs* (and maybe vertex inputs). // - if (layout && layout->stage == Stage::Fragment - && layout->FindResourceInfo(LayoutResourceKind::VaryingInput)) + if (layout && layout->getStage() == Stage::Fragment + && layout->usesResourceKind(LayoutResourceKind::VaryingInput)) { _maybeEmitGLSLFlatModifier(valueType); } @@ -1548,24 +1548,22 @@ void GLSLSourceEmitter::emitVarDecorationsImpl(IRInst* varDecl) } } -void GLSLSourceEmitter::emitMatrixLayoutModifiersImpl(VarLayout* layout) +void GLSLSourceEmitter::emitMatrixLayoutModifiersImpl(IRVarLayout* layout) { // When a variable has a matrix type, we want to emit an explicit // layout qualifier based on what the layout has been computed to be. // - auto typeLayout = layout->typeLayout; - while (auto arrayTypeLayout = as<ArrayTypeLayout>(typeLayout)) - typeLayout = arrayTypeLayout->elementTypeLayout; + auto typeLayout = layout->getTypeLayout()->unwrapArray(); - if (auto matrixTypeLayout = typeLayout.as<MatrixTypeLayout>()) + if (auto matrixTypeLayout = as<IRMatrixTypeLayout>(typeLayout)) { // Reminder: the meaning of row/column major layout // in our semantics is the *opposite* of what GLSL // calls them, because what they call "columns" // are what we call "rows." // - switch (matrixTypeLayout->mode) + switch (matrixTypeLayout->getMode()) { case kMatrixLayoutMode_ColumnMajor: m_writer->emit("layout(row_major)\n"); diff --git a/source/slang/slang-emit-glsl.h b/source/slang/slang-emit-glsl.h index e3517d5be..bbb9c132a 100644 --- a/source/slang/slang-emit-glsl.h +++ b/source/slang/slang-emit-glsl.h @@ -22,18 +22,18 @@ protected: virtual void emitParameterGroupImpl(IRGlobalParam* varDecl, IRUniformParameterGroupType* type) SLANG_OVERRIDE; virtual void emitEntryPointAttributesImpl(IRFunc* irFunc, IREntryPointDecoration* entryPointDecor) SLANG_OVERRIDE; virtual void emitImageFormatModifierImpl(IRInst* varDecl, IRType* varType) SLANG_OVERRIDE; - virtual void emitLayoutQualifiersImpl(VarLayout* layout) SLANG_OVERRIDE; + virtual void emitLayoutQualifiersImpl(IRVarLayout* layout) SLANG_OVERRIDE; virtual void emitTextureOrTextureSamplerTypeImpl(IRTextureTypeBase* type, char const* baseName) SLANG_OVERRIDE { _emitGLSLTextureOrTextureSamplerType(type, baseName); } virtual void emitPreprocessorDirectivesImpl() SLANG_OVERRIDE; virtual void emitLayoutDirectivesImpl(TargetRequest* targetReq) SLANG_OVERRIDE; virtual void emitRateQualifiersImpl(IRRate* rate) SLANG_OVERRIDE; - virtual void emitInterpolationModifiersImpl(IRInst* varInst, IRType* valueType, VarLayout* layout) SLANG_OVERRIDE; + virtual void emitInterpolationModifiersImpl(IRInst* varInst, IRType* valueType, IRVarLayout* layout) SLANG_OVERRIDE; virtual void emitSimpleTypeImpl(IRType* type) SLANG_OVERRIDE; virtual void emitVectorTypeNameImpl(IRType* elementType, IRIntegerValue elementCount) SLANG_OVERRIDE; virtual void emitVarDecorationsImpl(IRInst* varDecl) SLANG_OVERRIDE; - virtual void emitMatrixLayoutModifiersImpl(VarLayout* layout) SLANG_OVERRIDE; + virtual void emitMatrixLayoutModifiersImpl(IRVarLayout* layout) SLANG_OVERRIDE; virtual void handleCallExprDecorationsImpl(IRInst* funcValue) SLANG_OVERRIDE; @@ -48,7 +48,7 @@ protected: void _emitGLSLImageFormatModifier(IRInst* var, IRTextureType* resourceType); - void _emitGLSLLayoutQualifiers(RefPtr<VarLayout> layout, EmitVarChain* inChain, LayoutResourceKind filter = LayoutResourceKind::None); + void _emitGLSLLayoutQualifiers(IRVarLayout* layout, EmitVarChain* inChain, LayoutResourceKind filter = LayoutResourceKind::None); bool _emitGLSLLayoutQualifier(LayoutResourceKind kind, EmitVarChain* chain); void _emitGLSLTypePrefix(IRType* type, bool promoteHalfToFloat = false); diff --git a/source/slang/slang-emit-hlsl.cpp b/source/slang/slang-emit-hlsl.cpp index 0ee51e117..9d2334dc3 100644 --- a/source/slang/slang-emit-hlsl.cpp +++ b/source/slang/slang-emit-hlsl.cpp @@ -40,7 +40,7 @@ void HLSLSourceEmitter::_emitHLSLRegisterSemantic(LayoutResourceKind kind, EmitV { if (!chain) return; - if (!chain->varLayout->FindResourceInfo(kind)) + if (!chain->varLayout->usesResourceKind(kind)) return; UInt index = getBindingOffset(chain, kind); @@ -144,13 +144,13 @@ void HLSLSourceEmitter::_emitHLSLRegisterSemantics(EmitVarChain* chain, char con break; } - for (auto rr : layout->resourceInfos) + for (auto rr : layout->getOffsetAttrs()) { - _emitHLSLRegisterSemantic(rr.kind, chain, uniformSemanticSpelling); + _emitHLSLRegisterSemantic(rr->getResourceKind(), chain, uniformSemanticSpelling); } } -void HLSLSourceEmitter::_emitHLSLRegisterSemantics(VarLayout* varLayout, char const* uniformSemanticSpelling) +void HLSLSourceEmitter::_emitHLSLRegisterSemantics(IRVarLayout* varLayout, char const* uniformSemanticSpelling) { if (!varLayout) return; @@ -165,13 +165,13 @@ void HLSLSourceEmitter::_emitHLSLParameterGroupFieldLayoutSemantics(EmitVarChain return; auto layout = chain->varLayout; - for (auto rr : layout->resourceInfos) + for (auto rr : layout->getOffsetAttrs()) { - _emitHLSLRegisterSemantic(rr.kind, chain, "packoffset"); + _emitHLSLRegisterSemantic(rr->getResourceKind(), chain, "packoffset"); } } -void HLSLSourceEmitter::_emitHLSLParameterGroupFieldLayoutSemantics(RefPtr<VarLayout> fieldLayout, EmitVarChain* inChain) +void HLSLSourceEmitter::_emitHLSLParameterGroupFieldLayoutSemantics(IRVarLayout* fieldLayout, EmitVarChain* inChain) { EmitVarChain chain(fieldLayout, inChain); _emitHLSLParameterGroupFieldLayoutSemantics(&chain); @@ -197,13 +197,13 @@ void HLSLSourceEmitter::_emitHLSLParameterGroup(IRGlobalParam* varDecl, IRUnifor EmitVarChain containerChain = blockChain; EmitVarChain elementChain = blockChain; - auto typeLayout = varLayout->typeLayout; - if (auto parameterGroupTypeLayout = as<ParameterGroupTypeLayout>(typeLayout)) + auto typeLayout = varLayout->getTypeLayout(); + if (auto parameterGroupTypeLayout = as<IRParameterGroupTypeLayout>(typeLayout)) { - containerChain = EmitVarChain(parameterGroupTypeLayout->containerVarLayout, &blockChain); - elementChain = EmitVarChain(parameterGroupTypeLayout->elementVarLayout, &blockChain); + containerChain = EmitVarChain(parameterGroupTypeLayout->getContainerVarLayout(), &blockChain); + elementChain = EmitVarChain(parameterGroupTypeLayout->getElementVarLayout(), &blockChain); - typeLayout = parameterGroupTypeLayout->elementVarLayout->typeLayout; + typeLayout = parameterGroupTypeLayout->getElementVarLayout()->getTypeLayout(); } _emitHLSLRegisterSemantic(LayoutResourceKind::ConstantBuffer, &containerChain); @@ -645,14 +645,14 @@ void HLSLSourceEmitter::emitSemanticsImpl(IRInst* inst) if (auto layoutDecoration = inst->findDecoration<IRLayoutDecoration>()) { - auto layout = layoutDecoration->getIRLayout()->getASTLayout(); - if (auto varLayout = as<VarLayout>(layout)) + auto layout = layoutDecoration->getLayout(); + if (auto varLayout = as<IRVarLayout>(layout)) { emitSemantics(varLayout); } - else if (auto entryPointLayout = as<EntryPointLayout>(layout)) + else if (auto entryPointLayout = as<IREntryPointLayout>(layout)) { - if (auto resultLayout = entryPointLayout->resultLayout) + if (auto resultLayout = entryPointLayout->getResultLayout()) { emitSemantics(resultLayout); } @@ -691,7 +691,7 @@ static UnownedStringSlice _getInterpolationModifierText(IRInterpolationMode mode } } -void HLSLSourceEmitter::emitInterpolationModifiersImpl(IRInst* varInst, IRType* valueType, VarLayout* layout) +void HLSLSourceEmitter::emitInterpolationModifiersImpl(IRInst* varInst, IRType* valueType, IRVarLayout* layout) { SLANG_UNUSED(layout); SLANG_UNUSED(valueType); @@ -720,19 +720,17 @@ void HLSLSourceEmitter::emitVarDecorationsImpl(IRInst* varDecl) } } -void HLSLSourceEmitter::emitMatrixLayoutModifiersImpl(VarLayout* layout) +void HLSLSourceEmitter::emitMatrixLayoutModifiersImpl(IRVarLayout* layout) { // When a variable has a matrix type, we want to emit an explicit // layout qualifier based on what the layout has been computed to be. // - auto typeLayout = layout->typeLayout; - while (auto arrayTypeLayout = as<ArrayTypeLayout>(typeLayout)) - typeLayout = arrayTypeLayout->elementTypeLayout; + auto typeLayout = layout->getTypeLayout()->unwrapArray(); - if (auto matrixTypeLayout = typeLayout.as<MatrixTypeLayout>()) + if (auto matrixTypeLayout = as<IRMatrixTypeLayout>(typeLayout)) { - switch (matrixTypeLayout->mode) + switch (matrixTypeLayout->getMode()) { case kMatrixLayoutMode_ColumnMajor: m_writer->emit("column_major "); diff --git a/source/slang/slang-emit-hlsl.h b/source/slang/slang-emit-hlsl.h index b06a24583..2120562e6 100644 --- a/source/slang/slang-emit-hlsl.h +++ b/source/slang/slang-emit-hlsl.h @@ -25,11 +25,11 @@ protected: virtual void emitRateQualifiersImpl(IRRate* rate) SLANG_OVERRIDE; virtual void emitSemanticsImpl(IRInst* inst) SLANG_OVERRIDE; virtual void emitSimpleFuncParamImpl(IRParam* param) SLANG_OVERRIDE; - virtual void emitInterpolationModifiersImpl(IRInst* varInst, IRType* valueType, VarLayout* layout) SLANG_OVERRIDE; + virtual void emitInterpolationModifiersImpl(IRInst* varInst, IRType* valueType, IRVarLayout* layout) SLANG_OVERRIDE; virtual void emitSimpleTypeImpl(IRType* type) SLANG_OVERRIDE; virtual void emitVectorTypeNameImpl(IRType* elementType, IRIntegerValue elementCount) SLANG_OVERRIDE; virtual void emitVarDecorationsImpl(IRInst* varDecl) SLANG_OVERRIDE; - virtual void emitMatrixLayoutModifiersImpl(VarLayout* layout) SLANG_OVERRIDE; + virtual void emitMatrixLayoutModifiersImpl(IRVarLayout* layout) SLANG_OVERRIDE; virtual bool tryEmitInstExprImpl(IRInst* inst, const EmitOpInfo& inOuterPrec) SLANG_OVERRIDE; @@ -39,10 +39,10 @@ protected: // Emit all the `register` semantics that are appropriate for a particular variable layout void _emitHLSLRegisterSemantics(EmitVarChain* chain, char const* uniformSemanticSpelling = "register"); - void _emitHLSLRegisterSemantics(VarLayout* varLayout, char const* uniformSemanticSpelling = "register"); + void _emitHLSLRegisterSemantics(IRVarLayout* varLayout, char const* uniformSemanticSpelling = "register"); void _emitHLSLParameterGroupFieldLayoutSemantics(EmitVarChain* chain); - void _emitHLSLParameterGroupFieldLayoutSemantics(RefPtr<VarLayout> fieldLayout, EmitVarChain* inChain); + void _emitHLSLParameterGroupFieldLayoutSemantics(IRVarLayout* fieldLayout, EmitVarChain* inChain); void _emitHLSLParameterGroup(IRGlobalParam* varDecl, IRUniformParameterGroupType* type); diff --git a/source/slang/slang-ir-bind-existentials.cpp b/source/slang/slang-ir-bind-existentials.cpp index ed400ba0a..651143847 100644 --- a/source/slang/slang-ir-bind-existentials.cpp +++ b/source/slang/slang-ir-bind-existentials.cpp @@ -196,14 +196,14 @@ struct BindExistentialSlots auto layoutDecoration = param->findDecoration<IRLayoutDecoration>(); if(!layoutDecoration) return; - auto varLayout = as<VarLayout>(layoutDecoration->getASTLayout()); + auto varLayout = as<IRVarLayout>(layoutDecoration->getLayout()); if(!varLayout) return; // We only care about parameters that are associated // with one or more existential slots. // - auto resInfo = varLayout->FindResourceInfo(LayoutResourceKind::ExistentialTypeParam); + auto resInfo = varLayout->findOffsetAttr(LayoutResourceKind::ExistentialTypeParam); if(!resInfo) return; @@ -212,8 +212,8 @@ struct BindExistentialSlots // the type to find out the number of slots. // UInt slotCount = 0; - if(auto typeResInfo = varLayout->getTypeLayout()->FindResourceInfo(LayoutResourceKind::ExistentialTypeParam)) - slotCount = UInt(typeResInfo->count.getFiniteValue()); + if(auto typeResInfo = varLayout->getTypeLayout()->findSizeAttr(LayoutResourceKind::ExistentialTypeParam)) + slotCount = UInt(typeResInfo->getFiniteSize()); // At this point we know that the parameter consumes // some number of slots, so it would be an error diff --git a/source/slang/slang-ir-entry-point-uniforms.cpp b/source/slang/slang-ir-entry-point-uniforms.cpp index 3a6e9e82c..ad535b747 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->getASTLayout()); + auto entryPointLayout = as<IREntryPointLayout>(funcLayoutDecoration->getLayout()); SLANG_ASSERT(entryPointLayout); if(!entryPointLayout) return; @@ -167,8 +167,10 @@ struct MoveEntryPointUniformParametersToGlobalScope // If we are in the latter case we will need to make sure to allocate // an explicit IR constant buffer for that wrapper, // - auto entryPointParamsLayout = entryPointLayout->parametersLayout; - bool needConstantBuffer = targetNeedsConstantBuffer && entryPointParamsLayout->typeLayout.is<ParameterGroupTypeLayout>(); + auto entryPointParamsLayout = entryPointLayout->getParamsLayout(); + bool needConstantBuffer = targetNeedsConstantBuffer && as<IRParameterGroupTypeLayout>(entryPointParamsLayout->getTypeLayout()); + + auto entryPointParamsStructLayout = getScopeStructLayout(entryPointLayout); // We will set up an IR builder so that we are ready to generate code. // @@ -196,9 +198,11 @@ struct MoveEntryPointUniformParametersToGlobalScope // us modifying it along the way. // IRParam* nextParam = nullptr; + UInt paramCounter = 0; for( IRParam* param = func->getFirstParam(); param; param = nextParam ) { nextParam = param->getNextParam(); + UInt paramIndex = paramCounter++; // We expect all entry-point parameters to have layout information, // but we will be defensive and skip parameters without the required @@ -208,7 +212,7 @@ struct MoveEntryPointUniformParametersToGlobalScope SLANG_ASSERT(layoutDecoration); if(!layoutDecoration) continue; - auto paramLayout = as<VarLayout>(layoutDecoration->getIRLayout()->getASTLayout()); + auto paramLayout = as<IRVarLayout>(layoutDecoration->getLayout()); SLANG_ASSERT(paramLayout); if(!paramLayout) continue; @@ -268,7 +272,21 @@ struct MoveEntryPointUniformParametersToGlobalScope auto paramType = param->getFullType(); builder->setInsertBefore(paramStructType); - auto paramFieldKey = builder->createStructKey(); + + // We need to know the "key" that should be used for the parameter, + // so we will read it off of the entry-point layout information. + // + // TODO: Maybe we should associate the key to the parameter via + // a decoration to avoid this indirection? + // + // TODO: Alternatively, we should make this pass responsible for + // dealing with the transfer of layout information from the entry + // point to its parameters, rather than baking that behavior into + // the linker. After all, this pass is traversing the same information + // anyway, so it could do the work while it is here... + // + auto paramFieldKey = entryPointParamsStructLayout->getFieldLayoutAttrs()[paramIndex]->getFieldKey(); + auto paramField = builder->createStructField(paramStructType, paramFieldKey, paramType); SLANG_UNUSED(paramField); @@ -280,17 +298,6 @@ struct MoveEntryPointUniformParametersToGlobalScope // param->transferDecorationsTo(paramFieldKey); - // There is a bit of a hacky issue, where downstream passes (notably - // type legalization) require the field keys for `struct` types to - // have mangled names, because those mangled names will be used to - // lookup field layout information inside of the layout information - // for the `struct` type. - // - // TODO: We should fix that design choice in how layout information - // is stored, to avoid the reliance on name strings. - // - builder->addExportDecoration(paramFieldKey, getMangledName(paramLayout->varDecl).getUnownedSlice()); - // At this point we want to eliminate the original entry point // parameter, in favor of the `struct` field we declared. // That required replacing any uses of the parameter with @@ -363,7 +370,7 @@ struct MoveEntryPointUniformParametersToGlobalScope // We need to be able to determine if a parameter is logically // a "varying" parameter based on its layout. // - bool isVaryingParameter(VarLayout* layout) + bool isVaryingParameter(IRVarLayout* layout) { // If *any* of the resources consumed by the parameter // is a varying resource kind (e.g., varying input) then @@ -372,9 +379,9 @@ struct MoveEntryPointUniformParametersToGlobalScope // This is reasonable because there is no way to declare // a parameter that mixes varying and non-varying fields. // - for( auto resInfo : layout->resourceInfos ) + for( auto resInfo : layout->getOffsetAttrs() ) { - if(isVaryingResourceKind(resInfo.kind)) + if(isVaryingResourceKind(resInfo->getResourceKind())) return true; } @@ -388,7 +395,7 @@ struct MoveEntryPointUniformParametersToGlobalScope // Note: an empty `struct` parameter would also show up the same way, but // we should eliminate any such parameters later on during type legalization. // - if(layout->resourceInfos.getCount() == 0) + if(layout->getOffsetAttrs().getCount() == 0) return true; // if none of the above tests determined that the diff --git a/source/slang/slang-ir-glsl-legalize.cpp b/source/slang/slang-ir-glsl-legalize.cpp index 373acb802..b70d37e80 100644 --- a/source/slang/slang-ir-glsl-legalize.cpp +++ b/source/slang/slang-ir-glsl-legalize.cpp @@ -209,7 +209,7 @@ struct GLSLLegalizationContext GLSLSystemValueInfo* getGLSLSystemValueInfo( GLSLLegalizationContext* context, - VarLayout* varLayout, + IRVarLayout* varLayout, LayoutResourceKind kind, Stage stage, GLSLSystemValueInfo* inStorage) @@ -217,10 +217,11 @@ GLSLSystemValueInfo* getGLSLSystemValueInfo( char const* name = nullptr; char const* outerArrayName = nullptr; - auto semanticNameSpelling = varLayout->systemValueSemantic; - if(semanticNameSpelling.getLength() == 0) + auto semanticInst = varLayout->findSystemValueSemanticAttr(); + if(!semanticInst) return nullptr; + String semanticNameSpelling = semanticInst->getName(); auto semanticName = semanticNameSpelling.toLower(); // HLSL semantic types can be found here @@ -543,7 +544,7 @@ GLSLSystemValueInfo* getGLSLSystemValueInfo( return inStorage; } - context->getSink()->diagnose(varLayout->varDecl.getDecl()->loc, Diagnostics::unknownSystemValueSemantic, semanticNameSpelling); + context->getSink()->diagnose(varLayout->sourceLoc, Diagnostics::unknownSystemValueSemantic, semanticNameSpelling); return nullptr; } @@ -551,8 +552,8 @@ ScalarizedVal createSimpleGLSLGlobalVarying( GLSLLegalizationContext* context, IRBuilder* builder, IRType* inType, - VarLayout* inVarLayout, - TypeLayout* inTypeLayout, + IRVarLayout* inVarLayout, + IRTypeLayout* inTypeLayout, LayoutResourceKind kind, Stage stage, UInt bindingIndex, @@ -578,7 +579,7 @@ ScalarizedVal createSimpleGLSLGlobalVarying( // Construct the actual type and type-layout for the global variable // - RefPtr<TypeLayout> typeLayout = inTypeLayout; + IRTypeLayout* typeLayout = inTypeLayout; for( auto dd = declarator; dd; dd = dd->next ) { // We only have one declarator case right now... @@ -588,23 +589,18 @@ ScalarizedVal createSimpleGLSLGlobalVarying( type, dd->elementCount); - RefPtr<ArrayTypeLayout> arrayTypeLayout = new ArrayTypeLayout(); -// arrayTypeLayout->type = arrayType; - arrayTypeLayout->rules = typeLayout->rules; - arrayTypeLayout->originalElementTypeLayout = typeLayout; - arrayTypeLayout->elementTypeLayout = typeLayout; - arrayTypeLayout->uniformStride = 0; - - if( auto resInfo = inTypeLayout->FindResourceInfo(kind) ) + IRArrayTypeLayout::Builder arrayTypeLayoutBuilder(builder, typeLayout); + if( auto resInfo = inTypeLayout->findSizeAttr(kind) ) { // TODO: it is kind of gross to be re-running some // of the type layout logic here. UInt elementCount = (UInt) GetIntVal(dd->elementCount); - arrayTypeLayout->addResourceUsage( + arrayTypeLayoutBuilder.addResourceUsage( kind, - resInfo->count * elementCount); + resInfo->getSize() * elementCount); } + auto arrayTypeLayout = arrayTypeLayoutBuilder.build(); type = arrayType; typeLayout = arrayTypeLayout; @@ -614,16 +610,11 @@ ScalarizedVal createSimpleGLSLGlobalVarying( // if the original had its own layout, because it might be // an `inout` parameter, and we only want to deal with the case // described by our `kind` parameter. - RefPtr<VarLayout> varLayout = new VarLayout(); - varLayout->varDecl = inVarLayout->varDecl; - varLayout->typeLayout = typeLayout; - varLayout->flags = inVarLayout->flags; - varLayout->systemValueSemantic = inVarLayout->systemValueSemantic; - varLayout->systemValueSemanticIndex = inVarLayout->systemValueSemanticIndex; - varLayout->semanticName = inVarLayout->semanticName; - varLayout->semanticIndex = inVarLayout->semanticIndex; - varLayout->stage = inVarLayout->stage; - varLayout->AddResourceInfo(kind)->index = bindingIndex; + // + IRVarLayout::Builder varLayoutBuilder(builder, typeLayout); + varLayoutBuilder.cloneEverythingButOffsetsFrom(inVarLayout); + varLayoutBuilder.findOrAddResourceInfo(kind)->offset = bindingIndex; + IRVarLayout* varLayout = varLayoutBuilder.build(); // We are going to be creating a global parameter to replace // the function parameter, but we need to handle the case @@ -678,8 +669,8 @@ ScalarizedVal createGLSLGlobalVaryingsImpl( GLSLLegalizationContext* context, IRBuilder* builder, IRType* type, - VarLayout* varLayout, - TypeLayout* typeLayout, + IRVarLayout* varLayout, + IRTypeLayout* typeLayout, LayoutResourceKind kind, Stage stage, UInt bindingIndex, @@ -714,9 +705,9 @@ ScalarizedVal createGLSLGlobalVaryingsImpl( auto elementType = arrayType->getElementType(); auto elementCount = arrayType->getElementCount(); - auto arrayLayout = as<ArrayTypeLayout>(typeLayout); + auto arrayLayout = as<IRArrayTypeLayout>(typeLayout); SLANG_ASSERT(arrayLayout); - auto elementTypeLayout = arrayLayout->elementTypeLayout; + auto elementTypeLayout = arrayLayout->getElementTypeLayout(); GlobalVaryingDeclarator arrayDeclarator; arrayDeclarator.flavor = GlobalVaryingDeclarator::Flavor::array; @@ -737,9 +728,9 @@ ScalarizedVal createGLSLGlobalVaryingsImpl( else if( auto streamType = as<IRHLSLStreamOutputType>(type)) { auto elementType = streamType->getElementType(); - auto streamLayout = as<StreamOutputTypeLayout>(typeLayout); + auto streamLayout = as<IRStreamOutputTypeLayout>(typeLayout); SLANG_ASSERT(streamLayout); - auto elementTypeLayout = streamLayout->elementTypeLayout; + auto elementTypeLayout = streamLayout->getElementTypeLayout(); return createGLSLGlobalVaryingsImpl( context, @@ -757,7 +748,7 @@ ScalarizedVal createGLSLGlobalVaryingsImpl( // We need to recurse down into the individual fields, // and generate a variable for each of them. - auto structTypeLayout = as<StructTypeLayout>(typeLayout); + auto structTypeLayout = as<IRStructTypeLayout>(typeLayout); SLANG_ASSERT(structTypeLayout); RefPtr<ScalarizedTupleValImpl> tupleValImpl = new ScalarizedTupleValImpl(); @@ -781,18 +772,18 @@ ScalarizedVal createGLSLGlobalVaryingsImpl( { UInt fieldIndex = fieldCounter++; - auto fieldLayout = structTypeLayout->fields[fieldIndex]; + auto fieldLayout = structTypeLayout->getFieldLayout(fieldIndex); UInt fieldBindingIndex = bindingIndex; - if(auto fieldResInfo = fieldLayout->FindResourceInfo(kind)) - fieldBindingIndex += fieldResInfo->index; + if(auto fieldResInfo = fieldLayout->findOffsetAttr(kind)) + fieldBindingIndex += fieldResInfo->getOffset(); auto fieldVal = createGLSLGlobalVaryingsImpl( context, builder, field->getFieldType(), fieldLayout, - fieldLayout->typeLayout, + fieldLayout->getTypeLayout(), kind, stage, fieldBindingIndex, @@ -820,16 +811,16 @@ ScalarizedVal createGLSLGlobalVaryings( GLSLLegalizationContext* context, IRBuilder* builder, IRType* type, - VarLayout* layout, + IRVarLayout* layout, LayoutResourceKind kind, Stage stage) { UInt bindingIndex = 0; - if(auto rr = layout->FindResourceInfo(kind)) - bindingIndex = rr->index; + if(auto rr = layout->findOffsetAttr(kind)) + bindingIndex = rr->getOffset(); return createGLSLGlobalVaryingsImpl( context, - builder, type, layout, layout->typeLayout, kind, stage, bindingIndex, nullptr); + builder, type, layout, layout->getTypeLayout(), kind, stage, bindingIndex, nullptr); } ScalarizedVal extractField( @@ -1189,7 +1180,7 @@ void legalizeRayTracingEntryPointParameterForGLSL( GLSLLegalizationContext* context, IRFunc* func, IRParam* pp, - VarLayout* paramLayout) + IRVarLayout* paramLayout) { auto builder = context->getBuilder(); auto paramType = pp->getDataType(); @@ -1241,7 +1232,7 @@ void legalizeEntryPointParameterForGLSL( GLSLLegalizationContext* context, IRFunc* func, IRParam* pp, - VarLayout* paramLayout) + IRVarLayout* paramLayout) { auto builder = context->getBuilder(); auto stage = context->getStage(); @@ -1553,22 +1544,25 @@ void legalizeEntryPointForGLSL( DiagnosticSink* sink, GLSLExtensionTracker* glslExtensionTracker) { + auto entryPointDecor = func->findDecoration<IREntryPointDecoration>(); + SLANG_ASSERT(entryPointDecor); + + auto stage = entryPointDecor->getProfile().GetStage(); + auto layoutDecoration = func->findDecoration<IRLayoutDecoration>(); SLANG_ASSERT(layoutDecoration); - auto entryPointLayout = as<EntryPointLayout>(layoutDecoration->getIRLayout()->getASTLayout()); + auto entryPointLayout = as<IREntryPointLayout>(layoutDecoration->getLayout()); SLANG_ASSERT(entryPointLayout); GLSLLegalizationContext context; context.session = session; - context.stage = entryPointLayout->profile.GetStage(); + context.stage = stage; context.sink = sink; context.glslExtensionTracker = glslExtensionTracker; - Stage stage = entryPointLayout->profile.GetStage(); - // We require that the entry-point function has no uses, // because otherwise we'd invalidate the signature // at all existing call sites. @@ -1631,7 +1625,7 @@ void legalizeEntryPointForGLSL( &context, &builder, resultType, - entryPointLayout->resultLayout, + entryPointLayout->getResultLayout(), LayoutResourceKind::VaryingOutput, stage); @@ -1684,7 +1678,7 @@ void legalizeEntryPointForGLSL( // auto paramLayoutDecoration = pp->findDecoration<IRLayoutDecoration>(); SLANG_ASSERT(paramLayoutDecoration); - auto paramLayout = as<VarLayout>(paramLayoutDecoration->getIRLayout()->getASTLayout()); + auto paramLayout = as<IRVarLayout>(paramLayoutDecoration->getLayout()); SLANG_ASSERT(paramLayout); legalizeEntryPointParameterForGLSL( diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h index 2e896cc93..00ac21e70 100644 --- a/source/slang/slang-ir-inst-defs.h +++ b/source/slang/slang-ir-inst-defs.h @@ -456,18 +456,9 @@ INST(HighLevelDeclDecoration, highLevelDecl, 1, 0) INST(ExportDecoration, export, 1, 0) INST_RANGE(LinkageDecoration, ImportDecoration, ExportDecoration) - /* Layout decorations that do not use AST */ - INST(StageLayoutDecoration, stageLayoutDecoration, 1, 0) - INST(ResourceInfoLayoutDecoration, resourceInfoLayoutDecoration, 3, 0) + INST(SemanticDecoration, semantic, 2, 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) + INST_RANGE(Decoration, HighLevelDeclDecoration, SemanticDecoration) // @@ -495,11 +486,36 @@ INST(ExtractTaggedUnionPayload, extractTaggedUnionPayload, 1, 0) INST(BitCast, bitCast, 1, 0) /* Layout */ - INST(VarLayout, varLayout, 4, 0) - INST(TypeLayout, typeLayout, 1, 0) + INST(VarLayout, varLayout, 1, 0) + + /* TypeLayout */ + INST(TypeLayoutBase, typeLayout, 0, 0) + INST(ParameterGroupTypeLayout, parameterGroupTypeLayout, 2, 0) + INST(ArrayTypeLayout, arrayTypeLayout, 1, 0) + INST(StreamOutputTypeLayout, streamOutputTypeLayout, 1, 0) + INST(MatrixTypeLayout, matrixTypeLayout, 1, 0) + INST(TaggedUnionTypeLayout, taggedUnionTypeLayout, 0, 0) + INST(StructTypeLayout, structTypeLayout, 0, 0) + INST_RANGE(TypeLayout, TypeLayoutBase, StructTypeLayout) + INST(EntryPointLayout, EntryPointLayout, 1, 0) INST_RANGE(Layout, VarLayout, EntryPointLayout) +/* Attr */ + INST(PendingLayoutAttr, pendingLayout, 1, 0) + INST(StageAttr, stage, 1, 0) + INST(StructFieldLayoutAttr, fieldLayout, 2, 0) + INST(CaseTypeLayoutAttr, caseLayout, 1, 0) + /* SemanticAttr */ + INST(UserSemanticAttr, userSemantic, 2, 0) + INST(SystemValueSemanticAttr, systemValueSemantic, 2, 0) + INST_RANGE(SemanticAttr, UserSemanticAttr, SystemValueSemanticAttr) + /* LayoutResourceInfoAttr */ + INST(TypeSizeAttr, size, 2, 0) + INST(VarOffsetAttr, offset, 2, 0) + INST_RANGE(LayoutResourceInfoAttr, TypeSizeAttr, VarOffsetAttr) +INST_RANGE(Attr, PendingLayoutAttr, VarOffsetAttr) + PSEUDO_INST(Pos) PSEUDO_INST(PreInc) diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h index 96ef40103..b6a3c3e93 100644 --- a/source/slang/slang-ir-insts.h +++ b/source/slang/slang-ir-insts.h @@ -370,129 +370,688 @@ struct IRLookupWitnessTable : IRInst // Layout decorations -struct IRStageLayoutDecoration : public 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 : 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) + IR_LEAF_ISA(SemanticDecoration) 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 + /// An attribute that can be attached to another instruction as an operand. + /// + /// Attributes serve a similar role to decorations, in that both are ways + /// to attach additional information to an instruction, where the operand + /// of the attribute/decoration identifies the purpose of the additional + /// information. + /// + /// The key difference between decorations and attributes is that decorations + /// are stored as children of an instruction (in terms of the ownership + /// hierarchy), while attributes are referenced as operands. + /// + /// The key benefit of having attributes be operands is that they must + /// be present at the time an instruction is created, which means that + /// they can affect the conceptual value/identity of an instruction + /// in cases where we deduplicate/hash instructions by value. + /// +struct IRAttr : public IRInst { - enum { kOp = kIROp_SemanticDecoration }; - IR_LEAF_ISA(SemanticDecoration) + IR_PARENT_ISA(Attr); }; -struct IRSystemSemanticDecoration : IRSemanticDecorationBase + /// An attribute that specifies layout information for a single resource kind. +struct IRLayoutResourceInfoAttr : public IRAttr { - enum { kOp = kIROp_SystemSemanticDecoration }; - IR_LEAF_ISA(SystemSemanticDecoration) + IR_PARENT_ISA(LayoutResourceInfoAttr); + + IRIntLit* getResourceKindInst() { return cast<IRIntLit>(getOperand(0)); } + LayoutResourceKind getResourceKind() { return LayoutResourceKind(GetIntVal(getResourceKindInst())); } }; -/// 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 + /// An attribute that specifies offset information for a single resource kind. + /// + /// This operation can appear as `varOffset(kind, offset)` or + /// `varOffset(kind, offset, space)`. The latter form is only + /// used when `space` is non-zero. + /// +struct IRVarOffsetAttr : public IRLayoutResourceInfoAttr { - enum { kOp = kIROp_ResourceInfoLayoutDecoration }; - IR_LEAF_ISA(ResourceInfoLayoutDecoration) + IR_LEAF_ISA(VarOffsetAttr); - // What kind of register was it? - IRIntLit* getResourceKindInst() { return cast<IRIntLit>(getOperand(0)); } - LayoutResourceKind getResourceKind() { return (LayoutResourceKind)GetIntVal(getResourceKindInst()); } + IRIntLit* getOffsetInst() { return cast<IRIntLit>(getOperand(1)); } + UInt getOffset() { return UInt(GetIntVal(getOffsetInst())); } - // What binding space (HLSL) or set (Vulkan) are we placed in? - IRIntLit* getSpaceInst() { return cast<IRIntLit>(getOperand(1)); } - UInt getSpace() { return UInt(GetIntVal(getSpaceInst())); } + IRIntLit* getSpaceInst() + { + if(getOperandCount() > 2) + return cast<IRIntLit>(getOperand(2)); + return nullptr; + } - // 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())); } + UInt getSpace() + { + if(auto spaceInst = getSpaceInst()) + return UInt(GetIntVal(spaceInst)); + return 0; + } +}; + + /// An attribute that specifies size information for a single resource kind. +struct IRTypeSizeAttr : public IRLayoutResourceInfoAttr +{ + IR_LEAF_ISA(TypeSizeAttr); + + IRIntLit* getSizeInst() { return cast<IRIntLit>(getOperand(1)); } + LayoutSize getSize() { return LayoutSize::fromRaw(LayoutSize::RawValue(GetIntVal(getSizeInst()))); } + size_t getFiniteSize() { return getSize().getFiniteValue(); } }; // Layout + /// Base type for instructions that represent layout information. + /// + /// Layout instructions are effectively just meta-data constants. + /// struct IRLayout : IRInst { IR_PARENT_ISA(Layout) +}; + +struct IRVarLayout; + + /// An attribute to specify that a layout has another layout attached for "pending" data. + /// + /// "Pending" data refers to the parts of a type or variable that + /// couldn't be laid out until the concrete types for existential + /// type slots were filled in. The layout of pending data may not + /// be contiguous with the layout of the original type/variable. + /// +struct IRPendingLayoutAttr : IRAttr +{ + IR_LEAF_ISA(PendingLayoutAttr); - /// 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(); } + IRLayout* getLayout() { return cast<IRLayout>(getOperand(0)); } }; + /// Layout information for a type. + /// + /// The most important thing this instruction provides is the + /// resource usage (aka "size") of the type for each of the + /// resource kinds it consumes. + /// + /// Subtypes of `IRTypeLayout` will include additional type-specific + /// operands or attributes. For example, a type layout for a + /// `struct` type will include offset information for its fields. + /// struct IRTypeLayout : IRLayout { - IR_LEAF_ISA(TypeLayout); - TypeLayout* getLayout() { return static_cast<TypeLayout*>(getASTLayout()); } + IR_PARENT_ISA(TypeLayout); + + /// Find the attribute that stores offset information for `kind`. + /// + /// Returns null if no attribute is found, indicating that this + /// type does not consume any resources of `kind`. + /// + IRTypeSizeAttr* findSizeAttr(LayoutResourceKind kind); + + /// Get all the attributes representing size information. + IROperandList<IRTypeSizeAttr> getSizeAttrs(); + + /// Unwrap any layers of array-ness and return the outer-most non-array type. + IRTypeLayout* unwrapArray(); + + /// Get the layout for pending data, if present. + IRTypeLayout* getPendingDataTypeLayout(); + + /// A builder for constructing `IRTypeLayout`s + struct Builder + { + /// Begin building. + /// + /// The `irBuilder` will be used to construct the + /// type layout and any additional instructions required. + /// + Builder(IRBuilder* irBuilder); + + /// Add `size` units of resource `kind` to the resource usage of this type. + void addResourceUsage( + LayoutResourceKind kind, + LayoutSize size); + + /// Add the resource usage specified by `sizeAttr`. + void addResourceUsage(IRTypeSizeAttr* sizeAttr); + + /// Add all resource usage from `typeLayout`. + void addResourceUsageFrom(IRTypeLayout* typeLayout); + + /// Set the (optional) layout for pending data. + void setPendingTypeLayout( + IRTypeLayout* typeLayout) + { + m_pendingTypeLayout = typeLayout; + } + + /// Build a type layout according to the information specified so far. + IRTypeLayout* build(); + + protected: + // The following services are provided so that + // subtypes of `IRTypeLayout` can provide their + // own `Builder` subtypes that construct appropriate + // layouts. + + /// Override to customize the opcode of the generated layout. + virtual IROp getOp() { return kIROp_TypeLayoutBase; } + + /// Override to add additional operands to the generated layout. + virtual void addOperandsImpl(List<IRInst*>&) {} + + /// Override to add additional attributes to the generated layout. + virtual void addAttrsImpl(List<IRInst*>&) {} + + /// Use to access the underlying IR builder. + IRBuilder* getIRBuilder() { return m_irBuilder; }; + + private: + void addOperands(List<IRInst*>&); + void addAttrs(List<IRInst*>& ioOperands); + + IRBuilder* m_irBuilder = nullptr; + IRTypeLayout* m_pendingTypeLayout = nullptr; + + struct ResInfo + { + LayoutResourceKind kind = LayoutResourceKind::None; + LayoutSize size = 0; + }; + ResInfo m_resInfos[SLANG_PARAMETER_CATEGORY_COUNT]; + }; }; + /// Type layout for parameter groups (constant buffers and parameter blocks) +struct IRParameterGroupTypeLayout : IRTypeLayout +{ +private: + typedef IRTypeLayout Super; + +public: + IR_LEAF_ISA(ParameterGroupTypeLayout) + + IRVarLayout* getContainerVarLayout() + { + return cast<IRVarLayout>(getOperand(0)); + } + + IRVarLayout* getElementVarLayout() + { + return cast<IRVarLayout>(getOperand(1)); + } + + // TODO: There shouldn't be a need for the IR to store an "offset" element type layout, + // but there are just enough places that currently use that information so that removing + // it would require some careful refactoring. + // + IRTypeLayout* getOffsetElementTypeLayout() + { + return cast<IRTypeLayout>(getOperand(2)); + } + + /// Specialized builder for parameter group type layouts. + struct Builder : Super::Builder + { + public: + Builder(IRBuilder* irBuilder) + : Super::Builder(irBuilder) + {} + + void setContainerVarLayout(IRVarLayout* varLayout) + { + m_containerVarLayout = varLayout; + } + + void setElementVarLayout(IRVarLayout* varLayout) + { + m_elementVarLayout = varLayout; + } + + void setOffsetElementTypeLayout(IRTypeLayout* typeLayout) + { + m_offsetElementTypeLayout = typeLayout; + } + + IRParameterGroupTypeLayout* build(); + + protected: + IROp getOp() SLANG_OVERRIDE { return kIROp_ParameterGroupTypeLayout; } + void addOperandsImpl(List<IRInst*>& ioOperands) SLANG_OVERRIDE; + + IRVarLayout* m_containerVarLayout; + IRVarLayout* m_elementVarLayout; + IRTypeLayout* m_offsetElementTypeLayout; + }; +}; + + /// Specialized layout information for array types +struct IRArrayTypeLayout : IRTypeLayout +{ + typedef IRTypeLayout Super; + + IR_LEAF_ISA(ArrayTypeLayout) + + IRTypeLayout* getElementTypeLayout() + { + return cast<IRTypeLayout>(getOperand(0)); + } + + struct Builder : Super::Builder + { + Builder(IRBuilder* irBuilder, IRTypeLayout* elementTypeLayout) + : Super::Builder(irBuilder) + , m_elementTypeLayout(elementTypeLayout) + {} + + IRArrayTypeLayout* build() + { + return cast<IRArrayTypeLayout>(Super::Builder::build()); + } + + protected: + IROp getOp() SLANG_OVERRIDE { return kIROp_ArrayTypeLayout; } + void addOperandsImpl(List<IRInst*>& ioOperands) SLANG_OVERRIDE; + + IRTypeLayout* m_elementTypeLayout; + }; +}; + + /// Specialized layout information for stream-output types +struct IRStreamOutputTypeLayout : IRTypeLayout +{ + typedef IRTypeLayout Super; + + IR_LEAF_ISA(StreamOutputTypeLayout) + + IRTypeLayout* getElementTypeLayout() + { + return cast<IRTypeLayout>(getOperand(0)); + } + + struct Builder : Super::Builder + { + Builder(IRBuilder* irBuilder, IRTypeLayout* elementTypeLayout) + : Super::Builder(irBuilder) + , m_elementTypeLayout(elementTypeLayout) + {} + + IRArrayTypeLayout* build() + { + return cast<IRArrayTypeLayout>(Super::Builder::build()); + } + + protected: + IROp getOp() SLANG_OVERRIDE { return kIROp_StreamOutputTypeLayout; } + void addOperandsImpl(List<IRInst*>& ioOperands) SLANG_OVERRIDE; + + IRTypeLayout* m_elementTypeLayout; + }; +}; + + /// Specialized layout information for matrix types +struct IRMatrixTypeLayout : IRTypeLayout +{ + typedef IRTypeLayout Super; + + IR_LEAF_ISA(MatrixTypeLayout) + + MatrixLayoutMode getMode() + { + return MatrixLayoutMode(GetIntVal(cast<IRIntLit>(getOperand(0)))); + } + + struct Builder : Super::Builder + { + Builder(IRBuilder* irBuilder, MatrixLayoutMode mode); + + IRMatrixTypeLayout* build() + { + return cast<IRMatrixTypeLayout>(Super::Builder::build()); + } + + protected: + IROp getOp() SLANG_OVERRIDE { return kIROp_MatrixTypeLayout; } + void addOperandsImpl(List<IRInst*>& ioOperands) SLANG_OVERRIDE; + + IRInst* m_modeInst = nullptr; + }; +}; + + /// Attribute that specifies the layout for one field of a structure type. +struct IRStructFieldLayoutAttr : IRAttr +{ + IR_LEAF_ISA(StructFieldLayoutAttr) + + IRStructKey* getFieldKey() + { + return cast<IRStructKey>(getOperand(0)); + } + + IRVarLayout* getLayout() + { + return cast<IRVarLayout>(getOperand(1)); + } +}; + + /// Specialized layout information for structure types. +struct IRStructTypeLayout : IRTypeLayout +{ + IR_LEAF_ISA(StructTypeLayout) + + typedef IRTypeLayout Super; + + /// Get all of the attributes that represent field layouts. + IROperandList<IRStructFieldLayoutAttr> getFieldLayoutAttrs() + { + return findAttrs<IRStructFieldLayoutAttr>(); + } + + /// Get the number of fields for which layout information is stored. + UInt getFieldCount() + { + return getFieldLayoutAttrs().getCount(); + } + + /// Get the layout information for a field by `index` + IRVarLayout* getFieldLayout(UInt index) + { + return getFieldLayoutAttrs()[index]->getLayout(); + } + + /// Specialized builder for structure type layouts. + struct Builder : Super::Builder + { + Builder(IRBuilder* irBuilder) + : Super::Builder(irBuilder) + {} + + void addField(IRStructKey* key, IRVarLayout* layout) + { + FieldInfo info; + info.key = key; + info.layout = layout; + m_fields.add(info); + } + + IRStructTypeLayout* build() + { + return cast<IRStructTypeLayout>(Super::Builder::build()); + } + + protected: + IROp getOp() SLANG_OVERRIDE { return kIROp_StructTypeLayout; } + void addAttrsImpl(List<IRInst*>& ioOperands) SLANG_OVERRIDE; + + struct FieldInfo + { + IRStructKey* key; + IRVarLayout* layout; + }; + + List<FieldInfo> m_fields; + }; +}; + + /// Attribute that represents the layout for one case of a union type +struct IRCaseTypeLayoutAttr : IRAttr +{ + IR_LEAF_ISA(CaseTypeLayoutAttr); + + IRTypeLayout* getTypeLayout() + { + return cast<IRTypeLayout>(getOperand(0)); + } +}; + + /// Specialized layout information for tagged union types +struct IRTaggedUnionTypeLayout : IRTypeLayout +{ + typedef IRTypeLayout Super; + + IR_LEAF_ISA(TaggedUnionTypeLayout) + + /// Get the (byte) offset of the tagged union's tag (aka "discriminator") field + LayoutSize getTagOffset() + { + return LayoutSize::fromRaw(LayoutSize::RawValue(GetIntVal(cast<IRIntLit>(getOperand(0))))); + } + + /// Get all the attributes representing layouts for the difference cases + IROperandList<IRCaseTypeLayoutAttr> getCaseTypeLayoutAttrs() + { + return findAttrs<IRCaseTypeLayoutAttr>(); + } + + /// Get the number of cases for which layout information is stored + UInt getCaseCount() + { + return getCaseTypeLayoutAttrs().getCount(); + } + + /// Get the layout information for the case at the given `index` + IRTypeLayout* getCaseTypeLayout(UInt index) + { + return getCaseTypeLayoutAttrs()[index]->getTypeLayout(); + } + + /// Specialized builder for tagged union type layouts + struct Builder : Super::Builder + { + Builder(IRBuilder* irBuilder, LayoutSize tagOffset); + + void addCaseTypeLayout(IRTypeLayout* typeLayout); + + IRTaggedUnionTypeLayout* build() + { + return cast<IRTaggedUnionTypeLayout>(Super::Builder::build()); + } + + protected: + IROp getOp() SLANG_OVERRIDE { return kIROp_TaggedUnionTypeLayout; } + void addOperandsImpl(List<IRInst*>& ioOperands) SLANG_OVERRIDE; + void addAttrsImpl(List<IRInst*>& ioOperands) SLANG_OVERRIDE; + + IRInst* m_tagOffset = nullptr; + List<IRAttr*> m_caseTypeLayoutAttrs; + }; +}; + + /// Layout information for an entry point struct IREntryPointLayout : IRLayout { IR_LEAF_ISA(EntryPointLayout) - EntryPointLayout* getLayout() { return static_cast<EntryPointLayout*>(getASTLayout()); } + + /// Get the layout information for the entry point parameters. + /// + /// The parameters layout will either be a structure type layout + /// with one field per parameter, or a parameter group type + /// layout wrapping such a structure, if the entry point parameters + /// needed to be allocated into a constant buffer. + /// + IRVarLayout* getParamsLayout() + { + return cast<IRVarLayout>(getOperand(0)); + } + + /// Get the layout information for the entry point result. + /// + /// This represents the return value of the entry point. + /// Note that it does *not* represent all of the entry + /// point outputs, because the parameter list may also + /// contain `out` or `inout` parameters. + /// + IRVarLayout* getResultLayout() + { + return cast<IRVarLayout>(getOperand(1)); + } +}; + + /// Given an entry-point layout, extract the layout for the parameters struct. +IRStructTypeLayout* getScopeStructLayout(IREntryPointLayout* scopeLayout); + + /// Attribute that associates a variable layout with a known stage. +struct IRStageAttr : IRAttr +{ + IR_LEAF_ISA(StageAttr); + + IRIntLit* getStageOperand() { return cast<IRIntLit>(getOperand(0)); } + Stage getStage() { return Stage(GetIntVal(getStageOperand())); } +}; + + /// Base type for attributes that associate a variable layout with a semantic name and index. +struct IRSemanticAttr : IRAttr +{ + IR_PARENT_ISA(SemanticAttr); + + IRStringLit* getNameOperand() { return cast<IRStringLit>(getOperand(0)); } + UnownedStringSlice getName() { return getNameOperand()->getStringSlice(); } + + IRIntLit* getIndexOperand() { return cast<IRIntLit>(getOperand(1)); } + UInt getIndex() { return UInt(GetIntVal(getIndexOperand())); } +}; + + /// Attribute that associates a variable with a system-value semantic name and index +struct IRSystemValueSemanticAttr : IRSemanticAttr +{ + IR_LEAF_ISA(SystemValueSemanticAttr); }; -// 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 + /// Attribute that associates a variable with a user-defined semantic name and index +struct IRUserSemanticAttr : IRSemanticAttr +{ + IR_LEAF_ISA(UserSemanticAttr); +}; + + /// Layout infromation for a single parameter/field 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 the type layout information for this variable + IRTypeLayout* getTypeLayout() { return cast<IRTypeLayout>(getOperand(0)); } + + /// Get all the attributes representing resource-kind-specific offsets + IROperandList<IRVarOffsetAttr> getOffsetAttrs(); + + /// Find the offset information (if present) for the given resource `kind` + IRVarOffsetAttr* findOffsetAttr(LayoutResourceKind kind); + + /// Does this variable use any resources of the given `kind`? + bool usesResourceKind(LayoutResourceKind kind); + + /// Get the fixed/known stage that this variable is associated with. + /// + /// This will be a specific stage for entry-point parameters, but + /// will be `Stage::Unknown` for any parameter that is not bound + /// solely to one entry point. + /// + Stage getStage(); + + /// Find the system-value semantic attribute for this variable, if any. + IRSystemValueSemanticAttr* findSystemValueSemanticAttr(); + + /// Get the (optional) layout for any "pending" data assocaited with this variable. + IRVarLayout* getPendingVarLayout(); + + /// Builder for construction `IRVarLayout`s in a stateful fashion + struct Builder + { + /// Begin building a variable layout with the given `typeLayout` + /// + /// The result layout and any instructions needed along the way + /// will be allocated with `irBuilder`. + /// + Builder( + IRBuilder* irBuilder, + IRTypeLayout* typeLayout); + + /// Represents resource-kind-specific offset information + struct ResInfo + { + LayoutResourceKind kind = LayoutResourceKind::None; + UInt offset = 0; + UInt space = 0; + }; + + /// Has any resource usage/offset been registered for the given resource `kind`? + bool usesResourceKind(LayoutResourceKind kind); + + /// Either fetch or add a `ResInfo` record for `kind` and return it + ResInfo* findOrAddResourceInfo(LayoutResourceKind kind); + + /// Set the (optional) variable layout for pending data. + void setPendingVarLayout(IRVarLayout* varLayout) + { + m_pendingVarLayout = varLayout; + } + + /// Set the (optional) system-valeu semantic for this variable. + void setSystemValueSemantic(String const& name, UInt index); + + /// Set the (optional) user-defined semantic for this variable. + void setUserSemantic(String const& name, UInt index); + + /// Set the (optional) known stage for this variable. + void setStage(Stage stage); + + /// Clone all of the layout information from the `other` layout, except for offsets. + /// + /// This is convenience when one wants to build a variable layout "like that other one, but..." + void cloneEverythingButOffsetsFrom( + IRVarLayout* other); + + /// Build a variable layout using the current state that has been set. + IRVarLayout* build(); - /// Get/set absolute layout - IRVarLayout* getAbsoluteLayout() { return cast<IRVarLayout>(getOperand(3)); } - void setAbsoluteLayout(IRVarLayout* layout) { getOperands()[3].set(layout); } + private: + IRBuilder* m_irBuilder; + IRBuilder* getIRBuilder() { return m_irBuilder; }; + + IRTypeLayout* m_typeLayout = nullptr; + IRVarLayout* m_pendingVarLayout = nullptr; + + IRSystemValueSemanticAttr* m_systemValueSemantic = nullptr; + IRUserSemanticAttr* m_userSemantic = nullptr; + IRStageAttr* m_stageAttr = nullptr; + + ResInfo m_resInfos[SLANG_PARAMETER_CATEGORY_COUNT]; + }; }; -// Associates an IR-level decoration with a source layout + /// Associate layout information with an instruction. + /// + /// This decoration is used in three main ways: + /// + /// * To attach an `IRVarLayout` to an `IRGlobalParam` or entry-point `IRParam` representing a shader parameter + /// * To attach an `IREntryPointLayout` to an `IRFunc` representing an entry point + /// * To attach an `IRTaggedUnionTypeLayout` to an `IRTaggedUnionType` + /// 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(); - } + /// Get the layout that is being attached to the parent instruction + IRLayout* getLayout() { return cast<IRLayout>(getOperand(0)); } }; // @@ -1434,9 +1993,57 @@ struct IRBuilder void addHighLevelDeclDecoration(IRInst* value, Decl* decl); - void addLayoutDecoration(IRInst* value, Layout* layout); +// void addLayoutDecoration(IRInst* value, Layout* layout); + void addLayoutDecoration(IRInst* value, IRLayout* layout); + +// IRLayout* getLayout(Layout* astLayout); + + IRTypeSizeAttr* getTypeSizeAttr( + LayoutResourceKind kind, + LayoutSize size); + IRVarOffsetAttr* getVarOffsetAttr( + LayoutResourceKind kind, + UInt offset, + UInt space = 0); + IRPendingLayoutAttr* getPendingLayoutAttr( + IRLayout* pendingLayout); + IRStructFieldLayoutAttr* getFieldLayoutAttr( + IRStructKey* key, + IRVarLayout* layout); + IRCaseTypeLayoutAttr* getCaseTypeLayoutAttr( + IRTypeLayout* layout); + + IRSemanticAttr* getSemanticAttr( + IROp op, + String const& name, + UInt index); + IRSystemValueSemanticAttr* getSystemValueSemanticAttr( + String const& name, + UInt index) + { + return cast<IRSystemValueSemanticAttr>(getSemanticAttr( + kIROp_SystemValueSemanticAttr, + name, + index)); + } + IRUserSemanticAttr* getUserSemanticAttr( + String const& name, + UInt index) + { + return cast<IRUserSemanticAttr>(getSemanticAttr( + kIROp_UserSemanticAttr, + name, + index)); + } + + IRStageAttr* getStageAttr(Stage stage); + + IRTypeLayout* getTypeLayout(IROp op, List<IRInst*> const& operands); + IRVarLayout* getVarLayout(List<IRInst*> const& operands); + IREntryPointLayout* getEntryPointLayout( + IRVarLayout* paramsLayout, + IRVarLayout* resultLayout); - IRLayout* getLayout(Layout* astLayout); void addNameHintDecoration(IRInst* value, IRStringLit* name) { diff --git a/source/slang/slang-ir-legalize-types.cpp b/source/slang/slang-ir-legalize-types.cpp index f2a3e3d9a..f9c78a021 100644 --- a/source/slang/slang-ir-legalize-types.cpp +++ b/source/slang/slang-ir-legalize-types.cpp @@ -123,7 +123,7 @@ static LegalVal declareVars( IRTypeLegalizationContext* context, IROp op, LegalType type, - TypeLayout* typeLayout, + IRTypeLayout* typeLayout, LegalVarChain const& varChain, UnownedStringSlice nameHint, IRInst* leafVar, @@ -1246,10 +1246,10 @@ static LegalVal legalizeInst( } } -RefPtr<VarLayout> findVarLayout(IRInst* value) +IRVarLayout* findVarLayout(IRInst* value) { if (auto layoutDecoration = value->findDecoration<IRLayoutDecoration>()) - return as<VarLayout>(layoutDecoration->getIRLayout()->getASTLayout()); + return as<IRVarLayout>(layoutDecoration->getLayout()); return nullptr; } @@ -1274,8 +1274,8 @@ static LegalVal legalizeLocalVar( auto originalRate = irLocalVar->getRate(); - RefPtr<VarLayout> varLayout = findVarLayout(irLocalVar); - RefPtr<TypeLayout> typeLayout = varLayout ? varLayout->typeLayout : nullptr; + IRVarLayout* varLayout = findVarLayout(irLocalVar); + IRTypeLayout* typeLayout = varLayout ? varLayout->getTypeLayout() : nullptr; // If we've decided to do implicit deref on the type, // then go ahead and declare a value of the pointed-to type. @@ -1580,7 +1580,7 @@ static LegalVal declareSimpleVar( IRTypeLegalizationContext* context, IROp op, IRType* type, - TypeLayout* typeLayout, + IRTypeLayout* typeLayout, LegalVarChain const& varChain, UnownedStringSlice nameHint, IRInst* leafVar, @@ -1588,9 +1588,7 @@ static LegalVal declareSimpleVar( { SLANG_UNUSED(globalNameInfo); - RefPtr<VarLayout> varLayout = createVarLayout(varChain, typeLayout); - - DeclRef<VarDeclBase> varDeclRef = varChain.getLeafVarDeclRef(); + IRVarLayout* varLayout = createVarLayout(context->builder, varChain, typeLayout); IRBuilder* builder = context->builder; @@ -1654,11 +1652,6 @@ static LegalVal declareSimpleVar( builder->addLayoutDecoration(irVar, varLayout); } - if (varDeclRef) - { - builder->addHighLevelDeclDecoration(irVar, varDeclRef.getDecl()); - } - if( nameHint.size() ) { context->builder->addNameHintDecoration(irVar, nameHint); @@ -1710,11 +1703,12 @@ static LegalVal declareSimpleVar( /// when the legal wrapped buffer type was created. /// static void _addFieldsToWrappedBufferElementTypeLayout( - TypeLayout* elementTypeLayout, // layout of the original field type - StructTypeLayout* newTypeLayout, // layout we are filling in - LegalElementWrapping const& elementInfo, // information on how the original type got wrapped - LegalVarChain const& varChain, // chain of variables that is leading to this field - bool isSpecial) // should we assume a leaf field is a special (interface) type? + IRBuilder* irBuilder, + IRTypeLayout* elementTypeLayout, // layout of the original field type + IRStructTypeLayout::Builder* newTypeLayout, // layout we are filling in + LegalElementWrapping const& elementInfo, // information on how the original type got wrapped + LegalVarChain const& varChain, // chain of variables that is leading to this field + bool isSpecial) // should we assume a leaf field is a special (interface) type? { // The way we handle things depends primary on the // `elementInfo`, because that tells us how things @@ -1760,14 +1754,17 @@ static void _addFieldsToWrappedBufferElementTypeLayout( // cases. We will be computing layout information // for a field of the new/wrapped buffer element type. // - RefPtr<VarLayout> newFieldLayout; + IRVarLayout* newFieldLayout = nullptr; if(isSpecial) { // In the special case, that field will be laid out // based on the "pending" var chain, and the type // of the pending data for the element. // - newFieldLayout = createSimpleVarLayout(varChain.pendingChain, elementTypeLayout->pendingDataTypeLayout); + newFieldLayout = createSimpleVarLayout( + irBuilder, + varChain.pendingChain, + elementTypeLayout->getPendingDataTypeLayout()); } else { @@ -1775,7 +1772,10 @@ static void _addFieldsToWrappedBufferElementTypeLayout( // information and the primary/nominal type of // the field. // - newFieldLayout = createSimpleVarLayout(varChain.primaryChain, elementTypeLayout); + newFieldLayout = createSimpleVarLayout( + irBuilder, + varChain.primaryChain, + elementTypeLayout); } // Either way, we add the new field to the struct type @@ -1783,8 +1783,7 @@ static void _addFieldsToWrappedBufferElementTypeLayout( // information so that we can find the field layout // based on the IR key for the struct field. // - newTypeLayout->fields.add(newFieldLayout); - newTypeLayout->mapKeyToLayout.Add(simpleInfo->key, newFieldLayout); + newTypeLayout->addField(simpleInfo->key, newFieldLayout); } break; @@ -1799,6 +1798,7 @@ static void _addFieldsToWrappedBufferElementTypeLayout( // auto implicitDerefInfo = elementInfo.getImplicitDeref(); _addFieldsToWrappedBufferElementTypeLayout( + irBuilder, elementTypeLayout, newTypeLayout, implicitDerefInfo->field, @@ -1824,12 +1824,14 @@ static void _addFieldsToWrappedBufferElementTypeLayout( // auto pairElementInfo = elementInfo.getPair(); _addFieldsToWrappedBufferElementTypeLayout( + irBuilder, elementTypeLayout, newTypeLayout, pairElementInfo->ordinary, varChain, false); _addFieldsToWrappedBufferElementTypeLayout( + irBuilder, elementTypeLayout, newTypeLayout, pairElementInfo->special, @@ -1858,7 +1860,8 @@ static void _addFieldsToWrappedBufferElementTypeLayout( LegalVarChainLink fieldChain(varChain, oldFieldLayout); _addFieldsToWrappedBufferElementTypeLayout( - oldFieldLayout->typeLayout, + irBuilder, + oldFieldLayout->getTypeLayout(), newTypeLayout, ee.field, fieldChain, @@ -1880,13 +1883,13 @@ static void _addFieldsToWrappedBufferElementTypeLayout( /// being relative to the "pending" data. /// static void _addOffsetVarLayoutEntry( - VarLayout* resultVarLayout, + IRVarLayout::Builder* resultVarLayout, LegalVarChain const& varChain, LayoutResourceKind kind) { // If the target already has an offset for this kind, bail out. // - if(resultVarLayout->FindResourceInfo(kind)) + if(resultVarLayout->usesResourceKind(kind)) return; // Add the `ResourceInfo` that will represent the offset for @@ -1901,10 +1904,10 @@ static void _addOffsetVarLayoutEntry( // for(auto vv = varChain.pendingChain; vv; vv = vv->next ) { - if( auto chainResInfo = vv->varLayout->FindResourceInfo(kind) ) + if( auto chainResInfo = vv->varLayout->findOffsetAttr(kind) ) { - resultResInfo->index += chainResInfo->index; - resultResInfo->space += chainResInfo->space; + resultResInfo->offset += chainResInfo->getOffset(); + resultResInfo->space += chainResInfo->getSpace(); } } @@ -1914,10 +1917,10 @@ static void _addOffsetVarLayoutEntry( // for(auto vv = varChain.primaryChain; vv; vv = vv->next ) { - if( auto chainResInfo = vv->varLayout->FindResourceInfo(kind) ) + if( auto chainResInfo = vv->varLayout->findOffsetAttr(kind) ) { - resultResInfo->index -= chainResInfo->index; - resultResInfo->space -= chainResInfo->space; + resultResInfo->offset -= chainResInfo->getOffset(); + resultResInfo->space -= chainResInfo->getSpace(); } } } @@ -1929,42 +1932,43 @@ static void _addOffsetVarLayoutEntry( /// to be made relative to the "primary" data, because we are /// legalizing the pending data out of the code. /// -static RefPtr<VarLayout> _createOffsetVarLayout( +static IRVarLayout* _createOffsetVarLayout( + IRBuilder* irBuilder, LegalVarChain const& varChain, - TypeLayout* typeLayout) + IRTypeLayout* typeLayout) { - RefPtr<VarLayout> resultVarLayout = new VarLayout(); + IRVarLayout::Builder resultVarLayoutBuilder(irBuilder, typeLayout); // For every resource kind the type consumes, we will // compute an adjusted offset for the variable that // encodes the (absolute) offset of the pending data // in `varChain` relative to its primary data. // - for( auto resInfo : typeLayout->resourceInfos ) + for( auto resInfo : typeLayout->getSizeAttrs() ) { - _addOffsetVarLayoutEntry(resultVarLayout, varChain, resInfo.kind); + _addOffsetVarLayoutEntry(&resultVarLayoutBuilder, varChain, resInfo->getResourceKind()); } - return resultVarLayout; + return resultVarLayoutBuilder.build(); } /// Place offset information from `srcResInfo` onto `dstLayout`, /// offset by whatever is in `offsetVarLayout` static void addOffsetResInfo( - VarLayout* dstLayout, - VarLayout::ResourceInfo const& srcResInfo, - VarLayout* offsetVarLayout) + IRVarLayout::Builder* dstLayout, + IRVarOffsetAttr* srcResInfo, + IRVarLayout* offsetVarLayout) { - auto kind = srcResInfo.kind; + auto kind = srcResInfo->getResourceKind(); auto dstResInfo = dstLayout->findOrAddResourceInfo(kind); - dstResInfo->index = srcResInfo.index; - dstResInfo->space = srcResInfo.space; + dstResInfo->offset = srcResInfo->getOffset(); + dstResInfo->space = srcResInfo->getSpace(); - if( auto offsetResInfo = offsetVarLayout->findOrAddResourceInfo(kind) ) + if( auto offsetResInfo = offsetVarLayout->findOffsetAttr(kind) ) { - dstResInfo->index += offsetResInfo->index; - dstResInfo->space += offsetResInfo->space; + dstResInfo->offset += offsetResInfo->getOffset(); + dstResInfo->space += offsetResInfo->getSpace(); } } @@ -1996,15 +2000,16 @@ static void addOffsetResInfo( /// of the surrounding context (e.g., the global shader parameter /// that has this type). /// -static RefPtr<TypeLayout> _createWrappedBufferTypeLayout( - TypeLayout* oldTypeLayout, +static IRTypeLayout* _createWrappedBufferTypeLayout( + IRBuilder* irBuilder, + IRTypeLayout* oldTypeLayout, WrappedBufferPseudoType* wrappedBufferTypeInfo, LegalVarChain const& outerVarChain) { // We shouldn't get invoked unless there was a parameter group type, // so we will sanity check for that just to be sure. // - auto oldParameterGroupTypeLayout = as<ParameterGroupTypeLayout>(oldTypeLayout); + auto oldParameterGroupTypeLayout = as<IRParameterGroupTypeLayout>(oldTypeLayout); SLANG_ASSERT(oldParameterGroupTypeLayout); if(!oldParameterGroupTypeLayout) return oldTypeLayout; @@ -2022,12 +2027,9 @@ static RefPtr<TypeLayout> _createWrappedBufferTypeLayout( // re-create the original intention of the split layout (both primary // and pending data) for a type that now only has the "primary" data. // - RefPtr<ParameterGroupTypeLayout> newTypeLayout = new ParameterGroupTypeLayout(); - newTypeLayout->type = oldTypeLayout->type; - newTypeLayout->rules = oldTypeLayout->rules; - newTypeLayout->uniformAlignment = oldTypeLayout->uniformAlignment; - for(auto resInfo : oldTypeLayout->resourceInfos) - newTypeLayout->addResourceUsage(resInfo); + + IRParameterGroupTypeLayout::Builder newTypeLayoutBuilder(irBuilder); + newTypeLayoutBuilder.addResourceUsageFrom(oldTypeLayout); // Any fields in the "pending" data will have offset information // that is relative to the pending data for their parent, and so on. @@ -2047,7 +2049,10 @@ static RefPtr<TypeLayout> _createWrappedBufferTypeLayout( // itself, and so the offsets already *are* relative to the start // of the buffer). // - auto offsetVarLayout = _createOffsetVarLayout(outerVarChain, oldTypeLayout->pendingDataTypeLayout); + auto offsetVarLayout = _createOffsetVarLayout( + irBuilder, + outerVarChain, + oldTypeLayout->getPendingDataTypeLayout()); LegalVarChainLink offsetVarChain(LegalVarChain(), offsetVarLayout); // We will start our construction of the pieces of the output @@ -2072,27 +2077,39 @@ static RefPtr<TypeLayout> _createWrappedBufferTypeLayout( // container type/var layout, and constructing new objects // that will represent the layout for our wrapped buffer. // - auto oldPrimaryContainerVarLayout = oldParameterGroupTypeLayout->containerVarLayout; - auto oldPrimaryContainerTypeLayout = oldPrimaryContainerVarLayout->typeLayout; + auto oldPrimaryContainerVarLayout = oldParameterGroupTypeLayout->getContainerVarLayout(); + auto oldPrimaryContainerTypeLayout = oldPrimaryContainerVarLayout->getTypeLayout(); + + IRTypeLayout::Builder newContainerTypeLayoutBuilder(irBuilder); + newContainerTypeLayoutBuilder.addResourceUsageFrom(oldPrimaryContainerTypeLayout); - RefPtr<TypeLayout> newContainerTypeLayout = new TypeLayout(); - newContainerTypeLayout->type = oldPrimaryContainerTypeLayout->type; + if( auto oldPendingContainerVarLayout = oldPrimaryContainerVarLayout->getPendingVarLayout() ) + { + // Whatever resources were allocated for the pending data type, + // our new combined container type needs to account for them + // (e.g., if we didn't have a constant buffer in the primary + // data, but one got allocated in the pending data, we need + // to end up with type layout information that includes a + // constnat buffer). + // + auto oldPendingContainerTypeLayout = oldPendingContainerVarLayout->getTypeLayout(); + newContainerTypeLayoutBuilder.addResourceUsageFrom(oldPendingContainerTypeLayout); + } + auto newContainerTypeLayout = newContainerTypeLayoutBuilder.build(); - RefPtr<VarLayout> newContainerVarLayout = new VarLayout(); - newContainerVarLayout->typeLayout = newContainerTypeLayout; - newTypeLayout->containerVarLayout = newContainerVarLayout; + + IRVarLayout::Builder newContainerVarLayoutBuilder(irBuilder, newContainerTypeLayout); // Whatever got allocated for the primary container should get copied // over to the new layout (e.g., if we allocated a constant buffer // for `gMat` then we need to retain that information). // - newContainerTypeLayout->addResourceUsageFrom(oldPrimaryContainerTypeLayout); - for( auto resInfo : oldPrimaryContainerVarLayout->resourceInfos ) + for( auto resInfo : oldPrimaryContainerVarLayout->getOffsetAttrs() ) { - auto newResInfo = newContainerVarLayout->findOrAddResourceInfo(resInfo.kind); - newResInfo->index = resInfo.index; - newResInfo->space = resInfo.space; + auto newResInfo = newContainerVarLayoutBuilder.findOrAddResourceInfo(resInfo->getResourceKind()); + newResInfo->offset = resInfo->getOffset(); + newResInfo->space = resInfo->getSpace(); } // It is possible that a constant buffer and/or space didn't get @@ -2103,18 +2120,8 @@ static RefPtr<TypeLayout> _createWrappedBufferTypeLayout( // we need to account for that case and copy over the relevant // resource usage from the pending data, if there is any. // - if( auto oldPendingContainerVarLayout = oldPrimaryContainerVarLayout->pendingVarLayout ) + if( auto oldPendingContainerVarLayout = oldPrimaryContainerVarLayout->getPendingVarLayout() ) { - // Whatever resources were allocated for the pending data type, - // our new combined container type needs to account for them - // (e.g., if we didn't have a constant buffer in the primary - // data, but one got allocated in the pending data, we need - // to end up with type layout information that includes a - // constnat buffer). - // - auto oldPendingContainerTypeLayout = oldPendingContainerVarLayout->typeLayout; - newContainerTypeLayout->addResourceUsageFrom(oldPendingContainerTypeLayout); - // We also need to add offset information based on the "pending" // var layout, but we need to deal with the fact that this information // is currently stored relative to the pending var layout for the surrounding @@ -2124,11 +2131,14 @@ static RefPtr<TypeLayout> _createWrappedBufferTypeLayout( // in handy, because it represents the value(s) we need to // add to each of the per-resource-kind offsets. // - for( auto resInfo : oldPendingContainerVarLayout->resourceInfos ) + for( auto resInfo : oldPendingContainerVarLayout->getOffsetAttrs() ) { - addOffsetResInfo(newContainerVarLayout, resInfo, offsetVarLayout); + addOffsetResInfo(&newContainerVarLayoutBuilder, resInfo, offsetVarLayout); } } + + auto newContainerVarLayout = newContainerVarLayoutBuilder.build(); + newTypeLayoutBuilder.setContainerVarLayout(newContainerVarLayout); } // Now that we've dealt with the container variable, we can turn @@ -2142,14 +2152,13 @@ static RefPtr<TypeLayout> _createWrappedBufferTypeLayout( // the objects we'll use to represent the type/var layout for // the new element type. // - auto oldElementVarLayout = oldParameterGroupTypeLayout->elementVarLayout; - auto oldElementTypeLayout = oldElementVarLayout->typeLayout; + auto oldElementVarLayout = oldParameterGroupTypeLayout->getElementVarLayout(); + auto oldElementTypeLayout = oldElementVarLayout->getTypeLayout(); // Now matter what, the element type of a wrapped buffer // will always have a structure type. // - RefPtr<StructTypeLayout> newElementTypeLayout = new StructTypeLayout(); - newElementTypeLayout->type = oldElementTypeLayout->type; + IRStructTypeLayout::Builder newElementTypeLayoutBuilder(irBuilder); // The `wrappedBufferTypeInfo` that was passed in tells // us how the fields of the original type got turned into @@ -2171,12 +2180,15 @@ static RefPtr<TypeLayout> _createWrappedBufferTypeLayout( varChainForElementType.pendingChain = offsetVarChain.primaryChain; _addFieldsToWrappedBufferElementTypeLayout( + irBuilder, oldElementTypeLayout, - newElementTypeLayout, + &newElementTypeLayoutBuilder, wrappedBufferTypeInfo->elementInfo, varChainForElementType, true); + auto newElementTypeLayout = newElementTypeLayoutBuilder.build(); + // A parameter group type layout holds a `VarLayout` for the element type, // which encodes the offset of the element type with respect to the // start of the parameter group as a whole (e.g., to handle the case @@ -2184,26 +2196,34 @@ static RefPtr<TypeLayout> _createWrappedBufferTypeLayout( // element type, so the offset to the first `binding` for the element // type is one, not zero. // - LegalVarChainLink elementVarChain(LegalVarChain(), oldParameterGroupTypeLayout->elementVarLayout); - auto newElementVarLayout = createVarLayout(elementVarChain, newElementTypeLayout); - newTypeLayout->elementVarLayout = newElementVarLayout; + LegalVarChainLink elementVarChain(LegalVarChain(), oldParameterGroupTypeLayout->getElementVarLayout()); + auto newElementVarLayout = createVarLayout(irBuilder, elementVarChain, newElementTypeLayout); + + newTypeLayoutBuilder.setElementVarLayout(newElementVarLayout); // For legacy/API reasons, we also need to compute a version of the // element type where the offset stored in the `elementVarLayout` // gets "baked in" to the fields of the element type. // - newTypeLayout->offsetElementTypeLayout = applyOffsetToTypeLayout( - newElementTypeLayout, - newElementVarLayout); + // TODO: For IR-based layout information the offset layout should + // not really be required, and it is only being used in a few places + // that could in principle be refactored. We need to make sure to + // do that cleanup eventually. + // + newTypeLayoutBuilder.setOffsetElementTypeLayout( + applyOffsetToTypeLayout( + irBuilder, + newElementTypeLayout, + newElementVarLayout)); - return newTypeLayout; + return newTypeLayoutBuilder.build(); } static LegalVal declareVars( IRTypeLegalizationContext* context, IROp op, LegalType type, - TypeLayout* inTypeLayout, + IRTypeLayout* inTypeLayout, LegalVarChain const& inVarChain, UnownedStringSlice nameHint, IRInst* leafVar, @@ -2211,7 +2231,7 @@ static LegalVal declareVars( bool isSpecial) { LegalVarChain varChain = inVarChain; - TypeLayout* typeLayout = inTypeLayout; + IRTypeLayout* typeLayout = inTypeLayout; if( isSpecial ) { if( varChain.pendingChain ) @@ -2221,7 +2241,7 @@ static LegalVal declareVars( } if( typeLayout ) { - if( auto pendingTypeLayout = typeLayout->pendingDataTypeLayout ) + if( auto pendingTypeLayout = typeLayout->getPendingDataTypeLayout() ) { typeLayout = pendingTypeLayout; } @@ -2274,7 +2294,7 @@ static LegalVal declareVars( for (auto ee : tupleType->elements) { auto fieldLayout = getFieldLayout(typeLayout, ee.key); - RefPtr<TypeLayout> fieldTypeLayout = fieldLayout ? fieldLayout->typeLayout : nullptr; + IRTypeLayout* fieldTypeLayout = fieldLayout ? fieldLayout->getTypeLayout() : nullptr; // If we have a type layout coming in, we really expect to have a layout for each field. SLANG_ASSERT(fieldLayout || !typeLayout); @@ -2326,7 +2346,11 @@ static LegalVal declareVars( { auto wrappedBuffer = type.getWrappedBuffer(); - auto wrappedTypeLayout = _createWrappedBufferTypeLayout(typeLayout, wrappedBuffer, varChain); + auto wrappedTypeLayout = _createWrappedBufferTypeLayout( + context->builder, + typeLayout, + wrappedBuffer, + varChain); auto innerVal = declareSimpleVar( context, @@ -2403,8 +2427,8 @@ static LegalVal legalizeGlobalParam( context, irGlobalParam->getFullType()); - RefPtr<VarLayout> varLayout = findVarLayout(irGlobalParam); - RefPtr<TypeLayout> typeLayout = varLayout ? varLayout->typeLayout : nullptr; + IRVarLayout* varLayout = findVarLayout(irGlobalParam); + IRTypeLayout* typeLayout = varLayout ? varLayout->getTypeLayout() : nullptr; switch (legalValueType.flavor) { diff --git a/source/slang/slang-ir-link.cpp b/source/slang/slang-ir-link.cpp index f46a0db09..1cdc2cfc9 100644 --- a/source/slang/slang-ir-link.cpp +++ b/source/slang/slang-ir-link.cpp @@ -754,23 +754,26 @@ static void maybeCopyLayoutInformationToParameters( if(!layoutDecor) return; - auto entryPointLayout = as<EntryPointLayout>(layoutDecor->getASTLayout()); + auto entryPointLayout = as<IREntryPointLayout>(layoutDecor->getLayout()); if(!entryPointLayout) return; if( auto firstBlock = func->getFirstBlock() ) { auto paramsStructLayout = getScopeStructLayout(entryPointLayout); - Index paramLayoutCount = paramsStructLayout->fields.getCount(); + Index paramLayoutCount = paramsStructLayout->getFieldCount(); Index paramCounter = 0; for( auto pp = firstBlock->getFirstParam(); pp; pp = pp->getNextParam() ) { Index paramIndex = paramCounter++; if( paramIndex < paramLayoutCount ) { - auto paramLayout = paramsStructLayout->fields[paramIndex]; + auto paramLayout = paramsStructLayout->getFieldLayout(paramIndex); - auto offsetParamLayout = applyOffsetToVarLayout(paramLayout, entryPointLayout->parametersLayout); + auto offsetParamLayout = applyOffsetToVarLayout( + builder, + paramLayout, + entryPointLayout->getParamsLayout()); builder->addLayoutDecoration( pp, diff --git a/source/slang/slang-ir-union.cpp b/source/slang/slang-ir-union.cpp index d456cebee..0efca5602 100644 --- a/source/slang/slang-ir-union.cpp +++ b/source/slang/slang-ir-union.cpp @@ -101,7 +101,7 @@ struct DesugarUnionTypesContext // unions in the generated IR have an associated (target-specific) // layout. // - TaggedUnionTypeLayout* taggedUnionTypeLayout; + IRTaggedUnionTypeLayout* taggedUnionTypeLayout; // The basic approach we will use 16-byte chunks (represented as an array // of `uint4`s) to reprent the "bulk" of a type, and then use a single field @@ -269,8 +269,8 @@ struct DesugarUnionTypesContext // for fields, etc.). // auto taggedUnionTypeLayout = taggedUnionInfo->taggedUnionTypeLayout; - SLANG_ASSERT(caseTagIndex < UInt(taggedUnionTypeLayout->caseTypeLayouts.getCount())); - auto caseTypeLayout = taggedUnionTypeLayout->caseTypeLayouts[caseTagIndex]; + SLANG_ASSERT(caseTagIndex < UInt(taggedUnionTypeLayout->getCaseCount())); + auto caseTypeLayout = taggedUnionTypeLayout->getCaseTypeLayout(caseTagIndex); // At this point we know the type we are trying to extract, as well // as its layout. We will defer the actual implementation of extraction @@ -342,7 +342,7 @@ struct DesugarUnionTypesContext IRType* payloadType, // - The memory layout of that payload type. - TypeLayout* payloadTypeLayout, + IRTypeLayout* payloadTypeLayout, // - The byte offset at which we want to fetch the payload. UInt64 payloadOffset) @@ -366,7 +366,7 @@ struct DesugarUnionTypesContext // there to be complete type layout information for the // types involved. // - auto structTypeLayout = as<StructTypeLayout>(payloadTypeLayout); + auto structTypeLayout = as<IRStructTypeLayout>(payloadTypeLayout); SLANG_ASSERT(structTypeLayout); // We are going to emit code to extract each of the fields @@ -384,15 +384,15 @@ struct DesugarUnionTypesContext // IR struct and the fields of the layout still align. // UInt fieldIndex = fieldCounter++; - auto fieldLayout = structTypeLayout->fields[fieldIndex]; + auto fieldLayout = structTypeLayout->getFieldLayout(fieldIndex); auto fieldTypeLayout = fieldLayout->getTypeLayout(); // The offset of the field can be computed from the base // offset passed in, plus the reflection data for the field. // UInt64 fieldOffset = payloadOffset; - if(auto resInfo = fieldLayout->FindResourceInfo(LayoutResourceKind::Uniform)) - fieldOffset += resInfo->index; + if(auto resInfo = fieldLayout->findOffsetAttr(LayoutResourceKind::Uniform)) + fieldOffset += resInfo->getOffset(); // We make a recursive call to extract each field, expecting // that this will bottom out eventually. @@ -429,10 +429,10 @@ struct DesugarUnionTypesContext // no way to query the layout of the elements of a vector // type. Until that gets added we will kludge things here. // - TypeLayout* elementTypeLayout = nullptr; + IRTypeLayout* elementTypeLayout = nullptr; size_t elementSize = 0; - if(auto resInfo = payloadTypeLayout->FindResourceInfo(LayoutResourceKind::Uniform)) - elementSize = resInfo->count.getFiniteValue() / elementCount; + if(auto resInfo = payloadTypeLayout->findSizeAttr(LayoutResourceKind::Uniform)) + elementSize = resInfo->getSize().getFiniteValue() / elementCount; // Similar to the `struct` case above, we will extract a // value for each element of the vector, and then use @@ -465,13 +465,13 @@ struct DesugarUnionTypesContext // we have an individual scalar field that we need to fetch. // UInt64 payloadSize = 0; - if( auto resInfo = payloadTypeLayout->FindResourceInfo(LayoutResourceKind::Uniform) ) + if( auto resInfo = payloadTypeLayout->findSizeAttr(LayoutResourceKind::Uniform) ) { // TODO: somebody before this point should generate an error if // we have a `union` type that contains a potentially unbounded // amount of data. // - payloadSize = resInfo->count.getFiniteValue(); + payloadSize = resInfo->getSize().getFiniteValue(); } if( payloadSize != 4 ) @@ -670,9 +670,9 @@ struct DesugarUnionTypesContext // auto layoutDecoration = type->findDecoration<IRLayoutDecoration>(); SLANG_ASSERT(layoutDecoration); - auto layout = layoutDecoration->getIRLayout()->getASTLayout(); + auto layout = layoutDecoration->getLayout(); SLANG_ASSERT(layout); - auto taggedUnionTypeLayout = as<TaggedUnionTypeLayout>(layout); + auto taggedUnionTypeLayout = as<IRTaggedUnionTypeLayout>(layout); SLANG_ASSERT(taggedUnionTypeLayout); info->taggedUnionTypeLayout = taggedUnionTypeLayout; @@ -684,7 +684,7 @@ struct DesugarUnionTypesContext // of the tag's alignment. We should deal with that when/if we support // types smaller than 4 bytes in unions. // - auto payloadSize = taggedUnionTypeLayout->tagOffset.getFiniteValue(); + auto payloadSize = taggedUnionTypeLayout->getTagOffset().getFiniteValue(); // We are going to be construction IR code that makes use of the `int` // and `uint` types in several cases, so we go ahead and get a pointer diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp index 78758d944..89e49816f 100644 --- a/source/slang/slang-ir.cpp +++ b/source/slang/slang-ir.cpp @@ -205,6 +205,24 @@ namespace Slang return nullptr; } + IROperandListBase IRInst::getAllAttrs() + { + // We assume as an invariant that all attributes appear at the end of the operand + // list, after all the non-attribute operands. + // + // We will therefore define a range that ends at the end of the operand list ... + // + IRUse* end = getOperands() + getOperandCount(); + // + // ... and begins after the last non-attribute operand. + // + IRUse* cursor = getOperands(); + while(cursor != end && !as<IRAttr>(cursor->get())) + cursor++; + + return IROperandListBase(cursor, end); + } + // IRConstant IRIntegerValue GetIntVal(IRInst* inst) @@ -696,6 +714,371 @@ namespace Slang } // + // IRTypeLayout + // + + IRTypeSizeAttr* IRTypeLayout::findSizeAttr(LayoutResourceKind kind) + { + // TODO: If we could assume the attributes were sorted + // by `kind`, then we could use a binary search here + // instead of linear. + // + // In practice, the number of entries will be very small, + // so the cost of the linear search should not be too bad. + + for( auto sizeAttr : getSizeAttrs() ) + { + if(sizeAttr->getResourceKind() == kind) + return sizeAttr; + } + return nullptr; + } + + IRTypeLayout* IRTypeLayout::unwrapArray() + { + auto typeLayout = this; + while(auto arrayTypeLayout = as<IRArrayTypeLayout>(typeLayout)) + typeLayout = arrayTypeLayout->getElementTypeLayout(); + return typeLayout; + } + + IRTypeLayout* IRTypeLayout::getPendingDataTypeLayout() + { + if(auto attr = findAttr<IRPendingLayoutAttr>()) + return cast<IRTypeLayout>(attr->getLayout()); + return nullptr; + } + + IROperandList<IRTypeSizeAttr> IRTypeLayout::getSizeAttrs() + { + return findAttrs<IRTypeSizeAttr>(); + } + + IRTypeLayout::Builder::Builder(IRBuilder* irBuilder) + : m_irBuilder(irBuilder) + {} + + void IRTypeLayout::Builder::addResourceUsage( + LayoutResourceKind kind, + LayoutSize size) + { + auto& resInfo = m_resInfos[Int(kind)]; + resInfo.kind = kind; + resInfo.size += size; + } + + void IRTypeLayout::Builder::addResourceUsage(IRTypeSizeAttr* sizeAttr) + { + addResourceUsage( + sizeAttr->getResourceKind(), + sizeAttr->getSize()); + } + + void IRTypeLayout::Builder::addResourceUsageFrom(IRTypeLayout* typeLayout) + { + for( auto sizeAttr : typeLayout->getSizeAttrs() ) + { + addResourceUsage(sizeAttr); + } + } + + IRTypeLayout* IRTypeLayout::Builder::build() + { + IRBuilder* irBuilder = getIRBuilder(); + + List<IRInst*> operands; + + addOperands(operands); + addAttrs(operands); + + return irBuilder->getTypeLayout( + getOp(), + operands); + } + + void IRTypeLayout::Builder::addOperands(List<IRInst*>& operands) + { + addOperandsImpl(operands); + } + + void IRTypeLayout::Builder::addAttrs(List<IRInst*>& operands) + { + auto irBuilder = getIRBuilder(); + + for(auto resInfo : m_resInfos) + { + if(resInfo.kind == LayoutResourceKind::None) + continue; + + IRInst* sizeAttr = irBuilder->getTypeSizeAttr( + resInfo.kind, + resInfo.size); + operands.add(sizeAttr); + } + + if( auto pendingTypeLayout = m_pendingTypeLayout ) + { + operands.add(irBuilder->getPendingLayoutAttr( + pendingTypeLayout)); + } + + addAttrsImpl(operands); + } + + // + // IRParameterGroupTypeLayout + // + + void IRParameterGroupTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) + { + ioOperands.add(m_containerVarLayout); + ioOperands.add(m_elementVarLayout); + ioOperands.add(m_offsetElementTypeLayout); + } + + IRParameterGroupTypeLayout* IRParameterGroupTypeLayout::Builder::build() + { + return cast<IRParameterGroupTypeLayout>(Super::Builder::build()); + } + + // + // IRStructTypeLayout + // + + void IRStructTypeLayout::Builder::addAttrsImpl(List<IRInst*>& ioOperands) + { + auto irBuilder = getIRBuilder(); + for(auto field : m_fields) + { + ioOperands.add( + irBuilder->getFieldLayoutAttr(field.key, field.layout)); + } + } + + // + // IRArrayTypeLayout + // + + void IRArrayTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) + { + ioOperands.add(m_elementTypeLayout); + } + + // + // IRStreamOutputTypeLayout + // + + void IRStreamOutputTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) + { + ioOperands.add(m_elementTypeLayout); + } + + // + // IRMatrixTypeLayout + // + + IRMatrixTypeLayout::Builder::Builder(IRBuilder* irBuilder, MatrixLayoutMode mode) + : Super::Builder(irBuilder) + { + m_modeInst = irBuilder->getIntValue(irBuilder->getIntType(), IRIntegerValue(mode)); + } + + void IRMatrixTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) + { + ioOperands.add(m_modeInst); + } + + // + // IRTaggedUnionTypeLayout + // + + IRTaggedUnionTypeLayout::Builder::Builder(IRBuilder* irBuilder, LayoutSize tagOffset) + : Super::Builder(irBuilder) + { + m_tagOffset = irBuilder->getIntValue(irBuilder->getIntType(), tagOffset.raw); + } + + void IRTaggedUnionTypeLayout::Builder::addCaseTypeLayout(IRTypeLayout* typeLayout) + { + m_caseTypeLayoutAttrs.add(getIRBuilder()->getCaseTypeLayoutAttr(typeLayout)); + } + + void IRTaggedUnionTypeLayout::Builder::addOperandsImpl(List<IRInst*>& ioOperands) + { + ioOperands.add(m_tagOffset); + } + + void IRTaggedUnionTypeLayout::Builder::addAttrsImpl(List<IRInst*>& ioOperands) + { + for(auto attr : m_caseTypeLayoutAttrs) + ioOperands.add(attr); + } + + // + // IRVarLayout + // + + bool IRVarLayout::usesResourceKind(LayoutResourceKind kind) + { + // TODO: basing this check on whether or not the + // var layout has an entry for `kind` means that + // we can't just optimize away any entry where + // the offset is zero (which might be a small + // but nice optimization). We could consider shifting + // this test to use the entries on the type layout + // instead (since non-zero resource consumption + // should be an equivalent test). + + return findOffsetAttr(kind) != nullptr; + } + + IRSystemValueSemanticAttr* IRVarLayout::findSystemValueSemanticAttr() + { + return findAttr<IRSystemValueSemanticAttr>(); + } + + IRVarOffsetAttr* IRVarLayout::findOffsetAttr(LayoutResourceKind kind) + { + for( auto offsetAttr : getOffsetAttrs() ) + { + if(offsetAttr->getResourceKind() == kind) + return offsetAttr; + } + return nullptr; + } + + IROperandList<IRVarOffsetAttr> IRVarLayout::getOffsetAttrs() + { + return findAttrs<IRVarOffsetAttr>(); + } + + Stage IRVarLayout::getStage() + { + if(auto stageAttr = findAttr<IRStageAttr>()) + return stageAttr->getStage(); + return Stage::Unknown; + } + + IRVarLayout* IRVarLayout::getPendingVarLayout() + { + if( auto pendingLayoutAttr = findAttr<IRPendingLayoutAttr>() ) + { + return cast<IRVarLayout>(pendingLayoutAttr->getLayout()); + } + return nullptr; + } + + IRVarLayout::Builder::Builder( + IRBuilder* irBuilder, + IRTypeLayout* typeLayout) + : m_irBuilder(irBuilder) + , m_typeLayout(typeLayout) + {} + + bool IRVarLayout::Builder::usesResourceKind(LayoutResourceKind kind) + { + return m_resInfos[Int(kind)].kind != LayoutResourceKind::None; + } + + IRVarLayout::Builder::ResInfo* IRVarLayout::Builder::findOrAddResourceInfo(LayoutResourceKind kind) + { + auto& resInfo = m_resInfos[Int(kind)]; + resInfo.kind = kind; + return &resInfo; + } + + void IRVarLayout::Builder::setSystemValueSemantic(String const& name, UInt index) + { + m_systemValueSemantic = getIRBuilder()->getSystemValueSemanticAttr(name, index); + } + + void IRVarLayout::Builder::setUserSemantic(String const& name, UInt index) + { + m_userSemantic = getIRBuilder()->getUserSemanticAttr(name, index); + } + + void IRVarLayout::Builder::setStage(Stage stage) + { + m_stageAttr = getIRBuilder()->getStageAttr(stage); + } + + void IRVarLayout::Builder::cloneEverythingButOffsetsFrom( + IRVarLayout* that) + { + if(auto systemValueSemantic = that->findAttr<IRSystemValueSemanticAttr>()) + m_systemValueSemantic = systemValueSemantic; + + if(auto userSemantic = that->findAttr<IRUserSemanticAttr>()) + m_userSemantic = userSemantic; + + if(auto stageAttr = that->findAttr<IRStageAttr>()) + m_stageAttr = stageAttr; + } + + IRVarLayout* IRVarLayout::Builder::build() + { + SLANG_ASSERT(m_typeLayout); + + IRBuilder* irBuilder = getIRBuilder(); + + List<IRInst*> operands; + + operands.add(m_typeLayout); + + for(auto resInfo : m_resInfos) + { + if(resInfo.kind == LayoutResourceKind::None) + continue; + + IRInst* varOffsetAttr = irBuilder->getVarOffsetAttr( + resInfo.kind, + resInfo.offset, + resInfo.space); + operands.add(varOffsetAttr); + } + + if(auto semanticAttr = m_userSemantic) + operands.add(semanticAttr); + + if(auto semanticAttr = m_systemValueSemantic) + operands.add(semanticAttr); + + if(auto stageAttr = m_stageAttr) + operands.add(stageAttr); + + if(auto pendingVarLayout = m_pendingVarLayout) + { + IRInst* pendingLayoutAttr = irBuilder->getPendingLayoutAttr( + pendingVarLayout); + operands.add(pendingLayoutAttr); + } + + return irBuilder->getVarLayout(operands); + } + + // + // IREntryPointLayout + // + + IRStructTypeLayout* getScopeStructLayout(IREntryPointLayout* scopeLayout) + { + auto scopeTypeLayout = scopeLayout->getParamsLayout()->getTypeLayout(); + + if( auto constantBufferTypeLayout = as<IRParameterGroupTypeLayout>(scopeTypeLayout) ) + { + scopeTypeLayout = constantBufferTypeLayout->getOffsetElementTypeLayout(); + } + + if( auto structTypeLayout = as<IRStructTypeLayout>(scopeTypeLayout) ) + { + return structTypeLayout; + } + + SLANG_UNEXPECTED("uhandled global-scope binding layout"); + UNREACHABLE_RETURN(nullptr); + } + + // IRBlock* IRBuilder::getBlock() { @@ -3086,79 +3469,149 @@ namespace Slang addDecoration(inst, kIROp_HighLevelDeclDecoration, ptrConst); } - void IRBuilder::addLayoutDecoration(IRInst* value, Layout* layout) + void IRBuilder::addLayoutDecoration(IRInst* value, IRLayout* layout) + { + addDecoration(value, kIROp_LayoutDecoration, layout); + } + + IRTypeSizeAttr* IRBuilder::getTypeSizeAttr( + LayoutResourceKind kind, + LayoutSize size) { - IRLayout* irLayout = getLayout(layout); - addDecoration(value, kIROp_LayoutDecoration, irLayout); + auto kindInst = getIntValue(getIntType(), IRIntegerValue(kind)); + auto sizeInst = getIntValue(getIntType(), IRIntegerValue(size.raw)); + IRInst* operands[] = { kindInst, sizeInst }; + return cast<IRTypeSizeAttr>(findOrEmitHoistableInst( + getVoidType(), + kIROp_TypeSizeAttr, + SLANG_COUNT_OF(operands), + operands)); } - IRLayout* IRBuilder::getLayout(Layout* astLayout) + IRVarOffsetAttr* IRBuilder::getVarOffsetAttr( + LayoutResourceKind kind, + UInt offset, + UInt space) { - if (astLayout == nullptr) - { - return nullptr; - } + IRInst* operands[3]; + UInt operandCount = 0; - IRLayout* irLayout = nullptr; - if(sharedBuilder->layoutMap.TryGetValue(astLayout, irLayout)) - { - SLANG_ASSERT(irLayout->getASTLayout() == astLayout); - return irLayout; - } + auto kindInst = getIntValue(getIntType(), IRIntegerValue(kind)); + operands[operandCount++] = kindInst; + + auto offsetInst = getIntValue(getIntType(), IRIntegerValue(offset)); + operands[operandCount++] = offsetInst; - if (EntryPointLayout* entryPointLayout = as<EntryPointLayout>(astLayout)) + if(space) { - irLayout = createInst<IREntryPointLayout>(this, kIROp_EntryPointLayout, nullptr, getPtrValue(astLayout)); + auto spaceInst = getIntValue(getIntType(), IRIntegerValue(space)); + operands[operandCount++] = spaceInst; } - 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) - }; + return cast<IRVarOffsetAttr>(findOrEmitHoistableInst( + getVoidType(), + kIROp_VarOffsetAttr, + operandCount, + operands)); + } - 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"); - } + IRPendingLayoutAttr* IRBuilder::getPendingLayoutAttr( + IRLayout* pendingLayout) + { + IRInst* operands[] = { pendingLayout }; + + return cast<IRPendingLayoutAttr>(findOrEmitHoistableInst( + getVoidType(), + kIROp_PendingLayoutAttr, + SLANG_COUNT_OF(operands), + operands)); + } + + IRStructFieldLayoutAttr* IRBuilder::getFieldLayoutAttr( + IRStructKey* key, + IRVarLayout* layout) + { + IRInst* operands[] = { key, layout }; + + return cast<IRStructFieldLayoutAttr>(findOrEmitHoistableInst( + getVoidType(), + kIROp_StructFieldLayoutAttr, + SLANG_COUNT_OF(operands), + operands)); + } + + IRCaseTypeLayoutAttr* IRBuilder::getCaseTypeLayoutAttr( + IRTypeLayout* layout) + { + IRInst* operands[] = { layout }; + + return cast<IRCaseTypeLayoutAttr>(findOrEmitHoistableInst( + getVoidType(), + kIROp_CaseTypeLayoutAttr, + SLANG_COUNT_OF(operands), + operands)); + } + + IRSemanticAttr* IRBuilder::getSemanticAttr( + IROp op, + String const& name, + UInt index) + { + auto nameInst = getStringValue(name.getUnownedSlice()); + auto indexInst = getIntValue(getIntType(), index); + + IRInst* operands[] = { nameInst, indexInst }; + + return cast<IRSemanticAttr>(findOrEmitHoistableInst( + getVoidType(), + op, + SLANG_COUNT_OF(operands), + operands)); + } + + IRStageAttr* IRBuilder::getStageAttr(Stage stage) + { + auto stageInst = getIntValue(getIntType(), IRIntegerValue(stage)); + IRInst* operands[] = { stageInst }; + return cast<IRStageAttr>(findOrEmitHoistableInst( + getVoidType(), + kIROp_StageAttr, + SLANG_COUNT_OF(operands), + operands)); + } - SLANG_ASSERT(irLayout); - SLANG_ASSERT(irLayout->getASTLayout() == astLayout); - sharedBuilder->layoutMap[astLayout] = irLayout; + IRTypeLayout* IRBuilder::getTypeLayout(IROp op, List<IRInst*> const& operands) + { + return cast<IRTypeLayout>(findOrEmitHoistableInst( + getVoidType(), + op, + operands.getCount(), + operands.getBuffer())); + } - addGlobalValue(this, irLayout); + IRVarLayout* IRBuilder::getVarLayout(List<IRInst*> const& operands) + { + return cast<IRVarLayout>(findOrEmitHoistableInst( + getVoidType(), + kIROp_VarLayout, + operands.getCount(), + operands.getBuffer())); + } - // need to keep in scope - addRefObjectToFree(astLayout); + IREntryPointLayout* IRBuilder::getEntryPointLayout( + IRVarLayout* paramsLayout, + IRVarLayout* resultLayout) + { + IRInst* operands[] = { paramsLayout, resultLayout }; - return irLayout; + return cast<IREntryPointLayout>(findOrEmitHoistableInst( + getVoidType(), + kIROp_EntryPointLayout, + SLANG_COUNT_OF(operands), + operands)); } // diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h index f13568356..fbb560a60 100644 --- a/source/slang/slang-ir.h +++ b/source/slang/slang-ir.h @@ -219,7 +219,108 @@ struct IRInstList : IRInstListBase Iterator end(); }; + /// A list of contiguous operands that can be iterated over as `IRInst`s. +struct IROperandListBase +{ + IROperandListBase() + : m_begin(nullptr) + , m_end(nullptr) + {} + + IROperandListBase( + IRUse* begin, + IRUse* end) + : m_begin(begin) + , m_end(end) + {} + + struct Iterator + { + public: + Iterator() + : m_cursor(nullptr) + {} + + Iterator(IRUse* use) + : m_cursor(use) + {} + + IRInst* operator*() const + { + return m_cursor->get(); + } + + IRUse* getCursor() const { return m_cursor; } + + void operator++() + { + m_cursor++; + } + + bool operator!=(Iterator const& that) const + { + return m_cursor != that.m_cursor; + } + + protected: + IRUse* m_cursor; + }; + + Iterator begin() const { return Iterator(m_begin); } + Iterator end() const { return Iterator(m_end); } + + Int getCount() const { return m_end - m_begin; } + + IRInst* operator[](Int index) const + { + return m_begin[index].get(); + } + +protected: + IRUse* m_begin; + IRUse* m_end; +}; + + /// A list of contiguous operands that can be iterated over as all being of type `T`. +template<typename T> +struct IROperandList : IROperandListBase +{ + typedef IROperandListBase Super; +public: + IROperandList() + {} + + IROperandList( + IRUse* begin, + IRUse* end) + : Super(begin, end) + {} + + struct Iterator : public IROperandListBase::Iterator + { + typedef IROperandListBase::Iterator Super; + public: + Iterator() + {} + Iterator(IRUse* use) + : Super(use) + {} + + T* operator*() const + { + return (T*) m_cursor->get(); + } + }; + + Iterator begin() const { return Iterator(m_begin); } + Iterator end() const { return Iterator(m_end); } + + T* operator[](Int index) const + { + return (T*) m_begin[index].get(); + } +}; // Every value in the IR is an instruction (even things // like literal values). @@ -260,6 +361,40 @@ struct IRInst template<typename T> T* findDecoration(); + /// Get all the attributes attached to this instruction. + IROperandListBase getAllAttrs(); + + /// Find the first attribute of type `T` attached to this instruction. + template<typename T> + T* findAttr() + { + for( auto attr : getAllAttrs() ) + { + if(auto found = as<T>(attr)) + return found; + } + return nullptr; + } + + /// Find all attributes of type `T` attached to this instruction. + /// + /// This operation assumes that attributes are grouped by type, + /// so that all the attributes of type `T` are contiguous. + /// + template<typename T> + IROperandList<T> findAttrs() + { + auto allAttrs = getAllAttrs(); + auto bb = allAttrs.begin(); + auto end = allAttrs.end(); + while(bb != end && !as<T>(*bb)) + ++bb; + auto ee = bb; + while(ee != end && as<T>(*ee)) + ++ee; + return IROperandList<T>(bb.getCursor(),ee.getCursor()); + } + // The first use of this value (start of a linked list) IRUse* firstUse = nullptr; diff --git a/source/slang/slang-legalize-types.cpp b/source/slang/slang-legalize-types.cpp index d541818ba..cc729e1cf 100644 --- a/source/slang/slang-legalize-types.cpp +++ b/source/slang/slang-legalize-types.cpp @@ -1288,22 +1288,22 @@ LegalType legalizeType( // -RefPtr<TypeLayout> getDerefTypeLayout( - TypeLayout* typeLayout) +IRTypeLayout* getDerefTypeLayout( + IRTypeLayout* typeLayout) { if (!typeLayout) return nullptr; - if (auto parameterGroupTypeLayout = as<ParameterGroupTypeLayout>(typeLayout)) + if (auto parameterGroupTypeLayout = as<IRParameterGroupTypeLayout>(typeLayout)) { - return parameterGroupTypeLayout->offsetElementTypeLayout; + return parameterGroupTypeLayout->getOffsetElementTypeLayout(); } return typeLayout; } -RefPtr<VarLayout> getFieldLayout( - TypeLayout* typeLayout, +IRVarLayout* getFieldLayout( + IRTypeLayout* typeLayout, IRInst* fieldKey) { if (!typeLayout) @@ -1311,13 +1311,13 @@ RefPtr<VarLayout> getFieldLayout( for(;;) { - if(auto arrayTypeLayout = as<ArrayTypeLayout>(typeLayout)) + if(auto arrayTypeLayout = as<IRArrayTypeLayout>(typeLayout)) { - typeLayout = arrayTypeLayout->elementTypeLayout; + typeLayout = arrayTypeLayout->getElementTypeLayout(); } - else if(auto parameterGroupTypeLayout = as<ParameterGroupTypeLayout>(typeLayout)) + else if(auto parameterGroupTypeLayout = as<IRParameterGroupTypeLayout>(typeLayout)) { - typeLayout = parameterGroupTypeLayout->offsetElementTypeLayout; + typeLayout = parameterGroupTypeLayout->getOffsetElementTypeLayout(); } else { @@ -1326,30 +1326,13 @@ RefPtr<VarLayout> getFieldLayout( } - if (auto structTypeLayout = as<StructTypeLayout>(typeLayout)) + if (auto structTypeLayout = as<IRStructTypeLayout>(typeLayout)) { - // First, let's see if the field had a layout registered - // directly using its IR key. - // - RefPtr<VarLayout> fieldLayout; - if(structTypeLayout->mapKeyToLayout.TryGetValue(fieldKey, fieldLayout)) - return fieldLayout; - - // Otherwise, fall back to doing lookup using the linkage - // attached to the key, and its mangled name. - // - auto fieldLinkage = fieldKey->findDecoration<IRLinkageDecoration>(); - if(!fieldLinkage) - return nullptr; - auto mangledFieldName = fieldLinkage->getMangledName(); - - // In this case we fall back to a linear search over the fields. - // - for(auto ff : structTypeLayout->fields) + for(auto ff : structTypeLayout->getFieldLayoutAttrs()) { - if(mangledFieldName == getMangledName(ff->varDecl.getDecl()).getUnownedSlice() ) + if(ff->getFieldKey() == fieldKey) { - return ff; + return ff->getLayout(); } } } @@ -1357,22 +1340,22 @@ RefPtr<VarLayout> getFieldLayout( return nullptr; } -RefPtr<VarLayout> createSimpleVarLayout( +void buildSimpleVarLayout( + IRVarLayout::Builder* builder, SimpleLegalVarChain* varChain, - TypeLayout* typeLayout) + IRTypeLayout* typeLayout) { - if (!typeLayout) - return nullptr; - // We need to construct a layout for the new variable // that reflects both the type we have given it, as // well as all the offset information that has accumulated // along the chain of parent variables. - // TODO: this logic needs to propagate through semantics... - - RefPtr<VarLayout> varLayout = new VarLayout(); - varLayout->typeLayout = typeLayout; + // TODO: This logic doesn't currently handle semantics or + // other attributes that might have been present on the + // original variable layout. That is probably okay for now + // as the legalization logic does not apply to varying + // parameters (where resource types would be illegal anyway), + // but it is probably worth addressing sooner or later. // For most resource kinds, the register index/space to use should // be the sum along the entire chain of variables. @@ -1387,16 +1370,17 @@ RefPtr<VarLayout> createSimpleVarLayout( // the offset for `s` (10 texture registers) to get the final // binding to apply. // - for (auto rr : typeLayout->resourceInfos) + for (auto rr : typeLayout->getSizeAttrs()) { - auto resInfo = varLayout->findOrAddResourceInfo(rr.kind); + auto kind = rr->getResourceKind(); + auto resInfo = builder->findOrAddResourceInfo(kind); for (auto vv = varChain; vv; vv = vv->next) { - if (auto parentResInfo = vv->varLayout->FindResourceInfo(rr.kind)) + if (auto parentResInfo = vv->varLayout->findOffsetAttr(kind)) { - resInfo->index += parentResInfo->index; - resInfo->space += parentResInfo->space; + resInfo->offset += parentResInfo->getOffset(); + resInfo->space += parentResInfo->getSpace(); } } } @@ -1404,44 +1388,57 @@ RefPtr<VarLayout> createSimpleVarLayout( // As a special case, if the leaf variable doesn't hold an entry for // `RegisterSpace`, but at least one declaration in the chain *does*, // then we want to make sure that we add such an entry. - if (!varLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace)) + // + if (!builder->usesResourceKind(LayoutResourceKind::RegisterSpace)) { // Sum up contributions from all parents. UInt space = 0; for (auto vv = varChain; vv; vv = vv->next) { - if (auto parentResInfo = vv->varLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace)) + if (auto parentResInfo = vv->varLayout->findOffsetAttr(LayoutResourceKind::RegisterSpace)) { - space += parentResInfo->index; + space += parentResInfo->getOffset(); } } // If there were non-zero contributions, then add an entry to represent them. if (space) { - varLayout->findOrAddResourceInfo(LayoutResourceKind::RegisterSpace)->index = space; + builder->findOrAddResourceInfo(LayoutResourceKind::RegisterSpace)->offset = space; } } - - return varLayout; } +IRVarLayout* createSimpleVarLayout( + IRBuilder* irBuilder, + SimpleLegalVarChain* varChain, + IRTypeLayout* typeLayout) +{ + if (!typeLayout) + return nullptr; + IRVarLayout::Builder varLayoutBuilder(irBuilder, typeLayout); + buildSimpleVarLayout(&varLayoutBuilder, varChain, typeLayout); + return varLayoutBuilder.build(); +} -RefPtr<VarLayout> createVarLayout( +IRVarLayout* createVarLayout( + IRBuilder* irBuilder, LegalVarChain const& varChain, - TypeLayout* typeLayout) + IRTypeLayout* typeLayout) { if(!typeLayout) return nullptr; - auto varLayout = createSimpleVarLayout(varChain.primaryChain, typeLayout); + IRVarLayout::Builder varLayoutBuilder(irBuilder, typeLayout); + buildSimpleVarLayout(&varLayoutBuilder, varChain.primaryChain, typeLayout); - if(auto pendingDataTypeLayout = typeLayout->pendingDataTypeLayout) + if(auto pendingDataTypeLayout = typeLayout->getPendingDataTypeLayout()) { - varLayout->pendingVarLayout = createSimpleVarLayout(varChain.pendingChain, typeLayout); + varLayoutBuilder.setPendingVarLayout( + createSimpleVarLayout(irBuilder, varChain.pendingChain, typeLayout)); } - return varLayout; + return varLayoutBuilder.build(); } // diff --git a/source/slang/slang-legalize-types.h b/source/slang/slang-legalize-types.h index e92a9fc41..1201f669d 100644 --- a/source/slang/slang-legalize-types.h +++ b/source/slang/slang-legalize-types.h @@ -323,11 +323,11 @@ struct WrappedBufferPseudoType : LegalTypeImpl // -RefPtr<TypeLayout> getDerefTypeLayout( - TypeLayout* typeLayout); +IRTypeLayout* getDerefTypeLayout( + IRTypeLayout* typeLayout); -RefPtr<VarLayout> getFieldLayout( - TypeLayout* typeLayout, +IRVarLayout* getFieldLayout( + IRTypeLayout* typeLayout, IRInst* fieldKey); /// Represents a "chain" of variables leading to some leaf field. @@ -362,7 +362,7 @@ struct SimpleLegalVarChain SimpleLegalVarChain* next = nullptr; // The layout for the variable at this link in thain. - VarLayout* varLayout = nullptr; + IRVarLayout* varLayout = nullptr; }; /// A "chain" of variable declarations that can handle both primary and "pending" data. @@ -389,15 +389,6 @@ struct LegalVarChain // The chain of variables that represents the pending allocation. SimpleLegalVarChain* pendingChain = nullptr; - - // If the primary chain is non-empty, gets the variable at the leaf. - DeclRef<VarDeclBase> getLeafVarDeclRef() const - { - if(!primaryChain) - return DeclRef<VarDeclBase>(); - - return primaryChain->varLayout->varDecl; - } }; /// RAII type for adding a link to a `LegalVarChain` as needed. @@ -432,7 +423,7 @@ struct LegalVarChainLink : LegalVarChain {} /// Construct a chain that extends `parent` with `varLayout`, if it is non-null. - LegalVarChainLink(LegalVarChain const& parent, VarLayout* varLayout) + LegalVarChainLink(LegalVarChain const& parent, IRVarLayout* varLayout) : LegalVarChain(parent) { if( varLayout ) @@ -441,7 +432,7 @@ struct LegalVarChainLink : LegalVarChain primaryLink.varLayout = varLayout; primaryChain = &primaryLink; - if( auto pendingVarLayout = varLayout->pendingVarLayout ) + if( auto pendingVarLayout = varLayout->getPendingVarLayout() ) { pendingLink.next = parent.pendingChain; pendingLink.varLayout = pendingVarLayout; @@ -454,13 +445,15 @@ struct LegalVarChainLink : LegalVarChain SimpleLegalVarChain pendingLink; }; -RefPtr<VarLayout> createVarLayout( +IRVarLayout* createVarLayout( + IRBuilder* irBuilder, LegalVarChain const& varChain, - TypeLayout* typeLayout); + IRTypeLayout* typeLayout); -RefPtr<VarLayout> createSimpleVarLayout( +IRVarLayout* createSimpleVarLayout( + IRBuilder* irBuilder, SimpleLegalVarChain* varChain, - TypeLayout* typeLayout); + IRTypeLayout* typeLayout); // // The result of legalizing an IR value will be diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index cd398f4e7..113dcbfad 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -6714,6 +6714,278 @@ RefPtr<IRModule> TargetProgram::getOrCreateIRModuleForLayout(DiagnosticSink* sin return m_irModuleForLayout; } + /// Specialized IR generation context for when generating IR for layouts. +struct IRLayoutGenContext : IRGenContext +{ + IRLayoutGenContext(SharedIRGenContext* shared) + : IRGenContext(shared) + {} + + /// Cache for custom key instructions used for entry-point parameter layout information. + Dictionary<ParamDecl*, IRStructKey*> mapEntryPointParamToKey; +}; + + /// Lower an AST-level type layout to an IR-level type layout. +IRTypeLayout* lowerTypeLayout( + IRLayoutGenContext* context, + TypeLayout* typeLayout); + + /// Lower an AST-level variable layout to an IR-level variable layout. +IRVarLayout* lowerVarLayout( + IRLayoutGenContext* context, + VarLayout* varLayout); + + /// Shared code for most `lowerTypeLayout` cases. + /// + /// Handles copying of resource usage and pending data type layout + /// from the AST `typeLayout` to the specified `builder`. + /// +static IRTypeLayout* _lowerTypeLayoutCommon( + IRLayoutGenContext* context, + IRTypeLayout::Builder* builder, + TypeLayout* typeLayout) +{ + for( auto resInfo : typeLayout->resourceInfos ) + { + builder->addResourceUsage(resInfo.kind, resInfo.count); + } + + if( auto pendingTypeLayout = typeLayout->pendingDataTypeLayout ) + { + builder->setPendingTypeLayout( + lowerTypeLayout(context, pendingTypeLayout)); + } + + return builder->build(); +} + +IRTypeLayout* lowerTypeLayout( + IRLayoutGenContext* context, + TypeLayout* typeLayout) +{ + // TODO: We chould consider caching the layouts we create based on `typeLayout` + // and re-using them. This isn't strictly necessary because we emit the + // instructions as "hoistable" which should give us de-duplication, and it wouldn't + // help much until/unless the AST level gets less wasteful about how it computes layout. + + // We will use casting to detect if `typeLayout` is + // one of the cases that requires a dedicated sub-type + // of IR type layout. + // + if( auto paramGroupTypeLayout = as<ParameterGroupTypeLayout>(typeLayout) ) + { + IRParameterGroupTypeLayout::Builder builder(context->irBuilder); + + builder.setContainerVarLayout( + lowerVarLayout(context, paramGroupTypeLayout->containerVarLayout)); + builder.setElementVarLayout( + lowerVarLayout(context, paramGroupTypeLayout->elementVarLayout)); + builder.setOffsetElementTypeLayout( + lowerTypeLayout(context, paramGroupTypeLayout->offsetElementTypeLayout)); + + return _lowerTypeLayoutCommon(context, &builder, paramGroupTypeLayout); + } + else if( auto structTypeLayout = as<StructTypeLayout>(typeLayout) ) + { + IRStructTypeLayout::Builder builder(context->irBuilder); + + for( auto fieldLayout : structTypeLayout->fields ) + { + auto fieldDecl = fieldLayout->varDecl; + + IRStructKey* irFieldKey = nullptr; + if(auto paramDecl = as<ParamDecl>(fieldDecl) ) + { + // There is a subtle special case here. + // + // A `StructTypeLayout` might be used to represent + // the parameters of an entry point, and this is the + // one and only case where the "fields" being used + // might actually be `ParamDecl`s. + // + // The IR encoding of structure type layouts relies + // on using field "key" instructions to identify + // the fields, but these don't exist (by default) + // for function parameters. + // + // To get around this problem we will create key + // instructions to stand in for the entry-point parameters + // as needed when generating layout. + // + // We need to cache the generated keys on the context, + // so that if we run into another type layout for the + // same entry point we will re-use the same keys. + // + if( !context->mapEntryPointParamToKey.TryGetValue(paramDecl, irFieldKey) ) + { + irFieldKey = context->irBuilder->createStructKey(); + + // TODO: It might eventually be a good idea to attach a mangled + // name to the key we just generated (derived from the entry point + // and parameter name), even though parameters don't usually have + // linkage. + // + // Doing so would ensure that if we ever combined partial layout + // information from different modules they would agree on the key + // to use for entry-point parameters. + // + // For now this is a non-issue because both the creation and use + // of these keys will be local to a single `IREntryPointLayout`, + // and we don't support combination at a finer granularity than that. + + context->mapEntryPointParamToKey.Add(paramDecl, irFieldKey); + } + } + else + { + IRInst* irFieldKeyInst = getSimpleVal(context, + ensureDecl(context, fieldDecl)); + irFieldKey = as<IRStructKey>(irFieldKeyInst); + } + SLANG_ASSERT(irFieldKey); + + auto irFieldLayout = lowerVarLayout(context, fieldLayout); + builder.addField(irFieldKey, irFieldLayout); + } + + return _lowerTypeLayoutCommon(context, &builder, structTypeLayout); + } + else if( auto arrayTypeLayout = as<ArrayTypeLayout>(typeLayout) ) + { + auto irElementTypeLayout = lowerTypeLayout(context, arrayTypeLayout->elementTypeLayout); + IRArrayTypeLayout::Builder builder(context->irBuilder, irElementTypeLayout); + return _lowerTypeLayoutCommon(context, &builder, arrayTypeLayout); + } + else if( auto taggedUnionTypeLayout = as<TaggedUnionTypeLayout>(typeLayout) ) + { + IRTaggedUnionTypeLayout::Builder builder(context->irBuilder, taggedUnionTypeLayout->tagOffset); + + for( auto caseTypeLayout : taggedUnionTypeLayout->caseTypeLayouts ) + { + builder.addCaseTypeLayout( + lowerTypeLayout( + context, + caseTypeLayout)); + } + + return _lowerTypeLayoutCommon(context, &builder, taggedUnionTypeLayout); + } + else if( auto streamOutputTypeLayout = as<StreamOutputTypeLayout>(typeLayout) ) + { + auto irElementTypeLayout = lowerTypeLayout(context, streamOutputTypeLayout->elementTypeLayout); + + IRStreamOutputTypeLayout::Builder builder(context->irBuilder, irElementTypeLayout); + return _lowerTypeLayoutCommon(context, &builder, streamOutputTypeLayout); + } + else if( auto matrixTypeLayout = as<MatrixTypeLayout>(typeLayout) ) + { + // TODO: Our support for explicit layouts on matrix types is minimal, so whether + // or not we even include `IRMatrixTypeLayout` doesn't impact any behavior we + // currently test. + // + // Our handling of matrix types and their layout needs a complete overhaul, but + // that isn't something we can get to right away, so we'll just try to pass + // along this data as best we can for now. + + IRMatrixTypeLayout::Builder builder(context->irBuilder, matrixTypeLayout->mode); + return _lowerTypeLayoutCommon(context, &builder, matrixTypeLayout); + } + else + { + // If no special case applies we will build a generic `IRTypeLayout`. + // + IRTypeLayout::Builder builder(context->irBuilder); + return _lowerTypeLayoutCommon(context, &builder, typeLayout); + } +} + +IRVarLayout* lowerVarLayout( + IRLayoutGenContext* context, + VarLayout* varLayout) +{ + auto irTypeLayout = lowerTypeLayout(context, varLayout->typeLayout); + IRVarLayout::Builder irLayoutBuilder(context->irBuilder, irTypeLayout); + + for( auto resInfo : varLayout->resourceInfos ) + { + auto irResInfo = irLayoutBuilder.findOrAddResourceInfo(resInfo.kind); + irResInfo->offset = resInfo.index; + irResInfo->space = resInfo.space; + } + + if( auto pendingVarLayout = varLayout->pendingVarLayout ) + { + irLayoutBuilder.setPendingVarLayout( + lowerVarLayout(context, pendingVarLayout)); + } + + // We will only generate layout information with *either* a system-value + // semantic or a user-defined semantic, and we will always check for + // the system-value semantic first because the AST-level representation + // seems to encode both when a system-value semantic is present. + // + if( varLayout->systemValueSemantic.getLength() ) + { + irLayoutBuilder.setSystemValueSemantic( + varLayout->systemValueSemantic, + varLayout->systemValueSemanticIndex); + } + else if( varLayout->semanticName.getLength() ) + { + irLayoutBuilder.setUserSemantic( + varLayout->semanticName, + varLayout->semanticIndex); + } + + if( varLayout->stage != Stage::Unknown ) + { + irLayoutBuilder.setStage(varLayout->stage); + } + + return irLayoutBuilder.build(); +} + + /// Handle the lowering of an entry-point result layout to the IR +IRVarLayout* lowerEntryPointResultLayout( + IRLayoutGenContext* context, + VarLayout* layout) +{ + // The easy case is when there is a non-null `layout`, because we + // can handle it like any other var layout. + // + if(layout) + return lowerVarLayout(context, layout); + + // Right now the AST-level layout logic will leave a null layout + // for the result when an entry point has a `void` result type. + // + // TODO: We should fix this at the AST level instead of the IR, + // but doing so would impact reflection, where clients could + // be using a null check to test for a `void` result. + // + // As a workaround, we will create an empty type layout and + // an empty var layout that represents it, consistent with the + // way that a `void` value consumes no resources. + // + IRTypeLayout::Builder typeLayoutBuilder(context->irBuilder); + auto irTypeLayout = typeLayoutBuilder.build(); + IRVarLayout::Builder varLayoutBuilder(context->irBuilder, irTypeLayout); + return varLayoutBuilder.build(); +} + + /// Lower AST-level layout information for an entry point to the IR +IREntryPointLayout* lowerEntryPointLayout( + IRLayoutGenContext* context, + EntryPointLayout* entryPointLayout) +{ + auto irParamsLayout = lowerVarLayout(context, entryPointLayout->parametersLayout); + auto irResultLayout = lowerEntryPointResultLayout(context, entryPointLayout->resultLayout); + + return context->irBuilder->getEntryPointLayout( + irParamsLayout, + irResultLayout); +} + RefPtr<IRModule> TargetProgram::createIRModuleForLayout(DiagnosticSink* sink) { if(m_irModuleForLayout) @@ -6735,7 +7007,7 @@ RefPtr<IRModule> TargetProgram::createIRModuleForLayout(DiagnosticSink* sink) sink); auto sharedContext = &sharedContextStorage; - IRGenContext contextStorage(sharedContext); + IRLayoutGenContext contextStorage(sharedContext); auto context = &contextStorage; SharedIRBuilder sharedBuilderStorage; @@ -6768,9 +7040,11 @@ RefPtr<IRModule> TargetProgram::createIRModuleForLayout(DiagnosticSink* sink) // auto irVar = getSimpleVal(context, ensureDecl(context, varDecl)); + auto irLayout = lowerVarLayout(context, varLayout); + // Now attach the decoration to the variable. // - builder->addLayoutDecoration(irVar, varLayout); + builder->addLayoutDecoration(irVar, irLayout); } for( auto entryPointLayout : programLayout->entryPoints ) @@ -6785,14 +7059,19 @@ RefPtr<IRModule> TargetProgram::createIRModuleForLayout(DiagnosticSink* sink) builder->addImportDecoration(irFunc, getMangledName(funcDeclRef).getUnownedSlice()); } - builder->addLayoutDecoration(irFunc, entryPointLayout); + auto irEntryPointLayout = lowerEntryPointLayout(context, entryPointLayout); + + builder->addLayoutDecoration(irFunc, irEntryPointLayout); } for( auto taggedUnionTypeLayout : programLayout->taggedUnionTypeLayouts ) { auto taggedUnionType = taggedUnionTypeLayout->getType(); auto irType = lowerType(context, taggedUnionType); - builder->addLayoutDecoration(irType, taggedUnionTypeLayout); + + auto irTypeLayout = lowerTypeLayout(context, taggedUnionTypeLayout); + + builder->addLayoutDecoration(irType, irTypeLayout); } m_irModuleForLayout = irModule; diff --git a/source/slang/slang-type-layout.cpp b/source/slang/slang-type-layout.cpp index 807d0cab4..29ebe7de6 100644 --- a/source/slang/slang-type-layout.cpp +++ b/source/slang/slang-type-layout.cpp @@ -2,6 +2,7 @@ #include "slang-type-layout.h" #include "slang-syntax.h" +#include "slang-ir-insts.h" #include <assert.h> @@ -1356,73 +1357,110 @@ RefPtr<TypeLayout> applyOffsetToTypeLayout( return newTypeLayout; } -RefPtr<VarLayout> applyOffsetToVarLayout( - VarLayout* baseLayout, - VarLayout* offsetLayout) +IRTypeLayout* applyOffsetToTypeLayout( + IRBuilder* irBuilder, + IRTypeLayout* oldTypeLayout, + IRVarLayout* offsetVarLayout) { - RefPtr<VarLayout> adjustedLayout = new VarLayout(); - adjustedLayout->typeLayout = baseLayout->typeLayout; - adjustedLayout->varDecl = baseLayout->varDecl; - adjustedLayout->flags = baseLayout->flags; - adjustedLayout->semanticName = baseLayout->semanticName; - adjustedLayout->semanticIndex = baseLayout->semanticIndex; - adjustedLayout->systemValueSemantic = baseLayout->systemValueSemantic; - adjustedLayout->systemValueSemanticIndex = baseLayout->systemValueSemanticIndex; - adjustedLayout->stage = baseLayout->stage; + // The body of this function is derived from the AST case defined above. + // + // TODO: We shouldn't need this function at all because "offset" type + // layouts were only introduced as a legacy workaround for some bad choices + // in the reflection API. + // - if( auto basePendingLayout = baseLayout->pendingVarLayout ) + // There is no need to apply offsets if the old type and the offset + // don't share any resource infos in common. + bool anyHit = false; + for (auto oldResInfo : oldTypeLayout->getSizeAttrs()) { - if( auto offsetPendingLayout = offsetLayout->pendingVarLayout ) + if (auto offsetResInfo = offsetVarLayout->findOffsetAttr(oldResInfo->getResourceKind())) { - adjustedLayout->pendingVarLayout = applyOffsetToVarLayout( - basePendingLayout, - offsetPendingLayout); + anyHit = true; + break; } } - for( auto baseResInfo : baseLayout->resourceInfos ) + if (!anyHit) + return oldTypeLayout; + + if (auto oldStructTypeLayout = as<IRStructTypeLayout>(oldTypeLayout)) { - auto adjustedResInfo = baseResInfo; - if( auto offsetResInfo = offsetLayout->FindResourceInfo(baseResInfo.kind) ) + IRStructTypeLayout::Builder newStructTypeLayoutBuilder(irBuilder); + newStructTypeLayoutBuilder.addResourceUsageFrom(oldTypeLayout); + + for (auto oldFieldAttr : oldStructTypeLayout->getFieldLayoutAttrs()) { - adjustedResInfo.index += offsetResInfo->index; - adjustedResInfo.space += offsetResInfo->space; - } - adjustedLayout->resourceInfos.add(adjustedResInfo); - } + auto fieldKey = oldFieldAttr->getFieldKey(); + auto oldFieldLayout = oldFieldAttr->getLayout(); - return adjustedLayout; -} + IRVarLayout::Builder newFieldBuilder(irBuilder, oldFieldLayout->getTypeLayout()); + newFieldBuilder.cloneEverythingButOffsetsFrom(oldFieldLayout); -EntryPointLayout* EntryPointLayout::getAbsoluteLayout( - VarLayout* parentLayout) -{ - SLANG_ASSERT(parentLayout); + for (auto oldResInfo : oldFieldLayout->getOffsetAttrs()) + { + auto kind = oldResInfo->getResourceKind(); + auto newResInfo = newFieldBuilder.findOrAddResourceInfo(kind); + newResInfo->offset = oldResInfo->getOffset(); + newResInfo->space = oldResInfo->getSpace(); + if (auto offsetResInfo = offsetVarLayout->findOffsetAttr(kind)) + { + newResInfo->offset += offsetResInfo->getOffset(); + } + } - if(m_absoluteLayout) - return m_absoluteLayout; + newStructTypeLayoutBuilder.addField(fieldKey, newFieldBuilder.build()); + } - RefPtr<EntryPointLayout> adjustedLayout = new EntryPointLayout(); - adjustedLayout->entryPoint = this->entryPoint; - adjustedLayout->flags = this->flags; - adjustedLayout->parametersLayout = this->parametersLayout->getAbsoluteLayout(parentLayout); - adjustedLayout->profile = this->profile; - if( auto baseResultLayout = this->resultLayout ) + return newStructTypeLayoutBuilder.build(); + } + else { - adjustedLayout->resultLayout = baseResultLayout->getAbsoluteLayout(parentLayout); + // We can only effectively apply this offsetting to basic struct types, + // and so we won't even attempt it for anything else. This matches the + // AST implementation of this function, and shouldn't matter in the long + // run since we will remove the concept of offset type layouts from + // the IR. + // + return oldTypeLayout; } - - m_absoluteLayout = adjustedLayout; - return adjustedLayout; } -VarLayout* VarLayout::getAbsoluteLayout(VarLayout* parentAbsoluteLayout) +IRVarLayout* applyOffsetToVarLayout( + IRBuilder* irBuilder, + IRVarLayout* baseLayout, + IRVarLayout* offsetLayout) { - if( !m_absoluteLayout ) + IRVarLayout::Builder adjustedLayoutBuilder(irBuilder, baseLayout->getTypeLayout()); + adjustedLayoutBuilder.cloneEverythingButOffsetsFrom(baseLayout); + + if( auto basePendingLayout = baseLayout->getPendingVarLayout() ) { - m_absoluteLayout = applyOffsetToVarLayout(this, parentAbsoluteLayout); + if( auto offsetPendingLayout = offsetLayout->getPendingVarLayout() ) + { + adjustedLayoutBuilder.setPendingVarLayout( + applyOffsetToVarLayout( + irBuilder, + basePendingLayout, + offsetPendingLayout)); + } + } + + for( auto baseResInfo : baseLayout->getOffsetAttrs() ) + { + auto kind = baseResInfo->getResourceKind(); + auto adjustedResInfo = adjustedLayoutBuilder.findOrAddResourceInfo(kind); + adjustedResInfo->offset = baseResInfo->getOffset(); + adjustedResInfo->space = baseResInfo->getSpace(); + + if( auto offsetResInfo = offsetLayout->findOffsetAttr(baseResInfo->getResourceKind()) ) + { + adjustedResInfo->offset += offsetResInfo->getOffset(); + adjustedResInfo->space += offsetResInfo->getSpace(); + } } - return m_absoluteLayout; + + return adjustedLayoutBuilder.build(); } static bool _usesResourceKind(RefPtr<TypeLayout> typeLayout, LayoutResourceKind kind) diff --git a/source/slang/slang-type-layout.h b/source/slang/slang-type-layout.h index f63dd01c0..ac0ef3bd7 100644 --- a/source/slang/slang-type-layout.h +++ b/source/slang/slang-type-layout.h @@ -44,6 +44,13 @@ struct LayoutSize SLANG_ASSERT(size != RawValue(-1)); } + static LayoutSize fromRaw(RawValue raw) + { + LayoutSize size; + size.raw = raw; + return size; + } + static LayoutSize infinite() { LayoutSize result; @@ -469,21 +476,6 @@ public: } RefPtr<VarLayout> pendingVarLayout; - - - /// Get the "absolute" layout of this variable, if applicable. - /// - /// The `parentAbsoluteLayout` must be the absolute layout - /// of the parent of this variable. - /// - /// The absolute layout will be created once and cached on - /// future accesses; if `parentAbsoluteLayout` is not - /// consistent at different call sites an unexpected - /// layout could be returned. - /// - VarLayout* getAbsoluteLayout(VarLayout* parentAbsoluteLayout); - - RefPtr<VarLayout> m_absoluteLayout; }; // type layout for a variable that has a constant-buffer type @@ -510,16 +502,6 @@ public: // so that any fields (if the element type is a `struct`) // will be offset by the resource usage of the container. RefPtr<TypeLayout> offsetElementTypeLayout; - - // If the element type layout had any "pending" data, then - // as much of that data as possible will be flushed to - // fit into the overall layout of the parameter group. - // - // This field stores the offset information for where - // the pending data got stored relative to the start of - // the group. - // -// RefPtr<VarLayout> flushedDataVarLayout; }; // type layout for a variable that has a constant-buffer type @@ -604,12 +586,6 @@ public: // in the array above, rather than to the actual pointer, // so that we 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" - // instructions to map to fields. - // - Dictionary<IRInst*, RefPtr<VarLayout>> mapKeyToLayout; }; class GenericParamTypeLayout : public TypeLayout @@ -691,9 +667,9 @@ public: }; unsigned flags = 0; - EntryPointLayout* getAbsoluteLayout(VarLayout* parentLayout); +// EntryPointLayout* getAbsoluteLayout(VarLayout* parentLayout); - RefPtr<EntryPointLayout> m_absoluteLayout; +// RefPtr<EntryPointLayout> m_absoluteLayout; }; /// Reflection/layout information about a specialization parameter @@ -1170,10 +1146,20 @@ RefPtr<TypeLayout> applyOffsetToTypeLayout( RefPtr<TypeLayout> oldTypeLayout, RefPtr<VarLayout> offsetVarLayout); +struct IRBuilder; +struct IRTypeLayout; +struct IRVarLayout; + +IRTypeLayout* applyOffsetToTypeLayout( + IRBuilder* irBuilder, + IRTypeLayout* oldTypeLayout, + IRVarLayout* offsetVarLayout); + /// Create a layout like `baseLayout`, but offset by `offsetLayout` -RefPtr<VarLayout> applyOffsetToVarLayout( - VarLayout* baseLayout, - VarLayout* offsetLayout); +IRVarLayout* applyOffsetToVarLayout( + IRBuilder* irBuilder, + IRVarLayout* baseLayout, + IRVarLayout* offsetLayout); } |
