Skip to content

[HLSL] Implement the faceforward intrinsic #135878

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 6 commits into from
Apr 24, 2025
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/BuiltinsSPIRV.td
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,9 @@ def SPIRVSmoothStep : Builtin {
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "void(...)";
}

def SPIRVFaceForward : Builtin {
let Spellings = ["__builtin_spirv_faceforward"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Prototype = "void(...)";
}
12 changes: 12 additions & 0 deletions clang/lib/CodeGen/TargetBuiltins/SPIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@ Value *CodeGenFunction::EmitSPIRVBuiltinExpr(unsigned BuiltinID,
ArrayRef<Value *>{Min, Max, X}, /*FMFSource=*/nullptr,
"spv.smoothstep");
}
case SPIRV::BI__builtin_spirv_faceforward: {
Value *N = EmitScalarExpr(E->getArg(0));
Value *I = EmitScalarExpr(E->getArg(1));
Value *Ng = EmitScalarExpr(E->getArg(2));
assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
E->getArg(1)->getType()->hasFloatingRepresentation() &&
E->getArg(2)->getType()->hasFloatingRepresentation() &&
"FaceForward operands must have a float representation");
return Builder.CreateIntrinsic(
/*ReturnType=*/N->getType(), Intrinsic::spv_faceforward,
ArrayRef<Value *>{N, I, Ng}, /*FMFSource=*/nullptr, "spv.faceforward");
}
}
return nullptr;
}
8 changes: 8 additions & 0 deletions clang/lib/Headers/hlsl/hlsl_intrinsic_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@ template <typename T> constexpr vector<T, 4> lit_impl(T NDotL, T NDotH, T M) {
return Result;
}

template <typename T> constexpr T faceforward_impl(T N, T I, T Ng) {
#if (__has_builtin(__builtin_spirv_faceforward))
return __builtin_spirv_faceforward(N, I, Ng);
#else
return select(dot(I, Ng) < 0, N, -N);
#endif
}

} // namespace __detail
} // namespace hlsl

Expand Down
47 changes: 47 additions & 0 deletions clang/lib/Headers/hlsl/hlsl_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,53 @@ const inline double4 dst(double4 Src0, double4 Src1) {
return __detail::dst_impl(Src0, Src1);
}

//===----------------------------------------------------------------------===//
// faceforward builtin
//===----------------------------------------------------------------------===//

/// \fn T faceforward(T N, T I, T Ng)
/// \brief Flips the surface-normal (if needed) to face in a direction opposite
/// to \a I. Returns the result in terms of \a N.
/// \param N The resulting floating-point surface-normal vector.
/// \param I A floating-point, incident vector that points from the view
/// position to the shading position.
/// \param Ng A floating-point surface-normal vector.
///
/// Return a floating-point, surface normal vector that is facing the view
/// direction.

template <typename T>
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
const inline __detail::enable_if_t<__detail::is_arithmetic<T>::Value &&
__detail::is_same<half, T>::value,
T> faceforward(T N, T I, T Ng) {
return __detail::faceforward_impl(N, I, Ng);
}

template <typename T>
const inline __detail::enable_if_t<
__detail::is_arithmetic<T>::Value && __detail::is_same<float, T>::value, T>
faceforward(T N, T I, T Ng) {
return __detail::faceforward_impl(N, I, Ng);
}

template <int L>
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
const inline __detail::HLSL_FIXED_VECTOR<half, L> faceforward(
__detail::HLSL_FIXED_VECTOR<half, L> N,
__detail::HLSL_FIXED_VECTOR<half, L> I,
__detail::HLSL_FIXED_VECTOR<half, L> Ng) {
return __detail::faceforward_impl(N, I, Ng);
}

template <int L>
const inline __detail::HLSL_FIXED_VECTOR<float, L>
faceforward(__detail::HLSL_FIXED_VECTOR<float, L> N,
__detail::HLSL_FIXED_VECTOR<float, L> I,
__detail::HLSL_FIXED_VECTOR<float, L> Ng) {
return __detail::faceforward_impl(N, I, Ng);
}

