-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[HLSL] Implementation of the elementwise fmod builtin #108849
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
Conversation
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
@llvm/pr-subscribers-clang-codegen @llvm/pr-subscribers-backend-x86 Author: Zhengxing li (lizhengxing) ChangesThis change implements the frontend for #99118 Builtins.td - add the fmod builtin Full diff: https://github.com/llvm/llvm-project/pull/108849.diff 7 Files Affected:
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 6cf03d27055cd9..56a6813ecd205e 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4782,6 +4782,12 @@ def HLSLStep: LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}
+def HLSLFmod : LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_elementwise_fmod"];
+ let Attributes = [NoThrow, Const];
+ let Prototype = "void(...)";
+}
+
// Builtins for XRay.
def XRayCustomEvent : Builtin {
let Spellings = ["__xray_customevent"];
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index a52e880a764252..840c773020dfab 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -18709,6 +18709,25 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
CGM.getHLSLRuntime().getNormalizeIntrinsic(), ArrayRef<Value *>{X},
nullptr, "hlsl.normalize");
}
+ case Builtin::BI__builtin_hlsl_elementwise_fmod: {
+ Value *Op0 = EmitScalarExpr(E->getArg(0));
+ Value *Op1 = EmitScalarExpr(E->getArg(1));
+ if (!E->getArg(0)->getType()->hasFloatingRepresentation() ||
+ (E->getArg(0)->getType() != E->getArg(1)->getType()))
+ llvm_unreachable("fmod operands must have the same float representation");
+
+ llvm::Triple::ArchType Arch = CGM.getTarget().getTriple().getArch();
+ assert(((Arch == llvm::Triple::dxil) || (Arch == llvm::Triple::spirv)) &&
+ "Unknown target architecture");
+
+ if (CGM.getTarget().getTriple().getArch() == llvm::Triple::dxil)
+ return Builder.CreateIntrinsic(
+ /*ReturnType=*/Op0->getType(), Intrinsic::dx_fmod,
+ ArrayRef<Value *>{Op0, Op1}, nullptr, "dx.fmod");
+
+ // HLSL's Fmod builtin is equivalent to SPIRV's OpFRem instruction
+ return Builder.CreateFRem(Op0, Op1, "fmod");
+ }
case Builtin::BI__builtin_hlsl_elementwise_frac: {
Value *Op0 = EmitScalarExpr(E->getArg(0));
if (!E->getArg(0)->getType()->hasFloatingRepresentation())
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index d08dcd350d558b..6ff45e191923e8 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -807,6 +807,39 @@ float3 floor(float3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_floor)
float4 floor(float4);
+//===----------------------------------------------------------------------===//
+// fmod builtins
+//===----------------------------------------------------------------------===//
+
+/// \fn T fmod(T x, T y)
+/// \brief Returns the linear interpolation of x to y.
+/// \param x [in] The dividend.
+/// \param y [in] The divisor.
+///
+/// Return the floating-point remainder of the x parameter divided by the y parameter.
+
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_fmod)
+half fmod(half, half);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_fmod)
+half2 fmod(half2, half2);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_fmod)
+half3 fmod(half3, half3);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_fmod)
+half4 fmod(half4, half4);
+
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_fmod)
+float fmod(float, float);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_fmod)
+float2 fmod(float2, float2);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_fmod)
+float3 fmod(float3, float3);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_fmod)
+float4 fmod(float4, float4);
+
//===----------------------------------------------------------------------===//
// frac builtins
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 527718c8e7e324..e144d649b6c427 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1649,6 +1649,18 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
return true;
break;
}
+ case Builtin::BI__builtin_hlsl_elementwise_fmod: {
+ if (SemaRef.checkArgCount(TheCall, 2))
+ return true;
+ if (CheckFloatOrHalfRepresentations(&SemaRef, TheCall))
+ return true;
+
+ ExprResult A = TheCall->getArg(0);
+ QualType ArgTyA = A.get()->getType();
+ // return type is the same as the input type
+ TheCall->setType(ArgTyA);
+ break;
+ }
case Builtin::BI__builtin_hlsl_select: {
if (SemaRef.checkArgCount(TheCall, 3))
return true;
diff --git a/clang/test/CodeGenHLSL/builtins/fmod.hlsl b/clang/test/CodeGenHLSL/builtins/fmod.hlsl
new file mode 100644
index 00000000000000..228bbdef76dc39
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/fmod.hlsl
@@ -0,0 +1,122 @@
+// DirectX target:
+//
+// 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=DX-CHECK,DX-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=DX-CHECK,DX-NO_HALF
+
+
+
+// Spirv target:
+//
+// 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=SPV-CHECK,SPV-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=SPV-CHECK,SPV-NO_HALF
+
+
+
+// DX-NATIVE_HALF: define noundef half @
+// DX-NATIVE_HALF: %dx.fmod = call half @llvm.dx.fmod.f16(
+// DX-NATIVE_HALF: ret half %dx.fmod
+// DX-NO_HALF: define noundef float @
+// DX-NO_HALF: %dx.fmod = call float @llvm.dx.fmod.f32(
+// DX-NO_HALF: ret float %dx.fmod
+//
+// SPV-NATIVE_HALF: define spir_func noundef half @
+// SPV-NATIVE_HALF: %fmod = frem half
+// SPV-NATIVE_HALF: ret half %fmod
+// SPV-NO_HALF: define spir_func noundef float @
+// SPV-NO_HALF: %fmod = frem float
+// SPV-NO_HALF: ret float %fmod
+half test_fmod_half(half p0, half p1) { return fmod(p0, p1); }
+
+// DX-NATIVE_HALF: define noundef <2 x half> @
+// DX-NATIVE_HALF: %dx.fmod = call <2 x half> @llvm.dx.fmod.v2f16
+// DX-NATIVE_HALF: ret <2 x half> %dx.fmod
+// DX-NO_HALF: define noundef <2 x float> @
+// DX-NO_HALF: %dx.fmod = call <2 x float> @llvm.dx.fmod.v2f32(
+// DX-NO_HALF: ret <2 x float> %dx.fmod
+//
+// SPV-NATIVE_HALF: define spir_func noundef <2 x half> @
+// SPV-NATIVE_HALF: %fmod = frem <2 x half>
+// SPV-NATIVE_HALF: ret <2 x half> %fmod
+// SPV-NO_HALF: define spir_func noundef <2 x float> @
+// SPV-NO_HALF: %fmod = frem <2 x float>
+// SPV-NO_HALF: ret <2 x float> %fmod
+half2 test_fmod_half2(half2 p0, half2 p1) { return fmod(p0, p1); }
+
+// DX-NATIVE_HALF: define noundef <3 x half> @
+// DX-NATIVE_HALF: %dx.fmod = call <3 x half> @llvm.dx.fmod.v3f16
+// DX-NATIVE_HALF: ret <3 x half> %dx.fmod
+// DX-NO_HALF: define noundef <3 x float> @
+// DX-NO_HALF: %dx.fmod = call <3 x float> @llvm.dx.fmod.v3f32(
+// DX-NO_HALF: ret <3 x float> %dx.fmod
+//
+// SPV-NATIVE_HALF: define spir_func noundef <3 x half> @
+// SPV-NATIVE_HALF: %fmod = frem <3 x half>
+// SPV-NATIVE_HALF: ret <3 x half> %fmod
+// SPV-NO_HALF: define spir_func noundef <3 x float> @
+// SPV-NO_HALF: %fmod = frem <3 x float>
+// SPV-NO_HALF: ret <3 x float> %fmod
+half3 test_fmod_half3(half3 p0, half3 p1) { return fmod(p0, p1); }
+
+// DX-NATIVE_HALF: define noundef <4 x half> @
+// DX-NATIVE_HALF: %dx.fmod = call <4 x half> @llvm.dx.fmod.v4f16
+// DX-NATIVE_HALF: ret <4 x half> %dx.fmod
+// DX-NO_HALF: define noundef <4 x float> @
+// DX-NO_HALF: %dx.fmod = call <4 x float> @llvm.dx.fmod.v4f32(
+// DX-NO_HALF: ret <4 x float> %dx.fmod
+//
+// SPV-NATIVE_HALF: define spir_func noundef <4 x half> @
+// SPV-NATIVE_HALF: %fmod = frem <4 x half>
+// SPV-NATIVE_HALF: ret <4 x half> %fmod
+// SPV-NO_HALF: define spir_func noundef <4 x float> @
+// SPV-NO_HALF: %fmod = frem <4 x float>
+// SPV-NO_HALF: ret <4 x float> %fmod
+half4 test_fmod_half4(half4 p0, half4 p1) { return fmod(p0, p1); }
+
+// DX-CHECK: define noundef float @
+// DX-CHECK: %dx.fmod = call float @llvm.dx.fmod.f32(
+// DX-CHECK: ret float %dx.fmod
+//
+// SPV-CHECK: define spir_func noundef float @
+// SPV-CHECK: %fmod = frem float
+// SPV-CHECK: ret float %fmod
+float test_fmod_float(float p0, float p1) { return fmod(p0, p1); }
+
+// DX-CHECK: define noundef <2 x float> @
+// DX-CHECK: %dx.fmod = call <2 x float> @llvm.dx.fmod.v2f32
+// DX-CHECK: ret <2 x float> %dx.fmod
+//
+// SPV-CHECK: define spir_func noundef <2 x float> @
+// SPV-CHECK: %fmod = frem <2 x float>
+// SPV-CHECK: ret <2 x float> %fmod
+float2 test_fmod_float2(float2 p0, float2 p1) { return fmod(p0, p1); }
+
+// DX-CHECK: define noundef <3 x float> @
+// DX-CHECK: %dx.fmod = call <3 x float> @llvm.dx.fmod.v3f32
+// DX-CHECK: ret <3 x float> %dx.fmod
+//
+// SPV-CHECK: define spir_func noundef <3 x float> @
+// SPV-CHECK: %fmod = frem <3 x float>
+// SPV-CHECK: ret <3 x float> %fmod
+float3 test_fmod_float3(float3 p0, float3 p1) { return fmod(p0, p1); }
+
+// DX-CHECK: define noundef <4 x float> @
+// DX-CHECK: %dx.fmod = call <4 x float> @llvm.dx.fmod.v4f32
+// DX-CHECK: ret <4 x float> %dx.fmod
+//
+// SPV-CHECK: define spir_func noundef <4 x float> @
+// SPV-CHECK: %fmod = frem <4 x float>
+// SPV-CHECK: ret <4 x float> %fmod
+float4 test_fmod_float4(float4 p0, float4 p1) { return fmod(p0, p1); }
+
diff --git a/clang/test/SemaHLSL/BuiltIns/fmod-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/fmod-errors.hlsl
new file mode 100644
index 00000000000000..ce1940403ba53b
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/fmod-errors.hlsl
@@ -0,0 +1,38 @@
+
+// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -emit-llvm-only -disable-llvm-passes -verify -verify-ignore-unexpected
+
+float test_too_few_arg() {
+ return __builtin_hlsl_elementwise_fmod();
+ // expected-error@-1 {{too few arguments to function call, expected 2, have 0}}
+}
+
+float2 test_too_many_arg(float2 p0, float2 p1, float2 p3) {
+ return __builtin_hlsl_elementwise_fmod(p0, p1, p3);
+ // expected-error@-1 {{too many arguments to function call, expected 2, have 3}}
+}
+
+float builtin_bool_to_float_type_promotion(bool p1, bool p2) {
+ return __builtin_hlsl_elementwise_fmod(p1, p2);
+ // expected-error@-1 {{passing 'bool' to parameter of incompatible type 'float'}}
+}
+
+float builtin_fmod_int_to_float_promotion(int p1, int p2) {
+ return __builtin_hlsl_elementwise_fmod(p1, p2);
+ // expected-error@-1 {{passing 'int' to parameter of incompatible type 'float'}}
+}
+
+float2 builtin_fmod_int2_to_float2_promotion(int2 p1, int2 p2) {
+ return __builtin_hlsl_elementwise_fmod(p1, p2);
+ // expected-error@-1 {{passing 'int2' (aka 'vector<int, 2>') to parameter of incompatible type '__attribute__((__vector_size__(2 * sizeof(float)))) float' (vector of 2 'float' values)}}
+}
+
+// builtins are variadic functions and so are subject to DefaultVariadicArgumentPromotion
+half builtin_fmod_half_scalar (half p0, half p1) {
+ return __builtin_hlsl_elementwise_fmod(p0, p1);
+ // expected-error@-1 {{passing 'double' to parameter of incompatible type 'float'}}
+}
+
+float builtin_fmod_float_scalar (float p0, float p1) {
+ return __builtin_hlsl_elementwise_fmod (p0, p1);
+ // expected-error@-1 {{passing 'double' to parameter of incompatible type 'float'}}
+}
\ No newline at end of file
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index 97c69638fbb0ef..cadc7c1e8ba0fd 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -90,4 +90,5 @@ def int_dx_rsqrt : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]
def int_dx_wave_is_first_lane : DefaultAttrsIntrinsic<[llvm_i1_ty], [], [IntrConvergent]>;
def int_dx_sign : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_any_ty]>;
def int_dx_step : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>]>;
+def int_dx_fmod : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>]>;
}
|
565ec72
to
9942e31
Compare
9942e31
to
a12d65f
Compare
The SPIRV frem testing doesn't appear to be complete in for all our cases we defined in Personally I'd like to see at least one test case in: |
We should announce |
I'll add those codegen tests in the second PR for #99118 . That PR is backend specific. |
a12d65f
to
4201517
Compare
Done. |
This LGTM. There is one last thing we might want to do. But I'm not requiring it for sign off.
Also I'd take a look at the existing fmod test cases and make sure there is nothing we need to update there: clang/test/CodeGen/X86/math-builtins.c:11: f = __builtin_fmod(f,f); f = __builtin_fmodf(f,f); f = __builtin_fmodl(f,f); f = __builtin_fmodf128(f,f);
clang/test/CodeGen/builtins.c:318: resd = __builtin_fmod(D,D);
clang/test/CodeGen/constrained-math-builtins.c:11: f = __builtin_fmod(f,f); f = __builtin_fmodf(f,f); f = __builtin_fmodl(f,f); f = __builtin_fmodf128(f,f);
clang/test/CodeGenOpenCL/builtins-f16.cl:55: res = __builtin_fmodf16(h0, h1); |
change the title of this PR to remove the word intrinsic. That word has a specific meaning in LLVM and folks might get confused. Something like: If you really want to use the word intrinsic then just reuse the HLSL keyword so HLSL is a modifier to specify the kind of intrinsic: |
4201517
to
516d0e5
Compare
Done. |
Done. Renamed it to |
42edede
to
9ed71b8
Compare
✅ With the latest revision this PR passed the C/C++ code formatter. |
@lizhengxing please run clang-format. |
9ed71b8
to
a48abd9
Compare
This change add the elementwise fmod builtin to support HLSL function 'fmod' in clang for llvm#99118 Builtins.td - add the fmod builtin CGBuiltin.cpp - lower the builtin to llvm FRem instruction hlsl_intrinsics.h - add the fmod api SemaChecking.cpp - add type checks for builtin SemaHLSL.cpp - add HLSL type checks for builtin clang/docs/LanguageExtensions.rst - add the builtin in *Elementwise Builtins* clang/docs/ReleaseNotes.rst - announce the builtin
45b8d26
to
96a8f95
Compare
@lizhengxing Congratulations on having your first Pull Request (PR) merged into the LLVM Project! Your changes will be combined with recent changes from other authors, then tested by our build bots. If there is a problem with a build, you may receive a report in an email or a comment on this PR. Please check whether problems have been caused by your change specifically, as the builds can include changes from many authors. It is not uncommon for your change to be included in a build that fails due to someone else's changes, or infrastructure issues. How to do this, and the rest of the post-merge process, is covered in detail here. If your change does cause a problem, it may be reverted, or you can revert it yourself. This is a normal part of LLVM development. You can fix your changes and open a new PR to merge them again. If you don't get any reports, no action is required from you. Your changes are working as expected, well done! |
This change add the elementwise fmod builtin to support HLSL function 'fmod' in clang for #99118
Builtins.td - add the fmod builtin
CGBuiltin.cpp - lower the builtin to llvm FRem instruction
hlsl_intrinsics.h - add the fmod api
SemaChecking.cpp - add type checks for builtin
SemaHLSL.cpp - add HLSL type checks for builtin
clang/docs/LanguageExtensions.rst - add the builtin in Elementwise Builtins
clang/docs/ReleaseNotes.rst - announce the builtin