Skip to content
Merged
Changes from 1 commit
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
190 changes: 121 additions & 69 deletions clang/test/CodeGenHLSL/builtins/lerp-overloads.hlsl
Original file line number Diff line number Diff line change
@@ -1,108 +1,160 @@
// RUN: %clang_cc1 -std=hlsl202x -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 \
// RUN: -DFNATTRS="noundef nofpclass(nan inf)" -DTARGET=dx
// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -x hlsl -triple \
// RUN: spirv-unknown-vulkan-compute %s -emit-llvm -disable-llvm-passes \
// RUN: -o - | FileCheck %s --check-prefixes=CHECK \
// RUN: -DFNATTRS="spir_func noundef nofpclass(nan inf)" -DTARGET=spv

// CHECK-LABEL: test_lerp_double
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn float @llvm.[[TARGET]].lerp.f32(float %{{.*}}, float %{{.*}}, float %{{.*}})
// CHECK: ret float %hlsl.lerp
// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -x hlsl -triple dxil-pc-shadermodel6.3-library %s -emit-llvm -O1 -o - | FileCheck %s --check-prefixes=CHECK -DFNATTRS="noundef nofpclass(nan inf)" -DTARGET=dx
Copy link
Member

@farzonl farzonl Apr 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you need O1 for this you can check for fptrunc or uitofp without you just can't do CHECK-NEXT it would have to be all CHECK calls.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With -disable-llvm-passes we're testing IR that looks like this:

; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
define noundef nofpclass(nan inf) float @_Z16test_lerp_doubled(double noundef nofpclass(nan inf) %p0) #0 {
entry:
  %p0.addr = alloca double, align 8
  store double %p0, ptr %p0.addr, align 8
  %0 = load double, ptr %p0.addr, align 8
  %1 = load double, ptr %p0.addr, align 8
  %2 = load double, ptr %p0.addr, align 8
  %call = call reassoc nnan ninf nsz arcp afn noundef nofpclass(nan inf) float @_ZN4hlsl4lerpEddd(double noundef nofpclass(nan inf) %0, double noundef nofpclass(nan inf) %1, double noundef nofpclass(nan inf) %2) #2
  ret float %call
}

; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
define linkonce_odr noundef nofpclass(nan inf) float @_ZN4hlsl4lerpEddd(double noundef nofpclass(nan inf) %V1, double noundef nofpclass(nan inf) %V2, double noundef nofpclass(nan inf) %V3) #0 {
entry:
  %V1.addr = alloca double, align 8
  %V2.addr = alloca double, align 8
  %V3.addr = alloca double, align 8
  store double %V1, ptr %V1.addr, align 8
  store double %V2, ptr %V2.addr, align 8
  store double %V3, ptr %V3.addr, align 8
  %0 = load double, ptr %V1.addr, align 8
  %conv = fptrunc reassoc nnan ninf nsz arcp afn double %0 to float
  %1 = load double, ptr %V2.addr, align 8
  %conv1 = fptrunc reassoc nnan ninf nsz arcp afn double %1 to float
  %2 = load double, ptr %V3.addr, align 8
  %conv2 = fptrunc reassoc nnan ninf nsz arcp afn double %2 to float
  %hlsl.lerp = call reassoc nnan ninf nsz arcp afn float @llvm.dx.lerp.f32(float %conv, float %conv1, float %conv2)
  ret float %hlsl.lerp
}

The first check matched the start of the @_Z16test_lerp_doubled function, and the second check matched the second last instruction in @_ZN4hlsl4lerpEddd. This relies on the called function happening to come immediately after the caller, and of course it breaks if we have two tests that call the same overload. In this case we happened not to be.

Arguably, we could do -O0 instead and test the following:

