Skip to content

[SPIRV][HLSL] Add lowering of frac to SPIR-V #97111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 23, 2024
Merged

Conversation

ritskii
Copy link
Contributor

@ritskii ritskii commented Jun 28, 2024

Implements frac lowering to SPIR-V.

Closes #88059

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:codegen IR generation bugs: mangling, exceptions, etc. HLSL HLSL Language Support backend:SPIR-V llvm:ir labels Jun 28, 2024
@llvmbot
Copy link
Member

llvmbot commented Jun 28, 2024

@llvm/pr-subscribers-hlsl
@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-codegen

Author: Andrii Levytskyi (aabysswalker)

Changes

Implements frac lowering to SPIR-V.

Closes #88059


Full diff: https://github.com/llvm/llvm-project/pull/97111.diff

6 Files Affected:

  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+4-4)
  • (modified) clang/lib/CodeGen/CGHLSLRuntime.h (+1)
  • (modified) clang/test/CodeGenHLSL/builtins/frac.hlsl (+70-39)
  • (modified) llvm/include/llvm/IR/IntrinsicsSPIRV.td (+1)
  • (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+22)
  • (added) llvm/test/CodeGen/SPIRV/hlsl-intrinsics/frac.ll (+68)
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 98c2f70664ec7..418df5bc5935a 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -18312,10 +18312,10 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
     if (!E->getArg(0)->getType()->hasFloatingRepresentation())
       llvm_unreachable("frac operand must have a float representation");
     return Builder.CreateIntrinsic(
-        /*ReturnType=*/Op0->getType(), Intrinsic::dx_frac,
-        ArrayRef<Value *>{Op0}, nullptr, "dx.frac");
-  }
-  case Builtin::BI__builtin_hlsl_elementwise_isinf: {
+        /*ReturnType=*/Op0->getType(), CGM.getHLSLRuntime().getFracIntrinsic(),
+        ArrayRef<Value *>{Op0}, nullptr, "hlsl.frac");
+}
+case Builtin::BI__builtin_hlsl_elementwise_isinf: {
     Value *Op0 = EmitScalarExpr(E->getArg(0));
     llvm::Type *Xty = Op0->getType();
     llvm::Type *retType = llvm::Type::getInt1Ty(this->getLLVMContext());
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index 4036ce711bea1..8c067f4963955 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -74,6 +74,7 @@ class CGHLSLRuntime {
 
   GENERATE_HLSL_INTRINSIC_FUNCTION(All, all)
   GENERATE_HLSL_INTRINSIC_FUNCTION(Any, any)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(Frac, frac)
   GENERATE_HLSL_INTRINSIC_FUNCTION(Lerp, lerp)
   GENERATE_HLSL_INTRINSIC_FUNCTION(Rsqrt, rsqrt)
   GENERATE_HLSL_INTRINSIC_FUNCTION(ThreadId, thread_id)
diff --git a/clang/test/CodeGenHLSL/builtins/frac.hlsl b/clang/test/CodeGenHLSL/builtins/frac.hlsl
index 7c4d1468e96d2..b457f5c278791 100644
--- a/clang/test/CodeGenHLSL/builtins/frac.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/frac.hlsl
@@ -1,53 +1,84 @@
 // RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
 // RUN:   dxil-pc-shadermodel6.3-library %s -fnative-half-type \
-// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s \ 
-// RUN:   --check-prefixes=CHECK,NATIVE_HALF
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
+// RUN:   --check-prefixes=CHECK,DXIL_CHECK,DXIL_NATIVE_HALF,NATIVE_HALF
 // RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
 // RUN:   dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \
-// RUN:   -o - | FileCheck %s --check-prefixes=CHECK,NO_HALF
+// RUN:   -o - | FileCheck %s --check-prefixes=CHECK,DXIL_CHECK,NO_HALF,DXIL_NO_HALF
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
+// RUN:   spirv-unknown-vulkan-compute %s -fnative-half-type \
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
+// RUN:   --check-prefixes=CHECK,SPIR_CHECK,NATIVE_HALF,SPIR_NATIVE_HALF
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
+// RUN:   spirv-unknown-vulkan-compute %s -emit-llvm -disable-llvm-passes \
+// RUN:   -o - | FileCheck %s --check-prefixes=CHECK,SPIR_CHECK,NO_HALF,SPIR_NO_HALF
 
-// NATIVE_HALF: define noundef half @
-// NATIVE_HALF: %dx.frac = call half @llvm.dx.frac.f16(
-// NATIVE_HALF: ret half %dx.frac
-// NO_HALF: define noundef float @"?test_frac_half@@YA$halff@$halff@@Z"(
-// NO_HALF: %dx.frac = call float @llvm.dx.frac.f32(
-// NO_HALF: ret float %dx.frac
+// DXIL_NATIVE_HALF: define noundef half @
+// SPIR_NATIVE_HALF: define spir_func noundef half @
+// DXIL_NATIVE_HALF: %hlsl.frac = call half @llvm.dx.frac.f16(
+// SPIR_NATIVE_HALF: %hlsl.frac = call half @llvm.spv.frac.f16(
+// NATIVE_HALF: ret half %hlsl.frac
+// DXIL_NO_HALF: define noundef float @
+// SPIR_NO_HALF: define spir_func noundef float @
+// DXIL_NO_HALF: %hlsl.frac = call float @llvm.dx.frac.f32(
+// SPIR_NO_HALF: %hlsl.frac = call float @llvm.spv.frac.f32(
+// NO_HALF: ret float %hlsl.frac
 half test_frac_half(half p0) { return frac(p0); }
-// NATIVE_HALF: define noundef <2 x half> @
-// NATIVE_HALF: %dx.frac = call <2 x half> @llvm.dx.frac.v2f16
-// NATIVE_HALF: ret <2 x half> %dx.frac
-// NO_HALF: define noundef <2 x float> @
-// NO_HALF: %dx.frac = call <2 x float> @llvm.dx.frac.v2f32(
-// NO_HALF: ret <2 x float> %dx.frac
+// DXIL_NATIVE_HALF: define noundef <2 x half> @
+// SPIR_NATIVE_HALF: define spir_func noundef <2 x half> @
+// DXIL_NATIVE_HALF: %hlsl.frac = call <2 x half> @llvm.dx.frac.v2f16
+// SPIR_NATIVE_HALF: %hlsl.frac = call <2 x half> @llvm.spv.frac.v2f16
+// NATIVE_HALF: ret <2 x half> %hlsl.frac
+// DXIL_NO_HALF: define noundef <2 x float> @
+// SPIR_NO_HALF: define spir_func noundef <2 x float> @
+// DXIL_NO_HALF: %hlsl.frac = call <2 x float> @llvm.dx.frac.v2f32(
+// SPIR_NO_HALF: %hlsl.frac = call <2 x float> @llvm.spv.frac.v2f32(
+// NO_HALF: ret <2 x float> %hlsl.frac
 half2 test_frac_half2(half2 p0) { return frac(p0); }
-// NATIVE_HALF: define noundef <3 x half> @
-// NATIVE_HALF: %dx.frac = call <3 x half> @llvm.dx.frac.v3f16
-// NATIVE_HALF: ret <3 x half> %dx.frac
-// NO_HALF: define noundef <3 x float> @
-// NO_HALF: %dx.frac = call <3 x float> @llvm.dx.frac.v3f32(
-// NO_HALF: ret <3 x float> %dx.frac
+// DXIL_NATIVE_HALF: define noundef <3 x half> @
+// SPIR_NATIVE_HALF: define spir_func noundef <3 x half> @
+// DXIL_NATIVE_HALF: %hlsl.frac = call <3 x half> @llvm.dx.frac.v3f16
+// SPIR_NATIVE_HALF: %hlsl.frac = call <3 x half> @llvm.spv.frac.v3f16
+// NATIVE_HALF: ret <3 x half> %hlsl.frac
+// DXIL_NO_HALF: define noundef <3 x float> @
+// SPIR_NO_HALF: define spir_func noundef <3 x float> @
+// DXIL_NO_HALF: %hlsl.frac = call <3 x float> @llvm.dx.frac.v3f32(
+// SPIR_NO_HALF: %hlsl.frac = call <3 x float> @llvm.spv.frac.v3f32(
+// NO_HALF: ret <3 x float> %hlsl.frac
 half3 test_frac_half3(half3 p0) { return frac(p0); }
-// NATIVE_HALF: define noundef <4 x half> @
-// NATIVE_HALF: %dx.frac = call <4 x half> @llvm.dx.frac.v4f16
-// NATIVE_HALF: ret <4 x half> %dx.frac
-// NO_HALF: define noundef <4 x float> @
-// NO_HALF: %dx.frac = call <4 x float> @llvm.dx.frac.v4f32(
-// NO_HALF: ret <4 x float> %dx.frac
+// DXIL_NATIVE_HALF: define noundef <4 x half> @
+// SPIR_NATIVE_HALF: define spir_func noundef <4 x half> @
+// DXIL_NATIVE_HALF: %hlsl.frac = call <4 x half> @llvm.dx.frac.v4f16
+// SPIR_NATIVE_HALF: %hlsl.frac = call <4 x half> @llvm.spv.frac.v4f16
+// NATIVE_HALF: ret <4 x half> %hlsl.frac
+// DXIL_NO_HALF: define noundef <4 x float> @
+// SPIR_NO_HALF: define spir_func noundef <4 x float> @
+// DXIL_NO_HALF: %hlsl.frac = call <4 x float> @llvm.dx.frac.v4f32(
+// SPIR_NO_HALF: %hlsl.frac = call <4 x float> @llvm.spv.frac.v4f32(
+// NO_HALF: ret <4 x float> %hlsl.frac
 half4 test_frac_half4(half4 p0) { return frac(p0); }
 
-// CHECK: define noundef float @
-// CHECK: %dx.frac = call float @llvm.dx.frac.f32(
-// CHECK: ret float %dx.frac
+// DXIL_CHECK: define noundef float @
+// SPIR_CHECK: define spir_func noundef float @
+// DXIL_CHECK: %hlsl.frac = call float @llvm.dx.frac.f32(
+// SPIR_CHECK: %hlsl.frac = call float @llvm.spv.frac.f32(
+// CHECK: ret float %hlsl.frac
 float test_frac_float(float p0) { return frac(p0); }
-// CHECK: define noundef <2 x float> @
-// CHECK: %dx.frac = call <2 x float> @llvm.dx.frac.v2f32
-// CHECK: ret <2 x float> %dx.frac
+// DXIL_CHECK: define noundef <2 x float> @
+// SPIR_CHECK: define spir_func noundef <2 x float> @
+// DXIL_CHECK: %hlsl.frac = call <2 x float> @llvm.dx.frac.v2f32
+// SPIR_CHECK: %hlsl.frac = call <2 x float> @llvm.spv.frac.v2f32
+// CHECK: ret <2 x float> %hlsl.frac
 float2 test_frac_float2(float2 p0) { return frac(p0); }
-// CHECK: define noundef <3 x float> @
-// CHECK: %dx.frac = call <3 x float> @llvm.dx.frac.v3f32
-// CHECK: ret <3 x float> %dx.frac
+// DXIL_CHECK: define noundef <3 x float> @
+// SPIR_CHECK: define spir_func noundef <3 x float> @
+// DXIL_CHECK: %hlsl.frac = call <3 x float> @llvm.dx.frac.v3f32
+// SPIR_CHECK: %hlsl.frac = call <3 x float> @llvm.spv.frac.v3f32
+// CHECK: ret <3 x float> %hlsl.frac
 float3 test_frac_float3(float3 p0) { return frac(p0); }
-// CHECK: define noundef <4 x float> @
-// CHECK: %dx.frac = call <4 x float> @llvm.dx.frac.v4f32
-// CHECK: ret <4 x float> %dx.frac
+// DXIL_CHECK: define noundef <4 x float> @
+// SPIR_CHECK: define spir_func noundef <4 x float> @
+// DXIL_CHECK: %hlsl.frac = call <4 x float> @llvm.dx.frac.v4f32
+// SPIR_CHECK: %hlsl.frac = call <4 x float> @llvm.spv.frac.v4f32
+// CHECK: ret <4 x float> %hlsl.frac
 float4 test_frac_float4(float4 p0) { return frac(p0); }
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 683acf4a6ffa9..ef6ddf12c32f6 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -60,6 +60,7 @@ let TargetPrefix = "spv" in {
       Intrinsic<[ llvm_ptr_ty ], [llvm_i8_ty], [IntrWillReturn]>;
   def int_spv_all : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty]>;
   def int_spv_any : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty]>;
+  def int_spv_frac : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]>;
   def int_spv_lerp : Intrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>,LLVMMatchType<0>], 
     [IntrNoMem, IntrWillReturn] >;
   def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]>;
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 9be736ce88ce4..74aeb7aec06a7 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -173,6 +173,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
   bool selectFmix(Register ResVReg, const SPIRVType *ResType,
                   MachineInstr &I) const;
 
