Skip to content

Commit

Permalink
[HLSL] Implementation of the elementwise fmod builtin
Browse files Browse the repository at this point in the history
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
  • Loading branch information
lizhengxing committed Sep 26, 2024
1 parent 0813c76 commit 516d0e5
Show file tree
Hide file tree
Showing 13 changed files with 237 additions and 2 deletions.
2 changes: 2 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,8 @@ Unless specified otherwise operation(±0) = ±0 and operation(±infinity) = ±in
T __builtin_elementwise_canonicalize(T x) return the platform specific canonical encoding floating point types
of a floating-point number
T __builtin_elementwise_copysign(T x, T y) return the magnitude of x with the sign of y. floating point types
T __builtin_elementwise_fmod(T x, T y) return The floating-point remainder of (x/y) whose sign floating point types
matches the sign of x.
T __builtin_elementwise_max(T x, T y) return x or y, whichever is larger integer and floating point types
T __builtin_elementwise_min(T x, T y) return x or y, whichever is smaller integer and floating point types
T __builtin_elementwise_add_sat(T x, T y) return the sum of x and y, clamped to the range of integer types
Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ C++ Language Changes

- Add ``__builtin_elementwise_popcount`` builtin for integer types only.

- Add ``__builtin_elementwise_fmod`` builtin for floating point types only.

- The builtin type alias ``__builtin_common_type`` has been added to improve the
performance of ``std::common_type``.

Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -1328,6 +1328,12 @@ def ElementwisePopcount : Builtin {
let Prototype = "void(...)";
}

def ElementwiseFmod : Builtin {
let Spellings = ["__builtin_elementwise_fmod"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "void(...)";
}

def ElementwisePow : Builtin {
let Spellings = ["__builtin_elementwise_pow"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2878,7 +2878,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
case Builtin::BI__builtin_fmodf:
case Builtin::BI__builtin_fmodf16:
case Builtin::BI__builtin_fmodl:
case Builtin::BI__builtin_fmodf128: {
case Builtin::BI__builtin_fmodf128:
case Builtin::BI__builtin_elementwise_fmod: {
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(*this, E);
Value *Arg1 = EmitScalarExpr(E->getArg(0));
Value *Arg2 = EmitScalarExpr(E->getArg(1));
Expand Down
33 changes: 33 additions & 0 deletions clang/lib/Headers/hlsl/hlsl_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,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_elementwise_fmod)
half fmod(half, half);
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_fmod)
half2 fmod(half2, half2);
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_fmod)
half3 fmod(half3, half3);
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_fmod)
half4 fmod(half4, half4);

_HLSL_BUILTIN_ALIAS(__builtin_elementwise_fmod)
float fmod(float, float);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_fmod)
float2 fmod(float2, float2);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_fmod)
float3 fmod(float3, float3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_fmod)
float4 fmod(float4, float4);

//===----------------------------------------------------------------------===//
// frac builtins
//===----------------------------------------------------------------------===//
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2755,6 +2755,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,

// These builtins restrict the element type to floating point
// types only, and take in two arguments.
case Builtin::BI__builtin_elementwise_fmod:
case Builtin::BI__builtin_elementwise_pow: {
if (BuiltinElementwiseMath(TheCall))
return ExprError();
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1645,6 +1645,18 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
return true;
break;
}
case Builtin::BI__builtin_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;
Expand Down
20 changes: 20 additions & 0 deletions clang/test/CodeGen/builtins-elementwise-math.c
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,26 @@ void test_builtin_elementwise_popcount(si8 vi1, si8 vi2,
si = __builtin_elementwise_popcount(si);
}

void test_builtin_elementwise_fmod(float f1, float f2, double d1, double d2,
float4 vf1, float4 vf2) {

// CHECK-LABEL: define void @test_builtin_elementwise_fmod(
// CHECK: [[F1:%.+]] = load float, ptr %f1.addr, align 4
// CHECK: [[F2:%.+]] = load float, ptr %f2.addr, align 4
// CHECK-NEXT: frem float [[F1]], [[F2]]
f2 = __builtin_elementwise_fmod(f1, f2);

// CHECK: [[D1:%.+]] = load double, ptr %d1.addr, align 8
// CHECK: [[D2:%.+]] = load double, ptr %d2.addr, align 8
// CHECK-NEXT: frem double [[D1]], [[D2]]
d2 = __builtin_elementwise_fmod(d1, d2);

// CHECK: [[VF1:%.+]] = load <4 x float>, ptr %vf1.addr, align 16
// CHECK: [[VF2:%.+]] = load <4 x float>, ptr %vf2.addr, align 16
// CHECK-NEXT: frem <4 x float> [[VF1]], [[VF2]]
vf2 = __builtin_elementwise_fmod(vf1, vf2);
}