//===----------------------------------------------------------------------===//
// fmod builtins
//===----------------------------------------------------------------------===//
Expand Down
69 changes: 46 additions & 23 deletions clang/lib/Sema/SemaSPIRV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,23 @@ namespace clang {

SemaSPIRV::SemaSPIRV(Sema &S) : SemaBase(S) {}

static bool CheckAllArgsHaveSameType(Sema *S, CallExpr *TheCall) {
assert(TheCall->getNumArgs() > 1);
QualType ArgTy0 = TheCall->getArg(0)->getType();

for (unsigned I = 1, N = TheCall->getNumArgs(); I < N; ++I) {
if (!S->getASTContext().hasSameUnqualifiedType(
ArgTy0, TheCall->getArg(I)->getType())) {
S->Diag(TheCall->getBeginLoc(), diag::err_vec_builtin_incompatible_vector)
<< TheCall->getDirectCallee() << /*useAllTerminology*/ true
<< SourceRange(TheCall->getArg(0)->getBeginLoc(),
TheCall->getArg(N - 1)->getEndLoc());
return true;
}
}
return false;
}

bool SemaSPIRV::CheckSPIRVBuiltinFunctionCall(unsigned BuiltinID,
CallExpr *TheCall) {
switch (BuiltinID) {
Expand Down Expand Up @@ -105,35 +122,41 @@ bool SemaSPIRV::CheckSPIRVBuiltinFunctionCall(unsigned BuiltinID,
if (SemaRef.checkArgCount(TheCall, 3))
return true;

// check if the all arguments have floating representation
for (unsigned i = 0; i < TheCall->getNumArgs(); ++i) {
ExprResult Arg = TheCall->getArg(i);
QualType ArgTy = Arg.get()->getType();
if (!ArgTy->hasFloatingRepresentation()) {
SemaRef.Diag(Arg.get()->getBeginLoc(),
diag::err_builtin_invalid_arg_type)
<< i + 1 << /* scalar or vector */ 5 << /* no int */ 0 << /* fp */ 1
<< ArgTy;
return true;
}
// Check if first argument has floating representation
ExprResult A = TheCall->getArg(0);
QualType ArgTyA = A.get()->getType();
if (!ArgTyA->hasFloatingRepresentation()) {
SemaRef.Diag(A.get()->getBeginLoc(), diag::err_builtin_invalid_arg_type)
<< /* ordinal */ 1 << /* scalar or vector */ 5 << /* no int */ 0
<< /* fp */ 1 << ArgTyA;
return true;
}

// check if all arguments are of the same type
if (CheckAllArgsHaveSameType(&SemaRef, TheCall))
return true;

QualType RetTy = ArgTyA;
TheCall->setType(RetTy);
break;
}
case SPIRV::BI__builtin_spirv_faceforward: {
if (SemaRef.checkArgCount(TheCall, 3))
return true;

// Check if first argument has floating representation
ExprResult A = TheCall->getArg(0);
ExprResult B = TheCall->getArg(1);
ExprResult C = TheCall->getArg(2);
if (!(SemaRef.getASTContext().hasSameUnqualifiedType(A.get()->getType(),
B.get()->getType()) &&
SemaRef.getASTContext().hasSameUnqualifiedType(A.get()->getType(),
C.get()->getType()))) {
SemaRef.Diag(TheCall->getBeginLoc(),
diag::err_vec_builtin_incompatible_vector)
<< TheCall->getDirectCallee() << /*useAllTerminology*/ true
<< SourceRange(A.get()->getBeginLoc(), C.get()->getEndLoc());
QualType ArgTyA = A.get()->getType();
if (!ArgTyA->hasFloatingRepresentation()) {
SemaRef.Diag(A.get()->getBeginLoc(), diag::err_builtin_invalid_arg_type)
<< /* ordinal */ 1 << /* scalar or vector */ 5 << /* no int */ 0
<< /* fp */ 1 << ArgTyA;
return true;
}

QualType RetTy = A.get()->getType();
if (CheckAllArgsHaveSameType(&SemaRef, TheCall))
return true;

QualType RetTy = ArgTyA;
TheCall->setType(RetTy);
break;
}
Expand Down
94 changes: 94 additions & 0 deletions clang/test/CodeGenHLSL/builtins/faceforward.hlsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// RUN: %clang_cc1 -finclude-default-header -triple \
// RUN: dxil-pc-shadermodel6.3-library %s -fnative-half-type \
// RUN: -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 -finclude-default-header -triple \
// RUN: spirv-unknown-vulkan-compute %s -fnative-half-type \
// RUN: -emit-llvm -o - | FileCheck %s --check-prefix=SPVCHECK

// CHECK-LABEL: test_faceforward_half
// CHECK: %hlsl.dot.i = fmul reassoc nnan ninf nsz arcp afn half %{{.*}}, %{{.*}}
// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt half %hlsl.dot.i, 0xH0000
// CHECK: %fneg.i = fneg reassoc nnan ninf nsz arcp afn half %{{.*}}
// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %cmp.i, half %{{.*}}, half %fneg.i
// CHECK: ret half %hlsl.select.i
// SPVCHECK-LABEL: test_faceforward_half
// SPVCHECK: %spv.faceforward.i = call reassoc nnan ninf nsz arcp afn noundef half @llvm.spv.faceforward.f16(half %{{.*}}, half %{{.*}}, half %{{.*}})
// SPVCHECK: ret half %spv.faceforward.i
half test_faceforward_half(half N, half I, half Ng) { return faceforward(N, I, Ng); }

// CHECK-LABEL: test_faceforward_half2
// CHECK: %hlsl.dot.i = call reassoc nnan ninf nsz arcp afn half @llvm.dx.fdot.v2f16(<2 x half> %{{.*}}, <2 x half> %{{.*}})
// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt half %hlsl.dot.i, 0xH0000
// CHECK: %fneg.i = fneg reassoc nnan ninf nsz arcp afn <2 x half> %{{.*}}
// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %cmp.i, <2 x half> %{{.*}}, <2 x half> %fneg.i
// CHECK: ret <2 x half> %hlsl.select.i
// SPVCHECK-LABEL: test_faceforward_half2
// SPVCHECK: %spv.faceforward.i = call reassoc nnan ninf nsz arcp afn noundef <2 x half> @llvm.spv.faceforward.v2f16(<2 x half> %{{.*}}, <2 x half> %{{.*}}, <2 x half> %{{.*}})
// SPVCHECK: ret <2 x half> %spv.faceforward.i
half2 test_faceforward_half2(half2 N, half2 I, half2 Ng) { return faceforward(N, I, Ng); }

// CHECK-LABEL: test_faceforward_half3
// CHECK: %hlsl.dot.i = call reassoc nnan ninf nsz arcp afn half @llvm.dx.fdot.v3f16(<3 x half> %{{.*}}, <3 x half> %{{.*}})
// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt half %hlsl.dot.i, 0xH0000
// CHECK: %fneg.i = fneg reassoc nnan ninf nsz arcp afn <3 x half> %{{.*}}
// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %cmp.i, <3 x half> %{{.*}}, <3 x half> %fneg.i
// CHECK: ret <3 x half> %hlsl.select.i
// SPVCHECK-LABEL: test_faceforward_half3
// SPVCHECK: %spv.faceforward.i = call reassoc nnan ninf nsz arcp afn noundef <3 x half> @llvm.spv.faceforward.v3f16(<3 x half> %{{.*}}, <3 x half> %{{.*}}, <3 x half> %{{.*}})
// SPVCHECK: ret <3 x half> %spv.faceforward.i
half3 test_faceforward_half3(half3 N, half3 I, half3 Ng) { return faceforward(N, I, Ng); }

// CHECK-LABEL: test_faceforward_half4
// CHECK: %hlsl.dot.i = call reassoc nnan ninf nsz arcp afn half @llvm.dx.fdot.v4f16(<4 x half> %{{.*}}, <4 x half> %{{.*}})
// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt half %hlsl.dot.i, 0xH0000
// CHECK: %fneg.i = fneg reassoc nnan ninf nsz arcp afn <4 x half> %{{.*}}
// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %cmp.i, <4 x half> %{{.*}}, <4 x half> %fneg.i
// CHECK: ret <4 x half> %hlsl.select.i
// SPVCHECK-LABEL: test_faceforward_half4
// SPVCHECK: %spv.faceforward.i = call reassoc nnan ninf nsz arcp afn noundef <4 x half> @llvm.spv.faceforward.v4f16(<4 x half> %{{.*}}, <4 x half> %{{.*}}, <4 x half> %{{.*}})
// SPVCHECK: ret <4 x half> %spv.faceforward.i
half4 test_faceforward_half4(half4 N, half4 I, half4 Ng) { return faceforward(N, I, Ng); }

// CHECK-LABEL: test_faceforward_float
// CHECK: %hlsl.dot.i = fmul reassoc nnan ninf nsz arcp afn float %{{.*}}, %{{.*}}
// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt float %hlsl.dot.i, 0.000000e+00
// CHECK: %fneg.i = fneg reassoc nnan ninf nsz arcp afn float %{{.*}}
// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %cmp.i, float %{{.*}}, float %fneg.i
// CHECK: ret float %hlsl.select.i
// SPVCHECK-LABEL: test_faceforward_float
// SPVCHECK: %spv.faceforward.i = call reassoc nnan ninf nsz arcp afn noundef float @llvm.spv.faceforward.f32(float %{{.*}}, float %{{.*}}, float %{{.*}})
// SPVCHECK: ret float %spv.faceforward.i
float test_faceforward_float(float N, float I, float Ng) { return faceforward(N, I, Ng); }

// CHECK-LABEL: test_faceforward_float2
// CHECK: %hlsl.dot.i = call reassoc nnan ninf nsz arcp afn float @llvm.dx.fdot.v2f32(<2 x float> %{{.*}}, <2 x float> %{{.*}})
// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt float %hlsl.dot.i, 0.000000e+00
// CHECK: %fneg.i = fneg reassoc nnan ninf nsz arcp afn <2 x float> %{{.*}}
// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %cmp.i, <2 x float> %{{.*}}, <2 x float> %fneg.i
// CHECK: ret <2 x float> %hlsl.select.i
// SPVCHECK-LABEL: test_faceforward_float2
// SPVCHECK: %spv.faceforward.i = call reassoc nnan ninf nsz arcp afn noundef <2 x float> @llvm.spv.faceforward.v2f32(<2 x float> %{{.*}}, <2 x float> %{{.*}}, <2 x float> %{{.*}})
// SPVCHECK: ret <2 x float> %spv.faceforward.i
float2 test_faceforward_float2(float2 N, float2 I, float2 Ng) { return faceforward(N, I, Ng); }

// CHECK-LABEL: test_faceforward_float3
// CHECK: %hlsl.dot.i = call reassoc nnan ninf nsz arcp afn float @llvm.dx.fdot.v3f32(<3 x float> %{{.*}}, <3 x float> %{{.*}})
// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt float %hlsl.dot.i, 0.000000e+00
// CHECK: %fneg.i = fneg reassoc nnan ninf nsz arcp afn <3 x float> %{{.*}}
// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %cmp.i, <3 x float> %{{.*}}, <3 x float> %fneg.i
// CHECK: ret <3 x float> %hlsl.select.i
// SPVCHECK-LABEL: test_faceforward_float3
// SPVCHECK: %spv.faceforward.i = call reassoc nnan ninf nsz arcp afn noundef <3 x float> @llvm.spv.faceforward.v3f32(<3 x float> %{{.*}}, <3 x float> %{{.*}}, <3 x float> %{{.*}})
// SPVCHECK: ret <3 x float> %spv.faceforward.i
float3 test_faceforward_float3(float3 N, float3 I, float3 Ng) { return faceforward(N, I, Ng); }

// CHECK-LABEL: test_faceforward_float4
// CHECK: %hlsl.dot.i = call reassoc nnan ninf nsz arcp afn float @llvm.dx.fdot.v4f32(<4 x float> %{{.*}}, <4 x float> %{{.*}})
// CHECK: %cmp.i = fcmp reassoc nnan ninf nsz arcp afn olt float %hlsl.dot.i, 0.000000e+00
// CHECK: %fneg.i = fneg reassoc nnan ninf nsz arcp afn <4 x float> %{{.*}}
// CHECK: %hlsl.select.i = select reassoc nnan ninf nsz arcp afn i1 %cmp.i, <4 x float> %{{.*}}, <4 x float> %fneg.i
// CHECK: ret <4 x float> %hlsl.select.i
// SPVCHECK-LABEL: test_faceforward_float4
// SPVCHECK: %spv.faceforward.i = call reassoc nnan ninf nsz arcp afn noundef <4 x float> @llvm.spv.faceforward.v4f32(<4 x float> %{{.*}}, <4 x float> %{{.*}}, <4 x float> %{{.*}})
// SPVCHECK: ret <4 x float> %spv.faceforward.i
float4 test_faceforward_float4(float4 N, float4 I, float4 Ng) { return faceforward(N, I, Ng); }
67 changes: 67 additions & 0 deletions clang/test/CodeGenSPIRV/Builtins/faceforward.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5

// RUN: %clang_cc1 -O1 -triple spirv-pc-vulkan-compute %s -emit-llvm -o - | FileCheck %s

typedef _Float16 half;
typedef half half2 __attribute__((ext_vector_type(2)));
typedef half half3 __attribute__((ext_vector_type(3)));
typedef half half4 __attribute__((ext_vector_type(4)));
typedef float float2 __attribute__((ext_vector_type(2)));
typedef float float3 __attribute__((ext_vector_type(3)));
typedef float float4 __attribute__((ext_vector_type(4)));

// CHECK-LABEL: define spir_func half @test_faceforward_half(
// CHECK-SAME: half noundef [[N:%.*]], half noundef [[I:%.*]], half noundef [[NG:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[SPV_FACEFORWARD:%.*]] = tail call half @llvm.spv.faceforward.f16(half [[N]], half [[I]], half [[NG]])
// CHECK-NEXT: ret half [[SPV_FACEFORWARD]]
half test_faceforward_half(half N, half I, half Ng) { return __builtin_spirv_faceforward(N, I, Ng); }

// CHECK-LABEL: define spir_func <2 x half> @test_faceforward_half2(
// CHECK-SAME: <2 x half> noundef [[N:%.*]], <2 x half> noundef [[I:%.*]], <2 x half> noundef [[NG:%.*]]) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[SPV_FACEFORWARD:%.*]] = tail call <2 x half> @llvm.spv.faceforward.v2f16(<2 x half> [[N]], <2 x half> [[I]], <2 x half> [[NG]])
// CHECK-NEXT: ret <2 x half> [[SPV_FACEFORWARD]]
half2 test_faceforward_half2(half2 N, half2 I, half2 Ng) { return __builtin_spirv_faceforward(N, I, Ng); }

// CHECK-LABEL: define spir_func <3 x half> @test_faceforward_half3(
// CHECK-SAME: <3 x half> noundef [[N:%.*]], <3 x half> noundef [[I:%.*]], <3 x half> noundef [[NG:%.*]]) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[SPV_FACEFORWARD:%.*]] = tail call <3 x half> @llvm.spv.faceforward.v3f16(<3 x half> [[N]], <3 x half> [[I]], <3 x half> [[NG]])
// CHECK-NEXT: ret <3 x half> [[SPV_FACEFORWARD]]
half3 test_faceforward_half3(half3 N, half3 I, half3 Ng) { return __builtin_spirv_faceforward(N, I, Ng); }

// CHECK-LABEL: define spir_func <4 x half> @test_faceforward_half4(
// CHECK-SAME: <4 x half> noundef [[N:%.*]], <4 x half> noundef [[I:%.*]], <4 x half> noundef [[NG:%.*]]) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[SPV_FACEFORWARD:%.*]] = tail call <4 x half> @llvm.spv.faceforward.v4f16(<4 x half> [[N]], <4 x half> [[I]], <4 x half> [[NG]])
// CHECK-NEXT: ret <4 x half> [[SPV_FACEFORWARD]]
half4 test_faceforward_half4(half4 N, half4 I, half4 Ng) { return __builtin_spirv_faceforward(N, I, Ng); }

// CHECK-LABEL: define spir_func float @test_faceforward_float(
// CHECK-SAME: float noundef [[N:%.*]], float noundef [[I:%.*]], float noundef [[NG:%.*]]) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[SPV_FACEFORWARD:%.*]] = tail call float @llvm.spv.faceforward.f32(float [[N]], float [[I]], float [[NG]])
// CHECK-NEXT: ret float [[SPV_FACEFORWARD]]
float test_faceforward_float(float N, float I, float Ng) { return __builtin_spirv_faceforward(N, I, Ng); }

// CHECK-LABEL: define spir_func <2 x float> @test_faceforward_float2(
// CHECK-SAME: <2 x float> noundef [[N:%.*]], <2 x float> noundef [[I:%.*]], <2 x float> noundef [[NG:%.*]]) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[SPV_FACEFORWARD:%.*]] = tail call <2 x float> @llvm.spv.faceforward.v2f32(<2 x float> [[N]], <2 x float> [[I]], <2 x float> [[NG]])
// CHECK-NEXT: ret <2 x float> [[SPV_FACEFORWARD]]
float2 test_faceforward_float2(float2 N, float2 I, float2 Ng) { return __builtin_spirv_faceforward(N, I, Ng); }

// CHECK-LABEL: define spir_func <3 x float> @test_faceforward_float3(
// CHECK-SAME: <3 x float> noundef [[N:%.*]], <3 x float> noundef [[I:%.*]], <3 x float> noundef [[NG:%.*]]) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[SPV_FACEFORWARD:%.*]] = tail call <3 x float> @llvm.spv.faceforward.v3f32(<3 x float> [[N]], <3 x float> [[I]], <3 x float> [[NG]])
// CHECK-NEXT: ret <3 x float> [[SPV_FACEFORWARD]]
float3 test_faceforward_float3(float3 N, float3 I, float3 Ng) { return __builtin_spirv_faceforward(N, I, Ng); }

// CHECK-LABEL: define spir_func <4 x float> @test_faceforward_float4(
// CHECK-SAME: <4 x float> noundef [[N:%.*]], <4 x float> noundef [[I:%.*]], <4 x float> noundef [[NG:%.*]]) local_unnamed_addr #[[ATTR0]] {
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[SPV_FACEFORWARD:%.*]] = tail call <4 x float> @llvm.spv.faceforward.v4f32(<4 x float> [[N]], <4 x float> [[I]], <4 x float> [[NG]])
// CHECK-NEXT: ret <4 x float> [[SPV_FACEFORWARD]]
float4 test_faceforward_float4(float4 N, float4 I, float4 Ng) { return __builtin_spirv_faceforward(N, I, Ng); }
Loading
Loading