Skip to content

[HLSL] Implement support for HLSL intrinsic - saturate #104619

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 4 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -4745,6 +4745,12 @@ def HLSLRSqrt : LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}

def HLSLSaturate : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_elementwise_saturate"];
let Attributes = [NoThrow, Const];
let Prototype = "void(...)";
}

// Builtins for XRay.
def XRayCustomEvent : Builtin {
let Spellings = ["__xray_customevent"];
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18667,6 +18667,15 @@ case Builtin::BI__builtin_hlsl_elementwise_isinf: {
/*ReturnType=*/Op0->getType(), CGM.getHLSLRuntime().getRsqrtIntrinsic(),
ArrayRef<Value *>{Op0}, nullptr, "hlsl.rsqrt");
}
case Builtin::BI__builtin_hlsl_elementwise_saturate: {
Value *Op0 = EmitScalarExpr(E->getArg(0));
assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
"saturate operand must have a float representation");
return Builder.CreateIntrinsic(
/*ReturnType=*/Op0->getType(),
CGM.getHLSLRuntime().getSaturateIntrinsic(), ArrayRef<Value *>{Op0},
nullptr, "hlsl.saturate");
}
case Builtin::BI__builtin_hlsl_wave_get_lane_index: {
return EmitRuntimeCall(CGM.CreateRuntimeFunction(
llvm::FunctionType::get(IntTy, {}, false), "__hlsl_wave_get_lane_index",
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGHLSLRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class CGHLSLRuntime {
GENERATE_HLSL_INTRINSIC_FUNCTION(Lerp, lerp)
GENERATE_HLSL_INTRINSIC_FUNCTION(Normalize, normalize)
GENERATE_HLSL_INTRINSIC_FUNCTION(Rsqrt, rsqrt)
GENERATE_HLSL_INTRINSIC_FUNCTION(Saturate, saturate)
GENERATE_HLSL_INTRINSIC_FUNCTION(ThreadId, thread_id)

//===----------------------------------------------------------------------===//
Expand Down
41 changes: 40 additions & 1 deletion clang/lib/Headers/hlsl/hlsl_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -916,7 +916,7 @@ float4 lerp(float4, float4, float4);
/// \brief Returns the length of the specified floating-point vector.
/// \param x [in] The vector of floats, or a scalar float.
///
/// Length is based on the following formula: sqrt(x[0]^2 + x[1]^2 + ).
/// Length is based on the following formula: sqrt(x[0]^2 + x[1]^2 + ...).

_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
Expand Down Expand Up @@ -1564,6 +1564,45 @@ float3 round(float3);
_HLSL_BUILTIN_ALIAS(__builtin_elementwise_roundeven)
float4 round(float4);

//===----------------------------------------------------------------------===//
// saturate builtins
//===----------------------------------------------------------------------===//

/// \fn T saturate(T Val)
/// \brief Returns input value, \a Val, clamped within the range of 0.0f
/// to 1.0f. \param Val The input value.

_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
half saturate(half);
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
half2 saturate(half2);
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
half3 saturate(half3);
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
half4 saturate(half4);

_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
float saturate(float);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
float2 saturate(float2);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
float3 saturate(float3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
float4 saturate(float4);

_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
double saturate(double);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
double2 saturate(double2);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
double3 saturate(double3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_saturate)
double4 saturate(double4);

//===----------------------------------------------------------------------===//
// sin builtins
//===----------------------------------------------------------------------===//
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ static bool isLegalTypeForHLSLSV_DispatchThreadID(QualType T) {
return true;
}

void SemaHLSL::handleSV_DispatchThreadIDAttr(Decl *D, const ParsedAttr &AL) {
void SemaHLSL::handleSV_DispatchThreadIDAttr(Decl *D, const ParsedAttr &AL) {
auto *VD = cast<ValueDecl>(D);
if (!isLegalTypeForHLSLSV_DispatchThreadID(VD->getType())) {
Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type)
Expand Down Expand Up @@ -1045,6 +1045,7 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
return true;
break;
}
case Builtin::BI__builtin_hlsl_elementwise_saturate:
case Builtin::BI__builtin_hlsl_elementwise_rcp: {
if (CheckAllArgsHaveFloatRepresentation(&SemaRef, TheCall))
return true;
Expand Down
95 changes: 95 additions & 0 deletions clang/test/CodeGenHLSL/builtins/saturate.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// RUN: %clang_cc1 -std=hlsl2021 -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: %clang_cc1 -std=hlsl2021 -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: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
// RUN: spirv-unknown-vulkan-library %s -fnative-half-type \
// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
// RUN: --check-prefixes=SPIRV,SPIRV_HALF
// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -x hlsl -triple \
// RUN: spirv-unknown-vulkan-library %s \
// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
// RUN: --check-prefixes=SPIRV,SPIRV_NO_HALF

// NATIVE_HALF: define noundef half @
// NATIVE_HALF: call half @llvm.dx.saturate.f16(
// NO_HALF: define noundef float @"?test_saturate_half
// NO_HALF: call float @llvm.dx.saturate.f32(
// SPIRV_HALF: define spir_func noundef half @_Z18test_saturate_halfDh(half
// SPIRV_HALF: call half @llvm.spv.saturate.f16(half
// SPIRV_NO_HALF: define spir_func noundef float @_Z18test_saturate_halfDh(float
// SPIRV_NO_HALF: call float @llvm.spv.saturate.f32(float
half test_saturate_half(half p0) { return saturate(p0); }
// NATIVE_HALF: define noundef <2 x half> @
// NATIVE_HALF: call <2 x half> @llvm.dx.saturate.v2f16
// NO_HALF: define noundef <2 x float> @"?test_saturate_half2
// NO_HALF: call <2 x float> @llvm.dx.saturate.v2f32(
// SPIRV_HALF: define spir_func noundef <2 x half> @_Z19test_saturate_half2Dv2_Dh(
// SPIRV_HALF: call <2 x half> @llvm.spv.saturate.v2f16(<2 x half>
// SPIRV_NO_HALF: define spir_func noundef <2 x float> @_Z19test_saturate_half2Dv2_Dh(<2 x float>
// SPIRV_NO_HALF: call <2 x float> @llvm.spv.saturate.v2f32(<2 x float>
half2 test_saturate_half2(half2 p0) { return saturate(p0); }
// NATIVE_HALF: define noundef <3 x half> @
// NATIVE_HALF: call <3 x half> @llvm.dx.saturate.v3f16
// NO_HALF: define noundef <3 x float> @"?test_saturate_half3
// NO_HALF: call <3 x float> @llvm.dx.saturate.v3f32(
// SPIRV_HALF: define spir_func noundef <3 x half> @_Z19test_saturate_half3Dv3_Dh(
// SPIRV_HALF: call <3 x half> @llvm.spv.saturate.v3f16(<3 x half>
// SPIRV_NO_HALF: define spir_func noundef <3 x float> @_Z19test_saturate_half3Dv3_Dh(<3 x float>
// SPIRV_NO_HALF: call <3 x float> @llvm.spv.saturate.v3f32(<3 x float>
half3 test_saturate_half3(half3 p0) { return saturate(p0); }
// NATIVE_HALF: define noundef <4 x half> @
// NATIVE_HALF: call <4 x half> @llvm.dx.saturate.v4f16
// NO_HALF: define noundef <4 x float> @"?test_saturate_half4
// NO_HALF: call <4 x float> @llvm.dx.saturate.v4f32(
// SPIRV_HALF: define spir_func noundef <4 x half> @_Z19test_saturate_half4Dv4_Dh(
// SPIRV_HALF: call <4 x half> @llvm.spv.saturate.v4f16(<4 x half>
// SPIRV_NO_HALF: define spir_func noundef <4 x float> @_Z19test_saturate_half4Dv4_Dh(<4 x float>
// SPIRV_NO_HALF: call <4 x float> @llvm.spv.saturate.v4f32(<4 x float>
half4 test_saturate_half4(half4 p0) { return saturate(p0); }

// CHECK: define noundef float @"?test_saturate_float
// CHECK: call float @llvm.dx.saturate.f32(
// SPIRV: define spir_func noundef float @_Z19test_saturate_floatf(float
// SPIRV: call float @llvm.spv.saturate.f32(float
float test_saturate_float(float p0) { return saturate(p0); }
// CHECK: define noundef <2 x float> @"?test_saturate_float2
// CHECK: call <2 x float> @llvm.dx.saturate.v2f32
// SPIRV: define spir_func noundef <2 x float> @_Z20test_saturate_float2Dv2_f(<2 x float>
// SPIRV: call <2 x float> @llvm.spv.saturate.v2f32(<2 x float>
float2 test_saturate_float2(float2 p0) { return saturate(p0); }
// CHECK: define noundef <3 x float> @"?test_saturate_float3
// CHECK: call <3 x float> @llvm.dx.saturate.v3f32
// SPIRV: define spir_func noundef <3 x float> @_Z20test_saturate_float3Dv3_f(<3 x float>
// SPIRV: call <3 x float> @llvm.spv.saturate.v3f32(<3 x float>
float3 test_saturate_float3(float3 p0) { return saturate(p0); }
// CHECK: define noundef <4 x float> @"?test_saturate_float4
// CHECK: call <4 x float> @llvm.dx.saturate.v4f32
// SPIRV: define spir_func noundef <4 x float> @_Z20test_saturate_float4Dv4_f(<4 x float>
// SPIRV: call <4 x float> @llvm.spv.saturate.v4f32(<4 x float>
float4 test_saturate_float4(float4 p0) { return saturate(p0); }

// CHECK: define noundef double @
// CHECK: call double @llvm.dx.saturate.f64(
// SPIRV: define spir_func noundef double @_Z20test_saturate_doubled(double
// SPIRV: call double @llvm.spv.saturate.f64(double
double test_saturate_double(double p0) { return saturate(p0); }
// CHECK: define noundef <2 x double> @
// CHECK: call <2 x double> @llvm.dx.saturate.v2f64
// SPIRV: define spir_func noundef <2 x double> @_Z21test_saturate_double2Dv2_d(<2 x double>
// SPIRV: call <2 x double> @llvm.spv.saturate.v2f64(<2 x double>
double2 test_saturate_double2(double2 p0) { return saturate(p0); }
// CHECK: define noundef <3 x double> @
// CHECK: call <3 x double> @llvm.dx.saturate.v3f64
// SPIRV: define spir_func noundef <3 x double> @_Z21test_saturate_double3Dv3_d(<3 x double>
// SPIRV: call <3 x double> @llvm.spv.saturate.v3f64(<3 x double>
double3 test_saturate_double3(double3 p0) { return saturate(p0); }
// CHECK: define noundef <4 x double> @
// CHECK: call <4 x double> @llvm.dx.saturate.v4f64
// SPIRV: define spir_func noundef <4 x double> @_Z21test_saturate_double4Dv4_d(<4 x double>
// SPIRV: call <4 x double> @llvm.spv.saturate.v4f64(<4 x double>
double4 test_saturate_double4(double4 p0) { return saturate(p0); }
31 changes: 31 additions & 0 deletions clang/test/SemaHLSL/BuiltIns/saturate-errors.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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 -Werror

float2 test_no_arg() {
return saturate();
// expected-error@-1 {{no matching function for call to 'saturate'}}
}

float2 test_too_many_arg(float2 p0) {
return saturate(p0, p0, p0, p0);
// expected-error@-1 {{no matching function for call to 'saturate'}}
}

float2 test_saturate_vector_size_mismatch(float3 p0) {
return saturate(p0);
// expected-error@-1 {{implicit conversion truncates vector: 'float3' (aka 'vector<float, 3>') to 'vector<float, 2>'}}
}

float2 test_saturate_float2_int_splat(int p0) {
return saturate(p0);
// expected-error@-1 {{call to 'saturate' is ambiguous}}
}

float2 test_saturate_int_vect_to_float_vec_promotion(int2 p0) {
return saturate(p0);
// expected-error@-1 {{call to 'saturate' is ambiguous}}
}

float test_saturate_bool_type_promotion(bool p0) {
return saturate(p0);
// expected-error@-1 {{call to 'saturate' is ambiguous}}
}
1 change: 1 addition & 0 deletions llvm/include/llvm/IR/IntrinsicsDirectX.td
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def int_dx_all : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty]>;
def int_dx_any : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty]>;
def int_dx_clamp : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>]>;
def int_dx_uclamp : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>]>;
def int_dx_saturate : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>;

def int_dx_dot2 :
Intrinsic<[LLVMVectorElementType<0>],
Expand Down
3 changes: 2 additions & 1 deletion llvm/include/llvm/IR/IntrinsicsSPIRV.td
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@ let TargetPrefix = "spv" in {
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>],
def int_spv_lerp : Intrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>,LLVMMatchType<0>],
[IntrNoMem, IntrWillReturn] >;
def int_spv_length : DefaultAttrsIntrinsic<[LLVMVectorElementType<0>], [llvm_anyfloat_ty]>;
def int_spv_normalize : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]>;
def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]>;
def int_spv_saturate : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>;
}
10 changes: 10 additions & 0 deletions llvm/lib/Target/DirectX/DXIL.td
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,16 @@ def Abs : DXILOp<6, unary> {
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}

def Saturate : DXILOp<7, unary> {
let Doc = "Clamps a single or double precision floating point value to [0.0f...1.0f].";
let LLVMIntrinsic = int_dx_saturate;
let arguments = [overloadTy];
let result = overloadTy;
let overloads = [Overloads<DXIL1_0, [halfTy, floatTy, doubleTy]>];
let stages = [Stages<DXIL1_0, [all_stages]>];
let attributes = [Attributes<DXIL1_0, [ReadNone]>];
}

def IsInf : DXILOp<9, isSpecialFloat> {
let Doc = "Determines if the specified value is infinite.";
let LLVMIntrinsic = int_dx_isinf;
Expand Down
Loading
Loading