summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2018-11-19 09:07:34 -0800
committerGitHub <noreply@github.com>2018-11-19 09:07:34 -0800
commit7e0c5ad677e04be4ecd2ce087d856ba26f1667a4 (patch)
treefd4168b307fff97e3811defe4813029b660ec697
parent0f67d92df9ca1f528b7a7fc8a8712aaea56f7115 (diff)
Add Vulkan cross-compilation for byte-address buffers (#721)
* Add Vulkan cross-compilation for byte-address buffers This covers `ByteAddressBuffer`, `RWByteAddressBuffer`, and `RasterizerOrderedByteAddressBuffer`. A declaration of any of these types translates to a GLSL `buffer` declaration with a single `uint` array of data. Most of the methods on these types then have straightforward translations to operations on the array. The overall translation is similar to what was already being done for structured buffers. While implementing GLSL translation for the various atomic (`Interlocked*`) methods, I discovered that some of these included declarations that aren't actually included in HLSL. I cleaned these up, including in the declarations of the global `Interlocked*` functions. The test case that is included here covers only the most basic functionality: `Load`, `Load2`, `Load4` and `Store`. We should try to back-fill tests for the remaining methods when we have time. Two large caveats with this work: 1. We don't handle arrays of byte-address buffers, just as we don't handle arrays of structured buffers. That will take additional work. 2. We don't handle byte-address (or structured) buffers being passed as function parameters, since the parameter would need to be declared as a bare `uint[]` array. * Fixup: don't lump raytracing acceleration structures in with buffers Raytracing acceleration structures share a common base class with byte-address buffers (they are both buffer resources without a specific element type), and I was mistakently matching on this base class in an attempt to have a catch-all that applied to all byte-address buffers. The fix here was to add a distinct base class for all byte-address buffers and catch that instead. * Fixup: typos
-rw-r--r--source/slang/emit.cpp71
-rw-r--r--source/slang/hlsl.meta.slang61
-rw-r--r--source/slang/hlsl.meta.slang.h61
-rw-r--r--source/slang/ir-inst-defs.h8
-rw-r--r--source/slang/ir.h7
-rw-r--r--tests/compute/byte-address-buffer.slang40
-rw-r--r--tests/compute/byte-address-buffer.slang.expected.txt4
7 files changed, 212 insertions, 40 deletions
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index 8038561d0..a3b99691d 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -5999,6 +5999,66 @@ struct EmitVisitor
emit(";\n");
}
+ void emitIRByteAddressBuffer_GLSL(
+ EmitContext* ctx,
+ IRGlobalVar* varDecl,
+ IRByteAddressBufferTypeBase* /* byteAddressBufferType */)
+ {
+ // TODO: A lot of this logic is copy-pasted from `emitIRStructuredBuffer_GLSL`.
+ // It might be worthwhile to share the common code to avoid regressions sneaking
+ // in when one or the other, but not both, gets updated.
+
+ // Shader storage buffer is an OpenGL 430 feature
+ //
+ // TODO: we should require either the extension or the version...
+ requireGLSLVersion(430);
+
+ Emit("layout(std430");
+
+ auto layout = getVarLayout(ctx, varDecl);
+ if (layout)
+ {
+ LayoutResourceKind kind = LayoutResourceKind::DescriptorTableSlot;
+ EmitVarChain chain(layout);
+
+ const UInt index = getBindingOffset(&chain, kind);
+ const UInt space = getBindingSpace(&chain, kind);
+
+ Emit(", binding = ");
+ Emit(index);
+ if (space)
+ {
+ Emit(", set = ");
+ Emit(space);
+ }
+ }
+
+ emit(") buffer ");
+
+ // Generate a dummy name for the block
+ emit("_S");
+ Emit(ctx->shared->uniqueIDCounter++);
+ emit("\n{\n");
+ indent();
+
+ emit("uint ");
+ emit(getIRName(varDecl));
+ emit("[];\n");
+
+ dedent();
+ 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(";\n");
+ }
+
void emitIRGlobalVar(
EmitContext* ctx,
IRGlobalVar* varDecl)
@@ -6048,6 +6108,17 @@ struct EmitVisitor
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(
+ ctx,
+ varDecl,
+ byteAddressBufferType);
+ return;
+ }
+
// We want to skip the declaration of any system-value variables
// when outputting GLSL (well, except in the case where they
// actually *require* redeclaration...).
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index 36cefc699..57c4e1324 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -18,19 +18,28 @@ __magic_type(HLSLByteAddressBufferType)
__intrinsic_type($(kIROp_HLSLByteAddressBufferType))
struct ByteAddressBuffer
{
+ __target_intrinsic(glsl, "$1 = $0.length()")
void GetDimensions(
out uint dim);
+ __target_intrinsic(glsl, "$0[$1]")
uint Load(int location);
+
uint Load(int location, out uint status);
+ __target_intrinsic(glsl, "uvec2($0[$1], $0[$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])")
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])")
uint4 Load4(int location);
+
uint4 Load4(int location, out uint status);
};
@@ -93,112 +102,136 @@ __magic_type(HLSL$(item.name)Type)
__intrinsic_type($(item.op))
struct $(item.name)
{
- // Note(tfoley): supports alll operations from `ByteAddressBuffer`
+ // Note(tfoley): supports all operations from `ByteAddressBuffer`
// TODO(tfoley): can this be made a sub-type?
+ __target_intrinsic(glsl, "$1 = $0.length()")
void GetDimensions(
out uint dim);
+ __target_intrinsic(glsl, "$0[$1]")
uint Load(int location);
+
uint Load(int location, out uint status);
+ __target_intrinsic(glsl, "uvec2($0[$1], $0[$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])")
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])")
uint4 Load4(int location);
+
uint4 Load4(int location, out uint status);
// Added operations:
+ __target_intrinsic(glsl, "($3 = atomicAdd($0[$1], $2))")
void InterlockedAdd(
UINT dest,
UINT value,
out UINT original_value);
+
+ __target_intrinsic(glsl, "atomicAdd($0[$1], $2)")
void InterlockedAdd(
UINT dest,
UINT value);
+ __target_intrinsic(glsl, "($3 = atomicAnd($0[$1], $2))")
void InterlockedAnd(
UINT dest,
UINT value,
out UINT original_value);
+
+ __target_intrinsic(glsl, "atomicAnd($0[$1], $2)")
void InterlockedAnd(
UINT dest,
UINT value);
+ __target_intrinsic(glsl, "($4 = atomicCompSwap($0[$1], $2, $3))")
void InterlockedCompareExchange(
UINT dest,
UINT compare_value,
UINT value,
out UINT original_value);
- void InterlockedCompareExchange(
- UINT dest,
- UINT compare_value,
- UINT value);
+ __target_intrinsic(glsl, "atomicCompSwap($0[$1], $2, $3)")
void InterlockedCompareStore(
UINT dest,
UINT compare_value,
UINT value);
- void InterlockedCompareStore(
- UINT dest,
- UINT compare_value);
+ __target_intrinsic(glsl, "($3 = atomicExchange($0[$1], $2))")
void InterlockedExchange(
UINT dest,
UINT value,
out UINT original_value);
- void InterlockedExchange(
- UINT dest,
- UINT value);
+ __target_intrinsic(glsl, "($3 = atomicMax($0[$1], $2))")
void InterlockedMax(
UINT dest,
UINT value,
out UINT original_value);
+
+ __target_intrinsic(glsl, "atomicMax($0[$1], $2)")
void InterlockedMax(
UINT dest,
UINT value);
+ __target_intrinsic(glsl, "($3 = atomicMin($0[$1], $2))")
void InterlockedMin(
UINT dest,
UINT value,
out UINT original_value);
+
+ __target_intrinsic(glsl, "atomicMin($0[$1], $2)")
void InterlockedMin(
UINT dest,
UINT value);
+ __target_intrinsic(glsl, "($3 = atomicOr($0[$1], $2))")
void InterlockedOr(
UINT dest,
UINT value,
out UINT original_value);
+
+ __target_intrinsic(glsl, "atomicOr($0[$1], $2)")
void InterlockedOr(
UINT dest,
UINT value);
+ __target_intrinsic(glsl, "($3 = atomicXor($0[$1], $2))")
void InterlockedXor(
UINT dest,
UINT value,
out UINT original_value);
+
+ __target_intrinsic(glsl, "atomicXor($0[$1], $2)")
void InterlockedXor(
UINT dest,
UINT value);
+ __target_intrinsic(glsl, "$0[$1] = $2")
void Store(
uint address,
uint value);
+ __target_intrinsic(glsl, "$0[$1] = $2.x, $0[$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")
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")
void Store4(
uint address,
uint4 value);
@@ -687,12 +720,6 @@ void InterlockedCompareStore(__ref int dest, int compare_value, int value);
__target_intrinsic(glsl, "$atomicCompSwap($A, $1, $2)")
void InterlockedCompareStore(__ref uint dest, uint compare_value, uint value);
-__target_intrinsic(glsl, "$atomicExchange($A, $1)")
-void InterlockedExchange(__ref int dest, int value);
-
-__target_intrinsic(glsl, "$atomicExchange($A, $1)")
-void InterlockedExchange(__ref uint dest, uint value);
-
__target_intrinsic(glsl, "($2 = $atomicExchange($A, $1))")
void InterlockedExchange(__ref int dest, int value, out int original_value);
diff --git a/source/slang/hlsl.meta.slang.h b/source/slang/hlsl.meta.slang.h
index 7194c9990..9b8205536 100644
--- a/source/slang/hlsl.meta.slang.h
+++ b/source/slang/hlsl.meta.slang.h
@@ -24,19 +24,28 @@ 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(" void GetDimensions(\n")
SLANG_RAW(" out uint dim);\n")
SLANG_RAW("\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"$0[$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(" 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(" 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(" uint4 Load4(int location);\n")
+SLANG_RAW("\n")
SLANG_RAW(" uint4 Load4(int location, out uint status);\n")
SLANG_RAW("};\n")
SLANG_RAW("\n")
@@ -120,112 +129,136 @@ SLANG_SPLICE(item.name
)
SLANG_RAW("\n")
SLANG_RAW("{\n")
-SLANG_RAW(" // Note(tfoley): supports alll operations from `ByteAddressBuffer`\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(" void GetDimensions(\n")
SLANG_RAW(" out uint dim);\n")
SLANG_RAW("\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"$0[$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(" 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(" 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(" 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(" 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(" 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(" 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(" 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(" 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(" void InterlockedCompareExchange(\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, \"atomicCompSwap($0[$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(" void InterlockedCompareStore(\n")
-SLANG_RAW(" UINT dest,\n")
-SLANG_RAW(" UINT compare_value);\n")
SLANG_RAW("\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"($3 = atomicExchange($0[$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(" void InterlockedExchange(\n")
-SLANG_RAW(" UINT dest,\n")
-SLANG_RAW(" UINT value);\n")
SLANG_RAW("\n")
+SLANG_RAW(" __target_intrinsic(glsl, \"($3 = atomicMax($0[$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(" 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(" 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(" 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(" 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(" 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(" 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(" 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(" 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(" 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(" 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(" void Store4(\n")
SLANG_RAW(" uint address,\n")
SLANG_RAW(" uint4 value);\n")
@@ -732,12 +765,6 @@ SLANG_RAW("\n")
SLANG_RAW("__target_intrinsic(glsl, \"$atomicCompSwap($A, $1, $2)\")\n")
SLANG_RAW("void InterlockedCompareStore(__ref uint dest, uint compare_value, uint value);\n")
SLANG_RAW("\n")
-SLANG_RAW("__target_intrinsic(glsl, \"$atomicExchange($A, $1)\")\n")
-SLANG_RAW("void InterlockedExchange(__ref int dest, int value);\n")
-SLANG_RAW("\n")
-SLANG_RAW("__target_intrinsic(glsl, \"$atomicExchange($A, $1)\")\n")
-SLANG_RAW("void InterlockedExchange(__ref uint dest, uint value);\n")
-SLANG_RAW("\n")
SLANG_RAW("__target_intrinsic(glsl, \"($2 = $atomicExchange($A, $1))\")\n")
SLANG_RAW("void InterlockedExchange(__ref int dest, int value, out int original_value);\n")
SLANG_RAW("\n")
diff --git a/source/slang/ir-inst-defs.h b/source/slang/ir-inst-defs.h
index 14318bc91..8f997cbe2 100644
--- a/source/slang/ir-inst-defs.h
+++ b/source/slang/ir-inst-defs.h
@@ -90,9 +90,11 @@ INST(Nop, nop, 0, 0)
/* UntypedBufferResourceType */
- INST(HLSLByteAddressBufferType, ByteAddressBuffer, 0, 0)
- INST(HLSLRWByteAddressBufferType, RWByteAddressBuffer, 0, 0)
- INST(HLSLRasterizerOrderedByteAddressBufferType, RasterizerOrderedByteAddressBuffer, 0, 0)
+ /* ByteAddressBufferTypeBase */
+ INST(HLSLByteAddressBufferType, ByteAddressBuffer, 0, 0)
+ INST(HLSLRWByteAddressBufferType, RWByteAddressBuffer, 0, 0)
+ INST(HLSLRasterizerOrderedByteAddressBufferType, RasterizerOrderedByteAddressBuffer, 0, 0)
+ INST_RANGE(ByteAddressBufferTypeBase, HLSLByteAddressBufferType, HLSLRasterizerOrderedByteAddressBufferType)
INST(RaytracingAccelerationStructureType, RaytracingAccelerationStructure, 0, 0)
INST_RANGE(UntypedBufferResourceType, HLSLByteAddressBufferType, RaytracingAccelerationStructureType)
diff --git a/source/slang/ir.h b/source/slang/ir.h
index 9c7637360..b3c690c26 100644
--- a/source/slang/ir.h
+++ b/source/slang/ir.h
@@ -787,9 +787,10 @@ SIMPLE_IR_TYPE(HLSLRWStructuredBufferType, HLSLStructuredBufferTypeBase)
SIMPLE_IR_TYPE(HLSLRasterizerOrderedStructuredBufferType, HLSLStructuredBufferTypeBase)
SIMPLE_IR_PARENT_TYPE(UntypedBufferResourceType, Type)
-SIMPLE_IR_TYPE(HLSLByteAddressBufferType, UntypedBufferResourceType)
-SIMPLE_IR_TYPE(HLSLRWByteAddressBufferType, UntypedBufferResourceType)
-SIMPLE_IR_TYPE(HLSLRasterizerOrderedByteAddressBufferType, UntypedBufferResourceType)
+SIMPLE_IR_PARENT_TYPE(ByteAddressBufferTypeBase, UntypedBufferResourceType)
+SIMPLE_IR_TYPE(HLSLByteAddressBufferType, ByteAddressBufferTypeBase)
+SIMPLE_IR_TYPE(HLSLRWByteAddressBufferType, ByteAddressBufferTypeBase)
+SIMPLE_IR_TYPE(HLSLRasterizerOrderedByteAddressBufferType, ByteAddressBufferTypeBase)
SIMPLE_IR_TYPE(HLSLAppendStructuredBufferType, HLSLStructuredBufferTypeBase)
SIMPLE_IR_TYPE(HLSLConsumeStructuredBufferType, HLSLStructuredBufferTypeBase)
diff --git a/tests/compute/byte-address-buffer.slang b/tests/compute/byte-address-buffer.slang
new file mode 100644
index 000000000..2efbeb630
--- /dev/null
+++ b/tests/compute/byte-address-buffer.slang
@@ -0,0 +1,40 @@
+// byte-address-buffer.slang
+
+//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute
+//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-vk -compute
+//TEST(compute, vulkan):COMPARE_COMPUTE_EX:-d3d12 -compute
+
+// Confirm cross-compilation of `(RW)ByteAddressBuffer`
+//
+// TODO: I'm only using `RWByteAddressBuffer` for now because I don't
+// know if `render-test` supports the non-UAV case.
+
+//TEST_INPUT:ubuffer(data=[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]):dxbinding(0),glbinding(0)
+RWByteAddressBuffer inputBuffer;
+
+//TEST_INPUT:ubuffer(data=[0 0 0 0]):dxbinding(1),glbinding(1),out
+RWByteAddressBuffer outputBuffer;
+
+void test(int val)
+{
+ uint tmp = val;
+
+ tmp = inputBuffer.Load(tmp * 4);
+
+ uint2 pair = inputBuffer.Load2(tmp * 4);
+ tmp = (pair.x + pair.y) & 0xF;
+
+ uint4 quad = inputBuffer.Load4(tmp * 4);
+ tmp = (quad.x + quad.y + quad.z + quad.w) & 0xF;
+
+ outputBuffer.Store(val * 4, tmp);
+}
+
+[numthreads(4, 1, 1)]
+void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
+{
+ uint tid = dispatchThreadID.x;
+
+ int val = int(tid);
+ test(val);
+} \ No newline at end of file
diff --git a/tests/compute/byte-address-buffer.slang.expected.txt b/tests/compute/byte-address-buffer.slang.expected.txt
new file mode 100644
index 000000000..ba948e8bb
--- /dev/null
+++ b/tests/compute/byte-address-buffer.slang.expected.txt
@@ -0,0 +1,4 @@
+A
+2
+A
+2