Skip to content

Commit

Permalink
[clang][HLSL] Add sign intrinsic part 3 (llvm#101989)
Browse files Browse the repository at this point in the history
partially fixes llvm#70078

### Changes
- Implemented `sign` clang builtin
- Linked `sign` clang builtin with `hlsl_intrinsics.h`
- Added sema checks for `sign` to `CheckHLSLBuiltinFunctionCall` in
`SemaChecking.cpp`
- Add codegen for `sign` to `EmitHLSLBuiltinExpr` in `CGBuiltin.cpp`
- Add codegen tests to `clang/test/CodeGenHLSL/builtins/sign.hlsl`
- Add sema tests to `clang/test/SemaHLSL/BuiltIns/sign-errors.hlsl`

### Related PRs
- llvm#101987
- llvm#101988

### Discussion
- Should there be a `usign` intrinsic that handles the unsigned cases?
  • Loading branch information
tgymnich authored and VitaNuo committed Sep 12, 2024
1 parent 5c9706c commit 647265e
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 0 deletions.
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -4769,6 +4769,12 @@ def HLSLSelect : LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}

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

// Builtins for XRay.
def XRayCustomEvent : Builtin {
let Spellings = ["__xray_customevent"];
Expand Down
17 changes: 17 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18870,6 +18870,23 @@ case Builtin::BI__builtin_hlsl_elementwise_isinf: {
Intrinsic::ID ID = CGM.getHLSLRuntime().getWaveIsFirstLaneIntrinsic();
return EmitRuntimeCall(Intrinsic::getDeclaration(&CGM.getModule(), ID));
}
case Builtin::BI__builtin_hlsl_elementwise_sign: {
Value *Op0 = EmitScalarExpr(E->getArg(0));
llvm::Type *Xty = Op0->getType();
llvm::Type *retType = llvm::Type::getInt32Ty(this->getLLVMContext());
if (Xty->isVectorTy()) {
auto *XVecTy = E->getArg(0)->getType()->getAs<VectorType>();
retType = llvm::VectorType::get(
retType, ElementCount::getFixed(XVecTy->getNumElements()));
}
assert((E->getArg(0)->getType()->hasFloatingRepresentation() ||
E->getArg(0)->getType()->hasSignedIntegerRepresentation()) &&
"sign operand must have a float or int representation");

return Builder.CreateIntrinsic(
retType, CGM.getHLSLRuntime().getSignIntrinsic(),
ArrayRef<Value *>{Op0}, nullptr, "hlsl.sign");
}
}
return nullptr;
}
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 @@ -80,6 +80,7 @@ class CGHLSLRuntime {
GENERATE_HLSL_INTRINSIC_FUNCTION(Normalize, normalize)
GENERATE_HLSL_INTRINSIC_FUNCTION(Rsqrt, rsqrt)
GENERATE_HLSL_INTRINSIC_FUNCTION(Saturate, saturate)
GENERATE_HLSL_INTRINSIC_FUNCTION(Sign, sign)
GENERATE_HLSL_INTRINSIC_FUNCTION(ThreadId, thread_id)
GENERATE_HLSL_INTRINSIC_FUNCTION(FDot, fdot)
GENERATE_HLSL_INTRINSIC_FUNCTION(SDot, sdot)
Expand Down
71 changes: 71 additions & 0 deletions clang/lib/Headers/hlsl/hlsl_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -1826,5 +1826,76 @@ _HLSL_AVAILABILITY(shadermodel, 6.0)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_wave_is_first_lane)
__attribute__((convergent)) bool WaveIsFirstLane();

//===----------------------------------------------------------------------===//
// sign builtins
//===----------------------------------------------------------------------===//

/// \fn T sign(T Val)
/// \brief Returns -1 if \a Val is less than zero; 0 if \a Val equals zero; and
/// 1 if \a Val is greater than zero. \param Val The input value.

#ifdef __HLSL_ENABLE_16_BIT
_HLSL_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int sign(int16_t);
_HLSL_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int2 sign(int16_t2);
_HLSL_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int3 sign(int16_t3);
_HLSL_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int4 sign(int16_t4);
#endif

