From 9919c823938ae929b16efac9d507f6d5eb122bf4 Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Mon, 6 Nov 2017 10:37:27 -0800 Subject: Parameter blocks (#245) * Rename existing ParameterBlock to ParameterGroup We are planning to add a new `ParameterBlock` type, which maps to the notion of a "parameter block" as used in the Spire research work. Unfortunately, the compiler codebase already uses the term `ParameterBlock` as catch-all to encompass all of HLSL `cbuffer`/`tbuffer` and GLSL `uniform`/`buffer`/`in`/`out` blocks (all of which are lexical `{}`-enclosed blocks that define parameters...). This change instead renames all of the existing concepts over to `ParameterGroup`, which isn't an ideal name, but at least doesn't directly overlap the new terminology or any existing terminology. The new `ParameterBlockType` case will probably be a subclass of `ParameterGroupType`, since it is a logical extension of the underlying concept. * Add Shader Model 5.1 profiles The HLSL `register(..., space0)` syntax is only allowed on "SM5.1" and later profiles (which is supported by the newer version of `d3dcompiler_47.dll` that comes with the Win10 SDK, but not the older version of `d3dcompiler_47.dll` - good luck figuring out which you have!). This change adds those profiles to our master list of profiles, and nothing else. * First pass at support for `ParameterBlock` - Add the type declaration in stdlib - Add a special case of `ParameterGroupType` for parameter blocks - Handle parameter blocks in type layout (currently handling them identically to constant buffers for now, which isn't going to be right in the long term) - Add an IR pass that basically replaces `ParameterBlock` with `T` - Eventually this should replace it with either `T` or `ConstantBuffer`, depending on whether the layout that was computed required a constant buffer to hold any "free" uniforms - Add first stab at an IR pass to "scalarize" global variables using aggregate types with resources inside. - This currently only applies to global variables, so it won't handle things passed through functions, or used as local variables - It also only supports cases where the references to the original variable are always references to its fields, and not the whole value itself - Add a single test case that technically passes with this level of support, but probably isn't very representative of what we need from the feature * Fold parameter-block desugaring into a more complete "type legalization" pass The basic problem that was arising is that once you desugar `ParameterBlock` into `T`, you then need todeal with splitting `T` into its constituent fields if it contains any resource types. Handling those transformations by following the usual use-def chains wasn't really helping, because you might need systematic rewriting that can really only be handled bottom-up. This change adds a new pass that is intended to perform multiple kinds of type "legalization" at once: - It will turn `ParameterBlock` into `T` - It may at some point also convert `ConstantBuffer` into `T` as well - It will turn an value of an aggregate type that contains resources into N different values (one per field) - As a result of this, it will also deal with AOS-to-SOA conversion of these types Legalization is applied to *every* function/instruction/value, so that it can make large-scale changes that would be tough to manage with a work list. This pass needs to be run *after* generics have been fully specialized, so that we know we are always dealing with fully concrete types, so that their legalization for a given target is completely known. This is still work in progress; there's more to be done to get this working with all our test cases, and finish the remaining `ParameterBlock` work. * Improve binding/layout information when using parameter blocks - When doing type layout for a parameter block, don't include the resources consumed by the element type in the resource usage for the parameter block - Note that this is pretty much identical to how a `ConstantBuffer` does not report any `LayoutResourceKind::Uniform` usage, except that `ParameterBlock` is *also* going to hide underlying texture/sampler reigster usage - The one exception here is that any nested items that use up entire `space`s or `set`s those need to be exposed in the resource usage of the parent (I don't have a test for this) - When type legalization needs to scalarize things, it must propagate layout information down to the new leaf variables. In general, the register/index for a new leaf parameter should be the sum of the offsets for all of the parent variables along the "chain" from the original variable down to the leaf (we aren't dealing with arrays here just yet). - When type legalization decides to eliminate a pointer(-like) type (e.g., desugar `ParameterBlock` over to `T`), actually deal with that in terms of the `LegalVal`s created, so that we can know to turn a `load` into a no-op when applied to a value that got indirection removed. - Hack up the "complex" parameter-block test so that it actually passes (the big hack here is that the HLSL baseline is using names that are generated by the IR, and are unlikely to be stable as we add/remove transformations). - Note: I can't make these be compute tests right now, because regsiter spaces/sets are a feature of D3D12/Vulkan, and our test runner isn't using those APIs. --- source/slang/emit.cpp | 130 +++++++++++++++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 54 deletions(-) (limited to 'source/slang/emit.cpp') diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index 056454845..d95946204 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -3316,7 +3316,7 @@ struct EmitVisitor { // Don't emit a declaration that was only generated implicitly, for // the purposes of semantic checking. - if(decl->HasModifier()) + if(decl->HasModifier()) return; Emit("struct "); @@ -3498,7 +3498,7 @@ struct EmitVisitor return varLayout; } - void emitHLSLParameterBlockFieldLayoutSemantics( + void emitHLSLParameterGroupFieldLayoutSemantics( RefPtr layout, RefPtr fieldLayout) { @@ -3528,13 +3528,13 @@ struct EmitVisitor } } - void emitHLSLParameterBlockDecl( + void emitHLSLParameterGroupDecl( RefPtr varDecl, - RefPtr parameterBlockType, + RefPtr parameterGroupType, RefPtr layout) { // The data type that describes where stuff in the constant buffer should go - RefPtr dataType = parameterBlockType->elementType; + RefPtr dataType = parameterGroupType->elementType; // We expect/require the data type to be a user-defined `struct` type auto declRefType = dataType->As(); @@ -3545,22 +3545,22 @@ struct EmitVisitor SLANG_RELEASE_ASSERT(layout); // We expect the layout to be for a structured type... - RefPtr bufferLayout = layout->typeLayout.As(); + RefPtr bufferLayout = layout->typeLayout.As(); SLANG_RELEASE_ASSERT(bufferLayout); RefPtr structTypeLayout = bufferLayout->elementTypeLayout.As(); SLANG_RELEASE_ASSERT(structTypeLayout); - if( auto constantBufferType = parameterBlockType->As() ) + if( auto constantBufferType = parameterGroupType->As() ) { Emit("cbuffer "); } - else if( auto textureBufferType = parameterBlockType->As() ) + else if( auto textureBufferType = parameterGroupType->As() ) { Emit("tbuffer "); } - if( auto reflectionNameModifier = varDecl->FindModifier() ) + if( auto reflectionNameModifier = varDecl->FindModifier() ) { Emit(" "); emitName(reflectionNameModifier->nameAndLoc); @@ -3587,7 +3587,7 @@ struct EmitVisitor SLANG_RELEASE_ASSERT(fieldLayout->varDecl.GetName() == field.GetName()); // Emit explicit layout annotations for every field - emitHLSLParameterBlockFieldLayoutSemantics(layout, fieldLayout); + emitHLSLParameterGroupFieldLayoutSemantics(layout, fieldLayout); emitVarDeclInit(field); @@ -3696,13 +3696,13 @@ struct EmitVisitor } } - void emitGLSLParameterBlockDecl( + void emitGLSLParameterGroupDecl( RefPtr varDecl, - RefPtr parameterBlockType, + RefPtr parameterGroupType, RefPtr layout) { // The data type that describes where stuff in the constant buffer should go - RefPtr dataType = parameterBlockType->elementType; + RefPtr dataType = parameterGroupType->elementType; // We expect/require the data type to be a user-defined `struct` type auto declRefType = dataType->As(); @@ -3714,7 +3714,7 @@ struct EmitVisitor { auto typeLayout = layout->typeLayout; - if (auto bufferLayout = typeLayout.As()) + if (auto bufferLayout = typeLayout.As()) { typeLayout = bufferLayout->elementTypeLayout; } @@ -3729,19 +3729,19 @@ struct EmitVisitor EmitModifiers(varDecl); // Emit an apprpriate declaration keyword based on the kind of block - if (parameterBlockType->As()) + if (parameterGroupType->As()) { Emit("uniform"); } - else if (parameterBlockType->As()) + else if (parameterGroupType->As()) { Emit("in"); } - else if (parameterBlockType->As()) + else if (parameterGroupType->As()) { Emit("out"); } - else if (parameterBlockType->As()) + else if (parameterGroupType->As()) { Emit("buffer"); } @@ -3751,7 +3751,7 @@ struct EmitVisitor Emit("uniform"); } - if( auto reflectionNameModifier = varDecl->FindModifier() ) + if( auto reflectionNameModifier = varDecl->FindModifier() ) { Emit(" "); emitName(reflectionNameModifier->nameAndLoc); @@ -3789,19 +3789,19 @@ struct EmitVisitor Emit(";\n"); } - void emitParameterBlockDecl( + void emitParameterGroupDecl( RefPtr varDecl, - RefPtr parameterBlockType, + RefPtr parameterGroupType, RefPtr layout) { switch(context->shared->target) { case CodeGenTarget::HLSL: - emitHLSLParameterBlockDecl(varDecl, parameterBlockType, layout); + emitHLSLParameterGroupDecl(varDecl, parameterGroupType, layout); break; case CodeGenTarget::GLSL: - emitGLSLParameterBlockDecl(varDecl, parameterBlockType, layout); + emitGLSLParameterGroupDecl(varDecl, parameterGroupType, layout); break; default: @@ -3843,9 +3843,9 @@ struct EmitVisitor // // TODO(tfoley): there might be a better way to detect this, e.g., // with an attribute that gets attached to the variable declaration. - if (auto parameterBlockType = decl->type->As()) + if (auto parameterGroupType = decl->type->As()) { - emitParameterBlockDecl(decl, parameterBlockType, layout); + emitParameterGroupDecl(decl, parameterGroupType, layout); return; } @@ -4145,7 +4145,7 @@ emitDeclImpl(decl, nullptr); if(auto decoration = inst->findDecoration()) { auto decl = decoration->decl; - if (auto reflectionNameMod = decl->FindModifier()) + if (auto reflectionNameMod = decl->FindModifier()) { return getText(reflectionNameMod->nameAndLoc.name); } @@ -4492,7 +4492,7 @@ emitDeclImpl(decl, nullptr); // because they aren't allowed as types for temporary // variables. auto type = inst->getType(); - if(type->As()) + if(type->As()) { // TODO: we need to be careful here, because // HLSL shader model 6 allows these as explicit @@ -4518,7 +4518,7 @@ emitDeclImpl(decl, nullptr); { auto type = inst->getType(); - if(type->As()) + if(type->As()) { // TODO: we need to be careful here, because // HLSL shader model 6 allows these as explicit @@ -5812,10 +5812,10 @@ emitDeclImpl(decl, nullptr); } } - void emitHLSLParameterBlock( + void emitHLSLParameterGroup( EmitContext* ctx, IRGlobalVar* varDecl, - UniformParameterBlockType* type) + UniformParameterGroupType* type) { emit("cbuffer "); emit(getIRName(varDecl)); @@ -5832,9 +5832,9 @@ emitDeclImpl(decl, nullptr); auto elementType = type->getElementType(); auto typeLayout = layout->typeLayout; - if( auto parameterBlockTypeLayout = typeLayout.As() ) + if( auto parameterGroupTypeLayout = typeLayout.As() ) { - typeLayout = parameterBlockTypeLayout->elementTypeLayout; + typeLayout = parameterGroupTypeLayout->elementTypeLayout; } if(auto declRefType = elementType->As()) @@ -5864,7 +5864,7 @@ emitDeclImpl(decl, nullptr); auto fieldType = GetType(ff); emitIRType(ctx, fieldType, getIRName(ff)); - emitHLSLParameterBlockFieldLayoutSemantics(layout, fieldLayout); + emitHLSLParameterGroupFieldLayoutSemantics(layout, fieldLayout); emit(";\n"); } @@ -5878,10 +5878,10 @@ emitDeclImpl(decl, nullptr); emit("}\n"); } - void emitGLSLParameterBlock( + void emitGLSLParameterGroup( EmitContext* ctx, IRGlobalVar* varDecl, - UniformParameterBlockType* type) + UniformParameterGroupType* type) { auto layout = getVarLayout(ctx, varDecl); assert(layout); @@ -5909,9 +5909,9 @@ emitDeclImpl(decl, nullptr); auto elementType = type->getElementType(); auto typeLayout = layout->typeLayout; - if( auto parameterBlockTypeLayout = typeLayout.As() ) + if( auto parameterGroupTypeLayout = typeLayout.As() ) { - typeLayout = parameterBlockTypeLayout->elementTypeLayout; + typeLayout = parameterGroupTypeLayout->elementTypeLayout; } if(auto declRefType = elementType->As()) @@ -5941,7 +5941,7 @@ emitDeclImpl(decl, nullptr); auto fieldType = GetType(ff); emitIRType(ctx, fieldType, getIRName(ff)); -// emitHLSLParameterBlockFieldLayoutSemantics(layout, fieldLayout); +// emitHLSLParameterGroupFieldLayoutSemantics(layout, fieldLayout); emit(";\n"); } @@ -5960,19 +5960,19 @@ emitDeclImpl(decl, nullptr); emit("};\n"); } - void emitIRParameterBlock( + void emitIRParameterGroup( EmitContext* ctx, IRGlobalVar* varDecl, - UniformParameterBlockType* type) + UniformParameterGroupType* type) { switch (ctx->shared->target) { case CodeGenTarget::HLSL: - emitHLSLParameterBlock(ctx, varDecl, type); + emitHLSLParameterGroup(ctx, varDecl, type); break; case CodeGenTarget::GLSL: - emitGLSLParameterBlock(ctx, varDecl, type); + emitGLSLParameterGroup(ctx, varDecl, type); break; } } @@ -5990,7 +5990,7 @@ emitDeclImpl(decl, nullptr); { case kIROp_ConstantBufferType: case kIROp_TextureBufferType: - emitIRParameterBlock(ctx, varDecl, (IRUniformBufferType*) varType); + emitIRParameterGroup(ctx, varDecl, (IRUniformBufferType*) varType); return; default: @@ -6033,9 +6033,9 @@ emitDeclImpl(decl, nullptr); auto varType = allocatedType->getValueType(); // auto addressSpace = allocatedType->getAddressSpace(); - if (auto paramBlockType = varType->As()) + if (auto paramBlockType = varType->As()) { - emitIRParameterBlock( + emitIRParameterGroup( ctx, varDecl, paramBlockType); @@ -6305,7 +6305,7 @@ StructTypeLayout* getGlobalStructLayout( { return gs.Ptr(); } - else if( auto globalConstantBufferLayout = globalScopeLayout.As() ) + else if( auto globalConstantBufferLayout = globalScopeLayout.As() ) { // TODO: the `cbuffer` case really needs to be emitted very // carefully, but that is beyond the scope of what a simple rewriter @@ -6338,6 +6338,9 @@ StructTypeLayout* getGlobalStructLayout( } } +void legalizeTypes( + IRModule* module); + String emitEntryPoint( EntryPointRequest* entryPoint, ProgramLayout* programLayout, @@ -6401,20 +6404,37 @@ String emitEntryPoint( // so that we "just" need to specialize it as needed for the // specific target and entry point in use. // + // The first pass is to extract the IR code of the entry point, + // and any other symbols it references. At the same time, + // we go ahead and select the target-specific version of + // any such functions if they are available. We also go + // ahead and apply the layout information (from `programLayout`) + // to the IR code (which previously had no layout). + // + // Note: it is important that we extract a *copy* of all the + // relevant IR, so that transformations we make for one + // entry point (or target) don't mess up the IR used for other + // entry points (targets). + // auto lowered = specializeIRForEntryPoint( entryPoint, programLayout, target); - // debugging: + // If the user specified the flag that they want us to dump + // IR, then do it here, for the target-specific, but + // un-specialized IR. if (translationUnit->compileRequest->shouldDumpIR) { dumpIR(lowered); } - // TODO: we should apply some guaranteed transformations here, - // to eliminate constructs that aren't legal downstream (e.g. generics). - + // Next, we need to ensure that the code we emit for + // the target doesn't contain any operations that would + // be illegal on the target platform. For example, + // none of our target supports generics, or interfaces, + // so we need to specialize those away. + // specializeGenerics(lowered); // Debugging code for IR transformations... @@ -6424,10 +6444,12 @@ String emitEntryPoint( fprintf(stderr, "###\n"); #endif - // - // TODO: Need to decide whether to do these before or after - // target-specific legalization steps. Currently I've folded - // legalization into the specialization above. + // After we've fully specialized all generics, and + // "devirtualized" all the calls through interfaces, + // we need to ensure that the code only uses types + // that are legal on the chosen target. + // + legalizeTypes(lowered); // TODO: do we want to emit directly from IR, or translate the // IR back into AST for emission? -- cgit v1.2.3