void test_builtin_elementwise_pow(float f1, float f2, double d1, double d2,
float4 vf1, float4 vf2) {

Expand Down
11 changes: 11 additions & 0 deletions clang/test/CodeGen/strictfp-elementwise-bulitins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,14 @@ float4 strict_elementwise_fma(float4 a, float4 b, float4 c) {
float4 strict_elementwise_pow(float4 a, float4 b) {
return __builtin_elementwise_pow(a, b);
}

// CHECK-LABEL: define dso_local noundef <4 x float> @_Z23strict_elementwise_fmodDv4_fS_
// CHECK-SAME: (<4 x float> noundef [[A:%.*]], <4 x float> noundef [[B:%.*]]) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = tail call <4 x float> @llvm.experimental.constrained.frem.v4f32(<4 x float> [[A]], <4 x float> [[B]],
// CHECK-SAME: metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR4]]
// CHECK-NEXT: ret <4 x float> [[TMP0]]
//
float4 strict_elementwise_fmod(float4 a, float4 b) {
return __builtin_elementwise_fmod(a, b);
}
77 changes: 77 additions & 0 deletions clang/test/CodeGenHLSL/builtins/fmod.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// DirectX target:
//
// ---------- Native Half support test -----------
//
// 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: -DFNATTRS=noundef -DTYPE=half

//
// ---------- No Native Half support test -----------
//
// 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 \
// RUN: -DFNATTRS=noundef -DTYPE=float


// Spirv target:
//
// ---------- Native Half support test -----------
//
// 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: -DFNATTRS="spir_func noundef" -DTYPE=half

//
// ---------- No Native Half support test -----------
//
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
// RUN: spirv-unknown-vulkan-compute %s -emit-llvm -disable-llvm-passes \
// RUN: -o - | FileCheck %s \
// RUN: -DFNATTRS="spir_func noundef" -DTYPE=float



// CHECK: define [[FNATTRS]] [[TYPE]] @
// CHECK: %fmod = frem [[TYPE]]
// CHECK: ret [[TYPE]] %fmod
half test_fmod_half(half p0, half p1) { return fmod(p0, p1); }

// CHECK: define [[FNATTRS]] <2 x [[TYPE]]> @
// CHECK: %fmod = frem <2 x [[TYPE]]>
// CHECK: ret <2 x [[TYPE]]> %fmod
half2 test_fmod_half2(half2 p0, half2 p1) { return fmod(p0, p1); }

// CHECK: define [[FNATTRS]] <3 x [[TYPE]]> @
// CHECK: %fmod = frem <3 x [[TYPE]]>
// CHECK: ret <3 x [[TYPE]]> %fmod
half3 test_fmod_half3(half3 p0, half3 p1) { return fmod(p0, p1); }

// CHECK: define [[FNATTRS]] <4 x [[TYPE]]> @
// CHECK: %fmod = frem <4 x [[TYPE]]>
// CHECK: ret <4 x [[TYPE]]> %fmod
half4 test_fmod_half4(half4 p0, half4 p1) { return fmod(p0, p1); }

// CHECK: define [[FNATTRS]] float @
// CHECK: %fmod = frem float
// CHECK: ret float %fmod
float test_fmod_float(float p0, float p1) { return fmod(p0, p1); }

// CHECK: define [[FNATTRS]] <2 x float> @
// CHECK: %fmod = frem <2 x float>
// CHECK: ret <2 x float> %fmod
float2 test_fmod_float2(float2 p0, float2 p1) { return fmod(p0, p1); }

// CHECK: define [[FNATTRS]] <3 x float> @
// CHECK: %fmod = frem <3 x float>
// CHECK: ret <3 x float> %fmod
float3 test_fmod_float3(float3 p0, float3 p1) { return fmod(p0, p1); }

// CHECK: define [[FNATTRS]] <4 x float> @
// CHECK: %fmod = frem <4 x float>
// CHECK: ret <4 x float> %fmod
float4 test_fmod_float4(float4 p0, float4 p1) { return fmod(p0, p1); }

27 changes: 26 additions & 1 deletion clang/test/Sema/builtins-elementwise-math.c
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,32 @@ void test_builtin_elementwise_popcount(int i, float f, double d, float4 v, int3
// expected-error@-1 {{assigning to 'int3' (vector of 3 'int' values) from incompatible type 'unsigned3' (vector of 3 'unsigned int' values)}}
}

void test_builtin_elementwise_fmod(int i, short s, double d, float4 v, int3 iv, unsigned3 uv, int *p) {
i = __builtin_elementwise_fmod(p, d);
// expected-error@-1 {{arguments are of different types ('int *' vs 'double')}}

struct Foo foo = __builtin_elementwise_fmod(i, i);
// expected-error@-1 {{1st argument must be a floating point type (was 'int')}}

i = __builtin_elementwise_fmod(i);
// expected-error@-1 {{too few arguments to function call, expected 2, have 1}}

i = __builtin_elementwise_fmod();
// expected-error@-1 {{too few arguments to function call, expected 2, have 0}}

i = __builtin_elementwise_fmod(i, i, i);
// expected-error@-1 {{too many arguments to function call, expected 2, have 3}}

i = __builtin_elementwise_fmod(v, iv);
// expected-error@-1 {{arguments are of different types ('float4' (vector of 4 'float' values) vs 'int3' (vector of 3 'int' values))}}

i = __builtin_elementwise_fmod(uv, iv);
// expected-error@-1 {{arguments are of different types ('unsigned3' (vector of 3 'unsigned int' values) vs 'int3' (vector of 3 'int' values))}}

i = __builtin_elementwise_fmod(d, v);
// expected-error@-1 {{arguments are of different types ('double' vs 'float4' (vector of 4 'float' values))}}
}

void test_builtin_elementwise_pow(int i, short s, double d, float4 v, int3 iv, unsigned3 uv, int *p) {
i = __builtin_elementwise_pow(p, d);
// expected-error@-1 {{arguments are of different types ('int *' vs 'double')}}
Expand All @@ -562,7 +588,6 @@ void test_builtin_elementwise_pow(int i, short s, double d, float4 v, int3 iv, u

}


void test_builtin_elementwise_roundeven(int i, float f, double d, float4 v, int3 iv, unsigned u, unsigned4 uv) {

struct Foo s = __builtin_elementwise_roundeven(f);
Expand Down
8 changes: 8 additions & 0 deletions clang/test/SemaCXX/builtins-elementwise-math.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,14 @@ void test_builtin_elementwise_fma() {
static_assert(!is_const<decltype(__builtin_elementwise_fma(c, c, c))>::value);
}

void test_builtin_elementwise_fmod() {
const double a = 2;
double b = 1;
static_assert(!is_const<decltype(__builtin_elementwise_fmod(a, b))>::value);
static_assert(!is_const<decltype(__builtin_elementwise_fmod(b, a))>::value);
static_assert(!is_const<decltype(__builtin_elementwise_fmod(a, a))>::value);
}

void test_builtin_elementwise_pow() {
const double a = 2;
double b = 1;
Expand Down
37 changes: 37 additions & 0 deletions clang/test/SemaHLSL/BuiltIns/fmod-errors.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

// 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_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_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_elementwise_fmod(p1, p2);
// expected-error@-1 {{1st argument must be a vector, integer or floating point type (was 'bool')}}
}

float builtin_fmod_int_to_float_promotion(int p1, int p2) {
return __builtin_elementwise_fmod(p1, p2);
// expected-error@-1 {{1st argument must be a floating point type (was 'int')}}
}

float2 builtin_fmod_int2_to_float2_promotion(int2 p1, int2 p2) {
return __builtin_elementwise_fmod(p1, p2);
// expected-error@-1 {{1st argument must be a floating point type (was 'int2' (aka 'vector<int, 2>'))}}
}

half builtin_fmod_double_type (double p0, double p1) {
return __builtin_elementwise_fmod(p0, p1);
// expected-error@-1 {{passing 'double' to parameter of incompatible type 'float'}}
}

half builtin_fmod_double2_type (double2 p0, double2 p1) {
return __builtin_elementwise_fmod(p0, p1);
// expected-error@-1 {{passing 'double2' (aka 'vector<double, 2>') to parameter of incompatible type '__attribute__((__vector_size__(2 * sizeof(float)))) float' (vector of 2 'float' values)}}
}

0 comments on commit 516d0e5

Please sign in to comment.