_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int sign(half);
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int2 sign(half2);
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int3 sign(half3);
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int4 sign(half4);

_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int sign(int);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int2 sign(int2);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int3 sign(int3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int4 sign(int4);

_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int sign(float);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int2 sign(float2);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int3 sign(float3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int4 sign(float4);

_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int sign(int64_t);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int2 sign(int64_t2);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int3 sign(int64_t3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int4 sign(int64_t4);

_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int sign(double);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int2 sign(double2);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int3 sign(double3);
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_elementwise_sign)
int4 sign(double4);
} // namespace hlsl
#endif //_HLSL_HLSL_INTRINSICS_H_
17 changes: 17 additions & 0 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "clang/AST/Expr.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Type.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceLocation.h"
Expand Down Expand Up @@ -1513,6 +1514,14 @@ bool CheckNoDoubleVectors(Sema *S, CallExpr *TheCall) {
return CheckArgsTypesAreCorrect(S, TheCall, S->Context.FloatTy,
checkDoubleVector);
}
bool CheckFloatingOrSignedIntRepresentation(Sema *S, CallExpr *TheCall) {
auto checkAllSignedTypes = [](clang::QualType PassedType) -> bool {
return !PassedType->hasSignedIntegerRepresentation() &&
!PassedType->hasFloatingRepresentation();
};
return CheckArgsTypesAreCorrect(S, TheCall, S->Context.IntTy,
checkAllSignedTypes);
}

bool CheckUnsignedIntRepresentation(Sema *S, CallExpr *TheCall) {
auto checkAllUnsignedTypes = [](clang::QualType PassedType) -> bool {
Expand Down Expand Up @@ -1726,6 +1735,14 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
TheCall->setType(ArgTyA);
break;
}
case Builtin::BI__builtin_hlsl_elementwise_sign: {
if (CheckFloatingOrSignedIntRepresentation(&SemaRef, TheCall))
return true;
if (SemaRef.PrepareBuiltinElementwiseMathOneArgCall(TheCall))
return true;
SetElementTypeAsReturnType(&SemaRef, TheCall, getASTContext().IntTy);
break;
}
// Note these are llvm builtins that we want to catch invalid intrinsic
// generation. Normal handling of these builitns will occur elsewhere.
case Builtin::BI__builtin_elementwise_bitreverse: {
Expand Down
157 changes: 157 additions & 0 deletions clang/test/CodeGenHLSL/builtins/sign.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// 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: -DTARGET=dx -DFNATTRS=noundef
// 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: -DTARGET=dx -DFNATTRS=noundef
// 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,NATIVE_HALF \
// RUN: -DTARGET=spv -DFNATTRS="spir_func noundef"
// 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,NO_HALF \
// RUN: -DTARGET=spv -DFNATTRS="spir_func noundef"

// NATIVE_HALF: define [[FNATTRS]] i32 @
// NATIVE_HALF: %hlsl.sign = call i32 @llvm.[[TARGET]].sign.f16(
// NATIVE_HALF: ret i32 %hlsl.sign
// NO_HALF: define [[FNATTRS]] i32 @
// NO_HALF: %hlsl.sign = call i32 @llvm.[[TARGET]].sign.f32(
// NO_HALF: ret i32 %hlsl.sign
int test_sign_half(half p0) { return sign(p0); }

// NATIVE_HALF: define [[FNATTRS]] <2 x i32> @
// NATIVE_HALF: %hlsl.sign = call <2 x i32> @llvm.[[TARGET]].sign.v2f16(
// NATIVE_HALF: ret <2 x i32> %hlsl.sign
// NO_HALF: define [[FNATTRS]] <2 x i32> @
// NO_HALF: %hlsl.sign = call <2 x i32> @llvm.[[TARGET]].sign.v2f32(
// NO_HALF: ret <2 x i32> %hlsl.sign
int2 test_sign_half2(half2 p0) { return sign(p0); }

// NATIVE_HALF: define [[FNATTRS]] <3 x i32> @
// NATIVE_HALF: %hlsl.sign = call <3 x i32> @llvm.[[TARGET]].sign.v3f16(
// NATIVE_HALF: ret <3 x i32> %hlsl.sign
// NO_HALF: define [[FNATTRS]] <3 x i32> @
// NO_HALF: %hlsl.sign = call <3 x i32> @llvm.[[TARGET]].sign.v3f32(
// NO_HALF: ret <3 x i32> %hlsl.sign
int3 test_sign_half3(half3 p0) { return sign(p0); }

// NATIVE_HALF: define [[FNATTRS]] <4 x i32> @
// NATIVE_HALF: %hlsl.sign = call <4 x i32> @llvm.[[TARGET]].sign.v4f16(
// NATIVE_HALF: ret <4 x i32> %hlsl.sign
// NO_HALF: define [[FNATTRS]] <4 x i32> @
// NO_HALF: %hlsl.sign = call <4 x i32> @llvm.[[TARGET]].sign.v4f32(
// NO_HALF: ret <4 x i32> %hlsl.sign
int4 test_sign_half4(half4 p0) { return sign(p0); }


// CHECK: define [[FNATTRS]] i32 @
// CHECK: %hlsl.sign = call i32 @llvm.[[TARGET]].sign.f32(
// CHECK: ret i32 %hlsl.sign
int test_sign_float(float p0) { return sign(p0); }

// CHECK: define [[FNATTRS]] <2 x i32> @
// CHECK: %hlsl.sign = call <2 x i32> @llvm.[[TARGET]].sign.v2f32(
// CHECK: ret <2 x i32> %hlsl.sign
int2 test_sign_float2(float2 p0) { return sign(p0); }

// CHECK: define [[FNATTRS]] <3 x i32> @
// CHECK: %hlsl.sign = call <3 x i32> @llvm.[[TARGET]].sign.v3f32(
// CHECK: ret <3 x i32> %hlsl.sign
int3 test_sign_float3(float3 p0) { return sign(p0); }

// CHECK: define [[FNATTRS]] <4 x i32> @
// CHECK: %hlsl.sign = call <4 x i32> @llvm.[[TARGET]].sign.v4f32(
// CHECK: ret <4 x i32> %hlsl.sign
int4 test_sign_float4(float4 p0) { return sign(p0); }


// CHECK: define [[FNATTRS]] i32 @
// CHECK: %hlsl.sign = call i32 @llvm.[[TARGET]].sign.f64(
// CHECK: ret i32 %hlsl.sign
int test_sign_double(double p0) { return sign(p0); }

// CHECK: define [[FNATTRS]] <2 x i32> @
// CHECK: %hlsl.sign = call <2 x i32> @llvm.[[TARGET]].sign.v2f64(
// CHECK: ret <2 x i32> %hlsl.sign
int2 test_sign_double2(double2 p0) { return sign(p0); }

// CHECK: define [[FNATTRS]] <3 x i32> @
// CHECK: %hlsl.sign = call <3 x i32> @llvm.[[TARGET]].sign.v3f64(
// CHECK: ret <3 x i32> %hlsl.sign
int3 test_sign_double3(double3 p0) { return sign(p0); }

// CHECK: define [[FNATTRS]] <4 x i32> @
// CHECK: %hlsl.sign = call <4 x i32> @llvm.[[TARGET]].sign.v4f64(
// CHECK: ret <4 x i32> %hlsl.sign
int4 test_sign_double4(double4 p0) { return sign(p0); }


#ifdef __HLSL_ENABLE_16_BIT
// NATIVE_HALF: define [[FNATTRS]] i32 @
// NATIVE_HALF: %hlsl.sign = call i32 @llvm.[[TARGET]].sign.i16(
// NATIVE_HALF: ret i32 %hlsl.sign
int test_sign_int16_t(int16_t p0) { return sign(p0); }

// NATIVE_HALF: define [[FNATTRS]] <2 x i32> @
// NATIVE_HALF: %hlsl.sign = call <2 x i32> @llvm.[[TARGET]].sign.v2i16(
// NATIVE_HALF: ret <2 x i32> %hlsl.sign
int2 test_sign_int16_t2(int16_t2 p0) { return sign(p0); }

// NATIVE_HALF: define [[FNATTRS]] <3 x i32> @
// NATIVE_HALF: %hlsl.sign = call <3 x i32> @llvm.[[TARGET]].sign.v3i16(
// NATIVE_HALF: ret <3 x i32> %hlsl.sign
int3 test_sign_int16_t3(int16_t3 p0) { return sign(p0); }

// NATIVE_HALF: define [[FNATTRS]] <4 x i32> @
// NATIVE_HALF: %hlsl.sign = call <4 x i32> @llvm.[[TARGET]].sign.v4i16(
// NATIVE_HALF: ret <4 x i32> %hlsl.sign
int4 test_sign_int16_t4(int16_t4 p0) { return sign(p0); }
#endif // __HLSL_ENABLE_16_BIT


// CHECK: define [[FNATTRS]] i32 @
// CHECK: %hlsl.sign = call i32 @llvm.[[TARGET]].sign.i32(
// CHECK: ret i32 %hlsl.sign
int test_sign_int(int p0) { return sign(p0); }

// CHECK: define [[FNATTRS]] <2 x i32> @
// CHECK: %hlsl.sign = call <2 x i32> @llvm.[[TARGET]].sign.v2i32(
// CHECK: ret <2 x i32> %hlsl.sign
int2 test_sign_int2(int2 p0) { return sign(p0); }

// CHECK: define [[FNATTRS]] <3 x i32> @
// CHECK: %hlsl.sign = call <3 x i32> @llvm.[[TARGET]].sign.v3i32(
// CHECK: ret <3 x i32> %hlsl.sign
int3 test_sign_int3(int3 p0) { return sign(p0); }

// CHECK: define [[FNATTRS]] <4 x i32> @
// CHECK: %hlsl.sign = call <4 x i32> @llvm.[[TARGET]].sign.v4i32(
// CHECK: ret <4 x i32> %hlsl.sign
int4 test_sign_int4(int4 p0) { return sign(p0); }


// CHECK: define [[FNATTRS]] i32 @
// CHECK: %hlsl.sign = call i32 @llvm.[[TARGET]].sign.i64(
// CHECK: ret i32 %hlsl.sign
int test_sign_int64_t(int64_t p0) { return sign(p0); }

// CHECK: define [[FNATTRS]] <2 x i32> @
// CHECK: %hlsl.sign = call <2 x i32> @llvm.[[TARGET]].sign.v2i64(
// CHECK: ret <2 x i32> %hlsl.sign
int2 test_sign_int64_t2(int64_t2 p0) { return sign(p0); }

// CHECK: define [[FNATTRS]] <3 x i32> @
// CHECK: %hlsl.sign = call <3 x i32> @llvm.[[TARGET]].sign.v3i64(
// CHECK: ret <3 x i32> %hlsl.sign
int3 test_sign_int64_t3(int64_t3 p0) { return sign(p0); }

// CHECK: define [[FNATTRS]] <4 x i32> @
// CHECK: %hlsl.sign = call <4 x i32> @llvm.[[TARGET]].sign.v4i64(
// CHECK: ret <4 x i32> %hlsl.sign
int4 test_sign_int64_t4(int64_t4 p0) { return sign(p0); }
16 changes: 16 additions & 0 deletions clang/test/SemaHLSL/BuiltIns/sign-errors.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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

bool test_too_few_arg() {
return __builtin_hlsl_elementwise_sign();
// expected-error@-1 {{too few arguments to function call, expected 1, have 0}}
}

bool2 test_too_many_arg(float2 p0) {
return __builtin_hlsl_elementwise_sign(p0, p0);
// expected-error@-1 {{too many arguments to function call, expected 1, have 2}}
}

bool builtin_bool_to_float_type_promotion(bool p1) {
return __builtin_hlsl_elementwise_sign(p1);
// expected-error@-1 {passing 'bool' to parameter of incompatible type 'float'}}
}

0 comments on commit 647265e

Please sign in to comment.