[clang] ed5b0e1 - Add length builtins and length HLSL function to DirectX Backend (#101256)

via cfe-commits cfe-commits at lists.llvm.org
Fri Aug 2 21:16:29 PDT 2024


Author: Joshua Batista
Date: 2024-08-02T21:16:24-07:00
New Revision: ed5b0e1e6986a652de8be88fa5ff92bf75c40a21

URL: https://github.com/llvm/llvm-project/commit/ed5b0e1e6986a652de8be88fa5ff92bf75c40a21
DIFF: https://github.com/llvm/llvm-project/commit/ed5b0e1e6986a652de8be88fa5ff92bf75c40a21.diff

LOG: 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 at justinbogner.com>

Added: 
    clang/test/CodeGenHLSL/builtins/length.hlsl
    clang/test/SemaHLSL/BuiltIns/length-errors.hlsl
    llvm/test/CodeGen/DirectX/length.ll
    llvm/test/CodeGen/DirectX/length_error.ll
    llvm/test/CodeGen/DirectX/length_invalid_intrinsic_error.ll
    llvm/test/CodeGen/DirectX/length_invalid_intrinsic_error_scalar.ll

Modified: 
    clang/include/clang/Basic/Builtins.td
    clang/lib/CodeGen/CGBuiltin.cpp
    clang/lib/CodeGen/CGHLSLRuntime.h
    clang/lib/Headers/hlsl/hlsl_intrinsics.h
    clang/lib/Sema/SemaHLSL.cpp
    llvm/include/llvm/IR/IntrinsicsDirectX.td
    llvm/include/llvm/IR/IntrinsicsSPIRV.td
    llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index ccddeb9396284..b025a7681bfac 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 3221989e14351..13caab6c42111 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 8c067f4963955..3f2dc0ae7b84d 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -75,6 +75,7 @@ class CGHLSLRuntime {
   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 6d86d278bc711..e35a5262f9280 100644
--- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h
+++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h
@@ -908,6 +908,38 @@ float3 lerp(float3, float3, float3);
 _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 7724fafb253c5..a9c0c57e88221 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 0000000000000..1c23b0df04df9
--- /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 0000000000000..fe0046a2ab5a1
--- /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 at -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 at -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 at -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 at -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 at -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 a7f212da2f5b6..312c3862f240d 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 ef6ddf12c32f6..3f77ef6bfcdbe 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 4b162a35365c8..ac85859af8a53 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 0000000000000..d12fcf4722a07
--- /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 0000000000000..952c9155b2599
--- /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 0000000000000..277fbaa462b0f
--- /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 0000000000000..ac3a0513eb6b2
--- /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
+}


        


More information about the cfe-commits mailing list