define noundef nofpclass(nan inf) float @_Z16test_lerp_doubled(double noundef nofpclass(nan inf) %p0) #0 {
entry:
  %V1.addr.i = alloca double, align 8
  %V2.addr.i = alloca double, align 8
  %V3.addr.i = alloca double, align 8
  %p0.addr = alloca double, align 8
  store double %p0, ptr %p0.addr, align 8
  %0 = load double, ptr %p0.addr, align 8
  %1 = load double, ptr %p0.addr, align 8
  %2 = load double, ptr %p0.addr, align 8
  store double %0, ptr %V1.addr.i, align 8
  store double %1, ptr %V2.addr.i, align 8
  store double %2, ptr %V3.addr.i, align 8
  %3 = load double, ptr %V1.addr.i, align 8
  %conv.i = fptrunc reassoc nnan ninf nsz arcp afn double %3 to float
  %4 = load double, ptr %V2.addr.i, align 8
  %conv1.i = fptrunc reassoc nnan ninf nsz arcp afn double %4 to float
  %5 = load double, ptr %V3.addr.i, align 8
  %conv2.i = fptrunc reassoc nnan ninf nsz arcp afn double %5 to float
  %hlsl.lerp.i = call reassoc nnan ninf nsz arcp afn noundef float @llvm.dx.lerp.f32(float %conv.i, float %conv1.i, float %conv2.i)
  ret float %hlsl.lerp.i
}

In this case I could check for fptrunc. Of course, if I want to check that that fptrunc feeds to the call to lerp I need to check for 3 of them.

With -O1 we can write checks that this gives us precisely what we expect in a way that's easy to read, understand, and verify is correct.

Copy link
Member

@farzonl farzonl Apr 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concern is that we would have tests dependent on optimization level. I think in practice these are theoretical concerns but i’m going to make them anyway. First while unlikely this could obscure bugs in our implementation. I would want to confirm these test are correct before optimizations. Second while rare That could mean the next time someone changes what passes run as part of O1 that could change our tests.

We have been doing -O0 for other hlsl source dependent tests so we can take advantage of constexprs. I don’t know why that would be problematic here.

I need to check for 3 of them.

Why cant you just do a CHECK-COUNT?

Copy link
Contributor Author

@bogner bogner Apr 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, these tests are checking that a single function maps to a single intrinsic - unless we start adding passes that replace a target specific intrinsic with another I don't see what could possibly change here.

These are really tests that we have the right set of overloads, and that they map to the obvious implementation. Most, if not all, of the logic that's interesting for these tests is in Sema anyway. The only other kind of bug that these are likely to catch in practice is that we literally lower to the wrong operation. "If you pass in these arguments, it generates something sane - if you pass in those, it's an error".

I guess I'm fine with changing these to be -O0 tests, but that's mostly because I'm 100% convinced it doesn't matter. I definitely do not agree that those prove anything that these don't, and the tests are enough harder to read and write that I do think they're worse from the point of view of reviewability and understanding the code via the tests.

FWIW, it's incredibly prevalent in the clang CodeGen tests to use -O1 so that the tests are more readable (as the allocas for stack variables are just generally noisy):

$ grep -rho -- " -O[0-9]" clang/test/CodeGen*  | sort | uniq -c
    701  -O0
   1200  -O1
    522  -O2
    317  -O3

Why cant you just do a CHECK-COUNT?

Because you can't capture 3 patterns with CHECK-COUNT? As written, the tests pattern match the result of the fptrunc and make sure it's used in the call to the intrinsic. Since it's the same argument across the board, this keeps things concise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed offline with @farzonl - I've opted to switch this to -O0 to be consistent with many of the other hlsl builtins tests

// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -x hlsl -triple spirv-unknown-vulkan-compute %s -emit-llvm -O1 -o - | FileCheck %s --check-prefixes=CHECK -DFNATTRS="spir_func noundef nofpclass(nan inf)" -DTARGET=spv

