aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoshua Batista <jbatista@microsoft.com>2024-08-02 21:16:24 -0700
committerGitHub <noreply@github.com>2024-08-02 21:16:24 -0700
commited5b0e1e6986a652de8be88fa5ff92bf75c40a21 (patch)
tree30257b0fae34937bbb336a8a4e0b9c566948495b
parent53e87908c67f158bfe196a3c7cec690dc5eed1fc (diff)
downloadllvm-ed5b0e1e6986a652de8be88fa5ff92bf75c40a21.zip
llvm-ed5b0e1e6986a652de8be88fa5ff92bf75c40a21.tar.gz
llvm-ed5b0e1e6986a652de8be88fa5ff92bf75c40a21.tar.bz2
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 <mail@justinbogner.com>
-rw-r--r--clang/include/clang/Basic/Builtins.td6
-rw-r--r--clang/lib/CodeGen/CGBuiltin.cpp14
-rw-r--r--clang/lib/CodeGen/CGHLSLRuntime.h1
-rw-r--r--clang/lib/Headers/hlsl/hlsl_intrinsics.h32
-rw-r--r--clang/lib/Sema/SemaHLSL.cpp18
-rw-r--r--clang/test/CodeGenHLSL/builtins/length.hlsl73
-rw-r--r--clang/test/SemaHLSL/BuiltIns/length-errors.hlsl31
-rw-r--r--llvm/include/llvm/IR/IntrinsicsDirectX.td1
-rw-r--r--llvm/include/llvm/IR/IntrinsicsSPIRV.td1
-rw-r--r--llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp34
-rw-r--r--llvm/test/CodeGen/DirectX/length.ll116
-rw-r--r--llvm/test/CodeGen/DirectX/length_error.ll10
-rw-r--r--llvm/test/CodeGen/DirectX/length_invalid_intrinsic_error.ll10
-rw-r--r--llvm/test/CodeGen/DirectX/length_invalid_intrinsic_error_scalar.ll10
14 files changed, 357 insertions, 0 deletions
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index ccddeb9..b025a76 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -4707,6 +4707,12 @@ def HLSLIsinf : LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}
+def HLSLLength : LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_length"];
+ let Attributes = [NoThrow, Const];
+ let Prototype = "void(...)";
+}
+
def HLSLLerp : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_lerp"];
let Attributes = [NoThrow, Const];
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 3221989..13caab6 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -18452,6 +18452,20 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
/*ReturnType=*/X->getType(), CGM.getHLSLRuntime().getLerpIntrinsic(),
ArrayRef<Value *>{X, Y, S}, nullptr, "hlsl.lerp");
}
+ case Builtin::BI__builtin_hlsl_length: {
+ Value *X = EmitScalarExpr(E->getArg(0));
+
+ assert(E->getArg(0)->getType()->hasFloatingRepresentation() &&
+ "length operand must have a float representation");
+ // if the operand is a scalar, we can use the fabs llvm intrinsic directly
+ if (!E->getArg(0)->getType()->isVectorType())
+ return EmitFAbs(*this, X);
+
+ return Builder.CreateIntrinsic(
+ /*ReturnType=*/X->getType()->getScalarType(),
+ CGM.getHLSLRuntime().getLengthIntrinsic(), ArrayRef<Value *>{X},
+ nullptr, "hlsl.length");
+ }
case Builtin::BI__builtin_hlsl_elementwise_frac: {
Value *Op0 = EmitScalarExpr(E->getArg(0));
if (!E->getArg(0)->getType()->hasFloatingRepresentation())
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index 8c067f4..3f2dc0a 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -75,6 +75,7 @@ public:
GENERATE_HLSL_INTRINSIC_FUNCTION(All, all)
GENERATE_HLSL_INTRINSIC_FUNCTION(Any, any)
GENERATE_HLSL_INTRINSIC_FUNCTION(Frac, frac)
+ GENERATE_HLSL_INTRINSIC_FUNCTION(Length, length)
GENERATE_HLSL_INTRINSIC_FUNCTION(Lerp, lerp)
GENERATE_HLSL_INTRINSIC_FUNCTION(Rsqrt, rsqrt)
GENERATE_HLSL_INTRINSIC_FUNCTION(ThreadId, thread_id)
diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
index 6d86d27..e35a526 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -909,6 +909,38 @@ _HLSL_BUILTIN_ALIAS(__builtin_hlsl_lerp)
float4 lerp(float4, float4, float4);
//===----------------------------------------------------------------------===//
+// length builtins
+//===----------------------------------------------------------------------===//
+
+/// \fn T length(T x)
+/// \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 + …).
+
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
+half length(half);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
+half length(half2);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
+half length(half3);
+_HLSL_16BIT_AVAILABILITY(shadermodel, 6.2)
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
+half length(half4);
+
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
+float length(float);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
+float length(float2);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
+float length(float3);
+_HLSL_BUILTIN_ALIAS(__builtin_hlsl_length)
+float length(float4);
+
+//===----------------------------------------------------------------------===//
// log builtins
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 7724faf..a9c0c57 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1079,6 +1079,24 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
return true;
break;
}
+ case Builtin::BI__builtin_hlsl_length: {
+ if (CheckFloatOrHalfRepresentations(&SemaRef, TheCall))
+ return true;
+ if (SemaRef.checkArgCount(TheCall, 1))
+ return true;
+
+ ExprResult A = TheCall->getArg(0);
+ QualType ArgTyA = A.get()->getType();
+ QualType RetTy;
+
+ if (auto *VTy = ArgTyA->getAs<VectorType>())
+ RetTy = VTy->getElementType();
+ else
+ RetTy = TheCall->getArg(0)->getType();
+
+ TheCall->setType(RetTy);
+ break;
+ }
case Builtin::BI__builtin_hlsl_mad: {
if (SemaRef.checkArgCount(TheCall, 3))
return true;
diff --git a/clang/test/CodeGenHLSL/builtins/length.hlsl b/clang/test/CodeGenHLSL/builtins/length.hlsl
new file mode 100644
index 0000000..1c23b0d
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/length.hlsl
@@ -0,0 +1,73 @@
+// 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: %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
+
+// NATIVE_HALF: define noundef half @
+// NATIVE_HALF: call half @llvm.fabs.f16(half
+// NO_HALF: call float @llvm.fabs.f32(float
+// NATIVE_HALF: ret half
+// NO_HALF: ret float
+half test_length_half(half p0)
+{
+ return length(p0);
+}
+// NATIVE_HALF: define noundef half @
+// NATIVE_HALF: %hlsl.length = call half @llvm.dx.length.v2f16
+// NO_HALF: %hlsl.length = call float @llvm.dx.length.v2f32(
+// NATIVE_HALF: ret half %hlsl.length
+// NO_HALF: ret float %hlsl.length
+half test_length_half2(half2 p0)
+{
+ return length(p0);
+}
+// NATIVE_HALF: define noundef half @
+// NATIVE_HALF: %hlsl.length = call half @llvm.dx.length.v3f16
+// NO_HALF: %hlsl.length = call float @llvm.dx.length.v3f32(
+// NATIVE_HALF: ret half %hlsl.length
+// NO_HALF: ret float %hlsl.length
+half test_length_half3(half3 p0)
+{
+ return length(p0);
+}
+// NATIVE_HALF: define noundef half @
+// NATIVE_HALF: %hlsl.length = call half @llvm.dx.length.v4f16
+// NO_HALF: %hlsl.length = call float @llvm.dx.length.v4f32(
+// NATIVE_HALF: ret half %hlsl.length
+// NO_HALF: ret float %hlsl.length
+half test_length_half4(half4 p0)
+{
+ return length(p0);
+}
+
+// CHECK: define noundef float @
+// CHECK: call float @llvm.fabs.f32(float
+// CHECK: ret float
+float test_length_float(float p0)
+{
+ return length(p0);
+}
+// CHECK: define noundef float @
+// CHECK: %hlsl.length = call float @llvm.dx.length.v2f32(
+// CHECK: ret float %hlsl.length
+float test_length_float2(float2 p0)
+{
+ return length(p0);
+}
+// CHECK: define noundef float @
+// CHECK: %hlsl.length = call float @llvm.dx.length.v3f32(
+// CHECK: ret float %hlsl.length
+float test_length_float3(float3 p0)
+{
+ return length(p0);
+}
+// CHECK: define noundef float @
+// CHECK: %hlsl.length = call float @llvm.dx.length.v4f32(
+// CHECK: ret float %hlsl.length
+float test_length_float4(float4 p0)
+{
+ return length(p0);
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/length-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/length-errors.hlsl
new file mode 100644
index 0000000..fe0046a
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/length-errors.hlsl
@@ -0,0 +1,31 @@
+// 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
+
+void test_too_few_arg()
+{
+ return __builtin_hlsl_length();
+ // expected-error@-1 {{too few arguments to function call, expected 1, have 0}}
+}
+
+void test_too_many_arg(float2 p0)
+{
+ return __builtin_hlsl_length(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_length(p1);
+ // expected-error@-1 {passing 'bool' to parameter of incompatible type 'float'}}
+}
+
+bool builtin_length_int_to_float_promotion(int p1)
+{
+ return __builtin_hlsl_length(p1);
+ // expected-error@-1 {{passing 'int' to parameter of incompatible type 'float'}}
+}
+
+bool2 builtin_length_int2_to_float2_promotion(int2 p1)
+{
+ return __builtin_hlsl_length(p1);
+ // 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)}}
+}
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index a7f212d..312c386 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -55,6 +55,7 @@ def int_dx_isinf :
def int_dx_lerp : Intrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty, LLVMMatchType<0>,LLVMMatchType<0>],
[IntrNoMem, IntrWillReturn] >;
+def int_dx_length : DefaultAttrsIntrinsic<[LLVMVectorElementType<0>], [llvm_anyfloat_ty]>;
def int_dx_imad : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>]>;
def int_dx_umad : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>]>;
def int_dx_rcp : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>;
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index ef6ddf1..3f77ef6 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -63,5 +63,6 @@ let TargetPrefix = "spv" in {
def int_spv_frac : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]>;
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_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]>;
}
diff --git a/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp b/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
index 4b162a3..ac85859 100644
--- a/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
+++ b/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
@@ -42,6 +42,7 @@ static bool isIntrinsicExpansion(Function &F) {
case Intrinsic::dx_clamp:
case Intrinsic::dx_uclamp:
case Intrinsic::dx_lerp:
+ case Intrinsic::dx_length:
case Intrinsic::dx_sdot:
case Intrinsic::dx_udot:
return true;
@@ -157,6 +158,37 @@ static bool expandAnyIntrinsic(CallInst *Orig) {
return true;
}
+static bool expandLengthIntrinsic(CallInst *Orig) {
+ Value *X = Orig->getOperand(0);
+ IRBuilder<> Builder(Orig->getParent());
+ Builder.SetInsertPoint(Orig);
+ Type *Ty = X->getType();
+ Type *EltTy = Ty->getScalarType();
+
+ // Though dx.length does work on scalar type, we can optimize it to just emit
+ // fabs, in CGBuiltin.cpp. We shouldn't see a scalar type here because
+ // CGBuiltin.cpp should have emitted a fabs call.
+ Value *Elt = Builder.CreateExtractElement(X, (uint64_t)0);
+ auto *XVec = dyn_cast<FixedVectorType>(Ty);
+ unsigned XVecSize = XVec->getNumElements();
+ if (!(Ty->isVectorTy() && XVecSize > 1))
+ report_fatal_error(Twine("Invalid input type for length intrinsic"),
+ /* gen_crash_diag=*/false);
+
+ Value *Sum = Builder.CreateFMul(Elt, Elt);
+ for (unsigned I = 1; I < XVecSize; I++) {
+ Elt = Builder.CreateExtractElement(X, I);
+ Value *Mul = Builder.CreateFMul(Elt, Elt);
+ Sum = Builder.CreateFAdd(Sum, Mul);
+ }
+ Value *Result = Builder.CreateIntrinsic(
+ EltTy, Intrinsic::sqrt, ArrayRef<Value *>{Sum}, nullptr, "elt.sqrt");
+
+ Orig->replaceAllUsesWith(Result);
+ Orig->eraseFromParent();
+ return true;
+}
+
static bool expandLerpIntrinsic(CallInst *Orig) {
Value *X = Orig->getOperand(0);
Value *Y = Orig->getOperand(1);
@@ -280,6 +312,8 @@ static bool expandIntrinsic(Function &F, CallInst *Orig) {
return expandClampIntrinsic(Orig, F.getIntrinsicID());
case Intrinsic::dx_lerp:
return expandLerpIntrinsic(Orig);
+ case Intrinsic::dx_length:
+ return expandLengthIntrinsic(Orig);
case Intrinsic::dx_sdot:
case Intrinsic::dx_udot:
return expandIntegerDot(Orig, F.getIntrinsicID());
diff --git a/llvm/test/CodeGen/DirectX/length.ll b/llvm/test/CodeGen/DirectX/length.ll
new file mode 100644
index 0000000..d12fcf4
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/length.ll
@@ -0,0 +1,116 @@
+; RUN: opt -S -dxil-intrinsic-expansion < %s | FileCheck %s --check-prefixes=CHECK,EXPCHECK
+; RUN: opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library < %s | FileCheck %s --check-prefixes=CHECK,DOPCHECK
+
+; Make sure dxil operation function calls for length are generated for half/float.
+
+declare half @llvm.fabs.f16(half)
+declare half @llvm.dx.length.v2f16(<2 x half>)
+declare half @llvm.dx.length.v3f16(<3 x half>)
+declare half @llvm.dx.length.v4f16(<4 x half>)
+
+declare float @llvm.fabs.f32(float)
+declare float @llvm.dx.length.v2f32(<2 x float>)
+declare float @llvm.dx.length.v3f32(<3 x float>)
+declare float @llvm.dx.length.v4f32(<4 x float>)
+
+define noundef half @test_length_half2(<2 x half> noundef %p0) {
+entry:
+ ; CHECK: extractelement <2 x half> %{{.*}}, i64 0
+ ; CHECK: fmul half %{{.*}}, %{{.*}}
+ ; CHECK: extractelement <2 x half> %{{.*}}, i64 1
+ ; CHECK: fmul half %{{.*}}, %{{.*}}
+ ; CHECK: fadd half %{{.*}}, %{{.*}}
+ ; EXPCHECK: call half @llvm.sqrt.f16(half %{{.*}})
+ ; DOPCHECK: call half @dx.op.unary.f16(i32 24, half %{{.*}})
+
+ %hlsl.length = call half @llvm.dx.length.v2f16(<2 x half> %p0)
+ ret half %hlsl.length
+}
+
+define noundef half @test_length_half3(<3 x half> noundef %p0) {
+entry:
+ ; CHECK: extractelement <3 x half> %{{.*}}, i64 0
+ ; CHECK: fmul half %{{.*}}, %{{.*}}
+ ; CHECK: extractelement <3 x half> %{{.*}}, i64 1
+ ; CHECK: fmul half %{{.*}}, %{{.*}}
+ ; CHECK: fadd half %{{.*}}, %{{.*}}
+ ; CHECK: extractelement <3 x half> %{{.*}}, i64 2
+ ; CHECK: fmul half %{{.*}}, %{{.*}}
+ ; CHECK: fadd half %{{.*}}, %{{.*}}
+ ; EXPCHECK: call half @llvm.sqrt.f16(half %{{.*}})
+ ; DOPCHECK: call half @dx.op.unary.f16(i32 24, half %{{.*}})
+
+ %hlsl.length = call half @llvm.dx.length.v3f16(<3 x half> %p0)
+ ret half %hlsl.length
+}
+
+define noundef half @test_length_half4(<4 x half> noundef %p0) {
+entry:
+ ; CHECK: extractelement <4 x half> %{{.*}}, i64 0
+ ; CHECK: fmul half %{{.*}}, %{{.*}}
+ ; CHECK: extractelement <4 x half> %{{.*}}, i64 1
+ ; CHECK: fmul half %{{.*}}, %{{.*}}
+ ; CHECK: fadd half %{{.*}}, %{{.*}}
+ ; CHECK: extractelement <4 x half> %{{.*}}, i64 2
+ ; CHECK: fmul half %{{.*}}, %{{.*}}
+ ; CHECK: fadd half %{{.*}}, %{{.*}}
+ ; CHECK: extractelement <4 x half> %{{.*}}, i64 3
+ ; CHECK: fmul half %{{.*}}, %{{.*}}
+ ; CHECK: fadd half %{{.*}}, %{{.*}}
+ ; EXPCHECK: call half @llvm.sqrt.f16(half %{{.*}})
+ ; DOPCHECK: call half @dx.op.unary.f16(i32 24, half %{{.*}})
+
+ %hlsl.length = call half @llvm.dx.length.v4f16(<4 x half> %p0)
+ ret half %hlsl.length
+}
+
+define noundef float @test_length_float2(<2 x float> noundef %p0) {
+entry:
+ ; CHECK: extractelement <2 x float> %{{.*}}, i64 0
+ ; CHECK: fmul float %{{.*}}, %{{.*}}
+ ; CHECK: extractelement <2 x float> %{{.*}}, i64 1
+ ; CHECK: fmul float %{{.*}}, %{{.*}}
+ ; CHECK: fadd float %{{.*}}, %{{.*}}
+ ; EXPCHECK: call float @llvm.sqrt.f32(float %{{.*}})
+ ; DOPCHECK: call float @dx.op.unary.f32(i32 24, float %{{.*}})
+
+ %hlsl.length = call float @llvm.dx.length.v2f32(<2 x float> %p0)
+ ret float %hlsl.length
+}
+
+define noundef float @test_length_float3(<3 x float> noundef %p0) {
+entry:
+ ; CHECK: extractelement <3 x float> %{{.*}}, i64 0
+ ; CHECK: fmul float %{{.*}}, %{{.*}}
+ ; CHECK: extractelement <3 x float> %{{.*}}, i64 1
+ ; CHECK: fmul float %{{.*}}, %{{.*}}
+ ; CHECK: fadd float %{{.*}}, %{{.*}}
+ ; CHECK: extractelement <3 x float> %{{.*}}, i64 2
+ ; CHECK: fmul float %{{.*}}, %{{.*}}
+ ; CHECK: fadd float %{{.*}}, %{{.*}}
+ ; EXPCHECK: call float @llvm.sqrt.f32(float %{{.*}})
+ ; DOPCHECK: call float @dx.op.unary.f32(i32 24, float %{{.*}})
+
+ %hlsl.length = call float @llvm.dx.length.v3f32(<3 x float> %p0)
+ ret float %hlsl.length
+}
+
+define noundef float @test_length_float4(<4 x float> noundef %p0) {
+entry:
+ ; CHECK: extractelement <4 x float> %{{.*}}, i64 0
+ ; CHECK: fmul float %{{.*}}, %{{.*}}
+ ; CHECK: extractelement <4 x float> %{{.*}}, i64 1
+ ; CHECK: fmul float %{{.*}}, %{{.*}}
+ ; CHECK: fadd float %{{.*}}, %{{.*}}
+ ; CHECK: extractelement <4 x float> %{{.*}}, i64 2
+ ; CHECK: fmul float %{{.*}}, %{{.*}}
+ ; CHECK: fadd float %{{.*}}, %{{.*}}
+ ; CHECK: extractelement <4 x float> %{{.*}}, i64 3
+ ; CHECK: fmul float %{{.*}}, %{{.*}}
+ ; CHECK: fadd float %{{.*}}, %{{.*}}
+ ; EXPCHECK: call float @llvm.sqrt.f32(float %{{.*}})
+ ; DOPCHECK: call float @dx.op.unary.f32(i32 24, float %{{.*}})
+
+ %hlsl.length = call float @llvm.dx.length.v4f32(<4 x float> %p0)
+ ret float %hlsl.length
+}
diff --git a/llvm/test/CodeGen/DirectX/length_error.ll b/llvm/test/CodeGen/DirectX/length_error.ll
new file mode 100644
index 0000000..952c915
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/length_error.ll
@@ -0,0 +1,10 @@
+; RUN: not opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s
+
+; DXIL operation length does not support double overload type
+; CHECK: LLVM ERROR: Invalid Overload
+
+define noundef double @test_length_double2(<2 x double> noundef %p0) {
+entry:
+ %hlsl.length = call double @llvm.dx.length.v2f32(<2 x double> %p0)
+ ret double %hlsl.length
+}
diff --git a/llvm/test/CodeGen/DirectX/length_invalid_intrinsic_error.ll b/llvm/test/CodeGen/DirectX/length_invalid_intrinsic_error.ll
new file mode 100644
index 0000000..277fbaa
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/length_invalid_intrinsic_error.ll
@@ -0,0 +1,10 @@
+; RUN: not opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s
+
+; DXIL operation length does not support 1-element vector types.
+; CHECK: LLVM ERROR: Invalid input type for length intrinsic
+
+define noundef float @test_length_float(<1 x float> noundef %p0) {
+entry:
+ %hlsl.length = call float @llvm.dx.length.v1f32(<1 x float> %p0)
+ ret float %hlsl.length
+}
diff --git a/llvm/test/CodeGen/DirectX/length_invalid_intrinsic_error_scalar.ll b/llvm/test/CodeGen/DirectX/length_invalid_intrinsic_error_scalar.ll
new file mode 100644
index 0000000..ac3a051
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/length_invalid_intrinsic_error_scalar.ll
@@ -0,0 +1,10 @@
+; RUN: not opt -S -dxil-op-lower -mtriple=dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s
+
+; DXIL operation length does not support scalar types
+; CHECK: error: invalid intrinsic signature
+
+define noundef float @test_length_float(float noundef %p0) {
+entry:
+ %hlsl.length = call float @llvm.dx.length.f32(float %p0)
+ ret float %hlsl.length
+}