+  bool selectFrac(Register ResVReg, const SPIRVType *ResType,
+                  MachineInstr &I) const;
+
   bool selectRsqrt(Register ResVReg, const SPIRVType *ResType,
                    MachineInstr &I) const;
 
@@ -1330,6 +1333,23 @@ bool SPIRVInstructionSelector::selectFmix(Register ResVReg,
       .constrainAllUses(TII, TRI, RBI);
 }
 
+bool SPIRVInstructionSelector::selectFrac(Register ResVReg,
+                                           const SPIRVType *ResType,
+                                           MachineInstr &I) const {
+
+  assert(I.getNumOperands() == 3);
+  assert(I.getOperand(2).isReg());
+  MachineBasicBlock &BB = *I.getParent();
+
+  return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
+      .addDef(ResVReg)
+      .addUse(GR.getSPIRVTypeID(ResType))
+      .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
+      .addImm(GL::Fract)
+      .addUse(I.getOperand(2).getReg())
+      .constrainAllUses(TII, TRI, RBI);
+}
+
 bool SPIRVInstructionSelector::selectRsqrt(Register ResVReg,
                                            const SPIRVType *ResType,
                                            MachineInstr &I) const {
@@ -2059,6 +2079,8 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
     return selectAny(ResVReg, ResType, I);
   case Intrinsic::spv_lerp:
     return selectFmix(ResVReg, ResType, I);
+  case Intrinsic::spv_frac:
+    return selectFrac(ResVReg, ResType, I);
   case Intrinsic::spv_rsqrt:
     return selectRsqrt(ResVReg, ResType, I);
   case Intrinsic::spv_lifetime_start:
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/frac.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/frac.ll
new file mode 100644
index 0000000000000..3c48782a18586
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/frac.ll
@@ -0,0 +1,68 @@
+; RUN: llc -O0 -mtriple=spirv-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: %[[#op_ext_glsl:]] = OpExtInstImport "GLSL.std.450"
+
+; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#float_16:]] = OpTypeFloat 16
+; CHECK-DAG: %[[#float_64:]] = OpTypeFloat 64
+
+; CHECK-DAG: %[[#vec4_float_32:]] = OpTypeVector %[[#float_32]] 4
+; CHECK-DAG: %[[#vec4_float_16:]] = OpTypeVector %[[#float_16]] 4
+; CHECK-DAG: %[[#vec4_float_64:]] = OpTypeVector %[[#float_64]] 4
+
+define noundef float @frac_float(float noundef %a) {
+entry:
+; CHECK: %[[#float_32_arg:]] = OpFunctionParameter %[[#float_32]]
+; CHECK: %[[#]] = OpExtInst %[[#float_32]] %[[#op_ext_glsl]] Fract %[[#float_32_arg]]
+  %elt.frac = call float @llvm.spv.frac.f32(float %a)
+  ret float %elt.frac
+}
+
+define noundef half @frac_half(half noundef %a) {
+entry:
+; CHECK: %[[#float_16_arg:]] = OpFunctionParameter %[[#float_16]]
+; CHECK: %[[#]] = OpExtInst %[[#float_16]] %[[#op_ext_glsl]] Fract %[[#float_16_arg]]
+  %elt.frac = call half @llvm.spv.frac.f16(half %a)
+  ret half %elt.frac
+}
+
+define noundef double @frac_double(double noundef %a) {
+entry:
+; CHECK: %[[#float_64_arg:]] = OpFunctionParameter %[[#float_64]]
+; CHECK: %[[#]] = OpExtInst %[[#float_64]] %[[#op_ext_glsl]] Fract %[[#float_64_arg]]
+  %elt.frac = call double @llvm.spv.frac.f64(double %a)
+  ret double %elt.frac
+}
+
+define noundef <4 x float> @frac_float_vector(<4 x float> noundef %a) {
+entry:
+; CHECK: %[[#vec4_float_32_arg:]] = OpFunctionParameter %[[#vec4_float_32]]
+; CHECK: %[[#]] = OpExtInst %[[#vec4_float_32]] %[[#op_ext_glsl]] Fract %[[#vec4_float_32_arg]]
+  %elt.frac = call <4 x float> @llvm.spv.frac.v4f32(<4 x float> %a)
+  ret <4 x float> %elt.frac
+}
+
+define noundef <4 x half> @frac_half_vector(<4 x half> noundef %a) {
+entry:
+; CHECK: %[[#vec4_float_16_arg:]] = OpFunctionParameter %[[#vec4_float_16]]
+; CHECK: %[[#]] = OpExtInst %[[#vec4_float_16]] %[[#op_ext_glsl]] Fract %[[#vec4_float_16_arg]]
+  %elt.frac = call <4 x half> @llvm.spv.frac.v4f16(<4 x half> %a)
+  ret <4 x half> %elt.frac
+}
+
+define noundef <4 x double> @frac_double_vector(<4 x double> noundef %a) {
+entry:
+; CHECK: %[[#vec4_float_64_arg:]] = OpFunctionParameter %[[#vec4_float_64]]
+; CHECK: %[[#]] = OpExtInst %[[#vec4_float_64]] %[[#op_ext_glsl]] Fract %[[#vec4_float_64_arg]]
+  %elt.frac = call <4 x double> @llvm.spv.frac.v4f64(<4 x double> %a)
+  ret <4 x double> %elt.frac
+}
+
+declare half @llvm.spv.frac.f16(half)
+declare float @llvm.spv.frac.f32(float)
+declare double @llvm.spv.frac.f64(double)
+
+declare <4 x float> @llvm.spv.frac.v4f32(<4 x float>)
+declare <4 x half> @llvm.spv.frac.v4f16(<4 x half>)
+declare <4 x double> @llvm.spv.frac.v4f64(<4 x double>)

@llvmbot
Copy link
Member

llvmbot commented Jun 28, 2024

@llvm/pr-subscribers-backend-spir-v

Author: Andrii Levytskyi (aabysswalker)

Changes

Implements frac lowering to SPIR-V.

Closes #88059


Full diff: https://github.com/llvm/llvm-project/pull/97111.diff

6 Files Affected:

  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+4-4)
  • (modified) clang/lib/CodeGen/CGHLSLRuntime.h (+1)
  • (modified) clang/test/CodeGenHLSL/builtins/frac.hlsl (+70-39)
  • (modified) llvm/include/llvm/IR/IntrinsicsSPIRV.td (+1)
  • (modified) llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp (+22)
  • (added) llvm/test/CodeGen/SPIRV/hlsl-intrinsics/frac.ll (+68)
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 98c2f70664ec7..418df5bc5935a 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -18312,10 +18312,10 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
     if (!E->getArg(0)->getType()->hasFloatingRepresentation())
       llvm_unreachable("frac operand must have a float representation");
     return Builder.CreateIntrinsic(
-        /*ReturnType=*/Op0->getType(), Intrinsic::dx_frac,
-        ArrayRef<Value *>{Op0}, nullptr, "dx.frac");
-  }
-  case Builtin::BI__builtin_hlsl_elementwise_isinf: {
+        /*ReturnType=*/Op0->getType(), CGM.getHLSLRuntime().getFracIntrinsic(),
+        ArrayRef<Value *>{Op0}, nullptr, "hlsl.frac");
+}
+case Builtin::BI__builtin_hlsl_elementwise_isinf: {
     Value *Op0 = EmitScalarExpr(E->getArg(0));
     llvm::Type *Xty = Op0->getType();
     llvm::Type *retType = llvm::Type::getInt1Ty(this->getLLVMContext());
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index 4036ce711bea1..8c067f4963955 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -74,6 +74,7 @@ class CGHLSLRuntime {
 
   GENERATE_HLSL_INTRINSIC_FUNCTION(All, all)
   GENERATE_HLSL_INTRINSIC_FUNCTION(Any, any)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(Frac, frac)
   GENERATE_HLSL_INTRINSIC_FUNCTION(Lerp, lerp)
   GENERATE_HLSL_INTRINSIC_FUNCTION(Rsqrt, rsqrt)
   GENERATE_HLSL_INTRINSIC_FUNCTION(ThreadId, thread_id)
diff --git a/clang/test/CodeGenHLSL/builtins/frac.hlsl b/clang/test/CodeGenHLSL/builtins/frac.hlsl
index 7c4d1468e96d2..b457f5c278791 100644
--- a/clang/test/CodeGenHLSL/builtins/frac.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/frac.hlsl
@@ -1,53 +1,84 @@
 // RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
 // RUN:   dxil-pc-shadermodel6.3-library %s -fnative-half-type \
-// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s \ 
-// RUN:   --check-prefixes=CHECK,NATIVE_HALF
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
+// RUN:   --check-prefixes=CHECK,DXIL_CHECK,DXIL_NATIVE_HALF,NATIVE_HALF
 // RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
 // RUN:   dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \
-// RUN:   -o - | FileCheck %s --check-prefixes=CHECK,NO_HALF
+// RUN:   -o - | FileCheck %s --check-prefixes=CHECK,DXIL_CHECK,NO_HALF,DXIL_NO_HALF
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
+// RUN:   spirv-unknown-vulkan-compute %s -fnative-half-type \
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
+// RUN:   --check-prefixes=CHECK,SPIR_CHECK,NATIVE_HALF,SPIR_NATIVE_HALF
+// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
+// RUN:   spirv-unknown-vulkan-compute %s -emit-llvm -disable-llvm-passes \
+// RUN:   -o - | FileCheck %s --check-prefixes=CHECK,SPIR_CHECK,NO_HALF,SPIR_NO_HALF
 
-// NATIVE_HALF: define noundef half @
-// NATIVE_HALF: %dx.frac = call half @llvm.dx.frac.f16(
-// NATIVE_HALF: ret half %dx.frac
-// NO_HALF: define noundef float @"?test_frac_half@@YA$halff@$halff@@Z"(
-// NO_HALF: %dx.frac = call float @llvm.dx.frac.f32(
-// NO_HALF: ret float %dx.frac
+// DXIL_NATIVE_HALF: define noundef half @
+// SPIR_NATIVE_HALF: define spir_func noundef half @
+// DXIL_NATIVE_HALF: %hlsl.frac = call half @llvm.dx.frac.f16(
+// SPIR_NATIVE_HALF: %hlsl.frac = call half @llvm.spv.frac.f16(
+// NATIVE_HALF: ret half %hlsl.frac
+// DXIL_NO_HALF: define noundef float @
+// SPIR_NO_HALF: define spir_func noundef float @
+// DXIL_NO_HALF: %hlsl.frac = call float @llvm.dx.frac.f32(
+// SPIR_NO_HALF: %hlsl.frac = call float @llvm.spv.frac.f32(
+// NO_HALF: ret float %hlsl.frac
 half test_frac_half(half p0) { return frac(p0); }
-// NATIVE_HALF: define noundef <2 x half> @
-// NATIVE_HALF: %dx.frac = call <2 x half> @llvm.dx.frac.v2f16
-// NATIVE_HALF: ret <2 x half> %dx.frac
-// NO_HALF: define noundef <2 x float> @
-// NO_HALF: %dx.frac = call <2 x float> @llvm.dx.frac.v2f32(
-// NO_HALF: ret <2 x float> %dx.frac
+// DXIL_NATIVE_HALF: define noundef <2 x half> @
+// SPIR_NATIVE_HALF: define spir_func noundef <2 x half> @
+// DXIL_NATIVE_HALF: %hlsl.frac = call <2 x half> @llvm.dx.frac.v2f16
+// SPIR_NATIVE_HALF: %hlsl.frac = call <2 x half> @llvm.spv.frac.v2f16
+// NATIVE_HALF: ret <2 x half> %hlsl.frac
+// DXIL_NO_HALF: define noundef <2 x float> @
+// SPIR_NO_HALF: define spir_func noundef <2 x float> @
+// DXIL_NO_HALF: %hlsl.frac = call <2 x float> @llvm.dx.frac.v2f32(
+// SPIR_NO_HALF: %hlsl.frac = call <2 x float> @llvm.spv.frac.v2f32(
+// NO_HALF: ret <2 x float> %hlsl.frac
 half2 test_frac_half2(half2 p0) { return frac(p0); }
-// NATIVE_HALF: define noundef <3 x half> @
-// NATIVE_HALF: %dx.frac = call <3 x half> @llvm.dx.frac.v3f16
-// NATIVE_HALF: ret <3 x half> %dx.frac
-// NO_HALF: define noundef <3 x float> @
-// NO_HALF: %dx.frac = call <3 x float> @llvm.dx.frac.v3f32(
-// NO_HALF: ret <3 x float> %dx.frac
+// DXIL_NATIVE_HALF: define noundef <3 x half> @
+// SPIR_NATIVE_HALF: define spir_func noundef <3 x half> @
+// DXIL_NATIVE_HALF: %hlsl.frac = call <3 x half> @llvm.dx.frac.v3f16
+// SPIR_NATIVE_HALF: %hlsl.frac = call <3 x half> @llvm.spv.frac.v3f16
+// NATIVE_HALF: ret <3 x half> %hlsl.frac
+// DXIL_NO_HALF: define noundef <3 x float> @
+// SPIR_NO_HALF: define spir_func noundef <3 x float> @
+// DXIL_NO_HALF: %hlsl.frac = call <3 x float> @llvm.dx.frac.v3f32(
+// SPIR_NO_HALF: %hlsl.frac = call <3 x float> @llvm.spv.frac.v3f32(
+// NO_HALF: ret <3 x float> %hlsl.frac
 half3 test_frac_half3(half3 p0) { return frac(p0); }
-// NATIVE_HALF: define noundef <4 x half> @
-// NATIVE_HALF: %dx.frac = call <4 x half> @llvm.dx.frac.v4f16
-// NATIVE_HALF: ret <4 x half> %dx.frac
-// NO_HALF: define noundef <4 x float> @
-// NO_HALF: %dx.frac = call <4 x float> @llvm.dx.frac.v4f32(
-// NO_HALF: ret <4 x float> %dx.frac
+// DXIL_NATIVE_HALF: define noundef <4 x half> @
+// SPIR_NATIVE_HALF: define spir_func noundef <4 x half> @
+// DXIL_NATIVE_HALF: %hlsl.frac = call <4 x half> @llvm.dx.frac.v4f16
+// SPIR_NATIVE_HALF: %hlsl.frac = call <4 x half> @llvm.spv.frac.v4f16
+// NATIVE_HALF: ret <4 x half> %hlsl.frac
+// DXIL_NO_HALF: define noundef <4 x float> @
+// SPIR_NO_HALF: define spir_func noundef <4 x float> @
+// DXIL_NO_HALF: %hlsl.frac = call <4 x float> @llvm.dx.frac.v4f32(
+// SPIR_NO_HALF: %hlsl.frac = call <4 x float> @llvm.spv.frac.v4f32(
+// NO_HALF: ret <4 x float> %hlsl.frac
 half4 test_frac_half4(half4 p0) { return frac(p0); }
 
-// CHECK: define noundef float @
-// CHECK: %dx.frac = call float @llvm.dx.frac.f32(
-// CHECK: ret float %dx.frac
+// DXIL_CHECK: define noundef float @
+// SPIR_CHECK: define spir_func noundef float @
+// DXIL_CHECK: %hlsl.frac = call float @llvm.dx.frac.f32(
+// SPIR_CHECK: %hlsl.frac = call float @llvm.spv.frac.f32(
+// CHECK: ret float %hlsl.frac
 float test_frac_float(float p0) { return frac(p0); }
-// CHECK: define noundef <2 x float> @
-// CHECK: %dx.frac = call <2 x float> @llvm.dx.frac.v2f32
-// CHECK: ret <2 x float> %dx.frac
+// DXIL_CHECK: define noundef <2 x float> @
+// SPIR_CHECK: define spir_func noundef <2 x float> @
+// DXIL_CHECK: %hlsl.frac = call <2 x float> @llvm.dx.frac.v2f32
+// SPIR_CHECK: %hlsl.frac = call <2 x float> @llvm.spv.frac.v2f32
+// CHECK: ret <2 x float> %hlsl.frac
 float2 test_frac_float2(float2 p0) { return frac(p0); }
-// CHECK: define noundef <3 x float> @
-// CHECK: %dx.frac = call <3 x float> @llvm.dx.frac.v3f32
-// CHECK: ret <3 x float> %dx.frac
+// DXIL_CHECK: define noundef <3 x float> @
+// SPIR_CHECK: define spir_func noundef <3 x float> @
+// DXIL_CHECK: %hlsl.frac = call <3 x float> @llvm.dx.frac.v3f32
+// SPIR_CHECK: %hlsl.frac = call <3 x float> @llvm.spv.frac.v3f32
+// CHECK: ret <3 x float> %hlsl.frac
 float3 test_frac_float3(float3 p0) { return frac(p0); }
-// CHECK: define noundef <4 x float> @
-// CHECK: %dx.frac = call <4 x float> @llvm.dx.frac.v4f32
-// CHECK: ret <4 x float> %dx.frac
+// DXIL_CHECK: define noundef <4 x float> @
+// SPIR_CHECK: define spir_func noundef <4 x float> @
+// DXIL_CHECK: %hlsl.frac = call <4 x float> @llvm.dx.frac.v4f32
+// SPIR_CHECK: %hlsl.frac = call <4 x float> @llvm.spv.frac.v4f32
+// CHECK: ret <4 x float> %hlsl.frac
 float4 test_frac_float4(float4 p0) { return frac(p0); }
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index 683acf4a6ffa9..ef6ddf12c32f6 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -60,6 +60,7 @@ let TargetPrefix = "spv" in {
       Intrinsic<[ llvm_ptr_ty ], [llvm_i8_ty], [IntrWillReturn]>;
   def int_spv_all : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty]>;
   def int_spv_any : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty]>;
+  def int_spv_frac : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]>;
   def int_spv_lerp : Intrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>,LLVMMatchType<0>], 
     [IntrNoMem, IntrWillReturn] >;
   def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]>;
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 9be736ce88ce4..74aeb7aec06a7 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -173,6 +173,9 @@ class SPIRVInstructionSelector : public InstructionSelector {
   bool selectFmix(Register ResVReg, const SPIRVType *ResType,
                   MachineInstr &I) const;
 
+  bool selectFrac(Register ResVReg, const SPIRVType *ResType,
+                  MachineInstr &I) const;
+
   bool selectRsqrt(Register ResVReg, const SPIRVType *ResType,
                    MachineInstr &I) const;
 
@@ -1330,6 +1333,23 @@ bool SPIRVInstructionSelector::selectFmix(Register ResVReg,
       .constrainAllUses(TII, TRI, RBI);
 }
 
+bool SPIRVInstructionSelector::selectFrac(Register ResVReg,
+                                           const SPIRVType *ResType,
+                                           MachineInstr &I) const {
+
+  assert(I.getNumOperands() == 3);
+  assert(I.getOperand(2).isReg());
+  MachineBasicBlock &BB = *I.getParent();
+
+  return BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
+      .addDef(ResVReg)
+      .addUse(GR.getSPIRVTypeID(ResType))
+      .addImm(static_cast<uint32_t>(SPIRV::InstructionSet::GLSL_std_450))
+      .addImm(GL::Fract)
+      .addUse(I.getOperand(2).getReg())
+      .constrainAllUses(TII, TRI, RBI);
+}
+
 bool SPIRVInstructionSelector::selectRsqrt(Register ResVReg,
                                            const SPIRVType *ResType,
                                            MachineInstr &I) const {
@@ -2059,6 +2079,8 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
     return selectAny(ResVReg, ResType, I);
   case Intrinsic::spv_lerp:
     return selectFmix(ResVReg, ResType, I);
+  case Intrinsic::spv_frac:
+    return selectFrac(ResVReg, ResType, I);
   case Intrinsic::spv_rsqrt:
     return selectRsqrt(ResVReg, ResType, I);
   case Intrinsic::spv_lifetime_start:
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/frac.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/frac.ll
new file mode 100644
index 0000000000000..3c48782a18586
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/frac.ll
@@ -0,0 +1,68 @@
+; RUN: llc -O0 -mtriple=spirv-unknown-unknown %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; CHECK-DAG: %[[#op_ext_glsl:]] = OpExtInstImport "GLSL.std.450"
+
+; CHECK-DAG: %[[#float_32:]] = OpTypeFloat 32
+; CHECK-DAG: %[[#float_16:]] = OpTypeFloat 16
+; CHECK-DAG: %[[#float_64:]] = OpTypeFloat 64
+
+; CHECK-DAG: %[[#vec4_float_32:]] = OpTypeVector %[[#float_32]] 4
+; CHECK-DAG: %[[#vec4_float_16:]] = OpTypeVector %[[#float_16]] 4
+; CHECK-DAG: %[[#vec4_float_64:]] = OpTypeVector %[[#float_64]] 4
+
+define noundef float @frac_float(float noundef %a) {
+entry:
+; CHECK: %[[#float_32_arg:]] = OpFunctionParameter %[[#float_32]]
+; CHECK: %[[#]] = OpExtInst %[[#float_32]] %[[#op_ext_glsl]] Fract %[[#float_32_arg]]
+  %elt.frac = call float @llvm.spv.frac.f32(float %a)
+  ret float %elt.frac
+}
+
+define noundef half @frac_half(half noundef %a) {
+entry:
+; CHECK: %[[#float_16_arg:]] = OpFunctionParameter %[[#float_16]]
+; CHECK: %[[#]] = OpExtInst %[[#float_16]] %[[#op_ext_glsl]] Fract %[[#float_16_arg]]
+  %elt.frac = call half @llvm.spv.frac.f16(half %a)
+  ret half %elt.frac
+}
+
+define noundef double @frac_double(double noundef %a) {
+entry:
+; CHECK: %[[#float_64_arg:]] = OpFunctionParameter %[[#float_64]]
+; CHECK: %[[#]] = OpExtInst %[[#float_64]] %[[#op_ext_glsl]] Fract %[[#float_64_arg]]
+  %elt.frac = call double @llvm.spv.frac.f64(double %a)
+  ret double %elt.frac
+}
+
+define noundef <4 x float> @frac_float_vector(<4 x float> noundef %a) {
+entry:
+; CHECK: %[[#vec4_float_32_arg:]] = OpFunctionParameter %[[#vec4_float_32]]
+; CHECK: %[[#]] = OpExtInst %[[#vec4_float_32]] %[[#op_ext_glsl]] Fract %[[#vec4_float_32_arg]]
+  %elt.frac = call <4 x float> @llvm.spv.frac.v4f32(<4 x float> %a)
+  ret <4 x float> %elt.frac
+}
+
+define noundef <4 x half> @frac_half_vector(<4 x half> noundef %a) {
+entry:
+; CHECK: %[[#vec4_float_16_arg:]] = OpFunctionParameter %[[#vec4_float_16]]
+; CHECK: %[[#]] = OpExtInst %[[#vec4_float_16]] %[[#op_ext_glsl]] Fract %[[#vec4_float_16_arg]]
+  %elt.frac = call <4 x half> @llvm.spv.frac.v4f16(<4 x half> %a)
+  ret <4 x half> %elt.frac
+}
+
+define noundef <4 x double> @frac_double_vector(<4 x double> noundef %a) {
+entry:
+; CHECK: %[[#vec4_float_64_arg:]] = OpFunctionParameter %[[#vec4_float_64]]
+; CHECK: %[[#]] = OpExtInst %[[#vec4_float_64]] %[[#op_ext_glsl]] Fract %[[#vec4_float_64_arg]]
+  %elt.frac = call <4 x double> @llvm.spv.frac.v4f64(<4 x double> %a)
+  ret <4 x double> %elt.frac
+}
+
+declare half @llvm.spv.frac.f16(half)
+declare float @llvm.spv.frac.f32(float)
+declare double @llvm.spv.frac.f64(double)
+
+declare <4 x float> @llvm.spv.frac.v4f32(<4 x float>)
+declare <4 x half> @llvm.spv.frac.v4f16(<4 x half>)
+declare <4 x double> @llvm.spv.frac.v4f64(<4 x double>)

Copy link
Contributor

@Keenuts Keenuts left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM on the SPIR-V side, leaving MS look at the HLSL side

Copy link
Contributor

@bogner bogner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@farzonl
Copy link
Member

farzonl commented Jul 23, 2024

LGTM, @aabysswalker did you get commit access? If not do you need help merging?

Copy link
Contributor

@coopp coopp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

@ritskii
Copy link
Contributor Author

ritskii commented Jul 23, 2024

@farzonl thanks for review! I didn't, can you merge instead of me?

@farzonl farzonl merged commit c92d9b0 into llvm:main Jul 23, 2024
14 checks passed
yuxuanchen1997 pushed a commit that referenced this pull request Jul 25, 2024
Summary:
Implements frac lowering to SPIR-V.

Closes #88059

Test Plan: 

Reviewers: 

Subscribers: 

Tasks: 

Tags: 


Differential Revision: https://phabricator.intern.facebook.com/D60251390
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:SPIR-V clang:codegen IR generation bugs: mangling, exceptions, etc. clang Clang issues not falling into any other category HLSL HLSL Language Support llvm:ir
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

[SPIRV][HLSL] Implement frac lowering
6 participants