Skip to content

Implement the fmod HLSL Function #99118

Closed
@farzonl

Description

@farzonl

NOTE: SPIRV implementation is already completed.
NOTE: The change here shold be a header only changes with test case updates done to
clang/test/SemaHLSL/BuiltIns and clang/test/CodeGenHLSL/builtins/fmod.hlsl. The SPIRV tests should remain unchanged and continue to work.

fmod is currently defined as

_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);

replace it with a templatized version

_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
const inline half fmod(half X, half Y) {
  return __detail::fmod_impl(X, Y);
}

const inline float fmod(float X, float Y) {
  return __detail::fmod_impl(X, Y);
}
template <int N, typename = std::enable_if_t<(N > 1 && N <=4)>>
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
const inline vector<half, N> fmod(vector<half, N> X, vector<half, N> Y) {
  return __detail::fmod_vec_impl(X, Y);
}

template <int N, typename = std::enable_if_t<(N > 1 && N <=4)>>
const inline vector<float, N> fmod(vector<float, N> X, vector<float, N> Y) {
  return __detail::fmod_vec_impl(X, Y);
}

This should call a version in clang/lib/Headers/hlsl/hlsl_detail.h
that will do the target switching for us.

template <typename T>
constexpr enable_if_t<is_same<float, T>::value || is_same<half, T>::value, T>
fmod_impl(T X, T Y) {
#if !defined(__DirectX__)
  return __builtin_elementwise_fmod(X, Y));
#else 
  return /*insert algorithmic approach here*/;
#endif
}
template <typename T, int N>
constexpr vector<T, N> fmod_vec_impl(vector<T, N> X, vector<T, N> Y) {
#if !defined(__DirectX__)
  return __builtin_elementwise_fmod(X, Y));
#else 
  return /*insert algorithmic approach here*/;
#endif
}

DirectX

DXIL Opcode DXIL OpName Shader Model Shader Stages
6, 22 FAbs, Frc 6.0 ()

For reference the algorithm you are expected to implement in HLSL source shold match the DXC implementation.
Please find the reference implmentation linked here: https://github.com/microsoft/DirectXShaderCompiler/blob/c2ed9ad4ee775f3de903ce757c994aecc59a5306/lib/HLSL/HLOperationLower.cpp#L2253C1-L2271C2

  %1 = fdiv fast float %p1, %p2
  %2 = fsub fast float -0.000000e+00, %1
  %3 = fcmp fast oge float %1, %2
  %FAbs = call float @dx.op.unary.f32(i32 6, float %1)
  %Frc = call float @dx.op.unary.f32(i32 22, float %FAbs)
  %4 = fsub fast float -0.000000e+00, %Frc
  %5 = select i1 %3, float %Frc, float %4
  %6 = fmul fast float %5, %p2

SPIR-V

OpFRem:

Description:

The floating-point remainder whose sign matches the sign
of Operand 1.

Result Type must be a scalar or vector of floating-point
type
.

The types of Operand 1 and Operand 2 both must be the same as
Result Type.

Results are computed per component. The resulting value is undefined if
Operand 2 is 0. Otherwise, the result is the remainder
r of Operand 1 divided by Operand 2 where if r ≠ 0, the sign of
r is the same as the sign of Operand 1.

Word Count Opcode Results Operands

5

140

<id>
Result Type

Result <id>

<id>
Operand 1

<id>
Operand 2

Test Case(s)

Example 1

//dxc fmod_test.hlsl -T lib_6_8 -enable-16bit-types -O0

export float4 fn(float4 p1, float4 p2) {
    return fmod(p1, p2);
}

HLSL:

Returns the floating-point remainder of x/y.

ret fmod(x, y)

Parameters

Item Description
x
[in] The floating-point dividend.
y
[in] The floating-point divisor.

Return Value

The floating-point remainder of the x parameter divided by the y parameter.

Remarks

The floating-point remainder is calculated such that x = i * y + f, where i is an integer, f has the same sign as x, and the absolute value of f is less than the absolute value of y.

Type Description

Name Template Type Component Type Size
x scalar, vector, or matrix float any
y same as input x float same dimension(s) as input x
ret same as input x float same dimension(s) as input x

Minimum Shader Model

This function is supported in the following shader models.

Shader Model Supported
Shader Model 2 (DirectX HLSL) and higher shader models yes
Shader Model 1 (DirectX HLSL) vs_1_1

Requirements

Requirement Value
Header
Corecrt_math.h

See also

Intrinsic Functions (DirectX HLSL)

Metadata

Metadata

Assignees

Labels

HLSLHLSL Language Supportbackend:DirectXbackend:SPIR-Vbot:HLSLclang:headersHeaders provided by Clang, e.g. for intrinsicsmetaissueIssue to collect references to a group of similar or related issues.

Type

No type

Projects

Status

Closed

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions