Skip to content

Commit ed5b0e1

Browse files
bob80905bogner
andauthored
Add length builtins and length HLSL function to DirectX Backend (#101256)
This PR adds the length intrinsic and an HLSL function that uses it. The SPIRV implementation is left for a future PR. This PR addresses #99134, though some SPIR-V changes still need to be made to complete the task. Below is how this PR addresses #99134. - "Implement `length` clang builtin" was done by defining `HLSLL ength` in Builtins.td - "Link `length` clang builtin with hlsl_intrinsics.h" was done by using the alias attribute to make `length` an alias of `__builtin_hlsl_elementwise_length` in hlsl_intrinsics.h - "Add sema checks for `length` to `CheckHLSLBuiltinFunctionCall` in `SemaChecking.cpp` " was done, but in this case not in SemaChecking.cpp, rather SemaHLSL.cpp. A case was added to the builtin to check for semantic failures, and set `TheCall` up to have the right return type. - "Add codegen for `length` to `EmitHLSLBuiltinExpr` in `CGBuiltin.cpp`" was done. For scalars, fabs is emitted, otherwise, length is emitted. - "Add codegen tests to `clang/test/CodeGenHLSL/builtins/length.hlsl` was done to test that `length` in HLSL emits the right intrinsic. - "Add sema tests to `clang/test/SemaHLSL/BuiltIns/length-errors.hlsl`" was done to test for diagnostics emitted in SemaHLSL.cpp - "Create the `int_dx_length` intrinsic in `IntrinsicsDirectX.td`" was done. Specifying return types and parameter types was difficult, but `idot` was used for reference, and `llvm\include\llvm\IR\Intrinsics.td` contains all the ways to express return / parameter types. - "Create an intrinsic expansion of `int_dx_length` in `llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp`" was done, and was mostly derived by looking at `TranslateLength` in `HLOperationLower.cpp` in the DXC codebase. - "Create the `length.ll` and `length_errors.ll` tests in `llvm/test/CodeGen/DirectX/`" was done by taking the DXIL output of `clang/test/CodeGenHLSL/builtins/length.hlsl` and running `opt -S -dxil-intrinsic-expansion` and ` opt -S -dxil-op-lower` on it, checking for how the length intrinsic was either expanded or lowered. - "Create the `int_spv_length` intrinsic in `IntrinsicsSPIRV.td`" was done by copying `IntrinsicsDirectX.td`. --------- Co-authored-by: Justin Bogner <[email protected]>
1 parent 53e8790 commit ed5b0e1

File tree

14 files changed

+357
-0
lines changed

14 files changed

+357
-0
lines changed

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4707,6 +4707,12 @@ def HLSLIsinf : LangBuiltin<"HLSL_LANG"> {
47074707
let Prototype = "void(...)";
47084708
}
47094709

4710+
def HLSLLength : LangBuiltin<"HLSL_LANG"> {
4711+
let Spellings = ["__builtin_hlsl_length"];
4712+
let Attributes = [NoThrow, Const];
4713+
let Prototype = "void(...)";
4714+
}
4715+
47104716
def HLSLLerp : LangBuiltin<"HLSL_LANG"> {
47114717
let Spellings = ["__builtin_hlsl_lerp"];
47124718
let Attributes = [NoThrow, Const];

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18452,6 +18452,20 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
1845218452
/*ReturnType=*/X->getType(), CGM.getHLSLRuntime().getLerpIntrinsic(),
1845318453
ArrayRef<Value *>{X, Y, S}, nullptr, "hlsl.lerp");
1845418454
}
18455+
case Builtin::BI__builtin_hlsl_length: {
18456+
Value *X = EmitScalarExpr(E->getArg(0));
18457+
18458+
assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
18459+
"length operand must have a float representation");
18460+
// if the operand is a scalar, we can use the fabs llvm intrinsic directly
18461+
if (!E->getArg(0)->getType()->isVectorType())
18462+
return EmitFAbs(*this, X);
18463+
18464+
return Builder.CreateIntrinsic(
18465+
/*ReturnType=*/X->getType()->getScalarType(),
18466+
CGM.getHLSLRuntime().getLengthIntrinsic(), ArrayRef<Value *>{X},
18467+
nullptr, "hlsl.length");
18468+
}
1845518469
case Builtin::BI__builtin_hlsl_elementwise_frac: {
1845618470
Value *Op0 = EmitScalarExpr(E->getArg(0));
1845718471
if (!E->getArg(0)->getType()->hasFloatingRepresentation())

clang/lib/CodeGen/CGHLSLRuntime.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ class CGHLSLRuntime {
7575
GENERATE_HLSL_INTRINSIC_FUNCTION(All, all)
7676
GENERATE_HLSL_INTRINSIC_FUNCTION(Any, any)
7777
GENERATE_HLSL_INTRINSIC_FUNCTION(Frac, frac)
78+
GENERATE_HLSL_INTRINSIC_FUNCTION(Length, length)
7879
GENERATE_HLSL_INTRINSIC_FUNCTION(Lerp, lerp)
7980
GENERATE_HLSL_INTRINSIC_FUNCTION(Rsqrt, rsqrt)
8081
GENERATE_HLSL_INTRINSIC_FUNCTION(ThreadId, thread_id)

clang/lib/Headers/hlsl/hlsl_intrinsics.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,38 @@ float3 lerp(float3, float3, float3);
908908
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_lerp)
909909
float4 lerp(float4, float4, float4);
910910

911+
//===----------------------------------------------------------------------===//
912+
// length builtins
913+
//===----------------------------------------------------------------------===//
914+
915+
/// \fn T length(T x)
916+
/// \brief Returns the length of the specified floating-point vector.
917+
/// \param x [in] The vector of floats, or a scalar float.
918+
///
919+
/// Length is based on the following formula: sqrt(x[0]^2 + x[1]^2 + …).
920+
921+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
922+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
923+
half length(half);
924+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
925+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
926+
half length(half2);
927+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
928+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
929+
half length(half3);
930+
_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
931+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
932+
half length(half4);
933+
934+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
935+
float length(float);
936+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
937+
float length(float2);
938+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
939+
float length(float3);
940+
_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
941+
float length(float4);
942+
911943
//===----------------------------------------------------------------------===//
912944
// log builtins
913945
//===----------------------------------------------------------------------===//

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,6 +1079,24 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
10791079
return true;
10801080
break;
10811081
}
1082+
case Builtin::BI__builtin_hlsl_length: {
1083+
if (CheckFloatOrHalfRepresentations(&SemaRef, TheCall))
1084+
return true;
1085+
if (SemaRef.checkArgCount(TheCall, 1))
1086+
return true;
1087+
1088+
ExprResult A = TheCall->getArg(0);
1089+
QualType ArgTyA = A.get()->getType();
1090+
QualType RetTy;
1091+
1092+
if (auto *VTy = ArgTyA->getAs<VectorType>())
1093+
RetTy = VTy->getElementType();
1094+
else
1095+
RetTy = TheCall->getArg(0)->getType();
1096+
1097+
TheCall->setType(RetTy);
1098+
break;
1099+
}
10821100
case Builtin::BI__builtin_hlsl_mad: {
10831101
if (SemaRef.checkArgCount(TheCall, 3))
10841102
return true;
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
2+
// RUN: dxil-pc-shadermodel6.3-library %s -fnative-half-type \
3+
// RUN: -emit-llvm -disable-llvm-passes -o - | FileCheck %s \
4+
// RUN: --check-prefixes=CHECK,NATIVE_HALF
5+
// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \
6+
// RUN: dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \
7+
// RUN: -o - | FileCheck %s --check-prefixes=CHECK,NO_HALF
8+
9+
// NATIVE_HALF: define noundef half @
10+
// NATIVE_HALF: call half @llvm.fabs.f16(half
11+
// NO_HALF: call float @llvm.fabs.f32(float
12+
// NATIVE_HALF: ret half
13+
// NO_HALF: ret float
14+
half test_length_half(half p0)
15+
{
16+
return length(p0);
17+
}
18+
// NATIVE_HALF: define noundef half @
19+
// NATIVE_HALF: %hlsl.length = call half @llvm.dx.length.v2f16
20+
// NO_HALF: %hlsl.length = call float @llvm.dx.length.v2f32(
21+
// NATIVE_HALF: ret half %hlsl.length
22+
// NO_HALF: ret float %hlsl.length
23+
half test_length_half2(half2 p0)
24+
{
25+
return length(p0);
26+
}
27+
// NATIVE_HALF: define noundef half @
28+
// NATIVE_HALF: %hlsl.length = call half @llvm.dx.length.v3f16
29+
// NO_HALF: %hlsl.length = call float @llvm.dx.length.v3f32(
30+
// NATIVE_HALF: ret half %hlsl.length
31+
// NO_HALF: ret float %hlsl.length
32+
half test_length_half3(half3 p0)
33+
{
34+
return length(p0);
35+
}
36+
// NATIVE_HALF: define noundef half @
37+
// NATIVE_HALF: %hlsl.length = call half @llvm.dx.length.v4f16
38+
// NO_HALF: %hlsl.length = call float @llvm.dx.length.v4f32(
39+
// NATIVE_HALF: ret half %hlsl.length
40+
// NO_HALF: ret float %hlsl.length
41+
half test_length_half4(half4 p0)
42+
{
43+
return length(p0);
44+
}
45+
46+
// CHECK: define noundef float @
47+
// CHECK: call float @llvm.fabs.f32(float
48+
// CHECK: ret float
49+
float test_length_float(float p0)
50+
{
51+
return length(p0);
52+
}
53+
// CHECK: define noundef float @
54+
// CHECK: %hlsl.length = call float @llvm.dx.length.v2f32(
55+
// CHECK: ret float %hlsl.length
56+
float test_length_float2(float2 p0)
57+
{
58+
return length(p0);
59+
}
60+
// CHECK: define noundef float @
61+
// CHECK: %hlsl.length = call float @llvm.dx.length.v3f32(
62+
// CHECK: ret float %hlsl.length
63+
float test_length_float3(float3 p0)
64+
{
65+
return length(p0);
66+
}
67+
// CHECK: define noundef float @
68+
// CHECK: %hlsl.length = call float @llvm.dx.length.v4f32(
69+
// CHECK: ret float %hlsl.length
70+
float test_length_float4(float4 p0)
71+
{
72+
return length(p0);
73+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -fnative-half-type -emit-llvm -disable-llvm-passes -verify -verify-ignore-unexpected
2+
3+
void test_too_few_arg()
4+
{
5+
return __builtin_hlsl_length();
6+
// expected-error@-1 {{too few arguments to function call, expected 1, have 0}}
7+
}
8+
9+
void test_too_many_arg(float2 p0)
10+
{
11+
return __builtin_hlsl_length(p0, p0);
12+
// expected-error@-1 {{too many arguments to function call, expected 1, have 2}}
13+
}
14+
15+
bool builtin_bool_to_float_type_promotion(bool p1)
16+
{
17+
return __builtin_hlsl_length(p1);
18+
// expected-error@-1 {passing 'bool' to parameter of incompatible type 'float'}}
19+
}
20+
21+
bool builtin_length_int_to_float_promotion(int p1)
22+
{
23+
return __builtin_hlsl_length(p1);
24+
// expected-error@-1 {{passing 'int' to parameter of incompatible type 'float'}}
25+
}
26+
27+
bool2 builtin_length_int2_to_float2_promotion(int2 p1)
28+
{
29+
return __builtin_hlsl_length(p1);
30+
// expected-error@-1 {{passing 'int2' (aka 'vector<int, 2>') to parameter of incompatible type '__attribute__((__vector_size__(2 * sizeof(float)))) float' (vector of 2 'float' values)}}
31+
}

llvm/include/llvm/IR/IntrinsicsDirectX.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def int_dx_isinf :
5555
def int_dx_lerp : Intrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>,LLVMMatchType<0>],
5656
[IntrNoMem, IntrWillReturn] >;
5757

58+
def int_dx_length : DefaultAttrsIntrinsic<[LLVMVectorElementType<0>], [llvm_anyfloat_ty]>;
5859
def int_dx_imad : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>]>;
5960
def int_dx_umad : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>]>;
6061
def int_dx_rcp : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>;

