summaryrefslogtreecommitdiffstats
path: root/source/slang
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2018-12-07 13:31:06 -0800
committerGitHub <noreply@github.com>2018-12-07 13:31:06 -0800
commit135eaff6b892fc91a398714ddcf7ef377cd4cccb (patch)
treee69f30a4fadfdb834ea141c1ec9efc862ccc70d3 /source/slang
parentb0c2423f00b910f2f4d5010e6a04114112e294fd (diff)
Change how buffers are emitted (#741)
* Change how buffers are emitted This is a change with a lot of pieces, which can't always be separated out cleanly. I'm going to walk through them in what I hope is a logical order. The main goal of this change was to allow arrays of structured buffers to translate to Vulkan. Consider two declarations of structured buffers in HLSL/Slang: ```hlsl StructuredBuffer<X> single; StructuredBuffer<Y> multiple[10]; ``` The current translation logic was handling `single` by translating it into an *unnamed* GLSL `buffer` block like: ```glsl layout(std430) buffer _S1 { X single[]; }; ``` That syntax allows an expression like `single[i]` in Slang to be translated simply as `single[i]` in GLSL. But that naive translating doesn't work for `multiple`, since we need to declare a array of blocks in GLSL, which requires giving the whole thing a name: ```glsl layout(std430) buffer _S2 { Y _data[]; } multiple[10]; ``` Now a reference to `multiple[i][j]` in Slang needs to become `multiple[i]._data[j]` in GLSL. To avoid having way too many special cases around single structured buffers vs. arrays, it makes sense to allows emit things in the latter form, so that we instead lower `single` as: ```glsl layout(std430) buffer _S1 { X _data[]; } single; ``` So that now a reference to `single[i]` becomes `single._data[i]` in GLSL. Most of that can be handled in the standard library translation of the structured buffer indexing operations. The only wrinkle there is that there were some *old* special-case instructions in the IR intended to handle buffer load/store operations (these were added back when I was trying to keep the "VM" path working). These aren't really needed to have structured-buffer operations work; they can be handled as ordinary functions as far as the stdlib is concerned. I removed the old instructions. Along the way, it became clear that a few other cases follow the same pattern. Byte-addressed buffers are an obvious case. We were lowering HLSL/Slang: ```hlsl ByteAddressBuffer b; ... uint x = b.Load(0); ``` to GLSL like: ```glsl layout(std430) buffer _S1 { uint b[]; }; ... uint x = b[0]; ``` That logic would fail for arrays the same way that the structured buffer case was failing. The fix is the same: use named `buffer` blocks and then introduce an explicit `_data` field: ```glsl layout(std430) buffer _S1 { uint _data[]; } b; ... uint x = b._data[0]; ``` Just like with structured buffers, all of the VK translation for operations on byte-addressed buffers can be implemented directly in teh stdlib, so once the emit logic was changed it was just a matter of adding `._data` to a bunch of VK tranlsations. It turns out that arrays of constant buffers have more or less the same problem, and furthermore we have some problems with any code that directly uses the modern HLSL `ConstantBuffer<T>` type. Note: the emit logic around constant buffers sometimes refers to "parameter groups" because that is being used in the compiler as a catch-all term for constant buffers, texture buffers, and parameter blocks. The existing code was going out of its way to reproduce the way that constant buffer declarations are implicitly referenced in HLSL: ```hlsl cbuffer C { float f; } ... float tmp = f; // No reference to `C` here ``` This can be seen in the emit logic with the `isDerefBaseImplicit` function, which is used to take the internal IR representation for a reference to `f` (which is closer to the expression `(*C).f` or `C->f`) and leave off any reference to `C` so that we emit just `f`. That kind of logic just flat out doesn't work in some important cases. Arrays of constant buffers are a clear one: ```hlsl ConstantBuffer<X> cbArray[3]; ... X x = cbArray[0]; ``` There is no way to translate that to an ordinary `cbuffer` declaration at all. The same problem can be created without arrays, though: ```hlsl ConstantBuffer<X> singleCB; ... X x = singleCB; ``` The current strategy for translating constant buffers was translating `singleCB` into a `cbuffer` declaration that reproduced the fields of `X` as its members, which just wouldn't work: ```hlsl cbuffer singleCB { float f; // field of `X` } ... X x = singleCB; // ERROR: there is nothing named `singleCB` in this HLSL ``` The new strategy is more consistent. We still generate a `cbuffer` declaration for a single constant buffer, but we always give it a single field of the chosen element type: ```hlsl cbuffer singleCB { X singleCB; } ... X x = singleCB; // this works fine! ``` And in the array case we generate code that uses the explicit `ConstantBuffer<T>` type: ```hlsl ConstantBuffer<X> cbArray[3]; ... X x = cbArray[0]; ``` The GLSL output is more complicated because unlike with HLSL there is no implicit conversion from a uniform block to its element type (there is no notion of an element type). The array case thus needs a `_data` field similar to what we do for structured buffers: ```glsl layout(std140) uniform _S3 { X _data; } cbArray[3]; ... X x = cbArray[0]._data; ``` And then the non-array case needs to have a similar `_data` field for consistency: ```glsl layout(std140) uniform _S1 { X _data; } singleCB; ... X x = singleCB._data; ``` This is handled by inserting the necessary reference to `_data` whenever we dereference a constant buffer, either as part of a load instruction (loading from the whole CB as a pointer), or an `IRFieldAddress` instruction which forms a pointer into the CB (e.g., `&(singleCB->f)` becomes `singleCB._data.f`). The current emit logic handles `ParameterBlock<X>` differently from `ConstantBuffer<X>`, but really only to allow parameter blocks to be explicitly named in the output, while constant buffers were left implicit by default. Thus the only difference was a legacy one (from back when trying to exactly reproduce the HLSL text we got as input was considered an important goal), and the new approach to emitting constant buffers would get rid of it. I removed the separate logic for emitting `ParameterBlock<X>` and just let the handling for constant buffers deal with it. Note that any resource types inside of a `ParameterBlock<X>` would have been moved out as part of legalization, so that a parameter block is 100% equivalent to a constant buffer when it comes time to emit code. Unsurprisingly, changing the way we generate HLSL and GLSL output for all these buffer types meant that any tests that were directly comparing the output of `slangc` against `fxc`, `dxc`, or `glslang` broke. The basic approach to fixing the breakage in GLSL tests was to update the GLSL baseline to reflect the new output startegy. In some cases I used macros to name the various `_S<digits>` temporaries so that future renaming will hopefully be easier (it would be great if we auto-generated temporary names with a bit more context). There was one GLSL test (`tests/bugs/vk-structured-buffer-binding`) that was using raw GLSL expected output, and this was changed to use a GLSL baseline to generate SPIR-V for comparison. For HLSL tests we were sometimes running the same input file through `slangc` and `fxc`/`dxc`, and in these cases I macro-ized the various `cbuffer` declarations to generate different declarations depending on the compiler. I completely dropped the tests coming from the D3D SDK because they aren't providing much coverage, and updating them would change them so far from the original code that the purported benefit (using a body of existing shaders) would be lost. I also dropped the explicit matrix layout qualifiers in the `matrix-layout` test because the new output strategy breaks those for GLSL (you can't put matrix layout qualifiers on `struct` fields, and now the body of every constant buffer is inside a `struct`). This isn't as big of a loss as it seems, because our handling of those qualifiers wasn't really right to begin with. Slang users should only be setting the matrix layout mode globally (and we should probably switch to error out on the explicit qualifiers for now). The other thing that got dropped is tests involving `packoffset` modifiers. Slang already warns that it doesn't support these, and the way they were used in the test cases is actually misleading. For the binding/layout-related tests, the goal was to show that Slang reproduces the same layout as fxc, in which case explicitly enforcing a layout via `packoffset` seems like cheating (are we sure we enforced the layout fxc would have produced?). The real reason was that Slang used to emit explicit `packoffset` on *every* field of a `cbuffer` it would output, because of an `fxc` bug where you couldn't use `register` on textures/samplers declared inside a `cbuffer` unless *every* field in the `cbuffer` used a `register` or `packoffset` modifier. Slang hasn't required that behavior in a while because it now splits textures and samplers, and the one test case where we needed `packoffset` to work around the `fxc` bug in the baseline HLSL has been macro-ified even more to work around the bug. The amount of churn in the test cases is unfortunate, but it continues to point at the weakness of any testing strategy that checks for exact equivalent between Slang's output and that of other compilers. We need to keep working to replace these tests with better alternatives. In `check.cpp` there is logic to perform implicit dereferencing, so that if you write `obj.f` where `obj` is a `ConstantBuffer<X>` (or some other "pointer-like" type) and `f` is a field in `X`, then this effectively translates as `(*obj).f`. That is, we dereference the value of type `ConstantBuffer<X>` to get a value of type `X`, and then refer to the field of the `X` value. There was a problem where the logic to insert that kind of implicit dereference operation was using a reference (`auto& type = ...`) for the type of the expression being dereferenced, and then clobbering it. This would mean that an expression of type `ConstantBuffer<X>` would have its type overwritten to be just `X` and then codegen would break later on. I'm not sure how we haven't run into that before. The `array-of-buffers` test case was added to confirm that we now support arrays of constant, structured, and byte-address buffers for both DXIL and SPIR-V output. Okay, so that was a lot of stuff, but hopefully it is clear how this all works to make the output of the compiler more consistent and explicit, while also supporting the required new functionality. * fixup: review feedback
Diffstat (limited to 'source/slang')
-rw-r--r--source/slang/check.cpp11
-rw-r--r--source/slang/emit.cpp401
-rw-r--r--source/slang/hlsl.meta.slang72
-rw-r--r--source/slang/hlsl.meta.slang.h72
-rw-r--r--source/slang/ir-inst-defs.h4
-rw-r--r--source/slang/ir.cpp1
-rw-r--r--source/slang/type-layout.cpp11
-rw-r--r--source/slang/type-layout.h7
-rw-r--r--source/slang/vm.cpp66
9 files changed, 212 insertions, 433 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp
index e07bdf156..21e3b894b 100644
--- a/source/slang/check.cpp
+++ b/source/slang/check.cpp
@@ -7922,16 +7922,15 @@ namespace Slang
RefPtr<Expr> expr = inExpr;
for (;;)
{
- auto& type = expr->type;
- if (auto pointerLikeType = type->As<PointerLikeType>())
+ auto baseType = expr->type;
+ if (auto pointerLikeType = baseType->As<PointerLikeType>())
{
- type = QualType(pointerLikeType->elementType);
+ auto elementType = QualType(pointerLikeType->elementType);
+ elementType.IsLeftValue = baseType.IsLeftValue;
auto derefExpr = new DerefExpr();
derefExpr->base = expr;
- derefExpr->type = QualType(pointerLikeType->elementType);
-
- // TODO(tfoley): deal with l-value-ness here
+ derefExpr->type = elementType;
expr = derefExpr;
continue;
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index fb9968232..ba1b2177a 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -2383,7 +2383,6 @@ struct EmitVisitor
case kIROp_FieldAddress:
case kIROp_getElementPtr:
case kIROp_Specialize:
- case kIROp_BufferElementRef:
return true;
}
@@ -2536,25 +2535,6 @@ struct EmitVisitor
return true;
}
- bool isDerefBaseImplicit(
- EmitContext* /*context*/,
- IRInst* inst)
- {
- auto type = inst->getDataType();
-
- if(as<IRUniformParameterGroupType>(type) && !as<IRParameterBlockType>(type))
- {
- // TODO: we need to be careful here, because
- // HLSL shader model 6 allows these as explicit
- // types.
- return true;
- }
-
- return false;
- }
-
-
-
void emitIROperand(
EmitContext* ctx,
IRInst* inst,
@@ -3645,13 +3625,16 @@ struct EmitVisitor
IRFieldExtract* fieldExtract = (IRFieldExtract*) inst;
- if (!isDerefBaseImplicit(ctx, fieldExtract->getBase()))
- {
- auto prec = kEOp_Postfix;
- needClose = maybeEmitParens(outerPrec, prec);
+ auto prec = kEOp_Postfix;
+ needClose = maybeEmitParens(outerPrec, prec);
- emitIROperand(ctx, fieldExtract->getBase(), mode, leftSide(outerPrec, prec));
- emit(".");
+ auto base = fieldExtract->getBase();
+ emitIROperand(ctx, base, mode, leftSide(outerPrec, prec));
+ emit(".");
+ if(getTarget(ctx) == CodeGenTarget::GLSL
+ && as<IRUniformParameterGroupType>(base->getDataType()))
+ {
+ emit("_data.");
}
emit(getIRName(fieldExtract->getField()));
}
@@ -3663,15 +3646,17 @@ struct EmitVisitor
IRFieldAddress* ii = (IRFieldAddress*) inst;
- if (!isDerefBaseImplicit(ctx, ii->getBase()))
- {
- auto prec = kEOp_Postfix;
- needClose = maybeEmitParens(outerPrec, prec);
+ auto prec = kEOp_Postfix;
+ needClose = maybeEmitParens(outerPrec, prec);
- emitIROperand(ctx, ii->getBase(), mode, leftSide(outerPrec, prec));
- emit(".");
+ auto base = ii->getBase();
+ emitIROperand(ctx, base, mode, leftSide(outerPrec, prec));
+ emit(".");
+ if(getTarget(ctx) == CodeGenTarget::GLSL
+ && as<IRUniformParameterGroupType>(base->getDataType()))
+ {
+ emit("_data.");
}
-
emit(getIRName(ii->getField()));
}
break;
@@ -3774,7 +3759,15 @@ struct EmitVisitor
break;
case kIROp_Load:
- emitIROperand(ctx, inst->getOperand(0), mode, outerPrec);
+ {
+ auto base = inst->getOperand(0);
+ emitIROperand(ctx, base, mode, outerPrec);
+ if(getTarget(ctx) == CodeGenTarget::GLSL
+ && as<IRUniformParameterGroupType>(base->getDataType()))
+ {
+ emit("._data");
+ }
+ }
break;
case kIROp_Store:
@@ -3794,39 +3787,6 @@ struct EmitVisitor
}
break;
- case kIROp_BufferLoad:
- case kIROp_BufferElementRef:
- {
- auto prec = kEOp_Postfix;
- needClose = maybeEmitParens(outerPrec, prec);
-
- emitIROperand(ctx, inst->getOperand(0), mode, leftSide(outerPrec, prec));
- emit("[");
- emitIROperand(ctx, inst->getOperand(1), mode, kEOp_General);
- emit("]");
- }
- break;
-
- case kIROp_BufferStore:
- {
- auto precAssign = kEOp_Assign;
- needClose = maybeEmitParens(outerPrec, precAssign);
-
- auto outerPrecSubscript = precAssign;
- auto precSubscript = kEOp_Postfix;
- bool needCloseSubscript = maybeEmitParens(outerPrecSubscript, precSubscript);
-
- emitIROperand(ctx, inst->getOperand(0), mode, leftSide(outerPrecSubscript, precSubscript));
- emit("[");
- emitIROperand(ctx, inst->getOperand(1), mode, kEOp_General);
- emit("]");
- maybeCloseParens(needCloseSubscript);
-
- emit(" = ");
- emitIROperand(ctx, inst->getOperand(2), mode, rightSide(outerPrec, precAssign));
- }
- break;
-
case kIROp_GroupMemoryBarrierWithGroupSync:
emit("GroupMemoryBarrierWithGroupSync()");
break;
@@ -5618,63 +5578,19 @@ struct EmitVisitor
}
}
- void emitHLSLParameterBlock(
- EmitContext* ctx,
- IRGlobalVar* varDecl,
- IRParameterBlockType* type)
- {
- emit("cbuffer ");
-
- // Generate a dummy name for the block
- emit("_S");
- Emit(ctx->shared->uniqueIDCounter++);
-
- auto varLayout = getVarLayout(ctx, varDecl);
- SLANG_RELEASE_ASSERT(varLayout);
-
- EmitVarChain blockChain(varLayout);
-
- EmitVarChain containerChain = blockChain;
- EmitVarChain elementChain = blockChain;
-
- auto typeLayout = varLayout->typeLayout;
- if( auto parameterGroupTypeLayout = typeLayout.As<ParameterGroupTypeLayout>() )
- {
- containerChain = EmitVarChain(parameterGroupTypeLayout->containerVarLayout, &blockChain);
- elementChain = EmitVarChain(parameterGroupTypeLayout->elementVarLayout, &blockChain);
-
- typeLayout = parameterGroupTypeLayout->elementVarLayout->getTypeLayout();
- }
-
- emitHLSLRegisterSemantic(LayoutResourceKind::ConstantBuffer, &containerChain);
-
- emit("\n{\n");
- indent();
-
- auto elementType = type->getElementType();
-
-
- emitIRType(ctx, elementType, getIRName(varDecl));
-
- emitHLSLParameterGroupFieldLayoutSemantics(&elementChain);
- emit(";\n");
-
- dedent();
- emit("}\n");
- }
-
void emitHLSLParameterGroup(
EmitContext* ctx,
IRGlobalVar* varDecl,
IRUniformParameterGroupType* type)
{
- if(auto parameterBlockType = as<IRParameterBlockType>(type))
+ if(as<IRTextureBufferType>(type))
{
- emitHLSLParameterBlock(ctx, varDecl, parameterBlockType);
- return;
+ emit("tbuffer ");
+ }
+ else
+ {
+ emit("cbuffer ");
}
-
- emit("cbuffer ");
emit(getIRName(varDecl));
auto varLayout = getVarLayout(ctx, varDecl);
@@ -5701,111 +5617,37 @@ struct EmitVisitor
auto elementType = type->getElementType();
- if(auto structType = as<IRStructType>(elementType))
- {
- auto structTypeLayout = typeLayout.As<StructTypeLayout>();
- SLANG_RELEASE_ASSERT(structTypeLayout);
-
- UInt fieldIndex = 0;
- for(auto ff : structType->getFields())
- {
- // TODO: need a plan to deal with the case where the IR-level
- // `struct` type might not match the high-level type, so that
- // the numbering of fields is different.
- //
- // The right plan is probably to require that the lowering pass
- // create a fresh layout for any type/variable that it splits
- // in this fashion, so that the layout information it attaches
- // can always be assumed to apply to the actual instruciton.
- //
-
- auto fieldLayout = structTypeLayout->fields[fieldIndex++];
-
- auto fieldKey = ff->getKey();
- auto fieldType = ff->getFieldType();
-
- // Fields of `void` type aren't valid in HLSL/GLSL.
- //
- // TODO: legalization should get rid of any fields that have
- // empty, or effectively empty types (e.g., emptry structs
- // should be translated over to `void`).
- if(as<IRVoidType>(fieldType))
- continue;
-
- emitIRVarModifiers(ctx, fieldLayout, fieldKey, fieldType);
-
- emitIRType(ctx, fieldType, getIRName(fieldKey));
-
- emitHLSLParameterGroupFieldLayoutSemantics(fieldLayout, &elementChain);
-
- emit(";\n");
- }
- }
- else
- {
- // TODO: during legalization we should turn `ParameterGroup<X>` where `X`
- // is not a `struct` type into `ParameterGroup<S>` where `S` is defined
- // as something like `struct S { X _; };`
- //
- emit("/* unexpected */");
- }
+ emitIRType(ctx, elementType, getIRName(varDecl));
+ emit(";\n");
dedent();
emit("}\n");
}
- void emitGLSLParameterBlock(
- EmitContext* ctx,
- IRGlobalVar* varDecl,
- IRParameterBlockType* type)
+ void emitArrayBrackets(
+ EmitContext* ctx,
+ IRType* type)
{
- auto varLayout = getVarLayout(ctx, varDecl);
- SLANG_RELEASE_ASSERT(varLayout);
-
- EmitVarChain blockChain(varLayout);
-
- EmitVarChain containerChain = blockChain;
- EmitVarChain elementChain = blockChain;
+ SLANG_UNUSED(ctx);
- auto typeLayout = varLayout->typeLayout;
- if( auto parameterGroupTypeLayout = typeLayout.As<ParameterGroupTypeLayout>() )
+ if(auto arrayType = as<IRArrayType>(type))
{
- containerChain = EmitVarChain(parameterGroupTypeLayout->containerVarLayout, &blockChain);
- elementChain = EmitVarChain(parameterGroupTypeLayout->elementVarLayout, &blockChain);
-
- typeLayout = parameterGroupTypeLayout->elementVarLayout->getTypeLayout();
+ emit("[");
+ EmitVal(arrayType->getElementCount(), kEOp_General);
+ emit("]");
+ }
+ else if(auto unsizedArrayType = as<IRUnsizedArrayType>(type))
+ {
+ emit("[]");
}
-
- emitGLSLLayoutQualifier(LayoutResourceKind::DescriptorTableSlot, &containerChain);
- emit("layout(std140) uniform ");
-
- // Generate a dummy name for the block
- emit("_S");
- Emit(ctx->shared->uniqueIDCounter++);
-
- emit("\n{\n");
- indent();
-
- auto elementType = type->getElementType();
-
- emitIRType(ctx, elementType, getIRName(varDecl));
- emit(";\n");
-
- dedent();
- emit("};\n");
}
+
void emitGLSLParameterGroup(
EmitContext* ctx,
IRGlobalVar* varDecl,
IRUniformParameterGroupType* type)
{
- if(auto parameterBlockType = as<IRParameterBlockType>(type))
- {
- emitGLSLParameterBlock(ctx, varDecl, parameterBlockType);
- return;
- }
-
auto varLayout = getVarLayout(ctx, varDecl);
SLANG_RELEASE_ASSERT(varLayout);
@@ -5814,7 +5656,7 @@ struct EmitVisitor
EmitVarChain containerChain = blockChain;
EmitVarChain elementChain = blockChain;
- auto typeLayout = varLayout->typeLayout;
+ auto typeLayout = varLayout->typeLayout->unwrapArray();
if( auto parameterGroupTypeLayout = typeLayout.As<ParameterGroupTypeLayout>() )
{
containerChain = EmitVarChain(parameterGroupTypeLayout->containerVarLayout, &blockChain);
@@ -5841,71 +5683,28 @@ struct EmitVisitor
emit("layout(std140) uniform ");
}
- emit(getIRName(varDecl));
+ // Generate a dummy name for the block
+ emit("_S");
+ Emit(ctx->shared->uniqueIDCounter++);
emit("\n{\n");
indent();
auto elementType = type->getElementType();
- if(auto structType = as<IRStructType>(elementType))
- {
- auto structTypeLayout = typeLayout.As<StructTypeLayout>();
- SLANG_RELEASE_ASSERT(structTypeLayout);
-
- UInt fieldIndex = 0;
- for(auto ff : structType->getFields())
- {
- // TODO: need a plan to deal with the case where the IR-level
- // `struct` type might not match the high-level type, so that
- // the numbering of fields is different.
- //
- // The right plan is probably to require that the lowering pass
- // create a fresh layout for any type/variable that it splits
- // in this fashion, so that the layout information it attaches
- // can always be assumed to apply to the actual instruciton.
- //
-
- auto fieldLayout = structTypeLayout->fields[fieldIndex++];
-
- auto fieldKey = ff->getKey();
- auto fieldType = ff->getFieldType();
- if(as<IRVoidType>(fieldType))
- continue;
-
- // Note: we will emit matrix-layout modifiers here, but
- // we will refrain from emitting other modifiers that
- // might not be appropriate to the context (e.g., we
- // shouldn't go emitting `uniform` just because these
- // things are uniform...).
- //
- // TODO: we need a more refined set of modifiers that
- // we should allow on fields, because we might end
- // up supporting layout that isn't the default for
- // the given block type (e.g., something other than
- // `std140` for a uniform block).
- //
- emitIRMatrixLayoutModifiers(ctx, fieldLayout);
-
- emitIRType(ctx, fieldType, getIRName(fieldKey));
+ emitIRType(ctx, elementType, "_data");
+ emit(";\n");
-// emitHLSLParameterGroupFieldLayoutSemantics(layout, fieldLayout);
+ dedent();
+ emit("} ");
- emit(";\n");
- }
- }
- else
- {
- emit("/* unexpected */");
- }
+ emit(getIRName(varDecl));
- // TODO: we should consider always giving parameter blocks
- // names when outputting GLSL, since that shouldn't affect
- // the semantics of things, and will reduce the risk of
- // collisions in the global namespace...
+ // If the underlying variable was an array (or array of arrays, etc.)
+ // we need to emit all those array brackets here.
+ emitArrayBrackets(ctx, varDecl->getDataType()->getValueType());
- dedent();
- emit("};\n");
+ emit(";\n");
}
void emitIRParameterGroup(
@@ -6025,19 +5824,14 @@ struct EmitVisitor
auto elementType = structuredBufferType->getElementType();
- emitIRType(ctx, elementType, getIRName(varDecl) + "[]");
+ emitIRType(ctx, elementType, "_data[]");
emit(";\n");
dedent();
- emit("}");
+ emit("} ");
- // TODO: we need to consider the case where the type of the variable is
- // an *array* of structured buffers, in which case we need to declare
- // the block as an array too.
- //
- // The main challenge here is that then the block will have a name,
- // and also the field inside the block will have a name, so that when
- // the user had written `a[i][j]` we now need to emit `a[i].someName[j]`.
+ emit(getIRName(varDecl));
+ emitArrayBrackets(ctx, varDecl->getDataType()->getValueType());
emit(";\n");
}
@@ -6084,20 +5878,13 @@ struct EmitVisitor
emit("\n{\n");
indent();
- emit("uint ");
- emit(getIRName(varDecl));
- emit("[];\n");
+ emit("uint _data[];\n");
dedent();
- emit("}");
+ emit("} ");
- // TODO: we need to consider the case where the type of the variable is
- // an *array* of structured buffers, in which case we need to declare
- // the block as an array too.
- //
- // The main challenge here is that then the block will have a name,
- // and also the field inside the block will have a name, so that when
- // the user had written `a[i][j]` we now need to emit `a[i].someName[j]`.
+ emit(getIRName(varDecl));
+ emitArrayBrackets(ctx, varDecl->getDataType()->getValueType());
emit(";\n");
}
@@ -6129,6 +5916,16 @@ struct EmitVisitor
Emit("}\n");
}
+ // When a global shader parameter represents a "parameter group"
+ // (either a constant buffer or a parameter block with non-resource
+ // data in it), we will prefer to emit it as an ordinary `cbuffer`
+ // declaration or `uniform` block, even when emitting HLSL for
+ // D3D profiles that support the explicit `ConstantBuffer<T>` type.
+ //
+ // Alternatively, we could make this choice based on profile, and
+ // prefer `ConstantBuffer<T>` on profiles that support it and/or when
+ // the input code used that syntax.
+ //
if (auto paramBlockType = as<IRUniformParameterGroupType>(varType))
{
emitIRParameterGroup(
@@ -6140,8 +5937,31 @@ struct EmitVisitor
if(getTarget(ctx) == CodeGenTarget::GLSL)
{
- // When outputting GLSL, we need to transform any declaration of
- // a `*StructuredBuffer<T>` into an ordinary `buffer` declaration.
+ // There are a number of types that are (or can be)
+ // "first-class" in D3D HLSL, but are second-class in GLSL in
+ // that they require explicit global declarations for each value/object,
+ // and don't support declaration as ordinary variables.
+ //
+ // This includes constant buffers (`uniform` blocks) and well as
+ // structured and byte-address buffers (both mapping to `buffer` blocks).
+ //
+ // We intercept these types, and arrays thereof, to produce the required
+ // global declarations. This assumes that earlier "legalization" passes
+ // already performed the work of pulling fields with these types out of
+ // aggregates.
+ //
+ // Note: this also assumes that these types are not used as function
+ // parameters/results, local variables, etc. Additional legalization
+ // steps are required to guarantee these conditions.
+ //
+ if (auto paramBlockType = as<IRUniformParameterGroupType>(unwrapArray(varType)))
+ {
+ emitGLSLParameterGroup(
+ ctx,
+ varDecl,
+ paramBlockType);
+ return;
+ }
if( auto structuredBufferType = as<IRHLSLStructuredBufferTypeBase>(unwrapArray(varType)) )
{
emitIRStructuredBuffer_GLSL(
@@ -6150,9 +5970,6 @@ struct EmitVisitor
structuredBufferType);
return;
}
-
- // When outputting GLSL, we need to transform any declaration of
- // a `*ByteAddressBuffer<T>` into an ordinary `buffer` declaration.
if( auto byteAddressBufferType = as<IRByteAddressBufferTypeBase>(unwrapArray(varType)) )
{
emitIRByteAddressBuffer_GLSL(
@@ -6166,7 +5983,15 @@ struct EmitVisitor
// when outputting GLSL (well, except in the case where they
// actually *require* redeclaration...).
//
- // TODO: can we detect this more robustly?
+ // Note: these won't be variables the user declare explicitly
+ // in their code, but rather variables that we generated as
+ // part of legalizing the varying input/output signature of
+ // an entry point for GL/Vulkan.
+ //
+ // TODO: This could be handled more robustly by attaching an
+ // appropriate decoration to these variables to indicate their
+ // purpose.
+ //
if(getText(varDecl->mangledName).StartsWith("gl_"))
{
// The variable represents an OpenGL system value,
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index 950931fc2..98b50e574 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -18,26 +18,26 @@ __magic_type(HLSLByteAddressBufferType)
__intrinsic_type($(kIROp_HLSLByteAddressBufferType))
struct ByteAddressBuffer
{
- __target_intrinsic(glsl, "$1 = $0.length()")
+ __target_intrinsic(glsl, "$1 = $0._data.length()")
void GetDimensions(
out uint dim);
- __target_intrinsic(glsl, "$0[$1]")
+ __target_intrinsic(glsl, "$0._data[$1]")
uint Load(int location);
uint Load(int location, out uint status);
- __target_intrinsic(glsl, "uvec2($0[$1], $0[$1+1])")
+ __target_intrinsic(glsl, "uvec2($0._data[$1], $0._data[$1+1])")
uint2 Load2(int location);
uint2 Load2(int location, out uint status);
- __target_intrinsic(glsl, "uvec3($0[$1], $0[$1+1], $0[$1+2])")
+ __target_intrinsic(glsl, "uvec3($0._data[$1], $0._data[$1+1], $0._data[$1+2])")
uint3 Load3(int location);
uint3 Load3(int location, out uint status);
- __target_intrinsic(glsl, "uvec4($0[$1], $0[$1+1], $0[$1+2], $0[$1+3])")
+ __target_intrinsic(glsl, "uvec4($0._data[$1], $0._data[$1+1], $0._data[$1+2], $0._data[$1+3])")
uint4 Load4(int location);
uint4 Load4(int location, out uint status);
@@ -55,7 +55,11 @@ struct StructuredBuffer
T Load(int location);
T Load(int location, out uint status);
- __subscript(uint index) -> T { __intrinsic_op(bufferLoad) get; };
+ __subscript(uint index) -> T
+ {
+ __target_intrinsic(glsl, "$0._data[$1]")
+ get;
+ };
};
__generic<T>
@@ -105,133 +109,133 @@ struct $(item.name)
// Note(tfoley): supports all operations from `ByteAddressBuffer`
// TODO(tfoley): can this be made a sub-type?
- __target_intrinsic(glsl, "$1 = $0.length()")
+ __target_intrinsic(glsl, "$1 = $0._data.length()")
void GetDimensions(
out uint dim);
- __target_intrinsic(glsl, "$0[$1]")
+ __target_intrinsic(glsl, "$0._data[$1]")
uint Load(int location);
uint Load(int location, out uint status);
- __target_intrinsic(glsl, "uvec2($0[$1], $0[$1+4])")
+ __target_intrinsic(glsl, "uvec2($0._data[$1], $0._data[$1+4])")
uint2 Load2(int location);
uint2 Load2(int location, out uint status);
- __target_intrinsic(glsl, "uvec3($0[$1], $0[$1+4], $0[$1+8])")
+ __target_intrinsic(glsl, "uvec3($0._data[$1], $0._data[$1+4], $0._data[$1+8])")
uint3 Load3(int location);
uint3 Load3(int location, out uint status);
- __target_intrinsic(glsl, "uvec4($0[$1], $0[$1+4], $0[$1+8], $0[$1+12])")
+ __target_intrinsic(glsl, "uvec4($0._data[$1], $0._data[$1+4], $0._data[$1+8], $0._data[$1+12])")
uint4 Load4(int location);
uint4 Load4(int location, out uint status);
// Added operations:
- __target_intrinsic(glsl, "($3 = atomicAdd($0[$1], $2))")
+ __target_intrinsic(glsl, "($3 = atomicAdd($0._data[$1], $2))")
void InterlockedAdd(
UINT dest,
UINT value,
out UINT original_value);
- __target_intrinsic(glsl, "atomicAdd($0[$1], $2)")
+ __target_intrinsic(glsl, "atomicAdd($0._data[$1], $2)")
void InterlockedAdd(
UINT dest,
UINT value);
- __target_intrinsic(glsl, "($3 = atomicAnd($0[$1], $2))")
+ __target_intrinsic(glsl, "($3 = atomicAnd($0._data[$1], $2))")
void InterlockedAnd(
UINT dest,
UINT value,
out UINT original_value);
- __target_intrinsic(glsl, "atomicAnd($0[$1], $2)")
+ __target_intrinsic(glsl, "atomicAnd($0._data[$1], $2)")
void InterlockedAnd(
UINT dest,
UINT value);
- __target_intrinsic(glsl, "($4 = atomicCompSwap($0[$1], $2, $3))")
+ __target_intrinsic(glsl, "($4 = atomicCompSwap($0._data[$1], $2, $3))")
void InterlockedCompareExchange(
UINT dest,
UINT compare_value,
UINT value,
out UINT original_value);
- __target_intrinsic(glsl, "atomicCompSwap($0[$1], $2, $3)")
+ __target_intrinsic(glsl, "atomicCompSwap($0._data[$1], $2, $3)")
void InterlockedCompareStore(
UINT dest,
UINT compare_value,
UINT value);
- __target_intrinsic(glsl, "($3 = atomicExchange($0[$1], $2))")
+ __target_intrinsic(glsl, "($3 = atomicExchange($0._data[$1], $2))")
void InterlockedExchange(
UINT dest,
UINT value,
out UINT original_value);
- __target_intrinsic(glsl, "($3 = atomicMax($0[$1], $2))")
+ __target_intrinsic(glsl, "($3 = atomicMax($0._data[$1], $2))")
void InterlockedMax(
UINT dest,
UINT value,
out UINT original_value);
- __target_intrinsic(glsl, "atomicMax($0[$1], $2)")
+ __target_intrinsic(glsl, "atomicMax($0._data[$1], $2)")
void InterlockedMax(
UINT dest,
UINT value);
- __target_intrinsic(glsl, "($3 = atomicMin($0[$1], $2))")
+ __target_intrinsic(glsl, "($3 = atomicMin($0._data[$1], $2))")
void InterlockedMin(
UINT dest,
UINT value,
out UINT original_value);
- __target_intrinsic(glsl, "atomicMin($0[$1], $2)")
+ __target_intrinsic(glsl, "atomicMin($0._data[$1], $2)")
void InterlockedMin(
UINT dest,
UINT value);
- __target_intrinsic(glsl, "($3 = atomicOr($0[$1], $2))")
+ __target_intrinsic(glsl, "($3 = atomicOr($0._data[$1], $2))")
void InterlockedOr(
UINT dest,
UINT value,
out UINT original_value);
- __target_intrinsic(glsl, "atomicOr($0[$1], $2)")
+ __target_intrinsic(glsl, "atomicOr($0._data[$1], $2)")
void InterlockedOr(
UINT dest,
UINT value);
- __target_intrinsic(glsl, "($3 = atomicXor($0[$1], $2))")
+ __target_intrinsic(glsl, "($3 = atomicXor($0._data[$1], $2))")
void InterlockedXor(
UINT dest,
UINT value,
out UINT original_value);
- __target_intrinsic(glsl, "atomicXor($0[$1], $2)")
+ __target_intrinsic(glsl, "atomicXor($0._data[$1], $2)")
void InterlockedXor(
UINT dest,
UINT value);
- __target_intrinsic(glsl, "$0[$1] = $2")
+ __target_intrinsic(glsl, "$0._data[$1] = $2")
void Store(
uint address,
uint value);
- __target_intrinsic(glsl, "$0[$1] = $2.x, $0[$1+4] = $2.y")
+ __target_intrinsic(glsl, "$0._data[$1] = $2.x, $0._data[$1+4] = $2.y")
void Store2(
uint address,
uint2 value);
- __target_intrinsic(glsl, "$0[$1] = $2.x, $0[$1+4] = $2.y, $0[$1+8] = $2.z")
+ __target_intrinsic(glsl, "$0._data[$1] = $2.x, $0._data[$1+4] = $2.y, $0._data[$1+8] = $2.z")
void Store3(
uint address,
uint3 value);
- __target_intrinsic(glsl, "$0[$1] = $2.x, $0[$1+4] = $2.y, $0[$1+8] = $2.z, $0[$1+12] = $2.w")
+ __target_intrinsic(glsl, "$0._data[$1] = $2.x, $0._data[$1+4] = $2.y, $0._data[$1+8] = $2.z, $0._data[$1+12] = $2.w")
void Store4(
uint address,
uint4 value);
@@ -270,11 +274,11 @@ struct $(item.name)
T Load(int location);
T Load(int location, out uint status);
- __subscript(uint index) -> T
- {
- __intrinsic_op(bufferElementRef)
+ __subscript(uint index) -> T
+ {
+ __target_intrinsic(glsl, "$0._data[$1]")
ref;
- }
+ }
};
${{{{
diff --git a/source/slang/hlsl.meta.slang.h b/source/slang/hlsl.meta.slang.h
index 8d908c13d..ea21a0fde 100644
--- a/source/slang/hlsl.meta.slang.h
+++ b/source/slang/hlsl.meta.slang.h
@@ -24,26 +24,26 @@ SLANG_SPLICE(kIROp_HLSLByteAddressBufferType
SLANG_RAW(")\n")
SLANG_RAW("struct ByteAddressBuffer\n")
SLANG_RAW("{\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"$1 = $0.length()\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"$1 = $0._data.length()\")\n")
SLANG_RAW(" void GetDimensions(\n")
SLANG_RAW(" out uint dim);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"$0[$1]\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"$0._data[$1]\")\n")
SLANG_RAW(" uint Load(int location);\n")
SLANG_RAW("\n")
SLANG_RAW(" uint Load(int location, out uint status);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"uvec2($0[$1], $0[$1+1])\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"uvec2($0._data[$1], $0._data[$1+1])\")\n")
SLANG_RAW(" uint2 Load2(int location);\n")
SLANG_RAW("\n")
SLANG_RAW(" uint2 Load2(int location, out uint status);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"uvec3($0[$1], $0[$1+1], $0[$1+2])\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"uvec3($0._data[$1], $0._data[$1+1], $0._data[$1+2])\")\n")
SLANG_RAW(" uint3 Load3(int location);\n")
SLANG_RAW("\n")
SLANG_RAW(" uint3 Load3(int location, out uint status);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"uvec4($0[$1], $0[$1+1], $0[$1+2], $0[$1+3])\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"uvec4($0._data[$1], $0._data[$1+1], $0._data[$1+2], $0._data[$1+3])\")\n")
SLANG_RAW(" uint4 Load4(int location);\n")
SLANG_RAW("\n")
SLANG_RAW(" uint4 Load4(int location, out uint status);\n")
@@ -64,7 +64,11 @@ SLANG_RAW("\n")
SLANG_RAW(" T Load(int location);\n")
SLANG_RAW(" T Load(int location, out uint status);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __subscript(uint index) -> T { __intrinsic_op(bufferLoad) get; };\n")
+SLANG_RAW(" __subscript(uint index) -> T\n")
+SLANG_RAW(" {\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"$0._data[$1]\")\n")
+SLANG_RAW(" get;\n")
+SLANG_RAW(" };\n")
SLANG_RAW("};\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T>\n")
@@ -132,133 +136,133 @@ SLANG_RAW("{\n")
SLANG_RAW(" // Note(tfoley): supports all operations from `ByteAddressBuffer`\n")
SLANG_RAW(" // TODO(tfoley): can this be made a sub-type?\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"$1 = $0.length()\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"$1 = $0._data.length()\")\n")
SLANG_RAW(" void GetDimensions(\n")
SLANG_RAW(" out uint dim);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"$0[$1]\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"$0._data[$1]\")\n")
SLANG_RAW(" uint Load(int location);\n")
SLANG_RAW("\n")
SLANG_RAW(" uint Load(int location, out uint status);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"uvec2($0[$1], $0[$1+4])\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"uvec2($0._data[$1], $0._data[$1+4])\")\n")
SLANG_RAW(" uint2 Load2(int location);\n")
SLANG_RAW("\n")
SLANG_RAW(" uint2 Load2(int location, out uint status);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"uvec3($0[$1], $0[$1+4], $0[$1+8])\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"uvec3($0._data[$1], $0._data[$1+4], $0._data[$1+8])\")\n")
SLANG_RAW(" uint3 Load3(int location);\n")
SLANG_RAW("\n")
SLANG_RAW(" uint3 Load3(int location, out uint status);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"uvec4($0[$1], $0[$1+4], $0[$1+8], $0[$1+12])\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"uvec4($0._data[$1], $0._data[$1+4], $0._data[$1+8], $0._data[$1+12])\")\n")
SLANG_RAW(" uint4 Load4(int location);\n")
SLANG_RAW("\n")
SLANG_RAW(" uint4 Load4(int location, out uint status);\n")
SLANG_RAW("\n")
SLANG_RAW(" // Added operations:\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"($3 = atomicAdd($0[$1], $2))\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"($3 = atomicAdd($0._data[$1], $2))\")\n")
SLANG_RAW(" void InterlockedAdd(\n")
SLANG_RAW(" UINT dest,\n")
SLANG_RAW(" UINT value,\n")
SLANG_RAW(" out UINT original_value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"atomicAdd($0[$1], $2)\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"atomicAdd($0._data[$1], $2)\")\n")
SLANG_RAW(" void InterlockedAdd(\n")
SLANG_RAW(" UINT dest,\n")
SLANG_RAW(" UINT value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"($3 = atomicAnd($0[$1], $2))\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"($3 = atomicAnd($0._data[$1], $2))\")\n")
SLANG_RAW(" void InterlockedAnd(\n")
SLANG_RAW(" UINT dest,\n")
SLANG_RAW(" UINT value,\n")
SLANG_RAW(" out UINT original_value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"atomicAnd($0[$1], $2)\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"atomicAnd($0._data[$1], $2)\")\n")
SLANG_RAW(" void InterlockedAnd(\n")
SLANG_RAW(" UINT dest,\n")
SLANG_RAW(" UINT value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"($4 = atomicCompSwap($0[$1], $2, $3))\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"($4 = atomicCompSwap($0._data[$1], $2, $3))\")\n")
SLANG_RAW(" void InterlockedCompareExchange(\n")
SLANG_RAW(" UINT dest,\n")
SLANG_RAW(" UINT compare_value,\n")
SLANG_RAW(" UINT value,\n")
SLANG_RAW(" out UINT original_value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"atomicCompSwap($0[$1], $2, $3)\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"atomicCompSwap($0._data[$1], $2, $3)\")\n")
SLANG_RAW(" void InterlockedCompareStore(\n")
SLANG_RAW(" UINT dest,\n")
SLANG_RAW(" UINT compare_value,\n")
SLANG_RAW(" UINT value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"($3 = atomicExchange($0[$1], $2))\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"($3 = atomicExchange($0._data[$1], $2))\")\n")
SLANG_RAW(" void InterlockedExchange(\n")
SLANG_RAW(" UINT dest,\n")
SLANG_RAW(" UINT value,\n")
SLANG_RAW(" out UINT original_value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"($3 = atomicMax($0[$1], $2))\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"($3 = atomicMax($0._data[$1], $2))\")\n")
SLANG_RAW(" void InterlockedMax(\n")
SLANG_RAW(" UINT dest,\n")
SLANG_RAW(" UINT value,\n")
SLANG_RAW(" out UINT original_value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"atomicMax($0[$1], $2)\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"atomicMax($0._data[$1], $2)\")\n")
SLANG_RAW(" void InterlockedMax(\n")
SLANG_RAW(" UINT dest,\n")
SLANG_RAW(" UINT value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"($3 = atomicMin($0[$1], $2))\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"($3 = atomicMin($0._data[$1], $2))\")\n")
SLANG_RAW(" void InterlockedMin(\n")
SLANG_RAW(" UINT dest,\n")
SLANG_RAW(" UINT value,\n")
SLANG_RAW(" out UINT original_value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"atomicMin($0[$1], $2)\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"atomicMin($0._data[$1], $2)\")\n")
SLANG_RAW(" void InterlockedMin(\n")
SLANG_RAW(" UINT dest,\n")
SLANG_RAW(" UINT value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"($3 = atomicOr($0[$1], $2))\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"($3 = atomicOr($0._data[$1], $2))\")\n")
SLANG_RAW(" void InterlockedOr(\n")
SLANG_RAW(" UINT dest,\n")
SLANG_RAW(" UINT value,\n")
SLANG_RAW(" out UINT original_value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"atomicOr($0[$1], $2)\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"atomicOr($0._data[$1], $2)\")\n")
SLANG_RAW(" void InterlockedOr(\n")
SLANG_RAW(" UINT dest,\n")
SLANG_RAW(" UINT value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"($3 = atomicXor($0[$1], $2))\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"($3 = atomicXor($0._data[$1], $2))\")\n")
SLANG_RAW(" void InterlockedXor(\n")
SLANG_RAW(" UINT dest,\n")
SLANG_RAW(" UINT value,\n")
SLANG_RAW(" out UINT original_value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"atomicXor($0[$1], $2)\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"atomicXor($0._data[$1], $2)\")\n")
SLANG_RAW(" void InterlockedXor(\n")
SLANG_RAW(" UINT dest,\n")
SLANG_RAW(" UINT value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"$0[$1] = $2\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"$0._data[$1] = $2\")\n")
SLANG_RAW(" void Store(\n")
SLANG_RAW(" uint address,\n")
SLANG_RAW(" uint value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"$0[$1] = $2.x, $0[$1+4] = $2.y\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"$0._data[$1] = $2.x, $0._data[$1+4] = $2.y\")\n")
SLANG_RAW(" void Store2(\n")
SLANG_RAW(" uint address,\n")
SLANG_RAW(" uint2 value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"$0[$1] = $2.x, $0[$1+4] = $2.y, $0[$1+8] = $2.z\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"$0._data[$1] = $2.x, $0._data[$1+4] = $2.y, $0._data[$1+8] = $2.z\")\n")
SLANG_RAW(" void Store3(\n")
SLANG_RAW(" uint address,\n")
SLANG_RAW(" uint3 value);\n")
SLANG_RAW("\n")
-SLANG_RAW(" __target_intrinsic(glsl, \"$0[$1] = $2.x, $0[$1+4] = $2.y, $0[$1+8] = $2.z, $0[$1+12] = $2.w\")\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"$0._data[$1] = $2.x, $0._data[$1+4] = $2.y, $0._data[$1+8] = $2.z, $0._data[$1+12] = $2.w\")\n")
SLANG_RAW(" void Store4(\n")
SLANG_RAW(" uint address,\n")
SLANG_RAW(" uint4 value);\n")
@@ -306,11 +310,11 @@ SLANG_RAW("\n")
SLANG_RAW(" T Load(int location);\n")
SLANG_RAW(" T Load(int location, out uint status);\n")
SLANG_RAW("\n")
-SLANG_RAW("\t__subscript(uint index) -> T\n")
-SLANG_RAW("\t{\n")
-SLANG_RAW(" __intrinsic_op(bufferElementRef)\n")
+SLANG_RAW(" __subscript(uint index) -> T\n")
+SLANG_RAW(" {\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"$0._data[$1]\")\n")
SLANG_RAW(" ref;\n")
-SLANG_RAW("\t}\n")
+SLANG_RAW(" }\n")
SLANG_RAW("};\n")
SLANG_RAW("\n")
diff --git a/source/slang/ir-inst-defs.h b/source/slang/ir-inst-defs.h
index 8f997cbe2..09c11ed16 100644
--- a/source/slang/ir-inst-defs.h
+++ b/source/slang/ir-inst-defs.h
@@ -217,10 +217,6 @@ INST(Var, var, 0, 0)
INST(Load, load, 1, 0)
INST(Store, store, 2, 0)
-INST(BufferLoad, bufferLoad, 2, 0)
-INST(BufferStore, bufferStore, 3, 0)
-INST(BufferElementRef, bufferElementRef, 2, 0)
-
INST(FieldExtract, get_field, 2, 0)
INST(FieldAddress, get_field_addr, 2, 0)
diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp
index 599b02ea7..2f16f4ebc 100644
--- a/source/slang/ir.cpp
+++ b/source/slang/ir.cpp
@@ -3576,7 +3576,6 @@ namespace Slang
case kIROp_makeArray:
case kIROp_makeStruct:
case kIROp_Load: // We are ignoring the possibility of loads from bad addresses, or `volatile` loads
- case kIROp_BufferLoad:
case kIROp_FieldExtract:
case kIROp_FieldAddress:
case kIROp_getElement:
diff --git a/source/slang/type-layout.cpp b/source/slang/type-layout.cpp
index 8fc48fe4f..2d21d7aef 100644
--- a/source/slang/type-layout.cpp
+++ b/source/slang/type-layout.cpp
@@ -2276,6 +2276,17 @@ RefPtr<TypeLayout> CreateTypeLayout(
return typeLayout;
}
+RefPtr<TypeLayout> TypeLayout::unwrapArray()
+{
+ TypeLayout* typeLayout = this;
+
+ while(auto arrayTypeLayout = dynamic_cast<ArrayTypeLayout*>(typeLayout))
+ typeLayout = arrayTypeLayout->elementTypeLayout;
+
+ return typeLayout;
+}
+
+
RefPtr<GlobalGenericParamDecl> GenericParamTypeLayout::getGlobalGenericParamDecl()
{
auto declRefType = type->AsDeclRefType();
diff --git a/source/slang/type-layout.h b/source/slang/type-layout.h
index fa874cb80..6f6dad055 100644
--- a/source/slang/type-layout.h
+++ b/source/slang/type-layout.h
@@ -354,6 +354,13 @@ public:
info.count = count;
addResourceUsage(info);
}
+
+ /// "Unwrap" any layers of array-ness from this type layout.
+ ///
+ /// If this is an `ArrayTypeLayout`, returns the result of unwrapping the elemnt type layout.
+ /// Otherwise, returns this type layout.
+ ///
+ RefPtr<TypeLayout> unwrapArray();
};
typedef unsigned int VarLayoutFlags;
diff --git a/source/slang/vm.cpp b/source/slang/vm.cpp
index fa59a741b..0f79c763b 100644
--- a/source/slang/vm.cpp
+++ b/source/slang/vm.cpp
@@ -846,72 +846,6 @@ void resumeThread(
}
break;
- case kIROp_BufferLoad:
- {
- VMType type = decodeType(frame, &ip);
- UInt argCount = decodeUInt(&ip);
- void* argPtrs[16] = { 0 };
- for( UInt aa = 0; aa < argCount; ++aa )
- {
- void* argPtr = decodeOperandPtr<void>(frame, &ip);
- argPtrs[aa] = argPtr;
- }
-
- void* dest = decodeOperandPtr<void>(frame, &ip);
-
- char* bufferData = *(char**)argPtrs[0];
- uint32_t index = *(uint32_t*)argPtrs[1];
-
- auto size = type.getSize();
- char* elementData = bufferData + index*size;
- memcpy(dest, elementData, size);
- }
- break;
-
- case kIROp_BufferStore:
- {
- VMType resultType = decodeType(frame, &ip);
- /*UInt argCount = */decodeUInt(&ip);
-
- char* bufferData = decodeOperand<char*>(frame, &ip);
- uint32_t index = decodeOperand<uint32_t>(frame, &ip);
-
- auto srcPtrAndType = decodeOperandPtrAndType(frame, &ip);
- void* srcPtr = srcPtrAndType.ptr;
- VMType type = srcPtrAndType.type;
-
- auto size = type.getSize();
- char* elementData = bufferData + index*size;
- memcpy(elementData, srcPtr, size);
- }
- break;
-
- case kIROp_BufferElementRef:
- {
- VMType ptrType = decodeType(frame, &ip);
- VMType type = ((VMPtrTypeImpl*)ptrType.getImpl())->base;
-
- UInt argCount = decodeUInt(&ip);
- void* argPtrs[16] = { 0 };
- for( UInt aa = 0; aa < argCount; ++aa )
- {
- void* argPtr = decodeOperandPtr<void>(frame, &ip);
- argPtrs[aa] = argPtr;
- }
-
- void* dest = decodeOperandPtr<void>(frame, &ip);
-
- char* bufferData = *(char**)argPtrs[0];
- uint32_t index = *(uint32_t*)argPtrs[1];
-
- auto size = type.getSize();
- char* elementData = bufferData + index*size;
-
- *(void**)dest = elementData;
- }
- break;
-
-
case kIROp_Call:
{
VMType type = decodeType(frame, &ip);