diff options
Diffstat (limited to 'source')
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); } |
