From 301012e4a87145e24e5660a7874b101931eb5de8 Mon Sep 17 00:00:00 2001 From: luciechoi Date: Mon, 27 Oct 2025 03:10:59 +0000 Subject: [PATCH 1/2] Support `sign()` intrinsics for unsigned int. --- tools/clang/lib/SPIRV/SpirvEmitter.cpp | 9 ++++ .../CodeGenSPIRV/intrinsics.uintsign.hlsl | 52 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 tools/clang/test/CodeGenSPIRV/intrinsics.uintsign.hlsl diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.cpp b/tools/clang/lib/SPIRV/SpirvEmitter.cpp index 7956d4f43f..9343ea9aa9 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.cpp +++ b/tools/clang/lib/SPIRV/SpirvEmitter.cpp @@ -9352,6 +9352,15 @@ SpirvEmitter::processIntrinsicCallExpr(const CallExpr *callExpr) { case hlsl::IntrinsicOp::IOP_printf: retVal = processIntrinsicPrintf(callExpr); break; + case hlsl::IntrinsicOp::IOP_usign: { + // Do SAbs followed by SSign + auto *absVal = processIntrinsicUsingGLSLInst( + callExpr, GLSLstd450::GLSLstd450SAbs, + /*actPerRowForMatrices*/ true, srcLoc, srcRange); + retVal = spvBuilder.createGLSLExtInst(callExpr->getType(), + GLSLstd450::GLSLstd450SSign, {absVal}, + srcLoc, srcRange); + } break; case hlsl::IntrinsicOp::IOP_sign: { if (isFloatOrVecMatOfFloatType(callExpr->getArg(0)->getType())) retVal = processIntrinsicFloatSign(callExpr); diff --git a/tools/clang/test/CodeGenSPIRV/intrinsics.uintsign.hlsl b/tools/clang/test/CodeGenSPIRV/intrinsics.uintsign.hlsl new file mode 100644 index 0000000000..4493f77477 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/intrinsics.uintsign.hlsl @@ -0,0 +1,52 @@ +// RUN: %dxc -T vs_6_0 -E main -fcgl %s -spirv | FileCheck %s + +// CHECK: [[glsl:%[0-9]+]] = OpExtInstImport "GLSL.std.450" + +void main() { + int result; + int3 result3; + +// CHECK: [[a:%[0-9]+]] = OpLoad %uint %a +// CHECK: [[abs_a:%[0-9]+]] = OpExtInst %int [[glsl]] SAbs [[a]] +// CHECK-NEXT: [[sign_a:%[0-9]+]] = OpExtInst %int [[glsl]] SSign [[abs_a]] +// CHECK-NEXT: OpStore %result [[sign_a]] + uint a; + result = sign(a); + +// CHECK-NEXT: [[b:%[0-9]+]] = OpLoad %uint %b +// CHECK: [[abs_b:%[0-9]+]] = OpExtInst %int [[glsl]] SAbs [[b]] +// CHECK-NEXT: [[sign_b:%[0-9]+]] = OpExtInst %int [[glsl]] SSign [[abs_b]] +// CHECK-NEXT: OpStore %result [[sign_b]] + uint1 b; + result = sign(b); + +// CHECK-NEXT: [[c:%[0-9]+]] = OpLoad %v3uint %c +// CHECK: [[abs_c:%[0-9]+]] = OpExtInst %v3int [[glsl]] SAbs [[c]] +// CHECK-NEXT: [[sign_c:%[0-9]+]] = OpExtInst %v3int [[glsl]] SSign [[abs_c]] +// CHECK-NEXT: OpStore %result3 [[sign_c]] + uint3 c; + result3 = sign(c); + +// CHECK: [[d:%[0-9]+]] = OpLoad %uint %d +// CHECK: [[abs_d:%[0-9]+]] = OpExtInst %int [[glsl]] SAbs [[d]] +// CHECK-NEXT: [[sign_d:%[0-9]+]] = OpExtInst %int [[glsl]] SSign [[abs_d]] +// CHECK-NEXT: OpStore %result [[sign_d]] + uint1x1 d; + result = sign(d); + +// CHECK-NEXT: [[e:%[0-9]+]] = OpLoad %v2uint %e +// CHECK-NEXT: [[abs_e:%[0-9]+]] = OpExtInst %v2int [[glsl]] SAbs [[e]] +// CHECK-NEXT: [[sign_e:%[0-9]+]] = OpExtInst %v2int [[glsl]] SSign [[abs_e]] +// CHECK-NEXT: OpStore %result2 [[sign_e]] + uint1x2 e; + int2 result2 = sign(e); + +// CHECK-NEXT: [[f:%[0-9]+]] = OpLoad %v4uint %f +// CHECK-NEXT: [[abs_f:%[0-9]+]] = OpExtInst %v4int [[glsl]] SAbs [[f]] +// CHECK-NEXT: [[sign_f:%[0-9]+]] = OpExtInst %v4int [[glsl]] SSign [[abs_f]] +// CHECK-NEXT: OpStore %result4 [[sign_f]] + uint4x1 f; + int4 result4 = sign(f); + +// TODO: Integer matrices are not supported yet. See intrinsics.intsign.hlsl +} \ No newline at end of file From bf0b93d58d3055a1baec0524201ec354a749cedb Mon Sep 17 00:00:00 2001 From: luciechoi Date: Mon, 27 Oct 2025 20:34:18 +0000 Subject: [PATCH 2/2] Use OpSelect --- tools/clang/lib/SPIRV/SpirvEmitter.cpp | 86 ++++++++++++++++-- tools/clang/lib/SPIRV/SpirvEmitter.h | 3 + .../CodeGenSPIRV/intrinsics.uintsign.hlsl | 88 ++++++++++++------- 3 files changed, 135 insertions(+), 42 deletions(-) diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.cpp b/tools/clang/lib/SPIRV/SpirvEmitter.cpp index 9343ea9aa9..55c5f9f83d 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.cpp +++ b/tools/clang/lib/SPIRV/SpirvEmitter.cpp @@ -9352,15 +9352,9 @@ SpirvEmitter::processIntrinsicCallExpr(const CallExpr *callExpr) { case hlsl::IntrinsicOp::IOP_printf: retVal = processIntrinsicPrintf(callExpr); break; - case hlsl::IntrinsicOp::IOP_usign: { - // Do SAbs followed by SSign - auto *absVal = processIntrinsicUsingGLSLInst( - callExpr, GLSLstd450::GLSLstd450SAbs, - /*actPerRowForMatrices*/ true, srcLoc, srcRange); - retVal = spvBuilder.createGLSLExtInst(callExpr->getType(), - GLSLstd450::GLSLstd450SSign, {absVal}, - srcLoc, srcRange); - } break; + case hlsl::IntrinsicOp::IOP_usign: + retVal = processIntrinsicSignUnsignedInt(callExpr); + break; case hlsl::IntrinsicOp::IOP_sign: { if (isFloatOrVecMatOfFloatType(callExpr->getArg(0)->getType())) retVal = processIntrinsicFloatSign(callExpr); @@ -12665,6 +12659,80 @@ SpirvEmitter::processIntrinsicSaturate(const CallExpr *callExpr) { return nullptr; } +SpirvInstruction * +SpirvEmitter::processIntrinsicSignUnsignedInt(const CallExpr *callExpr) { + const auto srcLoc = callExpr->getExprLoc(); + const auto srcRange = callExpr->getSourceRange(); + + const Expr *firstArg = callExpr->getArg(0); + const QualType firstArgType = firstArg->getType(); + auto elemType = QualType{}; + uint32_t numRows; + uint32_t numCols; + uint32_t count; + bool isScalar = + isScalarType(firstArgType, &elemType) || + (isVectorType(firstArgType, &elemType, &count) && count == 1) || + (isMxNMatrix(firstArgType, &elemType, &numRows, &numCols) && + (numRows == 1 && numCols == 1)); + + auto *zero = getValueZero(astContext.IntTy); + auto *one = getValueOne(astContext.IntTy); + if (isScalar) { + auto *argVal = doExpr(callExpr->getArg(0)); + auto *zeroUint = getValueZero(callExpr->getArg(0)->getType()); + auto *cmp = + spvBuilder.createBinaryOp(spv::Op::OpUGreaterThan, astContext.BoolTy, + argVal, zeroUint, srcLoc, srcRange); + return spvBuilder.createSelect(astContext.IntTy, cmp, one, zero, srcLoc, + srcRange); + } + + uint32_t size; + if (isVectorType(firstArgType)) { + size = count; + } else if (is1xNMatrix(firstArgType)) { + size = numCols; + } else if (isMx1Matrix(firstArgType)) { + size = numRows; + } else { + size = numRows; + } + + const auto actOnEachVec = [this, srcLoc, srcRange, zero, one, elemType, + size](uint32_t index, QualType inType, + QualType outType, SpirvInstruction *curRow) { + auto zeroUint = getValueZero(elemType); + // Create `size` vector of uint zeros. + auto *zerosUint = spvBuilder.getConstantComposite( + astContext.getExtVectorType(elemType, size), + std::vector(size, zeroUint)); + // Compare if they are greater than zero. + auto *cmp = spvBuilder.createBinaryOp( + spv::Op::OpUGreaterThan, + astContext.getExtVectorType(astContext.BoolTy, size), curRow, zerosUint, + srcLoc, srcRange); + + // Create a vector of int ones and zeros. + auto *zeros = spvBuilder.getConstantComposite( + astContext.getExtVectorType(astContext.IntTy, size), + std::vector(size, zero)); + auto *ones = spvBuilder.getConstantComposite( + astContext.getExtVectorType(astContext.IntTy, size), + std::vector(size, one)); + // Select between ones and zeros based on the comparison. + return spvBuilder.createSelect( + astContext.getExtVectorType(astContext.IntTy, size), cmp, ones, zeros, + srcLoc, srcRange); + }; + + if (isVectorType(firstArgType)) { + return actOnEachVec(0, firstArgType, callExpr->getType(), doExpr(firstArg)); + } + return processEachVectorInMatrix(firstArg, doExpr(firstArg), actOnEachVec, + srcLoc, srcRange); +} + SpirvInstruction * SpirvEmitter::processIntrinsicFloatSign(const CallExpr *callExpr) { // Import the GLSL.std.450 extended instruction set. diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.h b/tools/clang/lib/SPIRV/SpirvEmitter.h index f228b3149e..d1f0363f6e 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.h +++ b/tools/clang/lib/SPIRV/SpirvEmitter.h @@ -648,6 +648,9 @@ class SpirvEmitter : public ASTConsumer { /// Processes the 'ReadClock' intrinsic function. SpirvInstruction *processIntrinsicReadClock(const CallExpr *); + /// Processes the 'sign' intrinsic function for unsigned integer types. + SpirvInstruction *processIntrinsicSignUnsignedInt(const CallExpr *callExpr); + /// Processes the 'sign' intrinsic function for float types. /// The FSign instruction in the GLSL instruction set returns a floating point /// result. The HLSL sign function, however, returns an integer. An extra diff --git a/tools/clang/test/CodeGenSPIRV/intrinsics.uintsign.hlsl b/tools/clang/test/CodeGenSPIRV/intrinsics.uintsign.hlsl index 4493f77477..454a9c004f 100644 --- a/tools/clang/test/CodeGenSPIRV/intrinsics.uintsign.hlsl +++ b/tools/clang/test/CodeGenSPIRV/intrinsics.uintsign.hlsl @@ -1,52 +1,74 @@ -// RUN: %dxc -T vs_6_0 -E main -fcgl %s -spirv | FileCheck %s +// RUN: %dxc -T vs_6_0 -E main -fcgl -Vd %s -spirv | FileCheck %s -// CHECK: [[glsl:%[0-9]+]] = OpExtInstImport "GLSL.std.450" +// CHECK-DAG: %int_0 = OpConstant %int 0 +// CHECK-DAG: %int_1 = OpConstant %int 1 +// CHECK-DAG: %uint_0 = OpConstant %uint 0 +// CHECK-DAG: %v3int = OpTypeVector %int 3 +// CHECK-DAG: %v3uint = OpTypeVector %uint 3 +// CHECK-DAG: [[zeros_uint3:%[0-9]+]] = OpConstantComposite %v3uint %uint_0 %uint_0 %uint_0 +// CHECK-DAG: [[zeros_int3:%[0-9]+]] = OpConstantComposite %v3int %int_0 %int_0 %int_0 +// CHECK-DAG: [[ones_int3:%[0-9]+]] = OpConstantComposite %v3int %int_1 %int_1 %int_1 void main() { int result; int3 result3; + int3x3 result3x3; // CHECK: [[a:%[0-9]+]] = OpLoad %uint %a -// CHECK: [[abs_a:%[0-9]+]] = OpExtInst %int [[glsl]] SAbs [[a]] -// CHECK-NEXT: [[sign_a:%[0-9]+]] = OpExtInst %int [[glsl]] SSign [[abs_a]] -// CHECK-NEXT: OpStore %result [[sign_a]] +// CHECK-NEXT: [[cmp_a:%[0-9]+]] = OpUGreaterThan %bool [[a]] %uint_0 +// CHECK-NEXT: [[select_a:%[0-9]+]] = OpSelect %int [[cmp_a]] %int_1 %int_0 +// CHECK-NEXT: OpStore %result [[select_a]] uint a; result = sign(a); -// CHECK-NEXT: [[b:%[0-9]+]] = OpLoad %uint %b -// CHECK: [[abs_b:%[0-9]+]] = OpExtInst %int [[glsl]] SAbs [[b]] -// CHECK-NEXT: [[sign_b:%[0-9]+]] = OpExtInst %int [[glsl]] SSign [[abs_b]] -// CHECK-NEXT: OpStore %result [[sign_b]] +// CHECK: [[b:%[0-9]+]] = OpLoad %uint %b +// CHECK-NEXT: [[cmp_b:%[0-9]+]] = OpUGreaterThan %bool [[b]] %uint_0 +// CHECK-NEXT: [[select_b:%[0-9]+]] = OpSelect %int [[cmp_b]] %int_1 %int_0 +// CHECK-NEXT: OpStore %result [[select_b]] uint1 b; result = sign(b); -// CHECK-NEXT: [[c:%[0-9]+]] = OpLoad %v3uint %c -// CHECK: [[abs_c:%[0-9]+]] = OpExtInst %v3int [[glsl]] SAbs [[c]] -// CHECK-NEXT: [[sign_c:%[0-9]+]] = OpExtInst %v3int [[glsl]] SSign [[abs_c]] -// CHECK-NEXT: OpStore %result3 [[sign_c]] +// CHECK: [[c:%[0-9]+]] = OpLoad %v3uint %c +// CHECK-NEXT: [[cmp_c:%[0-9]+]] = OpUGreaterThan %v3bool [[c]] [[zeros_uint3]] +// CHECK-NEXT: [[select_c:%[0-9]+]] = OpSelect %v3int [[cmp_c]] [[ones_int3]] [[zeros_int3]] +// CHECK-NEXT: OpStore %result3 [[select_c]] uint3 c; result3 = sign(c); -// CHECK: [[d:%[0-9]+]] = OpLoad %uint %d -// CHECK: [[abs_d:%[0-9]+]] = OpExtInst %int [[glsl]] SAbs [[d]] -// CHECK-NEXT: [[sign_d:%[0-9]+]] = OpExtInst %int [[glsl]] SSign [[abs_d]] -// CHECK-NEXT: OpStore %result [[sign_d]] + +// CHECK: [[d:%[0-9]+]] = OpLoad %uint %d +// CHECK-NEXT: [[cmp_d:%[0-9]+]] = OpUGreaterThan %bool [[d]] %uint_0 +// CHECK-NEXT: [[select_d:%[0-9]+]] = OpSelect %int [[cmp_d]] %int_1 %int_0 +// CHECK-NEXT: OpStore %result [[select_d]] uint1x1 d; result = sign(d); -// CHECK-NEXT: [[e:%[0-9]+]] = OpLoad %v2uint %e -// CHECK-NEXT: [[abs_e:%[0-9]+]] = OpExtInst %v2int [[glsl]] SAbs [[e]] -// CHECK-NEXT: [[sign_e:%[0-9]+]] = OpExtInst %v2int [[glsl]] SSign [[abs_e]] -// CHECK-NEXT: OpStore %result2 [[sign_e]] - uint1x2 e; - int2 result2 = sign(e); - -// CHECK-NEXT: [[f:%[0-9]+]] = OpLoad %v4uint %f -// CHECK-NEXT: [[abs_f:%[0-9]+]] = OpExtInst %v4int [[glsl]] SAbs [[f]] -// CHECK-NEXT: [[sign_f:%[0-9]+]] = OpExtInst %v4int [[glsl]] SSign [[abs_f]] -// CHECK-NEXT: OpStore %result4 [[sign_f]] - uint4x1 f; - int4 result4 = sign(f); - -// TODO: Integer matrices are not supported yet. See intrinsics.intsign.hlsl -} \ No newline at end of file +// CHECK: [[e:%[0-9]+]] = OpLoad %v3uint %e +// CHECK-NEXT: [[cmp_e:%[0-9]+]] = OpUGreaterThan %v3bool [[e]] [[zeros_uint3]] +// CHECK-NEXT: [[select_e:%[0-9]+]] = OpSelect %v3int [[cmp_e]] [[ones_int3]] [[zeros_int3]] +// CHECK-NEXT: OpStore %result3 [[select_e]] + uint1x3 e; + result3 = sign(e); + +// CHECK: [[f:%[0-9]+]] = OpLoad %v3uint %f +// CHECK-NEXT: [[cmp_f:%[0-9]+]] = OpUGreaterThan %v3bool [[f]] [[zeros_uint3]] +// CHECK-NEXT: [[select_f:%[0-9]+]] = OpSelect %v3int [[cmp_f]] [[ones_int3]] [[zeros_int3]] +// CHECK-NEXT: OpStore %result3 [[select_f]] + uint3x1 f; + result3 = sign(f); + +// CHECK: [[h:%[0-9]+]] = OpLoad %_arr_v3uint_uint_3 %h +// CHECK-NEXT: [[h_row0:%[0-9]+]] = OpCompositeExtract %v3uint [[h]] 0 +// CHECK-NEXT: [[cmp_h_row0:%[0-9]+]] = OpUGreaterThan %v3bool [[h_row0]] [[zeros_uint3]] +// CHECK-NEXT: [[select_h_row0:%[0-9]+]] = OpSelect %v3int [[cmp_h_row0]] [[ones_int3]] [[zeros_int3]] +// CHECK-NEXT: [[h_row1:%[0-9]+]] = OpCompositeExtract %v3uint [[h]] 1 +// CHECK-NEXT: [[cmp_h_row1:%[0-9]+]] = OpUGreaterThan %v3bool [[h_row1]] [[zeros_uint3]] +// CHECK-NEXT: [[select_h_row1:%[0-9]+]] = OpSelect %v3int [[cmp_h_row1]] [[ones_int3]] [[zeros_int3]] +// CHECK-NEXT: [[h_row2:%[0-9]+]] = OpCompositeExtract %v3uint [[h]] 2 +// CHECK-NEXT: [[cmp_h_row2:%[0-9]+]] = OpUGreaterThan %v3bool [[h_row2]] [[zeros_uint3]] +// CHECK-NEXT: [[select_h_row2:%[0-9]+]] = OpSelect %v3int [[cmp_h_row2]] [[ones_int3]] [[zeros_int3]] +// CHECK-NEXT: [[select_h:%[0-9]+]] = OpCompositeConstruct %_arr_v3uint_uint_3 [[select_h_row0]] [[select_h_row1]] [[select_h_row2]] +// CHECK-NEXT: OpStore %result3x3 [[select_h]] + uint3x3 h; + result3x3 = sign(h); +}