llvm/include/llvm/IR/IntrinsicsSPIRV.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,6 @@ let TargetPrefix = "spv" in {
6363
def int_spv_frac : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]>;
6464
def int_spv_lerp : Intrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>,LLVMMatchType<0>],
6565
[IntrNoMem, IntrWillReturn] >;
66+
def int_spv_length : DefaultAttrsIntrinsic<[LLVMVectorElementType<0>], [llvm_anyfloat_ty]>;
6667
def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]>;
6768
}

llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ static bool isIntrinsicExpansion(Function &F) {
4242
case Intrinsic::dx_clamp:
4343
case Intrinsic::dx_uclamp:
4444
case Intrinsic::dx_lerp:
45+
case Intrinsic::dx_length:
4546
case Intrinsic::dx_sdot:
4647
case Intrinsic::dx_udot:
4748
return true;
@@ -157,6 +158,37 @@ static bool expandAnyIntrinsic(CallInst *Orig) {
157158
return true;
158159
}
159160

161+
static bool expandLengthIntrinsic(CallInst *Orig) {
162+
Value *X = Orig->getOperand(0);
163+
IRBuilder<> Builder(Orig->getParent());
164+
Builder.SetInsertPoint(Orig);
165+
Type *Ty = X->getType();
166+
Type *EltTy = Ty->getScalarType();
167+
168+
// Though dx.length does work on scalar type, we can optimize it to just emit
169+
// fabs, in CGBuiltin.cpp. We shouldn't see a scalar type here because
170+
// CGBuiltin.cpp should have emitted a fabs call.
171+
Value *Elt = Builder.CreateExtractElement(X, (uint64_t)0);
172+
auto *XVec = dyn_cast<FixedVectorType>(Ty);
173+
unsigned XVecSize = XVec->getNumElements();
174+
if (!(Ty->isVectorTy() && XVecSize > 1))
175+
report_fatal_error(Twine("Invalid input type for length intrinsic"),
176+
/* gen_crash_diag=*/false);
177+
178+
Value *Sum = Builder.CreateFMul(Elt, Elt);
179+
for (unsigned I = 1; I < XVecSize; I++) {
180+
Elt = Builder.CreateExtractElement(X, I);
181+
Value *Mul = Builder.CreateFMul(Elt, Elt);
182+
Sum = Builder.CreateFAdd(Sum, Mul);
183+
}
184+
Value *Result = Builder.CreateIntrinsic(
185+
EltTy, Intrinsic::sqrt, ArrayRef<Value *>{Sum}, nullptr, "elt.sqrt");
186+
187+
Orig->replaceAllUsesWith(Result);
188+
Orig->eraseFromParent();
189+
return true;
190+
}
191+
160192
static bool expandLerpIntrinsic(CallInst *Orig) {
161193
Value *X = Orig->getOperand(0);
162194
Value *Y = Orig->getOperand(1);
@@ -280,6 +312,8 @@ static bool expandIntrinsic(Function &F, CallInst *Orig) {
280312
return expandClampIntrinsic(Orig, F.getIntrinsicID());
281313
case Intrinsic::dx_lerp:
282314
return expandLerpIntrinsic(Orig);
315+
case Intrinsic::dx_length:
316+
return expandLengthIntrinsic(Orig);
283317
case Intrinsic::dx_sdot:
284318
case Intrinsic::dx_udot:
285319
return expandIntegerDot(Orig, F.getIntrinsicID());

llvm/test/CodeGen/DirectX/length.ll

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
; RUN: opt -S -dxil-intrinsic-expansion < %s | FileCheck %s --check-prefixes=CHECK,EXPCHECK
2+
; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library < %s | FileCheck %s --check-prefixes=CHECK,DOPCHECK
3+
4+
; Make sure dxil operation function calls for length are generated for half/float.
5+
6+
declare half @llvm.fabs.f16(half)
7+
declare half @llvm.dx.length.v2f16(<2 x half>)
8+
declare half @llvm.dx.length.v3f16(<3 x half>)
9+
declare half @llvm.dx.length.v4f16(<4 x half>)
10+
11+
declare float @llvm.fabs.f32(float)
12+
declare float @llvm.dx.length.v2f32(<2 x float>)
13+
declare float @llvm.dx.length.v3f32(<3 x float>)
14+
declare float @llvm.dx.length.v4f32(<4 x float>)
15+
16+
define noundef half @test_length_half2(<2 x half> noundef %p0) {
17+
entry:
18+
; CHECK: extractelement <2 x half> %{{.*}}, i64 0
19+
; CHECK: fmul half %{{.*}}, %{{.*}}
20+
; CHECK: extractelement <2 x half> %{{.*}}, i64 1
21+
; CHECK: fmul half %{{.*}}, %{{.*}}
22+
; CHECK: fadd half %{{.*}}, %{{.*}}
23+
; EXPCHECK: call half @llvm.sqrt.f16(half %{{.*}})
24+
; DOPCHECK: call half @dx.op.unary.f16(i32 24, half %{{.*}})
25+
26+
%hlsl.length = call half @llvm.dx.length.v2f16(<2 x half> %p0)
27+
ret half %hlsl.length
28+
}
29+
30+
define noundef half @test_length_half3(<3 x half> noundef %p0) {
31+
entry:
32+
; CHECK: extractelement <3 x half> %{{.*}}, i64 0
33+
; CHECK: fmul half %{{.*}}, %{{.*}}
34+
; CHECK: extractelement <3 x half> %{{.*}}, i64 1
35+
; CHECK: fmul half %{{.*}}, %{{.*}}
36+
; CHECK: fadd half %{{.*}}, %{{.*}}
37+
; CHECK: extractelement <3 x half> %{{.*}}, i64 2
38+
; CHECK: fmul half %{{.*}}, %{{.*}}
39+
; CHECK: fadd half %{{.*}}, %{{.*}}
40+
; EXPCHECK: call half @llvm.sqrt.f16(half %{{.*}})
41+
; DOPCHECK: call half @dx.op.unary.f16(i32 24, half %{{.*}})
42+
43+
%hlsl.length = call half @llvm.dx.length.v3f16(<3 x half> %p0)
44+
ret half %hlsl.length
45+
}
46+
47+
define noundef half @test_length_half4(<4 x half> noundef %p0) {
48+
entry:
49+
; CHECK: extractelement <4 x half> %{{.*}}, i64 0
50+
; CHECK: fmul half %{{.*}}, %{{.*}}
51+
; CHECK: extractelement <4 x half> %{{.*}}, i64 1
52+
; CHECK: fmul half %{{.*}}, %{{.*}}
53+
; CHECK: fadd half %{{.*}}, %{{.*}}
54+
; CHECK: extractelement <4 x half> %{{.*}}, i64 2
55+
; CHECK: fmul half %{{.*}}, %{{.*}}
56+
; CHECK: fadd half %{{.*}}, %{{.*}}
57+
; CHECK: extractelement <4 x half> %{{.*}}, i64 3
58+
; CHECK: fmul half %{{.*}}, %{{.*}}
59+
; CHECK: fadd half %{{.*}}, %{{.*}}
60+
; EXPCHECK: call half @llvm.sqrt.f16(half %{{.*}})
61+
; DOPCHECK: call half @dx.op.unary.f16(i32 24, half %{{.*}})
62+
63+
%hlsl.length = call half @llvm.dx.length.v4f16(<4 x half> %p0)
64+
ret half %hlsl.length
65+
}
66+
67+
define noundef float @test_length_float2(<2 x float> noundef %p0) {
68+
entry:
69+
; CHECK: extractelement <2 x float> %{{.*}}, i64 0
70+
; CHECK: fmul float %{{.*}}, %{{.*}}
71+
; CHECK: extractelement <2 x float> %{{.*}}, i64 1
72+
; CHECK: fmul float %{{.*}}, %{{.*}}
73+
; CHECK: fadd float %{{.*}}, %{{.*}}
74+
; EXPCHECK: call float @llvm.sqrt.f32(float %{{.*}})
75+
; DOPCHECK: call float @dx.op.unary.f32(i32 24, float %{{.*}})
76+
77+
%hlsl.length = call float @llvm.dx.length.v2f32(<2 x float> %p0)
78+
ret float %hlsl.length
79+
}
80+
81+
define noundef float @test_length_float3(<3 x float> noundef %p0) {
82+
entry:
83+
; CHECK: extractelement <3 x float> %{{.*}}, i64 0
84+
; CHECK: fmul float %{{.*}}, %{{.*}}
85+
; CHECK: extractelement <3 x float> %{{.*}}, i64 1
86+
; CHECK: fmul float %{{.*}}, %{{.*}}
87+
; CHECK: fadd float %{{.*}}, %{{.*}}
88+
; CHECK: extractelement <3 x float> %{{.*}}, i64 2
89+
; CHECK: fmul float %{{.*}}, %{{.*}}
90+
; CHECK: fadd float %{{.*}}, %{{.*}}
91+
; EXPCHECK: call float @llvm.sqrt.f32(float %{{.*}})
92+
; DOPCHECK: call float @dx.op.unary.f32(i32 24, float %{{.*}})
93+
94+
%hlsl.length = call float @llvm.dx.length.v3f32(<3 x float> %p0)
95+
ret float %hlsl.length
96+
}
97+
98+
define noundef float @test_length_float4(<4 x float> noundef %p0) {
99+
entry:
100+
; CHECK: extractelement <4 x float> %{{.*}}, i64 0
101+
; CHECK: fmul float %{{.*}}, %{{.*}}
102+
; CHECK: extractelement <4 x float> %{{.*}}, i64 1
103+
; CHECK: fmul float %{{.*}}, %{{.*}}
104+
; CHECK: fadd float %{{.*}}, %{{.*}}
105+
; CHECK: extractelement <4 x float> %{{.*}}, i64 2
106+
; CHECK: fmul float %{{.*}}, %{{.*}}
107+
; CHECK: fadd float %{{.*}}, %{{.*}}
108+
; CHECK: extractelement <4 x float> %{{.*}}, i64 3
109+
; CHECK: fmul float %{{.*}}, %{{.*}}
110+
; CHECK: fadd float %{{.*}}, %{{.*}}
111+
; EXPCHECK: call float @llvm.sqrt.f32(float %{{.*}})
112+
; DOPCHECK: call float @dx.op.unary.f32(i32 24, float %{{.*}})
113+
114+
%hlsl.length = call float @llvm.dx.length.v4f32(<4 x float> %p0)
115+
ret float %hlsl.length
116+
}

0 commit comments

Comments
 (0)