// CHECK: define [[FNATTRS]] float @_Z16test_lerp_doubled(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = fptrunc {{.*}} double [[P0:%.*]] to float
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} float @llvm.[[TARGET]].lerp.f32(float [[CONV]], float [[CONV]], float [[CONV]])
// CHECK-NEXT: ret float [[LERP]]
float test_lerp_double(double p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_double2
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn <2 x float> @llvm.[[TARGET]].lerp.v2f32(<2 x float> %{{.*}}, <2 x float> %{{.*}}, <2 x float> %{{.*}})
// CHECK: ret <2 x float> %hlsl.lerp
// CHECK: define [[FNATTRS]] <2 x float> @_Z17test_lerp_double2Dv2_d(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = fptrunc {{.*}} <2 x double> [[P0:%.*]] to <2 x float>
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} <2 x float> @llvm.[[TARGET]].lerp.v2f32(<2 x float> [[CONV]], <2 x float> [[CONV]], <2 x float> [[CONV]])
// CHECK-NEXT: ret <2 x float> [[LERP]]
float2 test_lerp_double2(double2 p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_double3
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn <3 x float> @llvm.[[TARGET]].lerp.v3f32(<3 x float> %{{.*}}, <3 x float> %{{.*}}, <3 x float> %{{.*}})
// CHECK: ret <3 x float> %hlsl.lerp
// CHECK: define [[FNATTRS]] <3 x float> @_Z17test_lerp_double3Dv3_d(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = fptrunc {{.*}} <3 x double> [[P0:%.*]] to <3 x float>
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} <3 x float> @llvm.[[TARGET]].lerp.v3f32(<3 x float> [[CONV]], <3 x float> [[CONV]], <3 x float> [[CONV]])
// CHECK-NEXT: ret <3 x float> [[LERP]]
//
float3 test_lerp_double3(double3 p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_double4
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn <4 x float> @llvm.[[TARGET]].lerp.v4f32(<4 x float> %{{.*}}, <4 x float> %{{.*}}, <4 x float> %{{.*}})
// CHECK: ret <4 x float> %hlsl.lerp
// CHECK: define [[FNATTRS]] <4 x float> @_Z17test_lerp_double4Dv4_d(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = fptrunc {{.*}} <4 x double> [[P0:%.*]] to <4 x float>
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} <4 x float> @llvm.[[TARGET]].lerp.v4f32(<4 x float> [[CONV]], <4 x float> [[CONV]], <4 x float> [[CONV]])
// CHECK-NEXT: ret <4 x float> [[LERP]]
//
float4 test_lerp_double4(double4 p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_int
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn float @llvm.[[TARGET]].lerp.f32(float %{{.*}}, float %{{.*}}, float %{{.*}})
// CHECK: ret float %hlsl.lerp
// CHECK: define [[FNATTRS]] float @_Z13test_lerp_inti(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = sitofp i32 [[P0:%.*]] to float
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} float @llvm.[[TARGET]].lerp.f32(float [[CONV]], float [[CONV]], float [[CONV]])
// CHECK-NEXT: ret float [[LERP]]
//
float test_lerp_int(int p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_int2
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn <2 x float> @llvm.[[TARGET]].lerp.v2f32(<2 x float> %{{.*}}, <2 x float> %{{.*}}, <2 x float> %{{.*}})
// CHECK: ret <2 x float> %hlsl.lerp
// CHECK: define [[FNATTRS]] <2 x float> @_Z14test_lerp_int2Dv2_i(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = sitofp <2 x i32> [[P0:%.*]] to <2 x float>
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} <2 x float> @llvm.[[TARGET]].lerp.v2f32(<2 x float> [[CONV]], <2 x float> [[CONV]], <2 x float> [[CONV]])
// CHECK-NEXT: ret <2 x float> [[LERP]]
//
float2 test_lerp_int2(int2 p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_int3
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn <3 x float> @llvm.[[TARGET]].lerp.v3f32(<3 x float> %{{.*}}, <3 x float> %{{.*}}, <3 x float> %{{.*}})
// CHECK: ret <3 x float> %hlsl.lerp
// CHECK: define [[FNATTRS]] <3 x float> @_Z14test_lerp_int3Dv3_i(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = sitofp <3 x i32> [[P0:%.*]] to <3 x float>
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} <3 x float> @llvm.[[TARGET]].lerp.v3f32(<3 x float> [[CONV]], <3 x float> [[CONV]], <3 x float> [[CONV]])
// CHECK-NEXT: ret <3 x float> [[LERP]]
//
float3 test_lerp_int3(int3 p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_int4
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn <4 x float> @llvm.[[TARGET]].lerp.v4f32(<4 x float> %{{.*}}, <4 x float> %{{.*}}, <4 x float> %{{.*}})
// CHECK: ret <4 x float> %hlsl.lerp
// CHECK: define [[FNATTRS]] <4 x float> @_Z14test_lerp_int4Dv4_i(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = sitofp <4 x i32> [[P0:%.*]] to <4 x float>
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} <4 x float> @llvm.[[TARGET]].lerp.v4f32(<4 x float> [[CONV]], <4 x float> [[CONV]], <4 x float> [[CONV]])
// CHECK-NEXT: ret <4 x float> [[LERP]]
//
float4 test_lerp_int4(int4 p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_uint
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn float @llvm.[[TARGET]].lerp.f32(float %{{.*}}, float %{{.*}}, float %{{.*}})
// CHECK: ret float %hlsl.lerp
// CHECK: define [[FNATTRS]] float @_Z14test_lerp_uintj(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = uitofp i32 [[P0:%.*]] to float
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} float @llvm.[[TARGET]].lerp.f32(float [[CONV]], float [[CONV]], float [[CONV]])
// CHECK-NEXT: ret float [[LERP]]
//
float test_lerp_uint(uint p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_uint2
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn <2 x float> @llvm.[[TARGET]].lerp.v2f32(<2 x float> %{{.*}}, <2 x float> %{{.*}}, <2 x float> %{{.*}})
// CHECK: ret <2 x float> %hlsl.lerp
// CHECK: define [[FNATTRS]] <2 x float> @_Z15test_lerp_uint2Dv2_j(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = uitofp <2 x i32> [[P0:%.*]] to <2 x float>
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} <2 x float> @llvm.[[TARGET]].lerp.v2f32(<2 x float> [[CONV]], <2 x float> [[CONV]], <2 x float> [[CONV]])
// CHECK-NEXT: ret <2 x float> [[LERP]]
//
float2 test_lerp_uint2(uint2 p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_uint3
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn <3 x float> @llvm.[[TARGET]].lerp.v3f32(<3 x float> %{{.*}}, <3 x float> %{{.*}}, <3 x float> %{{.*}})
// CHECK: ret <3 x float> %hlsl.lerp
// CHECK: define [[FNATTRS]] <3 x float> @_Z15test_lerp_uint3Dv3_j(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = uitofp <3 x i32> [[P0:%.*]] to <3 x float>
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} <3 x float> @llvm.[[TARGET]].lerp.v3f32(<3 x float> [[CONV]], <3 x float> [[CONV]], <3 x float> [[CONV]])
// CHECK-NEXT: ret <3 x float> [[LERP]]
//
float3 test_lerp_uint3(uint3 p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_uint4
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn <4 x float> @llvm.[[TARGET]].lerp.v4f32(<4 x float> %{{.*}}, <4 x float> %{{.*}}, <4 x float> %{{.*}})
// CHECK: ret <4 x float> %hlsl.lerp
// CHECK: define [[FNATTRS]] <4 x float> @_Z15test_lerp_uint4Dv4_j(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = uitofp <4 x i32> [[P0:%.*]] to <4 x float>
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} <4 x float> @llvm.[[TARGET]].lerp.v4f32(<4 x float> [[CONV]], <4 x float> [[CONV]], <4 x float> [[CONV]])
// CHECK-NEXT: ret <4 x float> [[LERP]]
//
float4 test_lerp_uint4(uint4 p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_int64_t
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn float @llvm.[[TARGET]].lerp.f32(float %{{.*}}, float %{{.*}}, float %{{.*}})
// CHECK: ret float %hlsl.lerp
// CHECK: define [[FNATTRS]] float @_Z17test_lerp_int64_tl(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = sitofp i64 [[P0:%.*]] to float
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} float @llvm.[[TARGET]].lerp.f32(float [[CONV]], float [[CONV]], float [[CONV]])
// CHECK-NEXT: ret float [[LERP]]
//
float test_lerp_int64_t(int64_t p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_int64_t2
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn <2 x float> @llvm.[[TARGET]].lerp.v2f32(<2 x float> %{{.*}}, <2 x float> %{{.*}}, <2 x float> %{{.*}})
// CHECK: ret <2 x float> %hlsl.lerp
// CHECK: define [[FNATTRS]] <2 x float> @_Z18test_lerp_int64_t2Dv2_l(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = sitofp <2 x i64> [[P0:%.*]] to <2 x float>
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} <2 x float> @llvm.[[TARGET]].lerp.v2f32(<2 x float> [[CONV]], <2 x float> [[CONV]], <2 x float> [[CONV]])
// CHECK-NEXT: ret <2 x float> [[LERP]]
//
float2 test_lerp_int64_t2(int64_t2 p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_int64_t3
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn <3 x float> @llvm.[[TARGET]].lerp.v3f32(<3 x float> %{{.*}}, <3 x float> %{{.*}}, <3 x float> %{{.*}})
// CHECK: ret <3 x float> %hlsl.lerp
// CHECK: define [[FNATTRS]] <3 x float> @_Z18test_lerp_int64_t3Dv3_l(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = sitofp <3 x i64> [[P0:%.*]] to <3 x float>
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} <3 x float> @llvm.[[TARGET]].lerp.v3f32(<3 x float> [[CONV]], <3 x float> [[CONV]], <3 x float> [[CONV]])
// CHECK-NEXT: ret <3 x float> [[LERP]]
//
float3 test_lerp_int64_t3(int64_t3 p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_int64_t4
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn <4 x float> @llvm.[[TARGET]].lerp.v4f32(<4 x float> %{{.*}}, <4 x float> %{{.*}}, <4 x float> %{{.*}})
// CHECK: ret <4 x float> %hlsl.lerp
// CHECK: define [[FNATTRS]] <4 x float> @_Z18test_lerp_int64_t4Dv4_l(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = sitofp <4 x i64> [[P0:%.*]] to <4 x float>
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} <4 x float> @llvm.[[TARGET]].lerp.v4f32(<4 x float> [[CONV]], <4 x float> [[CONV]], <4 x float> [[CONV]])
// CHECK-NEXT: ret <4 x float> [[LERP]]
//
float4 test_lerp_int64_t4(int64_t4 p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_uint64_t
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn float @llvm.[[TARGET]].lerp.f32(float %{{.*}}, float %{{.*}}, float %{{.*}})
// CHECK: ret float %hlsl.lerp
// CHECK: define [[FNATTRS]] float @_Z18test_lerp_uint64_tm(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = uitofp i64 [[P0:%.*]] to float
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} float @llvm.[[TARGET]].lerp.f32(float [[CONV]], float [[CONV]], float [[CONV]])
// CHECK-NEXT: ret float [[LERP]]
//
float test_lerp_uint64_t(uint64_t p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_uint64_t2
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn <2 x float> @llvm.[[TARGET]].lerp.v2f32(<2 x float> %{{.*}}, <2 x float> %{{.*}}, <2 x float> %{{.*}})
// CHECK: ret <2 x float> %hlsl.lerp
// CHECK: define [[FNATTRS]] <2 x float> @_Z19test_lerp_uint64_t2Dv2_m(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = uitofp <2 x i64> [[P0:%.*]] to <2 x float>
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} <2 x float> @llvm.[[TARGET]].lerp.v2f32(<2 x float> [[CONV]], <2 x float> [[CONV]], <2 x float> [[CONV]])
// CHECK-NEXT: ret <2 x float> [[LERP]]
//
float2 test_lerp_uint64_t2(uint64_t2 p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_uint64_t3
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn <3 x float> @llvm.[[TARGET]].lerp.v3f32(<3 x float> %{{.*}}, <3 x float> %{{.*}}, <3 x float> %{{.*}})
// CHECK: ret <3 x float> %hlsl.lerp
// CHECK: define [[FNATTRS]] <3 x float> @_Z19test_lerp_uint64_t3Dv3_m(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = uitofp <3 x i64> [[P0:%.*]] to <3 x float>
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} <3 x float> @llvm.[[TARGET]].lerp.v3f32(<3 x float> [[CONV]], <3 x float> [[CONV]], <3 x float> [[CONV]])
// CHECK-NEXT: ret <3 x float> [[LERP]]
//
float3 test_lerp_uint64_t3(uint64_t3 p0) { return lerp(p0, p0, p0); }

// CHECK-LABEL: test_lerp_uint64_t4
// CHECK: %hlsl.lerp = call reassoc nnan ninf nsz arcp afn <4 x float> @llvm.[[TARGET]].lerp.v4f32(<4 x float> %{{.*}}, <4 x float> %{{.*}}, <4 x float> %{{.*}})
// CHECK: ret <4 x float> %hlsl.lerp
// CHECK: define [[FNATTRS]] <4 x float> @_Z19test_lerp_uint64_t4Dv4_m(
// CHECK-NEXT: [[ENTRY:.*:]]
// CHECK-NEXT: [[CONV:%.*]] = uitofp <4 x i64> [[P0:%.*]] to <4 x float>
// CHECK-NEXT: [[LERP:%.*]] = tail call {{.*}} <4 x float> @llvm.[[TARGET]].lerp.v4f32(<4 x float> [[CONV]], <4 x float> [[CONV]], <4 x float> [[CONV]])
// CHECK-NEXT: ret <4 x float> [[LERP]]
//
float4 test_lerp_uint64_t4(uint64_t4 p0) { return lerp(p0, p0, p0); }