summaryrefslogtreecommitdiffstats
path: root/source/slang
diff options
context:
space:
mode:
authorkaizhangNV <149626564+kaizhangNV@users.noreply.github.com>2024-12-12 16:50:44 -0600
committerGitHub <noreply@github.com>2024-12-12 14:50:44 -0800
commit78c9bd1c2fbd55889e62a2032e9bc96684ced3b5 (patch)
tree63332e647aa597450b642751c8c6b2fd7f66439e /source/slang
parentb4e63d7bc44fc969d24202fc51a774378a489294 (diff)
Bit extract (#5847)
* promoting bitfield extraction and insertion to become intrinsics for internal compiler use * removing duplicate intrinsics from glsl.meta.slang * refactor: update function signatures of bitfield extraction and insertion to use uint as the parameter type for offset and bits. --------- Co-authored-by: Nate Morrical <natemorrical@gmail.com> Co-authored-by: Yong He <yonghe@outlook.com>
Diffstat (limited to 'source/slang')
-rw-r--r--source/slang/core.meta.slang17
-rw-r--r--source/slang/glsl.meta.slang162
-rw-r--r--source/slang/slang-emit-c-like.cpp376
-rw-r--r--source/slang/slang-emit-c-like.h7
-rw-r--r--source/slang/slang-emit-glsl.cpp39
-rw-r--r--source/slang/slang-emit-glsl.h3
-rw-r--r--source/slang/slang-emit-spirv.cpp60
-rw-r--r--source/slang/slang-ir-inst-defs.h3
-rw-r--r--source/slang/slang-ir-insts.h4
-rw-r--r--source/slang/slang-ir.cpp34
10 files changed, 543 insertions, 162 deletions
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index 3a7df8e7a..625f8f608 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -2714,6 +2714,23 @@ __generic<T, U>
__intrinsic_op($(kIROp_Reinterpret))
T reinterpret(U value);
+/// `bitfieldInsert` inserts the bits least significant bits of `insert` into base at `offset` offset.
+/// The returned value will have bits [offset, offset + bits + 1] taken from [0, bits - 1] of `insert`
+/// and all other bits taken directly from the corresponding bits of `base`.
+__generic<T>
+[__readNone]
+[__unsafeForceInlineEarly]
+__intrinsic_op($(kIROp_BitfieldInsert))
+T bitfieldInsert(T base, T insert, uint offset, uint bits);
+
+/// `bitfieldExtract` extracts a subset of the bits of `value` and
+/// returns it in the least significant bits of the result. The range of bits extracted is [offset, offset + bits - 1].
+__generic<T>
+[__readNone]
+[__unsafeForceInlineEarly]
+__intrinsic_op($(kIROp_BitfieldExtract))
+T bitfieldExtract(T value, uint offset, uint bits);
+
/// Use an otherwise unused value
/// This can be used to silence the warning about returning before initializing an out paramter.
__generic<T>
diff --git a/source/slang/glsl.meta.slang b/source/slang/glsl.meta.slang
index 1d91930e4..361a956f2 100644
--- a/source/slang/glsl.meta.slang
+++ b/source/slang/glsl.meta.slang
@@ -1153,168 +1153,6 @@ public void imulExtended(highp vector<int,N> x, highp vector<int,N> y, out highp
[__readNone]
[ForceInline]
[require(cpp_cuda_glsl_hlsl_spirv, GLSL_400)]
-public int bitfieldExtract(int value, int offset, int bits)
-{
- __target_switch
- {
- case glsl: __intrinsic_asm "bitfieldExtract";
- case spirv: return spirv_asm {
- result:$$int = OpBitFieldSExtract $value $offset $bits
- };
- default:
- return int(uint(value >> offset) & ((1u << bits) - 1));
- }
-}
-
-__generic<let N:int>
-[__readNone]
-[ForceInline]
-[require(cpp_cuda_glsl_hlsl_spirv, GLSL_400)]
-public vector<int,N> bitfieldExtract(vector<int,N> value, int offset, int bits)
-{
- __target_switch
- {
- case glsl: __intrinsic_asm "bitfieldExtract";
- case spirv: return spirv_asm {
- result:$$vector<int,N> = OpBitFieldSExtract $value $offset $bits
- };
- default:
- vector<int,N> result;
- [ForceUnroll]
- for (int i = 0; i < N; ++i)
- {
- result[i] = bitfieldExtract(value[i], offset, bits);
- }
- return result;
- }
-}
-
-[__readNone]
-[ForceInline]
-[require(cpp_cuda_glsl_hlsl_spirv, GLSL_400)]
-public uint bitfieldExtract(uint value, int offset, int bits)
-{
- __target_switch
- {
- case glsl: __intrinsic_asm "bitfieldExtract";
- case spirv: return spirv_asm {
- result:$$uint = OpBitFieldUExtract $value $offset $bits
- };
- default:
- return (value >> offset) & ((1u << bits) - 1);
- }
-}
-
-__generic<let N:int>
-[__readNone]
-[ForceInline]
-[require(cpp_cuda_glsl_hlsl_spirv, GLSL_400)]
-public vector<uint,N> bitfieldExtract(vector<uint,N> value, int offset, int bits)
-{
- __target_switch
- {
- case glsl: __intrinsic_asm "bitfieldExtract";
- case spirv: return spirv_asm {
- result:$$vector<uint,N> = OpBitFieldUExtract $value $offset $bits
- };
- default:
- vector<uint,N> result;
- [ForceUnroll]
- for (int i = 0; i < N; ++i)
- {
- result[i] = bitfieldExtract(value[i], offset, bits);
- }
- return result;
- }
-}
-
-[__readNone]
-[ForceInline]
-[require(cpp_cuda_glsl_hlsl_spirv, GLSL_400)]
-public uint bitfieldInsert(uint base, uint insert, int offset, int bits)
-{
- __target_switch
- {
- case glsl: __intrinsic_asm "bitfieldInsert";
- case spirv: return spirv_asm {
- result:$$uint = OpBitFieldInsert $base $insert $offset $bits
- };
- default:
- uint clearMask = ~(((1u << bits) - 1u) << offset);
- uint clearedBase = base & clearMask;
- uint maskedInsert = (insert & ((1u << bits) - 1u)) << offset;
- return clearedBase | maskedInsert;
- }
-}
-
-__generic<let N:int>
-[__readNone]
-[ForceInline]
-[require(cpp_cuda_glsl_hlsl_spirv, GLSL_400)]
-public vector<uint,N> bitfieldInsert(vector<uint,N> base, vector<uint,N> insert, int offset, int bits)
-{
- __target_switch
- {
- case glsl: __intrinsic_asm "bitfieldInsert";
- case spirv: return spirv_asm {
- result:$$vector<uint,N> = OpBitFieldInsert $base $insert $offset $bits
- };
- default:
- vector<uint,N> result;
- [ForceUnroll]
- for (int i = 0; i < N; ++i)
- {
- result[i] = bitfieldInsert(base[i], insert[i], offset, bits);
- }
- return result;
- }
-}
-
-[__readNone]
-[ForceInline]
-[require(cpp_cuda_glsl_hlsl_spirv, GLSL_400)]
-public int bitfieldInsert(int base, int insert, int offset, int bits)
-{
- __target_switch
- {
- case glsl: __intrinsic_asm "bitfieldInsert";
- case spirv: return spirv_asm {
- result:$$int = OpBitFieldInsert $base $insert $offset $bits
- };
- default:
- uint clearMask = ~(((1u << bits) - 1u) << offset);
- uint clearedBase = base & clearMask;
- uint maskedInsert = (insert & ((1u << bits) - 1u)) << offset;
- return clearedBase | maskedInsert;
- }
-}
-
-__generic<let N:int>
-[__readNone]
-[ForceInline]
-[require(cpp_cuda_glsl_hlsl_spirv, GLSL_400)]
-public vector<int,N> bitfieldInsert(vector<int,N> base, vector<int,N> insert, int offset, int bits)
-{
- __target_switch
- {
- case glsl: __intrinsic_asm "bitfieldInsert";
- case spirv: return spirv_asm {
- result:$$vector<int,N> = OpBitFieldInsert $base $insert $offset $bits
- };
- default:
- vector<int,N> result;
- [ForceUnroll]
- for (int i = 0; i < N; ++i)
- {
- result[i] = bitfieldInsert(base[i], insert[i], offset, bits);
- }
- return result;
- }
-}
-
-[__readNone]
-[ForceInline]
-[require(cpp_cuda_glsl_hlsl_spirv, GLSL_400)]
public int bitfieldReverse(highp int value)
{
__target_switch
diff --git a/source/slang/slang-emit-c-like.cpp b/source/slang/slang-emit-c-like.cpp
index 50fb6655e..f38adc67e 100644
--- a/source/slang/slang-emit-c-like.cpp
+++ b/source/slang/slang-emit-c-like.cpp
@@ -2862,6 +2862,16 @@ void CLikeSourceEmitter::defaultEmitInstExpr(IRInst* inst, const EmitOpInfo& inO
m_writer->emit(")");
break;
}
+ case kIROp_BitfieldExtract:
+ {
+ emitBitfieldExtractImpl(inst);
+ break;
+ }
+ case kIROp_BitfieldInsert:
+ {
+ emitBitfieldInsertImpl(inst);
+ break;
+ }
case kIROp_PackAnyValue:
{
m_writer->emit("packAnyValue<");
@@ -3839,6 +3849,372 @@ void CLikeSourceEmitter::emitFuncDecorationsImpl(IRFunc* func)
}
}
+bool CLikeSourceEmitter::tryGetIntInfo(IRType* elementType, bool& isSigned, int& bitWidth)
+{
+ Slang::IROp type = elementType->getOp();
+ if (!(type >= kIROp_Int8Type && type <= kIROp_UInt64Type))
+ return false;
+ isSigned = (type >= kIROp_Int8Type && type <= kIROp_Int64Type);
+
+ Slang::IROp stype = (isSigned) ? type : Slang::IROp(type - 4);
+ bitWidth = 8 << (stype - kIROp_Int8Type);
+ return true;
+}
+
+void CLikeSourceEmitter::emitVecNOrScalar(
+ IRVectorType* vectorType,
+ std::function<void()> emitComponentLogic)
+{
+ if (vectorType)
+ {
+ int N = int(getIntVal(vectorType->getElementCount()));
+ Slang::IRType* elementType = vectorType->getElementType();
+
+ // Special handling required for CUDA target
+ if (isCUDATarget(getTargetReq()))
+ {
+ m_writer->emit("make_");
+
+ switch (elementType->getOp())
+ {
+ case kIROp_Int8Type:
+ m_writer->emit("char");
+ break;
+ case kIROp_Int16Type:
+ m_writer->emit("short");
+ break;
+ case kIROp_IntType:
+ m_writer->emit("int");
+ break;
+ case kIROp_Int64Type:
+ m_writer->emit("longlong");
+ break;
+ case kIROp_UInt8Type:
+ m_writer->emit("uchar");
+ break;
+ case kIROp_UInt16Type:
+ m_writer->emit("ushort");
+ break;
+ case kIROp_UIntType:
+ m_writer->emit("uint");
+ break;
+ case kIROp_UInt64Type:
+ m_writer->emit("ulonglong");
+ break;
+ default:
+ SLANG_ABORT_COMPILATION("Unhandled type emitting CUDA vector");
+ }
+
+ m_writer->emitRawText(std::to_string(N).c_str());
+ }
+
+ // In other languages, we can output the Slang vector type directly
+ else
+ {
+ emitType(vectorType);
+ }
+
+ m_writer->emit("(");
+ for (int i = 0; i < N; ++i)
+ {
+ emitType(elementType);
+ m_writer->emit("(");
+ emitComponentLogic();
+ m_writer->emit(")");
+ if (i != N - 1)
+ m_writer->emit(", ");
+ }
+ m_writer->emit(")");
+ }
+ else
+ {
+ m_writer->emit("(");
+ emitComponentLogic();
+ m_writer->emit(")");
+ }
+}
+
+String CLikeSourceEmitter::_emitLiteralOneWithType(int bitWidth)
+{
+ if (getTarget() == CodeGenTarget::WGSL)
+ {
+ if (bitWidth != 32)
+ {
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unexpected bit width");
+ return String();
+ }
+ else
+ {
+ String one;
+ one = "u32(1)";
+ return one;
+ }
+ }
+
+ String one;
+ switch (bitWidth)
+ {
+ case 8:
+ one = "uint8_t(1)";
+ break;
+ case 16:
+ one = "uint16_t(1)";
+ break;
+ case 32:
+ one = "uint32_t(1)";
+ break;
+ case 64:
+ one = "uint64_t(1)";
+ break;
+ default:
+ SLANG_DIAGNOSE_UNEXPECTED(getSink(), SourceLoc(), "unexpected bit width");
+ }
+ return one;
+}
+
+void CLikeSourceEmitter::emitBitfieldExtractImpl(IRInst* inst)
+{
+ // If unsigned, bfue := ((val>>off)&((1u<<bts)-1))
+ // Else signed, bfse := (((val>>off)&((1u<<bts)-1))<<(nbts-bts)>>(nbts-bts));
+ //
+ // Note: In WGSL, the data type for bit operators are more restricted than in other languages.
+ // The number of bits to shift must be a u32 or vecN<u32>, therefore we have to cast this
+ // operand to u32 always. Another constraint is that for "&" and "|" operators, the operands
+ // must have the same type.
+ // TODO: We can consider to bring the logic to WGSLSourceEmitter::emitBitfieldExtractImpl so
+ // that we don't have to have those special handling here.
+ Slang::IRType* dataType = inst->getDataType();
+ Slang::IRInst* val = inst->getOperand(0);
+ Slang::IRInst* off = inst->getOperand(1);
+ Slang::IRInst* bts = inst->getOperand(2);
+
+ Slang::IRType* elementType = dataType;
+ IRVectorType* vectorType = as<IRVectorType>(elementType);
+ IRVectorType* vectorTypeForShiftNumber = nullptr;
+
+ if (vectorType)
+ {
+ elementType = vectorType->getElementType();
+
+ if (getTarget() == CodeGenTarget::WGSL)
+ {
+ IRBuilder builder(elementType);
+ vectorTypeForShiftNumber =
+ builder.getVectorType(builder.getUIntType(), vectorType->getElementCount());
+ }
+ else
+ {
+ vectorTypeForShiftNumber = vectorType;
+ }
+ }
+
+ bool isSigned;
+ int bitWidth;
+ if (!tryGetIntInfo(elementType, isSigned, bitWidth))
+ {
+ SLANG_DIAGNOSE_UNEXPECTED(
+ getSink(),
+ SourceLoc(),
+ "non-integer element type given to bitfieldExtract");
+ return;
+ }
+
+ String one = _emitLiteralOneWithType(bitWidth);
+
+ // Emit open paren and type cast for later sign extension
+ if (isSigned)
+ {
+ m_writer->emit("(");
+ emitType(inst->getDataType());
+ m_writer->emit("(");
+ }
+
+ // Emit bitfield extraction ( (val >> off) & ((1u << bts) - 1) )
+ m_writer->emit("(");
+
+ // In WGSL, "&" operator requires the operands to have the same type, since the
+ // right operand '((1u << bts) - 1)' is known to be u32, we need to cast the left operand to
+ // u32.
+ if (getTarget() == CodeGenTarget::WGSL)
+ {
+ (vectorTypeForShiftNumber != nullptr) ? emitType(vectorTypeForShiftNumber)
+ : m_writer->emit("u32");
+ }
+
+ m_writer->emit("(");
+
+ emitOperand(val, getInfo(EmitOp::General));
+ m_writer->emit(">>");
+ emitVecNOrScalar(
+ vectorTypeForShiftNumber,
+ [&]() { emitOperand(off, getInfo(EmitOp::General)); });
+
+ m_writer->emit(")&(");
+ emitVecNOrScalar(
+ vectorTypeForShiftNumber,
+ [&]()
+ {
+ m_writer->emit("((" + one + "<<");
+ emitOperand(bts, getInfo(EmitOp::General));
+ m_writer->emit(")-" + one + ")");
+ });
+ m_writer->emit("))");
+
+ // Emit sign extension logic
+ // ( type(bitfield << (numBits - bts) ) >> (numBits - bts) )
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ if (isSigned)
+ {
+ m_writer->emit("<<");
+ emitVecNOrScalar(
+ vectorTypeForShiftNumber,
+ [&]()
+ {
+ m_writer->emit("(");
+ m_writer->emit(bitWidth);
+ m_writer->emit("-");
+ emitOperand(bts, getInfo(EmitOp::General));
+ m_writer->emit(")");
+ });
+ m_writer->emit(")>>");
+ emitVecNOrScalar(
+ vectorTypeForShiftNumber,
+ [&]()
+ {
+ m_writer->emit("(");
+ m_writer->emit(bitWidth);
+ m_writer->emit("-");
+ emitOperand(bts, getInfo(EmitOp::General));
+ m_writer->emit(")");
+ });
+ m_writer->emit(")");
+ }
+}
+
+void CLikeSourceEmitter::emitBitfieldInsertImpl(IRInst* inst)
+{
+ // uint clearMask = ~(((1u << bits) - 1u) << offset);
+ // uint clearedBase = base & clearMask;
+ // uint maskedInsert = (insert & ((1u << bits) - 1u)) << offset;
+ // BitfieldInsert := T(uint(clearedBase) | uint(maskedInsert));
+ Slang::IRType* dataType = inst->getDataType();
+ Slang::IRInst* base = inst->getOperand(0);
+ Slang::IRInst* insert = inst->getOperand(1);
+ Slang::IRInst* off = inst->getOperand(2);
+ Slang::IRInst* bts = inst->getOperand(3);
+
+
+ Slang::IRType* elementType = dataType;
+ IRVectorType* vectorType = as<IRVectorType>(elementType);
+ IRVectorType* vectorTypeForShiftNumber = nullptr;
+
+ if (vectorType)
+ {
+ elementType = vectorType->getElementType();
+
+ if (getTarget() == CodeGenTarget::WGSL)
+ {
+ IRBuilder builder(elementType);
+ vectorTypeForShiftNumber =
+ builder.getVectorType(builder.getUIntType(), vectorType->getElementCount());
+ }
+ else
+ {
+ vectorTypeForShiftNumber = vectorType;
+ }
+ }
+
+ bool isSigned;
+ int bitWidth;
+ if (!tryGetIntInfo(elementType, isSigned, bitWidth))
+ {
+ SLANG_DIAGNOSE_UNEXPECTED(
+ getSink(),
+ SourceLoc(),
+ "non-integer element type given to bitfieldInsert");
+ return;
+ }
+
+ String one = _emitLiteralOneWithType(bitWidth);
+
+ if (isSigned)
+ {
+ emitType(inst->getDataType());
+ m_writer->emit("(");
+ }
+ m_writer->emit("(");
+
+ // emit clearedBase := uint( base & ~( ((1u << bts) - 1u) << off ) )
+
+ // In WGSL, "&" operator requires the operands to have the same type, since the
+ // right operand '~( ((1u << bts) - 1u) << off )' is known to be u32, we need to
+ // cast the left operand to u32.
+ if (getTarget() == CodeGenTarget::WGSL)
+ {
+ (vectorTypeForShiftNumber != nullptr) ? emitType(vectorTypeForShiftNumber)
+ : m_writer->emit("u32");
+ }
+
+ m_writer->emit("(");
+ emitOperand(base, getInfo(EmitOp::General));
+ m_writer->emit(")");
+
+ m_writer->emit("&");
+ emitVecNOrScalar(
+ vectorTypeForShiftNumber,
+ [&]()
+ {
+ m_writer->emit("~(((" + one + "<<");
+ emitOperand(bts, getInfo(EmitOp::General));
+
+ m_writer->emit(")-" + one + ")<<");
+
+ emitOperand(off, getInfo(EmitOp::General));
+ m_writer->emit(")");
+ });
+
+ // bitwise or clearedBase with maskedInsert
+ m_writer->emit(")|(");
+
+ // Emit maskedInsert := ((insert & ((1u << bits) - 1u)) << offset);
+
+ // - first emit mask := (insert & ((1u << bits) - 1u))
+ m_writer->emit("(");
+
+ // For the same reason as above, we need to cast the left operand to u32 for WGSL target.
+ if (getTarget() == CodeGenTarget::WGSL)
+ {
+ (vectorTypeForShiftNumber != nullptr) ? emitType(vectorTypeForShiftNumber)
+ : m_writer->emit("u32");
+ }
+ m_writer->emit("(");
+ emitOperand(insert, getInfo(EmitOp::General));
+ m_writer->emit(")");
+
+ m_writer->emit("&");
+ emitVecNOrScalar(
+ vectorTypeForShiftNumber,
+ [&]()
+ {
+ m_writer->emit("(" + one + "<<");
+ emitOperand(bts, getInfo(EmitOp::General));
+ m_writer->emit(")-" + one);
+ });
+ m_writer->emit(")");
+
+ // then emit shift := << offset
+ m_writer->emit("<<");
+ emitVecNOrScalar(
+ vectorTypeForShiftNumber,
+ [&]() { emitOperand(off, getInfo(EmitOp::General)); });
+ m_writer->emit(")");
+
+ if (isSigned)
+ {
+ m_writer->emit(")");
+ }
+}
+
void CLikeSourceEmitter::emitStruct(IRStructType* structType)
{
ensureTypePrelude(structType);
diff --git a/source/slang/slang-emit-c-like.h b/source/slang/slang-emit-c-like.h
index 1da3a64dc..7f6f32923 100644
--- a/source/slang/slang-emit-c-like.h
+++ b/source/slang/slang-emit-c-like.h
@@ -615,6 +615,11 @@ protected:
SLANG_UNUSED(baseName);
}
+ bool tryGetIntInfo(IRType* elementType, bool& isSigned, int& bitWidth);
+ void emitVecNOrScalar(IRVectorType* vectorType, std::function<void()> func);
+ virtual void emitBitfieldExtractImpl(IRInst* inst);
+ virtual void emitBitfieldInsertImpl(IRInst* inst);
+
virtual void emitSubpassInputTypeImpl(IRSubpassInputType* type) { SLANG_UNUSED(type); }
// Again necessary for & prefix intrinsics. May be removable in the future
@@ -671,6 +676,8 @@ protected:
// one.
void _emitSwizzleStorePerElement(IRInst* inst);
+ String _emitLiteralOneWithType(int bitWidth);
+
CodeGenContext* m_codeGenContext = nullptr;
IRModule* m_irModule = nullptr;
diff --git a/source/slang/slang-emit-glsl.cpp b/source/slang/slang-emit-glsl.cpp
index f22419147..2c2447c53 100644
--- a/source/slang/slang-emit-glsl.cpp
+++ b/source/slang/slang-emit-glsl.cpp
@@ -2949,6 +2949,45 @@ void GLSLSourceEmitter::emitFuncDecorationImpl(IRDecoration* decoration)
}
}
+void GLSLSourceEmitter::emitBitfieldExtractImpl(IRInst* inst)
+{
+ m_writer->emit("bitfieldExtract(");
+
+ emitOperand(inst->getOperand(0), getInfo(EmitOp::General));
+ m_writer->emit(",");
+
+ m_writer->emit("int(");
+ emitOperand(inst->getOperand(1), getInfo(EmitOp::General));
+ m_writer->emit(")");
+ m_writer->emit(",");
+
+ m_writer->emit("int(");
+ emitOperand(inst->getOperand(2), getInfo(EmitOp::General));
+ m_writer->emit("))");
+}
+
+void GLSLSourceEmitter::emitBitfieldInsertImpl(IRInst* inst)
+{
+ m_writer->emit("bitfieldInsert(");
+
+ emitOperand(inst->getOperand(0), getInfo(EmitOp::General));
+ m_writer->emit(",");
+
+ emitOperand(inst->getOperand(1), getInfo(EmitOp::General));
+ m_writer->emit(",");
+
+ m_writer->emit("int(");
+ emitOperand(inst->getOperand(2), getInfo(EmitOp::General));
+ m_writer->emit(")");
+ m_writer->emit(",");
+
+ m_writer->emit("int(");
+ emitOperand(inst->getOperand(3), getInfo(EmitOp::General));
+ m_writer->emit(")");
+
+ m_writer->emit(")");
+}
+
void GLSLSourceEmitter::emitSimpleTypeImpl(IRType* type)
{
switch (type->getOp())
diff --git a/source/slang/slang-emit-glsl.h b/source/slang/slang-emit-glsl.h
index e9ec1dc00..49a7884c2 100644
--- a/source/slang/slang-emit-glsl.h
+++ b/source/slang/slang-emit-glsl.h
@@ -64,6 +64,9 @@ protected:
virtual void emitFuncDecorationImpl(IRDecoration* decoration) SLANG_OVERRIDE;
virtual void emitGlobalParamDefaultVal(IRGlobalParam* decl) SLANG_OVERRIDE;
+ virtual void emitBitfieldExtractImpl(IRInst* inst) SLANG_OVERRIDE;
+ virtual void emitBitfieldInsertImpl(IRInst* inst) SLANG_OVERRIDE;
+
virtual void handleRequiredCapabilitiesImpl(IRInst* inst) SLANG_OVERRIDE;
virtual bool tryEmitGlobalParamImpl(IRGlobalParam* varDecl, IRType* varType) SLANG_OVERRIDE;
diff --git a/source/slang/slang-emit-spirv.cpp b/source/slang/slang-emit-spirv.cpp
index 8759ea9d4..c618946ec 100644
--- a/source/slang/slang-emit-spirv.cpp
+++ b/source/slang/slang-emit-spirv.cpp
@@ -3404,6 +3404,12 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
case kIROp_BitCast:
result = emitOpBitcast(parent, inst, inst->getDataType(), inst->getOperand(0));
break;
+ case kIROp_BitfieldExtract:
+ result = emitBitfieldExtract(parent, inst);
+ break;
+ case kIROp_BitfieldInsert:
+ result = emitBitfieldInsert(parent, inst);
+ break;
case kIROp_MakeUInt64:
result = emitMakeUInt64(parent, inst);
break;
@@ -6537,6 +6543,60 @@ struct SPIRVEmitContext : public SourceEmitterBase, public SPIRVEmitSharedContex
inst->getOperand(0));
}
+ SpvInst* emitBitfieldExtract(SpvInstParent* parent, IRInst* inst)
+ {
+ auto dataType = inst->getDataType();
+ IRVectorType* vectorType = as<IRVectorType>(dataType);
+ Slang::IRType* elementType = dataType;
+ if (vectorType)
+ elementType = vectorType->getElementType();
+
+ const IntInfo i = getIntTypeInfo(elementType);
+
+ // NM: technically, using bitfield intrinsics for anything non-32-bit goes against
+ // VK specification: VUID-StandaloneSpirv-Base-04781. However, it works on at least
+ // NVIDIA HW.
+ SpvOp opcode = i.isSigned ? SpvOpBitFieldSExtract : SpvOpBitFieldUExtract;
+ return emitInst(
+ parent,
+ inst,
+ opcode,
+ inst->getFullType(),
+ kResultID,
+ inst->getOperand(0),
+ inst->getOperand(1),
+ inst->getOperand(2));
+ }
+
+ SpvInst* emitBitfieldInsert(SpvInstParent* parent, IRInst* inst)
+ {
+ auto dataType = inst->getDataType();
+ IRVectorType* vectorType = as<IRVectorType>(dataType);
+ Slang::IRType* elementType = dataType;
+ if (vectorType)
+ elementType = vectorType->getElementType();
+
+ const IntInfo i = getIntTypeInfo(elementType);
+
+ if (i.width == 64)
+ requireSPIRVCapability(SpvCapabilityInt64);
+ if (i.width == 16)
+ requireSPIRVCapability(SpvCapabilityInt16);
+ if (i.width == 8)
+ requireSPIRVCapability(SpvCapabilityInt8);
+
+ return emitInst(
+ parent,
+ inst,
+ SpvOpBitFieldInsert,
+ inst->getFullType(),
+ kResultID,
+ inst->getOperand(0),
+ inst->getOperand(1),
+ inst->getOperand(2),
+ inst->getOperand(3));
+ }
+
template<typename T, typename Ts>
SpvInst* emitCompositeConstruct(
SpvInstParent* parent,
diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h
index 6d4f7a2ca..01466ed00 100644
--- a/source/slang/slang-ir-inst-defs.h
+++ b/source/slang/slang-ir-inst-defs.h
@@ -387,6 +387,9 @@ INST(Alloca, alloca, 1, 0)
INST(UpdateElement, updateElement, 2, 0)
INST(DetachDerivative, detachDerivative, 1, 0)
+INST(BitfieldExtract, bitfieldExtract, 3, 0)
+INST(BitfieldInsert, bitfieldInsert, 4, 0)
+
INST(PackAnyValue, packAnyValue, 1, 0)
INST(UnpackAnyValue, unpackAnyValue, 1, 0)
diff --git a/source/slang/slang-ir-insts.h b/source/slang/slang-ir-insts.h
index 829e72575..53adce87a 100644
--- a/source/slang/slang-ir-insts.h
+++ b/source/slang/slang-ir-insts.h
@@ -3893,6 +3893,10 @@ public:
IRInst* emitGlobalValueRef(IRInst* globalInst);
+ IRInst* emitBitfieldExtract(IRType* type, IRInst* op0, IRInst* op1, IRInst* op2);
+
+ IRInst* emitBitfieldInsert(IRType* type, IRInst* op0, IRInst* op1, IRInst* op2, IRInst* op3);
+
IRInst* emitPackAnyValue(IRType* type, IRInst* value);
IRInst* emitUnpackAnyValue(IRType* type, IRInst* value);
diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp
index d1c16a3a1..5e5d94b14 100644
--- a/source/slang/slang-ir.cpp
+++ b/source/slang/slang-ir.cpp
@@ -1951,6 +1951,20 @@ static T* createInst(
}
template<typename T>
+static T* createInst(
+ IRBuilder* builder,
+ IROp op,
+ IRType* type,
+ IRInst* arg1,
+ IRInst* arg2,
+ IRInst* arg3,
+ IRInst* arg4)
+{
+ IRInst* args[] = {arg1, arg2, arg3, arg4};
+ return createInstImpl<T>(builder, op, type, 4, &args[0]);
+}
+
+template<typename T>
static T* createInstWithTrailingArgs(
IRBuilder* builder,
IROp op,
@@ -3625,7 +3639,25 @@ IRInst* IRBuilder::emitLookupInterfaceMethodInst(
IRInst* IRBuilder::emitGetSequentialIDInst(IRInst* rttiObj)
{
auto inst = createInst<IRAlloca>(this, kIROp_GetSequentialID, getUIntType(), rttiObj);
+ addInst(inst);
+ return inst;
+}
+IRInst* IRBuilder::emitBitfieldExtract(IRType* type, IRInst* value, IRInst* offset, IRInst* bits)
+{
+ auto inst = createInst<IRInst>(this, kIROp_BitfieldExtract, type, value, offset, bits);
+ addInst(inst);
+ return inst;
+}
+
+IRInst* IRBuilder::emitBitfieldInsert(
+ IRType* type,
+ IRInst* base,
+ IRInst* insert,
+ IRInst* offset,
+ IRInst* bits)
+{
+ auto inst = createInst<IRInst>(this, kIROp_BitfieldInsert, type, base, insert, offset, bits);
addInst(inst);
return inst;
}
@@ -8188,6 +8220,8 @@ bool IRInst::mightHaveSideEffects(SideEffectAnalysisOptions options)
case kIROp_PtrCast:
case kIROp_CastDynamicResource:
case kIROp_AllocObj:
+ case kIROp_BitfieldExtract:
+ case kIROp_BitfieldInsert:
case kIROp_PackAnyValue:
case kIROp_UnpackAnyValue:
case kIROp_Reinterpret: